@pierre/theme 0.0.18 → 0.0.21

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.
@@ -0,0 +1,606 @@
1
+ // src/zed-theme.ts
2
+ import type { Roles } from "./palette";
3
+
4
+ type ZedHighlightStyle = {
5
+ color?: string;
6
+ background_color?: string;
7
+ font_style?: "normal" | "italic" | "oblique";
8
+ font_weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
9
+ };
10
+
11
+ type ZedPlayerColor = {
12
+ cursor?: string;
13
+ background?: string;
14
+ selection?: string;
15
+ };
16
+
17
+ type ZedThemeStyle = {
18
+ // Backgrounds
19
+ background: string;
20
+ "background.appearance"?: "opaque" | "transparent" | "blurred";
21
+ "surface.background": string;
22
+ "elevated_surface.background": string;
23
+ "drop_target.background": string;
24
+
25
+ // Editor
26
+ "editor.background": string;
27
+ "editor.foreground": string;
28
+ "editor.gutter.background": string;
29
+ "editor.active_line.background": string;
30
+ "editor.active_line_number": string;
31
+ "editor.line_number": string;
32
+ "editor.highlighted_line.background": string;
33
+ "editor.indent_guide": string;
34
+ "editor.indent_guide_active": string;
35
+ "editor.invisible": string;
36
+ "editor.wrap_guide": string;
37
+ "editor.active_wrap_guide": string;
38
+ "editor.document_highlight.read_background": string;
39
+ "editor.document_highlight.write_background": string;
40
+ "editor.document_highlight.bracket_background": string;
41
+ "editor.subheader.background": string;
42
+
43
+ // Text
44
+ text: string;
45
+ "text.muted": string;
46
+ "text.placeholder": string;
47
+ "text.disabled": string;
48
+ "text.accent": string;
49
+
50
+ // Borders
51
+ border: string;
52
+ "border.variant": string;
53
+ "border.focused": string;
54
+ "border.selected": string;
55
+ "border.transparent": string;
56
+ "border.disabled": string;
57
+
58
+ // UI Elements
59
+ "element.background": string;
60
+ "element.hover": string;
61
+ "element.active": string;
62
+ "element.selected": string;
63
+ "element.disabled": string;
64
+ "ghost_element.background": string;
65
+ "ghost_element.hover": string;
66
+ "ghost_element.active": string;
67
+ "ghost_element.selected": string;
68
+ "ghost_element.disabled": string;
69
+
70
+ // Icons & Links
71
+ icon: string;
72
+ "icon.muted": string;
73
+ "icon.disabled": string;
74
+ "icon.placeholder": string;
75
+ "icon.accent": string;
76
+ "link_text.hover": string;
77
+
78
+ // Status colors
79
+ error: string;
80
+ "error.background": string;
81
+ "error.border": string;
82
+ warning: string;
83
+ "warning.background": string;
84
+ "warning.border": string;
85
+ success: string;
86
+ "success.background": string;
87
+ "success.border": string;
88
+ info: string;
89
+ "info.background": string;
90
+ "info.border": string;
91
+ hint: string;
92
+ "hint.background": string;
93
+ "hint.border": string;
94
+ predictive: string;
95
+ "predictive.background": string;
96
+ "predictive.border": string;
97
+ unreachable: string;
98
+ "unreachable.background": string;
99
+ "unreachable.border": string;
100
+
101
+ // Git status
102
+ created: string;
103
+ "created.background": string;
104
+ "created.border": string;
105
+ modified: string;
106
+ "modified.background": string;
107
+ "modified.border": string;
108
+ deleted: string;
109
+ "deleted.background": string;
110
+ "deleted.border": string;
111
+ conflict: string;
112
+ "conflict.background": string;
113
+ "conflict.border": string;
114
+ hidden: string;
115
+ "hidden.background": string;
116
+ "hidden.border": string;
117
+ ignored: string;
118
+ "ignored.background": string;
119
+ "ignored.border": string;
120
+ renamed: string;
121
+ "renamed.background": string;
122
+ "renamed.border": string;
123
+
124
+ // Search
125
+ "search.match_background": string;
126
+
127
+ // Tabs
128
+ "tab_bar.background": string;
129
+ "tab.active_background": string;
130
+ "tab.inactive_background": string;
131
+
132
+ // Toolbar & Title bar
133
+ "toolbar.background": string;
134
+ "title_bar.background": string;
135
+ "title_bar.inactive_background": string;
136
+
137
+ // Panel & Status bar
138
+ "panel.background": string;
139
+ "panel.focused_border": string;
140
+ "status_bar.background": string;
141
+
142
+ // Scrollbar
143
+ "scrollbar.thumb.background": string;
144
+ "scrollbar.thumb.hover_background": string;
145
+ "scrollbar.thumb.border": string;
146
+ "scrollbar.track.background": string;
147
+ "scrollbar.track.border": string;
148
+
149
+ // Terminal
150
+ "terminal.background": string;
151
+ "terminal.foreground": string;
152
+ "terminal.bright_foreground": string;
153
+ "terminal.dim_foreground": string;
154
+ "terminal.ansi.black": string;
155
+ "terminal.ansi.red": string;
156
+ "terminal.ansi.green": string;
157
+ "terminal.ansi.yellow": string;
158
+ "terminal.ansi.blue": string;
159
+ "terminal.ansi.magenta": string;
160
+ "terminal.ansi.cyan": string;
161
+ "terminal.ansi.white": string;
162
+ "terminal.ansi.bright_black": string;
163
+ "terminal.ansi.bright_red": string;
164
+ "terminal.ansi.bright_green": string;
165
+ "terminal.ansi.bright_yellow": string;
166
+ "terminal.ansi.bright_blue": string;
167
+ "terminal.ansi.bright_magenta": string;
168
+ "terminal.ansi.bright_cyan": string;
169
+ "terminal.ansi.bright_white": string;
170
+ "terminal.ansi.dim_black"?: string;
171
+ "terminal.ansi.dim_red"?: string;
172
+ "terminal.ansi.dim_green"?: string;
173
+ "terminal.ansi.dim_yellow"?: string;
174
+ "terminal.ansi.dim_blue"?: string;
175
+ "terminal.ansi.dim_magenta"?: string;
176
+ "terminal.ansi.dim_cyan"?: string;
177
+ "terminal.ansi.dim_white"?: string;
178
+
179
+ // Players (multiplayer cursors)
180
+ players: ZedPlayerColor[];
181
+
182
+ // Syntax highlighting
183
+ syntax: Record<string, ZedHighlightStyle>;
184
+ };
185
+
186
+ type ZedTheme = {
187
+ name: string;
188
+ appearance: "light" | "dark";
189
+ style: ZedThemeStyle;
190
+ };
191
+
192
+ type ZedThemeFamilyContent = {
193
+ $schema: string;
194
+ name: string;
195
+ author: string;
196
+ themes: ZedTheme[];
197
+ };
198
+
199
+ export type ZedThemeVariant = {
200
+ name: string;
201
+ appearance: "light" | "dark";
202
+ roles: Roles;
203
+ };
204
+
205
+ export function makeZedThemeFamily(
206
+ familyName: string,
207
+ author: string,
208
+ variants: ZedThemeVariant[]
209
+ ): ZedThemeFamilyContent {
210
+ return {
211
+ $schema: "https://zed.dev/schema/themes/v0.2.0.json",
212
+ name: familyName,
213
+ author,
214
+ themes: variants.map((v) => makeZedTheme(v.name, v.appearance, v.roles)),
215
+ };
216
+ }
217
+
218
+ function makeZedTheme(
219
+ name: string,
220
+ appearance: "light" | "dark",
221
+ c: Roles
222
+ ): ZedTheme {
223
+ const isDark = appearance === "dark";
224
+
225
+ return {
226
+ name,
227
+ appearance,
228
+ style: {
229
+ // Backgrounds
230
+ background: c.bg.window,
231
+ "surface.background": c.bg.window,
232
+ "elevated_surface.background": c.bg.elevated,
233
+ "drop_target.background": alpha(c.accent.primary, 0.15),
234
+
235
+ // Editor
236
+ "editor.background": c.bg.editor,
237
+ "editor.foreground": c.fg.base,
238
+ "editor.gutter.background": c.bg.editor,
239
+ "editor.active_line.background": alpha(c.accent.subtle, 0.55),
240
+ "editor.active_line_number": c.fg.fg2,
241
+ "editor.line_number": c.fg.fg3,
242
+ "editor.highlighted_line.background": alpha(c.accent.subtle, 0.35),
243
+ "editor.indent_guide": c.border.indentGuide,
244
+ "editor.indent_guide_active": c.border.indentGuideActive,
245
+ "editor.invisible": c.fg.fg4,
246
+ "editor.wrap_guide": c.border.indentGuide,
247
+ "editor.active_wrap_guide": c.border.indentGuideActive,
248
+ "editor.document_highlight.read_background": alpha(c.accent.primary, isDark ? 0.15 : 0.1),
249
+ "editor.document_highlight.write_background": alpha(c.accent.primary, isDark ? 0.25 : 0.18),
250
+ "editor.document_highlight.bracket_background": alpha(c.accent.primary, 0.2),
251
+ "editor.subheader.background": c.bg.window,
252
+
253
+ // Text
254
+ text: c.fg.base,
255
+ "text.muted": c.fg.fg3,
256
+ "text.placeholder": c.fg.fg4,
257
+ "text.disabled": c.fg.fg4,
258
+ "text.accent": c.accent.primary,
259
+
260
+ // Borders - use darker borders for dark themes
261
+ border: isDark ? c.border.indentGuide : c.border.editor,
262
+ "border.variant": isDark ? c.border.indentGuideActive : c.border.window,
263
+ "border.focused": c.accent.primary,
264
+ "border.selected": c.accent.primary,
265
+ "border.transparent": "transparent",
266
+ "border.disabled": isDark ? c.border.indentGuideActive : c.border.inset,
267
+
268
+ // UI Elements
269
+ "element.background": c.bg.inset,
270
+ "element.hover": alpha(c.accent.subtle, 0.5),
271
+ "element.active": alpha(c.accent.subtle, 0.7),
272
+ "element.selected": alpha(c.accent.subtle, isDark ? 0.6 : 0.8),
273
+ "element.disabled": alpha(c.bg.inset, 0.5),
274
+ "ghost_element.background": "transparent",
275
+ "ghost_element.hover": alpha(c.accent.subtle, 0.35),
276
+ "ghost_element.active": alpha(c.accent.subtle, 0.55),
277
+ "ghost_element.selected": alpha(c.accent.subtle, isDark ? 0.5 : 0.65),
278
+ "ghost_element.disabled": "transparent",
279
+
280
+ // Icons & Links
281
+ icon: c.fg.fg2,
282
+ "icon.muted": c.fg.fg3,
283
+ "icon.disabled": c.fg.fg4,
284
+ "icon.placeholder": c.fg.fg4,
285
+ "icon.accent": c.accent.primary,
286
+ "link_text.hover": c.accent.link,
287
+
288
+ // Status colors
289
+ error: c.states.danger,
290
+ "error.background": alpha(c.states.danger, 0.1),
291
+ "error.border": alpha(c.states.danger, 0.3),
292
+ warning: c.accent.primary,
293
+ "warning.background": alpha(c.accent.primary, 0.1),
294
+ "warning.border": alpha(c.accent.primary, 0.3),
295
+ success: c.states.success,
296
+ "success.background": alpha(c.states.success, 0.1),
297
+ "success.border": alpha(c.states.success, 0.3),
298
+ info: c.states.info,
299
+ "info.background": alpha(c.states.info, 0.1),
300
+ "info.border": alpha(c.states.info, 0.3),
301
+ hint: c.fg.fg3,
302
+ "hint.background": alpha(c.fg.fg3, 0.1),
303
+ "hint.border": alpha(c.fg.fg3, 0.2),
304
+ predictive: c.fg.fg4,
305
+ "predictive.background": alpha(c.fg.fg4, 0.1),
306
+ "predictive.border": alpha(c.fg.fg4, 0.2),
307
+ unreachable: c.fg.fg4,
308
+ "unreachable.background": alpha(c.fg.fg4, 0.05),
309
+ "unreachable.border": alpha(c.fg.fg4, 0.1),
310
+
311
+ // Git status
312
+ created: c.states.success,
313
+ "created.background": alpha(c.states.success, 0.1),
314
+ "created.border": alpha(c.states.success, 0.3),
315
+ modified: c.accent.primary,
316
+ "modified.background": alpha(c.accent.primary, 0.1),
317
+ "modified.border": alpha(c.accent.primary, 0.3),
318
+ deleted: c.states.danger,
319
+ "deleted.background": alpha(c.states.danger, 0.1),
320
+ "deleted.border": alpha(c.states.danger, 0.3),
321
+ conflict: c.accent.primary,
322
+ "conflict.background": alpha(c.accent.primary, 0.1),
323
+ "conflict.border": alpha(c.accent.primary, 0.3),
324
+ hidden: c.fg.fg4,
325
+ "hidden.background": alpha(c.fg.fg4, 0.05),
326
+ "hidden.border": alpha(c.fg.fg4, 0.1),
327
+ ignored: c.fg.fg3,
328
+ "ignored.background": alpha(c.fg.fg3, 0.05),
329
+ "ignored.border": alpha(c.fg.fg3, 0.1),
330
+ renamed: c.states.info,
331
+ "renamed.background": alpha(c.states.info, 0.1),
332
+ "renamed.border": alpha(c.states.info, 0.3),
333
+
334
+ // Search
335
+ "search.match_background": alpha(c.states.warn, 0.3),
336
+
337
+ // Tabs
338
+ "tab_bar.background": c.bg.window,
339
+ "tab.active_background": c.bg.window,
340
+ "tab.inactive_background": c.bg.window,
341
+
342
+ // Toolbar & Title bar
343
+ "toolbar.background": c.bg.window,
344
+ "title_bar.background": c.bg.window,
345
+ "title_bar.inactive_background": c.bg.window,
346
+
347
+ // Panel & Status bar
348
+ "panel.background": c.bg.window,
349
+ "panel.focused_border": c.accent.primary,
350
+ "status_bar.background": c.bg.window,
351
+
352
+ // Scrollbar
353
+ "scrollbar.thumb.background": alpha(c.fg.fg4, 0.3),
354
+ "scrollbar.thumb.hover_background": alpha(c.fg.fg4, 0.5),
355
+ "scrollbar.thumb.border": "transparent",
356
+ "scrollbar.track.background": "transparent",
357
+ "scrollbar.track.border": "transparent",
358
+
359
+ // Terminal
360
+ "terminal.background": c.bg.window,
361
+ "terminal.foreground": c.fg.fg2,
362
+ "terminal.bright_foreground": c.fg.base,
363
+ "terminal.dim_foreground": c.fg.fg3,
364
+ "terminal.ansi.black": c.ansi.black,
365
+ "terminal.ansi.red": c.ansi.red,
366
+ "terminal.ansi.green": c.ansi.green,
367
+ "terminal.ansi.yellow": c.ansi.yellow,
368
+ "terminal.ansi.blue": c.ansi.blue,
369
+ "terminal.ansi.magenta": c.ansi.magenta,
370
+ "terminal.ansi.cyan": c.ansi.cyan,
371
+ "terminal.ansi.white": c.ansi.white,
372
+ "terminal.ansi.bright_black": c.ansi.brightBlack,
373
+ "terminal.ansi.bright_red": c.ansi.brightRed,
374
+ "terminal.ansi.bright_green": c.ansi.brightGreen,
375
+ "terminal.ansi.bright_yellow": c.ansi.brightYellow,
376
+ "terminal.ansi.bright_blue": c.ansi.brightBlue,
377
+ "terminal.ansi.bright_magenta": c.ansi.brightMagenta,
378
+ "terminal.ansi.bright_cyan": c.ansi.brightCyan,
379
+ "terminal.ansi.bright_white": c.ansi.brightWhite,
380
+
381
+ // Players (multiplayer cursors) - use colors from the palette
382
+ players: [
383
+ { cursor: c.accent.primary, background: c.accent.primary, selection: alpha(c.accent.primary, 0.25) },
384
+ { cursor: c.states.success, background: c.states.success, selection: alpha(c.states.success, 0.25) },
385
+ { cursor: c.syntax.keyword, background: c.syntax.keyword, selection: alpha(c.syntax.keyword, 0.25) },
386
+ { cursor: c.syntax.func, background: c.syntax.func, selection: alpha(c.syntax.func, 0.25) },
387
+ { cursor: c.syntax.string, background: c.syntax.string, selection: alpha(c.syntax.string, 0.25) },
388
+ { cursor: c.states.warn, background: c.states.warn, selection: alpha(c.states.warn, 0.25) },
389
+ { cursor: c.syntax.type, background: c.syntax.type, selection: alpha(c.syntax.type, 0.25) },
390
+ { cursor: c.states.info, background: c.states.info, selection: alpha(c.states.info, 0.25) },
391
+ ],
392
+
393
+ // Syntax highlighting
394
+ syntax: {
395
+ // Comments
396
+ comment: { color: c.syntax.comment },
397
+ "comment.doc": { color: c.syntax.comment },
398
+
399
+ // Strings
400
+ string: { color: c.syntax.string },
401
+ "string.escape": { color: c.syntax.escape },
402
+ "string.regex": { color: c.syntax.regexp },
403
+ "string.special": { color: c.syntax.escape },
404
+ "string.special.symbol": { color: c.syntax.constant },
405
+
406
+ // Numbers & Constants
407
+ number: { color: c.syntax.number },
408
+ constant: { color: c.syntax.constant },
409
+ boolean: { color: c.syntax.number },
410
+
411
+ // Keywords
412
+ keyword: { color: c.syntax.keyword },
413
+ "keyword.operator": { color: c.syntax.operator },
414
+
415
+ // Functions
416
+ function: { color: c.syntax.func },
417
+ "function.method": { color: c.syntax.func },
418
+ "function.builtin": { color: c.syntax.func },
419
+ "function.special.definition": { color: c.syntax.func },
420
+ // CSS/SCSS function calls like var(), calc(), light-dark()
421
+ "function.call": { color: c.syntax.func },
422
+
423
+ // Types
424
+ type: { color: c.syntax.type },
425
+ "type.builtin": { color: c.syntax.type },
426
+ constructor: { color: c.syntax.type },
427
+
428
+ // Variables
429
+ variable: { color: c.syntax.variable },
430
+ "variable.builtin": { color: c.syntax.namespace }, // this, self, super
431
+ "variable.member": { color: c.syntax.variable },
432
+ "variable.parameter": { color: c.syntax.parameter },
433
+ "variable.special": { color: c.syntax.namespace },
434
+
435
+ // Properties - Used for JS object keys and property access
436
+ // Keep as variable color (orange) for JS compatibility
437
+ property: { color: c.syntax.variable },
438
+
439
+ // ========================================
440
+ // CSS/SCSS SPECIFIC
441
+ // ========================================
442
+ // CSS property names (e.g., position, display, margin) - blue
443
+ // These more specific scopes should override `property` for CSS
444
+ "property.css": { color: c.accent.primary },
445
+ "property.definition": { color: c.accent.primary },
446
+ property_name: { color: c.accent.primary },
447
+
448
+ // CSS property values that are keywords (e.g., relative, flex, auto, solid)
449
+ "value": { color: c.syntax.number },
450
+ "constant.css": { color: c.syntax.constant },
451
+ "string.plain": { color: c.syntax.number },
452
+ plain_value: { color: c.syntax.number },
453
+
454
+ // CSS selectors - element/tag selectors (p, ul, ol, div, table)
455
+ "tag.css": { color: c.syntax.tag },
456
+ tag_name: { color: c.syntax.tag },
457
+ // Class selectors (.prose, .container)
458
+ "class": { color: c.syntax.attribute },
459
+ class_name: { color: c.syntax.attribute },
460
+ "selector.class": { color: c.syntax.attribute },
461
+ // ID selectors (#main)
462
+ "selector.id": { color: c.syntax.func },
463
+ id_name: { color: c.syntax.func },
464
+ // Pseudo-elements and pseudo-classes (::before, :hover)
465
+ "selector.pseudo": { color: c.syntax.operator },
466
+ pseudo_class_selector: { color: c.syntax.operator },
467
+ pseudo_element_selector: { color: c.syntax.operator },
468
+
469
+ // @-rules (@use, @layer, @media, @mixin)
470
+ "keyword.directive": { color: c.syntax.keyword },
471
+ "keyword.control.at-rule": { color: c.syntax.keyword },
472
+ at_keyword: { color: c.syntax.keyword },
473
+
474
+ // SCSS/CSS variables - these should be orange (variable color)
475
+ // SCSS variables ($variable)
476
+ "variable.scss": { color: c.syntax.variable },
477
+ // CSS custom properties (--custom-prop) - orange
478
+ "variable.css": { color: c.syntax.variable },
479
+ "property.custom": { color: c.syntax.variable },
480
+
481
+ // Units (px, em, %, rem)
482
+ "unit": { color: c.syntax.number },
483
+ "number.unit": { color: c.syntax.number },
484
+
485
+ // Colors
486
+ "color": { color: c.syntax.constant },
487
+ "constant.color": { color: c.syntax.constant },
488
+
489
+ // Important
490
+ "keyword.important": { color: c.syntax.keyword },
491
+
492
+ // ========================================
493
+ // END CSS/SCSS SPECIFIC
494
+ // ========================================
495
+
496
+ // ========================================
497
+ // JAVASCRIPT/TYPESCRIPT SPECIFIC
498
+ // ========================================
499
+ // `this`, `self`, `super` - namespace/yellow color
500
+ "variable.language": { color: c.syntax.namespace },
501
+ this: { color: c.syntax.namespace },
502
+ self: { color: c.syntax.namespace },
503
+
504
+ // Class/Type names (Dropdown, BaseComponent, TypeError)
505
+ "type.class": { color: c.syntax.type },
506
+ // Note: class_name is defined in CSS section for CSS class selectors
507
+
508
+ // Object literal keys
509
+ "property.object": { color: c.syntax.variable },
510
+ property_identifier: { color: c.syntax.variable },
511
+ shorthand_property_identifier: { color: c.syntax.variable },
512
+ shorthand_property_identifier_pattern: { color: c.syntax.variable },
513
+
514
+ // Method definitions and calls
515
+ method_definition: { color: c.syntax.func },
516
+ "function.method.call": { color: c.syntax.func },
517
+
518
+ // Template literal interpolation
519
+ "string.template": { color: c.syntax.string },
520
+ template_string: { color: c.syntax.string },
521
+
522
+ // JSX
523
+ "tag.jsx": { color: c.syntax.tag },
524
+ "tag.component": { color: c.syntax.type },
525
+
526
+ // ========================================
527
+ // END JAVASCRIPT/TYPESCRIPT SPECIFIC
528
+ // ========================================
529
+
530
+ // Punctuation
531
+ punctuation: { color: c.syntax.punctuation },
532
+ "punctuation.bracket": { color: c.syntax.punctuation },
533
+ "punctuation.delimiter": { color: c.syntax.punctuation },
534
+ "punctuation.list_marker": { color: c.syntax.punctuation },
535
+ "punctuation.special": { color: c.syntax.keyword },
536
+
537
+ // Operators
538
+ operator: { color: c.syntax.operator },
539
+
540
+ // Tags (HTML/XML/JSX)
541
+ tag: { color: c.syntax.tag },
542
+ attribute: { color: c.syntax.attribute },
543
+
544
+ // Labels & Namespaces
545
+ label: { color: c.syntax.namespace },
546
+ namespace: { color: c.syntax.namespace },
547
+
548
+ // Embedded / Preprocessor
549
+ embedded: { color: c.fg.base },
550
+ preproc: { color: c.syntax.keyword },
551
+
552
+ // Markup
553
+ "text.literal": { color: c.syntax.string },
554
+ "markup.heading": { color: c.syntax.tag, font_weight: 700 },
555
+ "markup.bold": { color: c.syntax.constant, font_weight: 700 },
556
+ "markup.italic": { color: c.syntax.keyword, font_style: "italic" },
557
+ "markup.strikethrough": { color: c.fg.fg3 },
558
+ "markup.link.url": { color: c.accent.link },
559
+ "markup.link.text": { color: c.syntax.func },
560
+ "markup.quote": { color: c.syntax.comment, font_style: "italic" },
561
+ "markup.list": { color: c.syntax.tag },
562
+ "markup.list.numbered": { color: c.syntax.tag },
563
+ "markup.list.unnumbered": { color: c.syntax.tag },
564
+ "markup.raw": { color: c.syntax.string },
565
+ "markup.raw.inline": { color: c.syntax.string },
566
+ "markup.raw.block": { color: c.syntax.string },
567
+
568
+ // Diff
569
+ "diff.plus": { color: c.states.success },
570
+ "diff.minus": { color: c.states.danger },
571
+ "diff.delta": { color: c.states.warn },
572
+
573
+ // Links
574
+ link_text: { color: c.accent.link },
575
+ link_uri: { color: c.syntax.keyword },
576
+
577
+ // Emphasis & Primary
578
+ emphasis: { font_style: "italic" },
579
+ "emphasis.strong": { font_weight: 700 },
580
+ primary: { color: c.accent.primary },
581
+ title: { color: c.syntax.tag, font_weight: 700 },
582
+
583
+ // Predictive / AI suggestions
584
+ predictive: { color: c.fg.fg4, font_style: "italic" },
585
+ },
586
+ },
587
+ };
588
+ }
589
+
590
+ // Helper: add alpha to hex color
591
+ function alpha(color: string, opacity: number): string {
592
+ // Handle Display P3 color format
593
+ if (color.startsWith("color(display-p3")) {
594
+ if (color.includes(" / ")) {
595
+ return color.replace(/ \/ [\d.]+\)$/, ` / ${opacity.toFixed(6)})`);
596
+ } else {
597
+ return color.replace(/\)$/, ` / ${opacity.toFixed(6)})`);
598
+ }
599
+ }
600
+
601
+ // Handle hex color format
602
+ const alphaHex = Math.round(opacity * 255)
603
+ .toString(16)
604
+ .padStart(2, "0");
605
+ return `${color}${alphaHex}`;
606
+ }