diff --git a/bun.lock b/bun.lock index 34793cde8..9bd91760b 100644 --- a/bun.lock +++ b/bun.lock @@ -905,26 +905,26 @@ }, }, "trustedDependencies": [ + "esbuild", "tree-sitter-powershell", + "protobufjs", + "electron", "web-tree-sitter", "tree-sitter-bash", - "esbuild", - "electron", - "protobufjs", ], "patchedDependencies": { - "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", "@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch", - "@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch", - "pacote@21.5.0": "patches/pacote@21.5.0.patch", - "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", - "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", + "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", + "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", "@tanstack/solid-virtual@3.13.28": "patches/@tanstack%2Fsolid-virtual@3.13.28.patch", + "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", + "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", + "@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch", "@tanstack/virtual-core@3.17.0": "patches/@tanstack%2Fvirtual-core@3.17.0.patch", - "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", + "pacote@21.5.0": "patches/pacote@21.5.0.patch", }, "overrides": { "@opentui/core": "catalog:", diff --git a/packages/app/e2e/regression/session-timeline-collapse-state.spec.ts b/packages/app/e2e/regression/session-timeline-collapse-state.spec.ts index c902e7faf..7935b1001 100644 --- a/packages/app/e2e/regression/session-timeline-collapse-state.spec.ts +++ b/packages/app/e2e/regression/session-timeline-collapse-state.spec.ts @@ -196,7 +196,8 @@ test.describe("regression: session timeline local row state", () => { const events: EventPayload[] = [] const lines = Array.from({ length: 1_000 }, (_, index) => `export const value${index} = ${index}\n`).join("") const after = [100, 300, 500, 700, 900].reduce( - (result, index) => result.replace(`export const value${index} = ${index}`, `export const value${index} = compute(${index})`), + (result, index) => + result.replace(`export const value${index} = ${index}`, `export const value${index} = compute(${index})`), lines, ) const part = { @@ -385,11 +386,7 @@ function readExpanded(element: Element) { return !!content && content.getBoundingClientRect().height > 0 } -async function mockServer( - page: Page, - events: EventPayload[], - messages = [userMessage, assistantMessage], -) { +async function mockServer(page: Page, events: EventPayload[], messages = [userMessage, assistantMessage]) { await mockOpenCodeServer(page, { directory, project: project(), diff --git a/packages/app/e2e/smoke/session-timeline.spec.ts b/packages/app/e2e/smoke/session-timeline.spec.ts index de5eeb20c..925614cc2 100644 --- a/packages/app/e2e/smoke/session-timeline.spec.ts +++ b/packages/app/e2e/smoke/session-timeline.spec.ts @@ -172,7 +172,9 @@ test.describe("smoke: session timeline", () => { }) .map((element) => element.dataset.messageId!) .filter((id) => ids.has(id)) - const bottom = root.querySelector('[data-timeline-row="bottom-spacer"]')?.getBoundingClientRect() + const bottom = root + .querySelector('[data-timeline-row="bottom-spacer"]') + ?.getBoundingClientRect() samples.push({ ids: visible, last: visible.includes(last), bottomError: bottom?.bottom - view.bottom }) if (!firstPaint && visible.includes(last) && Math.abs((bottom?.bottom ?? Infinity) - view.bottom) <= 1) { firstPaint = true @@ -187,9 +189,11 @@ test.describe("smoke: session timeline", () => { requestAnimationFrame(sample) }, 0) } - ;(window as Window & { - __sessionTabPaint?: { samples: typeof samples; removed: () => number; stop: () => void } - }).__sessionTabPaint = { + ;( + window as Window & { + __sessionTabPaint?: { samples: typeof samples; removed: () => number; stop: () => void } + } + ).__sessionTabPaint = { samples, removed: () => removedFirstPaintNodes, stop: () => { @@ -203,19 +207,21 @@ test.describe("smoke: session timeline", () => { await switchTitlebarSession(page, fixture.targetID, fixture.expected.targetTitle) await page.waitForFunction(() => - (window as Window & { __sessionTabPaint?: { samples: Array<{ ids: string[] }> } }).__sessionTabPaint?.samples.some( - (sample) => sample.ids.length > 0, - ), + ( + window as Window & { __sessionTabPaint?: { samples: Array<{ ids: string[] }> } } + ).__sessionTabPaint?.samples.some((sample) => sample.ids.length > 0), ) await page.waitForTimeout(200) const first = await page.evaluate(() => { - const probe = (window as Window & { - __sessionTabPaint?: { - samples: Array<{ ids: string[]; last: boolean; bottomError?: number }> - removed: () => number - stop: () => void + const probe = ( + window as Window & { + __sessionTabPaint?: { + samples: Array<{ ids: string[]; last: boolean; bottomError?: number }> + removed: () => number + stop: () => void + } } - }).__sessionTabPaint! + ).__sessionTabPaint! probe.stop() return { first: probe.samples.find((sample) => sample.ids.length > 0), removed: probe.removed() } }) @@ -253,31 +259,36 @@ test.describe("smoke: session timeline", () => { await expectSessionTitle(page, fixture.expected.sourceTitle) const last = fixture.expected.targetMessageIDs.at(-1)! const destination = fixture.messages[fixture.targetID].map((message) => message.info.id) - await page.evaluate(({ destination, last }) => { - const ids = new Set(destination) - const samples: Array<{ destination: boolean; last: boolean; bottomError?: number }> = [] - const sample = () => { - const root = [...document.querySelectorAll(".scroll-view__viewport")].find((element) => - element.querySelector("[data-timeline-row]"), - ) - if (root) { - const view = root.getBoundingClientRect() - const spacer = root.querySelector('[data-timeline-row="bottom-spacer"]')?.getBoundingClientRect() - const messages = [...root.querySelectorAll("[data-message-id]")].filter((element) => { - const rect = element.getBoundingClientRect() - return rect.bottom > view.top && rect.top < view.bottom - }) - samples.push({ - destination: messages.some((element) => ids.has(element.dataset.messageId!)), - last: messages.some((element) => element.dataset.messageId === last), - bottomError: spacer ? spacer.bottom - view.bottom : undefined, - }) + await page.evaluate( + ({ destination, last }) => { + const ids = new Set(destination) + const samples: Array<{ destination: boolean; last: boolean; bottomError?: number }> = [] + const sample = () => { + const root = [...document.querySelectorAll(".scroll-view__viewport")].find((element) => + element.querySelector("[data-timeline-row]"), + ) + if (root) { + const view = root.getBoundingClientRect() + const spacer = root + .querySelector('[data-timeline-row="bottom-spacer"]') + ?.getBoundingClientRect() + const messages = [...root.querySelectorAll("[data-message-id]")].filter((element) => { + const rect = element.getBoundingClientRect() + return rect.bottom > view.top && rect.top < view.bottom + }) + samples.push({ + destination: messages.some((element) => ids.has(element.dataset.messageId!)), + last: messages.some((element) => element.dataset.messageId === last), + bottomError: spacer ? spacer.bottom - view.bottom : undefined, + }) + } + requestAnimationFrame(() => setTimeout(sample, 0)) } + ;(window as Window & { __coldTabSamples?: typeof samples }).__coldTabSamples = samples requestAnimationFrame(() => setTimeout(sample, 0)) - } - ;(window as Window & { __coldTabSamples?: typeof samples }).__coldTabSamples = samples - requestAnimationFrame(() => setTimeout(sample, 0)) - }, { destination, last }) + }, + { destination, last }, + ) await switchTitlebarSession(page, fixture.targetID, fixture.expected.targetTitle) await page.waitForFunction(() => @@ -286,9 +297,11 @@ test.describe("smoke: session timeline", () => { ), ) const result = await page.evaluate(() => { - const samples = (window as Window & { - __coldTabSamples?: Array<{ destination: boolean; last: boolean; bottomError?: number }> - }).__coldTabSamples! + const samples = ( + window as Window & { + __coldTabSamples?: Array<{ destination: boolean; last: boolean; bottomError?: number }> + } + ).__coldTabSamples! return samples.find((sample) => sample.destination)! }) expect(result.last).toBe(true) diff --git a/packages/app/src/pages/session/timeline/message-timeline.tsx b/packages/app/src/pages/session/timeline/message-timeline.tsx index 5c9de3d93..5a26f1781 100644 --- a/packages/app/src/pages/session/timeline/message-timeline.tsx +++ b/packages/app/src/pages/session/timeline/message-timeline.tsx @@ -82,10 +82,7 @@ type FramedTimelineRow = Exclude type TimelineRowByTag = Extract const timelineFallbackItemSize = 60 -const timelineCache = new Map< - string, - { measurements: VirtualItem[]; toolOpen: Record } ->() +const timelineCache = new Map }>() const taskDescription = (part: PartType, sessionID: string) => { if (part.type !== "tool" || part.tool !== "task") return diff --git a/packages/app/src/pages/session/timeline/model.ts b/packages/app/src/pages/session/timeline/model.ts index 0514e4975..7154827f9 100644 --- a/packages/app/src/pages/session/timeline/model.ts +++ b/packages/app/src/pages/session/timeline/model.ts @@ -55,11 +55,7 @@ export function createTimelineModel(input: { const id = input.sessionID() return !id || sync().data.message[id] !== undefined }) - const userMessages = createMemo( - () => selectUserMessages(messages()), - emptyUserMessages, - { equals: same }, - ) + const userMessages = createMemo(() => selectUserMessages(messages()), emptyUserMessages, { equals: same }) const visibleUserMessages = createMemo( () => { return selectVisibleUserMessages(userMessages(), input.revertMessageID()) diff --git a/packages/app/src/pages/session/timeline/projection.ts b/packages/app/src/pages/session/timeline/projection.ts index dfa924923..8c8489900 100644 --- a/packages/app/src/pages/session/timeline/projection.ts +++ b/packages/app/src/pages/session/timeline/projection.ts @@ -27,9 +27,12 @@ export function createTimelineProjection(input: { return result }) const activeMessageID = createMemo(() => { - const parentID = input.messages().findLast( - (message): message is AssistantMessage => message.role === "assistant" && typeof message.time.completed !== "number", - )?.parentID + const parentID = input + .messages() + .findLast( + (message): message is AssistantMessage => + message.role === "assistant" && typeof message.time.completed !== "number", + )?.parentID if (parentID) { const messages = input.messages() const result = Binary.search(messages, parentID, (message) => message.id) diff --git a/packages/ui/src/components/markdown-stream.test.ts b/packages/ui/src/components/markdown-stream.test.ts index 167278610..e792a3383 100644 --- a/packages/ui/src/components/markdown-stream.test.ts +++ b/packages/ui/src/components/markdown-stream.test.ts @@ -123,8 +123,12 @@ describe("markdown stream", () => { }) test("only reuses pending blocks with compatible identity and content", () => { - expect(canReusePendingBlock({ mode: "full", raw: "First\n\n" }, { mode: "full", raw: "# Inserted\n\n", src: "", })).toBe(false) - expect(canReusePendingBlock({ mode: "code", raw: "```ts\none" }, { mode: "code", raw: "```ts\none two", src: "" })).toBe(true) + expect( + canReusePendingBlock({ mode: "full", raw: "First\n\n" }, { mode: "full", raw: "# Inserted\n\n", src: "" }), + ).toBe(false) + expect( + canReusePendingBlock({ mode: "code", raw: "```ts\none" }, { mode: "code", raw: "```ts\none two", src: "" }), + ).toBe(true) expect(canReusePendingBlock({ mode: "code", raw: "```ts\none" }, { mode: "live", raw: "one", src: "" })).toBe(false) }) diff --git a/packages/ui/src/components/markdown-worker-protocol.test.ts b/packages/ui/src/components/markdown-worker-protocol.test.ts index 0c84ac557..45a169c6a 100644 --- a/packages/ui/src/components/markdown-worker-protocol.test.ts +++ b/packages/ui/src/components/markdown-worker-protocol.test.ts @@ -1,5 +1,9 @@ import { expect, test } from "bun:test" -import { applyMarkdownWorkerResponse, markdownBlockKey, shouldReleaseMarkdownWorkerState } from "./markdown-worker-protocol" +import { + applyMarkdownWorkerResponse, + markdownBlockKey, + shouldReleaseMarkdownWorkerState, +} from "./markdown-worker-protocol" const token = (content: string): [string, string] => [content, ""] const response = (id: number, reset: boolean, stable: [string, string][], unstable: [string, string][]) => ({ diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 018a6a7dd..78cff8abd 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -3,7 +3,16 @@ import { useI18n } from "../context/i18n" import DOMPurify from "dompurify" import morphdom from "morphdom" import { checksum } from "@opencode-ai/core/util/encode" -import { ComponentProps, createEffect, createMemo, createResource, createSignal, createUniqueId, onCleanup, splitProps } from "solid-js" +import { + ComponentProps, + createEffect, + createMemo, + createResource, + createSignal, + createUniqueId, + onCleanup, + splitProps, +} from "solid-js" import { isServer } from "solid-js/web" import { bundledLanguages } from "shiki" import { canReusePendingBlock, project, type Block, type Projection } from "./markdown-stream" @@ -439,9 +448,9 @@ export function Markdown( nextCodeKeys.forEach((key) => activeCodeKeys.add(key)) content.forEach((block, index) => updateBlock(container, index, block, labels)) while (container.children.length > content.length) container.lastElementChild?.remove() - container.querySelectorAll('[data-slot="markdown-copy-button"]').forEach((button) => - setCopyState(button, labels, button.dataset.copied === "true"), - ) + container + .querySelectorAll('[data-slot="markdown-copy-button"]') + .forEach((button) => setCopyState(button, labels, button.dataset.copied === "true")) if (!copyCleanup) copyCleanup = setupCodeCopy(container, () => ({ copy: i18n.t("ui.message.copy"), @@ -549,8 +558,7 @@ function updateCodeBlock( block: Extract, labels: CopyLabels, ) { - const existing = - current instanceof HTMLDivElement && current.dataset.markdownKey === block.key ? current : undefined + const existing = current instanceof HTMLDivElement && current.dataset.markdownKey === block.key ? current : undefined const next = existing ?? document.createElement("div") next.dataset.markdownBlock = "" next.dataset.markdownKey = block.key @@ -574,7 +582,10 @@ function updateCodeBlock( const prefix = prior.findIndex((token, index) => !sameToken(token, tail[index])) const keep = stableCount + (prefix < 0 ? Math.min(prior.length, tail.length) : prefix) while (code.children.length > keep) code.lastElementChild?.remove() - tail.slice(keep - stableCount).map(createTokenSpan).forEach((span) => code.appendChild(span)) + tail + .slice(keep - stableCount) + .map(createTokenSpan) + .forEach((span) => code.appendChild(span)) renderedCodeTokens.set(next, { language: block.language, generation: block.generation, diff --git a/packages/ui/src/components/scroll-view.tsx b/packages/ui/src/components/scroll-view.tsx index f8a4f18b7..9944da77d 100644 --- a/packages/ui/src/components/scroll-view.tsx +++ b/packages/ui/src/components/scroll-view.tsx @@ -38,10 +38,7 @@ export function scrollTopFromThumbPointer(input: { const padding = 8 const maxThumbTop = input.clientHeight - padding * 2 - input.thumbHeight if (maxThumbTop <= 0) return 0 - const thumbTop = Math.max( - 0, - Math.min(input.pointer - input.viewportTop - padding - input.grabOffset, maxThumbTop), - ) + const thumbTop = Math.max(0, Math.min(input.pointer - input.viewportTop - padding - input.grabOffset, maxThumbTop)) return (thumbTop / maxThumbTop) * Math.max(0, input.scrollHeight - input.clientHeight) } diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx index 762daaf29..e93c74a0c 100644 --- a/packages/ui/src/context/marked.tsx +++ b/packages/ui/src/context/marked.tsx @@ -7,374 +7,374 @@ import { createSimpleContext } from "./helper" import { getSharedHighlighter, registerCustomTheme, ThemeRegistrationResolved } from "@pierre/diffs" export const OpenCodeTheme = { - name: "OpenCode", - bg: "var(--color-background-stronger)", - fg: "var(--text-base)", - colors: { - "editor.background": "var(--color-background-stronger)", - "editor.foreground": "var(--text-base)", - "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", - "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", - "gitDecoration.modifiedResourceForeground": "var(--syntax-diff-unknown)", - // "gitDecoration.conflictingResourceForeground": "#ffca00", - // "gitDecoration.modifiedResourceForeground": "#1a76d4", - // "gitDecoration.untrackedResourceForeground": "#00cab1", - // "gitDecoration.ignoredResourceForeground": "#84848A", - // "terminal.titleForeground": "#adadb1", - // "terminal.titleInactiveForeground": "#84848A", - // "terminal.background": "#141415", - // "terminal.foreground": "#adadb1", - // "terminal.ansiBlack": "#141415", - // "terminal.ansiRed": "#ff2e3f", - // "terminal.ansiGreen": "#0dbe4e", - // "terminal.ansiYellow": "#ffca00", - // "terminal.ansiBlue": "#008cff", - // "terminal.ansiMagenta": "#c635e4", - // "terminal.ansiCyan": "#08c0ef", - // "terminal.ansiWhite": "#c6c6c8", - // "terminal.ansiBrightBlack": "#141415", - // "terminal.ansiBrightRed": "#ff2e3f", - // "terminal.ansiBrightGreen": "#0dbe4e", - // "terminal.ansiBrightYellow": "#ffca00", - // "terminal.ansiBrightBlue": "#008cff", - // "terminal.ansiBrightMagenta": "#c635e4", - // "terminal.ansiBrightCyan": "#08c0ef", - // "terminal.ansiBrightWhite": "#c6c6c8", + name: "OpenCode", + bg: "var(--color-background-stronger)", + fg: "var(--text-base)", + colors: { + "editor.background": "var(--color-background-stronger)", + "editor.foreground": "var(--text-base)", + "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", + "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", + "gitDecoration.modifiedResourceForeground": "var(--syntax-diff-unknown)", + // "gitDecoration.conflictingResourceForeground": "#ffca00", + // "gitDecoration.modifiedResourceForeground": "#1a76d4", + // "gitDecoration.untrackedResourceForeground": "#00cab1", + // "gitDecoration.ignoredResourceForeground": "#84848A", + // "terminal.titleForeground": "#adadb1", + // "terminal.titleInactiveForeground": "#84848A", + // "terminal.background": "#141415", + // "terminal.foreground": "#adadb1", + // "terminal.ansiBlack": "#141415", + // "terminal.ansiRed": "#ff2e3f", + // "terminal.ansiGreen": "#0dbe4e", + // "terminal.ansiYellow": "#ffca00", + // "terminal.ansiBlue": "#008cff", + // "terminal.ansiMagenta": "#c635e4", + // "terminal.ansiCyan": "#08c0ef", + // "terminal.ansiWhite": "#c6c6c8", + // "terminal.ansiBrightBlack": "#141415", + // "terminal.ansiBrightRed": "#ff2e3f", + // "terminal.ansiBrightGreen": "#0dbe4e", + // "terminal.ansiBrightYellow": "#ffca00", + // "terminal.ansiBrightBlue": "#008cff", + // "terminal.ansiBrightMagenta": "#c635e4", + // "terminal.ansiBrightCyan": "#08c0ef", + // "terminal.ansiBrightWhite": "#c6c6c8", + }, + tokenColors: [ + { + scope: ["comment", "punctuation.definition.comment", "string.comment"], + settings: { + foreground: "var(--syntax-comment)", + }, }, - tokenColors: [ - { - scope: ["comment", "punctuation.definition.comment", "string.comment"], - settings: { - foreground: "var(--syntax-comment)", - }, + { + scope: ["entity.other.attribute-name"], + settings: { + foreground: "var(--syntax-property)", // maybe attribute }, - { - scope: ["entity.other.attribute-name"], - settings: { - foreground: "var(--syntax-property)", // maybe attribute - }, - }, - { - scope: ["constant", "entity.name.constant", "variable.other.constant", "variable.language", "entity"], - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: ["entity.name", "meta.export.default", "meta.definition.variable"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: ["meta.object.member"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "variable.parameter.function", - "meta.jsx.children", - "meta.block", - "meta.tag.attributes", - "entity.name.constant", - "meta.embedded.expression", - "meta.template.expression", - "string.other.begin.yaml", - "string.other.end.yaml", - ], - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["entity.name.function", "support.type.primitive"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: ["support.class.component"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: "keyword", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "keyword.operator", - "storage.type.function.arrow", - "punctuation.separator.key-value.css", - "entity.name.tag.yaml", - "punctuation.separator.key-value.mapping.yaml", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["storage", "storage.type"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "string", - "punctuation.definition.string", - "string punctuation.section.embedded source", - "entity.name.tag", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "support", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: ["support.type.object.module", "variable.other.object", "support.type.property-name.css"], - settings: { - foreground: "var(--syntax-object)", - }, - }, - { - scope: "meta.property-name", - settings: { - foreground: "var(--syntax-property)", - }, - }, - { - scope: "variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "variable.other", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "invalid.broken", - "invalid.illegal", - "invalid.unimplemented", - "invalid.deprecated", - "message.error", - "markup.deleted", - "meta.diff.header.from-file", - "punctuation.definition.deleted", - "brackethighlighter.unmatched", - "token.error-token", - ], - settings: { - foreground: "var(--syntax-critical)", - }, - }, - { - scope: "carriage-return", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "string source", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "string variable", - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: [ - "source.regexp", - "string.regexp", - "string.regexp.character-class", - "string.regexp constant.character.escape", - "string.regexp source.ruby.embedded", - "string.regexp string.regexp.arbitrary-repitition", - "string.regexp constant.character.escape", - ], - settings: { - foreground: "var(--syntax-regexp)", - }, - }, - { - scope: "support.constant", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: "support.variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "meta.module-reference", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["markup.heading", "markup.heading entity.name"], - settings: { - fontStyle: "bold", - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.quote", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.italic", - settings: { - fontStyle: "italic", - // foreground: "", - }, - }, - { - scope: "markup.bold", - settings: { - fontStyle: "bold", - foreground: "var(--text-strong)", - }, - }, - { - scope: [ - "markup.raw", - "markup.inserted", - "meta.diff.header.to-file", - "punctuation.definition.inserted", - "markup.changed", - "punctuation.definition.changed", - "markup.ignored", - "markup.untracked", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "meta.diff.range", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.diff.header", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.separator", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.output", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.export.default", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: [ - "brackethighlighter.tag", - "brackethighlighter.curly", - "brackethighlighter.round", - "brackethighlighter.square", - "brackethighlighter.angle", - "brackethighlighter.quote", - ], - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: ["constant.other.reference.link", "string.other.link"], - settings: { - fontStyle: "underline", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "var(--syntax-warning)", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, - ], - semanticTokenColors: { - comment: "var(--syntax-comment)", - string: "var(--syntax-string)", - number: "var(--syntax-constant)", - regexp: "var(--syntax-regexp)", - keyword: "var(--syntax-keyword)", - variable: "var(--syntax-variable)", - parameter: "var(--syntax-variable)", - property: "var(--syntax-property)", - function: "var(--syntax-primitive)", - method: "var(--syntax-primitive)", - type: "var(--syntax-type)", - class: "var(--syntax-type)", - namespace: "var(--syntax-type)", - enumMember: "var(--syntax-primitive)", - "variable.constant": "var(--syntax-constant)", - "variable.defaultLibrary": "var(--syntax-unknown)", }, + { + scope: ["constant", "entity.name.constant", "variable.other.constant", "variable.language", "entity"], + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: ["entity.name", "meta.export.default", "meta.definition.variable"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: ["meta.object.member"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "variable.parameter.function", + "meta.jsx.children", + "meta.block", + "meta.tag.attributes", + "entity.name.constant", + "meta.embedded.expression", + "meta.template.expression", + "string.other.begin.yaml", + "string.other.end.yaml", + ], + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["entity.name.function", "support.type.primitive"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: ["support.class.component"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: "keyword", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: [ + "keyword.operator", + "storage.type.function.arrow", + "punctuation.separator.key-value.css", + "entity.name.tag.yaml", + "punctuation.separator.key-value.mapping.yaml", + ], + settings: { + foreground: "var(--syntax-operator)", + }, + }, + { + scope: ["storage", "storage.type"], + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "string", + "punctuation.definition.string", + "string punctuation.section.embedded source", + "entity.name.tag", + ], + settings: { + foreground: "var(--syntax-string)", + }, + }, + { + scope: "support", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: ["support.type.object.module", "variable.other.object", "support.type.property-name.css"], + settings: { + foreground: "var(--syntax-object)", + }, + }, + { + scope: "meta.property-name", + settings: { + foreground: "var(--syntax-property)", + }, + }, + { + scope: "variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "variable.other", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: [ + "invalid.broken", + "invalid.illegal", + "invalid.unimplemented", + "invalid.deprecated", + "message.error", + "markup.deleted", + "meta.diff.header.from-file", + "punctuation.definition.deleted", + "brackethighlighter.unmatched", + "token.error-token", + ], + settings: { + foreground: "var(--syntax-critical)", + }, + }, + { + scope: "carriage-return", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: "string source", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "string variable", + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: [ + "source.regexp", + "string.regexp", + "string.regexp.character-class", + "string.regexp constant.character.escape", + "string.regexp source.ruby.embedded", + "string.regexp string.regexp.arbitrary-repitition", + "string.regexp constant.character.escape", + ], + settings: { + foreground: "var(--syntax-regexp)", + }, + }, + { + scope: "support.constant", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: "support.variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "meta.module-reference", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "punctuation.definition.list.begin.markdown", + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["markup.heading", "markup.heading entity.name"], + settings: { + fontStyle: "bold", + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.quote", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.italic", + settings: { + fontStyle: "italic", + // foreground: "", + }, + }, + { + scope: "markup.bold", + settings: { + fontStyle: "bold", + foreground: "var(--text-strong)", + }, + }, + { + scope: [ + "markup.raw", + "markup.inserted", + "meta.diff.header.to-file", + "punctuation.definition.inserted", + "markup.changed", + "punctuation.definition.changed", + "markup.ignored", + "markup.untracked", + ], + settings: { + foreground: "var(--text-base)", + }, + }, + { + scope: "meta.diff.range", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.diff.header", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.separator", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.output", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.export.default", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: [ + "brackethighlighter.tag", + "brackethighlighter.curly", + "brackethighlighter.round", + "brackethighlighter.square", + "brackethighlighter.angle", + "brackethighlighter.quote", + ], + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: ["constant.other.reference.link", "string.other.link"], + settings: { + fontStyle: "underline", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "token.info-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "token.warn-token", + settings: { + foreground: "var(--syntax-warning)", + }, + }, + { + scope: "token.debug-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + ], + semanticTokenColors: { + comment: "var(--syntax-comment)", + string: "var(--syntax-string)", + number: "var(--syntax-constant)", + regexp: "var(--syntax-regexp)", + keyword: "var(--syntax-keyword)", + variable: "var(--syntax-variable)", + parameter: "var(--syntax-variable)", + property: "var(--syntax-property)", + function: "var(--syntax-primitive)", + method: "var(--syntax-primitive)", + type: "var(--syntax-type)", + class: "var(--syntax-type)", + namespace: "var(--syntax-type)", + enumMember: "var(--syntax-primitive)", + "variable.constant": "var(--syntax-constant)", + "variable.defaultLibrary": "var(--syntax-unknown)", + }, } as unknown as ThemeRegistrationResolved registerCustomTheme("OpenCode", () => Promise.resolve(OpenCodeTheme))