summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Hill <[email protected]>2025-09-23 18:36:27 +0100
committerDavid Hill <[email protected]>2025-09-23 18:36:27 +0100
commit9d53628e192065cd20f5fbae3712dae43b92b1e3 (patch)
tree14e7ac5201c56d011932f1c54a33b975c9cd6e47
parent869b4761455672535eba878aa90edf21ab6fecec (diff)
parent5ead6d7dd50e96c3cd6213c9be540c1ca8ac2fe5 (diff)
downloadopencode-9d53628e192065cd20f5fbae3712dae43b92b1e3.tar.gz
opencode-9d53628e192065cd20f5fbae3712dae43b92b1e3.zip
Merge branch 'dev' of https://github.com/sst/opencode into dev
-rw-r--r--STATS.md1
-rw-r--r--bun.lock3
-rw-r--r--packages/app/.gitignore1
-rw-r--r--packages/app/package.json1
-rw-r--r--packages/app/scripts/vite-theme-plugin.ts6
-rw-r--r--packages/app/src/assets/theme.css2385
-rw-r--r--packages/app/src/components/code.tsx1263
-rw-r--r--packages/app/src/components/markdown.tsx583
-rw-r--r--packages/app/src/context/event.tsx34
-rw-r--r--packages/app/src/context/index.ts3
-rw-r--r--packages/app/src/context/local.tsx51
-rw-r--r--packages/app/src/context/marked.tsx40
-rw-r--r--packages/app/src/context/shiki.tsx582
-rw-r--r--packages/app/src/context/sync.tsx106
-rw-r--r--packages/app/src/index.tsx34
-rw-r--r--packages/opencode/src/cli/cmd/debug/lsp.ts5
-rw-r--r--packages/opencode/src/file/watcher.ts1
-rw-r--r--packages/opencode/src/lsp/index.ts2
-rw-r--r--packages/web/src/content/docs/agents.mdx4
-rw-r--r--packages/web/src/content/docs/lsp.mdx2
-rw-r--r--packages/web/src/content/docs/providers.mdx2
21 files changed, 1129 insertions, 3980 deletions
diff --git a/STATS.md b/STATS.md
index c410e04af..e220e38cf 100644
--- a/STATS.md
+++ b/STATS.md
@@ -87,3 +87,4 @@
| 2025-09-20 | 372,092 (+6,691) | 276,917 (+5,058) | 649,009 (+11,749) |
| 2025-09-21 | 377,079 (+4,987) | 280,261 (+3,344) | 657,340 (+8,331) |
| 2025-09-22 | 382,492 (+5,413) | 284,009 (+3,748) | 666,501 (+9,161) |
+| 2025-09-23 | 387,008 (+4,516) | 289,129 (+5,120) | 676,137 (+9,636) |
diff --git a/bun.lock b/bun.lock
index 53ff88ad8..221696fcc 100644
--- a/bun.lock
+++ b/bun.lock
@@ -19,6 +19,7 @@
"@kobalte/core": "0.13.11",
"@opencode-ai/sdk": "workspace:*",
"@shikijs/transformers": "3.9.2",
+ "@solid-primitives/event-bus": "1.1.2",
"@solid-primitives/resize-observer": "2.1.3",
"@solid-primitives/scroll": "2.1.3",
"@solidjs/router": "0.15.3",
@@ -996,6 +997,8 @@
"@smithy/util-utf8": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/util-buffer-from": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ=="],
+ "@solid-primitives/event-bus": ["@solid-primitives/[email protected]", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-l+n10/51neGcMaP3ypYt21bXfoeWh8IaC8k7fYuY3ww2a8S1Zv2N2a7FF5Qn+waTu86l0V8/nRHjkyqVIZBYwA=="],
+
"@solid-primitives/event-listener": ["@solid-primitives/[email protected]", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg=="],
"@solid-primitives/keyed": ["@solid-primitives/[email protected]", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-BgoEdqPw48URnI+L5sZIHdF4ua4Las1eWEBBPaoSFs42kkhnHue+rwCBPL2Z9ebOyQ75sUhUfOETdJfmv0D6Kg=="],
diff --git a/packages/app/.gitignore b/packages/app/.gitignore
new file mode 100644
index 000000000..4a20d55a7
--- /dev/null
+++ b/packages/app/.gitignore
@@ -0,0 +1 @@
+src/assets/theme.css
diff --git a/packages/app/package.json b/packages/app/package.json
index 69bb3672e..322434204 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -24,6 +24,7 @@
"@kobalte/core": "0.13.11",
"@opencode-ai/sdk": "workspace:*",
"@shikijs/transformers": "3.9.2",
+ "@solid-primitives/event-bus": "1.1.2",
"@solid-primitives/resize-observer": "2.1.3",
"@solid-primitives/scroll": "2.1.3",
"@solidjs/router": "0.15.3",
diff --git a/packages/app/scripts/vite-theme-plugin.ts b/packages/app/scripts/vite-theme-plugin.ts
index 2d9cc9449..1241ffcd7 100644
--- a/packages/app/scripts/vite-theme-plugin.ts
+++ b/packages/app/scripts/vite-theme-plugin.ts
@@ -37,7 +37,7 @@ class ColorResolver {
if (typeof value === "string") {
if (value === "none") return { dark: value, light: value }
if (value.startsWith("#")) {
- return { dark: value.toUpperCase(), light: value.toUpperCase() }
+ return { dark: value.toLowerCase(), light: value.toLowerCase() }
}
const resolved = this.resolveReference(value)
return { dark: resolved, light: resolved }
@@ -57,7 +57,7 @@ class ColorResolver {
if (typeof value === "string") {
if (value === "none") return value
if (value.startsWith("#")) {
- return value.toUpperCase()
+ return value.toLowerCase()
}
return this.resolveReference(value)
}
@@ -72,7 +72,7 @@ class ColorResolver {
if (typeof colorValue === "string") {
if (colorValue === "none") return colorValue
if (colorValue.startsWith("#")) {
- return colorValue.toUpperCase()
+ return colorValue.toLowerCase()
}
return this.resolveReference(colorValue)
}
diff --git a/packages/app/src/assets/theme.css b/packages/app/src/assets/theme.css
deleted file mode 100644
index 48d91fc94..000000000
--- a/packages/app/src/assets/theme.css
+++ /dev/null
@@ -1,2385 +0,0 @@
-/* Auto-generated theme CSS - Do not edit manually */
-:root {
- --theme-primary: #3b7dd8;
- --theme-secondary: #7b5bb6;
- --theme-accent: #d68c27;
- --theme-error: #d1383d;
- --theme-warning: #d68c27;
- --theme-success: #3d9a57;
- --theme-info: #318795;
- --theme-text: #1a1a1a;
- --theme-text-muted: #8a8a8a;
- --theme-background: #ffffff;
- --theme-background-panel: #fafafa;
- --theme-background-element: #f5f5f5;
- --theme-border: #b8b8b8;
- --theme-border-active: #a0a0a0;
- --theme-border-subtle: #d4d4d4;
- --theme-diff-added: #1e725c;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #7086b5;
- --theme-diff-hunk-header: #7086b5;
- --theme-diff-highlight-added: #4db380;
- --theme-diff-highlight-removed: #f52a65;
- --theme-diff-added-bg: #d5e5d5;
- --theme-diff-removed-bg: #f7d8db;
- --theme-diff-context-bg: #fafafa;
- --theme-diff-line-number: #f5f5f5;
- --theme-diff-added-line-number-bg: #c5d5c5;
- --theme-diff-removed-line-number-bg: #e7c8cb;
- --theme-markdown-text: #1a1a1a;
- --theme-markdown-heading: #d68c27;
- --theme-markdown-link: #3b7dd8;
- --theme-markdown-link-text: #318795;
- --theme-markdown-code: #3d9a57;
- --theme-markdown-block-quote: #b0851f;
- --theme-markdown-emph: #b0851f;
- --theme-markdown-strong: #d68c27;
- --theme-markdown-horizontal-rule: #8a8a8a;
- --theme-markdown-list-item: #3b7dd8;
- --theme-markdown-list-enumeration: #318795;
- --theme-markdown-image: #3b7dd8;
- --theme-markdown-image-text: #318795;
- --theme-markdown-code-block: #1a1a1a;
- --theme-syntax-comment: #8a8a8a;
- --theme-syntax-keyword: #d68c27;
- --theme-syntax-function: #3b7dd8;
- --theme-syntax-variable: #d1383d;
- --theme-syntax-string: #3d9a57;
- --theme-syntax-number: #d68c27;
- --theme-syntax-type: #b0851f;
- --theme-syntax-operator: #318795;
- --theme-syntax-punctuation: #1a1a1a;
-}
-
-[data-theme="aura"][data-dark="false"] {
- --theme-primary: #a277ff;
- --theme-secondary: #f694ff;
- --theme-accent: #a277ff;
- --theme-error: #ff6767;
- --theme-warning: #ffca85;
- --theme-success: #61ffca;
- --theme-info: #a277ff;
- --theme-text: #edecee;
- --theme-text-muted: #6d6d6d;
- --theme-background: #0f0f0f;
- --theme-background-panel: #15141b;
- --theme-background-element: #15141b;
- --theme-border: #2d2d2d;
- --theme-border-active: #6d6d6d;
- --theme-border-subtle: #2d2d2d;
- --theme-diff-added: #61ffca;
- --theme-diff-removed: #ff6767;
- --theme-diff-context: #6d6d6d;
- --theme-diff-hunk-header: #6d6d6d;
- --theme-diff-highlight-added: #61ffca;
- --theme-diff-highlight-removed: #ff6767;
- --theme-diff-added-bg: #354933;
- --theme-diff-removed-bg: #3f191a;
- --theme-diff-context-bg: #15141b;
- --theme-diff-line-number: #2d2d2d;
- --theme-diff-added-line-number-bg: #162620;
- --theme-diff-removed-line-number-bg: #26161a;
- --theme-markdown-text: #edecee;
- --theme-markdown-heading: #a277ff;
- --theme-markdown-link: #f694ff;
- --theme-markdown-link-text: #a277ff;
- --theme-markdown-code: #61ffca;
- --theme-markdown-block-quote: #6d6d6d;
- --theme-markdown-emph: #ffca85;
- --theme-markdown-strong: #a277ff;
- --theme-markdown-horizontal-rule: #6d6d6d;
- --theme-markdown-list-item: #a277ff;
- --theme-markdown-list-enumeration: #a277ff;
- --theme-markdown-image: #f694ff;
- --theme-markdown-image-text: #a277ff;
- --theme-markdown-code-block: #edecee;
- --theme-syntax-comment: #6d6d6d;
- --theme-syntax-keyword: #f694ff;
- --theme-syntax-function: #a277ff;
- --theme-syntax-variable: #a277ff;
- --theme-syntax-string: #61ffca;
- --theme-syntax-number: #9dff65;
- --theme-syntax-type: #a277ff;
- --theme-syntax-operator: #f694ff;
- --theme-syntax-punctuation: #edecee;
-}
-
-[data-theme="aura"][data-dark="true"] {
- --theme-primary: #a277ff;
- --theme-secondary: #f694ff;
- --theme-accent: #a277ff;
- --theme-error: #ff6767;
- --theme-warning: #ffca85;
- --theme-success: #61ffca;
- --theme-info: #a277ff;
- --theme-text: #edecee;
- --theme-text-muted: #6d6d6d;
- --theme-background: #0f0f0f;
- --theme-background-panel: #15141b;
- --theme-background-element: #15141b;
- --theme-border: #2d2d2d;
- --theme-border-active: #6d6d6d;
- --theme-border-subtle: #2d2d2d;
- --theme-diff-added: #61ffca;
- --theme-diff-removed: #ff6767;
- --theme-diff-context: #6d6d6d;
- --theme-diff-hunk-header: #6d6d6d;
- --theme-diff-highlight-added: #61ffca;
- --theme-diff-highlight-removed: #ff6767;
- --theme-diff-added-bg: #354933;
- --theme-diff-removed-bg: #3f191a;
- --theme-diff-context-bg: #15141b;
- --theme-diff-line-number: #2d2d2d;
- --theme-diff-added-line-number-bg: #162620;
- --theme-diff-removed-line-number-bg: #26161a;
- --theme-markdown-text: #edecee;
- --theme-markdown-heading: #a277ff;
- --theme-markdown-link: #f694ff;
- --theme-markdown-link-text: #a277ff;
- --theme-markdown-code: #61ffca;
- --theme-markdown-block-quote: #6d6d6d;
- --theme-markdown-emph: #ffca85;
- --theme-markdown-strong: #a277ff;
- --theme-markdown-horizontal-rule: #6d6d6d;
- --theme-markdown-list-item: #a277ff;
- --theme-markdown-list-enumeration: #a277ff;
- --theme-markdown-image: #f694ff;
- --theme-markdown-image-text: #a277ff;
- --theme-markdown-code-block: #edecee;
- --theme-syntax-comment: #6d6d6d;
- --theme-syntax-keyword: #f694ff;
- --theme-syntax-function: #a277ff;
- --theme-syntax-variable: #a277ff;
- --theme-syntax-string: #61ffca;
- --theme-syntax-number: #9dff65;
- --theme-syntax-type: #a277ff;
- --theme-syntax-operator: #f694ff;
- --theme-syntax-punctuation: #edecee;
-}
-
-[data-theme="ayu"][data-dark="false"] {
- --theme-primary: #59c2ff;
- --theme-secondary: #d2a6ff;
- --theme-accent: #e6b450;
- --theme-error: #d95757;
- --theme-warning: #e6b673;
- --theme-success: #7fd962;
- --theme-info: #39bae6;
- --theme-text: #bfbdb6;
- --theme-text-muted: #565b66;
- --theme-background: #0b0e14;
- --theme-background-panel: #0f131a;
- --theme-background-element: #0d1017;
- --theme-border: #6c7380;
- --theme-border-active: #6c7380;
- --theme-border-subtle: #11151c;
- --theme-diff-added: #7fd962;
- --theme-diff-removed: #f26d78;
- --theme-diff-context: #acb6bf;
- --theme-diff-hunk-header: #acb6bf;
- --theme-diff-highlight-added: #aad94c;
- --theme-diff-highlight-removed: #f07178;
- --theme-diff-added-bg: #20303b;
- --theme-diff-removed-bg: #37222c;
- --theme-diff-context-bg: #0f131a;
- --theme-diff-line-number: #6c7380;
- --theme-diff-added-line-number-bg: #1b2b34;
- --theme-diff-removed-line-number-bg: #2d1f26;
- --theme-markdown-text: #bfbdb6;
- --theme-markdown-heading: #d2a6ff;
- --theme-markdown-link: #59c2ff;
- --theme-markdown-link-text: #39bae6;
- --theme-markdown-code: #aad94c;
- --theme-markdown-block-quote: #e6b673;
- --theme-markdown-emph: #e6b673;
- --theme-markdown-strong: #ffb454;
- --theme-markdown-horizontal-rule: #565b66;
- --theme-markdown-list-item: #59c2ff;
- --theme-markdown-list-enumeration: #39bae6;
- --theme-markdown-image: #59c2ff;
- --theme-markdown-image-text: #39bae6;
- --theme-markdown-code-block: #bfbdb6;
- --theme-syntax-comment: #acb6bf;
- --theme-syntax-keyword: #ff8f40;
- --theme-syntax-function: #ffb454;
- --theme-syntax-variable: #59c2ff;
- --theme-syntax-string: #aad94c;
- --theme-syntax-number: #d2a6ff;
- --theme-syntax-type: #e6b673;
- --theme-syntax-operator: #f29668;
- --theme-syntax-punctuation: #bfbdb6;
-}
-
-[data-theme="ayu"][data-dark="true"] {
- --theme-primary: #59c2ff;
- --theme-secondary: #d2a6ff;
- --theme-accent: #e6b450;
- --theme-error: #d95757;
- --theme-warning: #e6b673;
- --theme-success: #7fd962;
- --theme-info: #39bae6;
- --theme-text: #bfbdb6;
- --theme-text-muted: #565b66;
- --theme-background: #0b0e14;
- --theme-background-panel: #0f131a;
- --theme-background-element: #0d1017;
- --theme-border: #6c7380;
- --theme-border-active: #6c7380;
- --theme-border-subtle: #11151c;
- --theme-diff-added: #7fd962;
- --theme-diff-removed: #f26d78;
- --theme-diff-context: #acb6bf;
- --theme-diff-hunk-header: #acb6bf;
- --theme-diff-highlight-added: #aad94c;
- --theme-diff-highlight-removed: #f07178;
- --theme-diff-added-bg: #20303b;
- --theme-diff-removed-bg: #37222c;
- --theme-diff-context-bg: #0f131a;
- --theme-diff-line-number: #6c7380;
- --theme-diff-added-line-number-bg: #1b2b34;
- --theme-diff-removed-line-number-bg: #2d1f26;
- --theme-markdown-text: #bfbdb6;
- --theme-markdown-heading: #d2a6ff;
- --theme-markdown-link: #59c2ff;
- --theme-markdown-link-text: #39bae6;
- --theme-markdown-code: #aad94c;
- --theme-markdown-block-quote: #e6b673;
- --theme-markdown-emph: #e6b673;
- --theme-markdown-strong: #ffb454;
- --theme-markdown-horizontal-rule: #565b66;
- --theme-markdown-list-item: #59c2ff;
- --theme-markdown-list-enumeration: #39bae6;
- --theme-markdown-image: #59c2ff;
- --theme-markdown-image-text: #39bae6;
- --theme-markdown-code-block: #bfbdb6;
- --theme-syntax-comment: #acb6bf;
- --theme-syntax-keyword: #ff8f40;
- --theme-syntax-function: #ffb454;
- --theme-syntax-variable: #59c2ff;
- --theme-syntax-string: #aad94c;
- --theme-syntax-number: #d2a6ff;
- --theme-syntax-type: #e6b673;
- --theme-syntax-operator: #f29668;
- --theme-syntax-punctuation: #bfbdb6;
-}
-
-[data-theme="catppuccin"][data-dark="false"] {
- --theme-primary: #1e66f5;
- --theme-secondary: #8839ef;
- --theme-accent: #ea76cb;
- --theme-error: #d20f39;
- --theme-warning: #df8e1d;
- --theme-success: #40a02b;
- --theme-info: #179299;
- --theme-text: #4c4f69;
- --theme-text-muted: #5c5f77;
- --theme-background: #eff1f5;
- --theme-background-panel: #e6e9ef;
- --theme-background-element: #dce0e8;
- --theme-border: #ccd0da;
- --theme-border-active: #bcc0cc;
- --theme-border-subtle: #acb0be;
- --theme-diff-added: #40a02b;
- --theme-diff-removed: #d20f39;
- --theme-diff-context: #7c7f93;
- --theme-diff-hunk-header: #fe640b;
- --theme-diff-highlight-added: #40a02b;
- --theme-diff-highlight-removed: #d20f39;
- --theme-diff-added-bg: #d6f0d9;
- --theme-diff-removed-bg: #f6dfe2;
- --theme-diff-context-bg: #e6e9ef;
- --theme-diff-line-number: #bcc0cc;
- --theme-diff-added-line-number-bg: #c9e3cb;
- --theme-diff-removed-line-number-bg: #e9d3d6;
- --theme-markdown-text: #4c4f69;
- --theme-markdown-heading: #8839ef;
- --theme-markdown-link: #1e66f5;
- --theme-markdown-link-text: #04a5e5;
- --theme-markdown-code: #40a02b;
- --theme-markdown-block-quote: #df8e1d;
- --theme-markdown-emph: #df8e1d;
- --theme-markdown-strong: #fe640b;
- --theme-markdown-horizontal-rule: #6c6f85;
- --theme-markdown-list-item: #1e66f5;
- --theme-markdown-list-enumeration: #04a5e5;
- --theme-markdown-image: #1e66f5;
- --theme-markdown-image-text: #04a5e5;
- --theme-markdown-code-block: #4c4f69;
- --theme-syntax-comment: #7c7f93;
- --theme-syntax-keyword: #8839ef;
- --theme-syntax-function: #1e66f5;
- --theme-syntax-variable: #d20f39;
- --theme-syntax-string: #40a02b;
- --theme-syntax-number: #fe640b;
- --theme-syntax-type: #df8e1d;
- --theme-syntax-operator: #04a5e5;
- --theme-syntax-punctuation: #4c4f69;
-}
-
-[data-theme="catppuccin"][data-dark="true"] {
- --theme-primary: #89b4fa;
- --theme-secondary: #cba6f7;
- --theme-accent: #f5c2e7;
- --theme-error: #f38ba8;
- --theme-warning: #f9e2af;
- --theme-success: #a6e3a1;
- --theme-info: #94e2d5;
- --theme-text: #cdd6f4;
- --theme-text-muted: #bac2de;
- --theme-background: #1e1e2e;
- --theme-background-panel: #181825;
- --theme-background-element: #11111b;
- --theme-border: #313244;
- --theme-border-active: #45475a;
- --theme-border-subtle: #585b70;
- --theme-diff-added: #a6e3a1;
- --theme-diff-removed: #f38ba8;
- --theme-diff-context: #9399b2;
- --theme-diff-hunk-header: #fab387;
- --theme-diff-highlight-added: #a6e3a1;
- --theme-diff-highlight-removed: #f38ba8;
- --theme-diff-added-bg: #24312b;
- --theme-diff-removed-bg: #3c2a32;
- --theme-diff-context-bg: #181825;
- --theme-diff-line-number: #45475a;
- --theme-diff-added-line-number-bg: #1e2a25;
- --theme-diff-removed-line-number-bg: #32232a;
- --theme-markdown-text: #cdd6f4;
- --theme-markdown-heading: #cba6f7;
- --theme-markdown-link: #89b4fa;
- --theme-markdown-link-text: #89dceb;
- --theme-markdown-code: #a6e3a1;
- --theme-markdown-block-quote: #f9e2af;
- --theme-markdown-emph: #f9e2af;
- --theme-markdown-strong: #fab387;
- --theme-markdown-horizontal-rule: #a6adc8;
- --theme-markdown-list-item: #89b4fa;
- --theme-markdown-list-enumeration: #89dceb;
- --theme-markdown-image: #89b4fa;
- --theme-markdown-image-text: #89dceb;
- --theme-markdown-code-block: #cdd6f4;
- --theme-syntax-comment: #9399b2;
- --theme-syntax-keyword: #cba6f7;
- --theme-syntax-function: #89b4fa;
- --theme-syntax-variable: #f38ba8;
- --theme-syntax-string: #a6e3a1;
- --theme-syntax-number: #fab387;
- --theme-syntax-type: #f9e2af;
- --theme-syntax-operator: #89dceb;
- --theme-syntax-punctuation: #cdd6f4;
-}
-
-[data-theme="cobalt2"][data-dark="false"] {
- --theme-primary: #0066cc;
- --theme-secondary: #7c4dff;
- --theme-accent: #00acc1;
- --theme-error: #e91e63;
- --theme-warning: #ff9800;
- --theme-success: #4caf50;
- --theme-info: #ff5722;
- --theme-text: #193549;
- --theme-text-muted: #5c6b7d;
- --theme-background: #ffffff;
- --theme-background-panel: #f5f7fa;
- --theme-background-element: #e8ecf1;
- --theme-border: #d3dae3;
- --theme-border-active: #0066cc;
- --theme-border-subtle: #e8ecf1;
- --theme-diff-added: #4caf50;
- --theme-diff-removed: #e91e63;
- --theme-diff-context: #5c6b7d;
- --theme-diff-hunk-header: #00acc1;
- --theme-diff-highlight-added: #4caf50;
- --theme-diff-highlight-removed: #e91e63;
- --theme-diff-added-bg: #e8f5e9;
- --theme-diff-removed-bg: #ffebee;
- --theme-diff-context-bg: #f5f7fa;
- --theme-diff-line-number: #b0bec5;
- --theme-diff-added-line-number-bg: #e8f5e9;
- --theme-diff-removed-line-number-bg: #ffebee;
- --theme-markdown-text: #193549;
- --theme-markdown-heading: #ff9800;
- --theme-markdown-link: #0066cc;
- --theme-markdown-link-text: #00acc1;
- --theme-markdown-code: #4caf50;
- --theme-markdown-block-quote: #5c6b7d;
- --theme-markdown-emph: #ff5722;
- --theme-markdown-strong: #e91e63;
- --theme-markdown-horizontal-rule: #d3dae3;
- --theme-markdown-list-item: #0066cc;
- --theme-markdown-list-enumeration: #00acc1;
- --theme-markdown-image: #0066cc;
- --theme-markdown-image-text: #00acc1;
- --theme-markdown-code-block: #193549;
- --theme-syntax-comment: #5c6b7d;
- --theme-syntax-keyword: #ff5722;
- --theme-syntax-function: #ff9800;
- --theme-syntax-variable: #193549;
- --theme-syntax-string: #4caf50;
- --theme-syntax-number: #e91e63;
- --theme-syntax-type: #00acc1;
- --theme-syntax-operator: #ff5722;
- --theme-syntax-punctuation: #193549;
-}
-
-[data-theme="cobalt2"][data-dark="true"] {
- --theme-primary: #0088ff;
- --theme-secondary: #9a5feb;
- --theme-accent: #2affdf;
- --theme-error: #ff0088;
- --theme-warning: #ffc600;
- --theme-success: #9eff80;
- --theme-info: #ff9d00;
- --theme-text: #ffffff;
- --theme-text-muted: #adb7c9;
- --theme-background: #193549;
- --theme-background-panel: #122738;
- --theme-background-element: #1f4662;
- --theme-border: #1f4662;
- --theme-border-active: #0088ff;
- --theme-border-subtle: #0e1e2e;
- --theme-diff-added: #9eff80;
- --theme-diff-removed: #ff0088;
- --theme-diff-context: #adb7c9;
- --theme-diff-hunk-header: #2affdf;
- --theme-diff-highlight-added: #b9ff9f;
- --theme-diff-highlight-removed: #ff5fb3;
- --theme-diff-added-bg: #1a3a2a;
- --theme-diff-removed-bg: #3a1a2a;
- --theme-diff-context-bg: #122738;
- --theme-diff-line-number: #2d5a7b;
- --theme-diff-added-line-number-bg: #1a3a2a;
- --theme-diff-removed-line-number-bg: #3a1a2a;
- --theme-markdown-text: #ffffff;
- --theme-markdown-heading: #ffc600;
- --theme-markdown-link: #0088ff;
- --theme-markdown-link-text: #2affdf;
- --theme-markdown-code: #9eff80;
- --theme-markdown-block-quote: #adb7c9;
- --theme-markdown-emph: #ff9d00;
- --theme-markdown-strong: #ff628c;
- --theme-markdown-horizontal-rule: #2d5a7b;
- --theme-markdown-list-item: #0088ff;
- --theme-markdown-list-enumeration: #2affdf;
- --theme-markdown-image: #0088ff;
- --theme-markdown-image-text: #2affdf;
- --theme-markdown-code-block: #ffffff;
- --theme-syntax-comment: #0088ff;
- --theme-syntax-keyword: #ff9d00;
- --theme-syntax-function: #ffc600;
- --theme-syntax-variable: #ffffff;
- --theme-syntax-string: #9eff80;
- --theme-syntax-number: #ff628c;
- --theme-syntax-type: #2affdf;
- --theme-syntax-operator: #ff9d00;
- --theme-syntax-punctuation: #ffffff;
-}
-
-[data-theme="dracula"][data-dark="false"] {
- --theme-primary: #bd93f9;
- --theme-secondary: #ff79c6;
- --theme-accent: #8be9fd;
- --theme-error: #ff5555;
- --theme-warning: #f1fa8c;
- --theme-success: #50fa7b;
- --theme-info: #ffb86c;
- --theme-text: #282a36;
- --theme-text-muted: #6272a4;
- --theme-background: #f8f8f2;
- --theme-background-panel: #e8e8e2;
- --theme-background-element: #d8d8d2;
- --theme-border: #c8c8c2;
- --theme-border-active: #bd93f9;
- --theme-border-subtle: #e0e0e0;
- --theme-diff-added: #50fa7b;
- --theme-diff-removed: #ff5555;
- --theme-diff-context: #6272a4;
- --theme-diff-hunk-header: #6272a4;
- --theme-diff-highlight-added: #50fa7b;
- --theme-diff-highlight-removed: #ff5555;
- --theme-diff-added-bg: #e0ffe0;
- --theme-diff-removed-bg: #ffe0e0;
- --theme-diff-context-bg: #e8e8e2;
- --theme-diff-line-number: #c8c8c2;
- --theme-diff-added-line-number-bg: #e0ffe0;
- --theme-diff-removed-line-number-bg: #ffe0e0;
- --theme-markdown-text: #282a36;
- --theme-markdown-heading: #bd93f9;
- --theme-markdown-link: #8be9fd;
- --theme-markdown-link-text: #ff79c6;
- --theme-markdown-code: #50fa7b;
- --theme-markdown-block-quote: #6272a4;
- --theme-markdown-emph: #f1fa8c;
- --theme-markdown-strong: #ffb86c;
- --theme-markdown-horizontal-rule: #6272a4;
- --theme-markdown-list-item: #bd93f9;
- --theme-markdown-list-enumeration: #8be9fd;
- --theme-markdown-image: #8be9fd;
- --theme-markdown-image-text: #ff79c6;
- --theme-markdown-code-block: #282a36;
- --theme-syntax-comment: #6272a4;
- --theme-syntax-keyword: #ff79c6;
- --theme-syntax-function: #50fa7b;
- --theme-syntax-variable: #282a36;
- --theme-syntax-string: #f1fa8c;
- --theme-syntax-number: #bd93f9;
- --theme-syntax-type: #8be9fd;
- --theme-syntax-operator: #ff79c6;
- --theme-syntax-punctuation: #282a36;
-}
-
-[data-theme="dracula"][data-dark="true"] {
- --theme-primary: #bd93f9;
- --theme-secondary: #ff79c6;
- --theme-accent: #8be9fd;
- --theme-error: #ff5555;
- --theme-warning: #f1fa8c;
- --theme-success: #50fa7b;
- --theme-info: #ffb86c;
- --theme-text: #f8f8f2;
- --theme-text-muted: #6272a4;
- --theme-background: #282a36;
- --theme-background-panel: #21222c;
- --theme-background-element: #44475a;
- --theme-border: #44475a;
- --theme-border-active: #bd93f9;
- --theme-border-subtle: #191a21;
- --theme-diff-added: #50fa7b;
- --theme-diff-removed: #ff5555;
- --theme-diff-context: #6272a4;
- --theme-diff-hunk-header: #6272a4;
- --theme-diff-highlight-added: #50fa7b;
- --theme-diff-highlight-removed: #ff5555;
- --theme-diff-added-bg: #1a3a1a;
- --theme-diff-removed-bg: #3a1a1a;
- --theme-diff-context-bg: #21222c;
- --theme-diff-line-number: #44475a;
- --theme-diff-added-line-number-bg: #1a3a1a;
- --theme-diff-removed-line-number-bg: #3a1a1a;
- --theme-markdown-text: #f8f8f2;
- --theme-markdown-heading: #bd93f9;
- --theme-markdown-link: #8be9fd;
- --theme-markdown-link-text: #ff79c6;
- --theme-markdown-code: #50fa7b;
- --theme-markdown-block-quote: #6272a4;
- --theme-markdown-emph: #f1fa8c;
- --theme-markdown-strong: #ffb86c;
- --theme-markdown-horizontal-rule: #6272a4;
- --theme-markdown-list-item: #bd93f9;
- --theme-markdown-list-enumeration: #8be9fd;
- --theme-markdown-image: #8be9fd;
- --theme-markdown-image-text: #ff79c6;
- --theme-markdown-code-block: #f8f8f2;
- --theme-syntax-comment: #6272a4;
- --theme-syntax-keyword: #ff79c6;
- --theme-syntax-function: #50fa7b;
- --theme-syntax-variable: #f8f8f2;
- --theme-syntax-string: #f1fa8c;
- --theme-syntax-number: #bd93f9;
- --theme-syntax-type: #8be9fd;
- --theme-syntax-operator: #ff79c6;
- --theme-syntax-punctuation: #f8f8f2;
-}
-
-[data-theme="everforest"][data-dark="false"] {
- --theme-primary: #8da101;
- --theme-secondary: #3a94c5;
- --theme-accent: #df69ba;
- --theme-error: #f85552;
- --theme-warning: #f57d26;
- --theme-success: #8da101;
- --theme-info: #35a77c;
- --theme-text: #5c6a72;
- --theme-text-muted: #a6b0a0;
- --theme-background: #fdf6e3;
- --theme-background-panel: #efebd4;
- --theme-background-element: #f4f0d9;
- --theme-border: #939f91;
- --theme-border-active: #829181;
- --theme-border-subtle: #a6b0a0;
- --theme-diff-added: #1e725c;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #7086b5;
- --theme-diff-hunk-header: #7086b5;
- --theme-diff-highlight-added: #4db380;
- --theme-diff-highlight-removed: #f52a65;
- --theme-diff-added-bg: #d5e5d5;
- --theme-diff-removed-bg: #f7d8db;
- --theme-diff-context-bg: #efebd4;
- --theme-diff-line-number: #f4f0d9;
- --theme-diff-added-line-number-bg: #c5d5c5;
- --theme-diff-removed-line-number-bg: #e7c8cb;
- --theme-markdown-text: #5c6a72;
- --theme-markdown-heading: #df69ba;
- --theme-markdown-link: #8da101;
- --theme-markdown-link-text: #35a77c;
- --theme-markdown-code: #8da101;
- --theme-markdown-block-quote: #dfa000;
- --theme-markdown-emph: #dfa000;
- --theme-markdown-strong: #f57d26;
- --theme-markdown-horizontal-rule: #a6b0a0;
- --theme-markdown-list-item: #8da101;
- --theme-markdown-list-enumeration: #35a77c;
- --theme-markdown-image: #8da101;
- --theme-markdown-image-text: #35a77c;
- --theme-markdown-code-block: #5c6a72;
- --theme-syntax-comment: #a6b0a0;
- --theme-syntax-keyword: #df69ba;
- --theme-syntax-function: #8da101;
- --theme-syntax-variable: #f85552;
- --theme-syntax-string: #8da101;
- --theme-syntax-number: #f57d26;
- --theme-syntax-type: #dfa000;
- --theme-syntax-operator: #35a77c;
- --theme-syntax-punctuation: #5c6a72;
-}
-
-[data-theme="everforest"][data-dark="true"] {
- --theme-primary: #a7c080;
- --theme-secondary: #7fbbb3;
- --theme-accent: #d699b6;
- --theme-error: #e67e80;
- --theme-warning: #e69875;
- --theme-success: #a7c080;
- --theme-info: #83c092;
- --theme-text: #d3c6aa;
- --theme-text-muted: #7a8478;
- --theme-background: #2d353b;
- --theme-background-panel: #333c43;
- --theme-background-element: #343f44;
- --theme-border: #859289;
- --theme-border-active: #9da9a0;
- --theme-border-subtle: #7a8478;
- --theme-diff-added: #4fd6be;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #828bb8;
- --theme-diff-hunk-header: #828bb8;
- --theme-diff-highlight-added: #b8db87;
- --theme-diff-highlight-removed: #e26a75;
- --theme-diff-added-bg: #20303b;
- --theme-diff-removed-bg: #37222c;
- --theme-diff-context-bg: #333c43;
- --theme-diff-line-number: #343f44;
- --theme-diff-added-line-number-bg: #1b2b34;
- --theme-diff-removed-line-number-bg: #2d1f26;
- --theme-markdown-text: #d3c6aa;
- --theme-markdown-heading: #d699b6;
- --theme-markdown-link: #a7c080;
- --theme-markdown-link-text: #83c092;
- --theme-markdown-code: #a7c080;
- --theme-markdown-block-quote: #dbbc7f;
- --theme-markdown-emph: #dbbc7f;
- --theme-markdown-strong: #e69875;
- --theme-markdown-horizontal-rule: #7a8478;
- --theme-markdown-list-item: #a7c080;
- --theme-markdown-list-enumeration: #83c092;
- --theme-markdown-image: #a7c080;
- --theme-markdown-image-text: #83c092;
- --theme-markdown-code-block: #d3c6aa;
- --theme-syntax-comment: #7a8478;
- --theme-syntax-keyword: #d699b6;
- --theme-syntax-function: #a7c080;
- --theme-syntax-variable: #e67e80;
- --theme-syntax-string: #a7c080;
- --theme-syntax-number: #e69875;
- --theme-syntax-type: #dbbc7f;
- --theme-syntax-operator: #83c092;
- --theme-syntax-punctuation: #d3c6aa;
-}
-
-[data-theme="github"][data-dark="false"] {
- --theme-primary: #0969da;
- --theme-secondary: #8250df;
- --theme-accent: #1b7c83;
- --theme-error: #cf222e;
- --theme-warning: #9a6700;
- --theme-success: #1a7f37;
- --theme-info: #bc4c00;
- --theme-text: #24292f;
- --theme-text-muted: #57606a;
- --theme-background: #ffffff;
- --theme-background-panel: #f6f8fa;
- --theme-background-element: #f0f3f6;
- --theme-border: #d0d7de;
- --theme-border-active: #0969da;
- --theme-border-subtle: #d8dee4;
- --theme-diff-added: #1a7f37;
- --theme-diff-removed: #cf222e;
- --theme-diff-context: #57606a;
- --theme-diff-hunk-header: #0969da;
- --theme-diff-highlight-added: #1a7f37;
- --theme-diff-highlight-removed: #cf222e;
- --theme-diff-added-bg: #dafbe1;
- --theme-diff-removed-bg: #ffebe9;
- --theme-diff-context-bg: #f6f8fa;
- --theme-diff-line-number: #afb8c1;
- --theme-diff-added-line-number-bg: #dafbe1;
- --theme-diff-removed-line-number-bg: #ffebe9;
- --theme-markdown-text: #24292f;
- --theme-markdown-heading: #0969da;
- --theme-markdown-link: #0969da;
- --theme-markdown-link-text: #1b7c83;
- --theme-markdown-code: #bf3989;
- --theme-markdown-block-quote: #57606a;
- --theme-markdown-emph: #9a6700;
- --theme-markdown-strong: #bc4c00;
- --theme-markdown-horizontal-rule: #d0d7de;
- --theme-markdown-list-item: #0969da;
- --theme-markdown-list-enumeration: #1b7c83;
- --theme-markdown-image: #0969da;
- --theme-markdown-image-text: #1b7c83;
- --theme-markdown-code-block: #24292f;
- --theme-syntax-comment: #57606a;
- --theme-syntax-keyword: #cf222e;
- --theme-syntax-function: #8250df;
- --theme-syntax-variable: #bc4c00;
- --theme-syntax-string: #0969da;
- --theme-syntax-number: #1b7c83;
- --theme-syntax-type: #bc4c00;
- --theme-syntax-operator: #cf222e;
- --theme-syntax-punctuation: #24292f;
-}
-
-[data-theme="github"][data-dark="true"] {
- --theme-primary: #58a6ff;
- --theme-secondary: #bc8cff;
- --theme-accent: #39c5cf;
- --theme-error: #f85149;
- --theme-warning: #e3b341;
- --theme-success: #3fb950;
- --theme-info: #d29922;
- --theme-text: #c9d1d9;
- --theme-text-muted: #8b949e;
- --theme-background: #0d1117;
- --theme-background-panel: #010409;
- --theme-background-element: #161b22;
- --theme-border: #30363d;
- --theme-border-active: #58a6ff;
- --theme-border-subtle: #21262d;
- --theme-diff-added: #3fb950;
- --theme-diff-removed: #f85149;
- --theme-diff-context: #8b949e;
- --theme-diff-hunk-header: #58a6ff;
- --theme-diff-highlight-added: #3fb950;
- --theme-diff-highlight-removed: #f85149;
- --theme-diff-added-bg: #033a16;
- --theme-diff-removed-bg: #67060c;
- --theme-diff-context-bg: #010409;
- --theme-diff-line-number: #484f58;
- --theme-diff-added-line-number-bg: #033a16;
- --theme-diff-removed-line-number-bg: #67060c;
- --theme-markdown-text: #c9d1d9;
- --theme-markdown-heading: #58a6ff;
- --theme-markdown-link: #58a6ff;
- --theme-markdown-link-text: #39c5cf;
- --theme-markdown-code: #ff7b72;
- --theme-markdown-block-quote: #8b949e;
- --theme-markdown-emph: #e3b341;
- --theme-markdown-strong: #d29922;
- --theme-markdown-horizontal-rule: #30363d;
- --theme-markdown-list-item: #58a6ff;
- --theme-markdown-list-enumeration: #39c5cf;
- --theme-markdown-image: #58a6ff;
- --theme-markdown-image-text: #39c5cf;
- --theme-markdown-code-block: #c9d1d9;
- --theme-syntax-comment: #8b949e;
- --theme-syntax-keyword: #ff7b72;
- --theme-syntax-function: #bc8cff;
- --theme-syntax-variable: #d29922;
- --theme-syntax-string: #39c5cf;
- --theme-syntax-number: #58a6ff;
- --theme-syntax-type: #d29922;
- --theme-syntax-operator: #ff7b72;
- --theme-syntax-punctuation: #c9d1d9;
-}
-
-[data-theme="gruvbox"][data-dark="false"] {
- --theme-primary: #076678;
- --theme-secondary: #8f3f71;
- --theme-accent: #427b58;
- --theme-error: #9d0006;
- --theme-warning: #af3a03;
- --theme-success: #79740e;
- --theme-info: #b57614;
- --theme-text: #3c3836;
- --theme-text-muted: #7c6f64;
- --theme-background: #fbf1c7;
- --theme-background-panel: #ebdbb2;
- --theme-background-element: #d5c4a1;
- --theme-border: #bdae93;
- --theme-border-active: #3c3836;
- --theme-border-subtle: #d5c4a1;
- --theme-diff-added: #79740e;
- --theme-diff-removed: #9d0006;
- --theme-diff-context: #7c6f64;
- --theme-diff-hunk-header: #427b58;
- --theme-diff-highlight-added: #79740e;
- --theme-diff-highlight-removed: #9d0006;
- --theme-diff-added-bg: #e2e0b5;
- --theme-diff-removed-bg: #e9d8d5;
- --theme-diff-context-bg: #ebdbb2;
- --theme-diff-line-number: #bdae93;
- --theme-diff-added-line-number-bg: #d4d2a9;
- --theme-diff-removed-line-number-bg: #d8cbc8;
- --theme-markdown-text: #3c3836;
- --theme-markdown-heading: #076678;
- --theme-markdown-link: #427b58;
- --theme-markdown-link-text: #79740e;
- --theme-markdown-code: #b57614;
- --theme-markdown-block-quote: #7c6f64;
- --theme-markdown-emph: #8f3f71;
- --theme-markdown-strong: #af3a03;
- --theme-markdown-horizontal-rule: #7c6f64;
- --theme-markdown-list-item: #076678;
- --theme-markdown-list-enumeration: #427b58;
- --theme-markdown-image: #427b58;
- --theme-markdown-image-text: #79740e;
- --theme-markdown-code-block: #3c3836;
- --theme-syntax-comment: #7c6f64;
- --theme-syntax-keyword: #9d0006;
- --theme-syntax-function: #79740e;
- --theme-syntax-variable: #076678;
- --theme-syntax-string: #b57614;
- --theme-syntax-number: #8f3f71;
- --theme-syntax-type: #427b58;
- --theme-syntax-operator: #af3a03;
- --theme-syntax-punctuation: #3c3836;
-}
-
-[data-theme="gruvbox"][data-dark="true"] {
- --theme-primary: #83a598;
- --theme-secondary: #d3869b;
- --theme-accent: #8ec07c;
- --theme-error: #fb4934;
- --theme-warning: #fe8019;
- --theme-success: #b8bb26;
- --theme-info: #fabd2f;
- --theme-text: #ebdbb2;
- --theme-text-muted: #928374;
- --theme-background: #282828;
- --theme-background-panel: #3c3836;
- --theme-background-element: #504945;
- --theme-border: #665c54;
- --theme-border-active: #ebdbb2;
- --theme-border-subtle: #504945;
- --theme-diff-added: #98971a;
- --theme-diff-removed: #cc241d;
- --theme-diff-context: #928374;
- --theme-diff-hunk-header: #689d6a;
- --theme-diff-highlight-added: #b8bb26;
- --theme-diff-highlight-removed: #fb4934;
- --theme-diff-added-bg: #32302f;
- --theme-diff-removed-bg: #322929;
- --theme-diff-context-bg: #3c3836;
- --theme-diff-line-number: #665c54;
- --theme-diff-added-line-number-bg: #2a2827;
- --theme-diff-removed-line-number-bg: #2a2222;
- --theme-markdown-text: #ebdbb2;
- --theme-markdown-heading: #83a598;
- --theme-markdown-link: #8ec07c;
- --theme-markdown-link-text: #b8bb26;
- --theme-markdown-code: #fabd2f;
- --theme-markdown-block-quote: #928374;
- --theme-markdown-emph: #d3869b;
- --theme-markdown-strong: #fe8019;
- --theme-markdown-horizontal-rule: #928374;
- --theme-markdown-list-item: #83a598;
- --theme-markdown-list-enumeration: #8ec07c;
- --theme-markdown-image: #8ec07c;
- --theme-markdown-image-text: #b8bb26;
- --theme-markdown-code-block: #ebdbb2;
- --theme-syntax-comment: #928374;
- --theme-syntax-keyword: #fb4934;
- --theme-syntax-function: #b8bb26;
- --theme-syntax-variable: #83a598;
- --theme-syntax-string: #fabd2f;
- --theme-syntax-number: #d3869b;
- --theme-syntax-type: #8ec07c;
- --theme-syntax-operator: #fe8019;
- --theme-syntax-punctuation: #ebdbb2;
-}
-
-[data-theme="kanagawa"][data-dark="false"] {
- --theme-primary: #2d4f67;
- --theme-secondary: #957fb8;
- --theme-accent: #d27e99;
- --theme-error: #e82424;
- --theme-warning: #d7a657;
- --theme-success: #98bb6c;
- --theme-info: #76946a;
- --theme-text: #54433a;
- --theme-text-muted: #9e9389;
- --theme-background: #f2e9de;
- --theme-background-panel: #eae4d7;
- --theme-background-element: #e3dcd2;
- --theme-border: #d4cbbf;
- --theme-border-active: #c38d9d;
- --theme-border-subtle: #dcd4c9;
- --theme-diff-added: #98bb6c;
- --theme-diff-removed: #e82424;
- --theme-diff-context: #9e9389;
- --theme-diff-hunk-header: #2d4f67;
- --theme-diff-highlight-added: #89af5b;
- --theme-diff-highlight-removed: #d61f1f;
- --theme-diff-added-bg: #eaf3e4;
- --theme-diff-removed-bg: #fbe6e6;
- --theme-diff-context-bg: #eae4d7;
- --theme-diff-line-number: #c7beb4;
- --theme-diff-added-line-number-bg: #dde8d6;
- --theme-diff-removed-line-number-bg: #f2dada;
- --theme-markdown-text: #54433a;
- --theme-markdown-heading: #957fb8;
- --theme-markdown-link: #2d4f67;
- --theme-markdown-link-text: #76946a;
- --theme-markdown-code: #98bb6c;
- --theme-markdown-block-quote: #9e9389;
- --theme-markdown-emph: #c38d9d;
- --theme-markdown-strong: #d7a657;
- --theme-markdown-horizontal-rule: #9e9389;
- --theme-markdown-list-item: #2d4f67;
- --theme-markdown-list-enumeration: #76946a;
- --theme-markdown-image: #2d4f67;
- --theme-markdown-image-text: #76946a;
- --theme-markdown-code-block: #54433a;
- --theme-syntax-comment: #9e9389;
- --theme-syntax-keyword: #957fb8;
- --theme-syntax-function: #2d4f67;
- --theme-syntax-variable: #54433a;
- --theme-syntax-string: #98bb6c;
- --theme-syntax-number: #d7a657;
- --theme-syntax-type: #c38d9d;
- --theme-syntax-operator: #d27e99;
- --theme-syntax-punctuation: #54433a;
-}
-
-[data-theme="kanagawa"][data-dark="true"] {
- --theme-primary: #7e9cd8;
- --theme-secondary: #957fb8;
- --theme-accent: #d27e99;
- --theme-error: #e82424;
- --theme-warning: #d7a657;
- --theme-success: #98bb6c;
- --theme-info: #76946a;
- --theme-text: #dcd7ba;
- --theme-text-muted: #727169;
- --theme-background: #1f1f28;
- --theme-background-panel: #2a2a37;
- --theme-background-element: #363646;
- --theme-border: #54546d;
- --theme-border-active: #c38d9d;
- --theme-border-subtle: #363646;
- --theme-diff-added: #98bb6c;
- --theme-diff-removed: #e82424;
- --theme-diff-context: #727169;
- --theme-diff-hunk-header: #2d4f67;
- --theme-diff-highlight-added: #a9d977;
- --theme-diff-highlight-removed: #f24a4a;
- --theme-diff-added-bg: #252e25;
- --theme-diff-removed-bg: #362020;
- --theme-diff-context-bg: #2a2a37;
- --theme-diff-line-number: #54546d;
- --theme-diff-added-line-number-bg: #202820;
- --theme-diff-removed-line-number-bg: #2d1c1c;
- --theme-markdown-text: #dcd7ba;
- --theme-markdown-heading: #957fb8;
- --theme-markdown-link: #7e9cd8;
- --theme-markdown-link-text: #76946a;
- --theme-markdown-code: #98bb6c;
- --theme-markdown-block-quote: #727169;
- --theme-markdown-emph: #c38d9d;
- --theme-markdown-strong: #d7a657;
- --theme-markdown-horizontal-rule: #727169;
- --theme-markdown-list-item: #7e9cd8;
- --theme-markdown-list-enumeration: #76946a;
- --theme-markdown-image: #7e9cd8;
- --theme-markdown-image-text: #76946a;
- --theme-markdown-code-block: #dcd7ba;
- --theme-syntax-comment: #727169;
- --theme-syntax-keyword: #957fb8;
- --theme-syntax-function: #7e9cd8;
- --theme-syntax-variable: #dcd7ba;
- --theme-syntax-string: #98bb6c;
- --theme-syntax-number: #d7a657;
- --theme-syntax-type: #c38d9d;
- --theme-syntax-operator: #d27e99;
- --theme-syntax-punctuation: #dcd7ba;
-}
-
-[data-theme="material"][data-dark="false"] {
- --theme-primary: #6182b8;
- --theme-secondary: #7c4dff;
- --theme-accent: #39adb5;
- --theme-error: #e53935;
- --theme-warning: #ffb300;
- --theme-success: #91b859;
- --theme-info: #f4511e;
- --theme-text: #263238;
- --theme-text-muted: #90a4ae;
- --theme-background: #fafafa;
- --theme-background-panel: #f5f5f5;
- --theme-background-element: #e7e7e8;
- --theme-border: #e0e0e0;
- --theme-border-active: #6182b8;
- --theme-border-subtle: #eeeeee;
- --theme-diff-added: #91b859;
- --theme-diff-removed: #e53935;
- --theme-diff-context: #90a4ae;
- --theme-diff-hunk-header: #39adb5;
- --theme-diff-highlight-added: #91b859;
- --theme-diff-highlight-removed: #e53935;
- --theme-diff-added-bg: #e8f5e9;
- --theme-diff-removed-bg: #ffebee;
- --theme-diff-context-bg: #f5f5f5;
- --theme-diff-line-number: #cfd8dc;
- --theme-diff-added-line-number-bg: #e8f5e9;
- --theme-diff-removed-line-number-bg: #ffebee;
- --theme-markdown-text: #263238;
- --theme-markdown-heading: #6182b8;
- --theme-markdown-link: #39adb5;
- --theme-markdown-link-text: #7c4dff;
- --theme-markdown-code: #91b859;
- --theme-markdown-block-quote: #90a4ae;
- --theme-markdown-emph: #ffb300;
- --theme-markdown-strong: #f4511e;
- --theme-markdown-horizontal-rule: #e0e0e0;
- --theme-markdown-list-item: #6182b8;
- --theme-markdown-list-enumeration: #39adb5;
- --theme-markdown-image: #39adb5;
- --theme-markdown-image-text: #7c4dff;
- --theme-markdown-code-block: #263238;
- --theme-syntax-comment: #90a4ae;
- --theme-syntax-keyword: #7c4dff;
- --theme-syntax-function: #6182b8;
- --theme-syntax-variable: #263238;
- --theme-syntax-string: #91b859;
- --theme-syntax-number: #f4511e;
- --theme-syntax-type: #ffb300;
- --theme-syntax-operator: #39adb5;
- --theme-syntax-punctuation: #263238;
-}
-
-[data-theme="material"][data-dark="true"] {
- --theme-primary: #82aaff;
- --theme-secondary: #c792ea;
- --theme-accent: #89ddff;
- --theme-error: #f07178;
- --theme-warning: #ffcb6b;
- --theme-success: #c3e88d;
- --theme-info: #ffcb6b;
- --theme-text: #eeffff;
- --theme-text-muted: #546e7a;
- --theme-background: #263238;
- --theme-background-panel: #1e272c;
- --theme-background-element: #37474f;
- --theme-border: #37474f;
- --theme-border-active: #82aaff;
- --theme-border-subtle: #1e272c;
- --theme-diff-added: #c3e88d;
- --theme-diff-removed: #f07178;
- --theme-diff-context: #546e7a;
- --theme-diff-hunk-header: #89ddff;
- --theme-diff-highlight-added: #c3e88d;
- --theme-diff-highlight-removed: #f07178;
- --theme-diff-added-bg: #2e3c2b;
- --theme-diff-removed-bg: #3c2b2b;
- --theme-diff-context-bg: #1e272c;
- --theme-diff-line-number: #37474f;
- --theme-diff-added-line-number-bg: #2e3c2b;
- --theme-diff-removed-line-number-bg: #3c2b2b;
- --theme-markdown-text: #eeffff;
- --theme-markdown-heading: #82aaff;
- --theme-markdown-link: #89ddff;
- --theme-markdown-link-text: #c792ea;
- --theme-markdown-code: #c3e88d;
- --theme-markdown-block-quote: #546e7a;
- --theme-markdown-emph: #ffcb6b;
- --theme-markdown-strong: #ffcb6b;
- --theme-markdown-horizontal-rule: #37474f;
- --theme-markdown-list-item: #82aaff;
- --theme-markdown-list-enumeration: #89ddff;
- --theme-markdown-image: #89ddff;
- --theme-markdown-image-text: #c792ea;
- --theme-markdown-code-block: #eeffff;
- --theme-syntax-comment: #546e7a;
- --theme-syntax-keyword: #c792ea;
- --theme-syntax-function: #82aaff;
- --theme-syntax-variable: #eeffff;
- --theme-syntax-string: #c3e88d;
- --theme-syntax-number: #ffcb6b;
- --theme-syntax-type: #ffcb6b;
- --theme-syntax-operator: #89ddff;
- --theme-syntax-punctuation: #eeffff;
-}
-
-[data-theme="matrix"][data-dark="false"] {
- --theme-primary: #1cc24b;
- --theme-secondary: #24f6d9;
- --theme-accent: #c770ff;
- --theme-error: #ff4b4b;
- --theme-warning: #e6ff57;
- --theme-success: #1cc24b;
- --theme-info: #30b3ff;
- --theme-text: #203022;
- --theme-text-muted: #748476;
- --theme-background: #eef3ea;
- --theme-background-panel: #e4ebe1;
- --theme-background-element: #dae1d7;
- --theme-border: #748476;
- --theme-border-active: #1cc24b;
- --theme-border-subtle: #dae1d7;
- --theme-diff-added: #1cc24b;
- --theme-diff-removed: #ff4b4b;
- --theme-diff-context: #748476;
- --theme-diff-hunk-header: #30b3ff;
- --theme-diff-highlight-added: #5dac7e;
- --theme-diff-highlight-removed: #d53a3a;
- --theme-diff-added-bg: #e0efde;
- --theme-diff-removed-bg: #f9e5e5;
- --theme-diff-context-bg: #e4ebe1;
- --theme-diff-line-number: #748476;
- --theme-diff-added-line-number-bg: #d6e7d2;
- --theme-diff-removed-line-number-bg: #f2d2d2;
- --theme-markdown-text: #203022;
- --theme-markdown-heading: #24f6d9;
- --theme-markdown-link: #30b3ff;
- --theme-markdown-link-text: #24f6d9;
- --theme-markdown-code: #1cc24b;
- --theme-markdown-block-quote: #748476;
- --theme-markdown-emph: #ffa83d;
- --theme-markdown-strong: #e6ff57;
- --theme-markdown-horizontal-rule: #748476;
- --theme-markdown-list-item: #30b3ff;
- --theme-markdown-list-enumeration: #24f6d9;
- --theme-markdown-image: #30b3ff;
- --theme-markdown-image-text: #24f6d9;
- --theme-markdown-code-block: #203022;
- --theme-syntax-comment: #748476;
- --theme-syntax-keyword: #c770ff;
- --theme-syntax-function: #30b3ff;
- --theme-syntax-variable: #203022;
- --theme-syntax-string: #1cc24b;
- --theme-syntax-number: #ffa83d;
- --theme-syntax-type: #e6ff57;
- --theme-syntax-operator: #24f6d9;
- --theme-syntax-punctuation: #203022;
-}
-
-[data-theme="matrix"][data-dark="true"] {
- --theme-primary: #2eff6a;
- --theme-secondary: #00efff;
- --theme-accent: #c770ff;
- --theme-error: #ff4b4b;
- --theme-warning: #e6ff57;
- --theme-success: #62ff94;
- --theme-info: #30b3ff;
- --theme-text: #62ff94;
- --theme-text-muted: #8ca391;
- --theme-background: #0a0e0a;
- --theme-background-panel: #0e130d;
- --theme-background-element: #141c12;
- --theme-border: #1e2a1b;
- --theme-border-active: #2eff6a;
- --theme-border-subtle: #141c12;
- --theme-diff-added: #1cc24b;
- --theme-diff-removed: #ff4b4b;
- --theme-diff-context: #8ca391;
- --theme-diff-hunk-header: #30b3ff;
- --theme-diff-highlight-added: #77ffaf;
- --theme-diff-highlight-removed: #ff7171;
- --theme-diff-added-bg: #132616;
- --theme-diff-removed-bg: #261212;
- --theme-diff-context-bg: #0e130d;
- --theme-diff-line-number: #1e2a1b;
- --theme-diff-added-line-number-bg: #0f1b11;
- --theme-diff-removed-line-number-bg: #1b1414;
- --theme-markdown-text: #62ff94;
- --theme-markdown-heading: #00efff;
- --theme-markdown-link: #30b3ff;
- --theme-markdown-link-text: #24f6d9;
- --theme-markdown-code: #1cc24b;
- --theme-markdown-block-quote: #8ca391;
- --theme-markdown-emph: #ffa83d;
- --theme-markdown-strong: #e6ff57;
- --theme-markdown-horizontal-rule: #8ca391;
- --theme-markdown-list-item: #30b3ff;
- --theme-markdown-list-enumeration: #24f6d9;
- --theme-markdown-image: #30b3ff;
- --theme-markdown-image-text: #24f6d9;
- --theme-markdown-code-block: #62ff94;
- --theme-syntax-comment: #8ca391;
- --theme-syntax-keyword: #c770ff;
- --theme-syntax-function: #30b3ff;
- --theme-syntax-variable: #62ff94;
- --theme-syntax-string: #1cc24b;
- --theme-syntax-number: #ffa83d;
- --theme-syntax-type: #e6ff57;
- --theme-syntax-operator: #24f6d9;
- --theme-syntax-punctuation: #62ff94;
-}
-
-[data-theme="monokai"][data-dark="false"] {
- --theme-primary: #66d9ef;
- --theme-secondary: #ae81ff;
- --theme-accent: #a6e22e;
- --theme-error: #f92672;
- --theme-warning: #fd971f;
- --theme-success: #a6e22e;
- --theme-info: #fd971f;
- --theme-text: #272822;
- --theme-text-muted: #75715e;
- --theme-background: #fafafa;
- --theme-background-panel: #f0f0f0;
- --theme-background-element: #e0e0e0;
- --theme-border: #d0d0d0;
- --theme-border-active: #66d9ef;
- --theme-border-subtle: #e8e8e8;
- --theme-diff-added: #a6e22e;
- --theme-diff-removed: #f92672;
- --theme-diff-context: #75715e;
- --theme-diff-hunk-header: #75715e;
- --theme-diff-highlight-added: #a6e22e;
- --theme-diff-highlight-removed: #f92672;
- --theme-diff-added-bg: #e0ffe0;
- --theme-diff-removed-bg: #ffe0e0;
- --theme-diff-context-bg: #f0f0f0;
- --theme-diff-line-number: #d0d0d0;
- --theme-diff-added-line-number-bg: #e0ffe0;
- --theme-diff-removed-line-number-bg: #ffe0e0;
- --theme-markdown-text: #272822;
- --theme-markdown-heading: #f92672;
- --theme-markdown-link: #66d9ef;
- --theme-markdown-link-text: #ae81ff;
- --theme-markdown-code: #a6e22e;
- --theme-markdown-block-quote: #75715e;
- --theme-markdown-emph: #fd971f;
- --theme-markdown-strong: #fd971f;
- --theme-markdown-horizontal-rule: #75715e;
- --theme-markdown-list-item: #66d9ef;
- --theme-markdown-list-enumeration: #ae81ff;
- --theme-markdown-image: #66d9ef;
- --theme-markdown-image-text: #ae81ff;
- --theme-markdown-code-block: #272822;
- --theme-syntax-comment: #75715e;
- --theme-syntax-keyword: #f92672;
- --theme-syntax-function: #a6e22e;
- --theme-syntax-variable: #272822;
- --theme-syntax-string: #fd971f;
- --theme-syntax-number: #ae81ff;
- --theme-syntax-type: #66d9ef;
- --theme-syntax-operator: #f92672;
- --theme-syntax-punctuation: #272822;
-}
-
-[data-theme="monokai"][data-dark="true"] {
- --theme-primary: #66d9ef;
- --theme-secondary: #ae81ff;
- --theme-accent: #a6e22e;
- --theme-error: #f92672;
- --theme-warning: #e6db74;
- --theme-success: #a6e22e;
- --theme-info: #fd971f;
- --theme-text: #f8f8f2;
- --theme-text-muted: #75715e;
- --theme-background: #272822;
- --theme-background-panel: #1e1f1c;
- --theme-background-element: #3e3d32;
- --theme-border: #3e3d32;
- --theme-border-active: #66d9ef;
- --theme-border-subtle: #1e1f1c;
- --theme-diff-added: #a6e22e;
- --theme-diff-removed: #f92672;
- --theme-diff-context: #75715e;
- --theme-diff-hunk-header: #75715e;
- --theme-diff-highlight-added: #a6e22e;
- --theme-diff-highlight-removed: #f92672;
- --theme-diff-added-bg: #1a3a1a;
- --theme-diff-removed-bg: #3a1a1a;
- --theme-diff-context-bg: #1e1f1c;
- --theme-diff-line-number: #3e3d32;
- --theme-diff-added-line-number-bg: #1a3a1a;
- --theme-diff-removed-line-number-bg: #3a1a1a;
- --theme-markdown-text: #f8f8f2;
- --theme-markdown-heading: #f92672;
- --theme-markdown-link: #66d9ef;
- --theme-markdown-link-text: #ae81ff;
- --theme-markdown-code: #a6e22e;
- --theme-markdown-block-quote: #75715e;
- --theme-markdown-emph: #e6db74;
- --theme-markdown-strong: #fd971f;
- --theme-markdown-horizontal-rule: #75715e;
- --theme-markdown-list-item: #66d9ef;
- --theme-markdown-list-enumeration: #ae81ff;
- --theme-markdown-image: #66d9ef;
- --theme-markdown-image-text: #ae81ff;
- --theme-markdown-code-block: #f8f8f2;
- --theme-syntax-comment: #75715e;
- --theme-syntax-keyword: #f92672;
- --theme-syntax-function: #a6e22e;
- --theme-syntax-variable: #f8f8f2;
- --theme-syntax-string: #e6db74;
- --theme-syntax-number: #ae81ff;
- --theme-syntax-type: #66d9ef;
- --theme-syntax-operator: #f92672;
- --theme-syntax-punctuation: #f8f8f2;
-}
-
-[data-theme="nord"][data-dark="false"] {
- --theme-primary: #5e81ac;
- --theme-secondary: #81a1c1;
- --theme-accent: #8fbcbb;
- --theme-error: #bf616a;
- --theme-warning: #d08770;
- --theme-success: #a3be8c;
- --theme-info: #5e81ac;
- --theme-text: #2e3440;
- --theme-text-muted: #3b4252;
- --theme-background: #eceff4;
- --theme-background-panel: #e5e9f0;
- --theme-background-element: #d8dee9;
- --theme-border: #4c566a;
- --theme-border-active: #434c5e;
- --theme-border-subtle: #4c566a;
- --theme-diff-added: #a3be8c;
- --theme-diff-removed: #bf616a;
- --theme-diff-context: #4c566a;
- --theme-diff-hunk-header: #4c566a;
- --theme-diff-highlight-added: #a3be8c;
- --theme-diff-highlight-removed: #bf616a;
- --theme-diff-added-bg: #e5e9f0;
- --theme-diff-removed-bg: #e5e9f0;
- --theme-diff-context-bg: #e5e9f0;
- --theme-diff-line-number: #d8dee9;
- --theme-diff-added-line-number-bg: #e5e9f0;
- --theme-diff-removed-line-number-bg: #e5e9f0;
- --theme-markdown-text: #2e3440;
- --theme-markdown-heading: #5e81ac;
- --theme-markdown-link: #81a1c1;
- --theme-markdown-link-text: #8fbcbb;
- --theme-markdown-code: #a3be8c;
- --theme-markdown-block-quote: #4c566a;
- --theme-markdown-emph: #d08770;
- --theme-markdown-strong: #ebcb8b;
- --theme-markdown-horizontal-rule: #4c566a;
- --theme-markdown-list-item: #5e81ac;
- --theme-markdown-list-enumeration: #8fbcbb;
- --theme-markdown-image: #81a1c1;
- --theme-markdown-image-text: #8fbcbb;
- --theme-markdown-code-block: #2e3440;
- --theme-syntax-comment: #4c566a;
- --theme-syntax-keyword: #81a1c1;
- --theme-syntax-function: #88c0d0;
- --theme-syntax-variable: #8fbcbb;
- --theme-syntax-string: #a3be8c;
- --theme-syntax-number: #b48ead;
- --theme-syntax-type: #8fbcbb;
- --theme-syntax-operator: #81a1c1;
- --theme-syntax-punctuation: #2e3440;
-}
-
-[data-theme="nord"][data-dark="true"] {
- --theme-primary: #88c0d0;
- --theme-secondary: #81a1c1;
- --theme-accent: #8fbcbb;
- --theme-error: #bf616a;
- --theme-warning: #d08770;
- --theme-success: #a3be8c;
- --theme-info: #88c0d0;
- --theme-text: #eceff4;
- --theme-text-muted: #8b95a7;
- --theme-background: #2e3440;
- --theme-background-panel: #3b4252;
- --theme-background-element: #434c5e;
- --theme-border: #434c5e;
- --theme-border-active: #4c566a;
- --theme-border-subtle: #434c5e;
- --theme-diff-added: #a3be8c;
- --theme-diff-removed: #bf616a;
- --theme-diff-context: #8b95a7;
- --theme-diff-hunk-header: #8b95a7;
- --theme-diff-highlight-added: #a3be8c;
- --theme-diff-highlight-removed: #bf616a;
- --theme-diff-added-bg: #3b4252;
- --theme-diff-removed-bg: #3b4252;
- --theme-diff-context-bg: #3b4252;
- --theme-diff-line-number: #434c5e;
- --theme-diff-added-line-number-bg: #3b4252;
- --theme-diff-removed-line-number-bg: #3b4252;
- --theme-markdown-text: #d8dee9;
- --theme-markdown-heading: #88c0d0;
- --theme-markdown-link: #81a1c1;
- --theme-markdown-link-text: #8fbcbb;
- --theme-markdown-code: #a3be8c;
- --theme-markdown-block-quote: #8b95a7;
- --theme-markdown-emph: #d08770;
- --theme-markdown-strong: #ebcb8b;
- --theme-markdown-horizontal-rule: #8b95a7;
- --theme-markdown-list-item: #88c0d0;
- --theme-markdown-list-enumeration: #8fbcbb;
- --theme-markdown-image: #81a1c1;
- --theme-markdown-image-text: #8fbcbb;
- --theme-markdown-code-block: #d8dee9;
- --theme-syntax-comment: #8b95a7;
- --theme-syntax-keyword: #81a1c1;
- --theme-syntax-function: #88c0d0;
- --theme-syntax-variable: #8fbcbb;
- --theme-syntax-string: #a3be8c;
- --theme-syntax-number: #b48ead;
- --theme-syntax-type: #8fbcbb;
- --theme-syntax-operator: #81a1c1;
- --theme-syntax-punctuation: #d8dee9;
-}
-
-[data-theme="one-dark"][data-dark="false"] {
- --theme-primary: #4078f2;
- --theme-secondary: #a626a4;
- --theme-accent: #0184bc;
- --theme-error: #e45649;
- --theme-warning: #c18401;
- --theme-success: #50a14f;
- --theme-info: #986801;
- --theme-text: #383a42;
- --theme-text-muted: #a0a1a7;
- --theme-background: #fafafa;
- --theme-background-panel: #f0f0f1;
- --theme-background-element: #eaeaeb;
- --theme-border: #d1d1d2;
- --theme-border-active: #4078f2;
- --theme-border-subtle: #e0e0e1;
- --theme-diff-added: #50a14f;
- --theme-diff-removed: #e45649;
- --theme-diff-context: #a0a1a7;
- --theme-diff-hunk-header: #0184bc;
- --theme-diff-highlight-added: #489447;
- --theme-diff-highlight-removed: #d65145;
- --theme-diff-added-bg: #eafbe9;
- --theme-diff-removed-bg: #fce9e8;
- --theme-diff-context-bg: #f0f0f1;
- --theme-diff-line-number: #c9c9ca;
- --theme-diff-added-line-number-bg: #e1f3df;
- --theme-diff-removed-line-number-bg: #f5e2e1;
- --theme-markdown-text: #383a42;
- --theme-markdown-heading: #a626a4;
- --theme-markdown-link: #4078f2;
- --theme-markdown-link-text: #0184bc;
- --theme-markdown-code: #50a14f;
- --theme-markdown-block-quote: #a0a1a7;
- --theme-markdown-emph: #c18401;
- --theme-markdown-strong: #986801;
- --theme-markdown-horizontal-rule: #a0a1a7;
- --theme-markdown-list-item: #4078f2;
- --theme-markdown-list-enumeration: #0184bc;
- --theme-markdown-image: #4078f2;
- --theme-markdown-image-text: #0184bc;
- --theme-markdown-code-block: #383a42;
- --theme-syntax-comment: #a0a1a7;
- --theme-syntax-keyword: #a626a4;
- --theme-syntax-function: #4078f2;
- --theme-syntax-variable: #e45649;
- --theme-syntax-string: #50a14f;
- --theme-syntax-number: #986801;
- --theme-syntax-type: #c18401;
- --theme-syntax-operator: #0184bc;
- --theme-syntax-punctuation: #383a42;
-}
-
-[data-theme="one-dark"][data-dark="true"] {
- --theme-primary: #61afef;
- --theme-secondary: #c678dd;
- --theme-accent: #56b6c2;
- --theme-error: #e06c75;
- --theme-warning: #e5c07b;
- --theme-success: #98c379;
- --theme-info: #d19a66;
- --theme-text: #abb2bf;
- --theme-text-muted: #5c6370;
- --theme-background: #282c34;
- --theme-background-panel: #21252b;
- --theme-background-element: #353b45;
- --theme-border: #393f4a;
- --theme-border-active: #61afef;
- --theme-border-subtle: #2c313a;
- --theme-diff-added: #98c379;
- --theme-diff-removed: #e06c75;
- --theme-diff-context: #5c6370;
- --theme-diff-hunk-header: #56b6c2;
- --theme-diff-highlight-added: #aad482;
- --theme-diff-highlight-removed: #e8828b;
- --theme-diff-added-bg: #2c382b;
- --theme-diff-removed-bg: #3a2d2f;
- --theme-diff-context-bg: #21252b;
- --theme-diff-line-number: #495162;
- --theme-diff-added-line-number-bg: #283427;
- --theme-diff-removed-line-number-bg: #36292b;
- --theme-markdown-text: #abb2bf;
- --theme-markdown-heading: #c678dd;
- --theme-markdown-link: #61afef;
- --theme-markdown-link-text: #56b6c2;
- --theme-markdown-code: #98c379;
- --theme-markdown-block-quote: #5c6370;
- --theme-markdown-emph: #e5c07b;
- --theme-markdown-strong: #d19a66;
- --theme-markdown-horizontal-rule: #5c6370;
- --theme-markdown-list-item: #61afef;
- --theme-markdown-list-enumeration: #56b6c2;
- --theme-markdown-image: #61afef;
- --theme-markdown-image-text: #56b6c2;
- --theme-markdown-code-block: #abb2bf;
- --theme-syntax-comment: #5c6370;
- --theme-syntax-keyword: #c678dd;
- --theme-syntax-function: #61afef;
- --theme-syntax-variable: #e06c75;
- --theme-syntax-string: #98c379;
- --theme-syntax-number: #d19a66;
- --theme-syntax-type: #e5c07b;
- --theme-syntax-operator: #56b6c2;
- --theme-syntax-punctuation: #abb2bf;
-}
-
-[data-theme="opencode"][data-dark="false"] {
- --theme-primary: #3b7dd8;
- --theme-secondary: #7b5bb6;
- --theme-accent: #d68c27;
- --theme-error: #d1383d;
- --theme-warning: #d68c27;
- --theme-success: #3d9a57;
- --theme-info: #318795;
- --theme-text: #1a1a1a;
- --theme-text-muted: #8a8a8a;
- --theme-background: #ffffff;
- --theme-background-panel: #fafafa;
- --theme-background-element: #f5f5f5;
- --theme-border: #b8b8b8;
- --theme-border-active: #a0a0a0;
- --theme-border-subtle: #d4d4d4;
- --theme-diff-added: #1e725c;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #7086b5;
- --theme-diff-hunk-header: #7086b5;
- --theme-diff-highlight-added: #4db380;
- --theme-diff-highlight-removed: #f52a65;
- --theme-diff-added-bg: #d5e5d5;
- --theme-diff-removed-bg: #f7d8db;
- --theme-diff-context-bg: #fafafa;
- --theme-diff-line-number: #f5f5f5;
- --theme-diff-added-line-number-bg: #c5d5c5;
- --theme-diff-removed-line-number-bg: #e7c8cb;
- --theme-markdown-text: #1a1a1a;
- --theme-markdown-heading: #d68c27;
- --theme-markdown-link: #3b7dd8;
- --theme-markdown-link-text: #318795;
- --theme-markdown-code: #3d9a57;
- --theme-markdown-block-quote: #b0851f;
- --theme-markdown-emph: #b0851f;
- --theme-markdown-strong: #d68c27;
- --theme-markdown-horizontal-rule: #8a8a8a;
- --theme-markdown-list-item: #3b7dd8;
- --theme-markdown-list-enumeration: #318795;
- --theme-markdown-image: #3b7dd8;
- --theme-markdown-image-text: #318795;
- --theme-markdown-code-block: #1a1a1a;
- --theme-syntax-comment: #8a8a8a;
- --theme-syntax-keyword: #d68c27;
- --theme-syntax-function: #3b7dd8;
- --theme-syntax-variable: #d1383d;
- --theme-syntax-string: #3d9a57;
- --theme-syntax-number: #d68c27;
- --theme-syntax-type: #b0851f;
- --theme-syntax-operator: #318795;
- --theme-syntax-punctuation: #1a1a1a;
-}
-
-[data-theme="opencode"][data-dark="true"] {
- --theme-primary: #fab283;
- --theme-secondary: #5c9cf5;
- --theme-accent: #9d7cd8;
- --theme-error: #e06c75;
- --theme-warning: #f5a742;
- --theme-success: #7fd88f;
- --theme-info: #56b6c2;
- --theme-text: #eeeeee;
- --theme-text-muted: #808080;
- --theme-background: #0a0a0a;
- --theme-background-panel: #141414;
- --theme-background-element: #1e1e1e;
- --theme-border: #484848;
- --theme-border-active: #606060;
- --theme-border-subtle: #3c3c3c;
- --theme-diff-added: #4fd6be;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #828bb8;
- --theme-diff-hunk-header: #828bb8;
- --theme-diff-highlight-added: #b8db87;
- --theme-diff-highlight-removed: #e26a75;
- --theme-diff-added-bg: #20303b;
- --theme-diff-removed-bg: #37222c;
- --theme-diff-context-bg: #141414;
- --theme-diff-line-number: #1e1e1e;
- --theme-diff-added-line-number-bg: #1b2b34;
- --theme-diff-removed-line-number-bg: #2d1f26;
- --theme-markdown-text: #eeeeee;
- --theme-markdown-heading: #9d7cd8;
- --theme-markdown-link: #fab283;
- --theme-markdown-link-text: #56b6c2;
- --theme-markdown-code: #7fd88f;
- --theme-markdown-block-quote: #e5c07b;
- --theme-markdown-emph: #e5c07b;
- --theme-markdown-strong: #f5a742;
- --theme-markdown-horizontal-rule: #808080;
- --theme-markdown-list-item: #fab283;
- --theme-markdown-list-enumeration: #56b6c2;
- --theme-markdown-image: #fab283;
- --theme-markdown-image-text: #56b6c2;
- --theme-markdown-code-block: #eeeeee;
- --theme-syntax-comment: #808080;
- --theme-syntax-keyword: #9d7cd8;
- --theme-syntax-function: #fab283;
- --theme-syntax-variable: #e06c75;
- --theme-syntax-string: #7fd88f;
- --theme-syntax-number: #f5a742;
- --theme-syntax-type: #e5c07b;
- --theme-syntax-operator: #56b6c2;
- --theme-syntax-punctuation: #eeeeee;
-}
-
-[data-theme="palenight"][data-dark="false"] {
- --theme-primary: #4976eb;
- --theme-secondary: #a854f2;
- --theme-accent: #00acc1;
- --theme-error: #e53935;
- --theme-warning: #ffb300;
- --theme-success: #91b859;
- --theme-info: #f4511e;
- --theme-text: #292d3e;
- --theme-text-muted: #8796b0;
- --theme-background: #fafafa;
- --theme-background-panel: #f5f5f5;
- --theme-background-element: #e7e7e8;
- --theme-border: #e0e0e0;
- --theme-border-active: #4976eb;
- --theme-border-subtle: #eeeeee;
- --theme-diff-added: #91b859;
- --theme-diff-removed: #e53935;
- --theme-diff-context: #8796b0;
- --theme-diff-hunk-header: #00acc1;
- --theme-diff-highlight-added: #91b859;
- --theme-diff-highlight-removed: #e53935;
- --theme-diff-added-bg: #e8f5e9;
- --theme-diff-removed-bg: #ffebee;
- --theme-diff-context-bg: #f5f5f5;
- --theme-diff-line-number: #cfd8dc;
- --theme-diff-added-line-number-bg: #e8f5e9;
- --theme-diff-removed-line-number-bg: #ffebee;
- --theme-markdown-text: #292d3e;
- --theme-markdown-heading: #a854f2;
- --theme-markdown-link: #4976eb;
- --theme-markdown-link-text: #00acc1;
- --theme-markdown-code: #91b859;
- --theme-markdown-block-quote: #8796b0;
- --theme-markdown-emph: #ffb300;
- --theme-markdown-strong: #f4511e;
- --theme-markdown-horizontal-rule: #8796b0;
- --theme-markdown-list-item: #4976eb;
- --theme-markdown-list-enumeration: #00acc1;
- --theme-markdown-image: #4976eb;
- --theme-markdown-image-text: #00acc1;
- --theme-markdown-code-block: #292d3e;
- --theme-syntax-comment: #8796b0;
- --theme-syntax-keyword: #a854f2;
- --theme-syntax-function: #4976eb;
- --theme-syntax-variable: #292d3e;
- --theme-syntax-string: #91b859;
- --theme-syntax-number: #f4511e;
- --theme-syntax-type: #ffb300;
- --theme-syntax-operator: #00acc1;
- --theme-syntax-punctuation: #292d3e;
-}
-
-[data-theme="palenight"][data-dark="true"] {
- --theme-primary: #82aaff;
- --theme-secondary: #c792ea;
- --theme-accent: #89ddff;
- --theme-error: #f07178;
- --theme-warning: #ffcb6b;
- --theme-success: #c3e88d;
- --theme-info: #f78c6c;
- --theme-text: #a6accd;
- --theme-text-muted: #676e95;
- --theme-background: #292d3e;
- --theme-background-panel: #1e2132;
- --theme-background-element: #32364a;
- --theme-border: #32364a;
- --theme-border-active: #82aaff;
- --theme-border-subtle: #1e2132;
- --theme-diff-added: #c3e88d;
- --theme-diff-removed: #f07178;
- --theme-diff-context: #676e95;
- --theme-diff-hunk-header: #89ddff;
- --theme-diff-highlight-added: #c3e88d;
- --theme-diff-highlight-removed: #f07178;
- --theme-diff-added-bg: #2e3c2b;
- --theme-diff-removed-bg: #3c2b2b;
- --theme-diff-context-bg: #1e2132;
- --theme-diff-line-number: #444760;
- --theme-diff-added-line-number-bg: #2e3c2b;
- --theme-diff-removed-line-number-bg: #3c2b2b;
- --theme-markdown-text: #a6accd;
- --theme-markdown-heading: #c792ea;
- --theme-markdown-link: #82aaff;
- --theme-markdown-link-text: #89ddff;
- --theme-markdown-code: #c3e88d;
- --theme-markdown-block-quote: #676e95;
- --theme-markdown-emph: #ffcb6b;
- --theme-markdown-strong: #f78c6c;
- --theme-markdown-horizontal-rule: #676e95;
- --theme-markdown-list-item: #82aaff;
- --theme-markdown-list-enumeration: #89ddff;
- --theme-markdown-image: #82aaff;
- --theme-markdown-image-text: #89ddff;
- --theme-markdown-code-block: #a6accd;
- --theme-syntax-comment: #676e95;
- --theme-syntax-keyword: #c792ea;
- --theme-syntax-function: #82aaff;
- --theme-syntax-variable: #a6accd;
- --theme-syntax-string: #c3e88d;
- --theme-syntax-number: #f78c6c;
- --theme-syntax-type: #ffcb6b;
- --theme-syntax-operator: #89ddff;
- --theme-syntax-punctuation: #a6accd;
-}
-
-[data-theme="rosepine"][data-dark="false"] {
- --theme-primary: #31748f;
- --theme-secondary: #907aa9;
- --theme-accent: #d7827e;
- --theme-error: #b4637a;
- --theme-warning: #ea9d34;
- --theme-success: #286983;
- --theme-info: #56949f;
- --theme-text: #575279;
- --theme-text-muted: #9893a5;
- --theme-background: #faf4ed;
- --theme-background-panel: #fffaf3;
- --theme-background-element: #f2e9e1;
- --theme-border: #dfdad9;
- --theme-border-active: #31748f;
- --theme-border-subtle: #f4ede8;
- --theme-diff-added: #286983;
- --theme-diff-removed: #b4637a;
- --theme-diff-context: #9893a5;
- --theme-diff-hunk-header: #907aa9;
- --theme-diff-highlight-added: #286983;
- --theme-diff-highlight-removed: #b4637a;
- --theme-diff-added-bg: #e5f2f3;
- --theme-diff-removed-bg: #fce5e8;
- --theme-diff-context-bg: #fffaf3;
- --theme-diff-line-number: #9893a5;
- --theme-diff-added-line-number-bg: #e5f2f3;
- --theme-diff-removed-line-number-bg: #fce5e8;
- --theme-markdown-text: #575279;
- --theme-markdown-heading: #907aa9;
- --theme-markdown-link: #31748f;
- --theme-markdown-link-text: #d7827e;
- --theme-markdown-code: #286983;
- --theme-markdown-block-quote: #9893a5;
- --theme-markdown-emph: #ea9d34;
- --theme-markdown-strong: #b4637a;
- --theme-markdown-horizontal-rule: #dfdad9;
- --theme-markdown-list-item: #31748f;
- --theme-markdown-list-enumeration: #d7827e;
- --theme-markdown-image: #31748f;
- --theme-markdown-image-text: #d7827e;
- --theme-markdown-code-block: #575279;
- --theme-syntax-comment: #9893a5;
- --theme-syntax-keyword: #286983;
- --theme-syntax-function: #d7827e;
- --theme-syntax-variable: #575279;
- --theme-syntax-string: #ea9d34;
- --theme-syntax-number: #907aa9;
- --theme-syntax-type: #56949f;
- --theme-syntax-operator: #797593;
- --theme-syntax-punctuation: #797593;
-}
-
-[data-theme="rosepine"][data-dark="true"] {
- --theme-primary: #9ccfd8;
- --theme-secondary: #c4a7e7;
- --theme-accent: #ebbcba;
- --theme-error: #eb6f92;
- --theme-warning: #f6c177;
- --theme-success: #31748f;
- --theme-info: #9ccfd8;
- --theme-text: #e0def4;
- --theme-text-muted: #6e6a86;
- --theme-background: #191724;
- --theme-background-panel: #1f1d2e;
- --theme-background-element: #26233a;
- --theme-border: #403d52;
- --theme-border-active: #9ccfd8;
- --theme-border-subtle: #21202e;
- --theme-diff-added: #31748f;
- --theme-diff-removed: #eb6f92;
- --theme-diff-context: #6e6a86;
- --theme-diff-hunk-header: #c4a7e7;
- --theme-diff-highlight-added: #31748f;
- --theme-diff-highlight-removed: #eb6f92;
- --theme-diff-added-bg: #1f2d3a;
- --theme-diff-removed-bg: #3a1f2d;
- --theme-diff-context-bg: #1f1d2e;
- --theme-diff-line-number: #6e6a86;
- --theme-diff-added-line-number-bg: #1f2d3a;
- --theme-diff-removed-line-number-bg: #3a1f2d;
- --theme-markdown-text: #e0def4;
- --theme-markdown-heading: #c4a7e7;
- --theme-markdown-link: #9ccfd8;
- --theme-markdown-link-text: #ebbcba;
- --theme-markdown-code: #31748f;
- --theme-markdown-block-quote: #6e6a86;
- --theme-markdown-emph: #f6c177;
- --theme-markdown-strong: #eb6f92;
- --theme-markdown-horizontal-rule: #403d52;
- --theme-markdown-list-item: #9ccfd8;
- --theme-markdown-list-enumeration: #ebbcba;
- --theme-markdown-image: #9ccfd8;
- --theme-markdown-image-text: #ebbcba;
- --theme-markdown-code-block: #e0def4;
- --theme-syntax-comment: #6e6a86;
- --theme-syntax-keyword: #31748f;
- --theme-syntax-function: #ebbcba;
- --theme-syntax-variable: #e0def4;
- --theme-syntax-string: #f6c177;
- --theme-syntax-number: #c4a7e7;
- --theme-syntax-type: #9ccfd8;
- --theme-syntax-operator: #908caa;
- --theme-syntax-punctuation: #908caa;
-}
-
-[data-theme="solarized"][data-dark="false"] {
- --theme-primary: #268bd2;
- --theme-secondary: #6c71c4;
- --theme-accent: #2aa198;
- --theme-error: #dc322f;
- --theme-warning: #b58900;
- --theme-success: #859900;
- --theme-info: #cb4b16;
- --theme-text: #657b83;
- --theme-text-muted: #93a1a1;
- --theme-background: #fdf6e3;
- --theme-background-panel: #eee8d5;
- --theme-background-element: #eee8d5;
- --theme-border: #eee8d5;
- --theme-border-active: #93a1a1;
- --theme-border-subtle: #eee8d5;
- --theme-diff-added: #859900;
- --theme-diff-removed: #dc322f;
- --theme-diff-context: #93a1a1;
- --theme-diff-hunk-header: #93a1a1;
- --theme-diff-highlight-added: #859900;
- --theme-diff-highlight-removed: #dc322f;
- --theme-diff-added-bg: #eee8d5;
- --theme-diff-removed-bg: #eee8d5;
- --theme-diff-context-bg: #eee8d5;
- --theme-diff-line-number: #93a1a1;
- --theme-diff-added-line-number-bg: #eee8d5;
- --theme-diff-removed-line-number-bg: #eee8d5;
- --theme-markdown-text: #657b83;
- --theme-markdown-heading: #268bd2;
- --theme-markdown-link: #2aa198;
- --theme-markdown-link-text: #6c71c4;
- --theme-markdown-code: #859900;
- --theme-markdown-block-quote: #93a1a1;
- --theme-markdown-emph: #b58900;
- --theme-markdown-strong: #cb4b16;
- --theme-markdown-horizontal-rule: #93a1a1;
- --theme-markdown-list-item: #268bd2;
- --theme-markdown-list-enumeration: #2aa198;
- --theme-markdown-image: #2aa198;
- --theme-markdown-image-text: #6c71c4;
- --theme-markdown-code-block: #657b83;
- --theme-syntax-comment: #93a1a1;
- --theme-syntax-keyword: #859900;
- --theme-syntax-function: #268bd2;
- --theme-syntax-variable: #2aa198;
- --theme-syntax-string: #2aa198;
- --theme-syntax-number: #d33682;
- --theme-syntax-type: #b58900;
- --theme-syntax-operator: #859900;
- --theme-syntax-punctuation: #657b83;
-}
-
-[data-theme="solarized"][data-dark="true"] {
- --theme-primary: #268bd2;
- --theme-secondary: #6c71c4;
- --theme-accent: #2aa198;
- --theme-error: #dc322f;
- --theme-warning: #b58900;
- --theme-success: #859900;
- --theme-info: #cb4b16;
- --theme-text: #839496;
- --theme-text-muted: #586e75;
- --theme-background: #002b36;
- --theme-background-panel: #073642;
- --theme-background-element: #073642;
- --theme-border: #073642;
- --theme-border-active: #586e75;
- --theme-border-subtle: #073642;
- --theme-diff-added: #859900;
- --theme-diff-removed: #dc322f;
- --theme-diff-context: #586e75;
- --theme-diff-hunk-header: #586e75;
- --theme-diff-highlight-added: #859900;
- --theme-diff-highlight-removed: #dc322f;
- --theme-diff-added-bg: #073642;
- --theme-diff-removed-bg: #073642;
- --theme-diff-context-bg: #073642;
- --theme-diff-line-number: #586e75;
- --theme-diff-added-line-number-bg: #073642;
- --theme-diff-removed-line-number-bg: #073642;
- --theme-markdown-text: #839496;
- --theme-markdown-heading: #268bd2;
- --theme-markdown-link: #2aa198;
- --theme-markdown-link-text: #6c71c4;
- --theme-markdown-code: #859900;
- --theme-markdown-block-quote: #586e75;
- --theme-markdown-emph: #b58900;
- --theme-markdown-strong: #cb4b16;
- --theme-markdown-horizontal-rule: #586e75;
- --theme-markdown-list-item: #268bd2;
- --theme-markdown-list-enumeration: #2aa198;
- --theme-markdown-image: #2aa198;
- --theme-markdown-image-text: #6c71c4;
- --theme-markdown-code-block: #839496;
- --theme-syntax-comment: #586e75;
- --theme-syntax-keyword: #859900;
- --theme-syntax-function: #268bd2;
- --theme-syntax-variable: #2aa198;
- --theme-syntax-string: #2aa198;
- --theme-syntax-number: #d33682;
- --theme-syntax-type: #b58900;
- --theme-syntax-operator: #859900;
- --theme-syntax-punctuation: #839496;
-}
-
-[data-theme="synthwave84"][data-dark="false"] {
- --theme-primary: #00bcd4;
- --theme-secondary: #e91e63;
- --theme-accent: #9c27b0;
- --theme-error: #f44336;
- --theme-warning: #ff9800;
- --theme-success: #4caf50;
- --theme-info: #ff5722;
- --theme-text: #262335;
- --theme-text-muted: #5c5c8a;
- --theme-background: #fafafa;
- --theme-background-panel: #f5f5f5;
- --theme-background-element: #eeeeee;
- --theme-border: #e0e0e0;
- --theme-border-active: #00bcd4;
- --theme-border-subtle: #f0f0f0;
- --theme-diff-added: #4caf50;
- --theme-diff-removed: #f44336;
- --theme-diff-context: #5c5c8a;
- --theme-diff-hunk-header: #9c27b0;
- --theme-diff-highlight-added: #4caf50;
- --theme-diff-highlight-removed: #f44336;
- --theme-diff-added-bg: #e8f5e9;
- --theme-diff-removed-bg: #ffebee;
- --theme-diff-context-bg: #f5f5f5;
- --theme-diff-line-number: #b0b0b0;
- --theme-diff-added-line-number-bg: #e8f5e9;
- --theme-diff-removed-line-number-bg: #ffebee;
- --theme-markdown-text: #262335;
- --theme-markdown-heading: #e91e63;
- --theme-markdown-link: #00bcd4;
- --theme-markdown-link-text: #9c27b0;
- --theme-markdown-code: #4caf50;
- --theme-markdown-block-quote: #5c5c8a;
- --theme-markdown-emph: #ff9800;
- --theme-markdown-strong: #ff5722;
- --theme-markdown-horizontal-rule: #e0e0e0;
- --theme-markdown-list-item: #00bcd4;
- --theme-markdown-list-enumeration: #9c27b0;
- --theme-markdown-image: #00bcd4;
- --theme-markdown-image-text: #9c27b0;
- --theme-markdown-code-block: #262335;
- --theme-syntax-comment: #5c5c8a;
- --theme-syntax-keyword: #e91e63;
- --theme-syntax-function: #ff5722;
- --theme-syntax-variable: #262335;
- --theme-syntax-string: #ff9800;
- --theme-syntax-number: #9c27b0;
- --theme-syntax-type: #00bcd4;
- --theme-syntax-operator: #e91e63;
- --theme-syntax-punctuation: #262335;
-}
-
-[data-theme="synthwave84"][data-dark="true"] {
- --theme-primary: #36f9f6;
- --theme-secondary: #ff7edb;
- --theme-accent: #b084eb;
- --theme-error: #fe4450;
- --theme-warning: #fede5d;
- --theme-success: #72f1b8;
- --theme-info: #ff8b39;
- --theme-text: #ffffff;
- --theme-text-muted: #848bbd;
- --theme-background: #262335;
- --theme-background-panel: #1e1a29;
- --theme-background-element: #2a2139;
- --theme-border: #495495;
- --theme-border-active: #36f9f6;
- --theme-border-subtle: #241b2f;
- --theme-diff-added: #72f1b8;
- --theme-diff-removed: #fe4450;
- --theme-diff-context: #848bbd;
- --theme-diff-hunk-header: #b084eb;
- --theme-diff-highlight-added: #97f1d8;
- --theme-diff-highlight-removed: #ff5e5b;
- --theme-diff-added-bg: #1a3a2a;
- --theme-diff-removed-bg: #3a1a2a;
- --theme-diff-context-bg: #1e1a29;
- --theme-diff-line-number: #495495;
- --theme-diff-added-line-number-bg: #1a3a2a;
- --theme-diff-removed-line-number-bg: #3a1a2a;
- --theme-markdown-text: #ffffff;
- --theme-markdown-heading: #ff7edb;
- --theme-markdown-link: #36f9f6;
- --theme-markdown-link-text: #b084eb;
- --theme-markdown-code: #72f1b8;
- --theme-markdown-block-quote: #848bbd;
- --theme-markdown-emph: #fede5d;
- --theme-markdown-strong: #ff8b39;
- --theme-markdown-horizontal-rule: #495495;
- --theme-markdown-list-item: #36f9f6;
- --theme-markdown-list-enumeration: #b084eb;
- --theme-markdown-image: #36f9f6;
- --theme-markdown-image-text: #b084eb;
- --theme-markdown-code-block: #ffffff;
- --theme-syntax-comment: #848bbd;
- --theme-syntax-keyword: #ff7edb;
- --theme-syntax-function: #ff8b39;
- --theme-syntax-variable: #ffffff;
- --theme-syntax-string: #fede5d;
- --theme-syntax-number: #b084eb;
- --theme-syntax-type: #36f9f6;
- --theme-syntax-operator: #ff7edb;
- --theme-syntax-punctuation: #ffffff;
-}
-
-[data-theme="tokyonight"][data-dark="false"] {
- --theme-primary: #2e7de9;
- --theme-secondary: #9854f1;
- --theme-accent: #b15c00;
- --theme-error: #f52a65;
- --theme-warning: #b15c00;
- --theme-success: #587539;
- --theme-info: #2e7de9;
- --theme-text: #3760bf;
- --theme-text-muted: #8990a3;
- --theme-background: #e1e2e7;
- --theme-background-panel: #d5d6db;
- --theme-background-element: #c8c9ce;
- --theme-border: #737a8c;
- --theme-border-active: #5a607d;
- --theme-border-subtle: #9699a8;
- --theme-diff-added: #1e725c;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #7086b5;
- --theme-diff-hunk-header: #7086b5;
- --theme-diff-highlight-added: #4db380;
- --theme-diff-highlight-removed: #f52a65;
- --theme-diff-added-bg: #d5e5d5;
- --theme-diff-removed-bg: #f7d8db;
- --theme-diff-context-bg: #d5d6db;
- --theme-diff-line-number: #c8c9ce;
- --theme-diff-added-line-number-bg: #c5d5c5;
- --theme-diff-removed-line-number-bg: #e7c8cb;
- --theme-markdown-text: #3760bf;
- --theme-markdown-heading: #9854f1;
- --theme-markdown-link: #2e7de9;
- --theme-markdown-link-text: #007197;
- --theme-markdown-code: #587539;
- --theme-markdown-block-quote: #8c6c3e;
- --theme-markdown-emph: #8c6c3e;
- --theme-markdown-strong: #b15c00;
- --theme-markdown-horizontal-rule: #8990a3;
- --theme-markdown-list-item: #2e7de9;
- --theme-markdown-list-enumeration: #007197;
- --theme-markdown-image: #2e7de9;
- --theme-markdown-image-text: #007197;
- --theme-markdown-code-block: #3760bf;
- --theme-syntax-comment: #8990a3;
- --theme-syntax-keyword: #9854f1;
- --theme-syntax-function: #2e7de9;
- --theme-syntax-variable: #f52a65;
- --theme-syntax-string: #587539;
- --theme-syntax-number: #b15c00;
- --theme-syntax-type: #8c6c3e;
- --theme-syntax-operator: #007197;
- --theme-syntax-punctuation: #3760bf;
-}
-
-[data-theme="tokyonight"][data-dark="true"] {
- --theme-primary: #82aaff;
- --theme-secondary: #c099ff;
- --theme-accent: #ff966c;
- --theme-error: #ff757f;
- --theme-warning: #ff966c;
- --theme-success: #c3e88d;
- --theme-info: #82aaff;
- --theme-text: #c8d3f5;
- --theme-text-muted: #828bb8;
- --theme-background: #1a1b26;
- --theme-background-panel: #1e2030;
- --theme-background-element: #222436;
- --theme-border: #737aa2;
- --theme-border-active: #9099b2;
- --theme-border-subtle: #545c7e;
- --theme-diff-added: #4fd6be;
- --theme-diff-removed: #c53b53;
- --theme-diff-context: #828bb8;
- --theme-diff-hunk-header: #828bb8;
- --theme-diff-highlight-added: #b8db87;
- --theme-diff-highlight-removed: #e26a75;
- --theme-diff-added-bg: #20303b;
- --theme-diff-removed-bg: #37222c;
- --theme-diff-context-bg: #1e2030;
- --theme-diff-line-number: #222436;
- --theme-diff-added-line-number-bg: #1b2b34;
- --theme-diff-removed-line-number-bg: #2d1f26;
- --theme-markdown-text: #c8d3f5;
- --theme-markdown-heading: #c099ff;
- --theme-markdown-link: #82aaff;
- --theme-markdown-link-text: #86e1fc;
- --theme-markdown-code: #c3e88d;
- --theme-markdown-block-quote: #ffc777;
- --theme-markdown-emph: #ffc777;
- --theme-markdown-strong: #ff966c;
- --theme-markdown-horizontal-rule: #828bb8;
- --theme-markdown-list-item: #82aaff;
- --theme-markdown-list-enumeration: #86e1fc;
- --theme-markdown-image: #82aaff;
- --theme-markdown-image-text: #86e1fc;
- --theme-markdown-code-block: #c8d3f5;
- --theme-syntax-comment: #828bb8;
- --theme-syntax-keyword: #c099ff;
- --theme-syntax-function: #82aaff;
- --theme-syntax-variable: #ff757f;
- --theme-syntax-string: #c3e88d;
- --theme-syntax-number: #ff966c;
- --theme-syntax-type: #ffc777;
- --theme-syntax-operator: #86e1fc;
- --theme-syntax-punctuation: #c8d3f5;
-}
-
-[data-theme="vesper"][data-dark="false"] {
- --theme-primary: #ffc799;
- --theme-secondary: #99ffe4;
- --theme-accent: #ffc799;
- --theme-error: #ff8080;
- --theme-warning: #ffc799;
- --theme-success: #99ffe4;
- --theme-info: #ffc799;
- --theme-text: #101010;
- --theme-text-muted: #a0a0a0;
- --theme-background: #fff;
- --theme-background-panel: #f0f0f0;
- --theme-background-element: #e0e0e0;
- --theme-border: #d0d0d0;
- --theme-border-active: #ffc799;
- --theme-border-subtle: #e8e8e8;
- --theme-diff-added: #99ffe4;
- --theme-diff-removed: #ff8080;
- --theme-diff-context: #a0a0a0;
- --theme-diff-hunk-header: #a0a0a0;
- --theme-diff-highlight-added: #99ffe4;
- --theme-diff-highlight-removed: #ff8080;
- --theme-diff-added-bg: #e8f5e8;
- --theme-diff-removed-bg: #f5e8e8;
- --theme-diff-context-bg: #f8f8f8;
- --theme-diff-line-number: #808080;
- --theme-diff-added-line-number-bg: #e8f5e8;
- --theme-diff-removed-line-number-bg: #f5e8e8;
- --theme-markdown-text: #101010;
- --theme-markdown-heading: #ffc799;
- --theme-markdown-link: #ffc799;
- --theme-markdown-link-text: #a0a0a0;
- --theme-markdown-code: #a0a0a0;
- --theme-markdown-block-quote: #101010;
- --theme-markdown-emph: #101010;
- --theme-markdown-strong: #101010;
- --theme-markdown-horizontal-rule: #65737e;
- --theme-markdown-list-item: #101010;
- --theme-markdown-list-enumeration: #101010;
- --theme-markdown-image: #ffc799;
- --theme-markdown-image-text: #a0a0a0;
- --theme-markdown-code-block: #101010;
- --theme-syntax-comment: #8b8b8b94;
- --theme-syntax-keyword: #a0a0a0;
- --theme-syntax-function: #ffc799;
- --theme-syntax-variable: #101010;
- --theme-syntax-string: #99ffe4;
- --theme-syntax-number: #ffc799;
- --theme-syntax-type: #ffc799;
- --theme-syntax-operator: #a0a0a0;
- --theme-syntax-punctuation: #101010;
-}
-
-[data-theme="vesper"][data-dark="true"] {
- --theme-primary: #ffc799;
- --theme-secondary: #99ffe4;
- --theme-accent: #ffc799;
- --theme-error: #ff8080;
- --theme-warning: #ffc799;
- --theme-success: #99ffe4;
- --theme-info: #ffc799;
- --theme-text: #fff;
- --theme-text-muted: #a0a0a0;
- --theme-background: #101010;
- --theme-background-panel: #101010;
- --theme-background-element: #101010;
- --theme-border: #282828;
- --theme-border-active: #ffc799;
- --theme-border-subtle: #1c1c1c;
- --theme-diff-added: #99ffe4;
- --theme-diff-removed: #ff8080;
- --theme-diff-context: #a0a0a0;
- --theme-diff-hunk-header: #a0a0a0;
- --theme-diff-highlight-added: #99ffe4;
- --theme-diff-highlight-removed: #ff8080;
- --theme-diff-added-bg: #0d2818;
- --theme-diff-removed-bg: #281a1a;
- --theme-diff-context-bg: #101010;
- --theme-diff-line-number: #505050;
- --theme-diff-added-line-number-bg: #0d2818;
- --theme-diff-removed-line-number-bg: #281a1a;
- --theme-markdown-text: #fff;
- --theme-markdown-heading: #ffc799;
- --theme-markdown-link: #ffc799;
- --theme-markdown-link-text: #a0a0a0;
- --theme-markdown-code: #a0a0a0;
- --theme-markdown-block-quote: #fff;
- --theme-markdown-emph: #fff;
- --theme-markdown-strong: #fff;
- --theme-markdown-horizontal-rule: #65737e;
- --theme-markdown-list-item: #fff;
- --theme-markdown-list-enumeration: #fff;
- --theme-markdown-image: #ffc799;
- --theme-markdown-image-text: #a0a0a0;
- --theme-markdown-code-block: #fff;
- --theme-syntax-comment: #8b8b8b94;
- --theme-syntax-keyword: #a0a0a0;
- --theme-syntax-function: #ffc799;
- --theme-syntax-variable: #fff;
- --theme-syntax-string: #99ffe4;
- --theme-syntax-number: #ffc799;
- --theme-syntax-type: #ffc799;
- --theme-syntax-operator: #a0a0a0;
- --theme-syntax-punctuation: #fff;
-}
-
-[data-theme="zenburn"][data-dark="false"] {
- --theme-primary: #5f7f8f;
- --theme-secondary: #8f5f8f;
- --theme-accent: #5f8f8f;
- --theme-error: #8f5f5f;
- --theme-warning: #8f8f5f;
- --theme-success: #5f8f5f;
- --theme-info: #8f7f5f;
- --theme-text: #3f3f3f;
- --theme-text-muted: #6f6f6f;
- --theme-background: #ffffef;
- --theme-background-panel: #f5f5e5;
- --theme-background-element: #ebebdb;
- --theme-border: #d0d0c0;
- --theme-border-active: #5f7f8f;
- --theme-border-subtle: #e0e0d0;
- --theme-diff-added: #5f8f5f;
- --theme-diff-removed: #8f5f5f;
- --theme-diff-context: #6f6f6f;
- --theme-diff-hunk-header: #5f8f8f;
- --theme-diff-highlight-added: #5f8f5f;
- --theme-diff-highlight-removed: #8f5f5f;
- --theme-diff-added-bg: #efffef;
- --theme-diff-removed-bg: #ffefef;
- --theme-diff-context-bg: #f5f5e5;
- --theme-diff-line-number: #b0b0a0;
- --theme-diff-added-line-number-bg: #efffef;
- --theme-diff-removed-line-number-bg: #ffefef;
- --theme-markdown-text: #3f3f3f;
- --theme-markdown-heading: #8f8f5f;
- --theme-markdown-link: #5f7f8f;
- --theme-markdown-link-text: #5f8f8f;
- --theme-markdown-code: #5f8f5f;
- --theme-markdown-block-quote: #6f6f6f;
- --theme-markdown-emph: #8f8f5f;
- --theme-markdown-strong: #8f7f5f;
- --theme-markdown-horizontal-rule: #6f6f6f;
- --theme-markdown-list-item: #5f7f8f;
- --theme-markdown-list-enumeration: #5f8f8f;
- --theme-markdown-image: #5f7f8f;
- --theme-markdown-image-text: #5f8f8f;
- --theme-markdown-code-block: #3f3f3f;
- --theme-syntax-comment: #5f7f5f;
- --theme-syntax-keyword: #8f8f5f;
- --theme-syntax-function: #5f7f8f;
- --theme-syntax-variable: #3f3f3f;
- --theme-syntax-string: #8f5f5f;
- --theme-syntax-number: #5f8f5f;
- --theme-syntax-type: #5f8f8f;
- --theme-syntax-operator: #8f8f5f;
- --theme-syntax-punctuation: #3f3f3f;
-}
-
-[data-theme="zenburn"][data-dark="true"] {
- --theme-primary: #8cd0d3;
- --theme-secondary: #dc8cc3;
- --theme-accent: #93e0e3;
- --theme-error: #cc9393;
- --theme-warning: #f0dfaf;
- --theme-success: #7f9f7f;
- --theme-info: #dfaf8f;
- --theme-text: #dcdccc;
- --theme-text-muted: #9f9f9f;
- --theme-background: #3f3f3f;
- --theme-background-panel: #4f4f4f;
- --theme-background-element: #5f5f5f;
- --theme-border: #5f5f5f;
- --theme-border-active: #8cd0d3;
- --theme-border-subtle: #4f4f4f;
- --theme-diff-added: #7f9f7f;
- --theme-diff-removed: #cc9393;
- --theme-diff-context: #9f9f9f;
- --theme-diff-hunk-header: #93e0e3;
- --theme-diff-highlight-added: #8fb28f;
- --theme-diff-highlight-removed: #dca3a3;
- --theme-diff-added-bg: #4f5f4f;
- --theme-diff-removed-bg: #5f4f4f;
- --theme-diff-context-bg: #4f4f4f;
- --theme-diff-line-number: #6f6f6f;
- --theme-diff-added-line-number-bg: #4f5f4f;
- --theme-diff-removed-line-number-bg: #5f4f4f;
- --theme-markdown-text: #dcdccc;
- --theme-markdown-heading: #f0dfaf;
- --theme-markdown-link: #8cd0d3;
- --theme-markdown-link-text: #93e0e3;
- --theme-markdown-code: #7f9f7f;
- --theme-markdown-block-quote: #9f9f9f;
- --theme-markdown-emph: #e0cf9f;
- --theme-markdown-strong: #dfaf8f;
- --theme-markdown-horizontal-rule: #9f9f9f;
- --theme-markdown-list-item: #8cd0d3;
- --theme-markdown-list-enumeration: #93e0e3;
- --theme-markdown-image: #8cd0d3;
- --theme-markdown-image-text: #93e0e3;
- --theme-markdown-code-block: #dcdccc;
- --theme-syntax-comment: #7f9f7f;
- --theme-syntax-keyword: #f0dfaf;
- --theme-syntax-function: #8cd0d3;
- --theme-syntax-variable: #dcdccc;
- --theme-syntax-string: #cc9393;
- --theme-syntax-number: #8fb28f;
- --theme-syntax-type: #93e0e3;
- --theme-syntax-operator: #f0dfaf;
- --theme-syntax-punctuation: #dcdccc;
-}
diff --git a/packages/app/src/components/code.tsx b/packages/app/src/components/code.tsx
index 4eed5814e..f76bf5e2e 100644
--- a/packages/app/src/components/code.tsx
+++ b/packages/app/src/components/code.tsx
@@ -1,15 +1,6 @@
-import { bundledLanguages, codeToHtml, type ShikiTransformer } from "shiki"
-import {
- createResource,
- splitProps,
- Suspense,
- type ComponentProps,
- createEffect,
- onMount,
- onCleanup,
- createMemo,
-} from "solid-js"
-import { useLocal } from "@/context"
+import { bundledLanguages, type BundledLanguage, type ShikiTransformer } from "shiki"
+import { splitProps, type ComponentProps, createEffect, onMount, onCleanup, createMemo, createResource } from "solid-js"
+import { useLocal, useShiki } from "@/context"
import { getFileExtension, getNodeOffsetInLine, getSelectionInContainer } from "@/utils"
interface Props extends ComponentProps<"div"> {
@@ -17,6 +8,349 @@ interface Props extends ComponentProps<"div"> {
path: string
}
+export function Code(props: Props) {
+ const ctx = useLocal()
+ const highlighter = useShiki()
+ const [local, others] = splitProps(props, ["class", "classList", "code", "path"])
+ const lang = createMemo(() => getFileExtension(local.path))
+
+ let container: HTMLDivElement | undefined
+ let isProgrammaticSelection = false
+
+ const [html] = createResource(async () => {
+ if (!highlighter.getLoadedLanguages().includes(lang())) {
+ await highlighter.loadLanguage(lang() as BundledLanguage)
+ }
+ return highlighter.codeToHtml(local.code || "", {
+ lang: lang() && lang() in bundledLanguages ? lang() : "text",
+ theme: "opencode",
+ transformers: [transformerUnifiedDiff(), transformerDiffGroups()],
+ }) as string
+ })
+
+ onMount(() => {
+ if (!container) return
+
+ let ticking = false
+ const onScroll = () => {
+ if (!container) return
+ if (ctx.file.active()?.path !== local.path) return
+ if (ticking) return
+ ticking = true
+ requestAnimationFrame(() => {
+ ticking = false
+ ctx.file.scroll(local.path, container!.scrollTop)
+ })
+ }
+
+ const onSelectionChange = () => {
+ if (!container) return
+ if (isProgrammaticSelection) return
+ if (ctx.file.active()?.path !== local.path) return
+ const d = getSelectionInContainer(container)
+ if (!d) return
+ const p = ctx.file.node(local.path)?.selection
+ if (p && p.startLine === d.sl && p.endLine === d.el && p.startChar === d.sch && p.endChar === d.ech) return
+ ctx.file.select(local.path, { startLine: d.sl, startChar: d.sch, endLine: d.el, endChar: d.ech })
+ }
+
+ const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"
+ const onKeyDown = (e: KeyboardEvent) => {
+ if (ctx.file.active()?.path !== local.path) return
+ const ae = document.activeElement as HTMLElement | undefined
+ const tag = (ae?.tagName || "").toLowerCase()
+ const inputFocused = !!ae && (tag === "input" || tag === "textarea" || ae.isContentEditable)
+ if (inputFocused) return
+ if (e.getModifierState(MOD) && e.key.toLowerCase() === "a") {
+ e.preventDefault()
+ if (!container) return
+ const element = container.querySelector("code") as HTMLElement | undefined
+ if (!element) return
+ const lines = Array.from(element.querySelectorAll(".line"))
+ if (!lines.length) return
+ const r = document.createRange()
+ const last = lines[lines.length - 1]
+ r.selectNodeContents(last)
+ const lastLen = r.toString().length
+ ctx.file.select(local.path, { startLine: 1, startChar: 0, endLine: lines.length, endChar: lastLen })
+ }
+ }
+
+ container.addEventListener("scroll", onScroll)
+ document.addEventListener("selectionchange", onSelectionChange)
+ document.addEventListener("keydown", onKeyDown)
+
+ onCleanup(() => {
+ container?.removeEventListener("scroll", onScroll)
+ document.removeEventListener("selectionchange", onSelectionChange)
+ document.removeEventListener("keydown", onKeyDown)
+ })
+ })
+
+ // Restore scroll position from store when content is ready
+ createEffect(() => {
+ const content = html()
+ if (!container || !content) return
+ const top = ctx.file.node(local.path)?.scrollTop
+ if (top !== undefined && container.scrollTop !== top) container.scrollTop = top
+ })
+
+ // Sync selection from store -> DOM
+ createEffect(() => {
+ const content = html()
+ if (!container || !content) return
+ if (ctx.file.active()?.path !== local.path) return
+ const codeEl = container.querySelector("code") as HTMLElement | undefined
+ if (!codeEl) return
+ const target = ctx.file.node(local.path)?.selection
+ const current = getSelectionInContainer(container)
+ const sel = window.getSelection()
+ if (!sel) return
+ if (!target) {
+ if (current) {
+ isProgrammaticSelection = true
+ sel.removeAllRanges()
+ queueMicrotask(() => {
+ isProgrammaticSelection = false
+ })
+ }
+ return
+ }
+ const matches = !!(
+ current &&
+ current.sl === target.startLine &&
+ current.sch === target.startChar &&
+ current.el === target.endLine &&
+ current.ech === target.endChar
+ )
+ if (matches) return
+ const lines = Array.from(codeEl.querySelectorAll(".line"))
+ if (lines.length === 0) return
+ let sIdx = Math.max(0, target.startLine - 1)
+ let eIdx = Math.max(0, target.endLine - 1)
+ let sChar = Math.max(0, target.startChar || 0)
+ let eChar = Math.max(0, target.endChar || 0)
+ if (sIdx > eIdx || (sIdx === eIdx && sChar > eChar)) {
+ const ti = sIdx
+ sIdx = eIdx
+ eIdx = ti
+ const tc = sChar
+ sChar = eChar
+ eChar = tc
+ }
+ if (eChar === 0 && eIdx > sIdx) {
+ eIdx = eIdx - 1
+ eChar = Number.POSITIVE_INFINITY
+ }
+ if (sIdx >= lines.length) return
+ if (eIdx >= lines.length) eIdx = lines.length - 1
+ const s = getNodeOffsetInLine(lines[sIdx], sChar) ?? { node: lines[sIdx], offset: 0 }
+ const e = getNodeOffsetInLine(lines[eIdx], eChar) ?? { node: lines[eIdx], offset: lines[eIdx].childNodes.length }
+ const range = document.createRange()
+ range.setStart(s.node, s.offset)
+ range.setEnd(e.node, e.offset)
+ isProgrammaticSelection = true
+ sel.removeAllRanges()
+ sel.addRange(range)
+ queueMicrotask(() => {
+ isProgrammaticSelection = false
+ })
+ })
+
+ // Build/toggle split layout and apply folding (both unified and split)
+ createEffect(() => {
+ const content = html()
+ if (!container || !content) return
+ const view = ctx.file.view(local.path)
+
+ const pres = Array.from(container.querySelectorAll<HTMLPreElement>("pre"))
+ if (pres.length === 0) return
+ const originalPre = pres[0]
+
+ const split = container.querySelector<HTMLElement>(".diff-split")
+ if (view === "diff-split") {
+ applySplitDiff(container)
+ const next = container.querySelector<HTMLElement>(".diff-split")
+ if (next) next.style.display = ""
+ originalPre.style.display = "none"
+ } else {
+ if (split) split.style.display = "none"
+ originalPre.style.display = ""
+ }
+
+ const expanded = ctx.file.folded(local.path)
+ if (view === "diff-split") {
+ const left = container.querySelector<HTMLElement>(".diff-split pre:nth-child(1) code")
+ const right = container.querySelector<HTMLElement>(".diff-split pre:nth-child(2) code")
+ if (left)
+ applyDiffFolding(left, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "left" })
+ if (right)
+ applyDiffFolding(right, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "right" })
+ } else {
+ const code = container.querySelector<HTMLElement>("pre code")
+ if (code)
+ applyDiffFolding(code, 3, {
+ expanded,
+ onExpand: (key) => ctx.file.unfold(local.path, key),
+ })
+ }
+ })
+
+ // Highlight groups + scroll coupling
+ const clearHighlights = () => {
+ if (!container) return
+ container.querySelectorAll<HTMLElement>(".diff-selected").forEach((el) => el.classList.remove("diff-selected"))
+ }
+
+ const applyHighlight = (idx: number, scroll?: boolean) => {
+ if (!container) return
+ const view = ctx.file.view(local.path)
+ if (view === "raw") return
+
+ clearHighlights()
+
+ const nodes: HTMLElement[] = []
+ if (view === "diff-split") {
+ const left = container.querySelector<HTMLElement>(".diff-split pre:nth-child(1) code")
+ const right = container.querySelector<HTMLElement>(".diff-split pre:nth-child(2) code")
+ if (left)
+ nodes.push(...Array.from(left.querySelectorAll<HTMLElement>(`[data-chgrp="${idx}"][data-diff="remove"]`)))
+ if (right)
+ nodes.push(...Array.from(right.querySelectorAll<HTMLElement>(`[data-chgrp="${idx}"][data-diff="add"]`)))
+ } else {
+ const code = container.querySelector<HTMLElement>("pre code")
+ if (code) nodes.push(...Array.from(code.querySelectorAll<HTMLElement>(`[data-chgrp="${idx}"]`)))
+ }
+
+ for (const n of nodes) n.classList.add("diff-selected")
+ if (scroll && nodes.length) nodes[0].scrollIntoView({ block: "center", behavior: "smooth" })
+ }
+
+ const countGroups = () => {
+ if (!container) return 0
+ const code = container.querySelector<HTMLElement>("pre code")
+ if (!code) return 0
+ const set = new Set<string>()
+ for (const el of Array.from(code.querySelectorAll<HTMLElement>(".diff-line[data-chgrp]"))) {
+ const v = el.getAttribute("data-chgrp")
+ if (v != undefined) set.add(v)
+ }
+ return set.size
+ }
+
+ let lastIdx: number | undefined = undefined
+ let lastView: string | undefined
+ let lastContent: string | undefined
+ let lastRawIdx: number | undefined = undefined
+ createEffect(() => {
+ const content = html()
+ if (!container || !content) return
+ const view = ctx.file.view(local.path)
+ const raw = ctx.file.changeIndex(local.path)
+ if (raw === undefined) return
+ const total = countGroups()
+ if (total <= 0) return
+ const next = ((raw % total) + total) % total
+
+ const navigated = lastRawIdx !== undefined && lastRawIdx !== raw
+
+ if (next !== raw) {
+ ctx.file.setChangeIndex(local.path, next)
+ applyHighlight(next, true)
+ } else {
+ if (lastView !== view || lastContent !== content) applyHighlight(next)
+ if ((lastIdx !== undefined && lastIdx !== next) || navigated) applyHighlight(next, true)
+ }
+
+ lastRawIdx = raw
+ lastIdx = next
+ lastView = view
+ lastContent = content
+ })
+
+ return (
+ <div
+ ref={(el) => {
+ container = el
+ }}
+ innerHTML={html()}
+ class="
+ font-mono text-xs tracking-wide overflow-y-auto no-scrollbar h-full
+ [&]:[counter-reset:line]
+ [&_pre]:focus-visible:outline-none
+ [&_pre]:overflow-x-auto [&_pre]:no-scrollbar
+ [&_code]:min-w-full [&_code]:inline-block [&_code]:pb-40
+ [&_.tab]:relative
+ [&_.tab::before]:content['⇥']
+ [&_.tab::before]:absolute
+ [&_.tab::before]:opacity-0
+ [&_.space]:relative
+ [&_.space::before]:content-['·']
+ [&_.space::before]:absolute
+ [&_.space::before]:opacity-0
+ [&_.line]:inline-block [&_.line]:w-full
+ [&_.line]:hover:bg-background-element
+ [&_.line::before]:sticky [&_.line::before]:left-0
+ [&_.line::before]:w-12 [&_.line::before]:pr-4
+ [&_.line::before]:z-10
+ [&_.line::before]:bg-background-panel
+ [&_.line::before]:text-text-muted/60
+ [&_.line::before]:text-right [&_.line::before]:inline-block
+ [&_.line::before]:select-none
+ [&_.line::before]:[counter-increment:line]
+ [&_.line::before]:content-[counter(line)]
+ [&_code.code-diff_.line::before]:content-['']
+ [&_code.code-diff_.line::before]:w-0
+ [&_code.code-diff_.line::before]:pr-0
+ [&_.diff-split_code.code-diff::before]:w-10
+ [&_.diff-split_.diff-newln]:left-0
+ [&_.diff-oldln]:sticky [&_.diff-oldln]:left-0
+ [&_.diff-oldln]:w-10 [&_.diff-oldln]:pr-2
+ [&_.diff-oldln]:z-40
+ [&_.diff-oldln]:text-text-muted/60
+ [&_.diff-oldln]:text-right [&_.diff-oldln]:inline-block
+ [&_.diff-oldln]:select-none
+ [&_.diff-oldln]:bg-background-panel
+ [&_.diff-newln]:sticky [&_.diff-newln]:left-10
+ [&_.diff-newln]:w-10 [&_.diff-newln]:pr-2
+ [&_.diff-newln]:z-40
+ [&_.diff-newln]:text-text-muted/60
+ [&_.diff-newln]:text-right [&_.diff-newln]:inline-block
+ [&_.diff-newln]:select-none
+ [&_.diff-newln]:bg-background-panel
+ [&_.diff-add]:bg-success/20!
+ [&_.diff-add.diff-selected]:bg-success/50!
+ [&_.diff-add_.diff-oldln]:bg-success!
+ [&_.diff-add_.diff-oldln]:text-background-panel!
+ [&_.diff-add_.diff-newln]:bg-success!
+ [&_.diff-add_.diff-newln]:text-background-panel!
+ [&_.diff-remove]:bg-error/20!
+ [&_.diff-remove.diff-selected]:bg-error/50!
+ [&_.diff-remove_.diff-newln]:bg-error!
+ [&_.diff-remove_.diff-newln]:text-background-panel!
+ [&_.diff-remove_.diff-oldln]:bg-error!
+ [&_.diff-remove_.diff-oldln]:text-background-panel!
+ [&_.diff-sign]:inline-block [&_.diff-sign]:px-2 [&_.diff-sign]:select-none
+ [&_.diff-blank]:bg-background-element
+ [&_.diff-blank_.diff-oldln]:bg-background-element
+ [&_.diff-blank_.diff-newln]:bg-background-element
+ [&_.diff-collapsed]:block! [&_.diff-collapsed]:w-full [&_.diff-collapsed]:relative
+ [&_.diff-collapsed]:cursor-pointer [&_.diff-collapsed]:select-none
+ [&_.diff-collapsed]:bg-info/20 [&_.diff-collapsed]:hover:bg-info/40!
+ [&_.diff-collapsed]:text-info/80 [&_.diff-collapsed]:hover:text-info
+ [&_.diff-collapsed]:text-xs
+ [&_.diff-collapsed_.diff-oldln]:bg-info!
+ [&_.diff-collapsed_.diff-newln]:bg-info!
+ "
+ classList={{
+ ...(local.classList || {}),
+ [local.class ?? ""]: !!local.class,
+ }}
+ {...others}
+ ></div>
+ )
+}
+
function transformerUnifiedDiff(): ShikiTransformer {
const kinds = new Map<number, string>()
const meta = new Map<number, { old?: number; new?: number; sign?: string }>()
@@ -451,908 +785,3 @@ function applySplitDiff(container: HTMLElement) {
grid.appendChild(right.pre)
container.appendChild(grid)
}
-
-export function Code(props: Props) {
- const ctx = useLocal()
- const [local, others] = splitProps(props, ["class", "classList", "code", "path"])
- const lang = createMemo(() => getFileExtension(local.path))
-
- let container: HTMLDivElement | undefined
- let isProgrammaticSelection = false
-
- const [html] = createResource(
- () => [local.code, lang()],
- async ([code, lang]) => {
- return (await codeToHtml(code || "", {
- lang: lang && lang in bundledLanguages ? lang : "text",
- theme: {
- colors: {
- "actionBar.toggledBackground": "var(--theme-background-element)",
- "activityBarBadge.background": "var(--theme-accent)",
- "checkbox.border": "var(--theme-border)",
- "editor.background": "transparent",
- "editor.foreground": "var(--theme-text)",
- "editor.inactiveSelectionBackground": "var(--theme-background-element)",
- "editor.selectionHighlightBackground": "var(--theme-border-active)",
- "editorIndentGuide.activeBackground1": "var(--theme-border-subtle)",
- "editorIndentGuide.background1": "var(--theme-border-subtle)",
- "input.placeholderForeground": "var(--theme-text-muted)",
- "list.activeSelectionIconForeground": "var(--theme-text)",
- "list.dropBackground": "var(--theme-background-element)",
- "menu.background": "var(--theme-background-panel)",
- "menu.border": "var(--theme-border)",
- "menu.foreground": "var(--theme-text)",
- "menu.selectionBackground": "var(--theme-primary)",
- "menu.separatorBackground": "var(--theme-border)",
- "ports.iconRunningProcessForeground": "var(--theme-success)",
- "sideBarSectionHeader.background": "transparent",
- "sideBarSectionHeader.border": "var(--theme-border-subtle)",
- "sideBarTitle.foreground": "var(--theme-text-muted)",
- "statusBarItem.remoteBackground": "var(--theme-success)",
- "statusBarItem.remoteForeground": "var(--theme-text)",
- "tab.lastPinnedBorder": "var(--theme-border-subtle)",
- "tab.selectedBackground": "var(--theme-background-element)",
- "tab.selectedForeground": "var(--theme-text-muted)",
- "terminal.inactiveSelectionBackground": "var(--theme-background-element)",
- "widget.border": "var(--theme-border)",
- },
- displayName: "opencode",
- name: "opencode",
- semanticHighlighting: true,
- semanticTokenColors: {
- customLiteral: "var(--theme-syntax-function)",
- newOperator: "var(--theme-syntax-operator)",
- numberLiteral: "var(--theme-syntax-number)",
- stringLiteral: "var(--theme-syntax-string)",
- },
- tokenColors: [
- {
- scope: [
- "meta.embedded",
- "source.groovy.embedded",
- "string meta.image.inline.markdown",
- "variable.legacy.builtin.python",
- ],
- settings: {
- foreground: "var(--theme-text)",
- },
- },
- {
- scope: "emphasis",
- settings: {
- fontStyle: "italic",
- },
- },
- {
- scope: "strong",
- settings: {
- fontStyle: "bold",
- },
- },
- {
- scope: "header",
- settings: {
- foreground: "var(--theme-markdown-heading)",
- },
- },
- {
- scope: "comment",
- settings: {
- foreground: "var(--theme-syntax-comment)",
- },
- },
- {
- scope: "constant.language",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: [
- "constant.numeric",
- "variable.other.enummember",
- "keyword.operator.plus.exponent",
- "keyword.operator.minus.exponent",
- ],
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: "constant.regexp",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "entity.name.tag",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["entity.name.tag.css", "entity.name.tag.less"],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "entity.other.attribute-name",
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: [
- "entity.other.attribute-name.class.css",
- "source.css entity.other.attribute-name.class",
- "entity.other.attribute-name.id.css",
- "entity.other.attribute-name.parent-selector.css",
- "entity.other.attribute-name.parent.less",
- "source.css entity.other.attribute-name.pseudo-class",
- "entity.other.attribute-name.pseudo-element.css",
- "source.css.less entity.other.attribute-name.id",
- "entity.other.attribute-name.scss",
- ],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "invalid",
- settings: {
- foreground: "var(--theme-error)",
- },
- },
- {
- scope: "markup.underline",
- settings: {
- fontStyle: "underline",
- },
- },
- {
- scope: "markup.bold",
- settings: {
- fontStyle: "bold",
- foreground: "var(--theme-markdown-strong)",
- },
- },
- {
- scope: "markup.heading",
- settings: {
- fontStyle: "bold",
- foreground: "var(--theme-markdown-heading)",
- },
- },
- {
- scope: "markup.italic",
- settings: {
- fontStyle: "italic",
- },
- },
- {
- scope: "markup.strikethrough",
- settings: {
- fontStyle: "strikethrough",
- },
- },
- {
- scope: "markup.inserted",
- settings: {
- foreground: "var(--theme-diff-added)",
- },
- },
- {
- scope: "markup.deleted",
- settings: {
- foreground: "var(--theme-diff-removed)",
- },
- },
- {
- scope: "markup.changed",
- settings: {
- foreground: "var(--theme-diff-context)",
- },
- },
- {
- scope: "punctuation.definition.quote.begin.markdown",
- settings: {
- foreground: "var(--theme-markdown-block-quote)",
- },
- },
- {
- scope: "punctuation.definition.list.begin.markdown",
- settings: {
- foreground: "var(--theme-markdown-list-enumeration)",
- },
- },
- {
- scope: "markup.inline.raw",
- settings: {
- foreground: "var(--theme-markdown-code)",
- },
- },
- {
- scope: "punctuation.definition.tag",
- settings: {
- foreground: "var(--theme-syntax-punctuation)",
- },
- },
- {
- scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "meta.preprocessor.string",
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "meta.preprocessor.numeric",
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: "meta.structure.dictionary.key.python",
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: "meta.diff.header",
- settings: {
- foreground: "var(--theme-diff-hunk-header)",
- },
- },
- {
- scope: "storage",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "storage.type",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["storage.modifier", "keyword.operator.noexcept"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["string", "meta.embedded.assembly"],
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "string.tag",
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "string.value",
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "string.regexp",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: [
- "punctuation.definition.template-expression.begin",
- "punctuation.definition.template-expression.end",
- "punctuation.section.embedded",
- ],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["meta.template.expression"],
- settings: {
- foreground: "var(--theme-text)",
- },
- },
- {
- scope: [
- "support.type.vendored.property-name",
- "support.type.property-name",
- "source.css variable",
- "source.coffee.embedded",
- ],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: "keyword",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "keyword.control",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "keyword.operator",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: [
- "keyword.operator.new",
- "keyword.operator.expression",
- "keyword.operator.cast",
- "keyword.operator.sizeof",
- "keyword.operator.alignof",
- "keyword.operator.typeid",
- "keyword.operator.alignas",
- "keyword.operator.instanceof",
- "keyword.operator.logical.python",
- "keyword.operator.wordlike",
- ],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "keyword.other.unit",
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "support.function.git-rebase",
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: "constant.sha.git-rebase",
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: [
- "storage.modifier.import.java",
- "variable.language.wildcard.java",
- "storage.modifier.package.java",
- ],
- settings: {
- foreground: "var(--theme-text)",
- },
- },
- {
- scope: "variable.language",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: [
- "entity.name.function",
- "support.function",
- "support.constant.handlebars",
- "source.powershell variable.other.member",
- "entity.name.operator.custom-literal",
- ],
- settings: {
- foreground: "var(--theme-syntax-function)",
- },
- },
- {
- scope: [
- "support.class",
- "support.type",
- "entity.name.type",
- "entity.name.namespace",
- "entity.other.attribute",
- "entity.name.scope-resolution",
- "entity.name.class",
- "storage.type.numeric.go",
- "storage.type.byte.go",
- "storage.type.boolean.go",
- "storage.type.string.go",
- "storage.type.uintptr.go",
- "storage.type.error.go",
- "storage.type.rune.go",
- "storage.type.cs",
- "storage.type.generic.cs",
- "storage.type.modifier.cs",
- "storage.type.variable.cs",
- "storage.type.annotation.java",
- "storage.type.generic.java",
- "storage.type.java",
- "storage.type.object.array.java",
- "storage.type.primitive.array.java",
- "storage.type.primitive.java",
- "storage.type.token.java",
- "storage.type.groovy",
- "storage.type.annotation.groovy",
- "storage.type.parameters.groovy",
- "storage.type.generic.groovy",
- "storage.type.object.array.groovy",
- "storage.type.primitive.array.groovy",
- "storage.type.primitive.groovy",
- ],
- settings: {
- foreground: "var(--theme-syntax-type)",
- },
- },
- {
- scope: [
- "meta.type.cast.expr",
- "meta.type.new.expr",
- "support.constant.math",
- "support.constant.dom",
- "support.constant.json",
- "entity.other.inherited-class",
- "punctuation.separator.namespace.ruby",
- ],
- settings: {
- foreground: "var(--theme-syntax-type)",
- },
- },
- {
- scope: [
- "keyword.control",
- "source.cpp keyword.operator.new",
- "keyword.operator.delete",
- "keyword.other.using",
- "keyword.other.directive.using",
- "keyword.other.operator",
- "entity.name.operator",
- ],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: [
- "variable",
- "meta.definition.variable.name",
- "support.variable",
- "entity.name.variable",
- "constant.other.placeholder",
- ],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: ["variable.other.constant", "variable.other.enummember"],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: ["meta.object-literal.key"],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: [
- "support.constant.property-value",
- "support.constant.font-name",
- "support.constant.media-type",
- "support.constant.media",
- "constant.other.color.rgb-value",
- "constant.other.rgb-value",
- "support.constant.color",
- ],
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: [
- "punctuation.definition.group.regexp",
- "punctuation.definition.group.assertion.regexp",
- "punctuation.definition.character-class.regexp",
- "punctuation.character.set.begin.regexp",
- "punctuation.character.set.end.regexp",
- "keyword.operator.negation.regexp",
- "support.other.parenthesis.regexp",
- ],
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: [
- "constant.character.character-class.regexp",
- "constant.other.character-class.set.regexp",
- "constant.other.character-class.regexp",
- "constant.character.set.regexp",
- ],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "keyword.operator.quantifier.regexp",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: ["constant.character", "constant.other.option"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "constant.character.escape",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "entity.name.label",
- settings: {
- foreground: "var(--theme-text-muted)",
- },
- },
- ],
- type: "dark",
- },
- transformers: [transformerUnifiedDiff(), transformerDiffGroups()],
- })) as string
- },
- )
-
- onMount(() => {
- if (!container) return
-
- let ticking = false
- const onScroll = () => {
- if (!container) return
- if (ctx.file.active()?.path !== local.path) return
- if (ticking) return
- ticking = true
- requestAnimationFrame(() => {
- ticking = false
- ctx.file.scroll(local.path, container!.scrollTop)
- })
- }
-
- const onSelectionChange = () => {
- if (!container) return
- if (isProgrammaticSelection) return
- if (ctx.file.active()?.path !== local.path) return
- const d = getSelectionInContainer(container)
- if (!d) return
- const p = ctx.file.node(local.path)?.selection
- if (p && p.startLine === d.sl && p.endLine === d.el && p.startChar === d.sch && p.endChar === d.ech) return
- ctx.file.select(local.path, { startLine: d.sl, startChar: d.sch, endLine: d.el, endChar: d.ech })
- }
-
- const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"
- const onKeyDown = (e: KeyboardEvent) => {
- if (ctx.file.active()?.path !== local.path) return
- const ae = document.activeElement as HTMLElement | undefined
- const tag = (ae?.tagName || "").toLowerCase()
- const inputFocused = !!ae && (tag === "input" || tag === "textarea" || ae.isContentEditable)
- if (inputFocused) return
- if (e.getModifierState(MOD) && e.key.toLowerCase() === "a") {
- e.preventDefault()
- if (!container) return
- const element = container.querySelector("code") as HTMLElement | undefined
- if (!element) return
- const lines = Array.from(element.querySelectorAll(".line"))
- if (!lines.length) return
- const r = document.createRange()
- const last = lines[lines.length - 1]
- r.selectNodeContents(last)
- const lastLen = r.toString().length
- ctx.file.select(local.path, { startLine: 1, startChar: 0, endLine: lines.length, endChar: lastLen })
- }
- }
-
- container.addEventListener("scroll", onScroll)
- document.addEventListener("selectionchange", onSelectionChange)
- document.addEventListener("keydown", onKeyDown)
-
- onCleanup(() => {
- container?.removeEventListener("scroll", onScroll)
- document.removeEventListener("selectionchange", onSelectionChange)
- document.removeEventListener("keydown", onKeyDown)
- })
- })
-
- // Restore scroll position from store when content is ready
- createEffect(() => {
- const content = html()
- if (!container || !content) return
- const top = ctx.file.node(local.path)?.scrollTop
- if (top !== undefined && container.scrollTop !== top) container.scrollTop = top
- })
-
- // Sync selection from store -> DOM
- createEffect(() => {
- const content = html()
- if (!container || !content) return
- if (ctx.file.active()?.path !== local.path) return
- const codeEl = container.querySelector("code") as HTMLElement | undefined
- if (!codeEl) return
- const target = ctx.file.node(local.path)?.selection
- const current = getSelectionInContainer(container)
- const sel = window.getSelection()
- if (!sel) return
- if (!target) {
- if (current) {
- isProgrammaticSelection = true
- sel.removeAllRanges()
- queueMicrotask(() => {
- isProgrammaticSelection = false
- })
- }
- return
- }
- const matches = !!(
- current &&
- current.sl === target.startLine &&
- current.sch === target.startChar &&
- current.el === target.endLine &&
- current.ech === target.endChar
- )
- if (matches) return
- const lines = Array.from(codeEl.querySelectorAll(".line"))
- if (lines.length === 0) return
- let sIdx = Math.max(0, target.startLine - 1)
- let eIdx = Math.max(0, target.endLine - 1)
- let sChar = Math.max(0, target.startChar || 0)
- let eChar = Math.max(0, target.endChar || 0)
- if (sIdx > eIdx || (sIdx === eIdx && sChar > eChar)) {
- const ti = sIdx
- sIdx = eIdx
- eIdx = ti
- const tc = sChar
- sChar = eChar
- eChar = tc
- }
- if (eChar === 0 && eIdx > sIdx) {
- eIdx = eIdx - 1
- eChar = Number.POSITIVE_INFINITY
- }
- if (sIdx >= lines.length) return
- if (eIdx >= lines.length) eIdx = lines.length - 1
- const s = getNodeOffsetInLine(lines[sIdx], sChar) ?? { node: lines[sIdx], offset: 0 }
- const e = getNodeOffsetInLine(lines[eIdx], eChar) ?? { node: lines[eIdx], offset: lines[eIdx].childNodes.length }
- const range = document.createRange()
- range.setStart(s.node, s.offset)
- range.setEnd(e.node, e.offset)
- isProgrammaticSelection = true
- sel.removeAllRanges()
- sel.addRange(range)
- queueMicrotask(() => {
- isProgrammaticSelection = false
- })
- })
-
- // Build/toggle split layout and apply folding (both unified and split)
- createEffect(() => {
- const content = html()
- if (!container || !content) return
- const view = ctx.file.view(local.path)
-
- const pres = Array.from(container.querySelectorAll<HTMLPreElement>("pre"))
- if (pres.length === 0) return
- const originalPre = pres[0]
-
- const split = container.querySelector<HTMLElement>(".diff-split")
- if (view === "diff-split") {
- applySplitDiff(container)
- const next = container.querySelector<HTMLElement>(".diff-split")
- if (next) next.style.display = ""
- originalPre.style.display = "none"
- } else {
- if (split) split.style.display = "none"
- originalPre.style.display = ""
- }
-
- const expanded = ctx.file.folded(local.path)
- if (view === "diff-split") {
- const left = container.querySelector<HTMLElement>(".diff-split pre:nth-child(1) code")
- const right = container.querySelector<HTMLElement>(".diff-split pre:nth-child(2) code")
- if (left)
- applyDiffFolding(left, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "left" })
- if (right)
- applyDiffFolding(right, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "right" })
- } else {
- const code = container.querySelector<HTMLElement>("pre code")
- if (code)
- applyDiffFolding(code, 3, {
- expanded,
- onExpand: (key) => ctx.file.unfold(local.path, key),
- })
- }
- })
-
- // Highlight groups + scroll coupling
- const clearHighlights = () => {
- if (!container) return
- container.querySelectorAll<HTMLElement>(".diff-selected").forEach((el) => el.classList.remove("diff-selected"))
- }
-
- const applyHighlight = (idx: number, scroll?: boolean) => {
- if (!container) return
- const view = ctx.file.view(local.path)
- if (view === "raw") return
-
- clearHighlights()
-
- const nodes: HTMLElement[] = []
- if (view === "diff-split") {
- const left = container.querySelector<HTMLElement>(".diff-split pre:nth-child(1) code")
- const right = container.querySelector<HTMLElement>(".diff-split pre:nth-child(2) code")
- if (left)
- nodes.push(...Array.from(left.querySelectorAll<HTMLElement>(`[data-chgrp="${idx}"][data-diff="remove"]`)))
- if (right)
- nodes.push(...Array.from(right.querySelectorAll<HTMLElement>(`[data-chgrp="${idx}"][data-diff="add"]`)))
- } else {
- const code = container.querySelector<HTMLElement>("pre code")
- if (code) nodes.push(...Array.from(code.querySelectorAll<HTMLElement>(`[data-chgrp="${idx}"]`)))
- }
-
- for (const n of nodes) n.classList.add("diff-selected")
- if (scroll && nodes.length) nodes[0].scrollIntoView({ block: "center", behavior: "smooth" })
- }
-
- const countGroups = () => {
- if (!container) return 0
- const code = container.querySelector<HTMLElement>("pre code")
- if (!code) return 0
- const set = new Set<string>()
- for (const el of Array.from(code.querySelectorAll<HTMLElement>(".diff-line[data-chgrp]"))) {
- const v = el.getAttribute("data-chgrp")
- if (v != undefined) set.add(v)
- }
- return set.size
- }
-
- let lastIdx: number | undefined = undefined
- let lastView: string | undefined
- let lastContent: string | undefined
- let lastRawIdx: number | undefined = undefined
- createEffect(() => {
- const content = html()
- if (!container || !content) return
- const view = ctx.file.view(local.path)
- const raw = ctx.file.changeIndex(local.path)
- if (raw === undefined) return
- const total = countGroups()
- if (total <= 0) return
- const next = ((raw % total) + total) % total
-
- const navigated = lastRawIdx !== undefined && lastRawIdx !== raw
-
- if (next !== raw) {
- ctx.file.setChangeIndex(local.path, next)
- applyHighlight(next, true)
- } else {
- if (lastView !== view || lastContent !== content) applyHighlight(next)
- if ((lastIdx !== undefined && lastIdx !== next) || navigated) applyHighlight(next, true)
- }
-
- lastRawIdx = raw
- lastIdx = next
- lastView = view
- lastContent = content
- })
-
- return (
- <Suspense>
- <div
- ref={(el) => {
- container = el
- }}
- innerHTML={html()}
- class="
- font-mono text-xs tracking-wide overflow-y-auto no-scrollbar h-full
- [&]:[counter-reset:line]
- [&_pre]:focus-visible:outline-none
- [&_pre]:overflow-x-auto [&_pre]:no-scrollbar
- [&_code]:min-w-full [&_code]:inline-block [&_code]:pb-40
- [&_.tab]:relative
- [&_.tab::before]:content['⇥']
- [&_.tab::before]:absolute
- [&_.tab::before]:opacity-0
- [&_.space]:relative
- [&_.space::before]:content-['·']
- [&_.space::before]:absolute
- [&_.space::before]:opacity-0
- [&_.line]:inline-block [&_.line]:w-full
- [&_.line]:hover:bg-background-element
- [&_.line::before]:sticky [&_.line::before]:left-0
- [&_.line::before]:w-12 [&_.line::before]:pr-4
- [&_.line::before]:z-10
- [&_.line::before]:bg-background-panel
- [&_.line::before]:text-text-muted/60
- [&_.line::before]:text-right [&_.line::before]:inline-block
- [&_.line::before]:select-none
- [&_.line::before]:[counter-increment:line]
- [&_.line::before]:content-[counter(line)]
- [&_code.code-diff_.line::before]:content-['']
- [&_code.code-diff_.line::before]:w-0
- [&_code.code-diff_.line::before]:pr-0
- [&_.diff-split_code.code-diff::before]:w-10
- [&_.diff-split_.diff-newln]:left-0
- [&_.diff-oldln]:sticky [&_.diff-oldln]:left-0
- [&_.diff-oldln]:w-10 [&_.diff-oldln]:pr-2
- [&_.diff-oldln]:z-40
- [&_.diff-oldln]:text-text-muted/60
- [&_.diff-oldln]:text-right [&_.diff-oldln]:inline-block
- [&_.diff-oldln]:select-none
- [&_.diff-oldln]:bg-background-panel
- [&_.diff-newln]:sticky [&_.diff-newln]:left-10
- [&_.diff-newln]:w-10 [&_.diff-newln]:pr-2
- [&_.diff-newln]:z-40
- [&_.diff-newln]:text-text-muted/60
- [&_.diff-newln]:text-right [&_.diff-newln]:inline-block
- [&_.diff-newln]:select-none
- [&_.diff-newln]:bg-background-panel
- [&_.diff-add]:bg-success/20!
- [&_.diff-add.diff-selected]:bg-success/50!
- [&_.diff-add_.diff-oldln]:bg-success!
- [&_.diff-add_.diff-oldln]:text-background-panel!
- [&_.diff-add_.diff-newln]:bg-success!
- [&_.diff-add_.diff-newln]:text-background-panel!
- [&_.diff-remove]:bg-error/20!
- [&_.diff-remove.diff-selected]:bg-error/50!
- [&_.diff-remove_.diff-newln]:bg-error!
- [&_.diff-remove_.diff-newln]:text-background-panel!
- [&_.diff-remove_.diff-oldln]:bg-error!
- [&_.diff-remove_.diff-oldln]:text-background-panel!
- [&_.diff-sign]:inline-block [&_.diff-sign]:px-2 [&_.diff-sign]:select-none
- [&_.diff-blank]:bg-background-element
- [&_.diff-blank_.diff-oldln]:bg-background-element
- [&_.diff-blank_.diff-newln]:bg-background-element
- [&_.diff-collapsed]:block! [&_.diff-collapsed]:w-full [&_.diff-collapsed]:relative
- [&_.diff-collapsed]:cursor-pointer [&_.diff-collapsed]:select-none
- [&_.diff-collapsed]:bg-info/20 [&_.diff-collapsed]:hover:bg-info/40!
- [&_.diff-collapsed]:text-info/80 [&_.diff-collapsed]:hover:text-info
- [&_.diff-collapsed]:text-xs
- [&_.diff-collapsed_.diff-oldln]:bg-info!
- [&_.diff-collapsed_.diff-newln]:bg-info!
- "
- classList={{
- ...(local.classList || {}),
- [local.class ?? ""]: !!local.class,
- }}
- {...others}
- />
- </Suspense>
- )
-}
diff --git a/packages/app/src/components/markdown.tsx b/packages/app/src/components/markdown.tsx
index 1fb2cf836..ab6232ecf 100644
--- a/packages/app/src/components/markdown.tsx
+++ b/packages/app/src/components/markdown.tsx
@@ -1,584 +1,6 @@
-import { transformerNotationDiff } from "@shikijs/transformers"
-import { marked } from "marked"
-import markedShiki from "marked-shiki"
-import { codeToHtml } from "shiki"
+import { useMarked } from "@/context"
import { createResource } from "solid-js"
-const markedWithShiki = marked.use(
- markedShiki({
- highlight(code, lang) {
- return codeToHtml(code, {
- // structure: "inline",
- lang: lang || "text",
- tabindex: false,
- theme: {
- colors: {
- "actionBar.toggledBackground": "var(--theme-background-element)",
- "activityBarBadge.background": "var(--theme-accent)",
- "checkbox.border": "var(--theme-border)",
- "editor.background": "transparent",
- "editor.foreground": "var(--theme-text)",
- "editor.inactiveSelectionBackground": "var(--theme-background-element)",
- "editor.selectionHighlightBackground": "var(--theme-border-active)",
- "editorIndentGuide.activeBackground1": "var(--theme-border-subtle)",
- "editorIndentGuide.background1": "var(--theme-border-subtle)",
- "input.placeholderForeground": "var(--theme-text-muted)",
- "list.activeSelectionIconForeground": "var(--theme-text)",
- "list.dropBackground": "var(--theme-background-element)",
- "menu.background": "var(--theme-background-panel)",
- "menu.border": "var(--theme-border)",
- "menu.foreground": "var(--theme-text)",
- "menu.selectionBackground": "var(--theme-primary)",
- "menu.separatorBackground": "var(--theme-border)",
- "ports.iconRunningProcessForeground": "var(--theme-success)",
- "sideBarSectionHeader.background": "transparent",
- "sideBarSectionHeader.border": "var(--theme-border-subtle)",
- "sideBarTitle.foreground": "var(--theme-text-muted)",
- "statusBarItem.remoteBackground": "var(--theme-success)",
- "statusBarItem.remoteForeground": "var(--theme-text)",
- "tab.lastPinnedBorder": "var(--theme-border-subtle)",
- "tab.selectedBackground": "var(--theme-background-element)",
- "tab.selectedForeground": "var(--theme-text-muted)",
- "terminal.inactiveSelectionBackground": "var(--theme-background-element)",
- "widget.border": "var(--theme-border)",
- },
- displayName: "opencode",
- name: "opencode",
- semanticHighlighting: true,
- semanticTokenColors: {
- customLiteral: "var(--theme-syntax-function)",
- newOperator: "var(--theme-syntax-operator)",
- numberLiteral: "var(--theme-syntax-number)",
- stringLiteral: "var(--theme-syntax-string)",
- },
- tokenColors: [
- {
- scope: [
- "meta.embedded",
- "source.groovy.embedded",
- "string meta.image.inline.markdown",
- "variable.legacy.builtin.python",
- ],
- settings: {
- foreground: "var(--theme-text)",
- },
- },
- {
- scope: "emphasis",
- settings: {
- fontStyle: "italic",
- },
- },
- {
- scope: "strong",
- settings: {
- fontStyle: "bold",
- },
- },
- {
- scope: "header",
- settings: {
- foreground: "var(--theme-markdown-heading)",
- },
- },
- {
- scope: "comment",
- settings: {
- foreground: "var(--theme-syntax-comment)",
- },
- },
- {
- scope: "constant.language",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: [
- "constant.numeric",
- "variable.other.enummember",
- "keyword.operator.plus.exponent",
- "keyword.operator.minus.exponent",
- ],
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: "constant.regexp",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "entity.name.tag",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["entity.name.tag.css", "entity.name.tag.less"],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "entity.other.attribute-name",
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: [
- "entity.other.attribute-name.class.css",
- "source.css entity.other.attribute-name.class",
- "entity.other.attribute-name.id.css",
- "entity.other.attribute-name.parent-selector.css",
- "entity.other.attribute-name.parent.less",
- "source.css entity.other.attribute-name.pseudo-class",
- "entity.other.attribute-name.pseudo-element.css",
- "source.css.less entity.other.attribute-name.id",
- "entity.other.attribute-name.scss",
- ],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "invalid",
- settings: {
- foreground: "var(--theme-error)",
- },
- },
- {
- scope: "markup.underline",
- settings: {
- fontStyle: "underline",
- },
- },
- {
- scope: "markup.bold",
- settings: {
- fontStyle: "bold",
- foreground: "var(--theme-markdown-strong)",
- },
- },
- {
- scope: "markup.heading",
- settings: {
- fontStyle: "bold",
- foreground: "var(--theme-markdown-heading)",
- },
- },
- {
- scope: "markup.italic",
- settings: {
- fontStyle: "italic",
- },
- },
- {
- scope: "markup.strikethrough",
- settings: {
- fontStyle: "strikethrough",
- },
- },
- {
- scope: "markup.inserted",
- settings: {
- foreground: "var(--theme-diff-added)",
- },
- },
- {
- scope: "markup.deleted",
- settings: {
- foreground: "var(--theme-diff-removed)",
- },
- },
- {
- scope: "markup.changed",
- settings: {
- foreground: "var(--theme-diff-context)",
- },
- },
- {
- scope: "punctuation.definition.quote.begin.markdown",
- settings: {
- foreground: "var(--theme-markdown-block-quote)",
- },
- },
- {
- scope: "punctuation.definition.list.begin.markdown",
- settings: {
- foreground: "var(--theme-markdown-list-enumeration)",
- },
- },
- {
- scope: "markup.inline.raw",
- settings: {
- foreground: "var(--theme-markdown-code)",
- },
- },
- {
- scope: "punctuation.definition.tag",
- settings: {
- foreground: "var(--theme-syntax-punctuation)",
- },
- },
- {
- scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "meta.preprocessor.string",
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "meta.preprocessor.numeric",
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: "meta.structure.dictionary.key.python",
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: "meta.diff.header",
- settings: {
- foreground: "var(--theme-diff-hunk-header)",
- },
- },
- {
- scope: "storage",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "storage.type",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["storage.modifier", "keyword.operator.noexcept"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["string", "meta.embedded.assembly"],
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "string.tag",
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "string.value",
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: "string.regexp",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: [
- "punctuation.definition.template-expression.begin",
- "punctuation.definition.template-expression.end",
- "punctuation.section.embedded",
- ],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: ["meta.template.expression"],
- settings: {
- foreground: "var(--theme-text)",
- },
- },
- {
- scope: [
- "support.type.vendored.property-name",
- "support.type.property-name",
- "source.css variable",
- "source.coffee.embedded",
- ],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: "keyword",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "keyword.control",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "keyword.operator",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: [
- "keyword.operator.new",
- "keyword.operator.expression",
- "keyword.operator.cast",
- "keyword.operator.sizeof",
- "keyword.operator.alignof",
- "keyword.operator.typeid",
- "keyword.operator.alignas",
- "keyword.operator.instanceof",
- "keyword.operator.logical.python",
- "keyword.operator.wordlike",
- ],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "keyword.other.unit",
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "support.function.git-rebase",
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: "constant.sha.git-rebase",
- settings: {
- foreground: "var(--theme-syntax-number)",
- },
- },
- {
- scope: [
- "storage.modifier.import.java",
- "variable.language.wildcard.java",
- "storage.modifier.package.java",
- ],
- settings: {
- foreground: "var(--theme-text)",
- },
- },
- {
- scope: "variable.language",
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: [
- "entity.name.function",
- "support.function",
- "support.constant.handlebars",
- "source.powershell variable.other.member",
- "entity.name.operator.custom-literal",
- ],
- settings: {
- foreground: "var(--theme-syntax-function)",
- },
- },
- {
- scope: [
- "support.class",
- "support.type",
- "entity.name.type",
- "entity.name.namespace",
- "entity.other.attribute",
- "entity.name.scope-resolution",
- "entity.name.class",
- "storage.type.numeric.go",
- "storage.type.byte.go",
- "storage.type.boolean.go",
- "storage.type.string.go",
- "storage.type.uintptr.go",
- "storage.type.error.go",
- "storage.type.rune.go",
- "storage.type.cs",
- "storage.type.generic.cs",
- "storage.type.modifier.cs",
- "storage.type.variable.cs",
- "storage.type.annotation.java",
- "storage.type.generic.java",
- "storage.type.java",
- "storage.type.object.array.java",
- "storage.type.primitive.array.java",
- "storage.type.primitive.java",
- "storage.type.token.java",
- "storage.type.groovy",
- "storage.type.annotation.groovy",
- "storage.type.parameters.groovy",
- "storage.type.generic.groovy",
- "storage.type.object.array.groovy",
- "storage.type.primitive.array.groovy",
- "storage.type.primitive.groovy",
- ],
- settings: {
- foreground: "var(--theme-syntax-type)",
- },
- },
- {
- scope: [
- "meta.type.cast.expr",
- "meta.type.new.expr",
- "support.constant.math",
- "support.constant.dom",
- "support.constant.json",
- "entity.other.inherited-class",
- "punctuation.separator.namespace.ruby",
- ],
- settings: {
- foreground: "var(--theme-syntax-type)",
- },
- },
- {
- scope: [
- "keyword.control",
- "source.cpp keyword.operator.new",
- "keyword.operator.delete",
- "keyword.other.using",
- "keyword.other.directive.using",
- "keyword.other.operator",
- "entity.name.operator",
- ],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: [
- "variable",
- "meta.definition.variable.name",
- "support.variable",
- "entity.name.variable",
- "constant.other.placeholder",
- ],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: ["variable.other.constant", "variable.other.enummember"],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: ["meta.object-literal.key"],
- settings: {
- foreground: "var(--theme-syntax-variable)",
- },
- },
- {
- scope: [
- "support.constant.property-value",
- "support.constant.font-name",
- "support.constant.media-type",
- "support.constant.media",
- "constant.other.color.rgb-value",
- "constant.other.rgb-value",
- "support.constant.color",
- ],
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: [
- "punctuation.definition.group.regexp",
- "punctuation.definition.group.assertion.regexp",
- "punctuation.definition.character-class.regexp",
- "punctuation.character.set.begin.regexp",
- "punctuation.character.set.end.regexp",
- "keyword.operator.negation.regexp",
- "support.other.parenthesis.regexp",
- ],
- settings: {
- foreground: "var(--theme-syntax-string)",
- },
- },
- {
- scope: [
- "constant.character.character-class.regexp",
- "constant.other.character-class.set.regexp",
- "constant.other.character-class.regexp",
- "constant.character.set.regexp",
- ],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "keyword.operator.quantifier.regexp",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: ["constant.character", "constant.other.option"],
- settings: {
- foreground: "var(--theme-syntax-keyword)",
- },
- },
- {
- scope: "constant.character.escape",
- settings: {
- foreground: "var(--theme-syntax-operator)",
- },
- },
- {
- scope: "entity.name.label",
- settings: {
- foreground: "var(--theme-text-muted)",
- },
- },
- ],
- type: "dark",
- },
- transformers: [transformerNotationDiff()],
- })
- },
- }),
-)
-
function strip(text: string): string {
const wrappedRe = /^\s*<([A-Za-z]\w*)>\s*([\s\S]*?)\s*<\/\1>\s*$/
const match = text.match(wrappedRe)
@@ -586,10 +8,11 @@ function strip(text: string): string {
}
export default function Markdown(props: { text: string; class?: string }) {
+ const marked = useMarked()
const [html] = createResource(
() => strip(props.text),
async (markdown) => {
- return markedWithShiki.parse(markdown)
+ return marked.parse(markdown)
},
)
return (
diff --git a/packages/app/src/context/event.tsx b/packages/app/src/context/event.tsx
new file mode 100644
index 000000000..a2aa54181
--- /dev/null
+++ b/packages/app/src/context/event.tsx
@@ -0,0 +1,34 @@
+import { createContext, useContext, type ParentProps } from "solid-js"
+import { createEventBus } from "@solid-primitives/event-bus"
+import type { Event as SDKEvent } from "@opencode-ai/sdk"
+import { useSDK } from "@/context"
+
+export type Event = SDKEvent // can extend with custom events later
+
+function init() {
+ const sdk = useSDK()
+ const bus = createEventBus<Event>()
+ sdk.event.subscribe().then(async (events) => {
+ for await (const event of events.stream) {
+ bus.emit(event)
+ }
+ })
+ return bus
+}
+
+type EventContext = ReturnType<typeof init>
+
+const ctx = createContext<EventContext>()
+
+export function EventProvider(props: ParentProps) {
+ const value = init()
+ return <ctx.Provider value={value}>{props.children}</ctx.Provider>
+}
+
+export function useEvent() {
+ const value = useContext(ctx)
+ if (!value) {
+ throw new Error("useEvent must be used within a EventProvider")
+ }
+ return value
+}
diff --git a/packages/app/src/context/index.ts b/packages/app/src/context/index.ts
index ef2bbd9c3..bc4bf3b1d 100644
--- a/packages/app/src/context/index.ts
+++ b/packages/app/src/context/index.ts
@@ -1,4 +1,7 @@
+export { EventProvider, useEvent } from "./event"
export { LocalProvider, useLocal } from "./local"
+export { MarkedProvider, useMarked } from "./marked"
export { SDKProvider, useSDK } from "./sdk"
+export { ShikiProvider, useShiki } from "./shiki"
export { SyncProvider, useSync } from "./sync"
export { ThemeProvider, useTheme } from "./theme"
diff --git a/packages/app/src/context/local.tsx b/packages/app/src/context/local.tsx
index eff152642..825023616 100644
--- a/packages/app/src/context/local.tsx
+++ b/packages/app/src/context/local.tsx
@@ -1,9 +1,8 @@
import { createStore, produce, reconcile } from "solid-js/store"
import { batch, createContext, createEffect, createMemo, useContext, type ParentProps } from "solid-js"
-import { useSync } from "./sync"
import { uniqueBy } from "remeda"
import type { FileContent, FileNode } from "@opencode-ai/sdk"
-import { useSDK } from "./sdk"
+import { useSDK, useEvent, useSync } from "@/context"
export type LocalFile = FileNode &
Partial<{
@@ -165,17 +164,19 @@ function init() {
})
}
- const load = async (path: string) =>
- sdk.file.read({ query: { path } }).then((x) => {
+ const load = async (path: string) => {
+ const relative = path.replace(sync.data.path.directory + "/", "")
+ sdk.file.read({ query: { path: relative } }).then((x) => {
setStore(
"node",
- path,
+ relative,
produce((draft) => {
draft.loaded = true
draft.content = x.data
}),
)
})
+ }
const open = async (path: string) => {
const relative = path.replace(sync.data.path.directory + "/", "")
@@ -213,27 +214,27 @@ function init() {
})
}
- sdk.event.subscribe().then(async (events) => {
- for await (const event of events.stream) {
- switch (event.type) {
- case "message.part.updated":
- const part = event.properties.part
- if (part.type === "tool" && part.state.status === "completed") {
- switch (part.tool) {
- case "read":
- console.log("read", part.state.input)
- break
- case "edit":
- const absolute = part.state.input["filePath"] as string
- const path = absolute.replace(sync.data.path.directory + "/", "")
- load(path)
- break
- default:
- break
- }
+ const bus = useEvent()
+ bus.listen((event) => {
+ switch (event.type) {
+ case "message.part.updated":
+ const part = event.properties.part
+ if (part.type === "tool" && part.state.status === "completed") {
+ switch (part.tool) {
+ case "read":
+ console.log("read", part.state.input)
+ break
+ case "edit":
+ load(part.state.input["filePath"] as string)
+ break
+ default:
+ break
}
- break
- }
+ }
+ break
+ case "file.watcher.updated":
+ load(event.properties.file)
+ break
}
})
diff --git a/packages/app/src/context/marked.tsx b/packages/app/src/context/marked.tsx
new file mode 100644
index 000000000..33fea8db6
--- /dev/null
+++ b/packages/app/src/context/marked.tsx
@@ -0,0 +1,40 @@
+import { createContext, useContext, type ParentProps } from "solid-js"
+import { useShiki } from "@/context"
+import { marked } from "marked"
+import markedShiki from "marked-shiki"
+import type { BundledLanguage } from "shiki"
+
+function init(highlighter: ReturnType<typeof useShiki>) {
+ return marked.use(
+ markedShiki({
+ async highlight(code, lang) {
+ if (!highlighter.getLoadedLanguages().includes(lang)) {
+ await highlighter.loadLanguage(lang as BundledLanguage)
+ }
+ return highlighter.codeToHtml(code, {
+ lang: lang || "text",
+ theme: "opencode",
+ tabindex: false,
+ })
+ },
+ }),
+ )
+}
+
+type MarkedContext = ReturnType<typeof init>
+
+const ctx = createContext<MarkedContext>()
+
+export function MarkedProvider(props: ParentProps) {
+ const highlighter = useShiki()
+ const value = init(highlighter)
+ return <ctx.Provider value={value}>{props.children}</ctx.Provider>
+}
+
+export function useMarked() {
+ const value = useContext(ctx)
+ if (!value) {
+ throw new Error("useMarked must be used within a MarkedProvider")
+ }
+ return value
+}
diff --git a/packages/app/src/context/shiki.tsx b/packages/app/src/context/shiki.tsx
new file mode 100644
index 000000000..1930b907c
--- /dev/null
+++ b/packages/app/src/context/shiki.tsx
@@ -0,0 +1,582 @@
+import { createHighlighter, type ThemeInput } from "shiki"
+import { createContext, useContext, type ParentProps } from "solid-js"
+
+const theme: ThemeInput = {
+ colors: {
+ "actionBar.toggledBackground": "var(--theme-background-element)",
+ "activityBarBadge.background": "var(--theme-accent)",
+ "checkbox.border": "var(--theme-border)",
+ "editor.background": "transparent",
+ "editor.foreground": "var(--theme-text)",
+ "editor.inactiveSelectionBackground": "var(--theme-background-element)",
+ "editor.selectionHighlightBackground": "var(--theme-border-active)",
+ "editorIndentGuide.activeBackground1": "var(--theme-border-subtle)",
+ "editorIndentGuide.background1": "var(--theme-border-subtle)",
+ "input.placeholderForeground": "var(--theme-text-muted)",
+ "list.activeSelectionIconForeground": "var(--theme-text)",
+ "list.dropBackground": "var(--theme-background-element)",
+ "menu.background": "var(--theme-background-panel)",
+ "menu.border": "var(--theme-border)",
+ "menu.foreground": "var(--theme-text)",
+ "menu.selectionBackground": "var(--theme-primary)",
+ "menu.separatorBackground": "var(--theme-border)",
+ "ports.iconRunningProcessForeground": "var(--theme-success)",
+ "sideBarSectionHeader.background": "transparent",
+ "sideBarSectionHeader.border": "var(--theme-border-subtle)",
+ "sideBarTitle.foreground": "var(--theme-text-muted)",
+ "statusBarItem.remoteBackground": "var(--theme-success)",
+ "statusBarItem.remoteForeground": "var(--theme-text)",
+ "tab.lastPinnedBorder": "var(--theme-border-subtle)",
+ "tab.selectedBackground": "var(--theme-background-element)",
+ "tab.selectedForeground": "var(--theme-text-muted)",
+ "terminal.inactiveSelectionBackground": "var(--theme-background-element)",
+ "widget.border": "var(--theme-border)",
+ },
+ displayName: "opencode",
+ name: "opencode",
+ semanticHighlighting: true,
+ semanticTokenColors: {
+ customLiteral: "var(--theme-syntax-function)",
+ newOperator: "var(--theme-syntax-operator)",
+ numberLiteral: "var(--theme-syntax-number)",
+ stringLiteral: "var(--theme-syntax-string)",
+ },
+ tokenColors: [
+ {
+ scope: [
+ "meta.embedded",
+ "source.groovy.embedded",
+ "string meta.image.inline.markdown",
+ "variable.legacy.builtin.python",
+ ],
+ settings: {
+ foreground: "var(--theme-text)",
+ },
+ },
+ {
+ scope: "emphasis",
+ settings: {
+ fontStyle: "italic",
+ },
+ },
+ {
+ scope: "strong",
+ settings: {
+ fontStyle: "bold",
+ },
+ },
+ {
+ scope: "header",
+ settings: {
+ foreground: "var(--theme-markdown-heading)",
+ },
+ },
+ {
+ scope: "comment",
+ settings: {
+ foreground: "var(--theme-syntax-comment)",
+ },
+ },
+ {
+ scope: "constant.language",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: [
+ "constant.numeric",
+ "variable.other.enummember",
+ "keyword.operator.plus.exponent",
+ "keyword.operator.minus.exponent",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-number)",
+ },
+ },
+ {
+ scope: "constant.regexp",
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: "entity.name.tag",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: ["entity.name.tag.css", "entity.name.tag.less"],
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: "entity.other.attribute-name",
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: [
+ "entity.other.attribute-name.class.css",
+ "source.css entity.other.attribute-name.class",
+ "entity.other.attribute-name.id.css",
+ "entity.other.attribute-name.parent-selector.css",
+ "entity.other.attribute-name.parent.less",
+ "source.css entity.other.attribute-name.pseudo-class",
+ "entity.other.attribute-name.pseudo-element.css",
+ "source.css.less entity.other.attribute-name.id",
+ "entity.other.attribute-name.scss",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: "invalid",
+ settings: {
+ foreground: "var(--theme-error)",
+ },
+ },
+ {
+ scope: "markup.underline",
+ settings: {
+ fontStyle: "underline",
+ },
+ },
+ {
+ scope: "markup.bold",
+ settings: {
+ fontStyle: "bold",
+ foreground: "var(--theme-markdown-strong)",
+ },
+ },
+ {
+ scope: "markup.heading",
+ settings: {
+ fontStyle: "bold",
+ foreground: "var(--theme-markdown-heading)",
+ },
+ },
+ {
+ scope: "markup.italic",
+ settings: {
+ fontStyle: "italic",
+ },
+ },
+ {
+ scope: "markup.strikethrough",
+ settings: {
+ fontStyle: "strikethrough",
+ },
+ },
+ {
+ scope: "markup.inserted",
+ settings: {
+ foreground: "var(--theme-diff-added)",
+ },
+ },
+ {
+ scope: "markup.deleted",
+ settings: {
+ foreground: "var(--theme-diff-removed)",
+ },
+ },
+ {
+ scope: "markup.changed",
+ settings: {
+ foreground: "var(--theme-diff-context)",
+ },
+ },
+ {
+ scope: "punctuation.definition.quote.begin.markdown",
+ settings: {
+ foreground: "var(--theme-markdown-block-quote)",
+ },
+ },
+ {
+ scope: "punctuation.definition.list.begin.markdown",
+ settings: {
+ foreground: "var(--theme-markdown-list-enumeration)",
+ },
+ },
+ {
+ scope: "markup.inline.raw",
+ settings: {
+ foreground: "var(--theme-markdown-code)",
+ },
+ },
+ {
+ scope: "punctuation.definition.tag",
+ settings: {
+ foreground: "var(--theme-syntax-punctuation)",
+ },
+ },
+ {
+ scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "meta.preprocessor.string",
+ settings: {
+ foreground: "var(--theme-syntax-string)",
+ },
+ },
+ {
+ scope: "meta.preprocessor.numeric",
+ settings: {
+ foreground: "var(--theme-syntax-number)",
+ },
+ },
+ {
+ scope: "meta.structure.dictionary.key.python",
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: "meta.diff.header",
+ settings: {
+ foreground: "var(--theme-diff-hunk-header)",
+ },
+ },
+ {
+ scope: "storage",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "storage.type",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: ["storage.modifier", "keyword.operator.noexcept"],
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: ["string", "meta.embedded.assembly"],
+ settings: {
+ foreground: "var(--theme-syntax-string)",
+ },
+ },
+ {
+ scope: "string.tag",
+ settings: {
+ foreground: "var(--theme-syntax-string)",
+ },
+ },
+ {
+ scope: "string.value",
+ settings: {
+ foreground: "var(--theme-syntax-string)",
+ },
+ },
+ {
+ scope: "string.regexp",
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: [
+ "punctuation.definition.template-expression.begin",
+ "punctuation.definition.template-expression.end",
+ "punctuation.section.embedded",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: ["meta.template.expression"],
+ settings: {
+ foreground: "var(--theme-text)",
+ },
+ },
+ {
+ scope: [
+ "support.type.vendored.property-name",
+ "support.type.property-name",
+ "source.css variable",
+ "source.coffee.embedded",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: "keyword",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "keyword.control",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "keyword.operator",
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: [
+ "keyword.operator.new",
+ "keyword.operator.expression",
+ "keyword.operator.cast",
+ "keyword.operator.sizeof",
+ "keyword.operator.alignof",
+ "keyword.operator.typeid",
+ "keyword.operator.alignas",
+ "keyword.operator.instanceof",
+ "keyword.operator.logical.python",
+ "keyword.operator.wordlike",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "keyword.other.unit",
+ settings: {
+ foreground: "var(--theme-syntax-number)",
+ },
+ },
+ {
+ scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "support.function.git-rebase",
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: "constant.sha.git-rebase",
+ settings: {
+ foreground: "var(--theme-syntax-number)",
+ },
+ },
+ {
+ scope: ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"],
+ settings: {
+ foreground: "var(--theme-text)",
+ },
+ },
+ {
+ scope: "variable.language",
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: [
+ "entity.name.function",
+ "support.function",
+ "support.constant.handlebars",
+ "source.powershell variable.other.member",
+ "entity.name.operator.custom-literal",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-function)",
+ },
+ },
+ {
+ scope: [
+ "support.class",
+ "support.type",
+ "entity.name.type",
+ "entity.name.namespace",
+ "entity.other.attribute",
+ "entity.name.scope-resolution",
+ "entity.name.class",
+ "storage.type.numeric.go",
+ "storage.type.byte.go",
+ "storage.type.boolean.go",
+ "storage.type.string.go",
+ "storage.type.uintptr.go",
+ "storage.type.error.go",
+ "storage.type.rune.go",
+ "storage.type.cs",
+ "storage.type.generic.cs",
+ "storage.type.modifier.cs",
+ "storage.type.variable.cs",
+ "storage.type.annotation.java",
+ "storage.type.generic.java",
+ "storage.type.java",
+ "storage.type.object.array.java",
+ "storage.type.primitive.array.java",
+ "storage.type.primitive.java",
+ "storage.type.token.java",
+ "storage.type.groovy",
+ "storage.type.annotation.groovy",
+ "storage.type.parameters.groovy",
+ "storage.type.generic.groovy",
+ "storage.type.object.array.groovy",
+ "storage.type.primitive.array.groovy",
+ "storage.type.primitive.groovy",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-type)",
+ },
+ },
+ {
+ scope: [
+ "meta.type.cast.expr",
+ "meta.type.new.expr",
+ "support.constant.math",
+ "support.constant.dom",
+ "support.constant.json",
+ "entity.other.inherited-class",
+ "punctuation.separator.namespace.ruby",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-type)",
+ },
+ },
+ {
+ scope: [
+ "keyword.control",
+ "source.cpp keyword.operator.new",
+ "keyword.operator.delete",
+ "keyword.other.using",
+ "keyword.other.directive.using",
+ "keyword.other.operator",
+ "entity.name.operator",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: [
+ "variable",
+ "meta.definition.variable.name",
+ "support.variable",
+ "entity.name.variable",
+ "constant.other.placeholder",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: ["variable.other.constant", "variable.other.enummember"],
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: ["meta.object-literal.key"],
+ settings: {
+ foreground: "var(--theme-syntax-variable)",
+ },
+ },
+ {
+ scope: [
+ "support.constant.property-value",
+ "support.constant.font-name",
+ "support.constant.media-type",
+ "support.constant.media",
+ "constant.other.color.rgb-value",
+ "constant.other.rgb-value",
+ "support.constant.color",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-string)",
+ },
+ },
+ {
+ scope: [
+ "punctuation.definition.group.regexp",
+ "punctuation.definition.group.assertion.regexp",
+ "punctuation.definition.character-class.regexp",
+ "punctuation.character.set.begin.regexp",
+ "punctuation.character.set.end.regexp",
+ "keyword.operator.negation.regexp",
+ "support.other.parenthesis.regexp",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-string)",
+ },
+ },
+ {
+ scope: [
+ "constant.character.character-class.regexp",
+ "constant.other.character-class.set.regexp",
+ "constant.other.character-class.regexp",
+ "constant.character.set.regexp",
+ ],
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: "keyword.operator.quantifier.regexp",
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: ["constant.character", "constant.other.option"],
+ settings: {
+ foreground: "var(--theme-syntax-keyword)",
+ },
+ },
+ {
+ scope: "constant.character.escape",
+ settings: {
+ foreground: "var(--theme-syntax-operator)",
+ },
+ },
+ {
+ scope: "entity.name.label",
+ settings: {
+ foreground: "var(--theme-text-muted)",
+ },
+ },
+ ],
+ type: "dark",
+}
+
+const highlighter = await createHighlighter({
+ themes: [theme],
+ langs: [],
+})
+
+type ShikiContext = typeof highlighter
+
+const ctx = createContext<ShikiContext>()
+
+export function ShikiProvider(props: ParentProps) {
+ return <ctx.Provider value={highlighter}>{props.children}</ctx.Provider>
+}
+
+export function useShiki() {
+ const value = useContext(ctx)
+ if (!value) {
+ throw new Error("useShiki must be used within a ShikiProvider")
+ }
+ return value
+}
diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx
index 22140683d..907071d75 100644
--- a/packages/app/src/context/sync.tsx
+++ b/packages/app/src/context/sync.tsx
@@ -1,7 +1,7 @@
import type { Message, Agent, Provider, Session, Part, Config, Path, File, FileNode } from "@opencode-ai/sdk"
import { createStore, produce, reconcile } from "solid-js/store"
-import { useSDK } from "./sdk"
import { createContext, Show, useContext, type ParentProps } from "solid-js"
+import { useSDK, useEvent } from "@/context"
import { Binary } from "@/utils/binary"
function init() {
@@ -33,69 +33,67 @@ function init() {
changes: [],
})
- const sdk = useSDK()
-
- sdk.event.subscribe().then(async (events) => {
- for await (const event of events.stream) {
- switch (event.type) {
- case "session.updated": {
- const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
- if (result.found) {
- setStore("session", result.index, reconcile(event.properties.info))
- break
- }
- setStore(
- "session",
- produce((draft) => {
- draft.splice(result.index, 0, event.properties.info)
- }),
- )
+ const bus = useEvent()
+ bus.listen((event) => {
+ switch (event.type) {
+ case "session.updated": {
+ const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
+ if (result.found) {
+ setStore("session", result.index, reconcile(event.properties.info))
break
}
- case "message.updated": {
- const messages = store.message[event.properties.info.sessionID]
- if (!messages) {
- setStore("message", event.properties.info.sessionID, [event.properties.info])
- break
- }
- const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
- if (result.found) {
- setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
- break
- }
- setStore(
- "message",
- event.properties.info.sessionID,
- produce((draft) => {
- draft.splice(result.index, 0, event.properties.info)
- }),
- )
+ setStore(
+ "session",
+ produce((draft) => {
+ draft.splice(result.index, 0, event.properties.info)
+ }),
+ )
+ break
+ }
+ case "message.updated": {
+ const messages = store.message[event.properties.info.sessionID]
+ if (!messages) {
+ setStore("message", event.properties.info.sessionID, [event.properties.info])
break
}
- case "message.part.updated": {
- const parts = store.part[event.properties.part.messageID]
- if (!parts) {
- setStore("part", event.properties.part.messageID, [event.properties.part])
- break
- }
- const result = Binary.search(parts, event.properties.part.id, (p) => p.id)
- if (result.found) {
- setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part))
- break
- }
- setStore(
- "part",
- event.properties.part.messageID,
- produce((draft) => {
- draft.splice(result.index, 0, event.properties.part)
- }),
- )
+ const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
+ if (result.found) {
+ setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
break
}
+ setStore(
+ "message",
+ event.properties.info.sessionID,
+ produce((draft) => {
+ draft.splice(result.index, 0, event.properties.info)
+ }),
+ )
+ break
+ }
+ case "message.part.updated": {
+ const parts = store.part[event.properties.part.messageID]
+ if (!parts) {
+ setStore("part", event.properties.part.messageID, [event.properties.part])
+ break
+ }
+ const result = Binary.search(parts, event.properties.part.id, (p) => p.id)
+ if (result.found) {
+ setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part))
+ break
+ }
+ setStore(
+ "part",
+ event.properties.part.messageID,
+ produce((draft) => {
+ draft.splice(result.index, 0, event.properties.part)
+ }),
+ )
+ break
}
}
})
+ const sdk = useSDK()
Promise.all([
sdk.config.providers().then((x) => setStore("provider", x.data!.providers)),
sdk.path.get().then((x) => setStore("path", x.data!)),
diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx
index f305ab819..66ed93d3a 100644
--- a/packages/app/src/index.tsx
+++ b/packages/app/src/index.tsx
@@ -4,7 +4,15 @@ import { Router, Route } from "@solidjs/router"
import "@/index.css"
import Layout from "@/pages/layout"
import Home from "@/pages"
-import { SDKProvider, SyncProvider, LocalProvider, ThemeProvider } from "@/context"
+import {
+ EventProvider,
+ SDKProvider,
+ SyncProvider,
+ LocalProvider,
+ ThemeProvider,
+ ShikiProvider,
+ MarkedProvider,
+} from "@/context"
const root = document.getElementById("root")
@@ -18,15 +26,21 @@ render(
() => (
<div class="h-full bg-background text-text-muted">
<ThemeProvider defaultTheme="opencode" defaultDarkMode={true}>
- <SDKProvider>
- <SyncProvider>
- <LocalProvider>
- <Router root={Layout}>
- <Route path="/" component={Home} />
- </Router>
- </LocalProvider>
- </SyncProvider>
- </SDKProvider>
+ <ShikiProvider>
+ <MarkedProvider>
+ <SDKProvider>
+ <EventProvider>
+ <SyncProvider>
+ <LocalProvider>
+ <Router root={Layout}>
+ <Route path="/" component={Home} />
+ </Router>
+ </LocalProvider>
+ </SyncProvider>
+ </EventProvider>
+ </SDKProvider>
+ </MarkedProvider>
+ </ShikiProvider>
</ThemeProvider>
</div>
),
diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts
index 292c8ba6d..d906e5f81 100644
--- a/packages/opencode/src/cli/cmd/debug/lsp.ts
+++ b/packages/opencode/src/cli/cmd/debug/lsp.ts
@@ -2,6 +2,7 @@ import { LSP } from "../../../lsp"
import { bootstrap } from "../../bootstrap"
import { cmd } from "../cmd"
import { Log } from "../../../util/log"
+import { UI } from "../../ui"
export const LSPCommand = cmd({
command: "lsp",
@@ -15,6 +16,10 @@ const DiagnosticsCommand = cmd({
builder: (yargs) => yargs.positional("file", { type: "string", demandOption: true }),
async handler(args) {
await bootstrap(process.cwd(), async () => {
+ if (!(await Bun.file(args.file).exists())) {
+ UI.error(`File ${args.file} does not exist`)
+ return
+ }
await LSP.touchFile(args.file, true)
console.log(JSON.stringify(await LSP.diagnostics(), null, 2))
})
diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts
index fddff515f..84a80c0fb 100644
--- a/packages/opencode/src/file/watcher.ts
+++ b/packages/opencode/src/file/watcher.ts
@@ -28,7 +28,6 @@ export namespace FileWatcher {
const ignore = (cfg.watcher?.ignore ?? []).map((v) => new Bun.Glob(v))
const watcher = chokidar.watch(Instance.directory, {
ignoreInitial: true,
- awaitWriteFinish: true,
ignored: (filepath) => {
return FileIgnore.match(filepath, {
extra: ignore,
diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts
index bc83c8f7e..87dc16452 100644
--- a/packages/opencode/src/lsp/index.ts
+++ b/packages/opencode/src/lsp/index.ts
@@ -72,7 +72,7 @@ export namespace LSP {
...existing,
id: name,
root: existing?.root ?? (async () => Instance.directory),
- extensions: item.extensions ?? existing.extensions,
+ extensions: item.extensions ?? existing?.extensions ?? [],
spawn: async (root) => {
return {
process: spawn(item.command[0], item.command.slice(1), {
diff --git a/packages/web/src/content/docs/agents.mdx b/packages/web/src/content/docs/agents.mdx
index 4a4459280..3c7bb93af 100644
--- a/packages/web/src/content/docs/agents.mdx
+++ b/packages/web/src/content/docs/agents.mdx
@@ -495,9 +495,7 @@ For quick reference, here are common setups.
}
```
-See the full permissions guide for more patterns.
-
-- /docs/permissions
+See the full [permissions guide](/docs/permissions) for more patterns.
---
diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx
index e9e21cba8..95ef436c1 100644
--- a/packages/web/src/content/docs/lsp.mdx
+++ b/packages/web/src/content/docs/lsp.mdx
@@ -3,7 +3,7 @@ title: LSP Servers
description: opencode integrates with your LSP servers.
---
-opencode integrates with your Language Server Protocol (LSP) to help the LLM interacts with your codebase. It uses diagnostics to provide feedback to the LLM.
+opencode integrates with your Language Server Protocol (LSP) to help the LLM interact with your codebase. It uses diagnostics to provide feedback to the LLM.
---
diff --git a/packages/web/src/content/docs/providers.mdx b/packages/web/src/content/docs/providers.mdx
index f4fdc47de..10e0de2d3 100644
--- a/packages/web/src/content/docs/providers.mdx
+++ b/packages/web/src/content/docs/providers.mdx
@@ -356,6 +356,8 @@ To use your GitHub Copilot subscription with opencode:
:::note
Some models might need a [Pro+
subscription](https://github.com/features/copilot/plans) to use.
+
+Some models need to be manually enabled in your [GitHub Copilot settings](https://docs.github.com/en/copilot/how-tos/use-ai-models/configure-access-to-ai-models#setup-for-individual-use).
:::
1. Run `opencode auth login` and select GitHub Copilot.