@adea-ai/themes 0.1.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 (109) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +110 -0
  3. package/README.md +157 -0
  4. package/dist/adapters/base24.d.ts +117 -0
  5. package/dist/adapters/base24.d.ts.map +1 -0
  6. package/dist/adapters/base24.js +313 -0
  7. package/dist/adapters/base24.js.map +1 -0
  8. package/dist/adapters/css.d.ts +68 -0
  9. package/dist/adapters/css.d.ts.map +1 -0
  10. package/dist/adapters/css.js +107 -0
  11. package/dist/adapters/css.js.map +1 -0
  12. package/dist/adapters/shadcn.d.ts +43 -0
  13. package/dist/adapters/shadcn.d.ts.map +1 -0
  14. package/dist/adapters/shadcn.js +89 -0
  15. package/dist/adapters/shadcn.js.map +1 -0
  16. package/dist/adapters/shiki.d.ts +60 -0
  17. package/dist/adapters/shiki.d.ts.map +1 -0
  18. package/dist/adapters/shiki.js +135 -0
  19. package/dist/adapters/shiki.js.map +1 -0
  20. package/dist/adapters/tailwind.d.ts +35 -0
  21. package/dist/adapters/tailwind.d.ts.map +1 -0
  22. package/dist/adapters/tailwind.js +58 -0
  23. package/dist/adapters/tailwind.js.map +1 -0
  24. package/dist/adapters/xterm.d.ts +64 -0
  25. package/dist/adapters/xterm.d.ts.map +1 -0
  26. package/dist/adapters/xterm.js +112 -0
  27. package/dist/adapters/xterm.js.map +1 -0
  28. package/dist/catalogue.d.ts +66 -0
  29. package/dist/catalogue.d.ts.map +1 -0
  30. package/dist/catalogue.js +110 -0
  31. package/dist/catalogue.js.map +1 -0
  32. package/dist/derive.d.ts +89 -0
  33. package/dist/derive.d.ts.map +1 -0
  34. package/dist/derive.js +165 -0
  35. package/dist/derive.js.map +1 -0
  36. package/dist/generated/schemes.d.ts +16 -0
  37. package/dist/generated/schemes.d.ts.map +1 -0
  38. package/dist/generated/schemes.js +880 -0
  39. package/dist/generated/schemes.js.map +1 -0
  40. package/dist/generated/themes.d.ts +11 -0
  41. package/dist/generated/themes.d.ts.map +1 -0
  42. package/dist/generated/themes.js +1650 -0
  43. package/dist/generated/themes.js.map +1 -0
  44. package/dist/index.d.ts +67 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +58 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/normalize.d.ts +160 -0
  49. package/dist/normalize.d.ts.map +1 -0
  50. package/dist/normalize.js +795 -0
  51. package/dist/normalize.js.map +1 -0
  52. package/dist/oklch.d.ts +141 -0
  53. package/dist/oklch.d.ts.map +1 -0
  54. package/dist/oklch.js +306 -0
  55. package/dist/oklch.js.map +1 -0
  56. package/dist/schema.d.ts +178 -0
  57. package/dist/schema.d.ts.map +1 -0
  58. package/dist/schema.js +69 -0
  59. package/dist/schema.js.map +1 -0
  60. package/dist/sources.d.ts +171 -0
  61. package/dist/sources.d.ts.map +1 -0
  62. package/dist/sources.js +559 -0
  63. package/dist/sources.js.map +1 -0
  64. package/dist/validate.d.ts +121 -0
  65. package/dist/validate.d.ts.map +1 -0
  66. package/dist/validate.js +255 -0
  67. package/dist/validate.js.map +1 -0
  68. package/package.json +106 -0
  69. package/palettes/ayu-light.json +38 -0
  70. package/palettes/ayu-mirage.json +38 -0
  71. package/palettes/ayu.json +38 -0
  72. package/palettes/catppuccin-frappe.json +38 -0
  73. package/palettes/catppuccin-latte.json +38 -0
  74. package/palettes/catppuccin-macchiato.json +38 -0
  75. package/palettes/catppuccin-mocha.json +38 -0
  76. package/palettes/dracula.json +38 -0
  77. package/palettes/everforest-dark.json +38 -0
  78. package/palettes/everforest-light.json +38 -0
  79. package/palettes/gruvbox-dark.json +38 -0
  80. package/palettes/gruvbox-light.json +38 -0
  81. package/palettes/kanagawa.json +38 -0
  82. package/palettes/monokai.json +38 -0
  83. package/palettes/nord.json +38 -0
  84. package/palettes/one-dark.json +38 -0
  85. package/palettes/rosepine-dawn.json +38 -0
  86. package/palettes/rosepine-moon.json +38 -0
  87. package/palettes/rosepine.json +38 -0
  88. package/palettes/solarized-dark.json +38 -0
  89. package/palettes/solarized-light.json +38 -0
  90. package/palettes/tokyonight-day.json +38 -0
  91. package/palettes/tokyonight-night.json +38 -0
  92. package/palettes/tokyonight-storm.json +38 -0
  93. package/palettes/vesper.json +38 -0
  94. package/src/adapters/base24.ts +355 -0
  95. package/src/adapters/css.ts +149 -0
  96. package/src/adapters/shadcn.ts +99 -0
  97. package/src/adapters/shiki.ts +168 -0
  98. package/src/adapters/tailwind.ts +79 -0
  99. package/src/adapters/xterm.ts +159 -0
  100. package/src/catalogue.ts +129 -0
  101. package/src/derive.ts +203 -0
  102. package/src/generated/schemes.ts +882 -0
  103. package/src/generated/themes.ts +1652 -0
  104. package/src/index.ts +146 -0
  105. package/src/normalize.ts +1010 -0
  106. package/src/oklch.ts +366 -0
  107. package/src/schema.ts +222 -0
  108. package/src/sources.ts +682 -0
  109. package/src/validate.ts +325 -0
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The code adapter: a theme as a Shiki theme registration.
3
+ *
4
+ * Shiki drives the code views and diffs in Adea's editor and conversation surfaces,
5
+ * and its theme contract is an editor contract: a `colors` map for the chrome and a
6
+ * `settings` list of scope selectors. Neither is in the canonical schema, so both
7
+ * are derived — the chrome from the semantic roles, the scopes from the ANSI
8
+ * colours via `derive.syntaxRoles`.
9
+ *
10
+ * ## Hex, not OKLCH
11
+ *
12
+ * Shiki is the one adapter that gets hex. Its colour resolution runs through a
13
+ * syntax highlighter that does not evaluate CSS colour functions, and its output is
14
+ * used to build inline styles and a text-mate scopes map — places where an
15
+ * `oklch()` string is not a colour but a string. The conversion gamut-maps rather
16
+ * than clipping, so an out-of-gamut accent becomes a slightly less saturated real
17
+ * colour instead of a broken channel.
18
+ *
19
+ * ## Why a registration and not a bundled theme
20
+ *
21
+ * `themeToShikiTheme` returns the object; it does not call `loadTheme`, and it does
22
+ * not import Shiki. That keeps this package free of a dependency on a library that
23
+ * is an application's choice, and it means a consumer can register the result
24
+ * however it likes — synchronously, into a custom highlighter, or inside a worker.
25
+ */
26
+ import type { AdeaTheme } from '../schema';
27
+ /**
28
+ * A Shiki theme registration.
29
+ *
30
+ * Structurally compatible with `ThemeRegistration` from `shiki`; declared here so
31
+ * this package needs no dependency on it.
32
+ */
33
+ export interface ShikiThemeRegistration {
34
+ name: string;
35
+ type: 'dark' | 'light';
36
+ colors: Record<string, string>;
37
+ settings: ShikiTokenSetting[];
38
+ }
39
+ /** One entry in Shiki's `settings` list. */
40
+ export interface ShikiTokenSetting {
41
+ scope?: string | readonly string[];
42
+ settings: {
43
+ foreground?: string;
44
+ background?: string;
45
+ fontStyle?: string;
46
+ };
47
+ }
48
+ /**
49
+ * Converts a theme to a Shiki registration.
50
+ *
51
+ * Order matters in Shiki's `settings` list: later entries win, and the scopes here
52
+ * overlap by design (`keyword.operator` is claimed by both `keyword` and
53
+ * `operator`). `operator` is emitted last so its narrower claim wins, which is what
54
+ * every editor does and what makes `+` and `=` read differently from `if` and
55
+ * `return`.
56
+ */
57
+ export declare function toShikiTheme(theme: AdeaTheme): ShikiThemeRegistration;
58
+ /** Every theme as a registration list, ready for `createHighlighter({ themes })`. */
59
+ export declare function toShikiThemes(themes: readonly AdeaTheme[]): ShikiThemeRegistration[];
60
+ //# sourceMappingURL=shiki.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiki.d.ts","sourceRoot":"","sources":["../../src/adapters/shiki.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAK1C;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,QAAQ,EAAE,iBAAiB,EAAE,CAAA;CAC9B;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAyCD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,sBAAsB,CAsDrE;AAQD,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,sBAAsB,EAAE,CAEpF"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * The code adapter: a theme as a Shiki theme registration.
3
+ *
4
+ * Shiki drives the code views and diffs in Adea's editor and conversation surfaces,
5
+ * and its theme contract is an editor contract: a `colors` map for the chrome and a
6
+ * `settings` list of scope selectors. Neither is in the canonical schema, so both
7
+ * are derived — the chrome from the semantic roles, the scopes from the ANSI
8
+ * colours via `derive.syntaxRoles`.
9
+ *
10
+ * ## Hex, not OKLCH
11
+ *
12
+ * Shiki is the one adapter that gets hex. Its colour resolution runs through a
13
+ * syntax highlighter that does not evaluate CSS colour functions, and its output is
14
+ * used to build inline styles and a text-mate scopes map — places where an
15
+ * `oklch()` string is not a colour but a string. The conversion gamut-maps rather
16
+ * than clipping, so an out-of-gamut accent becomes a slightly less saturated real
17
+ * colour instead of a broken channel.
18
+ *
19
+ * ## Why a registration and not a bundled theme
20
+ *
21
+ * `themeToShikiTheme` returns the object; it does not call `loadTheme`, and it does
22
+ * not import Shiki. That keeps this package free of a dependency on a library that
23
+ * is an application's choice, and it means a consumer can register the result
24
+ * however it likes — synchronously, into a custom highlighter, or inside a worker.
25
+ */
26
+ import { syntaxRolesHex } from '../derive';
27
+ import { oklchToHex, parseColor } from '../oklch';
28
+ /**
29
+ * Syntax role → the TextMate scopes it colours.
30
+ *
31
+ * The scope lists are the ones an editor grammar actually emits, and several roles
32
+ * claim more than one scope because grammars disagree: a type annotation may arrive
33
+ * as `entity.name.type`, as `support.type`, or as `storage.type` depending on the
34
+ * language, and a theme that names only one of them renders that language
35
+ * unhighlighted. The lists are conservative — they claim the scopes that mean the
36
+ * role unambiguously, and leave the rest to the editor's default.
37
+ */
38
+ const SHIKI_SCOPES = Object.freeze({
39
+ keyword: [
40
+ 'keyword',
41
+ 'keyword.control',
42
+ 'keyword.operator.new',
43
+ 'storage',
44
+ 'storage.type',
45
+ 'storage.modifier',
46
+ ],
47
+ string: ['string', 'string.quoted', 'string.template', 'punctuation.definition.string'],
48
+ number: ['constant.numeric', 'constant.language', 'constant.other'],
49
+ comment: ['comment', 'punctuation.definition.comment'],
50
+ function: [
51
+ 'entity.name.function',
52
+ 'support.function',
53
+ 'meta.function-call',
54
+ 'variable.function',
55
+ ],
56
+ variable: ['variable', 'variable.other', 'variable.parameter', 'meta.definition.variable'],
57
+ type: ['entity.name.type', 'entity.name.class', 'support.type', 'support.class'],
58
+ tag: ['entity.name.tag', 'meta.tag'],
59
+ attribute: ['entity.other.attribute-name'],
60
+ operator: ['keyword.operator'],
61
+ heading: ['markup.heading', 'entity.name.section'],
62
+ link: ['markup.underline.link', 'string.other.link'],
63
+ constant: ['constant', 'support.constant', 'variable.language'],
64
+ punctuation: ['punctuation', 'meta.brace', 'punctuation.separator'],
65
+ });
66
+ /**
67
+ * Converts a theme to a Shiki registration.
68
+ *
69
+ * Order matters in Shiki's `settings` list: later entries win, and the scopes here
70
+ * overlap by design (`keyword.operator` is claimed by both `keyword` and
71
+ * `operator`). `operator` is emitted last so its narrower claim wins, which is what
72
+ * every editor does and what makes `+` and `=` read differently from `if` and
73
+ * `return`.
74
+ */
75
+ export function toShikiTheme(theme) {
76
+ const roles = syntaxRolesHex(theme);
77
+ const colors = {
78
+ 'editor.background': hex(theme.colors.background),
79
+ 'editor.foreground': hex(theme.colors.text),
80
+ 'editorCursor.foreground': hex(theme.cursor),
81
+ 'editor.selectionBackground': hex(theme.selection),
82
+ 'editor.lineHighlightBackground': hex(theme.colors.surface),
83
+ 'editorLineNumber.foreground': hex(theme.colors.textSubtle),
84
+ 'editorLineNumber.activeForeground': hex(theme.colors.textMuted),
85
+ 'editorIndentGuide.background': hex(theme.colors.borderMuted),
86
+ 'editorIndentGuide.activeBackground': hex(theme.colors.border),
87
+ 'editorWhitespace.foreground': hex(theme.colors.border),
88
+ 'editorWidget.background': hex(theme.colors.surfaceElevated),
89
+ 'editorWidget.border': hex(theme.colors.border),
90
+ // Diff surfaces read the semantic status roles rather than a separate scale,
91
+ // so a diff in the editor and a diff in the conversation use one palette.
92
+ 'diffEditor.insertedTextBackground': hex(theme.colors.surface),
93
+ 'diffEditor.removedTextBackground': hex(theme.colors.surface),
94
+ 'terminal.ansiRed': hex(theme.ansi.red),
95
+ 'terminal.ansiGreen': hex(theme.ansi.green),
96
+ 'terminal.ansiYellow': hex(theme.ansi.yellow),
97
+ 'terminal.ansiBlue': hex(theme.ansi.blue),
98
+ 'terminal.ansiMagenta': hex(theme.ansi.magenta),
99
+ 'terminal.ansiCyan': hex(theme.ansi.cyan),
100
+ 'terminal.ansiWhite': hex(theme.ansi.white),
101
+ };
102
+ const settings = [
103
+ { settings: { foreground: hex(theme.colors.text), background: hex(theme.colors.background) } },
104
+ ];
105
+ // Everything except `operator`, which is emitted last and narrows the claim.
106
+ for (const [role, scopes] of Object.entries(SHIKI_SCOPES)) {
107
+ if (role === 'operator')
108
+ continue;
109
+ settings.push({
110
+ scope: scopes,
111
+ settings: {
112
+ foreground: roles[role],
113
+ ...(role === 'comment' ? { fontStyle: 'italic' } : {}),
114
+ ...(role === 'heading' ? { fontStyle: 'bold' } : {}),
115
+ },
116
+ });
117
+ }
118
+ settings.push({ scope: SHIKI_SCOPES.operator, settings: { foreground: roles.operator } });
119
+ return {
120
+ name: `adea-${theme.id}`,
121
+ type: theme.appearance,
122
+ colors,
123
+ settings,
124
+ };
125
+ }
126
+ /** Formats a colour as hex, leaving it alone if it cannot be parsed. */
127
+ function hex(value) {
128
+ const parsed = parseColor(value);
129
+ return parsed ? oklchToHex(parsed) : value;
130
+ }
131
+ /** Every theme as a registration list, ready for `createHighlighter({ themes })`. */
132
+ export function toShikiThemes(themes) {
133
+ return themes.map(toShikiTheme);
134
+ }
135
+ //# sourceMappingURL=shiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiki.js","sourceRoot":"","sources":["../../src/adapters/shiki.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The Tailwind adapter.
3
+ *
4
+ * Tailwind v4 reads its design tokens from the stylesheet, not from a JavaScript
5
+ * config, so a theme becomes utilities by writing `@theme inline` declarations that
6
+ * point at the custom properties `adapters/css.ts` produces. `inline` is the
7
+ * important word: it makes Tailwind emit `var(--adea-background)` in the generated
8
+ * utility rather than baking the value in, which is what allows one stylesheet to
9
+ * serve every theme and a theme switch to cost nothing but an attribute change.
10
+ *
11
+ * Emitting this from the theme rather than hand-maintaining a `@theme` block per
12
+ * application is the point of the adapter: a new role in the schema becomes a
13
+ * utility in every consumer, and a consumer cannot silently fall behind.
14
+ */
15
+ export interface TailwindOptions {
16
+ /** The custom-property namespace the CSS adapter used. Defaults to `adea`. */
17
+ prefix?: string;
18
+ /**
19
+ * Extra utility namespaces for roles this schema does not hold but the consumer
20
+ * does, mapped to a custom property. Used to keep an application's existing
21
+ * vocabulary alive without duplicating the values.
22
+ */
23
+ extra?: Readonly<Record<string, string>>;
24
+ }
25
+ /**
26
+ * The `--color-*` namespace, which is what makes `bg-surface` a valid utility.
27
+ *
28
+ * Takes no theme, and that is the point rather than an oversight. Every declaration
29
+ * this emits is a `var()` reference, so the block is identical for all twenty-seven
30
+ * themes and one stylesheet serves every one of them. Accepting a theme would imply
31
+ * the output depended on which theme was passed, and the first person to notice would
32
+ * reasonably conclude they needed one block per theme.
33
+ */
34
+ export declare function toTailwindTheme(options?: TailwindOptions): string;
35
+ //# sourceMappingURL=tailwind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind.d.ts","sourceRoot":"","sources":["../../src/adapters/tailwind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACzC;AAMD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAoCrE"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The Tailwind adapter.
3
+ *
4
+ * Tailwind v4 reads its design tokens from the stylesheet, not from a JavaScript
5
+ * config, so a theme becomes utilities by writing `@theme inline` declarations that
6
+ * point at the custom properties `adapters/css.ts` produces. `inline` is the
7
+ * important word: it makes Tailwind emit `var(--adea-background)` in the generated
8
+ * utility rather than baking the value in, which is what allows one stylesheet to
9
+ * serve every theme and a theme switch to cost nothing but an attribute change.
10
+ *
11
+ * Emitting this from the theme rather than hand-maintaining a `@theme` block per
12
+ * application is the point of the adapter: a new role in the schema becomes a
13
+ * utility in every consumer, and a consumer cannot silently fall behind.
14
+ */
15
+ import { ANSI_KEYS, THEME_COLOR_KEYS } from '../schema';
16
+ import { STATUS_ROLES } from '../derive';
17
+ function kebab(name) {
18
+ return name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
19
+ }
20
+ /**
21
+ * The `--color-*` namespace, which is what makes `bg-surface` a valid utility.
22
+ *
23
+ * Takes no theme, and that is the point rather than an oversight. Every declaration
24
+ * this emits is a `var()` reference, so the block is identical for all twenty-seven
25
+ * themes and one stylesheet serves every one of them. Accepting a theme would imply
26
+ * the output depended on which theme was passed, and the first person to notice would
27
+ * reasonably conclude they needed one block per theme.
28
+ */
29
+ export function toTailwindTheme(options = {}) {
30
+ const prefix = options.prefix ?? 'adea';
31
+ const property = (name) => (prefix ? `--${prefix}-${name}` : `--${name}`);
32
+ const lines = [];
33
+ // `inline` so the utility resolves the variable at use time rather than at build
34
+ // time. Without it, every theme would need its own stylesheet.
35
+ lines.push('@theme inline {');
36
+ for (const role of THEME_COLOR_KEYS) {
37
+ lines.push(` --color-${kebab(role)}: var(${property(kebab(role))});`);
38
+ }
39
+ lines.push(` --color-surface-sunken: var(${property('surface-sunken')});`);
40
+ lines.push(` --color-cursor: var(${property('cursor')});`);
41
+ lines.push(` --color-selection: var(${property('selection')});`);
42
+ for (const role of STATUS_ROLES) {
43
+ lines.push(` --color-${role}-subtle: var(${property(`${role}-subtle`)});`);
44
+ lines.push(` --color-${role}-foreground: var(${property(`${role}-foreground`)});`);
45
+ }
46
+ for (let index = 1; index <= 6; index += 1) {
47
+ lines.push(` --color-chart-${index}: var(${property(`chart-${index}`)});`);
48
+ }
49
+ for (const key of ANSI_KEYS) {
50
+ lines.push(` --color-ansi-${kebab(key)}: var(${property(`ansi-${kebab(key)}`)});`);
51
+ }
52
+ for (const [name, value] of Object.entries(options.extra ?? {})) {
53
+ lines.push(` --color-${name}: ${value};`);
54
+ }
55
+ lines.push('}');
56
+ return lines.join('\n');
57
+ }
58
+ //# sourceMappingURL=tailwind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind.js","sourceRoot":"","sources":["../../src/adapters/tailwind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAaxC,SAAS,KAAK,CAAC,IAAY;IACzB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AACvE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,UAA2B,EAAE;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAA;IACvC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;IACzF,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,iFAAiF;IACjF,+DAA+D;IAC/D,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;IAE7B,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;IACxE,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,iCAAiC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,yBAAyB,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC3D,KAAK,CAAC,IAAI,CAAC,4BAA4B,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IAEjE,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,gBAAgB,QAAQ,CAAC,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,CAAA;QAC3E,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,oBAAoB,QAAQ,CAAC,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAA;IACrF,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,mBAAmB,KAAK,SAAS,QAAQ,CAAC,SAAS,KAAK,EAAE,CAAC,IAAI,CAAC,CAAA;IAC7E,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,kBAAkB,KAAK,CAAC,GAAG,CAAC,SAAS,QAAQ,CAAC,QAAQ,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;IACrF,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QAChE,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,KAAK,KAAK,GAAG,CAAC,CAAA;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The terminal adapter.
3
+ *
4
+ * xterm.js is the reason this adapter exists and the reason it works the way it
5
+ * does: its theme contract takes **hex strings only**, and it takes a
6
+ * `selectionForeground` and a `cursorAccent` that no palette publishes. Both gaps
7
+ * have to be closed here rather than at the call site, because the call site is a
8
+ * terminal emulator that will happily render invisible text if handed a
9
+ * foreground that matches its own background.
10
+ *
11
+ * The type is declared structurally rather than imported from `@xterm/xterm`. This
12
+ * package has no runtime dependencies and does not want one for a type that both
13
+ * sides already agree on; anything assignable to this shape can be passed
14
+ * straight to `terminal.options.theme`.
15
+ */
16
+ import type { AdeaTheme } from '../schema';
17
+ /**
18
+ * The shape xterm.js's `ITheme` requires.
19
+ *
20
+ * Field names are xterm's, including its `brightBlack`-style camel case, so this
21
+ * can be spread directly into `terminal.options.theme`.
22
+ */
23
+ export interface XtermTheme {
24
+ background: string;
25
+ foreground: string;
26
+ cursor: string;
27
+ cursorAccent: string;
28
+ selectionBackground: string;
29
+ selectionForeground: string;
30
+ black: string;
31
+ red: string;
32
+ green: string;
33
+ yellow: string;
34
+ blue: string;
35
+ magenta: string;
36
+ cyan: string;
37
+ white: string;
38
+ brightBlack: string;
39
+ brightRed: string;
40
+ brightGreen: string;
41
+ brightYellow: string;
42
+ brightBlue: string;
43
+ brightMagenta: string;
44
+ brightCyan: string;
45
+ brightWhite: string;
46
+ }
47
+ /**
48
+ * Converts a theme to xterm's shape.
49
+ *
50
+ * The two derived fields are the whole point:
51
+ *
52
+ * - **`cursorAccent`** is the glyph *under* the block cursor. xterm draws it in
53
+ * this colour, so it must contrast with `cursor` — not with the background.
54
+ * Palettes frequently set their cursor to the foreground colour, which puts the
55
+ * accent at the far end of the canvas.
56
+ * - **`selectionForeground`** is text inside a selection. Base24's `base02` is
57
+ * whatever the palette's author chose for a terminal selection, and several
58
+ * themes in the catalogue set it to a near-white; under near-white body text the
59
+ * selection would erase its own contents. Deriving the foreground from whichever
60
+ * of the canvas or the text measures better fixes that without discarding the
61
+ * author's selection colour.
62
+ */
63
+ export declare function toXtermTheme(theme: AdeaTheme): XtermTheme;
64
+ //# sourceMappingURL=xterm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xterm.d.ts","sourceRoot":"","sources":["../../src/adapters/xterm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAI1C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB;AAqBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAwCzD"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * The terminal adapter.
3
+ *
4
+ * xterm.js is the reason this adapter exists and the reason it works the way it
5
+ * does: its theme contract takes **hex strings only**, and it takes a
6
+ * `selectionForeground` and a `cursorAccent` that no palette publishes. Both gaps
7
+ * have to be closed here rather than at the call site, because the call site is a
8
+ * terminal emulator that will happily render invisible text if handed a
9
+ * foreground that matches its own background.
10
+ *
11
+ * The type is declared structurally rather than imported from `@xterm/xterm`. This
12
+ * package has no runtime dependencies and does not want one for a type that both
13
+ * sides already agree on; anything assignable to this shape can be passed
14
+ * straight to `terminal.options.theme`.
15
+ */
16
+ import { contrastRatio, oklchToHex, parseColor, repairContrast } from '../oklch';
17
+ /**
18
+ * The floor for text drawn inside a selection.
19
+ *
20
+ * Selected text is text, so it takes the body floor.
21
+ */
22
+ const SELECTION_FLOOR = 4.5;
23
+ /**
24
+ * The floor for the glyph under the cursor.
25
+ *
26
+ * Lower than the selection's, and deliberately. A cursor accent is a UI affordance
27
+ * rather than text, and it is derived from a colour the palette did not choose for
28
+ * this purpose: Solarized Light's cursor is a mid grey on a near-white canvas, and
29
+ * nothing in that palette reaches 4.5:1 against it except near-black, which would
30
+ * put a black glyph in a mid-grey cursor and lose the cursor's own shape. 3:1 is
31
+ * WCAG's non-text floor and is what a caret needs.
32
+ */
33
+ const CURSOR_FLOOR = 3;
34
+ /**
35
+ * Converts a theme to xterm's shape.
36
+ *
37
+ * The two derived fields are the whole point:
38
+ *
39
+ * - **`cursorAccent`** is the glyph *under* the block cursor. xterm draws it in
40
+ * this colour, so it must contrast with `cursor` — not with the background.
41
+ * Palettes frequently set their cursor to the foreground colour, which puts the
42
+ * accent at the far end of the canvas.
43
+ * - **`selectionForeground`** is text inside a selection. Base24's `base02` is
44
+ * whatever the palette's author chose for a terminal selection, and several
45
+ * themes in the catalogue set it to a near-white; under near-white body text the
46
+ * selection would erase its own contents. Deriving the foreground from whichever
47
+ * of the canvas or the text measures better fixes that without discarding the
48
+ * author's selection colour.
49
+ */
50
+ export function toXtermTheme(theme) {
51
+ const background = parseColor(theme.colors.background);
52
+ const foreground = parseColor(theme.colors.foreground);
53
+ const cursor = parseColor(theme.cursor) ?? foreground;
54
+ const selection = parseColor(theme.selection) ?? background;
55
+ // The ANSI extremes are candidates alongside the canvas and the text. On a theme
56
+ // whose cursor or selection is a mid tone, both of those measure poorly against
57
+ // it and the palette's own near-black or near-white is the right answer.
58
+ const extremes = [parseColor(theme.ansi.black), parseColor(theme.ansi.white)].filter((candidate) => !!candidate);
59
+ const candidates = [background, foreground, ...extremes].filter((candidate) => !!candidate);
60
+ return {
61
+ background: hex(theme.colors.background),
62
+ foreground: hex(theme.colors.foreground),
63
+ cursor: cursor ? oklchToHex(cursor) : hex(theme.colors.foreground),
64
+ cursorAccent: pickForeground(cursor, candidates, CURSOR_FLOOR),
65
+ selectionBackground: selection ? oklchToHex(selection) : hex(theme.colors.background),
66
+ selectionForeground: pickForeground(selection, candidates, SELECTION_FLOOR),
67
+ black: hex(theme.ansi.black),
68
+ red: hex(theme.ansi.red),
69
+ green: hex(theme.ansi.green),
70
+ yellow: hex(theme.ansi.yellow),
71
+ blue: hex(theme.ansi.blue),
72
+ magenta: hex(theme.ansi.magenta),
73
+ cyan: hex(theme.ansi.cyan),
74
+ white: hex(theme.ansi.white),
75
+ brightBlack: hex(theme.ansi.brightBlack),
76
+ brightRed: hex(theme.ansi.brightRed),
77
+ brightGreen: hex(theme.ansi.brightGreen),
78
+ brightYellow: hex(theme.ansi.brightYellow),
79
+ brightBlue: hex(theme.ansi.brightBlue),
80
+ brightMagenta: hex(theme.ansi.brightMagenta),
81
+ brightCyan: hex(theme.ansi.brightCyan),
82
+ brightWhite: hex(theme.ansi.brightWhite),
83
+ };
84
+ }
85
+ /**
86
+ * The better of the canvas and the text as a foreground for `surface`.
87
+ *
88
+ * Repaired if neither clears the floor, which happens when a palette picks a
89
+ * selection colour that sits exactly between its background and its text. The
90
+ * repair is bounded so it cannot drift into a colour the palette does not contain
91
+ * by more than a perceptible step.
92
+ */
93
+ function pickForeground(surface, candidates, floor) {
94
+ if (!surface)
95
+ return candidates[0] ? oklchToHex(candidates[0]) : '#000000';
96
+ if (candidates.length === 0)
97
+ return '#000000';
98
+ let best = candidates[0];
99
+ for (const candidate of candidates.slice(1)) {
100
+ if (contrastRatio(candidate, surface) > contrastRatio(best, surface))
101
+ best = candidate;
102
+ }
103
+ // A generous budget: the alternative to a large lightness move here is an
104
+ // invisible glyph, and the colour is already one of the palette's own.
105
+ const repaired = repairContrast(best, surface, floor, 0.9);
106
+ return oklchToHex(repaired.color);
107
+ }
108
+ function hex(value) {
109
+ const parsed = parseColor(value);
110
+ return parsed ? oklchToHex(parsed) : value;
111
+ }
112
+ //# sourceMappingURL=xterm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xterm.js","sourceRoot":"","sources":["../../src/adapters/xterm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The catalogue's public surface.
3
+ *
4
+ * The generated data is a flat, sorted array; everything a consumer wants to *do*
5
+ * with it — find one theme, group it by family, list what exists, check that a
6
+ * stored preference still names a real theme — is here.
7
+ *
8
+ * ## Lookups answer, they do not throw
9
+ *
10
+ * `getTheme` returns `undefined` rather than throwing, because the overwhelmingly
11
+ * common caller is restoring a stored preference and the overwhelmingly common
12
+ * failure is a preference naming a theme that has since been removed. A component
13
+ * that has to wrap a lookup in a try/catch to survive a stale preference is a
14
+ * component that will eventually ship without the try/catch.
15
+ */
16
+ import type { AdeaTheme, AdeaThemeRecord, ThemeAppearance, ThemeFamily } from './schema';
17
+ import type { Base24Scheme } from './adapters/base24';
18
+ /**
19
+ * Every theme in the catalogue, ordered by id.
20
+ *
21
+ * The full records, including provenance and tags, because a picker needs the
22
+ * labels and an audit needs the licences. Consumers that only want the theme
23
+ * contract can treat each entry as an {@link AdeaTheme}; the extra keys are
24
+ * additive.
25
+ */
26
+ export declare const themes: readonly AdeaThemeRecord[];
27
+ /** The default theme for each appearance. */
28
+ export declare const DEFAULT_THEME_IDS: Readonly<Record<ThemeAppearance, string>>;
29
+ /** Looks a theme up by id. Returns `undefined` for an id the catalogue does not have. */
30
+ export declare function getTheme(id: string): AdeaThemeRecord | undefined;
31
+ /**
32
+ * Looks a theme up, falling back to the default for an appearance.
33
+ *
34
+ * This is the function a preference restore should call: a stored id that no longer
35
+ * exists resolves to the default rather than to nothing, so removing a theme from
36
+ * the catalogue degrades to a theme change instead of a blank window.
37
+ */
38
+ export declare function resolveTheme(id: string | undefined | null, appearance: ThemeAppearance): AdeaThemeRecord;
39
+ /** True when the catalogue still contains this id. */
40
+ export declare function hasTheme(id: string): boolean;
41
+ /**
42
+ * The themes grouped by project, in the order the source list declares.
43
+ *
44
+ * Grouping is by family rather than by appearance so that a picker can show
45
+ * "Catppuccin: Latte, Frappé, Macchiato, Mocha" the way the project itself presents
46
+ * its flavours, which is how someone who wants Mocha looks for it.
47
+ */
48
+ export declare function themeFamilies(themesList?: readonly AdeaThemeRecord[]): ThemeFamily[];
49
+ /** The catalogued themes that match an appearance. */
50
+ export declare function themesByAppearance(appearance: ThemeAppearance, themesList?: readonly AdeaThemeRecord[]): AdeaThemeRecord[];
51
+ /**
52
+ * The vendored Base24 scheme for a theme, untouched.
53
+ *
54
+ * Present for interop and for licence auditing. `toBase24()` writes a scheme too,
55
+ * but it has to re-derive Base24's orange and brown slots — the canonical schema
56
+ * has no role for them — so it is not a byte-exact round trip. This returns the
57
+ * artefact as it was reproduced, so a caller that needs exactness has it.
58
+ */
59
+ export declare function getBase24Scheme(id: string): Base24Scheme | undefined;
60
+ /** The ids of every theme, for validation and for a preference guard. */
61
+ export declare function themeIds(): string[];
62
+ /** The catalogue's size, so a test can assert it without importing the data. */
63
+ export declare function themeCount(): number;
64
+ /** Narrows a theme record to the published contract, dropping catalogue metadata. */
65
+ export declare function toTheme(record: AdeaThemeRecord): AdeaTheme;
66
+ //# sourceMappingURL=catalogue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue.d.ts","sourceRoot":"","sources":["../src/catalogue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIrD;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,eAAe,EAAoB,CAAA;AAEjE,6CAA6C;AAC7C,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAGtE,CAAA;AAEF,yFAAyF;AACzF,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC7B,UAAU,EAAE,eAAe,GAC1B,eAAe,CAIjB;AAED,sDAAsD;AACtD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,GAAE,SAAS,eAAe,EAAoB,GAAG,WAAW,EAAE,CAerG;AAED,sDAAsD;AACtD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,eAAe,EAC3B,UAAU,GAAE,SAAS,eAAe,EAAoB,GACvD,eAAe,EAAE,CAEnB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEpE;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,IAAI,MAAM,EAAE,CAEnC;AAED,gFAAgF;AAChF,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAU1D"}