diff --git a/packages/tui/src/feature-plugins/system/diff-viewer-file-menu.tsx b/packages/tui/src/feature-plugins/system/diff-viewer-file-menu.tsx new file mode 100644 index 00000000000..710f7696bcd --- /dev/null +++ b/packages/tui/src/feature-plugins/system/diff-viewer-file-menu.tsx @@ -0,0 +1,87 @@ +/** @jsxImportSource @opentui/solid */ +import type { Plugin } from "@opencode-ai/plugin/tui" +import { BoxRenderable, MouseButton } from "@opentui/core" +import { Portal, useTerminalDimensions } from "@opentui/solid" +import { createSignal, onCleanup } from "solid-js" + +export function DiffFileMenu(props: { + context: Plugin.Context + state: { fileIndex: number; x: number; y: number } + reviewed: boolean + onToggle: () => void + onClose: () => void +}) { + const dimensions = useTerminalDimensions() + const theme = props.context.theme.contextual.overlay + const [hovered, setHovered] = createSignal(false) + const label = () => (props.reviewed ? "Mark incomplete" : "Mark complete") + const width = () => Math.min(19, dimensions().width) + const run = () => { + props.onClose() + props.onToggle() + } + onCleanup(props.context.keymap.mode.push("menu")) + props.context.keymap.layer(() => ({ + mode: "menu", + commands: [ + { bind: "escape,ctrl+c", title: "Close file menu", group: "Diff", run: props.onClose }, + { bind: "return", title: label(), group: "Diff", run }, + ], + })) + + return ( + { + if (!(container instanceof BoxRenderable)) return + // Portal's wrapper must also escape root flow, not follow the full-height app. + container.position = "absolute" + container.left = 0 + container.top = 0 + container.zIndex = 2600 + }} + > + { + props.onClose() + event.preventDefault() + event.stopPropagation() + }} + > + setHovered(true)} + onMouseOut={() => setHovered(false)} + onMouseDown={(event) => { + if (event.button === MouseButton.RIGHT) props.onClose() + event.preventDefault() + event.stopPropagation() + }} + onMouseUp={(event) => { + event.preventDefault() + event.stopPropagation() + if (event.button === MouseButton.LEFT) run() + }} + > + + {label()} + + + + + ) +} diff --git a/packages/tui/src/feature-plugins/system/diff-viewer.tsx b/packages/tui/src/feature-plugins/system/diff-viewer.tsx index 3bb6aabd235..a5e7d5fe9e7 100644 --- a/packages/tui/src/feature-plugins/system/diff-viewer.tsx +++ b/packages/tui/src/feature-plugins/system/diff-viewer.tsx @@ -14,6 +14,7 @@ import { filetype } from "../../util/filetype" import { useRenderer, useTerminalDimensions } from "@opentui/solid" import { createEffect, createMemo, createResource, createSignal, For, Match, onCleanup, Show, Switch } from "solid-js" import { DiffViewerFileTree } from "./diff-viewer-file-tree" +import { DiffFileMenu } from "./diff-viewer-file-menu" import { DiffViewerImage, isDiffImageFile } from "./diff-viewer-image" import { DialogSelect } from "../../ui/dialog-select" import { EmptyBorder } from "../../ui/border" @@ -1076,76 +1077,6 @@ export function DiffViewerContent(props: { ) } -function DiffFileMenu(props: { - context: Plugin.Context - state: FileMenuState - reviewed: boolean - onToggle: () => void - onClose: () => void -}) { - const dimensions = useTerminalDimensions() - const theme = props.context.theme.contextual.overlay - const [hovered, setHovered] = createSignal(false) - const label = () => (props.reviewed ? "Mark incomplete" : "Mark complete") - const run = () => { - props.onClose() - props.onToggle() - } - onCleanup(props.context.keymap.mode.push("menu")) - props.context.keymap.layer(() => ({ - mode: "menu", - commands: [ - { bind: "escape,ctrl+c", title: "Close file menu", group: "Diff", run: props.onClose }, - { bind: "return", title: label(), group: "Diff", run }, - ], - })) - - return ( - { - props.onClose() - event.preventDefault() - event.stopPropagation() - }} - > - setHovered(true)} - onMouseOut={() => setHovered(false)} - onMouseDown={(event) => { - if (event.button === MouseButton.RIGHT) props.onClose() - event.preventDefault() - event.stopPropagation() - }} - onMouseUp={(event) => { - event.preventDefault() - event.stopPropagation() - if (event.button === MouseButton.LEFT) run() - }} - > - - {label()} - - - - ) -} - function DiffViewerHelpDialog(props: { context: Plugin.Context; single: boolean }) { const dimensions = useTerminalDimensions() const theme = props.context.theme.contextual.elevated