diff options
| author | Adam <[email protected]> | 2025-10-30 12:02:44 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-30 12:02:51 -0500 |
| commit | ee7612a31c02ea442d8bf3e5d3b75ff572fac26a (patch) | |
| tree | 26c720bf7d08b2a7b2475acd5598ffc23c39b953 | |
| parent | 582ed7c363fec4faa8cf393e023024ac90e65b82 (diff) | |
| download | opencode-ee7612a31c02ea442d8bf3e5d3b75ff572fac26a.tar.gz opencode-ee7612a31c02ea442d8bf3e5d3b75ff572fac26a.zip | |
wip: desktop work
| -rw-r--r-- | packages/desktop/src/pages/index.tsx | 91 | ||||
| -rw-r--r-- | packages/ui/src/components/diff-changes.css | 11 | ||||
| -rw-r--r-- | packages/ui/src/components/diff-changes.tsx | 110 |
3 files changed, 117 insertions, 95 deletions
diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 0ff4423ad..9c27ab514 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -535,101 +535,14 @@ export default function Page() { > <For each={local.session.userMessages()}> {(message) => { - const countLines = (text: string) => { - if (!text) return 0 - return text.split("\n").length - } - - const additions = createMemo( - () => - message.summary?.diffs.reduce((acc, diff) => acc + (diff.additions ?? 0), 0) ?? 0, - ) - - const deletions = createMemo( - () => - message.summary?.diffs.reduce((acc, diff) => acc + (diff.deletions ?? 0), 0) ?? 0, - ) - - const totalBeforeLines = createMemo( - () => - message.summary?.diffs.reduce((acc, diff) => acc + countLines(diff.before), 0) ?? - 0, - ) - - const blockCounts = createMemo(() => { - const TOTAL_BLOCKS = 5 - - const adds = additions() - const dels = deletions() - const unchanged = Math.max(0, totalBeforeLines() - dels) - - const totalActivity = unchanged + adds + dels - - if (totalActivity === 0) { - return { added: 0, deleted: 0, neutral: TOTAL_BLOCKS } - } - - const percentAdded = adds / totalActivity - const percentDeleted = dels / totalActivity - const added_raw = percentAdded * TOTAL_BLOCKS - const deleted_raw = percentDeleted * TOTAL_BLOCKS - - let added = adds > 0 ? Math.ceil(added_raw) : 0 - let deleted = dels > 0 ? Math.ceil(deleted_raw) : 0 - - let total_allocated = added + deleted - if (total_allocated > TOTAL_BLOCKS) { - if (added_raw < deleted_raw) { - added = Math.floor(added_raw) - } else { - deleted = Math.floor(deleted_raw) - } - - total_allocated = added + deleted - if (total_allocated > TOTAL_BLOCKS) { - if (added_raw < deleted_raw) { - deleted = Math.floor(deleted_raw) - } else { - added = Math.floor(added_raw) - } - } - } - - const neutral = Math.max(0, TOTAL_BLOCKS - added - deleted) - - return { added, deleted, neutral } - }) - - const ADD_COLOR = "var(--icon-diff-add-base)" - const DELETE_COLOR = "var(--icon-diff-delete-base)" - const NEUTRAL_COLOR = "var(--icon-weak-base)" - - const visibleBlocks = createMemo(() => { - const counts = blockCounts() - const blocks = [ - ...Array(counts.added).fill(ADD_COLOR), - ...Array(counts.deleted).fill(DELETE_COLOR), - ...Array(counts.neutral).fill(NEUTRAL_COLOR), - ] - return blocks.slice(0, 5) - }) + const diffs = createMemo(() => message.summary?.diffs ?? []) return ( <li class="group/li flex items-center gap-x-2 py-1 self-stretch cursor-default" onClick={() => local.session.setActiveMessage(message.id)} > - <div class="w-[18px] shrink-0"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 12" fill="none"> - <g> - <For each={visibleBlocks()}> - {(color, i) => ( - <rect x={i() * 4} width="2" height="12" rx="1" fill={color} /> - )} - </For> - </g> - </svg> - </div> + <DiffChanges diff={diffs()} variant="bars" /> <div data-active={local.session.activeMessage()?.id === message.id} classList={{ diff --git a/packages/ui/src/components/diff-changes.css b/packages/ui/src/components/diff-changes.css index afca51474..eb95c4676 100644 --- a/packages/ui/src/components/diff-changes.css +++ b/packages/ui/src/components/diff-changes.css @@ -26,3 +26,14 @@ color: var(--text-diff-delete-base); } } + +[data-component="diff-changes"][data-variant="bars"] { + width: 18px; + flex-shrink: 0; + + svg { + display: block; + width: 100%; + height: auto; + } +} diff --git a/packages/ui/src/components/diff-changes.tsx b/packages/ui/src/components/diff-changes.tsx index 7661a9741..433c47f39 100644 --- a/packages/ui/src/components/diff-changes.tsx +++ b/packages/ui/src/components/diff-changes.tsx @@ -1,7 +1,9 @@ import type { FileDiff } from "@opencode-ai/sdk" -import { createMemo, Show } from "solid-js" +import { createMemo, For, Match, Show, Switch } from "solid-js" + +export function DiffChanges(props: { diff: FileDiff | FileDiff[]; variant?: "default" | "bars" }) { + const variant = () => props.variant ?? "default" -export function DiffChanges(props: { diff: FileDiff | FileDiff[] }) { const additions = createMemo(() => Array.isArray(props.diff) ? props.diff.reduce((acc, diff) => acc + (diff.additions ?? 0), 0) @@ -13,11 +15,107 @@ export function DiffChanges(props: { diff: FileDiff | FileDiff[] }) { : props.diff.deletions, ) const total = createMemo(() => (additions() ?? 0) + (deletions() ?? 0)) + + const countLines = (text: string) => { + if (!text) return 0 + return text.split("\n").length + } + + const totalBeforeLines = createMemo(() => { + if (!Array.isArray(props.diff)) return countLines(props.diff.before || "") + return props.diff.reduce((acc, diff) => acc + countLines(diff.before || ""), 0) + }) + + const blockCounts = createMemo(() => { + const TOTAL_BLOCKS = 5 + + const adds = additions() ?? 0 + const dels = deletions() ?? 0 + + if (adds === 0 && dels === 0) { + return { added: 0, deleted: 0, neutral: TOTAL_BLOCKS } + } + + const total = adds + dels + + if (total < 5) { + const added = adds > 0 ? 1 : 0 + const deleted = dels > 0 ? 1 : 0 + const neutral = TOTAL_BLOCKS - added - deleted + return { added, deleted, neutral } + } + + const ratio = adds > dels ? adds / dels : dels / adds + let BLOCKS_FOR_COLORS = TOTAL_BLOCKS + + if (total < 20) { + BLOCKS_FOR_COLORS = TOTAL_BLOCKS - 1 + } else if (ratio < 4) { + BLOCKS_FOR_COLORS = TOTAL_BLOCKS - 1 + } + + const percentAdded = adds / total + const percentDeleted = dels / total + + const added_raw = percentAdded * BLOCKS_FOR_COLORS + const deleted_raw = percentDeleted * BLOCKS_FOR_COLORS + + let added = adds > 0 ? Math.max(1, Math.round(added_raw)) : 0 + let deleted = dels > 0 ? Math.max(1, Math.round(deleted_raw)) : 0 + + // Cap bars based on actual change magnitude + if (adds > 0 && adds <= 5) added = Math.min(added, 1) + if (adds > 5 && adds <= 10) added = Math.min(added, 2) + if (dels > 0 && dels <= 5) deleted = Math.min(deleted, 1) + if (dels > 5 && dels <= 10) deleted = Math.min(deleted, 2) + + let total_allocated = added + deleted + if (total_allocated > BLOCKS_FOR_COLORS) { + if (added_raw > deleted_raw) { + added = BLOCKS_FOR_COLORS - deleted + } else { + deleted = BLOCKS_FOR_COLORS - added + } + total_allocated = added + deleted + } + + const neutral = Math.max(0, TOTAL_BLOCKS - total_allocated) + + return { added, deleted, neutral } + }) + + const ADD_COLOR = "var(--icon-diff-add-base)" + const DELETE_COLOR = "var(--icon-diff-delete-base)" + const NEUTRAL_COLOR = "var(--icon-weak-base)" + + const visibleBlocks = createMemo(() => { + const counts = blockCounts() + const blocks = [ + ...Array(counts.added).fill(ADD_COLOR), + ...Array(counts.deleted).fill(DELETE_COLOR), + ...Array(counts.neutral).fill(NEUTRAL_COLOR), + ] + return blocks.slice(0, 5) + }) + return ( - <Show when={total() > 0}> - <div data-component="diff-changes"> - <span data-slot="additions">{`+${additions()}`}</span> - <span data-slot="deletions">{`-${deletions()}`}</span> + <Show when={variant() === "default" ? total() > 0 : true}> + <div data-component="diff-changes" data-variant={variant()}> + <Switch> + <Match when={variant() === "bars"}> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 12" fill="none"> + <g> + <For each={visibleBlocks()}> + {(color, i) => <rect x={i() * 4} width="2" height="12" rx="1" fill={color} />} + </For> + </g> + </svg> + </Match> + <Match when={variant() === "default"}> + <span data-slot="additions">{`+${additions()}`}</span> + <span data-slot="deletions">{`-${deletions()}`}</span> + </Match> + </Switch> </div> </Show> ) |
