diff options
| author | Adam <[email protected]> | 2026-02-12 09:49:14 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-12 09:49:14 -0600 |
| commit | ff4414bb152acfddb5c0eb073c38bedc1df4ae14 (patch) | |
| tree | 78381c67d21ef6f089647f6b19e7aa2976840dbc /packages/app/src/pages/session | |
| parent | 56ad2db02055955f926fda0e4a89055b22ead6f9 (diff) | |
| download | opencode-ff4414bb152acfddb5c0eb073c38bedc1df4ae14.tar.gz opencode-ff4414bb152acfddb5c0eb073c38bedc1df4ae14.zip | |
chore: refactor packages/app files (#13236)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Frank <[email protected]>
Diffstat (limited to 'packages/app/src/pages/session')
| -rw-r--r-- | packages/app/src/pages/session/file-tabs.tsx | 94 | ||||
| -rw-r--r-- | packages/app/src/pages/session/message-timeline.tsx | 78 | ||||
| -rw-r--r-- | packages/app/src/pages/session/review-tab.tsx | 14 | ||||
| -rw-r--r-- | packages/app/src/pages/session/session-mobile-tabs.tsx | 14 | ||||
| -rw-r--r-- | packages/app/src/pages/session/session-prompt-dock.tsx | 9 | ||||
| -rw-r--r-- | packages/app/src/pages/session/session-side-panel.tsx | 24 | ||||
| -rw-r--r-- | packages/app/src/pages/session/terminal-panel.tsx | 5 | ||||
| -rw-r--r-- | packages/app/src/pages/session/use-session-commands.tsx | 152 |
8 files changed, 176 insertions, 214 deletions
diff --git a/packages/app/src/pages/session/file-tabs.tsx b/packages/app/src/pages/session/file-tabs.tsx index 0c8281a66..c94c0ff35 100644 --- a/packages/app/src/pages/session/file-tabs.tsx +++ b/packages/app/src/pages/session/file-tabs.tsx @@ -12,6 +12,13 @@ import { useFile, type SelectedLineRange } from "@/context/file" import { useComments } from "@/context/comments" import { useLanguage } from "@/context/language" +const formatCommentLabel = (range: SelectedLineRange) => { + const start = Math.min(range.start, range.end) + const end = Math.max(range.start, range.end) + if (start === end) return `line ${start}` + return `lines ${start}-${end}` +} + export function FileTabContent(props: { tab: string activeTab: () => string @@ -76,7 +83,6 @@ export function FileTabContent(props: { showToast({ variant: "error", title: props.language.t("toast.file.loadFailed.title"), - description: "Invalid base64 content.", }) }) const svgPreviewUrl = createMemo(() => { @@ -116,34 +122,6 @@ export function FileTabContent(props: { draftTop: undefined as number | undefined, }) - const openedComment = () => note.openedComment - const setOpenedComment = ( - value: typeof note.openedComment | ((value: typeof note.openedComment) => typeof note.openedComment), - ) => setNote("openedComment", value) - - const commenting = () => note.commenting - const setCommenting = (value: typeof note.commenting | ((value: typeof note.commenting) => typeof note.commenting)) => - setNote("commenting", value) - - const draft = () => note.draft - const setDraft = (value: typeof note.draft | ((value: typeof note.draft) => typeof note.draft)) => - setNote("draft", value) - - const positions = () => note.positions - const setPositions = (value: typeof note.positions | ((value: typeof note.positions) => typeof note.positions)) => - setNote("positions", value) - - const draftTop = () => note.draftTop - const setDraftTop = (value: typeof note.draftTop | ((value: typeof note.draftTop) => typeof note.draftTop)) => - setNote("draftTop", value) - - const commentLabel = (range: SelectedLineRange) => { - const start = Math.min(range.start, range.end) - const end = Math.max(range.start, range.end) - if (start === end) return `line ${start}` - return `lines ${start}-${end}` - } - const getRoot = () => { const el = wrap if (!el) return @@ -174,8 +152,8 @@ export function FileTabContent(props: { const el = wrap const root = getRoot() if (!el || !root) { - setPositions({}) - setDraftTop(undefined) + setNote("positions", {}) + setNote("draftTop", undefined) return } @@ -186,21 +164,21 @@ export function FileTabContent(props: { next[comment.id] = markerTop(el, marker) } - setPositions(next) + setNote("positions", next) - const range = commenting() + const range = note.commenting if (!range) { - setDraftTop(undefined) + setNote("draftTop", undefined) return } const marker = findMarker(root, range) if (!marker) { - setDraftTop(undefined) + setNote("draftTop", undefined) return } - setDraftTop(markerTop(el, marker)) + setNote("draftTop", markerTop(el, marker)) } const scheduleComments = () => { @@ -213,10 +191,10 @@ export function FileTabContent(props: { }) createEffect(() => { - const range = commenting() + const range = note.commenting scheduleComments() if (!range) return - setDraft("") + setNote("draft", "") }) createEffect(() => { @@ -229,8 +207,8 @@ export function FileTabContent(props: { const target = fileComments().find((comment) => comment.id === focus.id) if (!target) return - setOpenedComment(target.id) - setCommenting(null) + setNote("openedComment", target.id) + setNote("commenting", null) props.file.setSelectedLines(p, target.selection) requestAnimationFrame(() => props.comments.clearFocus()) }) @@ -390,16 +368,16 @@ export function FileTabContent(props: { const p = path() if (!p) return props.file.setSelectedLines(p, range) - if (!range) setCommenting(null) + if (!range) setNote("commenting", null) }} onLineSelectionEnd={(range: SelectedLineRange | null) => { if (!range) { - setCommenting(null) + setNote("commenting", null) return } - setOpenedComment(null) - setCommenting(range) + setNote("openedComment", null) + setNote("commenting", range) }} overflow="scroll" class="select-text" @@ -408,10 +386,10 @@ export function FileTabContent(props: { {(comment) => ( <LineCommentView id={comment.id} - top={positions()[comment.id]} - open={openedComment() === comment.id} + top={note.positions[comment.id]} + open={note.openedComment === comment.id} comment={comment.comment} - selection={commentLabel(comment.selection)} + selection={formatCommentLabel(comment.selection)} onMouseEnter={() => { const p = path() if (!p) return @@ -420,22 +398,22 @@ export function FileTabContent(props: { onClick={() => { const p = path() if (!p) return - setCommenting(null) - setOpenedComment((current) => (current === comment.id ? null : comment.id)) + setNote("commenting", null) + setNote("openedComment", (current) => (current === comment.id ? null : comment.id)) props.file.setSelectedLines(p, comment.selection) }} /> )} </For> - <Show when={commenting()}> + <Show when={note.commenting}> {(range) => ( - <Show when={draftTop() !== undefined}> + <Show when={note.draftTop !== undefined}> <LineCommentEditor - top={draftTop()} - value={draft()} - selection={commentLabel(range())} - onInput={(value) => setDraft(value)} - onCancel={() => setCommenting(null)} + top={note.draftTop} + value={note.draft} + selection={formatCommentLabel(range())} + onInput={(value) => setNote("draft", value)} + onCancel={() => setNote("commenting", null)} onSubmit={(value) => { const p = path() if (!p) return @@ -445,7 +423,7 @@ export function FileTabContent(props: { comment: value, origin: "file", }) - setCommenting(null) + setNote("commenting", null) }} onPopoverFocusOut={(e: FocusEvent) => { const current = e.currentTarget as HTMLDivElement @@ -454,7 +432,7 @@ export function FileTabContent(props: { setTimeout(() => { if (!document.activeElement || !current.contains(document.activeElement)) { - setCommenting(null) + setNote("commenting", null) } }, 0) }} diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index a4ca06dd5..d5f04ccf9 100644 --- a/packages/app/src/pages/session/message-timeline.tsx +++ b/packages/app/src/pages/session/message-timeline.tsx @@ -9,6 +9,37 @@ import { SessionTurn } from "@opencode-ai/ui/session-turn" import type { UserMessage } from "@opencode-ai/sdk/v2" import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/message-gesture" +const boundaryTarget = (root: HTMLElement, target: EventTarget | null) => { + const current = target instanceof Element ? target : undefined + const nested = current?.closest("[data-scrollable]") + if (!nested || nested === root) return root + if (!(nested instanceof HTMLElement)) return root + return nested +} + +const markBoundaryGesture = (input: { + root: HTMLDivElement + target: EventTarget | null + delta: number + onMarkScrollGesture: (target?: EventTarget | null) => void +}) => { + const target = boundaryTarget(input.root, input.target) + if (target === input.root) { + input.onMarkScrollGesture(input.root) + return + } + if ( + shouldMarkBoundaryGesture({ + delta: input.delta, + scrollTop: target.scrollTop, + scrollHeight: target.scrollHeight, + clientHeight: target.clientHeight, + }) + ) { + input.onMarkScrollGesture(input.root) + } +} + export function MessageTimeline(props: { mobileChanges: boolean mobileFallback: JSX.Element @@ -86,35 +117,13 @@ export function MessageTimeline(props: { ref={props.setScrollRef} onWheel={(e) => { const root = e.currentTarget - const target = e.target instanceof Element ? e.target : undefined - const nested = target?.closest("[data-scrollable]") - if (!nested || nested === root) { - props.onMarkScrollGesture(root) - return - } - - if (!(nested instanceof HTMLElement)) { - props.onMarkScrollGesture(root) - return - } - const delta = normalizeWheelDelta({ deltaY: e.deltaY, deltaMode: e.deltaMode, rootHeight: root.clientHeight, }) if (!delta) return - - if ( - shouldMarkBoundaryGesture({ - delta, - scrollTop: nested.scrollTop, - scrollHeight: nested.scrollHeight, - clientHeight: nested.clientHeight, - }) - ) { - props.onMarkScrollGesture(root) - } + markBoundaryGesture({ root, target: e.target, delta, onMarkScrollGesture: props.onMarkScrollGesture }) }} onTouchStart={(e) => { touchGesture = e.touches[0]?.clientY @@ -129,28 +138,7 @@ export function MessageTimeline(props: { if (!delta) return const root = e.currentTarget - const target = e.target instanceof Element ? e.target : undefined - const nested = target?.closest("[data-scrollable]") - if (!nested || nested === root) { - props.onMarkScrollGesture(root) - return - } - - if (!(nested instanceof HTMLElement)) { - props.onMarkScrollGesture(root) - return - } - - if ( - shouldMarkBoundaryGesture({ - delta, - scrollTop: nested.scrollTop, - scrollHeight: nested.scrollHeight, - clientHeight: nested.clientHeight, - }) - ) { - props.onMarkScrollGesture(root) - } + markBoundaryGesture({ root, target: e.target, delta, onMarkScrollGesture: props.onMarkScrollGesture }) }} onTouchEnd={() => { touchGesture = undefined diff --git a/packages/app/src/pages/session/review-tab.tsx b/packages/app/src/pages/session/review-tab.tsx index 72518c68e..634491c72 100644 --- a/packages/app/src/pages/session/review-tab.tsx +++ b/packages/app/src/pages/session/review-tab.tsx @@ -1,4 +1,5 @@ -import { createEffect, on, onCleanup, createSignal, type JSX } from "solid-js" +import { createEffect, on, onCleanup, type JSX } from "solid-js" +import { createStore } from "solid-js/store" import type { FileDiff } from "@opencode-ai/sdk/v2" import { SessionReview } from "@opencode-ai/ui/session-review" import type { SelectedLineRange } from "@/context/file" @@ -30,7 +31,7 @@ export interface SessionReviewTabProps { } export function StickyAddButton(props: { children: JSX.Element }) { - const [stuck, setStuck] = createSignal(false) + const [state, setState] = createStore({ stuck: false }) let button: HTMLDivElement | undefined createEffect(() => { @@ -43,7 +44,7 @@ export function StickyAddButton(props: { children: JSX.Element }) { const handler = () => { const rect = node.getBoundingClientRect() const scrollRect = scroll.getBoundingClientRect() - setStuck(rect.right >= scrollRect.right && scroll.scrollWidth > scroll.clientWidth) + setState("stuck", rect.right >= scrollRect.right && scroll.scrollWidth > scroll.clientWidth) } scroll.addEventListener("scroll", handler, { passive: true }) @@ -60,7 +61,7 @@ export function StickyAddButton(props: { children: JSX.Element }) { <div ref={button} class="bg-background-base h-full shrink-0 sticky right-0 z-10 flex items-center justify-center border-b border-border-weak-base px-3" - classList={{ "border-l": stuck() }} + classList={{ "border-l": state.stuck }} > {props.children} </div> @@ -78,7 +79,10 @@ export function SessionReviewTab(props: SessionReviewTabProps) { return sdk.client.file .read({ path }) .then((x) => x.data) - .catch(() => undefined) + .catch((error) => { + console.debug("[session-review] failed to read file", { path, error }) + return undefined + }) } const restoreScroll = () => { diff --git a/packages/app/src/pages/session/session-mobile-tabs.tsx b/packages/app/src/pages/session/session-mobile-tabs.tsx index 41f058231..6afe8024a 100644 --- a/packages/app/src/pages/session/session-mobile-tabs.tsx +++ b/packages/app/src/pages/session/session-mobile-tabs.tsx @@ -1,8 +1,9 @@ -import { Match, Show, Switch } from "solid-js" +import { Show } from "solid-js" import { Tabs } from "@opencode-ai/ui/tabs" export function SessionMobileTabs(props: { open: boolean + mobileTab: "session" | "changes" hasReview: boolean reviewCount: number onSession: () => void @@ -11,7 +12,7 @@ export function SessionMobileTabs(props: { }) { return ( <Show when={props.open}> - <Tabs class="h-auto"> + <Tabs value={props.mobileTab} class="h-auto"> <Tabs.List> <Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }} onClick={props.onSession}> {props.t("session.tab.session")} @@ -22,12 +23,9 @@ export function SessionMobileTabs(props: { classes={{ button: "w-full" }} onClick={props.onChanges} > - <Switch> - <Match when={props.hasReview}> - {props.t("session.review.filesChanged", { count: props.reviewCount })} - </Match> - <Match when={true}>{props.t("session.review.change.other")}</Match> - </Switch> + {props.hasReview + ? props.t("session.review.filesChanged", { count: props.reviewCount }) + : props.t("session.review.change.other")} </Tabs.Trigger> </Tabs.List> </Tabs> diff --git a/packages/app/src/pages/session/session-prompt-dock.tsx b/packages/app/src/pages/session/session-prompt-dock.tsx index eaf0564b2..8ec4f3b9f 100644 --- a/packages/app/src/pages/session/session-prompt-dock.tsx +++ b/packages/app/src/pages/session/session-prompt-dock.tsx @@ -1,15 +1,14 @@ -import { For, Show, type ComponentProps } from "solid-js" +import { For, Show } from "solid-js" +import type { QuestionRequest } from "@opencode-ai/sdk/v2" import { Button } from "@opencode-ai/ui/button" import { BasicTool } from "@opencode-ai/ui/basic-tool" import { PromptInput } from "@/components/prompt-input" import { QuestionDock } from "@/components/question-dock" import { questionSubtitle } from "@/pages/session/session-prompt-helpers" -const questionDockRequest = (value: unknown) => value as ComponentProps<typeof QuestionDock>["request"] - export function SessionPromptDock(props: { centered: boolean - questionRequest: () => { questions: unknown[] } | undefined + questionRequest: () => QuestionRequest | undefined permissionRequest: () => { patterns: string[]; permission: string } | undefined blocked: boolean promptReady: boolean @@ -48,7 +47,7 @@ export function SessionPromptDock(props: { subtitle, }} /> - <QuestionDock request={questionDockRequest(req)} /> + <QuestionDock request={req} /> </div> ) }} diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index d9460cc1a..15ad90ffe 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -21,6 +21,14 @@ import { useFile, type SelectedLineRange } from "@/context/file" import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" import { useSync } from "@/context/sync" +import type { Message, UserMessage } from "@opencode-ai/sdk/v2/client" + +type SessionSidePanelViewModel = { + messages: () => Message[] + visibleUserMessages: () => UserMessage[] + view: () => ReturnType<ReturnType<typeof useLayout>["view"]> + info: () => ReturnType<ReturnType<typeof useSync>["session"]["get"]> +} export function SessionSidePanel(props: { open: boolean @@ -31,7 +39,6 @@ export function SessionSidePanel(props: { dialog: ReturnType<typeof useDialog> file: ReturnType<typeof useFile> comments: ReturnType<typeof useComments> - sync: ReturnType<typeof useSync> hasReview: boolean reviewCount: number reviewTab: boolean @@ -43,10 +50,7 @@ export function SessionSidePanel(props: { openTab: (value: string) => void showAllFiles: () => void reviewPanel: () => JSX.Element - messages: () => unknown[] - visibleUserMessages: () => unknown[] - view: () => ReturnType<ReturnType<typeof useLayout>["view"]> - info: () => unknown + vm: SessionSidePanelViewModel handoffFiles: () => Record<string, SelectedLineRange | null> | undefined codeComponent: NonNullable<ValidComponent> addCommentToContext: (input: { @@ -187,10 +191,10 @@ export function SessionSidePanel(props: { <Show when={props.activeTab() === "context"}> <div class="relative pt-2 flex-1 min-h-0 overflow-hidden"> <SessionContextTab - messages={props.messages as never} - visibleUserMessages={props.visibleUserMessages as never} - view={props.view as never} - info={props.info as never} + messages={props.vm.messages} + visibleUserMessages={props.vm.visibleUserMessages} + view={props.vm.view} + info={props.vm.info} /> </div> </Show> @@ -203,7 +207,7 @@ export function SessionSidePanel(props: { tab={tab} activeTab={props.activeTab} tabs={props.tabs} - view={props.view} + view={props.vm.view} handoffFiles={props.handoffFiles} file={props.file} comments={props.comments} diff --git a/packages/app/src/pages/session/terminal-panel.tsx b/packages/app/src/pages/session/terminal-panel.tsx index 2e65fde0e..d3475c714 100644 --- a/packages/app/src/pages/session/terminal-panel.tsx +++ b/packages/app/src/pages/session/terminal-panel.tsx @@ -1,4 +1,4 @@ -import { createMemo, For, Show } from "solid-js" +import { For, Show } from "solid-js" import { Tabs } from "@opencode-ai/ui/tabs" import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { IconButton } from "@opencode-ai/ui/icon-button" @@ -141,9 +141,8 @@ export function TerminalPanel(props: { <DragOverlay> <Show when={props.activeTerminalDraggable()}> {(draggedId) => { - const pty = createMemo(() => props.terminal.all().find((t: LocalPTY) => t.id === draggedId())) return ( - <Show when={pty()}> + <Show when={props.terminal.all().find((t: LocalPTY) => t.id === draggedId())}> {(t) => ( <div class="relative p-1 h-10 flex items-center bg-background-stronger text-14-regular"> {terminalTabLabel({ diff --git a/packages/app/src/pages/session/use-session-commands.tsx b/packages/app/src/pages/session/use-session-commands.tsx index d52022d73..81c71133f 100644 --- a/packages/app/src/pages/session/use-session-commands.tsx +++ b/packages/app/src/pages/session/use-session-commands.tsx @@ -1,8 +1,8 @@ import { createMemo } from "solid-js" import { useNavigate, useParams } from "@solidjs/router" -import { useCommand } from "@/context/command" +import { useCommand, type CommandOption } from "@/context/command" import { useDialog } from "@opencode-ai/ui/context/dialog" -import { useFile, selectionFromLines, type FileSelection } from "@/context/file" +import { useFile, selectionFromLines, type FileSelection, type SelectedLineRange } from "@/context/file" import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" import { useLocal } from "@/context/local" @@ -22,7 +22,7 @@ import { UserMessage } from "@opencode-ai/sdk/v2" import { combineCommandSections } from "@/pages/session/helpers" import { canAddSelectionContext } from "@/pages/session/session-command-helpers" -export const useSessionCommands = (input: { +export type SessionCommandContext = { command: ReturnType<typeof useCommand> dialog: ReturnType<typeof useDialog> file: ReturnType<typeof useFile> @@ -49,32 +49,48 @@ export const useSessionCommands = (input: { setActiveMessage: (message: UserMessage | undefined) => void addSelectionToContext: (path: string, selection: FileSelection) => void focusInput: () => void -}) => { +} + +const withCategory = (category: string) => { + return (option: Omit<CommandOption, "category">): CommandOption => ({ + ...option, + category, + }) +} + +export const useSessionCommands = (input: SessionCommandContext) => { + const sessionCommand = withCategory(input.language.t("command.category.session")) + const fileCommand = withCategory(input.language.t("command.category.file")) + const contextCommand = withCategory(input.language.t("command.category.context")) + const viewCommand = withCategory(input.language.t("command.category.view")) + const terminalCommand = withCategory(input.language.t("command.category.terminal")) + const modelCommand = withCategory(input.language.t("command.category.model")) + const mcpCommand = withCategory(input.language.t("command.category.mcp")) + const agentCommand = withCategory(input.language.t("command.category.agent")) + const permissionsCommand = withCategory(input.language.t("command.category.permissions")) + const sessionCommands = createMemo(() => [ - { + sessionCommand({ id: "session.new", title: input.language.t("command.session.new"), - category: input.language.t("command.category.session"), keybind: "mod+shift+s", slash: "new", onSelect: () => input.navigate(`/${input.params.dir}/session`), - }, + }), ]) const fileCommands = createMemo(() => [ - { + fileCommand({ id: "file.open", title: input.language.t("command.file.open"), description: input.language.t("palette.search.placeholder"), - category: input.language.t("command.category.file"), keybind: "mod+p", slash: "open", onSelect: () => input.dialog.show(() => <DialogSelectFile onOpenFile={input.showAllFiles} />), - }, - { + }), + fileCommand({ id: "tab.close", title: input.language.t("command.tab.close"), - category: input.language.t("command.category.file"), keybind: "mod+w", disabled: !input.tabs().active(), onSelect: () => { @@ -82,15 +98,14 @@ export const useSessionCommands = (input: { if (!active) return input.tabs().close(active) }, - }, + }), ]) const contextCommands = createMemo(() => [ - { + contextCommand({ id: "context.addSelection", title: input.language.t("command.context.addSelection"), description: input.language.t("command.context.addSelection.description"), - category: input.language.t("command.category.context"), keybind: "mod+shift+l", disabled: !canAddSelectionContext({ active: input.tabs().active(), @@ -103,7 +118,7 @@ export const useSessionCommands = (input: { const path = input.file.pathFromTab(active) if (!path) return - const range = input.file.selectedLines(path) + const range = input.file.selectedLines(path) as SelectedLineRange | null | undefined if (!range) { showToast({ title: input.language.t("toast.context.noLineSelection.title"), @@ -114,58 +129,49 @@ export const useSessionCommands = (input: { input.addSelectionToContext(path, selectionFromLines(range)) }, - }, + }), ]) const viewCommands = createMemo(() => [ - { + viewCommand({ id: "terminal.toggle", title: input.language.t("command.terminal.toggle"), - description: "", - category: input.language.t("command.category.view"), keybind: "ctrl+`", slash: "terminal", onSelect: () => input.view().terminal.toggle(), - }, - { + }), + viewCommand({ id: "review.toggle", title: input.language.t("command.review.toggle"), - description: "", - category: input.language.t("command.category.view"), keybind: "mod+shift+r", onSelect: () => input.view().reviewPanel.toggle(), - }, - { + }), + viewCommand({ id: "fileTree.toggle", title: input.language.t("command.fileTree.toggle"), - description: "", - category: input.language.t("command.category.view"), keybind: "mod+\\", onSelect: () => input.layout.fileTree.toggle(), - }, - { + }), + viewCommand({ id: "input.focus", title: input.language.t("command.input.focus"), - category: input.language.t("command.category.view"), keybind: "ctrl+l", onSelect: () => input.focusInput(), - }, - { + }), + terminalCommand({ id: "terminal.new", title: input.language.t("command.terminal.new"), description: input.language.t("command.terminal.new.description"), - category: input.language.t("command.category.terminal"), keybind: "ctrl+alt+t", onSelect: () => { if (input.terminal.all().length > 0) input.terminal.new() input.view().terminal.open() }, - }, - { + }), + viewCommand({ id: "steps.toggle", title: input.language.t("command.steps.toggle"), description: input.language.t("command.steps.toggle.description"), - category: input.language.t("command.category.view"), keybind: "mod+e", slash: "steps", disabled: !input.params.id, @@ -174,86 +180,78 @@ export const useSessionCommands = (input: { if (!msg) return input.setExpanded(msg.id, (open: boolean | undefined) => !open) }, - }, + }), ]) const messageCommands = createMemo(() => [ - { + sessionCommand({ id: "message.previous", title: input.language.t("command.message.previous"), description: input.language.t("command.message.previous.description"), - category: input.language.t("command.category.session"), keybind: "mod+arrowup", disabled: !input.params.id, onSelect: () => input.navigateMessageByOffset(-1), - }, - { + }), + sessionCommand({ id: "message.next", title: input.language.t("command.message.next"), description: input.language.t("command.message.next.description"), - category: input.language.t("command.category.session"), keybind: "mod+arrowdown", disabled: !input.params.id, onSelect: () => input.navigateMessageByOffset(1), - }, + }), ]) const agentCommands = createMemo(() => [ - { + modelCommand({ id: "model.choose", title: input.language.t("command.model.choose"), description: input.language.t("command.model.choose.description"), - category: input.language.t("command.category.model"), keybind: "mod+'", slash: "model", onSelect: () => input.dialog.show(() => <DialogSelectModel />), - }, - { + }), + mcpCommand({ id: "mcp.toggle", title: input.language.t("command.mcp.toggle"), description: input.language.t("command.mcp.toggle.description"), - category: input.language.t("command.category.mcp"), keybind: "mod+;", slash: "mcp", onSelect: () => input.dialog.show(() => <DialogSelectMcp />), - }, - { + }), + agentCommand({ id: "agent.cycle", title: input.language.t("command.agent.cycle"), description: input.language.t("command.agent.cycle.description"), - category: input.language.t("command.category.agent"), keybind: "mod+.", slash: "agent", onSelect: () => input.local.agent.move(1), - }, - { + }), + agentCommand({ id: "agent.cycle.reverse", title: input.language.t("command.agent.cycle.reverse"), description: input.language.t("command.agent.cycle.reverse.description"), - category: input.language.t("command.category.agent"), keybind: "shift+mod+.", onSelect: () => input.local.agent.move(-1), - }, - { + }), + modelCommand({ id: "model.variant.cycle", title: input.language.t("command.model.variant.cycle"), description: input.language.t("command.model.variant.cycle.description"), - category: input.language.t("command.category.model"), keybind: "shift+mod+d", onSelect: () => { input.local.model.variant.cycle() }, - }, + }), ]) const permissionCommands = createMemo(() => [ - { + permissionsCommand({ id: "permissions.autoaccept", title: input.params.id && input.permission.isAutoAccepting(input.params.id, input.sdk.directory) ? input.language.t("command.permissions.autoaccept.disable") : input.language.t("command.permissions.autoaccept.enable"), - category: input.language.t("command.category.permissions"), keybind: "mod+shift+a", disabled: !input.params.id || !input.permission.permissionsEnabled(), onSelect: () => { @@ -269,15 +267,14 @@ export const useSessionCommands = (input: { : input.language.t("toast.permissions.autoaccept.off.description"), }) }, - }, + }), ]) const sessionActionCommands = createMemo(() => [ - { + sessionCommand({ id: "session.undo", title: input.language.t("command.session.undo"), description: input.language.t("command.session.undo.description"), - category: input.language.t("command.category.session"), slash: "undo", disabled: !input.params.id || input.visibleUserMessages().length === 0, onSelect: async () => { @@ -298,12 +295,11 @@ export const useSessionCommands = (input: { const priorMessage = findLast(input.userMessages(), (x) => x.id < message.id) input.setActiveMessage(priorMessage) }, - }, - { + }), + sessionCommand({ id: "session.redo", title: input.language.t("command.session.redo"), description: input.language.t("command.session.redo.description"), - category: input.language.t("command.category.session"), slash: "redo", disabled: !input.params.id || !input.info()?.revert?.messageID, onSelect: async () => { @@ -323,12 +319,11 @@ export const useSessionCommands = (input: { const priorMsg = findLast(input.userMessages(), (x) => x.id < nextMessage.id) input.setActiveMessage(priorMsg) }, - }, - { + }), + sessionCommand({ id: "session.compact", title: input.language.t("command.session.compact"), description: input.language.t("command.session.compact.description"), - category: input.language.t("command.category.session"), slash: "compact", disabled: !input.params.id || input.visibleUserMessages().length === 0, onSelect: async () => { @@ -348,22 +343,21 @@ export const useSessionCommands = (input: { providerID: model.provider.id, }) }, - }, - { + }), + sessionCommand({ id: "session.fork", title: input.language.t("command.session.fork"), description: input.language.t("command.session.fork.description"), - category: input.language.t("command.category.session"), slash: "fork", disabled: !input.params.id || input.visibleUserMessages().length === 0, onSelect: () => input.dialog.show(() => <DialogFork />), - }, + }), ]) const shareCommands = createMemo(() => { if (input.sync.data.config.share === "disabled") return [] return [ - { + sessionCommand({ id: "session.share", title: input.info()?.share?.url ? input.language.t("session.share.copy.copyLink") @@ -371,7 +365,6 @@ export const useSessionCommands = (input: { description: input.info()?.share?.url ? input.language.t("toast.session.share.success.description") : input.language.t("command.session.share.description"), - category: input.language.t("command.category.session"), slash: "share", disabled: !input.params.id, onSelect: async () => { @@ -441,12 +434,11 @@ export const useSessionCommands = (input: { await copy(url, false) }, - }, - { + }), + sessionCommand({ id: "session.unshare", title: input.language.t("command.session.unshare"), description: input.language.t("command.session.unshare.description"), - category: input.language.t("command.category.session"), slash: "unshare", disabled: !input.params.id || !input.info()?.share?.url, onSelect: async () => { @@ -468,7 +460,7 @@ export const useSessionCommands = (input: { }), ) }, - }, + }), ] }) |
