Diffs Performance Improvements (#5653)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
[data-component="diff"] {
|
||||
contain: content;
|
||||
|
||||
[data-slot="diff-hunk-separator-line-number"] {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
contain: strict;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
10
packages/ui/src/context/worker-pool.tsx
Normal file
10
packages/ui/src/context/worker-pool.tsx
Normal 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
|
||||
5
packages/ui/src/custom-elements.d.ts
vendored
5
packages/ui/src/custom-elements.d.ts
vendored
@@ -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>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
},
|
||||
})
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user