Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 11 additions & 36 deletions packages/tui/apps/publish/app.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,22 @@
import { Box, Text, useApp, useInput } from "ink"
import { useState } from "react"
import { useApp } from "ink"
import { WizardExpertSelector } from "../../src/components/index.js"
import type { WizardExpertChoice } from "../../src/types/wizard.js"

type PublishAppProps = {
experts: WizardExpertChoice[]
onSelect: (expertName: string) => void
}

export function PublishApp({ experts, onSelect }: PublishAppProps) {
const { exit } = useApp()
const [selectedIndex, setSelectedIndex] = useState(0)
useInput((input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : experts.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < experts.length - 1 ? prev + 1 : 0))
} else if (key.return) {
const expert = experts[selectedIndex]
if (expert) {
onSelect(expert.name)
exit()
}
} else if (input === "q" || key.escape) {
exit()
}
})
const handleSelect = (name: string) => {
onSelect(name)
exit()
}
return (
<Box flexDirection="column">
<Text bold>Select an Expert to publish:</Text>
<Box flexDirection="column" marginTop={1}>
{experts.map((expert, index) => (
<Box key={expert.name}>
<Text color={index === selectedIndex ? "cyan" : undefined}>
{index === selectedIndex ? "❯ " : " "}
{expert.name}
{expert.description && <Text dimColor> - {expert.description}</Text>}
</Text>
</Box>
))}
</Box>
<Box marginTop={1}>
<Text dimColor>↑↓ navigate · enter select · q quit</Text>
</Box>
</Box>
<WizardExpertSelector
title="Select an Expert to publish:"
experts={experts}
onSelect={handleSelect}
/>
)
}
57 changes: 8 additions & 49 deletions packages/tui/apps/status/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box, Text, useApp, useInput } from "ink"
import { useState } from "react"
import { ErrorStep } from "../../src/components/error-step.js"
import { ErrorStep, WizardExpertSelector } from "../../src/components/index.js"
import type { WizardExpertChoice, WizardVersionInfo } from "../../src/types/wizard.js"
import { getStatusColor } from "../../src/utils/index.js"

Expand All @@ -11,19 +11,16 @@ type WizardStep =
| { type: "selectStatus"; expertKey: string; currentStatus: string }
| { type: "confirm"; expertKey: string; status: string; currentStatus: string }
| { type: "error"; message: string }

type StatusWizardResult = {
expertKey: string
status: "available" | "deprecated" | "disabled"
}

type StatusAppProps = {
experts: WizardExpertChoice[]
onFetchVersions: (expertName: string) => Promise<WizardVersionInfo[]>
onComplete: (result: StatusWizardResult) => void
onCancel: () => void
}

function getAvailableStatusTransitions(currentStatus: string): string[] {
switch (currentStatus) {
case "available":
Expand All @@ -37,50 +34,6 @@ function getAvailableStatusTransitions(currentStatus: string): string[] {
}
}

function ExpertSelector({
experts,
onSelect,
}: {
experts: WizardExpertChoice[]
onSelect: (name: string) => void
}) {
const { exit } = useApp()
const [selectedIndex, setSelectedIndex] = useState(0)
useInput((input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : experts.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < experts.length - 1 ? prev + 1 : 0))
} else if (key.return) {
const expert = experts[selectedIndex]
if (expert) {
onSelect(expert.name)
}
} else if (input === "q" || key.escape) {
exit()
}
})
return (
<Box flexDirection="column">
<Text bold>Select an Expert to change status:</Text>
<Box flexDirection="column" marginTop={1}>
{experts.map((expert, index) => (
<Box key={expert.name}>
<Text color={index === selectedIndex ? "cyan" : undefined}>
{index === selectedIndex ? "❯ " : " "}
{expert.name}
{expert.description && <Text dimColor> - {expert.description}</Text>}
</Text>
</Box>
))}
</Box>
<Box marginTop={1}>
<Text dimColor>↑↓ navigate · enter select · q quit</Text>
</Box>
</Box>
)
}

function VersionSelector({
expertName,
versions,
Expand Down Expand Up @@ -323,7 +276,13 @@ export function StatusApp({ experts, onFetchVersions, onComplete, onCancel }: St
}
switch (step.type) {
case "selectExpert":
return <ExpertSelector experts={experts} onSelect={handleExpertSelect} />
return (
<WizardExpertSelector
title="Select an Expert to change status:"
experts={experts}
onSelect={handleExpertSelect}
/>
)
case "loadingVersions":
return (
<Box>
Expand Down
56 changes: 8 additions & 48 deletions packages/tui/apps/tag/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box, Text, useApp, useInput } from "ink"
import { useState } from "react"
import { ErrorStep } from "../../src/components/error-step.js"
import { ErrorStep, WizardExpertSelector } from "../../src/components/index.js"
import type { WizardExpertChoice, WizardVersionInfo } from "../../src/types/wizard.js"
import { getStatusColor } from "../../src/utils/index.js"

Expand All @@ -11,63 +11,17 @@ type WizardStep =
| { type: "inputTags"; expertKey: string; currentTags: string[] }
| { type: "confirm"; expertKey: string; tags: string[]; currentTags: string[] }
| { type: "error"; message: string }

type TagWizardResult = {
expertKey: string
tags: string[]
}

type TagAppProps = {
experts: WizardExpertChoice[]
onFetchVersions: (expertName: string) => Promise<WizardVersionInfo[]>
onComplete: (result: TagWizardResult) => void
onCancel: () => void
}

function ExpertSelector({
experts,
onSelect,
}: {
experts: WizardExpertChoice[]
onSelect: (name: string) => void
}) {
const { exit } = useApp()
const [selectedIndex, setSelectedIndex] = useState(0)
useInput((input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : experts.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < experts.length - 1 ? prev + 1 : 0))
} else if (key.return) {
const expert = experts[selectedIndex]
if (expert) {
onSelect(expert.name)
}
} else if (input === "q" || key.escape) {
exit()
}
})
return (
<Box flexDirection="column">
<Text bold>Select an Expert to tag:</Text>
<Box flexDirection="column" marginTop={1}>
{experts.map((expert, index) => (
<Box key={expert.name}>
<Text color={index === selectedIndex ? "cyan" : undefined}>
{index === selectedIndex ? "❯ " : " "}
{expert.name}
{expert.description && <Text dimColor> - {expert.description}</Text>}
</Text>
</Box>
))}
</Box>
<Box marginTop={1}>
<Text dimColor>↑↓ navigate · enter select · q quit</Text>
</Box>
</Box>
)
}

function VersionSelector({
expertName,
versions,
Expand Down Expand Up @@ -323,7 +277,13 @@ export function TagApp({ experts, onFetchVersions, onComplete, onCancel }: TagAp
}
switch (step.type) {
case "selectExpert":
return <ExpertSelector experts={experts} onSelect={handleExpertSelect} />
return (
<WizardExpertSelector
title="Select an Expert to tag:"
experts={experts}
onSelect={handleExpertSelect}
/>
)
case "loadingVersions":
return (
<Box>
Expand Down
53 changes: 8 additions & 45 deletions packages/tui/apps/unpublish/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box, Text, useApp, useInput } from "ink"
import { useState } from "react"
import { ErrorStep } from "../../src/components/error-step.js"
import { ErrorStep, WizardExpertSelector } from "../../src/components/index.js"
import type { WizardExpertChoice, WizardVersionInfo } from "../../src/types/wizard.js"
import { getStatusColor } from "../../src/utils/index.js"

Expand All @@ -19,49 +19,6 @@ type UnpublishAppProps = {
onComplete: (result: UnpublishWizardResult) => void
onCancel: () => void
}
function ExpertSelector({
experts,
onSelect,
}: {
experts: WizardExpertChoice[]
onSelect: (name: string) => void
}) {
const { exit } = useApp()
const [selectedIndex, setSelectedIndex] = useState(0)
useInput((input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : experts.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < experts.length - 1 ? prev + 1 : 0))
} else if (key.return) {
const expert = experts[selectedIndex]
if (expert) {
onSelect(expert.name)
}
} else if (input === "q" || key.escape) {
exit()
}
})
return (
<Box flexDirection="column">
<Text bold>Select an Expert to unpublish:</Text>
<Box flexDirection="column" marginTop={1}>
{experts.map((expert, index) => (
<Box key={expert.name}>
<Text color={index === selectedIndex ? "cyan" : undefined}>
{index === selectedIndex ? "❯ " : " "}
{expert.name}
{expert.description && <Text dimColor> - {expert.description}</Text>}
</Text>
</Box>
))}
</Box>
<Box marginTop={1}>
<Text dimColor>↑↓ navigate · enter select · q quit</Text>
</Box>
</Box>
)
}
function VersionSelector({
expertName,
versions,
Expand Down Expand Up @@ -222,7 +179,13 @@ export function UnpublishApp({
}
switch (step.type) {
case "selectExpert":
return <ExpertSelector experts={experts} onSelect={handleExpertSelect} />
return (
<WizardExpertSelector
title="Select an Expert to unpublish:"
experts={experts}
onSelect={handleExpertSelect}
/>
)
case "loadingVersions":
return (
<Box>
Expand Down
4 changes: 4 additions & 0 deletions packages/tui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ export { ExpertSelectorBase, type ExpertSelectorBaseProps } from "./expert-selec
export { ListBrowser, type ListBrowserProps } from "./list-browser.js"
export { RunSetting, type RunSettingProps } from "./run-setting.js"
export { Step } from "./step.js"
export {
WizardExpertSelector,
type WizardExpertSelectorProps,
} from "./wizard-expert-selector.js"
46 changes: 46 additions & 0 deletions packages/tui/src/components/wizard-expert-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Box, Text, useApp, useInput } from "ink"
import { useState } from "react"
import type { WizardExpertChoice } from "../types/wizard.js"

export type WizardExpertSelectorProps = {
title: string
experts: WizardExpertChoice[]
onSelect: (name: string) => void
}
export function WizardExpertSelector({ title, experts, onSelect }: WizardExpertSelectorProps) {
const { exit } = useApp()
const [selectedIndex, setSelectedIndex] = useState(0)
useInput((input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : experts.length - 1))
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < experts.length - 1 ? prev + 1 : 0))
} else if (key.return) {
const expert = experts[selectedIndex]
if (expert) {
onSelect(expert.name)
}
} else if (input === "q" || key.escape) {
exit()
}
})
return (
<Box flexDirection="column">
<Text bold>{title}</Text>
<Box flexDirection="column" marginTop={1}>
{experts.map((expert, index) => (
<Box key={expert.name}>
<Text color={index === selectedIndex ? "cyan" : undefined}>
{index === selectedIndex ? "❯ " : " "}
{expert.name}
{expert.description && <Text dimColor> - {expert.description}</Text>}
</Text>
</Box>
))}
</Box>
<Box marginTop={1}>
<Text dimColor>↑↓ navigate · enter select · q quit</Text>
</Box>
</Box>
)
}
Loading