Buckets:
| import { TextAttributes } from "@opentui/core" | |
| import { useKeyboard } from "@opentui/solid" | |
| import type { VcsFileStatus } from "@opencode-ai/sdk/v2" | |
| import { createMemo, For } from "solid-js" | |
| import { createStore } from "solid-js/store" | |
| import { Locale } from "../util/locale" | |
| import { useTheme } from "../context/theme" | |
| import { useTuiConfig } from "../config" | |
| import { useDialog, type DialogContext } from "../ui/dialog" | |
| import { getScrollAcceleration } from "../util/scroll" | |
| const options = ["no", "yes"] as const | |
| export type WorkspaceFileChangesChoice = (typeof options)[number] | |
| function statusLabel(status: VcsFileStatus["status"]) { | |
| if (status === "added") return "A" | |
| if (status === "deleted") return "D" | |
| return "M" | |
| } | |
| function changeCountWidth(file: VcsFileStatus) { | |
| // The "plus 2" is for spaces | |
| return `${file.additions ? `+${file.additions}` : ""}${file.deletions ? ` -${file.deletions}` : ""}`.length + 2 | |
| } | |
| export function DialogWorkspaceFileChanges(props: { | |
| files: VcsFileStatus[] | |
| onSelect: (choice: WorkspaceFileChangesChoice) => void | |
| title?: string | |
| message?: string | |
| }) { | |
| const dialog = useDialog() | |
| const { theme } = useTheme() | |
| const tuiConfig = useTuiConfig() | |
| const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig)) | |
| const [store, setStore] = createStore({ active: "yes" as WorkspaceFileChangesChoice }) | |
| const height = createMemo(() => Math.min(props.files.length, 8)) | |
| const fileNameWidth = createMemo(() => 48 - Math.max(Math.max(7, ...props.files.map(changeCountWidth)) - 7, 0)) | |
| function confirm() { | |
| props.onSelect(store.active) | |
| dialog.clear() | |
| } | |
| useKeyboard((evt) => { | |
| if (evt.name === "return") { | |
| evt.preventDefault() | |
| evt.stopPropagation() | |
| confirm() | |
| return | |
| } | |
| if (evt.name === "left") { | |
| evt.preventDefault() | |
| evt.stopPropagation() | |
| const index = options.indexOf(store.active) | |
| setStore("active", options[Math.max(index - 1, 0)]) | |
| return | |
| } | |
| if (evt.name === "right") { | |
| evt.preventDefault() | |
| evt.stopPropagation() | |
| const index = options.indexOf(store.active) | |
| setStore("active", options[Math.min(index + 1, options.length - 1)]) | |
| } | |
| }) | |
| return ( | |
| <box gap={1}> | |
| <box flexDirection="row" justifyContent="space-between" paddingLeft={2} paddingRight={2}> | |
| <text attributes={TextAttributes.BOLD} fg={theme.text}> | |
| {props.title ?? "File Changes Found"} | |
| </text> | |
| <text fg={theme.textMuted} onMouseUp={() => dialog.clear()}> | |
| esc | |
| </text> | |
| </box> | |
| <box paddingLeft={2} paddingRight={2}> | |
| <text fg={theme.textMuted} wrapMode="word"> | |
| {props.message ?? "Do you want to move these changes with the session?"} | |
| </text> | |
| </box> | |
| <scrollbox | |
| height={height()} | |
| backgroundColor={theme.backgroundElement} | |
| scrollbarOptions={{ visible: false }} | |
| scrollAcceleration={scrollAcceleration()} | |
| > | |
| <For each={props.files}> | |
| {(item) => ( | |
| <box flexDirection="row" justifyContent="space-between" paddingLeft={2} paddingRight={2}> | |
| <box flexDirection="row" minWidth={0} flexShrink={1}> | |
| <box width={2} flexShrink={0}> | |
| <text fg={theme.textMuted}>{statusLabel(item.status)}</text> | |
| </box> | |
| <text fg={theme.textMuted} wrapMode="none"> | |
| {Locale.truncateLeft(item.file, fileNameWidth())} | |
| </text> | |
| </box> | |
| <box flexDirection="row" gap={1} minWidth={7} flexShrink={0} justifyContent="flex-end"> | |
| <text> | |
| {" "} | |
| {item.additions ? <span style={{ fg: theme.diffAdded }}>+{item.additions}</span> : null} | |
| {item.deletions ? <span style={{ fg: theme.diffRemoved }}> -{item.deletions}</span> : null} | |
| </text> | |
| </box> | |
| </box> | |
| )} | |
| </For> | |
| </scrollbox> | |
| <box flexDirection="row" justifyContent="flex-end" paddingLeft={2} paddingRight={2} paddingBottom={1}> | |
| <For each={options}> | |
| {(item) => ( | |
| <box | |
| paddingLeft={2} | |
| paddingRight={2} | |
| backgroundColor={item === store.active ? theme.primary : undefined} | |
| onMouseUp={() => { | |
| setStore("active", item) | |
| props.onSelect(item) | |
| dialog.clear() | |
| }} | |
| > | |
| <text fg={item === store.active ? theme.selectedListItemText : theme.textMuted}>{item}</text> | |
| </box> | |
| )} | |
| </For> | |
| </box> | |
| </box> | |
| ) | |
| } | |
| DialogWorkspaceFileChanges.show = ( | |
| dialog: DialogContext, | |
| files: VcsFileStatus[], | |
| options?: { title?: string; message?: string }, | |
| ) => { | |
| return new Promise<WorkspaceFileChangesChoice | undefined>((resolve) => { | |
| dialog.replace( | |
| () => <DialogWorkspaceFileChanges files={files} onSelect={resolve} {...options} />, | |
| () => resolve(undefined), | |
| ) | |
| }) | |
| } | |
Xet Storage Details
- Size:
- 5.07 kB
- Xet hash:
- d7c5d34828a6380e8d88492ec1466ea5dd9f4a9f745b0480ecda6dd4809b2c0a
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.