summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/desktop/src/pages/layout.tsx4
-rw-r--r--packages/desktop/src/pages/session.tsx9
-rw-r--r--packages/enterprise/src/routes/share/[shareID].tsx460
-rw-r--r--packages/ui/package.json1
-rw-r--r--packages/ui/src/components/diff-ssr.tsx23
-rw-r--r--packages/ui/src/components/diff.css2
-rw-r--r--packages/ui/src/components/session-review.css1
-rw-r--r--packages/ui/src/context/worker-pool.tsx10
-rw-r--r--packages/ui/src/custom-elements.d.ts5
-rw-r--r--packages/ui/src/pierre/worker.ts36
10 files changed, 298 insertions, 253 deletions
diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx
index 618b84840..540c5d778 100644
--- a/packages/desktop/src/pages/layout.tsx
+++ b/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>
diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx
index 3415d0c4e..7d1392c20 100644
--- a/packages/desktop/src/pages/session.tsx
+++ b/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,
diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx
index 4f9f95c01..e2743344f 100644
--- a/packages/enterprise/src/routes/share/[shareID].tsx
+++ b/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>
</>
)
}}
diff --git a/packages/ui/package.json b/packages/ui/package.json
index b2e7e331d..618dbf1f0 100644
--- a/packages/ui/package.json
+++ b/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",
diff --git a/packages/ui/src/components/diff-ssr.tsx b/packages/ui/src/components/diff-ssr.tsx
index b38b4a34f..e367a4fbe 100644
--- a/packages/ui/src/components/diff-ssr.tsx
+++ b/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>
)
}
diff --git a/packages/ui/src/components/diff.css b/packages/ui/src/components/diff.css
index 345271a12..3251eb4c6 100644
--- a/packages/ui/src/components/diff.css
+++ b/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;
diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css
index 0cd228592..35df4a80f 100644
--- a/packages/ui/src/components/session-review.css
+++ b/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;
}
diff --git a/packages/ui/src/context/worker-pool.tsx b/packages/ui/src/context/worker-pool.tsx
new file mode 100644
index 000000000..fc2eecc03
--- /dev/null
+++ b/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
diff --git a/packages/ui/src/custom-elements.d.ts b/packages/ui/src/custom-elements.d.ts
index b756e51da..49ec4449f 100644
--- a/packages/ui/src/custom-elements.d.ts
+++ b/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>
}
}
}
diff --git a/packages/ui/src/pierre/worker.ts b/packages/ui/src/pierre/worker.ts
index e47268d4e..2d2640674 100644
--- a/packages/ui/src/pierre/worker.ts
+++ b/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"],
+ },
+ })
+})()