mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 09:44:21 +01:00
wip: desktop work
This commit is contained in:
@@ -535,101 +535,14 @@ export default function Page() {
|
|||||||
>
|
>
|
||||||
<For each={local.session.userMessages()}>
|
<For each={local.session.userMessages()}>
|
||||||
{(message) => {
|
{(message) => {
|
||||||
const countLines = (text: string) => {
|
const diffs = createMemo(() => message.summary?.diffs ?? [])
|
||||||
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)
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
class="group/li flex items-center gap-x-2 py-1 self-stretch cursor-default"
|
class="group/li flex items-center gap-x-2 py-1 self-stretch cursor-default"
|
||||||
onClick={() => local.session.setActiveMessage(message.id)}
|
onClick={() => local.session.setActiveMessage(message.id)}
|
||||||
>
|
>
|
||||||
<div class="w-[18px] shrink-0">
|
<DiffChanges diff={diffs()} 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>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
data-active={local.session.activeMessage()?.id === message.id}
|
data-active={local.session.activeMessage()?.id === message.id}
|
||||||
classList={{
|
classList={{
|
||||||
|
|||||||
@@ -26,3 +26,14 @@
|
|||||||
color: var(--text-diff-delete-base);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import type { FileDiff } from "@opencode-ai/sdk"
|
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(() =>
|
const additions = createMemo(() =>
|
||||||
Array.isArray(props.diff)
|
Array.isArray(props.diff)
|
||||||
? props.diff.reduce((acc, diff) => acc + (diff.additions ?? 0), 0)
|
? props.diff.reduce((acc, diff) => acc + (diff.additions ?? 0), 0)
|
||||||
@@ -13,11 +15,107 @@ export function DiffChanges(props: { diff: FileDiff | FileDiff[] }) {
|
|||||||
: props.diff.deletions,
|
: props.diff.deletions,
|
||||||
)
|
)
|
||||||
const total = createMemo(() => (additions() ?? 0) + (deletions() ?? 0))
|
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 (
|
return (
|
||||||
<Show when={total() > 0}>
|
<Show when={variant() === "default" ? total() > 0 : true}>
|
||||||
<div data-component="diff-changes">
|
<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="additions">{`+${additions()}`}</span>
|
||||||
<span data-slot="deletions">{`-${deletions()}`}</span>
|
<span data-slot="deletions">{`-${deletions()}`}</span>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user