diff options
| author | Dax Raad <[email protected]> | 2025-11-28 21:13:13 -0500 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-11-28 21:13:13 -0500 |
| commit | 204a31b6bbad951e1e03d985c2edc62165062a4e (patch) | |
| tree | b386540eda1ebe6bd679d12f888cf02f181c4435 /packages | |
| parent | 813d287a09b3bdffecdb7e79226b39b4cb8ee211 (diff) | |
| parent | 4dd9f33ebaa887f906387a2fb8700beff170648f (diff) | |
| download | opencode-204a31b6bbad951e1e03d985c2edc62165062a4e.tar.gz opencode-204a31b6bbad951e1e03d985c2edc62165062a4e.zip | |
Merge remote-tracking branch 'origin/dev' into dev
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/desktop/index.html | 3 | ||||
| -rw-r--r-- | packages/enterprise/src/entry-server.tsx | 3 | ||||
| -rw-r--r-- | packages/opencode/package.json | 6 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 156 | ||||
| -rw-r--r-- | packages/ui/src/components/diff.tsx | 2 |
5 files changed, 51 insertions, 119 deletions
diff --git a/packages/desktop/index.html b/packages/desktop/index.html index 6e67e6d47..0ac3d566d 100644 --- a/packages/desktop/index.html +++ b/packages/desktop/index.html @@ -9,7 +9,8 @@ <link rel="shortcut icon" href="/favicon.ico" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="manifest" href="/site.webmanifest" /> - <meta name="theme-color" content="var(--background-base)" /> + <meta name="theme-color" content="#F8F7F7" /> + <meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" /> <meta property="og:image" content="/social-share.png" /> <meta property="twitter:image" content="/social-share.png" /> </head> diff --git a/packages/enterprise/src/entry-server.tsx b/packages/enterprise/src/entry-server.tsx index 85d69e2e0..68f4325c8 100644 --- a/packages/enterprise/src/entry-server.tsx +++ b/packages/enterprise/src/entry-server.tsx @@ -9,7 +9,8 @@ export default createHandler(() => ( <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>OpenCode</title> - <meta name="theme-color" content="var(--background-base)" /> + <meta name="theme-color" content="#F8F7F7" /> + <meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" /> <meta property="og:image" content="/social-share.png" /> <meta property="twitter:image" content="/social-share.png" /> {assets} diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 968e9539b..e8e79f36f 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -62,8 +62,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.2.8", - "@opentui/core": "0.1.50", - "@opentui/solid": "0.1.50", + "@opentui/core": "0.1.51", + "@opentui/solid": "0.1.51", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -82,7 +82,7 @@ "jsonc-parser": "3.3.1", "minimatch": "10.0.3", "open": "10.1.2", - "opentui-spinner": "0.0.5", + "opentui-spinner": "0.0.6", "partial-json": "0.1.7", "remeda": "catalog:", "solid-js": "catalog:", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 3d14db149..fb0e07a86 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -81,6 +81,7 @@ const context = createContext<{ conceal: () => boolean showThinking: () => boolean showTimestamps: () => boolean + diffWrapMode: () => "word" | "none" sync: ReturnType<typeof useSync> }>() @@ -113,6 +114,7 @@ export function Session() { const [conceal, setConceal] = createSignal(true) const [showThinking, setShowThinking] = createSignal(kv.get("thinking_visibility", true)) const [showTimestamps, setShowTimestamps] = createSignal(kv.get("timestamps", "hide") === "show") + const [diffWrapMode, setDiffWrapMode] = createSignal<"word" | "none">("word") const wide = createMemo(() => dimensions().width > 120) const sidebarVisible = createMemo(() => { @@ -443,6 +445,15 @@ export function Session() { }, }, { + title: "Toggle diff wrapping", + value: "session.toggle.diffwrap", + category: "Session", + onSelect: (dialog) => { + setDiffWrapMode((prev) => (prev === "word" ? "none" : "word")) + dialog.clear() + }, + }, + { title: "Page up", value: "session.page.up", keybind: "messages_page_up", @@ -743,6 +754,7 @@ export function Session() { conceal, showThinking, showTimestamps, + diffWrapMode, sync, }} > @@ -1302,21 +1314,9 @@ ToolRegistry.register<typeof WriteTool>({ container: "block", render(props) { const { theme, syntax } = useTheme() - const lines = createMemo( - () => (typeof props.input.content === "string" ? props.input.content.split("\n") : []), - [] as string[], - ) const code = createMemo(() => { if (!props.input.content) return "" - const text = props.input.content - return text - }) - - const numbers = createMemo(() => { - const pad = lines().length.toString().length - return lines() - .map((_, index) => index + 1) - .map((x) => x.toString().padStart(pad, " ")) + return props.input.content }) const diagnostics = createMemo(() => props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? []) @@ -1326,14 +1326,9 @@ ToolRegistry.register<typeof WriteTool>({ <ToolTitle icon="←" fallback="Preparing write..." when={props.input.filePath}> Wrote {props.input.filePath} </ToolTitle> - <box flexDirection="row"> - <box flexShrink={0}> - <For each={numbers()}>{(value) => <text style={{ fg: theme.textMuted }}>{value}</text>}</For> - </box> - <box paddingLeft={1} flexGrow={1}> - <code fg={theme.text} filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} /> - </box> - </box> + <line_number fg={theme.textMuted} minWidth={3} paddingRight={1}> + <code fg={theme.text} filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} /> + </line_number> <Show when={diagnostics().length}> <For each={diagnostics()}> {(diagnostic) => ( @@ -1475,84 +1470,17 @@ ToolRegistry.register<typeof EditTool>({ const ctx = use() const { theme, syntax } = useTheme() - const style = createMemo(() => { + const view = createMemo(() => { const diffStyle = ctx.sync.data.config.tui?.diff_style - if (diffStyle === "stacked") return "stacked" + if (diffStyle === "stacked") return "unified" // Default to "auto" behavior - return ctx.width > 120 ? "split" : "stacked" - }) - - const diff = createMemo(() => { - const diff = props.metadata.diff ?? props.permission["diff"] - if (!diff) return null - - try { - const patches = parsePatch(diff) - if (patches.length === 0) return null - - const patch = patches[0] - const oldLines: string[] = [] - const newLines: string[] = [] - - for (const hunk of patch.hunks) { - let i = 0 - while (i < hunk.lines.length) { - const line = hunk.lines[i] - - if (line.startsWith("-")) { - const removedLines: string[] = [] - while (i < hunk.lines.length && hunk.lines[i].startsWith("-")) { - removedLines.push("- " + hunk.lines[i].slice(1)) - i++ - } - - const addedLines: string[] = [] - while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) { - addedLines.push("+ " + hunk.lines[i].slice(1)) - i++ - } - - const maxLen = Math.max(removedLines.length, addedLines.length) - for (let j = 0; j < maxLen; j++) { - oldLines.push(removedLines[j] ?? "") - newLines.push(addedLines[j] ?? "") - } - } else if (line.startsWith("+")) { - const addedLines: string[] = [] - while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) { - addedLines.push("+ " + hunk.lines[i].slice(1)) - i++ - } - - for (const added of addedLines) { - oldLines.push("") - newLines.push(added) - } - } else { - oldLines.push(" " + line.slice(1)) - newLines.push(" " + line.slice(1)) - i++ - } - } - } - - return { - oldContent: oldLines.join("\n"), - newContent: newLines.join("\n"), - } - } catch (error) { - return null - } - }) - - const code = createMemo(() => { - if (!props.metadata.diff) return "" - const text = props.metadata.diff.split("\n").slice(5).join("\n") - return text.trim() + return ctx.width > 120 ? "split" : "unified" }) const ft = createMemo(() => filetype(props.input.filePath)) + const diffContent = createMemo(() => props.metadata.diff ?? props.permission["diff"]) + const diagnostics = createMemo(() => { const arr = props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? [] return arr.filter((x) => x.severity === 1).slice(0, 3) @@ -1566,26 +1494,28 @@ ToolRegistry.register<typeof EditTool>({ replaceAll: props.input.replaceAll, })} </ToolTitle> - <Switch> - <Match when={props.permission["diff"]}> - <text fg={theme.text}>{props.permission["diff"]?.trim()}</text> - </Match> - <Match when={diff() && style() === "split"}> - <box paddingLeft={1} flexDirection="row" gap={2}> - <box flexGrow={1} flexBasis={0}> - <code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={diff()!.oldContent} /> - </box> - <box flexGrow={1} flexBasis={0}> - <code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={diff()!.newContent} /> - </box> - </box> - </Match> - <Match when={code()}> - <box paddingLeft={1}> - <code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={code()} /> - </box> - </Match> - </Switch> + <Show when={diffContent()}> + <box paddingLeft={1}> + <diff + diff={diffContent()} + view={view()} + filetype={ft()} + syntaxStyle={syntax()} + showLineNumbers={true} + width="100%" + wrapMode={ctx.diffWrapMode()} + addedBg={theme.diffAddedBg} + removedBg={theme.diffRemovedBg} + contextBg={theme.diffContextBg} + addedSignColor={theme.diffHighlightAdded} + removedSignColor={theme.diffHighlightRemoved} + lineNumberFg={theme.diffLineNumber} + lineNumberBg={theme.diffContextBg} + addedLineNumberBg={theme.diffAddedLineNumberBg} + removedLineNumberBg={theme.diffRemovedLineNumberBg} + /> + </box> + </Show> <Show when={diagnostics().length}> <box> <For each={diagnostics()}> diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index c895a6e29..bd2134515 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -46,7 +46,7 @@ export function Diff<T>(props: DiffProps<T>) { }) onMount(() => { - if (isServer) return + if (isServer || !props.preloadedDiff) return fileDiffInstance = new FileDiff<T>({ ...createDefaultOptions(props.diffStyle), ...others, |
