@kabmo-labs/kabmo 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +7 -0
  2. package/dist/client/index.html +1 -1
  3. package/dist/client/index.js +314 -314
  4. package/dist/client/static/js/{index-qwpvd9fv.js → index-bpptfkz2.js} +922 -892
  5. package/dist/client/static/js/index-k315p1e8.css +1 -0
  6. package/package.json +9 -1
  7. package/src/client/components/common/code-surface/documents.test.ts +120 -0
  8. package/src/client/components/common/code-surface/documents.ts +201 -0
  9. package/src/client/components/common/code-surface/extensions.test.ts +110 -0
  10. package/src/client/components/common/code-surface/extensions.ts +162 -0
  11. package/src/client/components/common/code-surface/handle.ts +102 -0
  12. package/src/client/components/common/code-surface/index.tsx +253 -314
  13. package/src/client/components/common/code-surface/keymap.test.ts +133 -0
  14. package/src/client/components/common/code-surface/keymap.ts +267 -0
  15. package/src/client/components/common/code-surface/marks.ts +74 -0
  16. package/src/client/components/common/code-surface/shiki.ts +234 -0
  17. package/src/client/components/common/code-surface/sync.test.ts +185 -0
  18. package/src/client/components/common/code-surface/sync.ts +58 -0
  19. package/src/client/components/common/code-surface/theme.test.ts +202 -0
  20. package/src/client/components/common/code-surface/theme.ts +111 -0
  21. package/src/client/components/common/editor-view-buttons/index.test.tsx +148 -0
  22. package/src/client/components/common/editor-view-buttons/index.tsx +109 -0
  23. package/src/client/components/common/shell-update-button/Modal.tsx +163 -0
  24. package/src/client/components/common/shell-update-button/index.test.tsx +251 -0
  25. package/src/client/components/common/shell-update-button/index.tsx +125 -0
  26. package/src/client/components/common/toolbar-row/index.test.tsx +166 -0
  27. package/src/client/components/common/toolbar-row/index.tsx +189 -0
  28. package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +41 -78
  29. package/src/client/components/layout/desktop-layout/index.tsx +47 -67
  30. package/src/client/components/layout/desktop-layout/resizer/index.tsx +6 -3
  31. package/src/client/components/layout/session-sidebar/Footer.tsx +8 -15
  32. package/src/client/components/mobile/LayoutWrapper.tsx +1 -0
  33. package/src/client/components/mobile/mobile-right-sidebar/FullDiff.tsx +3 -0
  34. package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +63 -38
  35. package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.test.tsx +137 -0
  36. package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.tsx +14 -3
  37. package/src/client/components/mobile/mobile-session-sidebar/Footer.tsx +9 -21
  38. package/src/client/components/settings/Modal.tsx +8 -0
  39. package/src/client/components/settings/categories/AppearanceSettings.tsx +3 -0
  40. package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.test.tsx +167 -0
  41. package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.tsx +184 -0
  42. package/src/client/components/settings/categories/project-settings/WorktreeSettings.tsx +8 -2
  43. package/src/client/components/workspace/chat-timeline/EmptyWorkspacePrompt.tsx +16 -8
  44. package/src/client/components/workspace/chat-timeline/SystemNotice.tsx +9 -4
  45. package/src/client/components/workspace/chat-timeline/TaskResultContent.tsx +9 -7
  46. package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.test.tsx +132 -0
  47. package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.tsx +120 -0
  48. package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.test.tsx +319 -0
  49. package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.tsx +285 -0
  50. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.test.ts +2 -0
  51. package/src/client/components/workspace/chat-timeline/tool-renderers/hashline-patch/index.tsx +34 -29
  52. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/AstEdit.tsx +11 -9
  53. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Bash.tsx +10 -7
  54. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Edit.tsx +25 -8
  55. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Eval.tsx +8 -11
  56. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.test.ts +114 -0
  57. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.tsx +111 -0
  58. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Read.tsx +7 -7
  59. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/SearchTool.tsx +9 -59
  60. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ArtifactBody.tsx +2 -7
  61. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.test.ts +27 -28
  62. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.tsx +39 -26
  63. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ExcerptCode.tsx +8 -8
  64. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/HighlightedCode.tsx +35 -0
  65. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock.tsx +4 -4
  66. package/src/client/components/workspace/diff-panel/MergeDiffView.tsx +179 -0
  67. package/src/client/components/workspace/diff-panel/ThemedMergeDiffView.tsx +24 -0
  68. package/src/client/components/workspace/diff-panel/Toolbar.tsx +224 -186
  69. package/src/client/components/workspace/diff-panel/ToolbarSegments.tsx +98 -0
  70. package/src/client/components/workspace/diff-panel/index.tsx +76 -145
  71. package/src/client/components/workspace/diff-panel/merge.ts +315 -0
  72. package/src/client/components/workspace/editor/Body.tsx +4 -7
  73. package/src/client/components/workspace/editor/CommandPalette.tsx +2 -2
  74. package/src/client/components/workspace/editor/FileDocument.tsx +58 -63
  75. package/src/client/components/workspace/editor/FilePane.tsx +4 -7
  76. package/src/client/components/workspace/editor/FindWidget.tsx +7 -2
  77. package/src/client/components/workspace/editor/Toolbar.tsx +220 -123
  78. package/src/client/components/workspace/editor/index.tsx +7 -9
  79. package/src/client/components/workspace/file-explorer/OutlineSection.test.tsx +18 -0
  80. package/src/client/components/workspace/file-explorer/OutlineSection.tsx +10 -3
  81. package/src/client/components/workspace/search-panel/Body.tsx +1 -1
  82. package/src/client/components/workspace/worktree-modal/index.test.tsx +118 -0
  83. package/src/client/hooks/chat/omp/reconnect.test.ts +338 -0
  84. package/src/client/hooks/chat/omp/reconnect.ts +187 -0
  85. package/src/client/hooks/chat/omp/run-liveness.test.ts +71 -0
  86. package/src/client/hooks/chat/omp/run-liveness.ts +65 -0
  87. package/src/client/hooks/chat/omp/session-list.activity.test.ts +114 -0
  88. package/src/client/hooks/chat/omp/session-list.test-util.ts +164 -0
  89. package/src/client/hooks/chat/omp/session-list.test.ts +30 -150
  90. package/src/client/hooks/chat/omp/session-list.tsx +69 -9
  91. package/src/client/hooks/chat/timeline/generating.ts +19 -2
  92. package/src/client/hooks/chat/timeline/index.ts +19 -24
  93. package/src/client/hooks/chat/timeline/lost-run.test.ts +329 -0
  94. package/src/client/hooks/chat/timeline/lost-run.ts +177 -0
  95. package/src/client/hooks/chat/timeline/session-load.test-util.ts +205 -0
  96. package/src/client/hooks/chat/timeline/session-load.test.ts +61 -162
  97. package/src/client/hooks/chat/timeline/session-load.ts +69 -37
  98. package/src/client/hooks/chat/timeline/session-switch.test.ts +65 -0
  99. package/src/client/hooks/chat/timeline/session-switch.ts +63 -0
  100. package/src/client/hooks/code/deferred-highlight.test.ts +182 -0
  101. package/src/client/hooks/code/deferred-highlight.ts +129 -0
  102. package/src/client/hooks/editor/find-state.ts +1 -1
  103. package/src/client/hooks/editor/theme.test.ts +206 -0
  104. package/src/client/hooks/editor/theme.ts +82 -0
  105. package/src/client/hooks/editor/use-editor-drop.test.ts +101 -0
  106. package/src/client/hooks/editor/use-editor-drop.ts +67 -14
  107. package/src/client/hooks/editor/use-editor-find.ts +7 -7
  108. package/src/client/hooks/editor/use-outline-view.test.ts +182 -0
  109. package/src/client/hooks/editor/use-outline-view.ts +94 -7
  110. package/src/client/hooks/editor/use-scroll-restore.test.ts +55 -2
  111. package/src/client/hooks/editor/use-scroll-restore.ts +124 -4
  112. package/src/client/hooks/editor/use-search-reveal.ts +1 -1
  113. package/src/client/hooks/ui/settings-modal.ts +56 -0
  114. package/src/client/hooks/ui/updates/shell-download.test.ts +2 -0
  115. package/src/client/hooks/ui/updates/shell-download.ts +55 -14
  116. package/src/client/hooks/ui/updates/shell-offer.test.ts +26 -0
  117. package/src/client/hooks/ui/updates/shell-update.test.ts +140 -0
  118. package/src/client/hooks/ui/updates/shell-update.ts +119 -0
  119. package/src/client/hooks/workspace/diff-file.ts +29 -10
  120. package/src/client/hooks/workspace/diff-git-actions.ts +147 -0
  121. package/src/client/hooks/workspace/editor-view.ts +61 -0
  122. package/src/client/hooks/workspace/file-tabs.test.ts +62 -5
  123. package/src/client/hooks/workspace/file-tabs.ts +24 -11
  124. package/src/client/hooks/workspace/navbar-session-actions.ts +72 -0
  125. package/src/client/hooks/workspace/worktree/index.ts +20 -5
  126. package/src/client/lib/desktop/bridge.ts +45 -0
  127. package/src/client/lib/search-reveal.ts +1 -1
  128. package/src/client/main.tsx +5 -1
  129. package/src/client/tailwind.css +37 -8
  130. package/src/server/lib/auth/guard.test.ts +28 -0
  131. package/src/server/lib/auth/guard.ts +10 -0
  132. package/src/server/lib/fs/worktree/create.ts +2 -3
  133. package/src/server/lib/fs/worktree/list.ts +13 -0
  134. package/src/server/lib/fs/worktree/remove.ts +3 -3
  135. package/src/server/lib/lifecycle/claim-port.test.ts +156 -0
  136. package/src/server/lib/lifecycle/port-guard.test.ts +20 -164
  137. package/src/server/lib/lifecycle/port-guard.ts +39 -7
  138. package/src/server/lib/lifecycle/probe-health.test.ts +166 -0
  139. package/src/server/lib/lifecycle/probe.test.ts +77 -164
  140. package/src/server/lib/lifecycle/probe.ts +121 -13
  141. package/src/server/lib/omp/rpc/frame-fold-signals.test.ts +112 -0
  142. package/src/server/lib/omp/rpc/frame-fold.ts +10 -5
  143. package/src/server/lib/omp/session/sidebar-activity.server.test.ts +162 -0
  144. package/src/server/lib/omp/session/sidebar-activity.server.ts +70 -0
  145. package/src/server/lib/omp/session/telemetry/compute.ts +13 -1
  146. package/src/server/lib/omp/session/telemetry.test.ts +33 -0
  147. package/src/server/lib/realtime/signals.server.ts +10 -0
  148. package/src/server/lib/realtime/topics/catalog.server.ts +4 -1
  149. package/src/server/lib/realtime/topics.server.ts +16 -0
  150. package/src/server/lib/updates/shell-assets.ts +94 -3
  151. package/src/server/lib/updates/shell-cache.ts +105 -0
  152. package/src/server/lib/updates/shell-pages.test.ts +33 -2
  153. package/src/server/lib/updates/shell-pages.ts +17 -5
  154. package/src/server/lib/updates/shell-release-desktop.test.ts +166 -0
  155. package/src/server/lib/updates/shell-release.test.ts +10 -27
  156. package/src/server/lib/updates/shell-release.ts +107 -111
  157. package/src/server/routes/fs/git-merge.test.ts +11 -1
  158. package/src/server/routes/fs/read.test.ts +24 -7
  159. package/src/server/routes/fs/scope.test.ts +15 -10
  160. package/src/server/routes/fs/scope.ts +3 -14
  161. package/src/server/routes/updates/android.ts +6 -1
  162. package/src/server/routes/updates/desktop.ts +29 -7
  163. package/src/server/routes/updates/ios.ts +5 -1
  164. package/src/server/routes/worktrees/list.ts +11 -1
  165. package/src/server/routes/worktrees/routes.test.ts +17 -0
  166. package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +36 -0
  167. package/src/shared/lib/chat/timeline/omp-callbacks.ts +18 -0
  168. package/src/shared/lib/chat/timeline/run-signals.test.ts +2 -0
  169. package/src/shared/lib/code/editor/comment-syntax.test.ts +37 -0
  170. package/src/shared/lib/code/editor/comment-syntax.ts +55 -0
  171. package/src/shared/lib/code/editor/find-replace.test.ts +134 -0
  172. package/src/shared/lib/code/editor/{find.ts → find-replace.ts} +23 -107
  173. package/src/shared/lib/code/editor/{find.test.ts → find-scan.test.ts} +19 -73
  174. package/src/shared/lib/code/editor/find-scan.ts +177 -0
  175. package/src/shared/lib/code/editor/keymap.ts +3 -3
  176. package/src/shared/lib/code/editor/lines.test.ts +7 -55
  177. package/src/shared/lib/code/editor/lines.ts +8 -48
  178. package/src/shared/lib/code/editor/marks.test.ts +4 -5
  179. package/src/shared/lib/code/editor/marks.ts +13 -25
  180. package/src/shared/lib/code/editor/theme-loader.ts +97 -0
  181. package/src/shared/lib/code/editor/themes.generated.ts +156 -0
  182. package/src/shared/lib/code/editor/themes.test.ts +165 -0
  183. package/src/shared/lib/code/editor/themes.ts +165 -0
  184. package/src/shared/lib/code/highlight-memo.test.ts +103 -0
  185. package/src/shared/lib/code/highlight-memo.ts +149 -0
  186. package/src/shared/lib/code/highlighter-warmup.ts +112 -0
  187. package/src/shared/lib/code/highlighter.ts +3 -1
  188. package/src/shared/lib/code/idle.ts +37 -0
  189. package/src/shared/lib/code/outline/rows.test.ts +96 -0
  190. package/src/shared/lib/code/outline/rows.ts +94 -0
  191. package/src/shared/lib/code/syntax-highlight.test.ts +1 -79
  192. package/src/shared/lib/code/syntax-highlight.ts +2 -3
  193. package/src/shared/lib/fs/file-kind-classify.test.ts +25 -11
  194. package/src/shared/lib/fs/file-kind.ts +47 -13
  195. package/src/shared/lib/fs/file-url.test.ts +52 -0
  196. package/src/shared/lib/fs/file-url.ts +37 -0
  197. package/src/shared/lib/fs/patch-sides.test.ts +114 -0
  198. package/src/shared/lib/fs/patch-sides.ts +139 -0
  199. package/src/shared/lib/realtime/protocol.ts +16 -0
  200. package/src/shared/lib/ui/toolbar-overflow.test.ts +83 -0
  201. package/src/shared/lib/ui/toolbar-overflow.ts +86 -0
  202. package/src/shared/lib/updates/shell-arch.ts +54 -0
  203. package/src/shared/lib/updates/shell-ua.ts +4 -4
  204. package/src/shared/lib/workspace/editor-view-mode.test.ts +70 -0
  205. package/src/shared/lib/workspace/editor-view-mode.ts +48 -0
  206. package/src/shared/lib/workspace/panel-widths.test.ts +30 -0
  207. package/src/shared/lib/workspace/panel-widths.ts +25 -5
  208. package/src/shared/lib/workspace/session-state/cache.ts +159 -0
  209. package/src/shared/lib/workspace/session-state/harness.test-util.ts +55 -0
  210. package/src/shared/lib/workspace/session-state/merge.ts +100 -0
  211. package/src/shared/lib/workspace/session-state/store-load.test.ts +206 -0
  212. package/src/shared/lib/workspace/session-state/store.test.ts +17 -139
  213. package/src/shared/lib/workspace/session-state/store.ts +92 -130
  214. package/src/shared/lib/workspace/workspace-panel-widths.ts +124 -0
  215. package/src/shared/types/realtime.ts +29 -0
  216. package/src/shared/types/settings/state.ts +10 -0
  217. package/src/test-support/loopback.ts +183 -0
  218. package/dist/client/static/js/index-gtbpewr7.css +0 -1
  219. package/src/client/components/common/code-editor/handler.test.ts +0 -274
  220. package/src/client/components/common/code-editor/handler.ts +0 -175
  221. package/src/client/components/common/code-editor/index.tsx +0 -314
  222. package/src/client/components/common/code-surface/measure.ts +0 -148
  223. package/src/client/components/workspace/chat-timeline/target-picker/index.test.tsx +0 -227
  224. package/src/client/components/workspace/chat-timeline/target-picker/index.tsx +0 -260
  225. package/src/client/components/workspace/diff-panel/SplitView.tsx +0 -209
  226. package/src/client/components/workspace/diff-panel/UnifiedView.tsx +0 -150
  227. package/src/client/hooks/editor/scroll-container.test.ts +0 -102
  228. package/src/client/hooks/editor/scroll-container.ts +0 -26
  229. package/src/client/hooks/editor/use-code-window.ts +0 -100
  230. package/src/client/hooks/editor/use-editor-history.test.ts +0 -216
  231. package/src/client/hooks/editor/use-editor-history.ts +0 -134
  232. package/src/client/hooks/editor/use-occurrences.test.ts +0 -236
  233. package/src/client/hooks/editor/use-occurrences.ts +0 -98
  234. package/src/shared/lib/code/editor/commands.test.ts +0 -155
  235. package/src/shared/lib/code/editor/commands.ts +0 -260
  236. package/src/shared/lib/code/editor/edits.test.ts +0 -100
  237. package/src/shared/lib/code/editor/edits.ts +0 -122
  238. package/src/shared/lib/code/editor/history.test.ts +0 -118
  239. package/src/shared/lib/code/editor/history.ts +0 -101
  240. package/src/shared/lib/code/editor/layers.test.ts +0 -77
  241. package/src/shared/lib/code/editor/layers.ts +0 -67
  242. package/src/shared/lib/code/editor/line-edits.test.ts +0 -131
  243. package/src/shared/lib/code/editor/line-edits.ts +0 -159
  244. package/src/shared/lib/code/editor/occurrences.test.ts +0 -117
  245. package/src/shared/lib/code/editor/occurrences.ts +0 -111
  246. package/src/shared/lib/code/editor/reset-css.test.ts +0 -35
  247. package/src/shared/lib/code/editor/reset-css.ts +0 -32
  248. package/src/shared/lib/code/editor/text-commands.test.ts +0 -178
  249. package/src/shared/lib/code/editor/text-commands.ts +0 -154
  250. package/src/shared/lib/code/lazy-window.test.ts +0 -161
  251. package/src/shared/lib/code/lazy-window.ts +0 -156
  252. package/src/shared/lib/code/windowed-highlight.ts +0 -114
  253. package/src/shared/lib/fs/diff-parser.test.ts +0 -116
  254. package/src/shared/lib/fs/diff-parser.ts +0 -213
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * GENERATED by `scripts/editor-themes.mjs` — do not edit by hand.
8
+ *
9
+ * One row per theme shipped by `@shikijs/themes`, in id order. Each row carries
10
+ * the colours the editor CHROME needs (surface, gutter, selection, cursor, find,
11
+ * border, the diff bands) plus three syntax accents for the picker's swatch, so
12
+ * the settings grid can draw 65 cards without fetching a theme module.
13
+ *
14
+ * The field order is the tuple's type (`EditorThemeRow` in `./themes.ts`).
15
+ * `themes.test.ts` re-derives every value from the installed package and fails on
16
+ * drift, so a `@shikijs/themes` bump surfaces as a test failure rather than a
17
+ * picker showing colours the package no longer ships.
18
+ */
19
+
20
+ /** `[id, label, variant, bg, fg, gutterFg, selection, cursor, find, findCurrent, border, added, removed, addedGutter, removedGutter, keyword, string, comment]` */
21
+ export const EDITOR_THEME_ROWS = [
22
+ ['andromeeda', 'Andromeeda', 'dark', '#23262E', '#D5CED9', '#746f77', '#3D4352', '#FFF', '#f39d1256', '#59b8b377', '#1B1D23', '#29BF1220', '#F21B3F20', '#9BC53DBB', '#FC644DBB', '#c74ded', '#96E072', '#A0A1A7cc'],
23
+ ['aurora-x', 'Aurora X', 'dark', '#07090F', '#576daf', '#262e47bb', '#262E47', '#86A5FF', '#576daf', '#262E47', '#15182B', '#64d3892c', '#dd50742c', '#2ea043', '#f85149', '#C792EA', '#C3E88D', '#546E7A'],
24
+ ['ayu-dark', 'Ayu Dark', 'dark', '#10141c', '#bfbdb6', '#5a6378a6', '#3388ff40', '#e6b450', '#4c4126', '#4c412680', '#1b1f29', '#70bf561f', '#f26d781f', '#70bf56', '#f26d78', '#ff8f40', '#aad94c', '#5a6673'],
25
+ ['ayu-light', 'Ayu Light', 'light', '#fcfcfc', '#5c6166', '#828e9f66', '#035bd626', '#f29718', '#ffe294', '#ffe29480', '#6b7d8f1f', '#6cbf431f', '#ff73831f', '#6cbf43', '#ff7383', '#fa8532', '#86b300', '#adaeb1'],
26
+ ['ayu-mirage', 'Ayu Mirage', 'dark', '#242936', '#cccac2', '#707a8c80', '#409fff40', '#ffcc66', '#736950', '#73695066', '#171b24', '#87d96c1f', '#f279831f', '#87d96c', '#f27983', '#ffa659', '#d5ff80', '#6e7c8f'],
27
+ ['catppuccin-frappe', 'Catppuccin Frappé', 'dark', '#303446', '#c6d0f5', '#838ba7', '#949cbb40', '#f2d5cf', '#674b59', '#506373', '#626880', '#a6d18926', '#e7828426', '#a6d189', '#e78284', '#ca9ee6', '#a6d189', '#949cbb'],
28
+ ['catppuccin-latte', 'Catppuccin Latte', 'light', '#eff1f5', '#4c4f69', '#8c8fa1', '#7c7f934d', '#dc8a78', '#e6adbd', '#a9daf0', '#acb0be', '#40a02b26', '#d20f3926', '#40a02b', '#d20f39', '#8839ef', '#40a02b', '#7c7f93'],
29
+ ['catppuccin-macchiato', 'Catppuccin Macchiato', 'dark', '#24273a', '#cad3f5', '#8087a2', '#939ab740', '#f4dbd6', '#604456', '#455c6d', '#5b6078', '#a6da9526', '#ed879626', '#a6da95', '#ed8796', '#c6a0f6', '#a6da95', '#939ab7'],
30
+ ['catppuccin-mocha', 'Catppuccin Mocha', 'dark', '#1e1e2e', '#cdd6f4', '#7f849c', '#9399b240', '#f5e0dc', '#5e3f53', '#3e5767', '#585b70', '#a6e3a126', '#f38ba826', '#a6e3a1', '#f38ba8', '#cba6f7', '#a6e3a1', '#9399b2'],
31
+ ['dark-plus', 'Dark Plus', 'dark', '#1E1E1E', '#D4D4D4', 'color-mix(in srgb, #D4D4D4 38%, #1E1E1E)', 'color-mix(in srgb, #D4D4D4 22%, #1E1E1E)', '#D4D4D4', 'color-mix(in srgb, #D4D4D4 18%, #1E1E1E)', 'color-mix(in srgb, #D4D4D4 30%, #1E1E1E)', 'color-mix(in srgb, #D4D4D4 16%, #1E1E1E)', 'color-mix(in srgb, #2ea043 22%, #1E1E1E)', 'color-mix(in srgb, #f85149 22%, #1E1E1E)', '#2ea043', '#f85149', '#569cd6', '#ce9178', '#6A9955'],
32
+ ['dracula', 'Dracula Theme', 'dark', '#282A36', '#F8F8F2', '#6272A4', '#44475A', '#F8F8F2', '#FFB86C80', '#FFFFFF40', '#BD93F9', '#50FA7B20', '#FF555550', '#50FA7B80', '#FF555580', '#FF79C6', '#F1FA8C', '#6272A4'],
33
+ ['dracula-soft', 'Dracula Theme Soft', 'dark', '#282A36', '#f6f6f4', '#7b7f8b', '#44475A', '#f6f6f4', '#FFB86C80', '#FFFFFF40', '#bf9eee', '#50FA7B20', '#FF555550', '#50FA7B80', '#FF555580', '#f286c4', '#e7ee98', '#7b7f8b'],
34
+ ['everforest-dark', 'Everforest Dark', 'dark', '#2d353b', '#d3c6aa', '#7f897da0', '#475258c0', '#d3c6aa', '#d77f4840', '#899c4040', '#2d353b', '#569d7930', '#da636230', '#899c40a0', '#da6362a0', '#e67e80', '#dbbc7f', '#859289'],
35
+ ['everforest-light', 'Everforest Light', 'light', '#fdf6e3', '#5c6a72', '#a4ad9ea0', '#e6e2cca0', '#5c6a72', '#f3945940', '#a4bb4a40', '#fdf6e3', '#6ec39830', '#f1706f30', '#a4bb4aa0', '#f1706fa0', '#f85552', '#dfa000', '#939f91'],
36
+ ['github-dark', 'GitHub Dark', 'dark', '#24292e', '#e1e4e8', '#444d56', '#3392FF44', '#c8e1ff', '#ffd33d44', '#ffd33d22', '#1b1f23', '#28a74530', '#d73a4930', '#28a745', '#ea4a5a', '#f97583', '#9ecbff', '#6a737d'],
37
+ ['github-dark-default', 'GitHub Dark Default', 'dark', '#0d1117', '#e6edf3', '#6e7681', 'color-mix(in srgb, #e6edf3 22%, #0d1117)', '#2f81f7', '#9e6a03', '#f2cc6080', '#30363d', '#23863626', '#da363326', '#2ea04366', '#f8514966', '#ff7b72', '#a5d6ff', '#8b949e'],
38
+ ['github-dark-dimmed', 'GitHub Dark Dimmed', 'dark', '#22272e', '#adbac7', '#636e7b', 'color-mix(in srgb, #adbac7 22%, #22272e)', '#539bf5', '#966600', '#eac55f80', '#444c56', '#347d3926', '#c93c3726', '#46954a66', '#e5534b66', '#f47067', '#96d0ff', '#768390'],
39
+ ['github-dark-high-contrast', 'GitHub Dark High Contrast', 'dark', '#0a0c10', '#f0f3f6', '#9ea7b3', '#ffffff', '#71b7ff', '#e09b13', '#fbd66980', '#7a828e', '#09b43a26', '#ff6a6926', '#09b43a', '#ff6a69', '#ff9492', '#addcff', '#bdc4cc'],
40
+ ['github-light', 'GitHub Light', 'light', '#fff', '#24292e', '#1b1f234d', '#0366d625', '#044289', '#ffdf5d', '#ffdf5d66', '#e1e4e8', '#34d05822', '#d73a4922', '#28a745', '#d73a49', '#d73a49', '#032f62', '#6a737d'],
41
+ ['github-light-default', 'GitHub Light Default', 'light', '#ffffff', '#1f2328', '#8c959f', 'color-mix(in srgb, #1f2328 22%, #ffffff)', '#0969da', '#bf8700', '#fae17d80', '#d0d7de', '#aceebb4d', '#ffcecb4d', '#4ac26b66', '#ff818266', '#cf222e', '#0a3069', '#6e7781'],
42
+ ['github-light-high-contrast', 'GitHub Light High Contrast', 'light', '#ffffff', '#0e1116', '#88929d', '#0e1116', '#0349b4', '#744500', '#f0ce5380', '#20252c', '#82e5964d', '#ffc1bc4d', '#26a148', '#ee5a5d', '#a0111f', '#032563', '#66707b'],
43
+ ['gruvbox-dark-hard', 'Gruvbox Dark Hard', 'dark', '#1d2021', '#ebdbb2', '#665c54', '#689d6a40', '#ebdbb2', '#83a59870', '#fe801930', '#3c3836', '#b8bb2630', '#fb493430', '#b8bb26', '#fb4934', '#fb4934', '#b8bb26', '#928374'],
44
+ ['gruvbox-dark-medium', 'Gruvbox Dark Medium', 'dark', '#282828', '#ebdbb2', '#665c54', '#689d6a40', '#ebdbb2', '#83a59870', '#fe801930', '#3c3836', '#b8bb2630', '#fb493430', '#b8bb26', '#fb4934', '#fb4934', '#b8bb26', '#928374'],
45
+ ['gruvbox-dark-soft', 'Gruvbox Dark Soft', 'dark', '#32302f', '#ebdbb2', '#665c54', '#689d6a40', '#ebdbb2', '#83a59870', '#fe801930', '#3c3836', '#b8bb2630', '#fb493430', '#b8bb26', '#fb4934', '#fb4934', '#b8bb26', '#928374'],
46
+ ['gruvbox-light-hard', 'Gruvbox Light Hard', 'light', '#f9f5d7', '#3c3836', '#bdae93', '#689d6a40', '#3c3836', '#07667870', '#af3a0330', '#ebdbb2', '#79740e30', '#9d000630', '#79740e', '#9d0006', '#9d0006', '#79740e', '#928374'],
47
+ ['gruvbox-light-medium', 'Gruvbox Light Medium', 'light', '#fbf1c7', '#3c3836', '#bdae93', '#689d6a40', '#3c3836', '#07667870', '#af3a0330', '#ebdbb2', '#79740e30', '#9d000630', '#79740e', '#9d0006', '#9d0006', '#79740e', '#928374'],
48
+ ['gruvbox-light-soft', 'Gruvbox Light Soft', 'light', '#f2e5bc', '#3c3836', '#bdae93', '#689d6a40', '#3c3836', '#07667870', '#af3a0330', '#ebdbb2', '#79740e30', '#9d000630', '#79740e', '#9d0006', '#9d0006', '#79740e', '#928374'],
49
+ ['horizon', 'Horizon', 'dark', '#1C1E26', '#D5D8DA', '#D5D8DA1A', '#2E303EB3', '#E95378', '#6C6F9380', '#6C6F934D', 'color-mix(in srgb, #D5D8DA 16%, #1C1E26)', '#09F7A01A', '#F43E5C1A', '#09F7A0B3', '#F43E5CB3', '#B877DBE6', '#FAB795E6', '#BBBBBB4D'],
50
+ ['horizon-bright', 'Horizon Bright', 'light', '#FDF0ED', '#06060C', '#06060C1A', '#F9CBBE80', '#E84A72', '#F9CEC380', '#F9CEC34D', 'color-mix(in srgb, #06060C 16%, #FDF0ED)', '#07DA8C1A', '#F43E5C1A', '#07DA8CB3', '#F43E5CB3', '#8A31B9', '#F6661E', '#33333380'],
51
+ ['houston', 'Houston', 'dark', '#17191e', '#eef0f9', '#545864', '#ad5dca44', '#aeafad', '#515c6a', '#ea5c0055', '#17191e', '#4bf3c824', '#dc365724', '#4bf3c8', '#f06788', '#54b9ff', '#ffd493', '#eef0f98f'],
52
+ ['kanagawa-dragon', 'Kanagawa Dragon', 'dark', '#181616', '#C5C9C5', '#625E5A', '#223249', '#C5C9C5', '#2D4F67', '#2D4F6780', '#0D0C0C', '#2B332880', 'color-mix(in srgb, #C34043 22%, #181616)', '#76946A', '#C34043', '#8992A7', '#8A9A7B', '#737C73'],
53
+ ['kanagawa-lotus', 'Kanagawa Lotus', 'light', '#F2ECBC', '#545464', '#766B90', '#C7D7E0', '#545464', '#B5CBD2', '#B5CBD280', '#D5CEA3', '#B7D0AE80', 'color-mix(in srgb, #D7474B 22%, #F2ECBC)', '#6E915F', '#D7474B', '#624C83', '#6F894E', '#716E61'],
54
+ ['kanagawa-wave', 'Kanagawa Wave', 'dark', '#1F1F28', '#DCD7BA', '#54546D', '#223249', '#DCD7BA', '#2D4F67', '#2D4F6780', '#16161D', '#2B332880', 'color-mix(in srgb, #C34043 22%, #1F1F28)', '#76946A', '#C34043', '#957FB8', '#98BB6C', '#727169'],
55
+ ['laserwave', 'LaserWave', 'dark', '#27212e', '#ffffff', 'color-mix(in srgb, #ffffff 38%, #27212e)', '#eb64b927', '#ffffff', '#40b4c48c', '#40b4c460', 'color-mix(in srgb, #ffffff 16%, #27212e)', '#74dfc423', '#eb64b940', '#74dfc4', '#eb64B9', '#a96bc0', '#b4dce7', '#91889b'],
56
+ ['light-plus', 'Light Plus', 'light', '#FFFFFF', '#000000', 'color-mix(in srgb, #000000 38%, #FFFFFF)', 'color-mix(in srgb, #000000 22%, #FFFFFF)', '#000000', 'color-mix(in srgb, #000000 18%, #FFFFFF)', 'color-mix(in srgb, #000000 30%, #FFFFFF)', 'color-mix(in srgb, #000000 16%, #FFFFFF)', 'color-mix(in srgb, #2ea043 22%, #FFFFFF)', 'color-mix(in srgb, #f85149 22%, #FFFFFF)', '#2ea043', '#f85149', '#0000ff', '#a31515', '#008000'],
57
+ ['material-theme', 'Material Theme', 'dark', '#263238', '#EEFFFF', '#465A64', '#80CBC420', '#FFCC00', '#000000', '#00000050', '#26323860', '#89DDFF20', '#ff9cac20', '#C3E88D60', '#f0717860', '#89DDFF', '#C3E88D', '#546E7A'],
58
+ ['material-theme-darker', 'Material Theme Darker', 'dark', '#212121', '#EEFFFF', '#424242', '#61616150', '#FFCC00', '#000000', '#00000050', '#21212160', '#89DDFF20', '#ff9cac20', '#C3E88D60', '#f0717860', '#89DDFF', '#C3E88D', '#545454'],
59
+ ['material-theme-lighter', 'Material Theme Lighter', 'light', '#FAFAFA', '#90A4AE', '#CFD8DC', '#80CBC440', '#272727', '#00000020', '#00000010', '#FAFAFA60', '#39ADB520', '#FF537020', '#91B85960', '#E5393560', '#39ADB5', '#91B859', '#90A4AE'],
60
+ ['material-theme-ocean', 'Material Theme Ocean', 'dark', '#0F111A', '#babed8', '#3B3F5180', '#717CB450', '#FFCC00', '#000000', '#00000050', '#0F111A60', '#89DDFF20', '#ff9cac20', '#C3E88D60', '#f0717860', '#89DDFF', '#C3E88D', '#464B5D'],
61
+ ['material-theme-palenight', 'Material Theme Palenight', 'dark', '#292D3E', '#babed8', '#3A3F58', '#717CB450', '#FFCC00', '#000000', '#00000050', '#292D3E60', '#89DDFF20', '#ff9cac20', '#C3E88D60', '#f0717860', '#89DDFF', '#C3E88D', '#676E95'],
62
+ ['min-dark', 'Min Dark', 'dark', '#1f1f1f', '#888888', '#727272', 'color-mix(in srgb, #888888 22%, #1f1f1f)', '#888888', 'color-mix(in srgb, #888888 18%, #1f1f1f)', 'color-mix(in srgb, #888888 30%, #1f1f1f)', 'color-mix(in srgb, #888888 16%, #1f1f1f)', '#3a632a4b', '#88063852', '#2ea043', '#f85149', '#f97583', '#9db1c5', '#6b737c'],
63
+ ['min-light', 'Min Light', 'light', '#ffffff', '#212121', '#CCC', 'color-mix(in srgb, #212121 22%, #ffffff)', '#212121', 'color-mix(in srgb, #212121 18%, #ffffff)', 'color-mix(in srgb, #212121 30%, #ffffff)', '#f4f4f4', '#b7e7a44b', '#e597af52', '#2ea043', '#f85149', '#D32F2F', '#2b5581', '#c2c3c5'],
64
+ ['monokai', 'Monokai', 'dark', '#272822', '#f8f8f2', '#90908a', '#878b9180', '#f8f8f0', 'color-mix(in srgb, #f8f8f2 18%, #272822)', 'color-mix(in srgb, #f8f8f2 30%, #272822)', '#414339', '#4b661680', '#90274A70', '#2ea043', '#f85149', '#F92672', '#E6DB74', '#88846f'],
65
+ ['night-owl', 'Night Owl', 'dark', '#011627', '#d6deeb', '#4b6479', '#1d3b53', '#80a4c2', '#5f7e9779', '#1085bb5d', '#5f7e97', '#99b76d23', '#ef535033', '#9CCC65', '#EF5350', '#c792ea', '#ecc48d', '#637777'],
66
+ ['night-owl-light', 'Night Owl Light', 'light', '#FBFBFB', '#403f53', '#90A7B2', '#E0E0E0', '#90A7B2', '#93A1A16c', '#93a1a16c', '#d9d9d9', 'color-mix(in srgb, #49d0c5 22%, #FBFBFB)', 'color-mix(in srgb, #f76e6e 22%, #FBFBFB)', '#49d0c5', '#f76e6e', '#994cc3', '#4876d6', '#989fb1'],
67
+ ['nord', 'Nord', 'dark', '#2e3440', '#d8dee9', '#4c566a', '#434c5ecc', '#d8dee9', '#88c0d066', '#88c0d033', '#3b4252', '#81a1c133', '#bf616a4d', '#a3be8c', '#bf616a', '#81A1C1', '#A3BE8C', '#616E88'],
68
+ ['one-dark-pro', 'One Dark Pro', 'dark', '#282c34', '#abb2bf', '#495162', '#67769660', '#528bff', '#d19a6644', '#ffffff22', '#3e4452', '#00809b33', 'color-mix(in srgb, #9A353D 22%, #282c34)', '#109868', '#9A353D', '#c678dd', '#98c379', '#7f848e'],
69
+ ['one-light', 'One Light', 'light', '#FAFAFA', '#383A42', '#9D9D9F', '#E5E5E6', '#526FFF', 'color-mix(in srgb, #383A42 18%, #FAFAFA)', '#526FFF33', 'color-mix(in srgb, #383A42 16%, #FAFAFA)', '#00809B33', 'color-mix(in srgb, #f85149 22%, #FAFAFA)', '#2ea043', '#f85149', '#A626A4', '#50A14F', '#A0A1A7'],
70
+ ['plastic', 'Plastic', 'dark', '#21252B', '#A9B2C3', '#5F6672', '#A9B2C333', '#A9B2C3', '#00000000', '#00000000', '#0D1117', '#CBF6AC0D', '#FF9FA80D', '#98C379', '#E06C75', '#E06C75', '#98C379', '#5F6672'],
71
+ ['poimandres', 'Poimandres', 'dark', '#1b1e28', '#a6accd', '#767c9d50', '#717cb425', '#a6accd', '#ADD7FF40', '#ADD7FF40', '#00000030', '#50647715', '#d0679d20', '#5fb3a140', '#d0679d40', '#a6accd', '#5DE4c7', '#767c9dB0'],
72
+ ['red', 'Red', 'dark', '#390000', '#F8F8F8', '#ff777788', '#750000', '#970000', 'color-mix(in srgb, #F8F8F8 18%, #390000)', 'color-mix(in srgb, #F8F8F8 30%, #390000)', 'color-mix(in srgb, #F8F8F8 16%, #390000)', 'color-mix(in srgb, #2ea043 22%, #390000)', 'color-mix(in srgb, #f85149 22%, #390000)', '#2ea043', '#f85149', '#f12727ff', '#cd8d8dff', '#e7c0c0ff'],
73
+ ['rose-pine', 'Rosé Pine', 'dark', '#191724', '#e0def4', '#908caa', '#6e6a8633', '#6e6a86', '#f6c17733', '#6e6a8666', '#0000', '#9ccfd826', '#eb6f9226', '#9ccfd8', '#eb6f92', '#31748f', '#f6c177', '#6e6a86'],
74
+ ['rose-pine-dawn', 'Rosé Pine Dawn', 'light', '#faf4ed', '#575279', '#797593', '#6e6a8614', '#9893a5', '#ea9d3433', '#6e6a8626', '#0000', '#56949f26', '#b4637a26', '#56949f', '#b4637a', '#286983', '#ea9d34', '#9893a5'],
75
+ ['rose-pine-moon', 'Rosé Pine Moon', 'dark', '#232136', '#e0def4', '#908caa', '#817c9c26', '#6e6a86', '#f6c17733', '#817c9c4d', '#0000', '#9ccfd826', '#eb6f9226', '#9ccfd8', '#eb6f92', '#3e8fb0', '#f6c177', '#6e6a86'],
76
+ ['slack-dark', 'Slack Dark', 'dark', '#222222', '#E6E6E6', 'color-mix(in srgb, #E6E6E6 38%, #222222)', 'color-mix(in srgb, #E6E6E6 22%, #222222)', '#E6E6E6', 'color-mix(in srgb, #E6E6E6 18%, #222222)', 'color-mix(in srgb, #E6E6E6 30%, #222222)', 'color-mix(in srgb, #E6E6E6 16%, #222222)', 'color-mix(in srgb, #2ea043 22%, #222222)', 'color-mix(in srgb, #f85149 22%, #222222)', '#2ea043', '#f85149', '#569cd6', '#ce9178', '#6A9955'],
77
+ ['slack-ochin', 'Slack Ochin', 'light', '#FFF', '#000', '#b9b9b9', '#AED4FB', '#000', '#AED4FB', '#AED4FB', '#2D3E4C', 'color-mix(in srgb, #2ea043 22%, #FFF)', 'color-mix(in srgb, #f85149 22%, #FFF)', '#2ea043', '#f85149', '#7b30d0', '#a44185', '#357b42'],
78
+ ['snazzy-light', 'Snazzy Light', 'light', '#FAFBFC', '#565869', '#9194A2aa', '#2DAE5822', '#565869', '#00E6E06A', '#00E6E02A', 'color-mix(in srgb, #565869 16%, #FAFBFC)', '#2DAE5824', '#FFAEAC44', '#2DAE58', '#FF5C57', '#F767BB', '#CF9C00', '#ADB1C2'],
79
+ ['solarized-dark', 'Solarized Dark', 'dark', '#002B36', '#839496', 'color-mix(in srgb, #839496 38%, #002B36)', '#274642', '#D30102', 'color-mix(in srgb, #839496 18%, #002B36)', 'color-mix(in srgb, #839496 30%, #002B36)', '#2b2b4a', 'color-mix(in srgb, #2ea043 22%, #002B36)', 'color-mix(in srgb, #f85149 22%, #002B36)', '#2ea043', '#f85149', '#859900', '#2AA198', '#586E75'],
80
+ ['solarized-light', 'Solarized Light', 'light', '#FDF6E3', '#657B83', 'color-mix(in srgb, #657B83 38%, #FDF6E3)', '#EEE8D5', '#657B83', 'color-mix(in srgb, #657B83 18%, #FDF6E3)', 'color-mix(in srgb, #657B83 30%, #FDF6E3)', '#DDD6C1', 'color-mix(in srgb, #2ea043 22%, #FDF6E3)', 'color-mix(in srgb, #f85149 22%, #FDF6E3)', '#2ea043', '#f85149', '#859900', '#2AA198', '#93A1A1'],
81
+ ['synthwave-84', 'Synthwave \'84', 'dark', '#262335', '#ffffff', '#ffffff73', '#ffffff20', '#f97e72', '#D18616bb', '#D1861655', 'color-mix(in srgb, #ffffff 16%, #262335)', '#0beb9935', '#fe445035', '#206d4bd6', '#fa2e46a4', '#fede5d', '#ff8b39', '#848bbd'],
82
+ ['tokyo-night', 'Tokyo Night', 'dark', '#1a1b26', '#a9b1d6', '#363b54', '#515c7e4d', '#c0caf5', '#3d59a166', '#3d59a166', '#101014', '#41a6b520', '#db4b4b22', '#164846', '#823c41', '#bb9af7', '#9ece6a', '#51597d'],
83
+ ['vesper', 'Vesper', 'dark', '#101010', '#FFF', '#505050', '#FFFFFF25', '#FFF', 'color-mix(in srgb, #FFF 18%, #101010)', 'color-mix(in srgb, #FFF 30%, #101010)', 'color-mix(in srgb, #FFF 16%, #101010)', '#99FFE415', '#FF808015', '#99FFE4', '#FF8080', '#A0A0A0', '#99FFE4', '#8b8b8b94'],
84
+ ['vitesse-black', 'Vitesse Black', 'dark', '#000', '#dbd7cacc', '#dedcd550', '#eeeeee18', '#dbd7cacc', '#e6cc7722', '#e6cc7744', '#191919', '#4d937550', '#ab595950', '#4d9375', '#cb7676', '#4d9375', '#c98a7d', '#758575dd'],
85
+ ['vitesse-dark', 'Vitesse Dark', 'dark', '#121212', '#dbd7caee', '#dedcd550', '#eeeeee18', '#dbd7caee', '#e6cc7722', '#e6cc7744', '#191919', '#4d937550', '#ab595950', '#4d9375', '#cb7676', '#4d9375', '#c98a7d', '#758575dd'],
86
+ ['vitesse-light', 'Vitesse Light', 'light', '#ffffff', '#393a34', '#393a3450', '#22222218', '#393a34', '#e6cc7744', '#e6cc7766', '#f0f0f0', '#1c6b4830', '#ab595940', '#1e754f', '#ab5959', '#1e754f', '#b56959', '#a0ada0'],
87
+ ] as const;
88
+
89
+ /** Lazy module loaders, so a theme nobody selects is never downloaded. */
90
+ export const EDITOR_THEME_LOADERS: Record<string, () => Promise<unknown>> = {
91
+ 'andromeeda': () => import('@shikijs/themes/andromeeda'),
92
+ 'aurora-x': () => import('@shikijs/themes/aurora-x'),
93
+ 'ayu-dark': () => import('@shikijs/themes/ayu-dark'),
94
+ 'ayu-light': () => import('@shikijs/themes/ayu-light'),
95
+ 'ayu-mirage': () => import('@shikijs/themes/ayu-mirage'),
96
+ 'catppuccin-frappe': () => import('@shikijs/themes/catppuccin-frappe'),
97
+ 'catppuccin-latte': () => import('@shikijs/themes/catppuccin-latte'),
98
+ 'catppuccin-macchiato': () => import('@shikijs/themes/catppuccin-macchiato'),
99
+ 'catppuccin-mocha': () => import('@shikijs/themes/catppuccin-mocha'),
100
+ 'dark-plus': () => import('@shikijs/themes/dark-plus'),
101
+ 'dracula': () => import('@shikijs/themes/dracula'),
102
+ 'dracula-soft': () => import('@shikijs/themes/dracula-soft'),
103
+ 'everforest-dark': () => import('@shikijs/themes/everforest-dark'),
104
+ 'everforest-light': () => import('@shikijs/themes/everforest-light'),
105
+ 'github-dark': () => import('@shikijs/themes/github-dark'),
106
+ 'github-dark-default': () => import('@shikijs/themes/github-dark-default'),
107
+ 'github-dark-dimmed': () => import('@shikijs/themes/github-dark-dimmed'),
108
+ 'github-dark-high-contrast': () => import('@shikijs/themes/github-dark-high-contrast'),
109
+ 'github-light': () => import('@shikijs/themes/github-light'),
110
+ 'github-light-default': () => import('@shikijs/themes/github-light-default'),
111
+ 'github-light-high-contrast': () => import('@shikijs/themes/github-light-high-contrast'),
112
+ 'gruvbox-dark-hard': () => import('@shikijs/themes/gruvbox-dark-hard'),
113
+ 'gruvbox-dark-medium': () => import('@shikijs/themes/gruvbox-dark-medium'),
114
+ 'gruvbox-dark-soft': () => import('@shikijs/themes/gruvbox-dark-soft'),
115
+ 'gruvbox-light-hard': () => import('@shikijs/themes/gruvbox-light-hard'),
116
+ 'gruvbox-light-medium': () => import('@shikijs/themes/gruvbox-light-medium'),
117
+ 'gruvbox-light-soft': () => import('@shikijs/themes/gruvbox-light-soft'),
118
+ 'horizon': () => import('@shikijs/themes/horizon'),
119
+ 'horizon-bright': () => import('@shikijs/themes/horizon-bright'),
120
+ 'houston': () => import('@shikijs/themes/houston'),
121
+ 'kanagawa-dragon': () => import('@shikijs/themes/kanagawa-dragon'),
122
+ 'kanagawa-lotus': () => import('@shikijs/themes/kanagawa-lotus'),
123
+ 'kanagawa-wave': () => import('@shikijs/themes/kanagawa-wave'),
124
+ 'laserwave': () => import('@shikijs/themes/laserwave'),
125
+ 'light-plus': () => import('@shikijs/themes/light-plus'),
126
+ 'material-theme': () => import('@shikijs/themes/material-theme'),
127
+ 'material-theme-darker': () => import('@shikijs/themes/material-theme-darker'),
128
+ 'material-theme-lighter': () => import('@shikijs/themes/material-theme-lighter'),
129
+ 'material-theme-ocean': () => import('@shikijs/themes/material-theme-ocean'),
130
+ 'material-theme-palenight': () => import('@shikijs/themes/material-theme-palenight'),
131
+ 'min-dark': () => import('@shikijs/themes/min-dark'),
132
+ 'min-light': () => import('@shikijs/themes/min-light'),
133
+ 'monokai': () => import('@shikijs/themes/monokai'),
134
+ 'night-owl': () => import('@shikijs/themes/night-owl'),
135
+ 'night-owl-light': () => import('@shikijs/themes/night-owl-light'),
136
+ 'nord': () => import('@shikijs/themes/nord'),
137
+ 'one-dark-pro': () => import('@shikijs/themes/one-dark-pro'),
138
+ 'one-light': () => import('@shikijs/themes/one-light'),
139
+ 'plastic': () => import('@shikijs/themes/plastic'),
140
+ 'poimandres': () => import('@shikijs/themes/poimandres'),
141
+ 'red': () => import('@shikijs/themes/red'),
142
+ 'rose-pine': () => import('@shikijs/themes/rose-pine'),
143
+ 'rose-pine-dawn': () => import('@shikijs/themes/rose-pine-dawn'),
144
+ 'rose-pine-moon': () => import('@shikijs/themes/rose-pine-moon'),
145
+ 'slack-dark': () => import('@shikijs/themes/slack-dark'),
146
+ 'slack-ochin': () => import('@shikijs/themes/slack-ochin'),
147
+ 'snazzy-light': () => import('@shikijs/themes/snazzy-light'),
148
+ 'solarized-dark': () => import('@shikijs/themes/solarized-dark'),
149
+ 'solarized-light': () => import('@shikijs/themes/solarized-light'),
150
+ 'synthwave-84': () => import('@shikijs/themes/synthwave-84'),
151
+ 'tokyo-night': () => import('@shikijs/themes/tokyo-night'),
152
+ 'vesper': () => import('@shikijs/themes/vesper'),
153
+ 'vitesse-black': () => import('@shikijs/themes/vitesse-black'),
154
+ 'vitesse-dark': () => import('@shikijs/themes/vitesse-dark'),
155
+ 'vitesse-light': () => import('@shikijs/themes/vitesse-light'),
156
+ };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * The editor theme catalogue, and the one property that makes it trustworthy:
8
+ * every colour it ships is the INSTALLED package's own.
9
+ *
10
+ * The grid draws 65 cards without fetching a theme module, so its colours are
11
+ * data. Data generated from a dependency drifts the moment the dependency
12
+ * updates — silently, because the picker still renders, just with a palette the
13
+ * highlighter no longer has. These cases re-derive each row from
14
+ * `@shikijs/themes` itself, which is the only check that can fail on a version
15
+ * bump rather than on a typo.
16
+ *
17
+ * The remaining cases pin the two rules the surfaces depend on: `auto` resolves
18
+ * to no theme (so nothing changes for an install that never opens the picker),
19
+ * and an explicit theme names ITSELF in both Shiki slots — which is what makes
20
+ * the editor ignore the app's light/dark variant without a second CSS path.
21
+ */
22
+
23
+ import { describe, expect, test } from 'bun:test';
24
+ import { readdirSync } from 'node:fs';
25
+ import { dirname, join } from 'node:path';
26
+
27
+ import {
28
+ EDITOR_AUTO_THEME_ID,
29
+ EDITOR_DEFAULT_THEME_ID,
30
+ EDITOR_THEMES,
31
+ EDITOR_THEME_LOADERS,
32
+ editorShikiThemes,
33
+ editorThemeVars,
34
+ isEditorThemeId,
35
+ resolveEditorTheme,
36
+ } from '@/shared/lib/code/editor/themes';
37
+ import { SHIKI_THEMES } from '@/shared/lib/code/shiki-themes';
38
+
39
+ /** The installed package's `dist`, resolved through the package itself so a
40
+ * hoisted or linked install resolves the same way the app's own imports do. */
41
+ const THEMES_DIR = join(dirname(Bun.resolveSync('@shikijs/themes/package.json', import.meta.dir)), 'dist');
42
+
43
+ /** The ids the installed package actually ships, in the generator's order. */
44
+ function installedIds(): string[] {
45
+ return readdirSync(THEMES_DIR)
46
+ .filter((file) => file.endsWith('.mjs') && file !== 'index.mjs')
47
+ .map((file) => file.slice(0, -'.mjs'.length))
48
+ .sort();
49
+ }
50
+
51
+ describe('catalogue', () => {
52
+ test('one row per theme the installed package ships, in id order', () => {
53
+ expect(EDITOR_THEMES.map((theme) => theme.id)).toEqual(installedIds());
54
+ });
55
+
56
+ test('every row has a loader, and every loader has a row', () => {
57
+ expect(Object.keys(EDITOR_THEME_LOADERS).sort()).toEqual(EDITOR_THEMES.map((theme) => theme.id));
58
+ });
59
+
60
+ test('a label is never empty and never the raw id with dashes', () => {
61
+ for (const theme of EDITOR_THEMES) {
62
+ expect(theme.label.length).toBeGreaterThan(0);
63
+ expect(theme.variant === 'light' || theme.variant === 'dark').toBe(true);
64
+ }
65
+ });
66
+ });
67
+
68
+ describe('generated colours', () => {
69
+ test('the surface and the ink are the theme’s own', async () => {
70
+ for (const theme of EDITOR_THEMES) {
71
+ // Dynamic on purpose: the specifier is the row's own id, read at runtime
72
+ // from a 65-entry catalogue — a static import cannot name it.
73
+ const module = (await import(`@shikijs/themes/${theme.id}`)).default as {
74
+ colors?: Record<string, string>;
75
+ };
76
+ const colors = module.colors ?? {};
77
+ expect(theme.bg).toBe(colors['editor.background'] as string);
78
+ // Five themes ship no `editor.foreground`; the generator falls back to the
79
+ // theme's own `foreground`, which is still ITS colour and not a borrow.
80
+ const own =
81
+ colors['editor.foreground'] ?? colors['foreground'] ?? colors['terminal.foreground'] ?? theme.fg;
82
+ expect(theme.fg).toBe(own);
83
+ }
84
+ });
85
+
86
+ test('a chrome colour the theme omits is a mix of its own ink and surface', () => {
87
+ for (const theme of EDITOR_THEMES) {
88
+ for (const value of [theme.gutterFg, theme.selection, theme.find, theme.border]) {
89
+ expect(value.length).toBeGreaterThan(0);
90
+ // Either the theme's own hex, or a `color-mix` over its own two colours
91
+ // — never a bare colour borrowed from another theme.
92
+ const isOwnHex = /^#[0-9a-f]{3,8}$/i.test(value);
93
+ const isOwnMix = value.startsWith('color-mix(') && value.includes(theme.bg);
94
+ expect(isOwnHex || isOwnMix).toBe(true);
95
+ }
96
+ }
97
+ });
98
+
99
+ test('the three accents differ enough to tell two themes apart', () => {
100
+ for (const theme of EDITOR_THEMES) {
101
+ expect(theme.keyword).toMatch(/^#[0-9a-f]{3,8}$/i);
102
+ expect(theme.string).toMatch(/^#[0-9a-f]{3,8}$/i);
103
+ expect(theme.comment).toMatch(/^#[0-9a-f]{3,8}$/i);
104
+ }
105
+ });
106
+ });
107
+
108
+ describe('resolution', () => {
109
+ test('auto is the default and resolves to no theme', () => {
110
+ expect(EDITOR_DEFAULT_THEME_ID).toBe(EDITOR_AUTO_THEME_ID);
111
+ expect(resolveEditorTheme(EDITOR_AUTO_THEME_ID)).toBeNull();
112
+ expect(resolveEditorTheme(undefined)).toBeNull();
113
+ expect(resolveEditorTheme(null)).toBeNull();
114
+ });
115
+
116
+ test('an id this build does not ship falls back to auto rather than half a theme', () => {
117
+ expect(resolveEditorTheme('no-such-theme')).toBeNull();
118
+ expect(isEditorThemeId('no-such-theme')).toBe(false);
119
+ });
120
+
121
+ test('a shipped id resolves to its own row', () => {
122
+ const dracula = resolveEditorTheme('dracula');
123
+ expect(dracula?.id).toBe('dracula');
124
+ expect(isEditorThemeId('dracula')).toBe(true);
125
+ });
126
+ });
127
+
128
+ describe('shiki themes', () => {
129
+ test('auto keeps the app’s dual pair, so an app theme flip repaints', () => {
130
+ expect(editorShikiThemes(null)).toEqual(SHIKI_THEMES);
131
+ expect(editorShikiThemes(EDITOR_AUTO_THEME_ID)).toEqual(SHIKI_THEMES);
132
+ });
133
+
134
+ test('an explicit theme names itself in BOTH slots', () => {
135
+ // Both slots carrying one theme is what makes the app's variant attribute
136
+ // harmless: `--shiki-light` and `--shiki-dark` hold the same value, so
137
+ // either selector paints the chosen theme.
138
+ expect(editorShikiThemes('dracula')).toEqual({ light: 'dracula', dark: 'dracula' });
139
+ });
140
+
141
+ test('an unknown id keeps the app pair', () => {
142
+ expect(editorShikiThemes('no-such-theme')).toEqual(SHIKI_THEMES);
143
+ });
144
+ });
145
+
146
+ describe('css variables', () => {
147
+ test('auto writes nothing, so every declaration keeps its palette fallback', () => {
148
+ expect(editorThemeVars(null)).toEqual({});
149
+ });
150
+
151
+ test('an explicit theme writes the surface, the gutter and the diff bands', () => {
152
+ const theme = resolveEditorTheme('nord');
153
+ expect(theme).not.toBeNull();
154
+ const vars = editorThemeVars(theme);
155
+ expect(vars['--editor-bg']).toBe(theme!.bg);
156
+ expect(vars['--editor-fg']).toBe(theme!.fg);
157
+ expect(vars['--editor-gutter-fg']).toBe(theme!.gutterFg);
158
+ expect(vars['--editor-selection']).toBe(theme!.selection);
159
+ expect(vars['--editor-find-current']).toBe(theme!.findCurrent);
160
+ expect(vars['--editor-added']).toBe(theme!.added);
161
+ expect(vars['--editor-removed-gutter']).toBe(theme!.removedGutter);
162
+ // Every key is a custom property, never a plain one.
163
+ for (const key of Object.keys(vars)) expect(key.startsWith('--editor-')).toBe(true);
164
+ });
165
+ });
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * The EDITOR's colour themes, independent of the app palette.
8
+ *
9
+ * CodeMirror can wear any of the 65 TextMate themes `@shikijs/themes` ships, and
10
+ * a reader who works in Dracula or Nord wants the code surface to say so while
11
+ * the console around it stays in the app's own palette. That separation is the
12
+ * whole feature: `SettingsState.editorTheme` is a second axis beside `theme`,
13
+ * and the editor paints its own surface, gutter, selection and tokens from it.
14
+ *
15
+ * Three rules keep it honest:
16
+ *
17
+ * - **`auto` is the default and means "the app's own palette".** Nothing changes
18
+ * for an install that never opens the picker: the surface stays transparent
19
+ * over the panel, the chrome reads `--theme-*`, and the tokenizer keeps the
20
+ * dual `one-light`/`one-dark-pro` pair the app variant selects.
21
+ * - **The data is generated, never typed.** `themes.generated.ts` is written by
22
+ * `scripts/editor-themes.mjs` from the installed package, so the picker's
23
+ * swatches are the theme's OWN colours and a `@shikijs/themes` bump cannot
24
+ * leave the grid showing a palette that no longer exists.
25
+ * - **A theme module is only fetched when it is selected.** The rows carry
26
+ * enough colour to draw the grid; `EDITOR_THEME_LOADERS` is what the
27
+ * highlighter calls, and 65 modules nobody picks are never downloaded.
28
+ *
29
+ * The picker's swatch shows the editor surface, the ink and three syntax
30
+ * accents — the five colours that actually tell two themes apart at 120px.
31
+ */
32
+
33
+ import { EDITOR_THEME_LOADERS, EDITOR_THEME_ROWS } from '@/shared/lib/code/editor/themes.generated';
34
+ import { SHIKI_THEMES } from '@/shared/lib/code/shiki-themes';
35
+
36
+ /** The setting value meaning "follow the app palette". */
37
+ export const EDITOR_AUTO_THEME_ID = 'auto';
38
+
39
+ /** What a settings row that predates this option holds. */
40
+ export const EDITOR_DEFAULT_THEME_ID = EDITOR_AUTO_THEME_ID;
41
+
42
+ export type EditorThemeVariant = 'light' | 'dark';
43
+
44
+ export interface EditorTheme {
45
+ /** Shiki's theme id, which is also the stored setting value. */
46
+ id: string;
47
+ label: string;
48
+ /** The theme's OWN light/dark verdict — never the app's. */
49
+ variant: EditorThemeVariant;
50
+ /** The editor surface and its ink. */
51
+ bg: string;
52
+ fg: string;
53
+ /** Line-number band. */
54
+ gutterFg: string;
55
+ /** Selection band, caret, and the two find-match bands. */
56
+ selection: string;
57
+ cursor: string;
58
+ find: string;
59
+ findCurrent: string;
60
+ /** Divider between the gutter and the text. */
61
+ border: string;
62
+ /** The two diff bands and their gutter markers. */
63
+ added: string;
64
+ removed: string;
65
+ addedGutter: string;
66
+ removedGutter: string;
67
+ /** Three syntax accents, for the picker's swatch only. */
68
+ keyword: string;
69
+ string: string;
70
+ comment: string;
71
+ }
72
+
73
+ /**
74
+ * The tuple's field order, stated once so the generated rows and this mapping
75
+ * cannot drift: a column added to the generator without a field here is a type
76
+ * error rather than a silently shifted colour.
77
+ */
78
+ type EditorThemeRow = (typeof EDITOR_THEME_ROWS)[number];
79
+
80
+ const FIELD_ORDER = [
81
+ 'id',
82
+ 'label',
83
+ 'variant',
84
+ 'bg',
85
+ 'fg',
86
+ 'gutterFg',
87
+ 'selection',
88
+ 'cursor',
89
+ 'find',
90
+ 'findCurrent',
91
+ 'border',
92
+ 'added',
93
+ 'removed',
94
+ 'addedGutter',
95
+ 'removedGutter',
96
+ 'keyword',
97
+ 'string',
98
+ 'comment',
99
+ ] as const satisfies ReadonlyArray<keyof EditorTheme>;
100
+
101
+ function toTheme(row: EditorThemeRow): EditorTheme {
102
+ const theme = {} as EditorTheme;
103
+ FIELD_ORDER.forEach((field, index) => {
104
+ // The tuple and the field list are asserted equal by the types, so this
105
+ // cast is the only place the two meet.
106
+ (theme as unknown as Record<string, string>)[field] = row[index];
107
+ });
108
+ return theme;
109
+ }
110
+
111
+ /** Every theme the installed Shiki ships, in id order (the generator's order). */
112
+ export const EDITOR_THEMES: readonly EditorTheme[] = EDITOR_THEME_ROWS.map(toTheme);
113
+
114
+ const BY_ID = new Map(EDITOR_THEMES.map((theme) => [theme.id, theme]));
115
+
116
+ /**
117
+ * The theme a stored setting names, or `null` for `auto` — and for an id this
118
+ * build no longer ships (a downgrade, a hand-edited row), which must render the
119
+ * app palette rather than a half-applied theme.
120
+ */
121
+ export function resolveEditorTheme(id: string | null | undefined): EditorTheme | null {
122
+ if (!id || id === EDITOR_AUTO_THEME_ID) return null;
123
+ return BY_ID.get(id) ?? null;
124
+ }
125
+
126
+ /** True when `id` is a theme this build can paint. */
127
+ export function isEditorThemeId(id: string): boolean {
128
+ return BY_ID.has(id);
129
+ }
130
+
131
+ /**
132
+ * The two theme names Shiki tokenizes with.
133
+ *
134
+ * `auto` keeps the app's dual pair, so the variant attribute picks the palette
135
+ * exactly as it does for every other surface. An explicit theme names ITSELF
136
+ * for both slots — one theme, both custom properties, identical values — which
137
+ * is what makes the editor ignore the app variant without a second CSS path.
138
+ */
139
+ export function editorShikiThemes(id: string | null | undefined): { light: string; dark: string } {
140
+ const theme = resolveEditorTheme(id);
141
+ if (!theme) return SHIKI_THEMES;
142
+ return { light: theme.id, dark: theme.id };
143
+ }
144
+
145
+ /** The CSS custom properties an explicit theme writes, keyed by variable name. */
146
+ export function editorThemeVars(theme: EditorTheme | null): Record<string, string> {
147
+ if (!theme) return {};
148
+ return {
149
+ '--editor-bg': theme.bg,
150
+ '--editor-fg': theme.fg,
151
+ '--editor-gutter-fg': theme.gutterFg,
152
+ '--editor-selection': theme.selection,
153
+ '--editor-cursor': theme.cursor,
154
+ '--editor-find': theme.find,
155
+ '--editor-find-current': theme.findCurrent,
156
+ '--editor-border': theme.border,
157
+ '--editor-added': theme.added,
158
+ '--editor-removed': theme.removed,
159
+ '--editor-added-gutter': theme.addedGutter,
160
+ '--editor-removed-gutter': theme.removedGutter,
161
+ };
162
+ }
163
+
164
+ /** The lazy module loaders, so the highlighter can fetch one theme on demand. */
165
+ export { EDITOR_THEME_LOADERS };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * The block memo in front of the tokenizer.
8
+ *
9
+ * Three properties matter and each was a decision:
10
+ *
11
+ * 1. **A repeat of the same `(language, code)` returns the same markup** — that
12
+ * is the whole point (a card closed and reopened re-tokenized a 1000-line
13
+ * read, measured as the dominant cost of its expand).
14
+ * 2. **A block whose grammar is not ready is NOT cached.** `highlightCode`
15
+ * answers escaped text in that state, and caching it would pin the plain
16
+ * fallback for the life of the entry.
17
+ * 3. **A find-in-progress bypasses the memo**, because its markup depends on the
18
+ * match ranges rather than on the code alone.
19
+ */
20
+
21
+ import { beforeAll, beforeEach, describe, expect, test } from 'bun:test';
22
+
23
+ import { bootSyntax, onLanguageReady, requestLanguage } from '@/shared/lib/code/highlighter';
24
+ import {
25
+ clearHighlightMemo,
26
+ highlightBlock,
27
+ highlightBlockLines,
28
+ highlightMemoSize,
29
+ } from '@/shared/lib/code/highlight-memo';
30
+
31
+ const TOKEN = '--shiki-light:';
32
+
33
+ beforeAll(async () => {
34
+ Object.assign(globalThis, { window: globalThis });
35
+ await bootSyntax();
36
+ Reflect.deleteProperty(globalThis, 'window');
37
+ const { promise, resolve } = Promise.withResolvers<void>();
38
+ const settle = () => {
39
+ if (requestLanguage('json') && requestLanguage('typescript')) {
40
+ off();
41
+ resolve();
42
+ }
43
+ };
44
+ const off = onLanguageReady(settle);
45
+ settle();
46
+ await promise;
47
+ });
48
+
49
+ beforeEach(() => clearHighlightMemo());
50
+
51
+ describe('highlightBlock', () => {
52
+ test('a repeated block returns the same markup and is held once', () => {
53
+ const code = 'const value = 1;\n';
54
+ const first = highlightBlock(code, 'typescript');
55
+ const second = highlightBlock(code, 'typescript');
56
+
57
+ expect(first).toBe(second);
58
+ expect(first).toContain(TOKEN);
59
+ expect(highlightMemoSize()).toBe(1);
60
+ });
61
+
62
+ test('the same code in another language is a different entry', () => {
63
+ highlightBlock('{"a":1}', 'json');
64
+ highlightBlock('{"a":1}', 'typescript');
65
+ expect(highlightMemoSize()).toBe(2);
66
+ });
67
+
68
+ test('a grammar that is not ready is never cached', () => {
69
+ const html = highlightBlock('const a = 1;', 'not-a-real-grammar');
70
+ // It still renders something — escaped text, via the javascript fallback —
71
+ // but caching that would freeze the fallback for good.
72
+ expect(html).toContain('const');
73
+ expect(highlightMemoSize()).toBe(0);
74
+ });
75
+
76
+ test('a find-in-progress bypasses the memo', () => {
77
+ highlightBlock('const a = 1;', 'typescript', { marks: [{ start: 0, end: 5 }] });
78
+ expect(highlightMemoSize()).toBe(0);
79
+ });
80
+
81
+ test('empty input is not an entry', () => {
82
+ expect(highlightBlock('', 'typescript')).toBe('');
83
+ expect(highlightMemoSize()).toBe(0);
84
+ });
85
+
86
+ test('the per-language bound holds', () => {
87
+ for (let index = 0; index < 30; index += 1) {
88
+ highlightBlock(`const value${index} = ${index};`, 'typescript');
89
+ }
90
+ expect(highlightMemoSize()).toBeLessThanOrEqual(24);
91
+ });
92
+ });
93
+
94
+ describe('highlightBlockLines', () => {
95
+ test('lines are cached and round-trip through the same key', () => {
96
+ const lines = highlightBlockLines('const a = 1;\nconst b = 2;', 'typescript');
97
+ expect(lines).toHaveLength(2);
98
+ expect(highlightMemoSize()).toBe(1);
99
+
100
+ const again = highlightBlockLines('const a = 1;\nconst b = 2;', 'typescript');
101
+ expect(again).toEqual(lines);
102
+ });
103
+ });