|
1 | 1 | /** |
2 | | - * `LoraQueryEditor` theme deriver. Builds a `Palette` from our |
3 | | - * design tokens and runs it through the package's `createTheme` |
4 | | - * helper so the editor surface, syntax tokens, popups, and diagnostic |
5 | | - * accents all snap to the playground palette. |
| 2 | + * `LoraQueryEditor` theme deriver. Builds a flat `LoraQueryTheme` |
| 3 | + * directly from our design tokens. |
6 | 4 | * |
7 | | - * Mirrors the dark/light preset construction in |
8 | | - * `@loradb/lora-query/themes.ts`: `createTheme(palette, overrides)` |
9 | | - * spreads typography defaults from `palettes.ts` and flattens the |
10 | | - * popup / diagnostic groups onto the flat `LoraQueryTheme` shape. |
| 5 | + * Why we don't use `createTheme` from `@loradb/lora-query`: |
| 6 | + * the package's built ESM bundle is wrapped by |
| 7 | + * `vite-plugin-top-level-await` (the parser entry needs WASM TLA), |
| 8 | + * so every export — `createTheme` included — is gated behind an |
| 9 | + * async `__tla` chain that Webpack does not auto-await. Calling |
| 10 | + * `createTheme` synchronously from a `useMemo` during the first |
| 11 | + * render therefore throws `U is not a function` until the WASM |
| 12 | + * promise resolves. The flatten itself is trivial, so we do it |
| 13 | + * inline and skip the TLA-gated import entirely. |
11 | 14 | */ |
12 | 15 |
|
13 | | -import { |
14 | | - createTheme, |
15 | | - type LoraQueryTheme, |
16 | | - type Palette, |
17 | | -} from "@loradb/lora-query"; |
| 16 | +import type { LoraQueryTheme } from "@loradb/lora-query"; |
18 | 17 |
|
19 | 18 | import type { Tokens } from "./tokens"; |
20 | 19 | import { hexA } from "./util"; |
21 | 20 |
|
22 | 21 | /** Derive a `LoraQueryTheme` from our token set. */ |
23 | 22 | export function deriveEditorTheme(tokens: Tokens): LoraQueryTheme { |
24 | | - const palette: Palette = { |
25 | | - surface: { |
26 | | - background: tokens.bg.editor, |
27 | | - foreground: tokens.fg.primary, |
28 | | - border: tokens.border.subtle, |
29 | | - muted: tokens.fg.muted, |
30 | | - accent: tokens.accent.primary, |
31 | | - activeLine: hexA(tokens.fg.primary, 0.06), |
32 | | - gutterBackground: tokens.bg.editor, |
33 | | - gutterForeground: tokens.fg.subtle, |
34 | | - cursor: tokens.fg.primary, |
35 | | - selectionBackground: hexA(tokens.accent.primary, 0.28), |
36 | | - }, |
37 | | - tokens: { |
38 | | - keyword: tokens.syntax.keyword, |
39 | | - variable: tokens.syntax.identifier, |
40 | | - parameter: tokens.syntax.identifier, |
41 | | - label: tokens.syntax.type, |
42 | | - relType: tokens.syntax.type, |
43 | | - property: tokens.syntax.identifier, |
44 | | - functionName: tokens.syntax.type, |
45 | | - namespace: tokens.syntax.type, |
46 | | - string: tokens.syntax.string, |
47 | | - number: tokens.syntax.number, |
48 | | - bool: tokens.syntax.keyword, |
49 | | - null: tokens.syntax.keyword, |
50 | | - operator: tokens.syntax.operator, |
51 | | - comment: tokens.syntax.comment, |
52 | | - }, |
53 | | - popup: { |
54 | | - background: tokens.bg.panel, |
55 | | - foreground: tokens.fg.primary, |
56 | | - border: tokens.border.subtle, |
57 | | - selectedBackground: tokens.accent.primary, |
58 | | - selectedForeground: tokens.fg.inverse, |
59 | | - shadow: `0 6px 16px ${hexA("#000000", 0.35)}`, |
60 | | - }, |
61 | | - diagnostic: { |
62 | | - error: tokens.accent.danger, |
63 | | - warning: tokens.accent.warning, |
64 | | - info: tokens.accent.info, |
65 | | - }, |
66 | | - scrollbar: { |
67 | | - track: tokens.bg.editor, |
68 | | - thumb: tokens.border.strong, |
69 | | - thumbHover: tokens.fg.subtle, |
70 | | - width: "auto", |
71 | | - size: "10px", |
72 | | - }, |
73 | | - }; |
| 23 | + const keyword = tokens.syntax.keyword; |
| 24 | + const identifier = tokens.syntax.identifier; |
| 25 | + const type = tokens.syntax.type; |
| 26 | + |
| 27 | + return { |
| 28 | + background: tokens.bg.editor, |
| 29 | + foreground: tokens.fg.primary, |
| 30 | + border: tokens.border.subtle, |
| 31 | + muted: tokens.fg.muted, |
| 32 | + accent: tokens.accent.primary, |
| 33 | + activeLine: hexA(tokens.fg.primary, 0.06), |
| 34 | + gutterBackground: tokens.bg.editor, |
| 35 | + gutterForeground: tokens.fg.subtle, |
| 36 | + cursor: tokens.fg.primary, |
| 37 | + selectionBackground: hexA(tokens.accent.primary, 0.28), |
74 | 38 |
|
75 | | - return createTheme(palette, { |
76 | 39 | fontFamily: tokens.font.ui, |
77 | 40 | monoFontFamily: tokens.font.mono, |
78 | 41 | fontSize: "13px", |
79 | 42 | popupFontSize: "12px", |
80 | | - }); |
| 43 | + |
| 44 | + keyword, |
| 45 | + variable: identifier, |
| 46 | + parameter: identifier, |
| 47 | + label: type, |
| 48 | + relType: type, |
| 49 | + property: identifier, |
| 50 | + functionName: type, |
| 51 | + namespace: type, |
| 52 | + string: tokens.syntax.string, |
| 53 | + number: tokens.syntax.number, |
| 54 | + bool: keyword, |
| 55 | + null: keyword, |
| 56 | + |
| 57 | + popupBackground: tokens.bg.panel, |
| 58 | + popupForeground: tokens.fg.primary, |
| 59 | + popupBorder: tokens.border.subtle, |
| 60 | + popupSelectedBackground: tokens.accent.primary, |
| 61 | + popupSelectedForeground: tokens.fg.inverse, |
| 62 | + popupShadow: `0 6px 16px ${hexA("#000000", 0.35)}`, |
| 63 | + |
| 64 | + errorAccent: tokens.accent.danger, |
| 65 | + warningAccent: tokens.accent.warning, |
| 66 | + infoAccent: tokens.accent.info, |
| 67 | + |
| 68 | + scrollbarTrack: tokens.bg.editor, |
| 69 | + scrollbarThumb: tokens.border.strong, |
| 70 | + scrollbarThumbHover: tokens.fg.subtle, |
| 71 | + scrollbarWidth: "auto", |
| 72 | + scrollbarSize: "10px", |
| 73 | + }; |
81 | 74 | } |
0 commit comments