From 39e913265c828a184bc9110a106b8c697919e3de Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Thu, 4 Dec 2025 14:26:23 +0900 Subject: [PATCH 1/2] Refactor: Share VersionSelector across wizards --- packages/tui/apps/status/app.tsx | 53 +---------------- packages/tui/apps/tag/app.tsx | 54 +---------------- packages/tui/apps/unpublish/app.tsx | 54 +---------------- packages/tui/src/components/index.ts | 1 + .../tui/src/components/version-selector.tsx | 59 +++++++++++++++++++ 5 files changed, 64 insertions(+), 157 deletions(-) create mode 100644 packages/tui/src/components/version-selector.tsx diff --git a/packages/tui/apps/status/app.tsx b/packages/tui/apps/status/app.tsx index 206c958a..5d111cee 100644 --- a/packages/tui/apps/status/app.tsx +++ b/packages/tui/apps/status/app.tsx @@ -1,6 +1,6 @@ import { Box, Text, useApp, useInput } from "ink" import { useState } from "react" -import { ErrorStep, WizardExpertSelector } from "../../src/components/index.js" +import { ErrorStep, VersionSelector, WizardExpertSelector } from "../../src/components/index.js" import type { WizardExpertChoice, WizardVersionInfo } from "../../src/types/wizard.js" import { getStatusColor } from "../../src/utils/index.js" @@ -34,57 +34,6 @@ function getAvailableStatusTransitions(currentStatus: string): string[] { } } -function VersionSelector({ - expertName, - versions, - onSelect, - onBack, -}: { - expertName: string - versions: WizardVersionInfo[] - onSelect: (version: WizardVersionInfo) => void - onBack: () => void -}) { - const { exit } = useApp() - const [selectedIndex, setSelectedIndex] = useState(0) - useInput((input, key) => { - if (key.upArrow) { - setSelectedIndex((prev) => (prev > 0 ? prev - 1 : versions.length - 1)) - } else if (key.downArrow) { - setSelectedIndex((prev) => (prev < versions.length - 1 ? prev + 1 : 0)) - } else if (key.return) { - const version = versions[selectedIndex] - if (version) { - onSelect(version) - } - } else if (key.escape || key.backspace || key.delete) { - onBack() - } else if (input === "q") { - exit() - } - }) - return ( - - Select a version of {expertName}: - - {versions.map((v, index) => ( - - - {index === selectedIndex ? "❯ " : " "} - {v.version} - {v.tags.length > 0 && [{v.tags.join(", ")}]} - {v.status} - - - ))} - - - ↑↓ navigate · enter select · esc back · q quit - - - ) -} - function StatusSelector({ expertKey, currentStatus, diff --git a/packages/tui/apps/tag/app.tsx b/packages/tui/apps/tag/app.tsx index 3247c35f..956409e0 100644 --- a/packages/tui/apps/tag/app.tsx +++ b/packages/tui/apps/tag/app.tsx @@ -1,8 +1,7 @@ import { Box, Text, useApp, useInput } from "ink" import { useState } from "react" -import { ErrorStep, WizardExpertSelector } from "../../src/components/index.js" +import { ErrorStep, VersionSelector, WizardExpertSelector } from "../../src/components/index.js" import type { WizardExpertChoice, WizardVersionInfo } from "../../src/types/wizard.js" -import { getStatusColor } from "../../src/utils/index.js" type WizardStep = | { type: "selectExpert" } @@ -22,57 +21,6 @@ type TagAppProps = { onCancel: () => void } -function VersionSelector({ - expertName, - versions, - onSelect, - onBack, -}: { - expertName: string - versions: WizardVersionInfo[] - onSelect: (version: WizardVersionInfo) => void - onBack: () => void -}) { - const { exit } = useApp() - const [selectedIndex, setSelectedIndex] = useState(0) - useInput((input, key) => { - if (key.upArrow) { - setSelectedIndex((prev) => (prev > 0 ? prev - 1 : versions.length - 1)) - } else if (key.downArrow) { - setSelectedIndex((prev) => (prev < versions.length - 1 ? prev + 1 : 0)) - } else if (key.return) { - const version = versions[selectedIndex] - if (version) { - onSelect(version) - } - } else if (key.escape || key.backspace || key.delete) { - onBack() - } else if (input === "q") { - exit() - } - }) - return ( - - Select a version of {expertName}: - - {versions.map((v, index) => ( - - - {index === selectedIndex ? "❯ " : " "} - {v.version} - {v.tags.length > 0 && [{v.tags.join(", ")}]} - {v.status} - - - ))} - - - ↑↓ navigate · enter select · esc back · q quit - - - ) -} - function TagInput({ expertKey, currentTags, diff --git a/packages/tui/apps/unpublish/app.tsx b/packages/tui/apps/unpublish/app.tsx index b654b70a..b650f477 100644 --- a/packages/tui/apps/unpublish/app.tsx +++ b/packages/tui/apps/unpublish/app.tsx @@ -1,8 +1,7 @@ import { Box, Text, useApp, useInput } from "ink" import { useState } from "react" -import { ErrorStep, WizardExpertSelector } from "../../src/components/index.js" +import { ErrorStep, VersionSelector, WizardExpertSelector } from "../../src/components/index.js" import type { WizardExpertChoice, WizardVersionInfo } from "../../src/types/wizard.js" -import { getStatusColor } from "../../src/utils/index.js" type WizardStep = | { type: "selectExpert" } @@ -19,56 +18,6 @@ type UnpublishAppProps = { onComplete: (result: UnpublishWizardResult) => void onCancel: () => void } -function VersionSelector({ - expertName, - versions, - onSelect, - onBack, -}: { - expertName: string - versions: WizardVersionInfo[] - onSelect: (version: WizardVersionInfo) => void - onBack: () => void -}) { - const { exit } = useApp() - const [selectedIndex, setSelectedIndex] = useState(0) - useInput((input, key) => { - if (key.upArrow) { - setSelectedIndex((prev) => (prev > 0 ? prev - 1 : versions.length - 1)) - } else if (key.downArrow) { - setSelectedIndex((prev) => (prev < versions.length - 1 ? prev + 1 : 0)) - } else if (key.return) { - const version = versions[selectedIndex] - if (version) { - onSelect(version) - } - } else if (key.escape || key.backspace || key.delete) { - onBack() - } else if (input === "q") { - exit() - } - }) - return ( - - Select a version of {expertName} to unpublish: - - {versions.map((v, index) => ( - - - {index === selectedIndex ? "❯ " : " "} - {v.version} - {v.tags.length > 0 && [{v.tags.join(", ")}]} - {v.status} - - - ))} - - - ↑↓ navigate · enter select · esc back · q quit - - - ) -} function ConfirmStep({ expertKey, version, @@ -199,6 +148,7 @@ export function UnpublishApp({ versions={step.versions} onSelect={handleVersionSelect} onBack={handleBack} + title={`Select a version of ${step.expertName} to unpublish:`} /> ) case "confirm": diff --git a/packages/tui/src/components/index.ts b/packages/tui/src/components/index.ts index a9ec538b..37101030 100644 --- a/packages/tui/src/components/index.ts +++ b/packages/tui/src/components/index.ts @@ -10,3 +10,4 @@ export { WizardExpertSelector, type WizardExpertSelectorProps, } from "./wizard-expert-selector.js" +export { VersionSelector, type VersionSelectorProps } from "./version-selector.js" diff --git a/packages/tui/src/components/version-selector.tsx b/packages/tui/src/components/version-selector.tsx new file mode 100644 index 00000000..1e9154b2 --- /dev/null +++ b/packages/tui/src/components/version-selector.tsx @@ -0,0 +1,59 @@ +import { Box, Text, useApp, useInput } from "ink" +import { useState } from "react" +import type { WizardVersionInfo } from "../types/wizard.js" +import { getStatusColor } from "../utils/status-color.js" + +export type VersionSelectorProps = { + expertName: string + versions: WizardVersionInfo[] + onSelect: (version: WizardVersionInfo) => void + onBack: () => void + title?: string +} + +export function VersionSelector({ + expertName, + versions, + onSelect, + onBack, + title, +}: VersionSelectorProps) { + const { exit } = useApp() + const [selectedIndex, setSelectedIndex] = useState(0) + useInput((input, key) => { + if (key.upArrow) { + setSelectedIndex((prev) => (prev > 0 ? prev - 1 : versions.length - 1)) + } else if (key.downArrow) { + setSelectedIndex((prev) => (prev < versions.length - 1 ? prev + 1 : 0)) + } else if (key.return) { + const version = versions[selectedIndex] + if (version) { + onSelect(version) + } + } else if (key.escape || key.backspace || key.delete) { + onBack() + } else if (input === "q") { + exit() + } + }) + return ( + + {title ?? `Select a version of ${expertName}:`} + + {versions.map((v, index) => ( + + + {index === selectedIndex ? "❯ " : " "} + {v.version} + {v.tags.length > 0 && [{v.tags.join(", ")}]} + {v.status} + + + ))} + + + ↑↓ navigate · enter select · esc back · q quit + + + ) +} From 7792b9f5437f4a4a6c4eb401c722dd02acd3191c Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Thu, 4 Dec 2025 14:27:41 +0900 Subject: [PATCH 2/2] Chore: Add changeset --- .changeset/share-version-selector.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/share-version-selector.md diff --git a/.changeset/share-version-selector.md b/.changeset/share-version-selector.md new file mode 100644 index 00000000..80695ec8 --- /dev/null +++ b/.changeset/share-version-selector.md @@ -0,0 +1,5 @@ +--- +"@perstack/tui": patch +--- + +Share VersionSelector component across wizard apps