Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions packages/app-bundle/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"scope": "complete fork-vs-base delta of packages/{app,ui,session-ui,schema,core,sdk}",
"fork_ref": "local/amicode",
"fork_tag": "v1.18.29-amicode.30-11-g0667bccd6d",
"fork_sha": "6fa55b660a317357ac30e360e694f65aa7410723",
"upstream_base": "v1.18.29",
"fork_sha": "8f0f5d9097550eb3b3824d9edd76411339786f06",
"upstream_base": "16747470f976aca3d362ad730bcd3fe82ecc2c9a",
"upstream_base_sha": "16747470f976aca3d362ad730bcd3fe82ecc2c9a",
"promoted_at": "2026-09-11T16:58:54.648Z",
"promoted_at": "2026-09-12T12:58:48.994Z",
"files": {
"packages/app/AGENTS.md": "6ad84d752791908326d30b96703136918b14a1822f1dd4424a2ebc7ebfa6c78b",
"packages/app/CONTEXT.md": "7dc4bf176a32ee578a54593a58a6b97907ffc5a9baf7ae7c16a8fa920d8e6e86",
Expand All @@ -23,7 +23,7 @@
"packages/app/e2e/regression/new-session-panel-corner.spec.ts": "df11b094012f8206117575c63fd9ac0475d14b54ea4f9d719c6b21aac04d1c4b",
"packages/app/e2e/regression/openai-browser-oauth.spec.ts": "7df4521d1d82a186e383afd9351eef64f33f2adf8a3dce86dd5915b5f4828b68",
"packages/app/e2e/regression/pane-boot-watch.spec.ts": "3aa869f8b1d9881797e14dceb9854c4408f768f490d4db8722e29e511bca1833",
"packages/app/e2e/regression/preview-baseline-contract.spec.ts": "27faae9571de30d05e7f7aef4e3b3ba318deea2042fe0662fe753cad8bf512e8",
"packages/app/e2e/regression/preview-baseline-contract.spec.ts": "78c23b9e52496a779ee10aabf5bfad6900afb9b905d82ddedfa954031ce633f5",
"packages/app/e2e/regression/project-picker-recent-search.spec.ts": "64f4249f29cfce29d5e755a544c5aa2f27599d9fd874d363628f696f5f0e02b9",
"packages/app/e2e/regression/review-line-comment.spec.ts": "953e9c67ed4cee91968369088237cf2288558eaa55e55b61f385571bccad7003",
"packages/app/e2e/regression/session-timeline-lifecycle-state.spec.ts": "00bfd4f6199c1b350d14a9eda5e3a4c546b020df32e3604609326281869d6d4f",
Expand Down Expand Up @@ -55,7 +55,7 @@
"packages/app/src/amicode/inspector/inspector-context.tsx": "ac81b9e0e2a4035f85b6af68b7f0cefcb9f256a175b244ba13136f3a307c0c77",
"packages/app/src/amicode/inspector/run-inspector.tsx": "63a6351fad12a8377550eb87f7602a712bae586fe482ff46cd847c8544fc6e8a",
"packages/app/src/app-desktop-commands.test.ts": "1d2057f238dc14e4407ff5c4e53afe091784790bc0fd0dbfde0eacd91e2bd968",
"packages/app/src/app.tsx": "91ea1817db2f7e21caae642f3c2e276b4835fcecf499699cb787e7a5521ae20b",
"packages/app/src/app.tsx": "c32eae50e48492404aa2057f37fc0ce229e24640d26ce9a81c3b64066483f62f",
"packages/app/src/components/amicode-default-model-value.ts": "416bb4a7cf051c678bdfc5246ddc5796a9a6efe48d1e5c2e933756800cfdae8d",
"packages/app/src/components/amicode-default-model.test.ts": "941750d074706bd5f4e433c9737b19ebc0ac0f9ba9cd1faaa599a24b22126e05",
"packages/app/src/components/amicode-default-model.tsx": "16a2de1feb12e07416fe5fbb25879391f912ee0909b553c868c1b531f2ed1f2d",
Expand Down Expand Up @@ -434,9 +434,10 @@
"packages/sdk/js/src/v2/gen/types.gen.ts": "0c23b262085d1e14b89bd03292a380c9ec8b52e66c4308304a05173362b79339",
"packages/sdk/js/test/session-history.test.ts": "f10913af3157694465273d54e05c03cf96a0002df0e34898d9808f92977b11fd",
"packages/sdk/openapi.json": "5bbd6493a1a488ef4294889341c896e420f814ecea95822100aaa9f3f95ab2d1",
"packages/session-ui/bunfig.toml": "82585987c8258caf58d07c6aaf4e49b7338e4da16f97c785af398aace9e6e722",
"packages/session-ui/bunfig.toml": "468114e9b52c3cf926d2385c12d93385992cd70c97ea9e32ed22154617b7799d",
"packages/session-ui/happydom.ts": "d81d6b14e988f058fa502c841df34b55307c57fc94a26fb4f8a5deb6aa0d4834",
"packages/session-ui/package.json": "c5838c61cc5726509e8da4d1708283590070df88601d26ae400e62e71e6fd0c6",
"packages/session-ui/package.json": "15edfe2287a76b544bb359c809da056087bf00e71684a34d22cdcc405a77868a",
"packages/session-ui/preload-worker-mock.ts": "b53280029a042575734d7bfdf27713f6c5cd6a6665599dc429d839a3e3372bd1",
"packages/session-ui/src/components/basic-tool.css": "0bcf0592ce8af0d902bd69da26ecad2f00f5a7890275392abbffe6f4d80c7853",
"packages/session-ui/src/components/basic-tool.stories.tsx": "059c4bdce6a9ddc93c69949f5baf7323631ef0d5e69f04030fde0f2771a9fc09",
"packages/session-ui/src/components/basic-tool.tsx": "ff2678cc17510e1b39657469f6dd1a0311843664cfd5161d385256e617b4e759",
Expand All @@ -449,7 +450,7 @@
"packages/session-ui/src/components/markdown-file-refs.test.ts": "e2b1bed33629d3ab53c521f33266814088ea4122b6e64d6ab294eacc4cf15070",
"packages/session-ui/src/components/markdown-file-refs.ts": "b75bddeb565dc0163635fab8f84bdd26723742f9f82d06b6fd5991777b311032",
"packages/session-ui/src/components/markdown-preload.test.ts": "3a2db02aa3c199375ae3a8a240d4505c2c0bd5e84e1ef3a25d19618071a0b810",
"packages/session-ui/src/components/markdown-shiki.worker.ts": "0f438e40aee871d3da81488e21f42c0fefcd9af4a0eec9c599cd969566c2610e",
"packages/session-ui/src/components/markdown-shiki.worker.ts": "2748ddbfcd625bf696b27d9b64895e64ee47a3f316c88930010e260b4ba37f4b",
"packages/session-ui/src/components/markdown-stream.test.ts": "b73b1a15ffa2cfea5358b2d6313ce17d26a5a11302b0e3b7d823cde0c98cbc83",
"packages/session-ui/src/components/markdown-stream.ts": "50e38abd5d4ff919564655799678be10463dec58502fc5487c1c97a9a8db5399",
"packages/session-ui/src/components/markdown-worker-protocol.test.ts": "fdd7f1a35b208c7c2b57238ce89bc81255d99cde4b8ac1e7f261b7c8feafcd1a",
Expand Down Expand Up @@ -482,23 +483,23 @@
"packages/session-ui/src/components/tool-error-card.tsx": "e27b89836b7c709120ac15fbffd2200038944058ea3506e61e5897ffb3537503",
"packages/session-ui/src/components/tool-status-title.tsx": "ae4561b58f55985d2294a352802e723b18943ac9a6551d97f1c4a6cc95368c8a",
"packages/session-ui/src/context/data.tsx": "7f7eb7e6d9bc3e2ad3dca7f3e873fade43a1be2ba441950aa210f3254be0f1cb",
"packages/session-ui/src/pierre/worker.ts": "b94539c599ff0628def07d2a07f857f5523241550673e207e8425a91ea78eef1",
"packages/session-ui/src/pierre/worker.ts": "7f210f931777956567eaeea15f2f5478befc7874a64a4b685504c59c66503fee",
"packages/session-ui/src/util/clipboard.ts": "c37ba53914714ed04135e5a254e8ea90186f097d7470fa0873a83de82f3fef2d",
"packages/session-ui/src/v2/components/attachment-card-v2.css": "3b4b9c23d69e76b35488bc3a65fb51211038c3e8aac015df3f531b4e97e96359",
"packages/session-ui/src/v2/components/basic-tool-v2.css": "55d3aa179704deddda7149342a951229ff5561127f786dd551e5df9aea8a7835",
"packages/session-ui/src/v2/components/basic-tool-v2.tsx": "ece46d761f7d51a7a0925b3df00a5c289d891e9a514ee721016eb723d87fb406",
"packages/session-ui/src/v2/components/diff-gutter-extension.test.ts": "7c3a9d541f8cc2cdb537cc6630d8902a17647f0df063439a93e8d563cd4a97b2",
"packages/session-ui/src/v2/components/diff-gutter-extension.ts": "aed07587c0c9a5f110f9ea91aa42679d6f9f66b2ec4de9043d6e2233a375acb8",
"packages/session-ui/src/v2/components/editable-diff-view-core.ts": "f465e41349b820805bb9a2395886cc6bb2612bb40cbe6f1157823a2441ae75f3",
"packages/session-ui/src/v2/components/editable-diff-view-core.ts": "f01a341c4c1cdb4a9522f9ccd3c856bd3b9b7310c01d87327b3ce777052c8c59",
"packages/session-ui/src/v2/components/editable-diff-view.test.ts": "ab206fa17779e6e9015d343ae37e3067dfc1821a93b6ba566b7a0dce0fc19acb",
"packages/session-ui/src/v2/components/editable-diff-view.tsx": "4c0ee2525b94b04b1e708a5cacab6d3eed29ca30644321986e429b2f912b730b",
"packages/session-ui/src/v2/components/editor-core.test.ts": "aee8416e89ec0c38da77e57d422a0a59e4add150aa0875955f5124535c0a9585",
"packages/session-ui/src/v2/components/editor-core.ts": "bae3cebbaa1138885f28da94f32781a12c6f3e0ef2990776b15be18d8aead10e",
"packages/session-ui/src/v2/components/editable-diff-view.tsx": "f682e398ece864b5b854ca36698581c7e1cc5964eed96e7b45cabfcb64748b24",
"packages/session-ui/src/v2/components/editor-core.test.ts": "bd6552c0e7080afd554cb57ad690cc1504555712274c8b02702c64bb09460234",
"packages/session-ui/src/v2/components/editor-core.ts": "a03da4863d0dc4333772b8369a63822dd730bfd84dfa876f965cdd594292484b",
"packages/session-ui/src/v2/components/file-name-picker.test.tsx": "1ce9741e315b549165d390c74b5885a43580aa70e24de987f9ba8388cd521b41",
"packages/session-ui/src/v2/components/markdown-utils.test.ts": "10e1034293bf385cf50d860bf1667d8370abc61dcee0c5a93295f21f5f55876a",
"packages/session-ui/src/v2/components/markdown-utils.ts": "3bc47cfed5b99ab23f857d5324be3189f3a7e800d991103d72ef1c2f970b910b",
"packages/session-ui/src/v2/components/preview-editor.test.ts": "b8da9320a594aaa680ed3848612fcea66f41c0a046a673f0cf6155c2a0b697dd",
"packages/session-ui/src/v2/components/preview-editor.tsx": "d859721bf3ea4e3bdbf24985e95198697307a78f8cb1644dc9fba5ddf3a189fc",
"packages/session-ui/src/v2/components/preview-editor.tsx": "d356ff168919abc8446c5fac2836791338fb3b9124f558c43ea7d3d5a1bcc5f4",
"packages/session-ui/src/v2/components/preview-file-routing.test.ts": "fca02a21261b291fd94001baa2294e7b2fa45b18b0c0403848f045763b41bf15",
"packages/session-ui/src/v2/components/preview-nav-state.test.ts": "cec09a8b3c08ab16581d3d5b6ff704584b3c87d81c25e8af20f6f00272f3593f",
"packages/session-ui/src/v2/components/preview-nav-state.ts": "ccaeb94d1ab39271f5dcd49c935c8fc7b54d4ffbd66e1352e4d797c9205553fc",
Expand All @@ -507,6 +508,13 @@
"packages/session-ui/src/v2/components/session-review-file-preview-v2.tsx": "fc06f46ed3c92c8066744da1f6ac97db7d8b7d2aeec5ef776d743dd93b317936",
"packages/session-ui/src/v2/components/session-review-v2.css": "bb306f226fda8f8ba6cf0ae706843ba9f2b48f3a3446433515e8a8dd1f9dc626",
"packages/session-ui/src/v2/components/session-review-v2.tsx": "4151286efb6e0e148246ee3eebae24a2e5d4eac2603fd5ea8546d0839a694161",
"packages/session-ui/src/v2/components/shiki-highlight-decorations.test.ts": "f50e8156b708f3e0ddbc509abf71751778a74923dac9d7e65385b178f6346718",
"packages/session-ui/src/v2/components/shiki-highlight-decorations.ts": "57fee1804df78c627f7ddfd092a55d71afe041baa153d0d5ed7c7d8781e30206",
"packages/session-ui/src/v2/components/shiki-highlight-integration.test.ts": "c3f62945f0258d7462bf9936a8b78294c4295f445e2a75fca186432f241443dc",
"packages/session-ui/src/v2/components/shiki-highlight-plugin.ts": "85b01203590f6de0af75b1cb4f35d1b87dea92989a950dbe6a9cf10aac19ffaf",
"packages/session-ui/src/v2/components/shiki-highlight-worker.ts": "0f9628b9e0d3ec9ff49d53d05cb77259e1762f66daf7b0e1f2403649385e66e4",
"packages/session-ui/src/v2/components/shiki-theme-state.test.ts": "b305c3f669b2083851fb574bacccab4f311228ce842f18465443b7807226e619",
"packages/session-ui/src/v2/components/shiki-theme-state.ts": "5ab15c15f4a32afce2279068adda732846404133bbc2a44b90779a4da88d123a",
"packages/session-ui/src/v2/components/tool-error-card-v2.css": "9eb0bf8b583a18159f3855465118ff6a853d0193ea783037d5c90a868fdb3ff1",
"packages/ui/package.json": "b1d168d0371e9094faae1107fc6c00be197f09bc69daa2247a3890d607f4b629",
"packages/ui/src/amicode/amico-presence.css": "b3f345b607e318dcfbefcdc677fe21cc7d387b9946b0f9d625b0a842fe7e31b9",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const sessionID = "ses_preview_baseline_contract"
const title = "Preview baseline contract"
const markdownFile = "notes/baseline.md"
const secondMarkdownFile = "notes/second.md"
const juliaFile = "scripts/preview.jl"
const juliaContent = "function evolve(state)\n return state + 1\nend\n"
const imageFile = "assets/preview.png"
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
const markdownContent = ["# Baseline Preview", "", "The renderer must keep focus."]
Expand Down Expand Up @@ -50,6 +52,32 @@ test("keeps Markdown typing in CodeMirror after its language support loads", asy
await expect(page.locator('[data-component="prompt-input"]')).toHaveText("")
})

test("applies the bridged VS Code token color to a Julia Preview editor", async ({ page }) => {
await openPreview(page)

// A distinctive color makes this an end-to-end check of the full chain:
// host message -> theme state -> Shiki Worker -> CM6 Decoration.mark.
await page.evaluate(() => {
window.postMessage({
source: "amicode",
kind: "syntax-theme",
theme: {
name: "Preview E2E Theme",
tokenColors: [{ scope: "keyword", settings: { foreground: "#ff00aa" } }],
},
}, "*")
})
await openPreviewFile(page, juliaFile)

const editor = page.locator(`#review-panel [data-preview-host="${juliaFile}"] .cm-content`)
await expect(editor).toBeVisible()
const functionToken = editor.locator("span").filter({ hasText: "function" }).first()
await expect(functionToken).toBeVisible()
await expect
.poll(() => functionToken.evaluate((element) => getComputedStyle(element).color))
.toBe("rgb(255, 0, 170)")
})

test("keeps the default cursor while a Preview tab crosses an editable file", async ({ page }) => {
await openPreview(page)
await openPreviewFile(page, secondMarkdownFile)
Expand Down Expand Up @@ -1505,6 +1533,7 @@ async function openPreview(page: Parameters<typeof mockOpenCodeServer>[0]) {
fileContent: (path) => {
if (path === markdownFile) return { type: "text", content: markdownContent }
if (path === secondMarkdownFile) return { type: "text", content: secondMarkdownContent }
if (path === juliaFile) return { type: "text", content: juliaContent }
if (path === imageFile)
return { type: "binary", content: imageContent, encoding: "base64", mimeType: "image/png" }
if (path === pdfFile)
Expand Down
5 changes: 5 additions & 0 deletions packages/app-bundle/overlay/packages/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DialogProvider } from "@opencode-ai/ui/context/dialog"
import { FileComponentProvider } from "@opencode-ai/ui/context/file"
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
import { File } from "@opencode-ai/session-ui/file"
import { handleSyntaxThemeMessage } from "@opencode-ai/session-ui/v2/shiki-theme-state"
import { Font } from "@opencode-ai/ui/font"
import { Splash } from "@opencode-ai/ui/logo"
import { ThemeProvider, useTheme } from "@opencode-ai/ui/theme/context"
Expand Down Expand Up @@ -451,6 +452,10 @@ function AmicodeThemeBridge() {
adoptExplorerIconTheme(d.theme)
return
}
if (d.kind === "syntax-theme") {
handleSyntaxThemeMessage((d as { theme?: string | object }).theme ?? "")
return
}
if (d.kind !== "theme") return
if (d.colorScheme === "light" || d.colorScheme === "dark") theme.setColorScheme(d.colorScheme)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[test]
root = "./src"
preload = ["./happydom.ts"]
preload = ["./happydom.ts", "./preload-worker-mock.ts"]
3 changes: 3 additions & 0 deletions packages/app-bundle/overlay/packages/session-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"./v2/*": "./src/v2/components/*.tsx",
"./v2/editor-core": "./src/v2/components/editor-core.ts",
"./v2/markdown-utils": "./src/v2/components/markdown-utils.ts",
"./v2/shiki-theme-state": "./src/v2/components/shiki-theme-state.ts",
"./v2/shiki-highlight-plugin": "./src/v2/components/shiki-highlight-plugin.ts",
"./v2/preview-editor": "./src/v2/components/preview-editor.tsx",
"./v2/preview-nav-state": "./src/v2/components/preview-nav-state.ts",
"./v2/prompt-input": "./src/v2/components/prompt-input/index.tsx",
Expand Down Expand Up @@ -53,6 +55,7 @@
"@codemirror/lang-markdown": "6.5.2",
"@codemirror/lang-python": "6.2.1",
"@codemirror/language": "6.12.4",
"@codemirror/language-data": "6.5.1",
"@codemirror/merge": "6.12.2",
"@codemirror/state": "6.7.1",
"@codemirror/view": "6.43.9",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Bun preload plugin: mock Vite-specific ?worker&url imports.
*
* Vite transforms `import Url from "./foo.ts?worker&url"` into a URL string
* at build time. Bun's test runner doesn't understand this suffix. This plugin
* intercepts any import ending in `?worker&url` and returns a dummy string,
* so modules that import worker URLs can load in tests without crashing.
*
* The Worker constructor will receive the dummy string and fail, which is
* expected — getWorker() catches the error and returns null, falling back
* to lezer-only highlighting in tests.
*/
import { plugin } from "bun"

plugin({
name: "vite-worker-url-mock",
setup(build) {
build.onResolve({ filter: /\?worker&url$/ }, (args) => {
return { path: args.path, namespace: "vite-worker-url-mock" }
})
build.onLoad({ filter: /.*/, namespace: "vite-worker-url-mock" }, () => {
return { contents: 'export default "mock-worker-url"', loader: "js" }
})
},
})
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Stream = {

const streams = new Map<string, Stream>()
let highlighter: ReturnType<typeof createHighlighter> | undefined
let currentThemeName = "OpenCode"
const queue = createLatestWorkerQueue<Extract<MarkdownWorkerRequest, { type: "highlight" }>>({
run: highlight,
supersede: (request) => post({ type: "superseded", id: request.id, key: request.key }),
Expand All @@ -35,6 +36,19 @@ self.onmessage = (event: MessageEvent<MarkdownWorkerRequest>) => {
queue.dispose(event.data.key)
return
}
// Theme update from the extension bridge (via shiki-theme-state)
if ((event.data as any).type === "theme-update") {
const msg = event.data as any
if (typeof msg.theme === "object" && highlighter) {
highlighter.then((instance) => {
if (instance) instance.loadTheme(msg.theme)
})
}
if (typeof msg.name === "string") currentThemeName = msg.name
// Clear all streams so they pick up the new theme on next highlight
streams.clear()
return
}

queue.highlight(event.data)
}
Expand All @@ -48,7 +62,7 @@ async function highlight(request: Extract<MarkdownWorkerRequest, { type: "highli
await instance.loadLanguage(bundledLanguages[language as BundledLanguage])

if (request.complete) {
const result = instance.codeToTokens(request.text, { lang: language as BundledLanguage, theme: "OpenCode" })
const result = instance.codeToTokens(request.text, { lang: language as BundledLanguage, theme: currentThemeName })
streams.delete(request.key)
post({
type: "highlight",
Expand All @@ -71,7 +85,7 @@ async function highlight(request: Extract<MarkdownWorkerRequest, { type: "highli
? {
language,
source: "",
tokenizer: new ShikiStreamTokenizer({ highlighter: instance, lang: language, theme: "OpenCode" }),
tokenizer: new ShikiStreamTokenizer({ highlighter: instance, lang: language, theme: currentThemeName }),
}
: previous
const result = await stream.tokenizer.enqueue(request.text.slice(stream.source.length))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WorkerPoolManager } from "@pierre/diffs/worker"
import ShikiWorkerUrl from "@pierre/diffs/worker/worker.js?worker&url"
import { getActiveShikiTheme, onThemeChange } from "../v2/components/shiki-theme-state"

export type WorkerPoolStyle = "unified" | "split"

Expand All @@ -8,6 +9,8 @@ export function workerFactory(): Worker {
}

function createPool(lineDiffType: "none" | "word-alt") {
// Use the active VS Code theme if available; falls back to "OpenCode"
const theme = getActiveShikiTheme()
const pool = new WorkerPoolManager(
{
workerFactory,
Expand All @@ -19,7 +22,7 @@ function createPool(lineDiffType: "none" | "word-alt") {
poolSize: 2,
},
{
theme: "OpenCode",
theme,
lineDiffType,
preferredHighlighter: "shiki-wasm",
},
Expand All @@ -32,6 +35,14 @@ function createPool(lineDiffType: "none" | "word-alt") {
let unified: WorkerPoolManager | undefined
let split: WorkerPoolManager | undefined

// When the theme changes, recreate the pools so @pierre/diffs picks up the
// new theme. The pools are lazy-created, so if they haven't been used yet
// this is a no-op.
onThemeChange(() => {
if (unified) { unified.terminate(); unified = undefined }
if (split) { split.terminate(); split = undefined }
})

export function getWorkerPool(style: WorkerPoolStyle | undefined): WorkerPoolManager | undefined {
if (typeof window === "undefined") return

Expand Down
Loading
Loading