Diffs Performance Improvements (#5653)

Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
This commit is contained in:
Amadeus Demarzi
2025-12-17 05:33:46 -08:00
committed by GitHub
parent 5da1c0087b
commit 5c490c51ed
10 changed files with 305 additions and 260 deletions

View File

@@ -613,7 +613,7 @@ export default function Layout(props: ParentProps) {
classList={{
"relative @container w-12 pb-5 shrink-0 bg-background-base": true,
"flex flex-col gap-5.5 items-start self-stretch justify-between": true,
"border-r border-border-weak-base": true,
"border-r border-border-weak-base contain-strict": true,
}}
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
>
@@ -755,7 +755,7 @@ export default function Layout(props: ParentProps) {
</Tooltip>
</div>
</div>
<main class="size-full overflow-x-hidden flex flex-col items-start">{props.children}</main>
<main class="size-full overflow-x-hidden flex flex-col items-start contain-strict">{props.children}</main>
</div>
<Toast.Region />
</div>

View File

@@ -578,7 +578,10 @@ export default function Page() {
</div>
</Tabs.List>
</div>
<Tabs.Content value="chat" class="@container select-text flex flex-col flex-1 min-h-0 overflow-y-hidden">
<Tabs.Content
value="chat"
class="@container select-text flex flex-col flex-1 min-h-0 overflow-y-hidden contain-strict"
>
<div
classList={{
"w-full flex-1 min-h-0": true,
@@ -661,7 +664,7 @@ export default function Page() {
<Show when={layout.review.state() === "pane" && diffs().length}>
<div
classList={{
"relative grow pt-3 flex-1 min-h-0 border-l border-border-weak-base": true,
"relative grow pt-3 flex-1 min-h-0 border-l border-border-weak-base contain-strict": true,
}}
>
<SessionReview
@@ -689,7 +692,7 @@ export default function Page() {
</div>
</Tabs.Content>
<Show when={layout.review.state() === "tab" && diffs().length}>
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden">
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden contain-strict">
<div
classList={{
"relative pt-3 flex-1 min-h-0 overflow-hidden": true,

View File

@@ -4,6 +4,7 @@ import { SessionReview } from "@opencode-ai/ui/session-review"
import { DataProvider } from "@opencode-ai/ui/context"
import { DiffComponentProvider } from "@opencode-ai/ui/context/diff"
import { CodeComponentProvider } from "@opencode-ai/ui/context/code"
import { WorkerPoolProvider } from "@opencode-ai/ui/context/worker-pool"
import { createAsync, query, useParams } from "@solidjs/router"
import { createEffect, createMemo, ErrorBoundary, For, Match, Show, Switch } from "solid-js"
import { Share } from "~/core/share"
@@ -29,6 +30,13 @@ import { Base64 } from "js-base64"
const ClientOnlyDiff = clientOnly(() => import("@opencode-ai/ui/diff").then((m) => ({ default: m.Diff })))
const ClientOnlyCode = clientOnly(() => import("@opencode-ai/ui/code").then((m) => ({ default: m.Code })))
const ClientOnlyWorkerPoolProvider = clientOnly(() =>
import("@opencode-ai/ui/pierre/worker").then((m) => ({
default: (props: { children: any }) => (
<WorkerPoolProvider pool={m.workerPool}>{props.children}</WorkerPoolProvider>
),
})),
)
const SessionDataMissingError = NamedError.create(
"SessionDataMissingError",
@@ -197,6 +205,7 @@ export default function () {
<Meta name="description" content="opencode - The AI coding agent built for the terminal." />
<Meta property="og:image" content={ogImage()} />
<Meta name="twitter:image" content={ogImage()} />
<ClientOnlyWorkerPoolProvider>
<DiffComponentProvider component={ClientOnlyDiff}>
<CodeComponentProvider component={ClientOnlyCode}>
<DataProvider data={data()} directory={info().directory}>
@@ -362,7 +371,9 @@ export default function () {
: "px-6"),
}}
>
<div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}>
<div
classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}
>
<Logo class="w-58.5 opacity-12" />
</div>
</SessionTurn>
@@ -447,6 +458,7 @@ export default function () {
</DataProvider>
</CodeComponentProvider>
</DiffComponentProvider>
</ClientOnlyWorkerPoolProvider>
</>
)
}}

View File

@@ -5,6 +5,7 @@
"exports": {
"./*": "./src/components/*.tsx",
"./pierre": "./src/pierre/index.ts",
"./pierre/*": "./src/pierre/*.ts",
"./hooks": "./src/hooks/index.ts",
"./context": "./src/context/index.ts",
"./context/*": "./src/context/*.tsx",

View File

@@ -1,8 +1,9 @@
import { FileDiff } from "@pierre/diffs"
import { DIFFS_TAG_NAME, FileDiff } from "@pierre/diffs"
import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr"
import { onCleanup, onMount, Show, splitProps } from "solid-js"
import { isServer } from "solid-js/web"
import { Dynamic, isServer } from "solid-js/web"
import { createDefaultOptions, styleVariables, type DiffProps } from "../pierre"
import { useWorkerPool } from "../context/worker-pool"
export type SSRDiffProps<T = {}> = DiffProps<T> & {
preloadedDiff: PreloadMultiFileDiffResult<T>
@@ -12,17 +13,21 @@ export function Diff<T>(props: SSRDiffProps<T>) {
let container!: HTMLDivElement
let fileDiffRef!: HTMLElement
const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"])
const workerPool = useWorkerPool()
let fileDiffInstance: FileDiff<T> | undefined
const cleanupFunctions: Array<() => void> = []
onMount(() => {
if (isServer || !props.preloadedDiff) return
fileDiffInstance = new FileDiff<T>({
fileDiffInstance = new FileDiff<T>(
{
...createDefaultOptions(props.diffStyle),
...others,
...props.preloadedDiff,
})
},
workerPool,
)
// @ts-expect-error - fileContainer is private but needed for SSR hydration
fileDiffInstance.fileContainer = fileDiffRef
fileDiffInstance.hydrate({
@@ -65,11 +70,11 @@ export function Diff<T>(props: SSRDiffProps<T>) {
return (
<div data-component="diff" style={styleVariables} ref={container}>
<diffs-container ref={fileDiffRef} id="ssr-diff">
<Dynamic component={DIFFS_TAG_NAME} ref={fileDiffRef} id="ssr-diff">
<Show when={isServer}>
<template shadowrootmode="open" innerHTML={props.preloadedDiff.prerenderedHTML} />
</Show>
</diffs-container>
</Dynamic>
</div>
)
}

View File

@@ -1,4 +1,6 @@
[data-component="diff"] {
contain: content;
[data-slot="diff-hunk-separator-line-number"] {
position: sticky;
left: 0;

View File

@@ -5,6 +5,7 @@
height: 100%;
overflow-y: auto;
scrollbar-width: none;
contain: strict;
&::-webkit-scrollbar {
display: none;
}

View File

@@ -0,0 +1,10 @@
import type { WorkerPoolManager } from "@pierre/diffs/worker"
import { createSimpleContext } from "./helper"
const ctx = createSimpleContext<WorkerPoolManager | undefined, { pool: WorkerPoolManager | undefined }>({
name: "WorkerPool",
init: (props) => props.pool,
})
export const WorkerPoolProvider = ctx.provider
export const useWorkerPool = ctx.use

View File

@@ -1,12 +1,15 @@
import { DIFFS_TAG_NAME } from "@pierre/diffs"
/**
* TypeScript declaration for the <diffs-container> custom element.
* This tells TypeScript that <diffs-container> is a valid JSX element in SolidJS.
* Required for using the @pierre/diffs web component in .tsx files.
*/
declare module "solid-js" {
namespace JSX {
interface IntrinsicElements {
"diffs-container": HTMLAttributes<HTMLElement>
[DIFFS_TAG_NAME]: HTMLAttributes<HTMLElement>
}
}
}

View File

@@ -1,16 +1,23 @@
import { getOrCreateWorkerPoolSingleton } from "@pierre/diffs/worker"
import { getOrCreateWorkerPoolSingleton, WorkerPoolManager } from "@pierre/diffs/worker"
import ShikiWorkerUrl from "@pierre/diffs/worker/worker.js?worker&url"
export function workerFactory(): Worker {
return new Worker(ShikiWorkerUrl, { type: "module" })
}
export const workerPool = getOrCreateWorkerPoolSingleton({
export const workerPool: WorkerPoolManager | undefined = (() => {
if (typeof window === "undefined") {
return undefined
}
return getOrCreateWorkerPoolSingleton({
poolOptions: {
workerFactory,
// poolSize defaults to 8. More workers = more parallelism but
// also more memory. Too many can actually slow things down.
// poolSize: 8,
// NOTE: 2 is probably better for OpenCode, as I think 8 might be
// a bit overkill, especially because Safari has a significantly slower
// boot up time for workers
poolSize: 2,
},
highlighterOptions: {
theme: "OpenCode",
@@ -18,3 +25,4 @@ export const workerPool = getOrCreateWorkerPoolSingleton({
// langs: ["typescript", "javascript", "css", "html"],
},
})
})()