@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.
- package/LICENSE +201 -0
- package/NOTICE +110 -0
- package/README.md +157 -0
- package/dist/adapters/base24.d.ts +117 -0
- package/dist/adapters/base24.d.ts.map +1 -0
- package/dist/adapters/base24.js +313 -0
- package/dist/adapters/base24.js.map +1 -0
- package/dist/adapters/css.d.ts +68 -0
- package/dist/adapters/css.d.ts.map +1 -0
- package/dist/adapters/css.js +107 -0
- package/dist/adapters/css.js.map +1 -0
- package/dist/adapters/shadcn.d.ts +43 -0
- package/dist/adapters/shadcn.d.ts.map +1 -0
- package/dist/adapters/shadcn.js +89 -0
- package/dist/adapters/shadcn.js.map +1 -0
- package/dist/adapters/shiki.d.ts +60 -0
- package/dist/adapters/shiki.d.ts.map +1 -0
- package/dist/adapters/shiki.js +135 -0
- package/dist/adapters/shiki.js.map +1 -0
- package/dist/adapters/tailwind.d.ts +35 -0
- package/dist/adapters/tailwind.d.ts.map +1 -0
- package/dist/adapters/tailwind.js +58 -0
- package/dist/adapters/tailwind.js.map +1 -0
- package/dist/adapters/xterm.d.ts +64 -0
- package/dist/adapters/xterm.d.ts.map +1 -0
- package/dist/adapters/xterm.js +112 -0
- package/dist/adapters/xterm.js.map +1 -0
- package/dist/catalogue.d.ts +66 -0
- package/dist/catalogue.d.ts.map +1 -0
- package/dist/catalogue.js +110 -0
- package/dist/catalogue.js.map +1 -0
- package/dist/derive.d.ts +89 -0
- package/dist/derive.d.ts.map +1 -0
- package/dist/derive.js +165 -0
- package/dist/derive.js.map +1 -0
- package/dist/generated/schemes.d.ts +16 -0
- package/dist/generated/schemes.d.ts.map +1 -0
- package/dist/generated/schemes.js +880 -0
- package/dist/generated/schemes.js.map +1 -0
- package/dist/generated/themes.d.ts +11 -0
- package/dist/generated/themes.d.ts.map +1 -0
- package/dist/generated/themes.js +1650 -0
- package/dist/generated/themes.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/normalize.d.ts +160 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +795 -0
- package/dist/normalize.js.map +1 -0
- package/dist/oklch.d.ts +141 -0
- package/dist/oklch.d.ts.map +1 -0
- package/dist/oklch.js +306 -0
- package/dist/oklch.js.map +1 -0
- package/dist/schema.d.ts +178 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +69 -0
- package/dist/schema.js.map +1 -0
- package/dist/sources.d.ts +171 -0
- package/dist/sources.d.ts.map +1 -0
- package/dist/sources.js +559 -0
- package/dist/sources.js.map +1 -0
- package/dist/validate.d.ts +121 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +255 -0
- package/dist/validate.js.map +1 -0
- package/package.json +106 -0
- package/palettes/ayu-light.json +38 -0
- package/palettes/ayu-mirage.json +38 -0
- package/palettes/ayu.json +38 -0
- package/palettes/catppuccin-frappe.json +38 -0
- package/palettes/catppuccin-latte.json +38 -0
- package/palettes/catppuccin-macchiato.json +38 -0
- package/palettes/catppuccin-mocha.json +38 -0
- package/palettes/dracula.json +38 -0
- package/palettes/everforest-dark.json +38 -0
- package/palettes/everforest-light.json +38 -0
- package/palettes/gruvbox-dark.json +38 -0
- package/palettes/gruvbox-light.json +38 -0
- package/palettes/kanagawa.json +38 -0
- package/palettes/monokai.json +38 -0
- package/palettes/nord.json +38 -0
- package/palettes/one-dark.json +38 -0
- package/palettes/rosepine-dawn.json +38 -0
- package/palettes/rosepine-moon.json +38 -0
- package/palettes/rosepine.json +38 -0
- package/palettes/solarized-dark.json +38 -0
- package/palettes/solarized-light.json +38 -0
- package/palettes/tokyonight-day.json +38 -0
- package/palettes/tokyonight-night.json +38 -0
- package/palettes/tokyonight-storm.json +38 -0
- package/palettes/vesper.json +38 -0
- package/src/adapters/base24.ts +355 -0
- package/src/adapters/css.ts +149 -0
- package/src/adapters/shadcn.ts +99 -0
- package/src/adapters/shiki.ts +168 -0
- package/src/adapters/tailwind.ts +79 -0
- package/src/adapters/xterm.ts +159 -0
- package/src/catalogue.ts +129 -0
- package/src/derive.ts +203 -0
- package/src/generated/schemes.ts +882 -0
- package/src/generated/themes.ts +1652 -0
- package/src/index.ts +146 -0
- package/src/normalize.ts +1010 -0
- package/src/oklch.ts +366 -0
- package/src/schema.ts +222 -0
- package/src/sources.ts +682 -0
- package/src/validate.ts +325 -0
|
@@ -0,0 +1,110 @@
|
|
|
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 { generatedSchemes } from './generated/schemes';
|
|
17
|
+
import { generatedThemes } from './generated/themes';
|
|
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 const themes = generatedThemes;
|
|
27
|
+
/** The default theme for each appearance. */
|
|
28
|
+
export const DEFAULT_THEME_IDS = Object.freeze({
|
|
29
|
+
dark: 'adea-dark',
|
|
30
|
+
light: 'adea-light',
|
|
31
|
+
});
|
|
32
|
+
/** Looks a theme up by id. Returns `undefined` for an id the catalogue does not have. */
|
|
33
|
+
export function getTheme(id) {
|
|
34
|
+
return generatedThemes.find((theme) => theme.id === id);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Looks a theme up, falling back to the default for an appearance.
|
|
38
|
+
*
|
|
39
|
+
* This is the function a preference restore should call: a stored id that no longer
|
|
40
|
+
* exists resolves to the default rather than to nothing, so removing a theme from
|
|
41
|
+
* the catalogue degrades to a theme change instead of a blank window.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveTheme(id, appearance) {
|
|
44
|
+
const found = id ? getTheme(id) : undefined;
|
|
45
|
+
if (found)
|
|
46
|
+
return found;
|
|
47
|
+
return getTheme(DEFAULT_THEME_IDS[appearance]);
|
|
48
|
+
}
|
|
49
|
+
/** True when the catalogue still contains this id. */
|
|
50
|
+
export function hasTheme(id) {
|
|
51
|
+
return generatedThemes.some((theme) => theme.id === id);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The themes grouped by project, in the order the source list declares.
|
|
55
|
+
*
|
|
56
|
+
* Grouping is by family rather than by appearance so that a picker can show
|
|
57
|
+
* "Catppuccin: Latte, Frappé, Macchiato, Mocha" the way the project itself presents
|
|
58
|
+
* its flavours, which is how someone who wants Mocha looks for it.
|
|
59
|
+
*/
|
|
60
|
+
export function themeFamilies(themesList = generatedThemes) {
|
|
61
|
+
const families = [];
|
|
62
|
+
const index = new Map();
|
|
63
|
+
for (const theme of themesList) {
|
|
64
|
+
let family = index.get(theme.family);
|
|
65
|
+
if (!family) {
|
|
66
|
+
family = { id: theme.family, label: theme.familyLabel, themes: [] };
|
|
67
|
+
index.set(theme.family, family);
|
|
68
|
+
families.push(family);
|
|
69
|
+
}
|
|
70
|
+
;
|
|
71
|
+
family.themes.push(theme);
|
|
72
|
+
}
|
|
73
|
+
return families;
|
|
74
|
+
}
|
|
75
|
+
/** The catalogued themes that match an appearance. */
|
|
76
|
+
export function themesByAppearance(appearance, themesList = generatedThemes) {
|
|
77
|
+
return themesList.filter((theme) => theme.appearance === appearance);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The vendored Base24 scheme for a theme, untouched.
|
|
81
|
+
*
|
|
82
|
+
* Present for interop and for licence auditing. `toBase24()` writes a scheme too,
|
|
83
|
+
* but it has to re-derive Base24's orange and brown slots — the canonical schema
|
|
84
|
+
* has no role for them — so it is not a byte-exact round trip. This returns the
|
|
85
|
+
* artefact as it was reproduced, so a caller that needs exactness has it.
|
|
86
|
+
*/
|
|
87
|
+
export function getBase24Scheme(id) {
|
|
88
|
+
return generatedSchemes[id];
|
|
89
|
+
}
|
|
90
|
+
/** The ids of every theme, for validation and for a preference guard. */
|
|
91
|
+
export function themeIds() {
|
|
92
|
+
return generatedThemes.map((theme) => theme.id);
|
|
93
|
+
}
|
|
94
|
+
/** The catalogue's size, so a test can assert it without importing the data. */
|
|
95
|
+
export function themeCount() {
|
|
96
|
+
return generatedThemes.length;
|
|
97
|
+
}
|
|
98
|
+
/** Narrows a theme record to the published contract, dropping catalogue metadata. */
|
|
99
|
+
export function toTheme(record) {
|
|
100
|
+
return {
|
|
101
|
+
id: record.id,
|
|
102
|
+
name: record.name,
|
|
103
|
+
appearance: record.appearance,
|
|
104
|
+
colors: record.colors,
|
|
105
|
+
ansi: record.ansi,
|
|
106
|
+
cursor: record.cursor,
|
|
107
|
+
selection: record.selection,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=catalogue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../src/catalogue.ts"],"names":[],"mappings":"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"}
|
package/dist/derive.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colours a theme does not store, derived from the ones it does.
|
|
3
|
+
*
|
|
4
|
+
* The canonical schema is deliberately small — seventeen surface roles, sixteen
|
|
5
|
+
* ANSI colours, a cursor and a selection — and three things an application needs
|
|
6
|
+
* are deliberately absent from it: the **syntax roles** a code view wants, the
|
|
7
|
+
* **chart series** a graph wants, and the **fills and foregrounds** a status chip
|
|
8
|
+
* wants.
|
|
9
|
+
*
|
|
10
|
+
* They are absent because each is a *function of* the roles that are present, and a
|
|
11
|
+
* schema that stored them would be a schema with three ways to say the same thing.
|
|
12
|
+
* Deriving them here, once, means every consumer derives them identically — the
|
|
13
|
+
* alternative, which this replaces, is each application inventing its own mapping
|
|
14
|
+
* and two applications disagreeing about what colour a type is.
|
|
15
|
+
*
|
|
16
|
+
* ## Why syntax roles come from ANSI
|
|
17
|
+
*
|
|
18
|
+
* A theme carries no syntax palette, and Base24's editor-oriented slots are not
|
|
19
|
+
* one: `base08` is documented as "variables" and `base0D` as "functions", but every
|
|
20
|
+
* palette's author filled those slots for a terminal, where the question is "what
|
|
21
|
+
* colour is `ls` output". Importing them as syntax roles would colour a diff by
|
|
22
|
+
* accident.
|
|
23
|
+
*
|
|
24
|
+
* Reading the roles off the sixteen ANSI colours instead has a property worth more
|
|
25
|
+
* than per-theme tuning: the ANSI set is the part of a palette its author *did*
|
|
26
|
+
* choose carefully, every theme in the catalogue has one, and it is already tuned
|
|
27
|
+
* for legibility against that theme's background — the same requirement a code view
|
|
28
|
+
* has. It is the reasoning that makes `ls --color` readable in each of these
|
|
29
|
+
* palettes today.
|
|
30
|
+
*/
|
|
31
|
+
import type { AdeaTheme } from './schema';
|
|
32
|
+
/** The syntax roles, matching the names Shiki's theme contract expects. */
|
|
33
|
+
export type SyntaxRole = 'keyword' | 'string' | 'number' | 'comment' | 'function' | 'variable' | 'type' | 'tag' | 'attribute' | 'operator' | 'heading' | 'link' | 'constant' | 'punctuation';
|
|
34
|
+
/**
|
|
35
|
+
* The syntax palette for a theme, as OKLCH strings.
|
|
36
|
+
*
|
|
37
|
+
* Comment and punctuation are additionally measured against the canvas: a
|
|
38
|
+
* palette's dim grey is chosen to be read against its *terminal* background, which
|
|
39
|
+
* is the same colour as the canvas here, so the measurement normally passes — but
|
|
40
|
+
* when it does not, the same minimal lightness repair the semantic roles use is
|
|
41
|
+
* applied rather than leaving unreadable comments.
|
|
42
|
+
*/
|
|
43
|
+
export declare function syntaxRoles(theme: AdeaTheme, options?: {
|
|
44
|
+
commentFloor?: number;
|
|
45
|
+
}): Record<SyntaxRole, string>;
|
|
46
|
+
/** The syntax palette as hex, for engines that cannot evaluate `oklch()`. */
|
|
47
|
+
export declare function syntaxRolesHex(theme: AdeaTheme): Record<SyntaxRole, string>;
|
|
48
|
+
/**
|
|
49
|
+
* The categorical chart series, derived from the ANSI hues.
|
|
50
|
+
*
|
|
51
|
+
* Six series, taken in the order that keeps adjacent ones furthest apart on the
|
|
52
|
+
* hue circle: blue, magenta, cyan, green, yellow, red. A palette's own ordering
|
|
53
|
+
* would put red next to green, which is the pair a pie chart most needs to
|
|
54
|
+
* separate. Constant across themes by construction — every theme has these six
|
|
55
|
+
* slots — so a chart's series colours mean the same thing in every theme.
|
|
56
|
+
*/
|
|
57
|
+
export declare const CHART_SERIES: readonly (keyof AdeaTheme['ansi'])[];
|
|
58
|
+
/** The chart series for a theme, as OKLCH strings. */
|
|
59
|
+
export declare function chartSeries(theme: AdeaTheme): readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* A tint of a role for use as a background behind text of that role.
|
|
62
|
+
*
|
|
63
|
+
* Status chips need a fill that is the role's colour at low strength, and doing
|
|
64
|
+
* that with alpha would put a translucent colour into a token whose contrast was
|
|
65
|
+
* measured as opaque — and a translucent fill's real contrast depends on whatever
|
|
66
|
+
* happens to be behind it. Blending toward the canvas in OKLCH keeps the result
|
|
67
|
+
* opaque, so the chip's own text pairing can be asserted like any other.
|
|
68
|
+
*/
|
|
69
|
+
export declare function tint(color: string, background: string, amount?: number): string;
|
|
70
|
+
/** The four status roles, in the order an alert stack shows them. */
|
|
71
|
+
export declare const STATUS_ROLES: readonly ["success", "warning", "error", "info"];
|
|
72
|
+
export type StatusRole = (typeof STATUS_ROLES)[number];
|
|
73
|
+
/**
|
|
74
|
+
* The text colour to draw on a **solid** fill of a status role.
|
|
75
|
+
*
|
|
76
|
+
* Not the appearance's `foreground`, which is the shortcut and is wrong half the
|
|
77
|
+
* time: a dark theme's foreground is near-white, and near-white on AdEA Dark's
|
|
78
|
+
* `success` at `#3fb950` measures 2.6:1 — illegible. The correct answer is
|
|
79
|
+
* whichever of the theme's two extremes measures better against the fill, which
|
|
80
|
+
* for a bright green is black and for a deep red is white.
|
|
81
|
+
*
|
|
82
|
+
* Where neither clears the floor — a mid-tone fill, which some palettes have —
|
|
83
|
+
* the better of the two is returned and {@link validateTheme} is what reports the
|
|
84
|
+
* pairing as failing, rather than a silent blend being substituted here.
|
|
85
|
+
*/
|
|
86
|
+
export declare function statusForeground(theme: AdeaTheme, role: StatusRole): string;
|
|
87
|
+
/** {@link statusForeground} as hex, for engines that cannot evaluate `oklch()`. */
|
|
88
|
+
export declare function statusForegroundHex(theme: AdeaTheme, role: StatusRole): string;
|
|
89
|
+
//# sourceMappingURL=derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAIzC,2EAA2E;AAC3E,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,UAAU,GACV,UAAU,GACV,MAAM,GACN,KAAK,GACL,WAAW,GACX,UAAU,GACV,SAAS,GACT,MAAM,GACN,UAAU,GACV,aAAa,CAAA;AAwBjB;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CA+B5B;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAQ3E;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC,EAOlD,CAAA;AAEX,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,MAAM,EAAE,CAE/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,MAAM,CAK7E;AAED,qEAAqE;AACrE,eAAO,MAAM,YAAY,kDAAmD,CAAA;AAC5E,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAEtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAS3E;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAG9E"}
|
package/dist/derive.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colours a theme does not store, derived from the ones it does.
|
|
3
|
+
*
|
|
4
|
+
* The canonical schema is deliberately small — seventeen surface roles, sixteen
|
|
5
|
+
* ANSI colours, a cursor and a selection — and three things an application needs
|
|
6
|
+
* are deliberately absent from it: the **syntax roles** a code view wants, the
|
|
7
|
+
* **chart series** a graph wants, and the **fills and foregrounds** a status chip
|
|
8
|
+
* wants.
|
|
9
|
+
*
|
|
10
|
+
* They are absent because each is a *function of* the roles that are present, and a
|
|
11
|
+
* schema that stored them would be a schema with three ways to say the same thing.
|
|
12
|
+
* Deriving them here, once, means every consumer derives them identically — the
|
|
13
|
+
* alternative, which this replaces, is each application inventing its own mapping
|
|
14
|
+
* and two applications disagreeing about what colour a type is.
|
|
15
|
+
*
|
|
16
|
+
* ## Why syntax roles come from ANSI
|
|
17
|
+
*
|
|
18
|
+
* A theme carries no syntax palette, and Base24's editor-oriented slots are not
|
|
19
|
+
* one: `base08` is documented as "variables" and `base0D` as "functions", but every
|
|
20
|
+
* palette's author filled those slots for a terminal, where the question is "what
|
|
21
|
+
* colour is `ls` output". Importing them as syntax roles would colour a diff by
|
|
22
|
+
* accident.
|
|
23
|
+
*
|
|
24
|
+
* Reading the roles off the sixteen ANSI colours instead has a property worth more
|
|
25
|
+
* than per-theme tuning: the ANSI set is the part of a palette its author *did*
|
|
26
|
+
* choose carefully, every theme in the catalogue has one, and it is already tuned
|
|
27
|
+
* for legibility against that theme's background — the same requirement a code view
|
|
28
|
+
* has. It is the reasoning that makes `ls --color` readable in each of these
|
|
29
|
+
* palettes today.
|
|
30
|
+
*/
|
|
31
|
+
import { contrastRatio, formatOklch, mix, oklchToHex, parseColor, repairContrast } from './oklch';
|
|
32
|
+
/** Which ANSI role each syntax role is read from, and why. */
|
|
33
|
+
const SYNTAX_SOURCE = Object.freeze({
|
|
34
|
+
// Magenta is the slot palettes spend on the most distinctive hue they have, and
|
|
35
|
+
// a keyword is the token most worth making distinctive.
|
|
36
|
+
keyword: 'magenta',
|
|
37
|
+
string: 'green',
|
|
38
|
+
number: 'yellow',
|
|
39
|
+
// The dim grey every palette defines specifically for text it wants present but
|
|
40
|
+
// quiet. Comments are the one role where "quiet" is the requirement.
|
|
41
|
+
comment: 'brightBlack',
|
|
42
|
+
function: 'blue',
|
|
43
|
+
variable: 'white',
|
|
44
|
+
type: 'cyan',
|
|
45
|
+
tag: 'red',
|
|
46
|
+
attribute: 'yellow',
|
|
47
|
+
operator: 'white',
|
|
48
|
+
heading: 'magenta',
|
|
49
|
+
link: 'blue',
|
|
50
|
+
constant: 'brightMagenta',
|
|
51
|
+
punctuation: 'brightBlack',
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* The syntax palette for a theme, as OKLCH strings.
|
|
55
|
+
*
|
|
56
|
+
* Comment and punctuation are additionally measured against the canvas: a
|
|
57
|
+
* palette's dim grey is chosen to be read against its *terminal* background, which
|
|
58
|
+
* is the same colour as the canvas here, so the measurement normally passes — but
|
|
59
|
+
* when it does not, the same minimal lightness repair the semantic roles use is
|
|
60
|
+
* applied rather than leaving unreadable comments.
|
|
61
|
+
*/
|
|
62
|
+
export function syntaxRoles(theme, options = {}) {
|
|
63
|
+
const background = parseColor(theme.colors.background);
|
|
64
|
+
/**
|
|
65
|
+
* Comments and punctuation are held to a *visibility* floor rather than a
|
|
66
|
+
* legibility one.
|
|
67
|
+
*
|
|
68
|
+
* Their whole purpose is to be the faintest thing on screen, and every palette
|
|
69
|
+
* here defines its comment colour that way on purpose: Everforest Light's is
|
|
70
|
+
* 1.9:1 against its own canvas. Holding them to 3:1 could only be satisfied by
|
|
71
|
+
* promoting comments to the body text's colour, which inverts the role — and the
|
|
72
|
+
* floors that *do* carry text are measured separately and are not relaxed.
|
|
73
|
+
*/
|
|
74
|
+
const floor = options.commentFloor ?? 2;
|
|
75
|
+
const roles = {};
|
|
76
|
+
for (const [role, source] of Object.entries(SYNTAX_SOURCE)) {
|
|
77
|
+
const value = parseColor(theme.ansi[source]);
|
|
78
|
+
if (!value)
|
|
79
|
+
continue;
|
|
80
|
+
if (role === 'comment' || role === 'punctuation') {
|
|
81
|
+
const repaired = background
|
|
82
|
+
? repairContrast(value, background, floor, 0.3)
|
|
83
|
+
: { color: value };
|
|
84
|
+
roles[role] = formatOklch(repaired.color);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
roles[role] = formatOklch(value);
|
|
88
|
+
}
|
|
89
|
+
return roles;
|
|
90
|
+
}
|
|
91
|
+
/** The syntax palette as hex, for engines that cannot evaluate `oklch()`. */
|
|
92
|
+
export function syntaxRolesHex(theme) {
|
|
93
|
+
const roles = syntaxRoles(theme);
|
|
94
|
+
return Object.fromEntries(Object.entries(roles).map(([role, value]) => {
|
|
95
|
+
const parsed = parseColor(value);
|
|
96
|
+
return [role, parsed ? oklchToHex(parsed) : value];
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The categorical chart series, derived from the ANSI hues.
|
|
101
|
+
*
|
|
102
|
+
* Six series, taken in the order that keeps adjacent ones furthest apart on the
|
|
103
|
+
* hue circle: blue, magenta, cyan, green, yellow, red. A palette's own ordering
|
|
104
|
+
* would put red next to green, which is the pair a pie chart most needs to
|
|
105
|
+
* separate. Constant across themes by construction — every theme has these six
|
|
106
|
+
* slots — so a chart's series colours mean the same thing in every theme.
|
|
107
|
+
*/
|
|
108
|
+
export const CHART_SERIES = Object.freeze([
|
|
109
|
+
'blue',
|
|
110
|
+
'magenta',
|
|
111
|
+
'cyan',
|
|
112
|
+
'green',
|
|
113
|
+
'yellow',
|
|
114
|
+
'red',
|
|
115
|
+
]);
|
|
116
|
+
/** The chart series for a theme, as OKLCH strings. */
|
|
117
|
+
export function chartSeries(theme) {
|
|
118
|
+
return CHART_SERIES.map((role) => formatOklch(parseColor(theme.ansi[role])));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A tint of a role for use as a background behind text of that role.
|
|
122
|
+
*
|
|
123
|
+
* Status chips need a fill that is the role's colour at low strength, and doing
|
|
124
|
+
* that with alpha would put a translucent colour into a token whose contrast was
|
|
125
|
+
* measured as opaque — and a translucent fill's real contrast depends on whatever
|
|
126
|
+
* happens to be behind it. Blending toward the canvas in OKLCH keeps the result
|
|
127
|
+
* opaque, so the chip's own text pairing can be asserted like any other.
|
|
128
|
+
*/
|
|
129
|
+
export function tint(color, background, amount = 0.14) {
|
|
130
|
+
const foreground = parseColor(color);
|
|
131
|
+
const canvas = parseColor(background);
|
|
132
|
+
if (!foreground || !canvas)
|
|
133
|
+
return color;
|
|
134
|
+
return formatOklch(mix(canvas, foreground, amount));
|
|
135
|
+
}
|
|
136
|
+
/** The four status roles, in the order an alert stack shows them. */
|
|
137
|
+
export const STATUS_ROLES = ['success', 'warning', 'error', 'info'];
|
|
138
|
+
/**
|
|
139
|
+
* The text colour to draw on a **solid** fill of a status role.
|
|
140
|
+
*
|
|
141
|
+
* Not the appearance's `foreground`, which is the shortcut and is wrong half the
|
|
142
|
+
* time: a dark theme's foreground is near-white, and near-white on AdEA Dark's
|
|
143
|
+
* `success` at `#3fb950` measures 2.6:1 — illegible. The correct answer is
|
|
144
|
+
* whichever of the theme's two extremes measures better against the fill, which
|
|
145
|
+
* for a bright green is black and for a deep red is white.
|
|
146
|
+
*
|
|
147
|
+
* Where neither clears the floor — a mid-tone fill, which some palettes have —
|
|
148
|
+
* the better of the two is returned and {@link validateTheme} is what reports the
|
|
149
|
+
* pairing as failing, rather than a silent blend being substituted here.
|
|
150
|
+
*/
|
|
151
|
+
export function statusForeground(theme, role) {
|
|
152
|
+
const fill = parseColor(theme.colors[role]);
|
|
153
|
+
const background = parseColor(theme.colors.background);
|
|
154
|
+
const foreground = parseColor(theme.colors.foreground);
|
|
155
|
+
if (!fill || !background || !foreground)
|
|
156
|
+
return theme.colors.foreground;
|
|
157
|
+
const best = contrastRatio(foreground, fill) >= contrastRatio(background, fill) ? foreground : background;
|
|
158
|
+
return formatOklch(best);
|
|
159
|
+
}
|
|
160
|
+
/** {@link statusForeground} as hex, for engines that cannot evaluate `oklch()`. */
|
|
161
|
+
export function statusForegroundHex(theme, role) {
|
|
162
|
+
const parsed = parseColor(statusForeground(theme, role));
|
|
163
|
+
return parsed ? oklchToHex(parsed) : theme.colors.foreground;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated by `bun run catalogue:build`. Do not edit.
|
|
3
|
+
*
|
|
4
|
+
* The catalogue is committed so that a palette change is a reviewable diff, and
|
|
5
|
+
* `bun run catalogue:check` fails the build when this file and its inputs
|
|
6
|
+
* disagree.
|
|
7
|
+
*/
|
|
8
|
+
import type { Base24Scheme } from '../adapters/base24';
|
|
9
|
+
/**
|
|
10
|
+
* The Base24 schemes as they were vendored, keyed by theme id.
|
|
11
|
+
*
|
|
12
|
+
* Untouched, so a consumer that needs the original bytes — an interop export, a
|
|
13
|
+
* licence audit — gets them rather than a re-derivation.
|
|
14
|
+
*/
|
|
15
|
+
export declare const generatedSchemes: Readonly<Record<string, Base24Scheme>>;
|
|
16
|
+
//# sourceMappingURL=schemes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemes.d.ts","sourceRoot":"","sources":["../../src/generated/schemes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAi2BlE,CAAA"}
|