Buckets:
| import { useRenderer, useTerminalDimensions } from "@opentui/solid" | |
| import { batch, createContext, createEffect, onCleanup, Show, useContext, type JSX, type ParentProps } from "solid-js" | |
| import { useTheme } from "../context/theme" | |
| import { MouseButton, Renderable, RGBA } from "@opentui/core" | |
| import { createStore } from "solid-js/store" | |
| import { useToast } from "./toast" | |
| import { Flag } from "@opencode-ai/core/flag/flag" | |
| import { useBindings, useOpencodeModeStack } from "../keymap" | |
| import { useClipboard } from "../context/clipboard" | |
| export function Dialog( | |
| props: ParentProps<{ | |
| size?: "medium" | "large" | "xlarge" | |
| onClose: () => void | |
| }>, | |
| ) { | |
| const dimensions = useTerminalDimensions() | |
| const { theme } = useTheme() | |
| const renderer = useRenderer() | |
| let dismiss = false | |
| const width = () => { | |
| if (props.size === "xlarge") return 116 | |
| if (props.size === "large") return 88 | |
| return 60 | |
| } | |
| return ( | |
| <box | |
| onMouseDown={() => { | |
| dismiss = !!renderer.getSelection() | |
| }} | |
| onMouseUp={() => { | |
| if (dismiss) { | |
| dismiss = false | |
| return | |
| } | |
| props.onClose?.() | |
| }} | |
| width={dimensions().width} | |
| height={dimensions().height} | |
| alignItems="center" | |
| position="absolute" | |
| zIndex={3000} | |
| paddingTop={dimensions().height / 4} | |
| left={0} | |
| top={0} | |
| backgroundColor={RGBA.fromInts(0, 0, 0, 150)} | |
| > | |
| <box | |
| onMouseUp={(e: { stopPropagation(): void }) => { | |
| dismiss = false | |
| e.stopPropagation() | |
| }} | |
| width={width()} | |
| maxWidth={dimensions().width - 2} | |
| backgroundColor={theme.backgroundPanel} | |
| paddingTop={1} | |
| > | |
| {props.children} | |
| </box> | |
| </box> | |
| ) | |
| } | |
| function init() { | |
| const [store, setStore] = createStore({ | |
| stack: [] as { | |
| element: JSX.Element | |
| onClose?: () => void | |
| }[], | |
| size: "medium" as "medium" | "large" | "xlarge", | |
| }) | |
| const renderer = useRenderer() | |
| const modeStack = useOpencodeModeStack() | |
| createEffect(() => { | |
| if (store.stack.length === 0) return | |
| const popMode = modeStack.push("modal") | |
| onCleanup(popMode) | |
| }) | |
| let focus: Renderable | null | |
| function refocus() { | |
| setTimeout(() => { | |
| if (!focus) return | |
| if (focus.isDestroyed) return | |
| function find(item: Renderable) { | |
| for (const child of item.getChildren()) { | |
| if (child === focus) return true | |
| if (find(child)) return true | |
| } | |
| return false | |
| } | |
| const found = find(renderer.root) | |
| if (!found) return | |
| focus.focus() | |
| }, 1) | |
| } | |
| useBindings(() => ({ | |
| enabled: store.stack.length > 0 && !renderer.getSelection()?.getSelectedText(), | |
| bindings: [ | |
| { | |
| key: "escape", | |
| desc: "Close dialog", | |
| group: "Dialog", | |
| cmd: () => { | |
| if (renderer.getSelection()) { | |
| renderer.clearSelection() | |
| } | |
| const current = store.stack.at(-1) | |
| current?.onClose?.() | |
| setStore("stack", store.stack.slice(0, -1)) | |
| refocus() | |
| }, | |
| }, | |
| { | |
| key: "ctrl+c", | |
| desc: "Close dialog", | |
| group: "Dialog", | |
| cmd: () => { | |
| if (renderer.getSelection()) { | |
| renderer.clearSelection() | |
| } | |
| const current = store.stack.at(-1) | |
| current?.onClose?.() | |
| setStore("stack", store.stack.slice(0, -1)) | |
| refocus() | |
| }, | |
| }, | |
| ], | |
| })) | |
| return { | |
| clear() { | |
| for (const item of store.stack) { | |
| if (item.onClose) item.onClose() | |
| } | |
| batch(() => { | |
| setStore("size", "medium") | |
| setStore("stack", []) | |
| }) | |
| refocus() | |
| }, | |
| replace(input: any, onClose?: () => void) { | |
| if (store.stack.length === 0) { | |
| focus = renderer.currentFocusedRenderable | |
| focus?.blur() | |
| } | |
| for (const item of store.stack) { | |
| if (item.onClose) item.onClose() | |
| } | |
| setStore("size", "medium") | |
| setStore("stack", [ | |
| { | |
| element: input, | |
| onClose, | |
| }, | |
| ]) | |
| }, | |
| get stack() { | |
| return store.stack | |
| }, | |
| get size() { | |
| return store.size | |
| }, | |
| setSize(size: "medium" | "large" | "xlarge") { | |
| setStore("size", size) | |
| }, | |
| } | |
| } | |
| export type DialogContext = ReturnType<typeof init> | |
| const ctx = createContext<DialogContext>() | |
| export function DialogProvider(props: ParentProps) { | |
| const value = init() | |
| const renderer = useRenderer() | |
| const toast = useToast() | |
| const clipboard = useClipboard() | |
| function copySelection() { | |
| const text = renderer.getSelection()?.getSelectedText() | |
| if (!text || !clipboard.write) return false | |
| void clipboard.write(text).then( | |
| () => toast.show({ message: "Copied to clipboard", variant: "info" }), | |
| (error) => toast.error(error), | |
| ) | |
| renderer.clearSelection() | |
| return true | |
| } | |
| return ( | |
| <ctx.Provider value={value}> | |
| {props.children} | |
| <box | |
| position="absolute" | |
| zIndex={3000} | |
| onMouseDown={(evt: { button: number; preventDefault(): void; stopPropagation(): void }) => { | |
| if (!Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT) return | |
| if (evt.button !== MouseButton.RIGHT) return | |
| if (!copySelection()) return | |
| evt.preventDefault() | |
| evt.stopPropagation() | |
| }} | |
| onMouseUp={!Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT ? copySelection : undefined} | |
| > | |
| <Show when={value.stack.length}> | |
| <Dialog onClose={() => value.clear()} size={value.size}> | |
| {value.stack.at(-1)!.element} | |
| </Dialog> | |
| </Show> | |
| </box> | |
| </ctx.Provider> | |
| ) | |
| } | |
| export function useDialog() { | |
| const value = useContext(ctx) | |
| if (!value) { | |
| throw new Error("useDialog must be used within a DialogProvider") | |
| } | |
| return value | |
| } | |
Xet Storage Details
- Size:
- 5.91 kB
- Xet hash:
- e97790285984316a66be2167586606775b7cfb26f7c6d18ce8ee677da3edf4c6
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.