diff --git a/packages/tui/src/feature-plugins/system/storybook/session-location-missing.tsx b/packages/tui/src/feature-plugins/system/storybook/session-location-missing.tsx index ac87602bfa7..75ef4cb9ddf 100644 --- a/packages/tui/src/feature-plugins/system/storybook/session-location-missing.tsx +++ b/packages/tui/src/feature-plugins/system/storybook/session-location-missing.tsx @@ -1,18 +1,17 @@ import type { Plugin } from "@opencode-ai/plugin/tui" import { useTerminalDimensions } from "@opentui/solid" import { TextAttributes } from "@opentui/core" -import { createSignal, For, Show } from "solid-js" +import { createSignal } from "solid-js" +import { SessionLocationMissing } from "../../../routes/session/location-missing" import type { Story } from "./index" import { StoryFooter } from "./footer" -const missing = "~/code/open-source/opencode-workerd-profile" -const destinations = ["~/code/open-source/opencode", "~/code/open-source/opencode-instruction-rename"] +const directory = "/Users/kit/code/open-source/opencode-workerd-profile" function SessionLocationMissingStory(props: { context: Plugin.Context }) { const dimensions = useTerminalDimensions() const theme = props.context.theme.contextual.elevated - const [state, setState] = createSignal<"missing" | "move" | "recovered">("missing") - const [selected, setSelected] = createSignal(0) + const [message, setMessage] = createSignal("Select a recovery action") props.context.keymap.layer(() => ({ commands: [ @@ -22,43 +21,15 @@ function SessionLocationMissingStory(props: { context: Plugin.Context }) { group: "Storybook", run: () => props.context.ui.router.navigate({ type: "plugin", name: "storybook" }), }, - { - bind: "return", - title: "Continue", - group: "Storybook", - run: () => { - if (state() === "missing") return setState("move") - if (state() === "move") return setState("recovered") - }, - }, - { - bind: "up,k", - title: "Previous destination", - group: "Storybook", - run: () => state() === "move" && setSelected((current) => (current + destinations.length - 1) % destinations.length), - }, - { - bind: "down,j", - title: "Next destination", - group: "Storybook", - run: () => state() === "move" && setSelected((current) => (current + 1) % destinations.length), - }, - { - bind: "r", - title: "Reset story", - group: "Storybook", - run: () => { - setSelected(0) - setState("missing") - }, - }, ], })) return ( - Workerd Modal workspace driver + + Workerd Modal workspace driver + build · GPT-5.6 Sol (high) You @@ -67,47 +38,15 @@ function SessionLocationMissingStory(props: { context: Plugin.Context }) { Build · GPT-5.6 Sol (high) The deployment is verified and the worktree is clean. - - - - Session directory no longer exists - - {missing} - Move this session to continue. - [ Move session ] [ Copy path ] - - - - - Move session - Choose a project directory - - {(directory, index) => ( - - {index() === selected() ? "› " : " "}{directory} - - )} - - - - - - Session moved - {destinations[selected()]} - - Ask anything... - - - + setMessage("Move session requested")} /> diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 154030ce9e7..fa986756902 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -109,7 +109,7 @@ import type { SessionInbox } from "@opencode-ai/schema/session-inbox" import { generateThinkingSyntax } from "./thinking-syntax" import { createDelayedPresence } from "../../util/delayed-presence" import { usePromptMove } from "../../component/prompt/move" -import { abbreviateHome } from "../../util/path-format" +import { SessionLocationMissing } from "./location-missing" addDefaultParsers(parsers.parsers) @@ -1169,52 +1169,6 @@ export function Session() { ) } -function SessionLocationMissing(props: { directory: string; onMove: () => void }) { - const theme = useTheme() - const paths = useTuiPaths() - const clipboard = useClipboard() - const toast = useToast() - const directory = createMemo(() => Locale.truncateMiddle(abbreviateHome(props.directory, paths.home), 72)) - - return ( - - - Session directory no longer exists - - {directory()} - Move this session to continue. - - - Move session - - { - void clipboard.write(props.directory) - toast.show({ message: "Copied session directory", variant: "success" }) - }} - > - Copy path - - - - ) -} - type SessionRowViewProps = { row: SessionRow message: (messageID: string) => SessionMessageInfo | undefined diff --git a/packages/tui/src/routes/session/location-missing.tsx b/packages/tui/src/routes/session/location-missing.tsx new file mode 100644 index 00000000000..15b3811bc1c --- /dev/null +++ b/packages/tui/src/routes/session/location-missing.tsx @@ -0,0 +1,38 @@ +import { createMemo } from "solid-js" +import { useClipboard } from "../../context/clipboard" +import { useTuiPaths } from "../../context/runtime" +import { useTheme } from "../../context/theme" +import { useToast } from "../../ui/toast" +import { Locale } from "../../util/locale" +import { abbreviateHome } from "../../util/path-format" +import { SessionQuestion } from "./permission" + +export function SessionLocationMissing(props: { directory: string; onMove: () => void }) { + const paths = useTuiPaths() + const clipboard = useClipboard() + const toast = useToast() + const theme = useTheme("elevated") + const directory = createMemo(() => Locale.truncateMiddle(abbreviateHome(props.directory, paths.home), 72)) + + return ( + + {directory()} + Move this session to continue. + + } + options={{ move: "Move session", copy: "Copy path" }} + onSelect={(option) => { + if (option === "move") return props.onMove() + void clipboard.write(props.directory) + toast.show({ message: "Copied session directory", variant: "success" }) + }} + /> + ) +} diff --git a/packages/tui/src/routes/session/permission.tsx b/packages/tui/src/routes/session/permission.tsx index 149a1c7753e..26253e870f5 100644 --- a/packages/tui/src/routes/session/permission.tsx +++ b/packages/tui/src/routes/session/permission.tsx @@ -141,7 +141,7 @@ export function PermissionPrompt(props: { request: PermissionRequest; directory? return ( - >(props: { +export function SessionQuestion>(props: { title: string semanticLabel?: string instance: string + id?: string + group?: string + choicesLabel?: string header?: JSX.Element body: JSX.Element options: T @@ -431,6 +434,8 @@ function Prompt>(props: { }) const narrow = createMemo(() => dimensions().width < 80) const shortcuts = Keymap.useShortcuts() + const id = () => props.id ?? "session.permission" + const group = () => props.group ?? "Permission" Keymap.createLayer(() => ({ mode: "base", @@ -438,7 +443,7 @@ function Prompt>(props: { { id: "app.exit", title: "Reject permission", - group: "Permission", + group: group(), bind: false, run() { if (!props.escapeKey) return @@ -448,7 +453,7 @@ function Prompt>(props: { { id: "permission.prompt.fullscreen", title: "Toggle permission fullscreen", - group: "Permission", + group: group(), bind: false, run() { if (!props.fullscreen) return @@ -457,8 +462,8 @@ function Prompt>(props: { }, { bind: "left", - title: "Previous permission option", - group: "Permission", + title: "Previous option", + group: group(), run: () => { const idx = keys.indexOf(store.selected) const next = keys[(idx - 1 + keys.length) % keys.length] @@ -467,8 +472,8 @@ function Prompt>(props: { }, { bind: "h", - title: "Previous permission option", - group: "Permission", + title: "Previous option", + group: group(), run: () => { const idx = keys.indexOf(store.selected) const next = keys[(idx - 1 + keys.length) % keys.length] @@ -477,8 +482,8 @@ function Prompt>(props: { }, { bind: "right", - title: "Next permission option", - group: "Permission", + title: "Next option", + group: group(), run: () => { const idx = keys.indexOf(store.selected) const next = keys[(idx + 1) % keys.length] @@ -487,8 +492,8 @@ function Prompt>(props: { }, { bind: "l", - title: "Next permission option", - group: "Permission", + title: "Next option", + group: group(), run: () => { const idx = keys.indexOf(store.selected) const next = keys[(idx + 1) % keys.length] @@ -497,7 +502,7 @@ function Prompt>(props: { }, { bind: "return", - title: "Select permission option", + title: "Select option", group: "Permission", run: () => props.onSelect(store.selected), }, @@ -506,7 +511,7 @@ function Prompt>(props: { { bind: "escape", title: "Reject permission", - group: "Permission", + group: group(), run: () => props.onSelect(props.escapeKey!), }, ] @@ -520,7 +525,7 @@ function Prompt>(props: { const content = () => ( ({ instance: props.instance, role: "dialog", @@ -571,11 +576,11 @@ function Prompt>(props: { alignItems={narrow() ? "flex-start" : "center"} > ({ instance: props.instance, role: "listbox", - label: "Permission choices", + label: props.choicesLabel ?? "Permission choices", }))} flexDirection="row" gap={1} @@ -584,7 +589,7 @@ function Prompt>(props: { {(option) => ( ({ instance: props.instance, role: "option",