Skip to content

Commit c0a3514

Browse files
committed
feat: better code and diff rendering performance
1 parent 221bb64 commit c0a3514

File tree

14 files changed

+226
-101
lines changed

14 files changed

+226
-101
lines changed

.opencode/opencode.jsonc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{
22
"$schema": "https://opencode.ai/config.json",
3-
"plugin": ["opencode-openai-codex-auth"],
4-
"enterprise": {
5-
"url": "https://enterprise.dev.opencode.ai",
6-
},
3+
"plugin": [
4+
"opencode-openai-codex-auth"
5+
],
6+
// "enterprise": {
7+
// "url": "https://enterprise.dev.opencode.ai",
8+
// },
79
"provider": {
810
"opencode": {
911
"options": {
@@ -18,7 +20,10 @@
1820
},
1921
"morph": {
2022
"type": "local",
21-
"command": ["bunx", "@morphllm/morphmcp"],
23+
"command": [
24+
"bunx",
25+
"@morphllm/morphmcp"
26+
],
2227
"environment": {
2328
"ENABLED_TOOLS": "warp_grep",
2429
},

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@tsconfig/bun": "1.0.9",
3131
"@cloudflare/workers-types": "4.20251008.0",
3232
"@openauthjs/openauth": "0.0.0-20250322224806",
33-
"@pierre/precision-diffs": "0.5.7",
33+
"@pierre/precision-diffs": "0.6.0-beta.3",
3434
"@tailwindcss/vite": "4.1.11",
3535
"diff": "8.0.2",
3636
"ai": "5.0.97",

packages/desktop/src/pages/session.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { useSync } from "@/context/sync"
3030
import { useSession } from "@/context/session"
3131
import { useLayout } from "@/context/layout"
3232
import { getDirectory, getFilename } from "@opencode-ai/util/path"
33+
import { Diff } from "@opencode-ai/ui/diff"
3334

3435
export default function Page() {
3536
const layout = useLayout()
@@ -357,6 +358,7 @@ export default function Page() {
357358
content: "pb-20",
358359
container: "w-full " + (wide() ? "max-w-146 mx-auto px-6" : "pr-6 pl-18"),
359360
}}
361+
diffComponent={Diff}
360362
/>
361363
</div>
362364
</Match>
@@ -405,6 +407,7 @@ export default function Page() {
405407
container: "px-6",
406408
}}
407409
diffs={session.diffs()}
410+
diffComponent={Diff}
408411
actions={
409412
<Tooltip value="Open in tab">
410413
<IconButton
@@ -436,6 +439,7 @@ export default function Page() {
436439
container: "px-6",
437440
}}
438441
diffs={session.diffs()}
442+
diffComponent={Diff}
439443
split
440444
/>
441445
</div>

packages/enterprise/src/routes/share/[shareID].tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import z from "zod"
1818
import NotFound from "../[...404]"
1919
import { Tabs } from "@opencode-ai/ui/tabs"
2020
import { preloadMultiFileDiff, PreloadMultiFileDiffResult } from "@pierre/precision-diffs/ssr"
21+
import { Diff } from "@opencode-ai/ui/diff-ssr"
22+
import { clientOnly } from "@solidjs/start"
23+
24+
const ClientOnlyDiff = clientOnly(() => import("@opencode-ai/ui/diff").then((m) => ({ default: m.Diff })))
2125

2226
const SessionDataMissingError = NamedError.create(
2327
"SessionDataMissingError",
@@ -230,6 +234,7 @@ export default function () {
230234
"flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
231235
container: "px-4",
232236
}}
237+
diffComponent={ClientOnlyDiff}
233238
/>
234239
)}
235240
</For>
@@ -299,6 +304,7 @@ export default function () {
299304
content: "flex flex-col justify-between items-start",
300305
container: "w-full pb-20 " + (wide() ? "max-w-146 mx-auto px-6" : "pr-6 pl-18"),
301306
}}
307+
diffComponent={ClientOnlyDiff}
302308
>
303309
<div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}>
304310
<Logo class="w-58.5 opacity-12" />
@@ -311,16 +317,18 @@ export default function () {
311317
<SessionReview
312318
class="@4xl:hidden"
313319
diffs={diffs()}
320+
diffComponent={Diff}
314321
classes={{
315322
root: "pb-20",
316323
header: "px-6",
317324
container: "px-6",
318325
}}
319326
/>
320327
<SessionReview
321-
class="hidden @4xl:flex"
322328
split
329+
class="hidden @4xl:flex"
323330
diffs={splitDiffs()}
331+
diffComponent={Diff}
324332
classes={{
325333
root: "pb-20",
326334
header: "px-6",
@@ -352,6 +360,7 @@ export default function () {
352360
<div class="relative h-full pt-8 overflow-y-auto no-scrollbar">
353361
<SessionReview
354362
diffs={diffs()}
363+
diffComponent={Diff}
355364
classes={{
356365
root: "pb-20",
357366
header: "px-4",

packages/ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "module",
55
"exports": {
66
"./*": "./src/components/*.tsx",
7-
"./pierre": "./src/components/pierre.ts",
7+
"./pierre": "./src/pierre/index.ts",
88
"./hooks": "./src/hooks/index.ts",
99
"./context": "./src/context/index.ts",
1010
"./context/*": "./src/context/*.tsx",

packages/ui/src/components/code.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
import { type FileContents, File, FileOptions, LineAnnotation } from "@pierre/precision-diffs"
22
import { ComponentProps, createEffect, splitProps } from "solid-js"
3-
import { createDefaultOptions, styleVariables } from "./pierre"
3+
import { createDefaultOptions, styleVariables } from "../pierre"
4+
import { getOrCreateWorkerPoolSingleton } from "@pierre/precision-diffs/worker"
5+
import { workerFactory } from "../pierre/worker"
6+
7+
const workerPool = getOrCreateWorkerPoolSingleton({
8+
poolOptions: {
9+
workerFactory,
10+
// poolSize defaults to 8. More workers = more parallelism but
11+
// also more memory. Too many can actually slow things down.
12+
// poolSize: 8,
13+
},
14+
highlighterOptions: {
15+
theme: "OpenCode",
16+
// Optionally preload languages to avoid lazy-loading delays
17+
// langs: ["typescript", "javascript", "css", "html"],
18+
},
19+
})
420

521
export type CodeProps<T = {}> = FileOptions<T> & {
622
file: FileContents
@@ -14,10 +30,13 @@ export function Code<T>(props: CodeProps<T>) {
1430
const [local, others] = splitProps(props, ["file", "class", "classList", "annotations"])
1531

1632
createEffect(() => {
17-
const instance = new File<T>({
18-
...createDefaultOptions<T>("unified"),
19-
...others,
20-
})
33+
const instance = new File<T>(
34+
{
35+
...createDefaultOptions<T>("unified"),
36+
...others,
37+
},
38+
workerPool,
39+
)
2140

2241
container.innerHTML = ""
2342
instance.render({
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { FileDiff } from "@pierre/precision-diffs"
2+
import { PreloadMultiFileDiffResult } from "@pierre/precision-diffs/ssr"
3+
import { onCleanup, onMount, Show, splitProps } from "solid-js"
4+
import { isServer } from "solid-js/web"
5+
import { createDefaultOptions, styleVariables, type DiffProps } from "../pierre"
6+
7+
export type SSRDiffProps<T = {}> = DiffProps<T> & {
8+
preloadedDiff: PreloadMultiFileDiffResult<T>
9+
}
10+
11+
export function Diff<T>(props: SSRDiffProps<T>) {
12+
let container!: HTMLDivElement
13+
let fileDiffRef!: HTMLElement
14+
const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"])
15+
16+
let fileDiffInstance: FileDiff<T> | undefined
17+
const cleanupFunctions: Array<() => void> = []
18+
19+
onMount(() => {
20+
if (isServer || !props.preloadedDiff) return
21+
fileDiffInstance = new FileDiff<T>({
22+
...createDefaultOptions(props.diffStyle),
23+
...others,
24+
...props.preloadedDiff,
25+
})
26+
// @ts-expect-error - fileContainer is private but needed for SSR hydration
27+
fileDiffInstance.fileContainer = fileDiffRef
28+
fileDiffInstance.hydrate({
29+
oldFile: local.before,
30+
newFile: local.after,
31+
lineAnnotations: local.annotations,
32+
fileContainer: fileDiffRef,
33+
containerWrapper: container,
34+
})
35+
36+
// Hydrate annotation slots with interactive SolidJS components
37+
// if (props.annotations.length > 0 && props.renderAnnotation != null) {
38+
// for (const annotation of props.annotations) {
39+
// const slotName = `annotation-${annotation.side}-${annotation.lineNumber}`;
40+
// const slotElement = fileDiffRef.querySelector(
41+
// `[slot="${slotName}"]`
42+
// ) as HTMLElement;
43+
//
44+
// if (slotElement != null) {
45+
// // Clear the static server-rendered content from the slot
46+
// slotElement.innerHTML = '';
47+
//
48+
// // Mount a fresh SolidJS component into this slot using render().
49+
// // This enables full SolidJS reactivity (signals, effects, etc.)
50+
// const dispose = render(
51+
// () => props.renderAnnotation!(annotation),
52+
// slotElement
53+
// );
54+
// cleanupFunctions.push(dispose);
55+
// }
56+
// }
57+
// }
58+
})
59+
60+
onCleanup(() => {
61+
// Clean up FileDiff event handlers and dispose SolidJS components
62+
fileDiffInstance?.cleanUp()
63+
cleanupFunctions.forEach((dispose) => dispose())
64+
})
65+
66+
return (
67+
<div data-component="diff" style={styleVariables} ref={container}>
68+
<file-diff ref={fileDiffRef} id="ssr-diff">
69+
<Show when={isServer}>
70+
<template shadowrootmode="open" innerHTML={props.preloadedDiff.prerenderedHTML} />
71+
</Show>
72+
</file-diff>
73+
</div>
74+
)
75+
}
Lines changed: 27 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
import { type FileContents, FileDiff, type DiffLineAnnotation, FileDiffOptions } from "@pierre/precision-diffs"
2-
import { PreloadMultiFileDiffResult } from "@pierre/precision-diffs/ssr"
3-
import { ComponentProps, createEffect, onCleanup, onMount, Show, splitProps } from "solid-js"
4-
import { isServer } from "solid-js/web"
5-
import { createDefaultOptions, styleVariables } from "./pierre"
6-
7-
export type DiffProps<T = {}> = FileDiffOptions<T> & {
8-
preloadedDiff?: PreloadMultiFileDiffResult<T>
9-
before: FileContents
10-
after: FileContents
11-
annotations?: DiffLineAnnotation<T>[]
12-
class?: string
13-
classList?: ComponentProps<"div">["classList"]
14-
}
1+
import { FileDiff } from "@pierre/precision-diffs"
2+
import { getOrCreateWorkerPoolSingleton } from "@pierre/precision-diffs/worker"
3+
import { createEffect, onCleanup, splitProps } from "solid-js"
4+
import { createDefaultOptions, type DiffProps, styleVariables } from "../pierre"
5+
import { workerFactory } from "../pierre/worker"
6+
7+
const workerPool = getOrCreateWorkerPoolSingleton({
8+
poolOptions: {
9+
workerFactory,
10+
// poolSize defaults to 8. More workers = more parallelism but
11+
// also more memory. Too many can actually slow things down.
12+
// poolSize: 8,
13+
},
14+
highlighterOptions: {
15+
theme: "OpenCode",
16+
// Optionally preload languages to avoid lazy-loading delays
17+
// langs: ["typescript", "javascript", "css", "html"],
18+
},
19+
})
1520

1621
// interface ThreadMetadata {
1722
// threadId: string
@@ -21,21 +26,21 @@ export type DiffProps<T = {}> = FileDiffOptions<T> & {
2126

2227
export function Diff<T>(props: DiffProps<T>) {
2328
let container!: HTMLDivElement
24-
let fileDiffRef!: HTMLElement
2529
const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"])
2630

2731
let fileDiffInstance: FileDiff<T> | undefined
2832
const cleanupFunctions: Array<() => void> = []
2933

3034
createEffect(() => {
31-
if (props.preloadedDiff) return
3235
container.innerHTML = ""
3336
if (!fileDiffInstance) {
34-
fileDiffInstance = new FileDiff<T>({
35-
...createDefaultOptions(props.diffStyle),
36-
...others,
37-
...(props.preloadedDiff ?? {}),
38-
})
37+
fileDiffInstance = new FileDiff<T>(
38+
{
39+
...createDefaultOptions(props.diffStyle),
40+
...others,
41+
},
42+
workerPool,
43+
)
3944
}
4045
fileDiffInstance.render({
4146
oldFile: local.before,
@@ -45,60 +50,11 @@ export function Diff<T>(props: DiffProps<T>) {
4550
})
4651
})
4752

48-
onMount(() => {
49-
if (isServer || !props.preloadedDiff) return
50-
fileDiffInstance = new FileDiff<T>({
51-
...createDefaultOptions(props.diffStyle),
52-
...others,
53-
...(props.preloadedDiff ?? {}),
54-
})
55-
// @ts-expect-error - fileContainer is private but needed for SSR hydration
56-
fileDiffInstance.fileContainer = fileDiffRef
57-
fileDiffInstance.hydrate({
58-
oldFile: local.before,
59-
newFile: local.after,
60-
lineAnnotations: local.annotations,
61-
fileContainer: fileDiffRef,
62-
containerWrapper: container,
63-
})
64-
65-
// Hydrate annotation slots with interactive SolidJS components
66-
// if (props.annotations.length > 0 && props.renderAnnotation != null) {
67-
// for (const annotation of props.annotations) {
68-
// const slotName = `annotation-${annotation.side}-${annotation.lineNumber}`;
69-
// const slotElement = fileDiffRef.querySelector(
70-
// `[slot="${slotName}"]`
71-
// ) as HTMLElement;
72-
//
73-
// if (slotElement != null) {
74-
// // Clear the static server-rendered content from the slot
75-
// slotElement.innerHTML = '';
76-
//
77-
// // Mount a fresh SolidJS component into this slot using render().
78-
// // This enables full SolidJS reactivity (signals, effects, etc.)
79-
// const dispose = render(
80-
// () => props.renderAnnotation!(annotation),
81-
// slotElement
82-
// );
83-
// cleanupFunctions.push(dispose);
84-
// }
85-
// }
86-
// }
87-
})
88-
8953
onCleanup(() => {
9054
// Clean up FileDiff event handlers and dispose SolidJS components
9155
fileDiffInstance?.cleanUp()
9256
cleanupFunctions.forEach((dispose) => dispose())
9357
})
9458

95-
return (
96-
<div data-component="diff" style={styleVariables} ref={container}>
97-
<file-diff ref={fileDiffRef} id="ssr-diff">
98-
<Show when={isServer && props.preloadedDiff}>
99-
{(preloadedDiff) => <template shadowrootmode="open" innerHTML={preloadedDiff().prerenderedHTML} />}
100-
</Show>
101-
</file-diff>
102-
</div>
103-
)
59+
return <div data-component="diff" style={styleVariables} ref={container} />
10460
}

0 commit comments

Comments
 (0)