Просмотр исходного кода

Diffs Performance Improvements (#5653)

Co-authored-by: Adam <[email protected]>
Amadeus Demarzi 2 месяцев назад
Родитель
Сommit
5c490c51ed

+ 2 - 2
packages/desktop/src/pages/layout.tsx

@@ -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>

+ 6 - 3
packages/desktop/src/pages/session.tsx

@@ -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,

+ 236 - 224
packages/enterprise/src/routes/share/[shareID].tsx

@@ -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,256 +205,260 @@ 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()} />
-              <DiffComponentProvider component={ClientOnlyDiff}>
-                <CodeComponentProvider component={ClientOnlyCode}>
-                  <DataProvider data={data()} directory={info().directory}>
-                    {iife(() => {
-                      const [store, setStore] = createStore({
-                        messageId: undefined as string | undefined,
-                      })
-                      const messages = createMemo(() =>
-                        data().sessionID
-                          ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort(
-                              (a, b) => a.time.created - b.time.created,
-                            )
-                          : [],
-                      )
-                      const firstUserMessage = createMemo(() => messages().at(0))
-                      const activeMessage = createMemo(
-                        () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(),
-                      )
-                      function setActiveMessage(message: UserMessage | undefined) {
-                        if (message) {
-                          setStore("messageId", message.id)
-                        } else {
-                          setStore("messageId", undefined)
+              <ClientOnlyWorkerPoolProvider>
+                <DiffComponentProvider component={ClientOnlyDiff}>
+                  <CodeComponentProvider component={ClientOnlyCode}>
+                    <DataProvider data={data()} directory={info().directory}>
+                      {iife(() => {
+                        const [store, setStore] = createStore({
+                          messageId: undefined as string | undefined,
+                        })
+                        const messages = createMemo(() =>
+                          data().sessionID
+                            ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort(
+                                (a, b) => a.time.created - b.time.created,
+                              )
+                            : [],
+                        )
+                        const firstUserMessage = createMemo(() => messages().at(0))
+                        const activeMessage = createMemo(
+                          () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(),
+                        )
+                        function setActiveMessage(message: UserMessage | undefined) {
+                          if (message) {
+                            setStore("messageId", message.id)
+                          } else {
+                            setStore("messageId", undefined)
+                          }
                         }
-                      }
-                      const provider = createMemo(() => activeMessage()?.model?.providerID)
-                      const modelID = createMemo(() => activeMessage()?.model?.modelID)
-                      const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID()))
-                      const diffs = createMemo(() => {
-                        const diffs = data().session_diff[data().sessionID] ?? []
-                        const preloaded = data().session_diff_preload[data().sessionID] ?? []
-                        return diffs.map((diff) => ({
-                          ...diff,
-                          preloaded: preloaded.find((d) => d.newFile.name === diff.file),
-                        }))
-                      })
-                      const splitDiffs = createMemo(() => {
-                        const diffs = data().session_diff[data().sessionID] ?? []
-                        const preloaded = data().session_diff_preload_split[data().sessionID] ?? []
-                        return diffs.map((diff) => ({
-                          ...diff,
-                          preloaded: preloaded.find((d) => d.newFile.name === diff.file),
-                        }))
-                      })
+                        const provider = createMemo(() => activeMessage()?.model?.providerID)
+                        const modelID = createMemo(() => activeMessage()?.model?.modelID)
+                        const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID()))
+                        const diffs = createMemo(() => {
+                          const diffs = data().session_diff[data().sessionID] ?? []
+                          const preloaded = data().session_diff_preload[data().sessionID] ?? []
+                          return diffs.map((diff) => ({
+                            ...diff,
+                            preloaded: preloaded.find((d) => d.newFile.name === diff.file),
+                          }))
+                        })
+                        const splitDiffs = createMemo(() => {
+                          const diffs = data().session_diff[data().sessionID] ?? []
+                          const preloaded = data().session_diff_preload_split[data().sessionID] ?? []
+                          return diffs.map((diff) => ({
+                            ...diff,
+                            preloaded: preloaded.find((d) => d.newFile.name === diff.file),
+                          }))
+                        })
 
-                      const title = () => (
-                        <div class="flex flex-col gap-4">
-                          <div class="h-8 flex gap-4 items-center justify-start self-stretch">
-                            <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
-                              <Mark class="shrink-0 w-3 my-0.5" />
-                              <div class="text-12-mono text-text-base">v{info().version}</div>
-                            </div>
-                            <div class="flex gap-2 items-center">
-                              <ProviderIcon
-                                id={provider() as IconName}
-                                class="size-3.5 shrink-0 text-icon-strong-base"
-                              />
-                              <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
-                            </div>
-                            <div class="text-12-regular text-text-weaker">
-                              {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
+                        const title = () => (
+                          <div class="flex flex-col gap-4">
+                            <div class="h-8 flex gap-4 items-center justify-start self-stretch">
+                              <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
+                                <Mark class="shrink-0 w-3 my-0.5" />
+                                <div class="text-12-mono text-text-base">v{info().version}</div>
+                              </div>
+                              <div class="flex gap-2 items-center">
+                                <ProviderIcon
+                                  id={provider() as IconName}
+                                  class="size-3.5 shrink-0 text-icon-strong-base"
+                                />
+                                <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
+                              </div>
+                              <div class="text-12-regular text-text-weaker">
+                                {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
+                              </div>
                             </div>
+                            <div class="text-left text-16-medium text-text-strong">{info().title}</div>
                           </div>
-                          <div class="text-left text-16-medium text-text-strong">{info().title}</div>
-                        </div>
-                      )
+                        )
 
-                      const turns = () => (
-                        <div class="relative mt-2 pt-6 pb-8 min-w-0 w-full h-full overflow-y-auto no-scrollbar">
-                          <div class="px-4">{title()}</div>
-                          <div class="flex flex-col gap-15 items-start justify-start mt-4">
-                            <For each={messages()}>
-                              {(message) => (
-                                <SessionTurn
-                                  sessionID={data().sessionID}
-                                  messageID={message.id}
-                                  classes={{
-                                    root: "min-w-0 w-full relative",
-                                    content:
-                                      "flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
-                                    container: "px-4",
-                                  }}
-                                />
-                              )}
-                            </For>
-                          </div>
-                          <div class="px-4 flex items-center justify-center pt-20 pb-8 shrink-0">
-                            <Logo class="w-58.5 opacity-12" />
+                        const turns = () => (
+                          <div class="relative mt-2 pt-6 pb-8 min-w-0 w-full h-full overflow-y-auto no-scrollbar">
+                            <div class="px-4">{title()}</div>
+                            <div class="flex flex-col gap-15 items-start justify-start mt-4">
+                              <For each={messages()}>
+                                {(message) => (
+                                  <SessionTurn
+                                    sessionID={data().sessionID}
+                                    messageID={message.id}
+                                    classes={{
+                                      root: "min-w-0 w-full relative",
+                                      content:
+                                        "flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
+                                      container: "px-4",
+                                    }}
+                                  />
+                                )}
+                              </For>
+                            </div>
+                            <div class="px-4 flex items-center justify-center pt-20 pb-8 shrink-0">
+                              <Logo class="w-58.5 opacity-12" />
+                            </div>
                           </div>
-                        </div>
-                      )
+                        )
 
-                      const wide = createMemo(() => diffs().length === 0)
+                        const wide = createMemo(() => diffs().length === 0)
 
-                      return (
-                        <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
-                          <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base">
-                            <div class="">
-                              <a href="https://opencode.ai">
-                                <Mark />
-                              </a>
-                            </div>
-                            <div class="flex gap-3 items-center">
-                              <IconButton
-                                as={"a"}
-                                href="https://github.com/sst/opencode"
-                                target="_blank"
-                                icon="github"
-                                variant="ghost"
-                              />
-                              <IconButton
-                                as={"a"}
-                                href="https://opencode.ai/discord"
-                                target="_blank"
-                                icon="discord"
-                                variant="ghost"
-                              />
-                            </div>
-                          </header>
-                          <div class="select-text flex flex-col flex-1 min-h-0">
-                            <div
-                              classList={{
-                                "hidden w-full flex-1 min-h-0": true,
-                                "md:flex": wide(),
-                                "lg:flex": !wide(),
-                              }}
-                            >
+                        return (
+                          <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
+                            <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base">
+                              <div class="">
+                                <a href="https://opencode.ai">
+                                  <Mark />
+                                </a>
+                              </div>
+                              <div class="flex gap-3 items-center">
+                                <IconButton
+                                  as={"a"}
+                                  href="https://github.com/sst/opencode"
+                                  target="_blank"
+                                  icon="github"
+                                  variant="ghost"
+                                />
+                                <IconButton
+                                  as={"a"}
+                                  href="https://opencode.ai/discord"
+                                  target="_blank"
+                                  icon="discord"
+                                  variant="ghost"
+                                />
+                              </div>
+                            </header>
+                            <div class="select-text flex flex-col flex-1 min-h-0">
                               <div
                                 classList={{
-                                  "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full": true,
-                                  "mx-auto max-w-200": !wide(),
+                                  "hidden w-full flex-1 min-h-0": true,
+                                  "md:flex": wide(),
+                                  "lg:flex": !wide(),
                                 }}
                               >
                                 <div
                                   classList={{
-                                    "w-full flex justify-start items-start min-w-0": true,
-                                    "max-w-200 mx-auto px-6": wide(),
-                                    "pr-6 pl-18": !wide() && messages().length > 1,
-                                    "px-6": !wide() && messages().length === 1,
+                                    "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full": true,
+                                    "mx-auto max-w-200": !wide(),
                                   }}
                                 >
-                                  {title()}
-                                </div>
-                                <div class="flex items-start justify-start h-full min-h-0">
-                                  <SessionMessageRail
-                                    messages={messages()}
-                                    current={activeMessage()}
-                                    onMessageSelect={setActiveMessage}
-                                    wide={wide()}
-                                  />
-                                  <SessionTurn
-                                    sessionID={data().sessionID}
-                                    messageID={store.messageId ?? firstUserMessage()!.id!}
-                                    classes={{
-                                      root: "grow",
-                                      content: "flex flex-col justify-between",
-                                      container:
-                                        "w-full pb-20 " +
-                                        (wide()
-                                          ? "max-w-200 mx-auto px-6"
-                                          : messages().length > 1
-                                            ? "pr-6 pl-18"
-                                            : "px-6"),
+                                  <div
+                                    classList={{
+                                      "w-full flex justify-start items-start min-w-0": true,
+                                      "max-w-200 mx-auto px-6": wide(),
+                                      "pr-6 pl-18": !wide() && messages().length > 1,
+                                      "px-6": !wide() && messages().length === 1,
                                     }}
                                   >
-                                    <div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}>
-                                      <Logo class="w-58.5 opacity-12" />
-                                    </div>
-                                  </SessionTurn>
-                                </div>
-                              </div>
-                              <Show when={diffs().length > 0}>
-                                <DiffComponentProvider component={SSRDiff}>
-                                  <div class="@container relative grow pt-14 flex-1 min-h-0 border-l border-border-weak-base">
-                                    <SessionReview
-                                      class="@4xl:hidden"
-                                      diffs={diffs()}
-                                      classes={{
-                                        root: "pb-20",
-                                        header: "px-6",
-                                        container: "px-6",
-                                      }}
+                                    {title()}
+                                  </div>
+                                  <div class="flex items-start justify-start h-full min-h-0">
+                                    <SessionMessageRail
+                                      messages={messages()}
+                                      current={activeMessage()}
+                                      onMessageSelect={setActiveMessage}
+                                      wide={wide()}
                                     />
-                                    <SessionReview
-                                      split
-                                      class="hidden @4xl:flex"
-                                      diffs={splitDiffs()}
+                                    <SessionTurn
+                                      sessionID={data().sessionID}
+                                      messageID={store.messageId ?? firstUserMessage()!.id!}
                                       classes={{
-                                        root: "pb-20",
-                                        header: "px-6",
-                                        container: "px-6",
+                                        root: "grow",
+                                        content: "flex flex-col justify-between",
+                                        container:
+                                          "w-full pb-20 " +
+                                          (wide()
+                                            ? "max-w-200 mx-auto px-6"
+                                            : messages().length > 1
+                                              ? "pr-6 pl-18"
+                                              : "px-6"),
                                       }}
-                                    />
+                                    >
+                                      <div
+                                        classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}
+                                      >
+                                        <Logo class="w-58.5 opacity-12" />
+                                      </div>
+                                    </SessionTurn>
                                   </div>
-                                </DiffComponentProvider>
-                              </Show>
-                            </div>
-                            <Switch>
-                              <Match when={diffs().length > 0}>
-                                <Tabs classList={{ "md:hidden": wide(), "lg:hidden": !wide() }}>
-                                  <Tabs.List>
-                                    <Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
-                                      Session
-                                    </Tabs.Trigger>
-                                    <Tabs.Trigger
+                                </div>
+                                <Show when={diffs().length > 0}>
+                                  <DiffComponentProvider component={SSRDiff}>
+                                    <div class="@container relative grow pt-14 flex-1 min-h-0 border-l border-border-weak-base">
+                                      <SessionReview
+                                        class="@4xl:hidden"
+                                        diffs={diffs()}
+                                        classes={{
+                                          root: "pb-20",
+                                          header: "px-6",
+                                          container: "px-6",
+                                        }}
+                                      />
+                                      <SessionReview
+                                        split
+                                        class="hidden @4xl:flex"
+                                        diffs={splitDiffs()}
+                                        classes={{
+                                          root: "pb-20",
+                                          header: "px-6",
+                                          container: "px-6",
+                                        }}
+                                      />
+                                    </div>
+                                  </DiffComponentProvider>
+                                </Show>
+                              </div>
+                              <Switch>
+                                <Match when={diffs().length > 0}>
+                                  <Tabs classList={{ "md:hidden": wide(), "lg:hidden": !wide() }}>
+                                    <Tabs.List>
+                                      <Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
+                                        Session
+                                      </Tabs.Trigger>
+                                      <Tabs.Trigger
+                                        value="review"
+                                        class="w-1/2 !border-r-0"
+                                        classes={{ button: "w-full" }}
+                                      >
+                                        {diffs().length} Files Changed
+                                      </Tabs.Trigger>
+                                    </Tabs.List>
+                                    <Tabs.Content value="session" class="!overflow-hidden">
+                                      {turns()}
+                                    </Tabs.Content>
+                                    <Tabs.Content
+                                      forceMount
                                       value="review"
-                                      class="w-1/2 !border-r-0"
-                                      classes={{ button: "w-full" }}
+                                      class="!overflow-hidden hidden data-[selected]:block"
                                     >
-                                      {diffs().length} Files Changed
-                                    </Tabs.Trigger>
-                                  </Tabs.List>
-                                  <Tabs.Content value="session" class="!overflow-hidden">
-                                    {turns()}
-                                  </Tabs.Content>
-                                  <Tabs.Content
-                                    forceMount
-                                    value="review"
-                                    class="!overflow-hidden hidden data-[selected]:block"
+                                      <div class="relative h-full pt-8 overflow-y-auto no-scrollbar">
+                                        <DiffComponentProvider component={SSRDiff}>
+                                          <SessionReview
+                                            diffs={diffs()}
+                                            classes={{
+                                              root: "pb-20",
+                                              header: "px-4",
+                                              container: "px-4",
+                                            }}
+                                          />
+                                        </DiffComponentProvider>
+                                      </div>
+                                    </Tabs.Content>
+                                  </Tabs>
+                                </Match>
+                                <Match when={true}>
+                                  <div
+                                    classList={{ "!overflow-hidden": true, "md:hidden": wide(), "lg:hidden": !wide() }}
                                   >
-                                    <div class="relative h-full pt-8 overflow-y-auto no-scrollbar">
-                                      <DiffComponentProvider component={SSRDiff}>
-                                        <SessionReview
-                                          diffs={diffs()}
-                                          classes={{
-                                            root: "pb-20",
-                                            header: "px-4",
-                                            container: "px-4",
-                                          }}
-                                        />
-                                      </DiffComponentProvider>
-                                    </div>
-                                  </Tabs.Content>
-                                </Tabs>
-                              </Match>
-                              <Match when={true}>
-                                <div
-                                  classList={{ "!overflow-hidden": true, "md:hidden": wide(), "lg:hidden": !wide() }}
-                                >
-                                  {turns()}
-                                </div>
-                              </Match>
-                            </Switch>
+                                    {turns()}
+                                  </div>
+                                </Match>
+                              </Switch>
+                            </div>
                           </div>
-                        </div>
-                      )
-                    })}
-                  </DataProvider>
-                </CodeComponentProvider>
-              </DiffComponentProvider>
+                        )
+                      })}
+                    </DataProvider>
+                  </CodeComponentProvider>
+                </DiffComponentProvider>
+              </ClientOnlyWorkerPoolProvider>
             </>
           )
         }}

+ 1 - 0
packages/ui/package.json

@@ -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",

+ 14 - 9
packages/ui/src/components/diff-ssr.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>({
-      ...createDefaultOptions(props.diffStyle),
-      ...others,
-      ...props.preloadedDiff,
-    })
+    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>
   )
 }

+ 2 - 0
packages/ui/src/components/diff.css

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

+ 1 - 0
packages/ui/src/components/session-review.css

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

+ 10 - 0
packages/ui/src/context/worker-pool.tsx

@@ -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

+ 4 - 1
packages/ui/src/custom-elements.d.ts

@@ -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>
     }
   }
 }

+ 22 - 14
packages/ui/src/pierre/worker.ts

@@ -1,20 +1,28 @@
-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({
-  poolOptions: {
-    workerFactory,
-    // poolSize defaults to 8. More workers = more parallelism but
-    // also more memory. Too many can actually slow things down.
-    // poolSize: 8,
-  },
-  highlighterOptions: {
-    theme: "OpenCode",
-    // Optionally preload languages to avoid lazy-loading delays
-    // langs: ["typescript", "javascript", "css", "html"],
-  },
-})
+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.
+      // 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",
+      // Optionally preload languages to avoid lazy-loading delays
+      // langs: ["typescript", "javascript", "css", "html"],
+    },
+  })
+})()