summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-10-30 12:02:44 -0500
committerAdam <[email protected]>2025-10-30 12:02:51 -0500
commitee7612a31c02ea442d8bf3e5d3b75ff572fac26a (patch)
tree26c720bf7d08b2a7b2475acd5598ffc23c39b953 /packages/ui/src/components
parent582ed7c363fec4faa8cf393e023024ac90e65b82 (diff)
downloadopencode-ee7612a31c02ea442d8bf3e5d3b75ff572fac26a.tar.gz
opencode-ee7612a31c02ea442d8bf3e5d3b75ff572fac26a.zip
wip: desktop work
Diffstat (limited to 'packages/ui/src/components')
-rw-r--r--packages/ui/src/components/diff-changes.css11
-rw-r--r--packages/ui/src/components/diff-changes.tsx110
2 files changed, 115 insertions, 6 deletions
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>
)