@sltsh/aion-tokens 0.3.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/dist/flatten.d.ts +2 -0
  4. package/dist/flatten.d.ts.map +1 -0
  5. package/dist/flatten.js +18 -0
  6. package/dist/flatten.js.map +1 -0
  7. package/dist/index.d.ts +21 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +12 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/light.d.ts +21 -0
  12. package/dist/light.d.ts.map +1 -0
  13. package/dist/light.js +72 -0
  14. package/dist/light.js.map +1 -0
  15. package/dist/oklch.d.ts +14 -0
  16. package/dist/oklch.d.ts.map +1 -0
  17. package/dist/oklch.js +86 -0
  18. package/dist/oklch.js.map +1 -0
  19. package/dist/palette.d.ts +155 -0
  20. package/dist/palette.d.ts.map +1 -0
  21. package/dist/palette.js +179 -0
  22. package/dist/palette.js.map +1 -0
  23. package/dist/preview.d.ts +29 -0
  24. package/dist/preview.d.ts.map +1 -0
  25. package/dist/preview.js +72 -0
  26. package/dist/preview.js.map +1 -0
  27. package/dist/report.d.ts +17 -0
  28. package/dist/report.d.ts.map +1 -0
  29. package/dist/report.js +279 -0
  30. package/dist/report.js.map +1 -0
  31. package/dist/rivals.d.ts +22 -0
  32. package/dist/rivals.d.ts.map +1 -0
  33. package/dist/rivals.js +83 -0
  34. package/dist/rivals.js.map +1 -0
  35. package/dist/semantic.d.ts +203 -0
  36. package/dist/semantic.d.ts.map +1 -0
  37. package/dist/semantic.js +99 -0
  38. package/dist/semantic.js.map +1 -0
  39. package/dist/solve.d.ts +44 -0
  40. package/dist/solve.d.ts.map +1 -0
  41. package/dist/solve.js +59 -0
  42. package/dist/solve.js.map +1 -0
  43. package/dist/states.d.ts +28 -0
  44. package/dist/states.d.ts.map +1 -0
  45. package/dist/states.js +89 -0
  46. package/dist/states.js.map +1 -0
  47. package/dist/status.d.ts +12 -0
  48. package/dist/status.d.ts.map +1 -0
  49. package/dist/status.js +12 -0
  50. package/dist/status.js.map +1 -0
  51. package/dist/util.d.ts +2 -0
  52. package/dist/util.d.ts.map +1 -0
  53. package/dist/util.js +2 -0
  54. package/dist/util.js.map +1 -0
  55. package/dist/verify.d.ts +2 -0
  56. package/dist/verify.d.ts.map +1 -0
  57. package/dist/verify.js +42 -0
  58. package/dist/verify.js.map +1 -0
  59. package/package.json +40 -0
@@ -0,0 +1,179 @@
1
+ import { mapValues } from './util.js';
2
+ export const BASE_HUE = 264;
3
+ export const BASE_CHROMA = 0.016;
4
+ // `hairline` and `divider` are decorative: they separate regions that already read as
5
+ // separate. `border` draws the edge of a control a keyboard user must find, so it clears
6
+ // the 3:1 non-text floor on `input`, the lightest surface a control sits on.
7
+ export const NEUTRAL_LIGHTNESS = {
8
+ editor: 0.195, terminal: 0.207, sidebar: 0.220, widget: 0.250,
9
+ input: 0.285, hover: 0.320, hairline: 0.375, divider: 0.440,
10
+ border: 0.560, muted: 0.580, textSecondary: 0.745, textPrimary: 0.930,
11
+ };
12
+ export const neutral = mapValues(NEUTRAL_LIGHTNESS, (L) => [L, BASE_CHROMA, BASE_HUE]);
13
+ export const ACCENTS = {
14
+ coral: [0.720, 0.1337, 22],
15
+ copper: [0.745, 0.1238, 52],
16
+ gold: [0.820, 0.1287, 90],
17
+ green: [0.780, 0.1287, 148],
18
+ teal: [0.780, 0.1040, 192],
19
+ blue: [0.755, 0.1139, 255],
20
+ violet: [0.740, 0.1238, 305],
21
+ };
22
+ export const ACCENT_NAMES = Object.keys(ACCENTS);
23
+ export const ACCENT_SUBTLE_LIGHTNESS = 0.255;
24
+ export const ACCENT_BORDER_LIGHTNESS = 0.520;
25
+ export const scaleOf = ([L, C, H]) => ({
26
+ subtle: [ACCENT_SUBTLE_LIGHTNESS, Math.min(C * 0.42, 0.055), H],
27
+ border: [ACCENT_BORDER_LIGHTNESS, Math.min(C * 0.62, 0.085), H],
28
+ solid: [L, C, H],
29
+ });
30
+ export const accentScale = (name) => scaleOf(ACCENTS[name]);
31
+ export const SYNTAX = {
32
+ variable: 'coral', number: 'copper', constant: 'copper', type: 'gold',
33
+ string: 'green', operator: 'teal', escape: 'teal', function: 'blue', keyword: 'violet',
34
+ };
35
+ export const ONE_DARK_PRO_HUE = {
36
+ variable: 17.0, number: 63.8, constant: 63.8, type: 82.3,
37
+ string: 133.0, operator: 206.3, escape: 206.3, function: 245.3, keyword: 318.2,
38
+ };
39
+ // The comment is the dimmest thing a reader has to read, so it sets the budget for every
40
+ // decoration that can sit under it. It is solved against the lightest supported stack —
41
+ // a selected word inside a highlighted current line — not against the plain editor.
42
+ //
43
+ // It stops one notch under `variable`, the dimmest accent. Below that the comment alone
44
+ // caps every decoration; above it `variable` binds instead and the extra lightness buys
45
+ // nothing. At 0.672 the diff fill could reach 1.07:1 against the editor, which no reader
46
+ // could see; here it reaches 1.22:1.
47
+ export const comment = [0.704, BASE_CHROMA + 0.008, BASE_HUE];
48
+ // Step 10 is a solid-hover step in the Radix model, not a text step. Line numbers borrow
49
+ // it under a documented exemption. Dimmed UI text cannot: it sits on the widget surface
50
+ // too, where step 10 reads 3.70:1. This clears the floor on all four dark surfaces.
51
+ export const dimText = [0.630, BASE_CHROMA, BASE_HUE];
52
+ export const STATUS = {
53
+ success: 'green', warning: 'copper', error: 'coral', info: 'blue',
54
+ };
55
+ // A diff marks three things. The gutter strip fills the margin column, which carries only
56
+ // the line number, so it is gated at `NON_TEXT_FLOOR` against that. The line wash covers a
57
+ // changed line and the word wash covers the characters that changed inside it.
58
+ //
59
+ // The two washes are translucent, and that is not a stylistic choice. VS Code renders the
60
+ // diff decorations in `DecorationsOverlay`, which it registers after `SelectionsOverlay`,
61
+ // so a diff fill paints over the selection rather than under it. An opaque fill hides the
62
+ // selection completely on every changed line. The alphas are solved so the selection still
63
+ // reads through the strongest fill at 60% of the shift it makes on a plain line.
64
+ //
65
+ // The colour-vision separation lives on the strips, which are opaque, always drawn and
66
+ // never covered: they hold a 0.06 lightness gap. The washes cannot, because a wash dark
67
+ // enough to open that gap under a red one is invisible. The gutter glyph carries the
68
+ // meaning as shape in either case.
69
+ const ADDED_WASH = [0.450, 0.130, ACCENTS.green[2]];
70
+ const REMOVED_WASH = [0.550, 0.190, ACCENTS.coral[2]];
71
+ export const diffWash = {
72
+ addedLine: { color: ADDED_WASH, alpha: 0.12 },
73
+ addedWord: { color: ADDED_WASH, alpha: 0.16 },
74
+ removedLine: { color: REMOVED_WASH, alpha: 0.16 },
75
+ removedWord: { color: REMOVED_WASH, alpha: 0.17 },
76
+ };
77
+ export const diff = {
78
+ addedStrip: [0.308, 0.050, ACCENTS.green[2]],
79
+ removedStrip: [0.248, 0.110, ACCENTS.coral[2]],
80
+ addedGutter: ACCENTS.green,
81
+ removedGutter: ACCENTS.coral,
82
+ };
83
+ // Every alpha here is a contrast budget, not a taste. These three stack under running
84
+ // code, so the gate composites them and measures the syntax colours on the result.
85
+ // `lineHighlight` blends toward `hairline` at a low alpha rather than toward a near
86
+ // surface at a high one: `sidebar` is one step off the editor, so no alpha of it
87
+ // separates the current line, and a high alpha of anything erases the diff fill under it.
88
+ export const overlay = {
89
+ // The selection is tinted rather than neutral. A neutral selection inside this budget
90
+ // landed within 0.05 of the current line, so the two read as one decoration; a hue
91
+ // separates them at a luminance the comment can still afford.
92
+ //
93
+ // A translucent overlay moves a surface by `alpha x (overlay - surface)`, so a pale wash
94
+ // that lifts the editor barely touches the diff fills, which are already lighter and
95
+ // already coloured. This is a dark, saturated blue at a high alpha instead: it replaces
96
+ // more of what is under it, so the shift is close to even across every surface the
97
+ // selection can land on. The worst of those went from 0.049 to 0.117 in OKLab.
98
+ //
99
+ // The hue stops at 260. The gamut opens up towards 285, and the search wants to go
100
+ // there, but a wash that close to violet flattens the violet keywords sitting on it.
101
+ selection: { color: [0.480, 0.195, 260], alpha: 0.39 },
102
+ // The other matches take the same dark-wash treatment as the selection, for the same
103
+ // reason: a pale gold at a low alpha is what the budget allows and it reads as nothing.
104
+ // This is solved to keep the selection reading through it at half its plain-line shift.
105
+ findMatchOther: { color: [0.400, 0.075, ACCENTS.gold[2]], alpha: 0.40 },
106
+ wordHighlight: { color: neutral.hover, alpha: 0.28 },
107
+ lineHighlight: { color: neutral.hairline, alpha: 0.36 },
108
+ };
109
+ // A find match keeps the syntax colours under it. It used to replace them, through
110
+ // `editor.findMatchForeground` and `editor.findMatchHighlightForeground`, and that cannot
111
+ // be made to work: `findWidget.ts` applies `editorFindMatchForeground` to `.findMatchInline`
112
+ // and `editorFindMatchHighlightForeground` to `.currentFindMatchInline`, which is the
113
+ // opposite of what both names and both descriptions say. A theme that sets them ships dark
114
+ // text on the pale match and pale text on the dark one. Aion measured 1.42:1 and 1.39:1.
115
+ //
116
+ // So the fill has to carry the whole job on its own. The current match is opaque, which
117
+ // VS Code's own dark default is too, so it takes the full budget however many decorations
118
+ // sit under it. The other matches stay translucent, as their description requires.
119
+ export const findMatch = {
120
+ current: [0.338, 0.072, ACCENTS.gold[2]],
121
+ };
122
+ export const bracketPairs = ['gold', 'teal', 'violet'];
123
+ export const cursor = ACCENTS.gold;
124
+ export const BRIGHT_TO_NORMAL = 0.06;
125
+ export const ANSI_HUE = {
126
+ red: 'coral', green: 'green', yellow: 'gold',
127
+ blue: 'blue', magenta: 'violet', cyan: 'teal',
128
+ };
129
+ export const capitalise = (value) => (value.charAt(0).toUpperCase() + value.slice(1));
130
+ export const ansi = (() => {
131
+ const out = {};
132
+ for (const [slot, accent] of Object.entries(ANSI_HUE)) {
133
+ const [L, C, H] = ACCENTS[accent];
134
+ out[slot] = [L - BRIGHT_TO_NORMAL, C, H];
135
+ out[`bright${capitalise(slot)}`] = [L, C, H];
136
+ }
137
+ // Slot 8 clears the text floor on both backgrounds and under a selection or a find
138
+ // match wash, because a prompt puts the time and the git status in it and the terminal
139
+ // has no foreground override to fall back on. Slot 0 cannot: see TERMINAL_BACKGROUNDS.
140
+ out.black = [0.300, BASE_CHROMA, BASE_HUE];
141
+ out.brightBlack = [0.652, BASE_CHROMA, BASE_HUE];
142
+ out.white = neutral.textSecondary;
143
+ out.brightWhite = neutral.textPrimary;
144
+ return out;
145
+ })();
146
+ export const ANSI_ORDER = [
147
+ 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
148
+ 'brightBlack', 'brightRed', 'brightGreen', 'brightYellow',
149
+ 'brightBlue', 'brightMagenta', 'brightCyan', 'brightWhite',
150
+ ];
151
+ // A slot can land on either of these, so every foreground slot is gated on both. The
152
+ // VS Code panel is the lighter of the two and therefore the one that binds.
153
+ export const TERMINAL_BACKGROUNDS = {
154
+ standalone: neutral.editor,
155
+ vscodePanel: neutral.terminal,
156
+ };
157
+ export const terminalBackground = TERMINAL_BACKGROUNDS.standalone;
158
+ // Windows Terminal paints the selection opaque behind the glyphs, so unlike the editor
159
+ // overlay this value carries every ANSI slot on its own. The neutral it used before read
160
+ // 1.40:1 under ANSI red, and no step of the neutral ramp clears the floor for all sixteen.
161
+ export const terminalSelection = accentScale('blue').subtle;
162
+ // SGR 30 selects slot 0 as a foreground, so the old claim that no application puts text
163
+ // in it was wrong. It reads about 1.3:1 on either background, and raising it far enough
164
+ // to be legible would take it past the point where SGR 40 and reverse video still carry
165
+ // text. Aion keeps slot 0 dark and guarantees the other direction instead: slots 7 and 15
166
+ // clear the text floor on top of it. `ANSI_BLACK_TEXT` is what that gate measures.
167
+ export const ANSI_BLACK_TEXT = ['white', 'brightWhite'];
168
+ export const CHROMA_CEILING = {
169
+ gold: 0.17, green: 0.17, blue: 0.13, violet: 0.13,
170
+ };
171
+ export const CHROMA_DEFAULT = [0.09, 0.15];
172
+ export const HUE_DRIFT_LIMIT = 15;
173
+ export const CONTRAST_FLOOR = 4.5;
174
+ export const NON_TEXT_FLOOR = 3.0;
175
+ export const MEANING_PAIR_GAP = 0.06;
176
+ // Semantic foreground and border names that the text floor does not apply to, because
177
+ // they never carry body text. `muted` is the line number, under its own exemption.
178
+ export const CONTRAST_EXEMPT = new Set(['muted', 'hairline', 'divider']);
179
+ //# sourceMappingURL=palette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.js","sourceRoot":"","sources":["../src/palette.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { AccentName, AccentScale, AnsiSlot, NeutralName, SyntaxRole } from './palette.js';
2
+ import { diff as shippedDiff, diffWash as shippedWash, findMatch as shippedFindMatch, overlay as shippedOverlay } from './palette.js';
3
+ import type { Oklch, Overlay } from './index.js';
4
+ export interface PreviewOptions {
5
+ readonly baseHue: number;
6
+ readonly baseChroma: number;
7
+ readonly surfaceShift: number;
8
+ readonly accentChroma: number;
9
+ readonly accentLightness: number;
10
+ readonly commentLightness: number;
11
+ }
12
+ export declare const PREVIEW_DEFAULTS: PreviewOptions;
13
+ export interface Palette {
14
+ readonly neutral: Record<NeutralName, Oklch>;
15
+ readonly dim: Oklch;
16
+ readonly accents: Record<AccentName, Oklch>;
17
+ readonly scales: Record<AccentName, AccentScale>;
18
+ readonly syntax: Record<SyntaxRole, Oklch>;
19
+ readonly comment: Oklch;
20
+ readonly ansi: Record<AnsiSlot, Oklch>;
21
+ readonly diff: Record<keyof typeof shippedDiff, Oklch>;
22
+ readonly diffWash: Record<keyof typeof shippedWash, Overlay>;
23
+ readonly overlay: Record<keyof typeof shippedOverlay, Overlay>;
24
+ readonly findMatch: Record<keyof typeof shippedFindMatch, Oklch>;
25
+ readonly brackets: readonly Oklch[];
26
+ readonly cursor: Oklch;
27
+ }
28
+ export declare function buildPalette(overrides?: Partial<PreviewOptions>): Palette;
29
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAqB,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAClH,OAAO,EAEgD,IAAI,IAAI,WAAW,EACxE,QAAQ,IAAI,WAAW,EACvB,SAAS,IAAI,gBAAgB,EAC7B,OAAO,IAAI,cAAc,EAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAOjD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,eAAO,MAAM,gBAAgB,EAAE,cAO9B,CAAC;AAEF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,OAAO,WAAW,EAAE,KAAK,CAAC,CAAC;IACvD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,WAAW,EAAE,OAAO,CAAC,CAAC;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,OAAO,cAAc,EAAE,OAAO,CAAC,CAAC;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,OAAO,gBAAgB,EAAE,KAAK,CAAC,CAAC;IACjE,QAAQ,CAAC,QAAQ,EAAE,SAAS,KAAK,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;CACxB;AAID,wBAAgB,YAAY,CAAC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GAAG,OAAO,CA4D7E"}
@@ -0,0 +1,72 @@
1
+ import { ACCENTS, ANSI_HUE, BASE_CHROMA, BASE_HUE, BRIGHT_TO_NORMAL, NEUTRAL_LIGHTNESS, SYNTAX, bracketPairs, capitalise, comment as shippedComment, diff as shippedDiff, diffWash as shippedWash, findMatch as shippedFindMatch, overlay as shippedOverlay, dimText as shippedDim, scaleOf, } from './palette.js';
2
+ import { mapValues } from './util.js';
3
+ const reHue = (marker, accent) => [marker[0], marker[1], accent[2]];
4
+ const reWash = (wash, accent) => ({ color: reHue(wash.color, accent), alpha: wash.alpha });
5
+ export const PREVIEW_DEFAULTS = {
6
+ baseHue: BASE_HUE,
7
+ baseChroma: BASE_CHROMA,
8
+ surfaceShift: 0,
9
+ accentChroma: 1,
10
+ accentLightness: 0,
11
+ commentLightness: shippedComment[0],
12
+ };
13
+ // The lab drives this. At the default options it must equal the shipped palette exactly,
14
+ // which a test asserts; that is what stops the lab showing a colour the extension lacks.
15
+ export function buildPalette(overrides = {}) {
16
+ const o = { ...PREVIEW_DEFAULTS, ...overrides };
17
+ const shift = (L) => Math.min(1, Math.max(0, L + o.surfaceShift));
18
+ const neutral = mapValues(NEUTRAL_LIGHTNESS, (L) => [shift(L), o.baseChroma, o.baseHue]);
19
+ const dim = [shift(shippedDim[0]), o.baseChroma, o.baseHue];
20
+ const accents = mapValues(ACCENTS, ([L, C, H]) => [
21
+ Math.min(1, Math.max(0, L + o.accentLightness)),
22
+ Math.max(0, C * o.accentChroma),
23
+ H,
24
+ ]);
25
+ const scales = mapValues(accents, (colour) => scaleOf(colour));
26
+ const ansi = {};
27
+ for (const [slot, accent] of Object.entries(ANSI_HUE)) {
28
+ const [L, C, H] = accents[accent];
29
+ ansi[slot] = [L - BRIGHT_TO_NORMAL, C, H];
30
+ ansi[`bright${capitalise(slot)}`] = [L, C, H];
31
+ }
32
+ ansi.black = [shift(0.300), o.baseChroma, o.baseHue];
33
+ ansi.brightBlack = [shift(0.652), o.baseChroma, o.baseHue];
34
+ ansi.white = neutral.textSecondary;
35
+ ansi.brightWhite = neutral.textPrimary;
36
+ const overlay = {
37
+ // Bespoke rather than a step of the blue scale: it carries more chroma than any accent
38
+ // is allowed, which is what keeps it visible over a diff fill.
39
+ selection: { color: [shippedOverlay.selection.color[0], shippedOverlay.selection.color[1],
40
+ o.baseHue - BASE_HUE + shippedOverlay.selection.color[2]], alpha: shippedOverlay.selection.alpha },
41
+ findMatchOther: { color: reHue(shippedOverlay.findMatchOther.color, accents.gold),
42
+ alpha: shippedOverlay.findMatchOther.alpha },
43
+ wordHighlight: { color: neutral.hover, alpha: shippedOverlay.wordHighlight.alpha },
44
+ lineHighlight: { color: neutral.hairline, alpha: shippedOverlay.lineHighlight.alpha },
45
+ };
46
+ return {
47
+ neutral,
48
+ dim,
49
+ accents,
50
+ scales,
51
+ syntax: mapValues(SYNTAX, (accent) => accents[accent]),
52
+ comment: [o.commentLightness, o.baseChroma + 0.008, o.baseHue],
53
+ ansi,
54
+ diff: {
55
+ addedStrip: reHue(shippedDiff.addedStrip, accents.green),
56
+ removedStrip: reHue(shippedDiff.removedStrip, accents.coral),
57
+ addedGutter: accents.green,
58
+ removedGutter: accents.coral,
59
+ },
60
+ diffWash: {
61
+ addedLine: reWash(shippedWash.addedLine, accents.green),
62
+ addedWord: reWash(shippedWash.addedWord, accents.green),
63
+ removedLine: reWash(shippedWash.removedLine, accents.coral),
64
+ removedWord: reWash(shippedWash.removedWord, accents.coral),
65
+ },
66
+ overlay,
67
+ findMatch: { current: reHue(shippedFindMatch.current, accents.gold) },
68
+ brackets: bracketPairs.map((name) => accents[name]),
69
+ cursor: accents.gold,
70
+ };
71
+ }
72
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { NEUTRAL_LIGHTNESS } from './palette.js';
2
+ export type CheckState = 'pass' | 'fail' | 'exempt' | 'info';
3
+ export interface Check {
4
+ readonly section: string;
5
+ readonly token: string;
6
+ readonly hex: string;
7
+ readonly surface: string;
8
+ readonly surfaceHex: string;
9
+ readonly ratio: number;
10
+ readonly floor: number;
11
+ readonly state: CheckState;
12
+ }
13
+ export declare const NEUTRAL_ROLE: Record<keyof typeof NEUTRAL_LIGHTNESS, string>;
14
+ export declare function checks(): Check[];
15
+ export declare const failures: (rows?: Check[]) => Check[];
16
+ export declare function designTables(): Record<string, string>;
17
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAIA,OAAO,EAC+D,iBAAiB,EAGtF,MAAM,cAAc,CAAC;AAOtB,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAiBD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,OAAO,iBAAiB,EAAE,MAAM,CAavE,CAAC;AAKF,wBAAgB,MAAM,IAAI,KAAK,EAAE,CA6IhC;AAED,eAAO,MAAM,QAAQ,GAAI,OAAM,KAAK,EAAa,KAAG,KAAK,EAA4C,CAAC;AAKtG,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0IrD"}
package/dist/report.js ADDED
@@ -0,0 +1,279 @@
1
+ import { hex, hexAlpha, compositeEmitted, contrastEmitted } from './oklch.js';
2
+ import { ACCENTS, ACCENT_NAMES, ANSI_BLACK_TEXT, ANSI_ORDER, CONTRAST_FLOOR, NEUTRAL_LIGHTNESS, NON_TEXT_FLOOR, ONE_DARK_PRO_HUE, SYNTAX, TERMINAL_BACKGROUNDS, accentScale, ansi, comment, diff, diffWash, dimText, findMatch, neutral, overlay, terminalSelection, } from './palette.js';
3
+ import { LIGHT_LIGHTNESS, lightAccentScale, lightNeutral } from './light.js';
4
+ import { status, statusLight } from './status.js';
5
+ import { BOUNDARY_PAIRS, BOUNDARY_PAIRS_LIGHT, bg, bgLight, border, borderLight } from './semantic.js';
6
+ import { readingForegrounds, readingStates } from './states.js';
7
+ import { RIVALS, contrastHex, measure, surfaceOrder } from './rivals.js';
8
+ const build = (section, token, colour, surface, surfaceColour, floor, state) => {
9
+ const ratio = contrastEmitted(colour, surfaceColour);
10
+ return {
11
+ section, token, hex: hex(colour), surface, surfaceHex: hex(surfaceColour), ratio, floor,
12
+ state: state ?? (ratio >= floor ? 'pass' : 'fail'),
13
+ };
14
+ };
15
+ const compositeOverlay = (name, under) => compositeEmitted(overlay[name].color, overlay[name].alpha, under);
16
+ export const NEUTRAL_ROLE = {
17
+ editor: 'editor',
18
+ terminal: 'terminal, panel',
19
+ sidebar: 'sidebar, activity bar, status bar, tab bar',
20
+ widget: 'widget, menu, hover card, notification',
21
+ input: 'input, inactive tab',
22
+ hover: 'hover state',
23
+ hairline: 'hairline border',
24
+ divider: 'divider',
25
+ border: 'UI border, focus edge',
26
+ muted: 'muted text, line number',
27
+ textSecondary: 'secondary text',
28
+ textPrimary: 'primary text',
29
+ };
30
+ const TEXT_STEPS = ['textSecondary', 'textPrimary'];
31
+ const SURFACES = ['editor', 'terminal', 'sidebar', 'widget'];
32
+ export function checks() {
33
+ const rows = [];
34
+ for (const [name, colour] of Object.entries(neutral)) {
35
+ const isText = TEXT_STEPS.includes(name);
36
+ rows.push(build('neutral', `neutral.${name}`, colour, 'editor', neutral.editor, isText ? CONTRAST_FLOOR : 0, name === 'editor' ? 'info' : isText ? undefined : 'info'));
37
+ }
38
+ // Documented exemption: the line number is decorative, so the text floor does not apply.
39
+ rows.push(build('neutral', 'neutral.muted (line number)', neutral.muted, 'editor', neutral.editor, CONTRAST_FLOOR, 'exempt'));
40
+ for (const step of TEXT_STEPS) {
41
+ for (const surface of SURFACES) {
42
+ rows.push(build('text', `neutral.${step}`, neutral[step], surface, neutral[surface], CONTRAST_FLOOR));
43
+ }
44
+ }
45
+ for (const surface of SURFACES) {
46
+ rows.push(build('text', 'dimText', dimText, surface, neutral[surface], CONTRAST_FLOOR));
47
+ }
48
+ for (const role of Object.keys(SYNTAX)) {
49
+ rows.push(build('syntax', role, ACCENTS[SYNTAX[role]], 'editor', neutral.editor, CONTRAST_FLOOR));
50
+ }
51
+ rows.push(build('syntax', 'comment', comment, 'editor', neutral.editor, CONTRAST_FLOOR));
52
+ rows.push(build('syntax', 'punctuation', neutral.textSecondary, 'editor', neutral.editor, CONTRAST_FLOOR));
53
+ for (const name of ACCENT_NAMES) {
54
+ const scale = accentScale(name);
55
+ rows.push(build('accent', `${name}.solid`, scale.solid, 'editor', neutral.editor, CONTRAST_FLOOR));
56
+ rows.push(build('accent', `${name}.border`, scale.border, 'editor', neutral.editor, NON_TEXT_FLOOR));
57
+ rows.push(build('accent', `text on ${name}.solid`, neutral.editor, `${name}.solid`, scale.solid, CONTRAST_FLOOR));
58
+ rows.push(build('accent', `text on ${name}.subtle`, neutral.textPrimary, `${name}.subtle`, scale.subtle, CONTRAST_FLOOR));
59
+ }
60
+ // Every syntax colour against every background the guarantee covers, decorations
61
+ // composited as the renderer composites them. Three of the nine defects in this
62
+ // repository came from pairing a foreground with a background it actually lands on.
63
+ for (const state of readingStates()) {
64
+ for (const [role, colour] of Object.entries(readingForegrounds())) {
65
+ rows.push(build('decorated', role, colour, state.name, state.background, CONTRAST_FLOOR));
66
+ }
67
+ }
68
+ // The find match replaces the syntax colour rather than tinting it, so the pair to
69
+ // measure is the override against the match, not the syntax against the match.
70
+ for (const [role, colour] of Object.entries(readingForegrounds())) {
71
+ rows.push(build('decorated', role, colour, 'find match, current', findMatch.current, CONTRAST_FLOOR));
72
+ }
73
+ for (const { edge, inside, outside } of BOUNDARY_PAIRS) {
74
+ for (const side of [inside, outside]) {
75
+ rows.push(build('boundary', `border.${edge}`, border[edge], side, bg[side], NON_TEXT_FLOOR));
76
+ }
77
+ }
78
+ for (const name of ['hairline', 'divider']) {
79
+ // Documented exemption: these separate regions that already read as separate. They
80
+ // are not the edge of anything a keyboard user has to find.
81
+ rows.push(build('boundary', `border.${name} (decorative)`, border[name], 'widget', bg.widget, NON_TEXT_FLOOR, 'exempt'));
82
+ }
83
+ for (const [background, surface] of Object.entries(TERMINAL_BACKGROUNDS)) {
84
+ for (const slot of ANSI_ORDER) {
85
+ // Documented exemption: SGR 30 selects slot 0 as a foreground and it is not legible
86
+ // on either default background. Slot 0 is guaranteed as a background instead.
87
+ rows.push(build('ansi', `ansi.${slot}`, ansi[slot], background, surface, CONTRAST_FLOOR, slot === 'black' ? 'exempt' : undefined));
88
+ }
89
+ }
90
+ for (const slot of ANSI_BLACK_TEXT) {
91
+ rows.push(build('ansi', `ansi.${slot} on ansi.black`, ansi[slot], 'ansi.black', ansi.black, CONTRAST_FLOOR));
92
+ }
93
+ // Windows Terminal paints its selection opaque, so every slot has to survive it.
94
+ for (const slot of ANSI_ORDER) {
95
+ if (slot === 'black')
96
+ continue;
97
+ rows.push(build('ansi', `ansi.${slot} on the selection`, ansi[slot], 'terminalSelection', terminalSelection, CONTRAST_FLOOR));
98
+ }
99
+ for (const [name, colour] of Object.entries(diff)) {
100
+ const strip = name.endsWith('Strip');
101
+ rows.push(build('diff', `diff.${name}`, colour, 'editor', neutral.editor, strip ? 0 : CONTRAST_FLOOR, strip ? 'info' : undefined));
102
+ }
103
+ // A word wash renders over its own line wash, never straight onto the editor, so the
104
+ // reported colour is the pair composited in that order.
105
+ for (const [name, value] of Object.entries(diffWash)) {
106
+ const line = diffWash[name.endsWith('Word')
107
+ ? `${name.slice(0, -4)}Line` : name];
108
+ const under = line === value ? neutral.editor
109
+ : compositeEmitted(line.color, line.alpha, neutral.editor);
110
+ rows.push(build('diff', `diffWash.${name}`, compositeEmitted(value.color, value.alpha, under), 'editor', neutral.editor, 0, 'info'));
111
+ }
112
+ // The gutter column carries the line number and nothing else, so the strip is gated on
113
+ // that one foreground rather than on the comment. It is why the strip may be the loudest
114
+ // part of a diff: no body text ever lands on it.
115
+ for (const name of ['addedStrip', 'removedStrip']) {
116
+ rows.push(build('diff', 'editorLineNumber.foreground', neutral.muted, `diff.${name}`, diff[name], NON_TEXT_FLOOR));
117
+ }
118
+ for (const [name, value] of Object.entries(overlay)) {
119
+ rows.push({
120
+ section: 'overlay', token: `overlay.${name}`, hex: hexAlpha(value.color, value.alpha),
121
+ surface: 'editor', surfaceHex: hex(compositeOverlay(name, neutral.editor)),
122
+ ratio: 0, floor: 0, state: 'info',
123
+ });
124
+ }
125
+ for (const [name, scale] of Object.entries(status)) {
126
+ rows.push(build('status', `status.${name}.text`, scale.text, 'editor', neutral.editor, CONTRAST_FLOOR));
127
+ rows.push(build('status', `status.${name}.border`, scale.border, 'editor', neutral.editor, NON_TEXT_FLOOR));
128
+ rows.push(build('status', `status.${name}.onSolid`, scale.onSolid, `${name}.solid`, scale.solid, CONTRAST_FLOOR));
129
+ }
130
+ for (const [name, colour] of Object.entries(lightNeutral)) {
131
+ const isText = TEXT_STEPS.includes(name) || name === 'muted';
132
+ rows.push(build('light', `light.${name}`, colour, 'page', lightNeutral.page, isText ? CONTRAST_FLOOR : 0, name === 'page' ? 'info' : isText ? undefined : 'info'));
133
+ }
134
+ for (const { edge, inside, outside } of BOUNDARY_PAIRS_LIGHT) {
135
+ for (const side of [inside, outside]) {
136
+ rows.push(build('light', `light.border.${edge}`, borderLight[edge], `light.${side}`, bgLight[side], NON_TEXT_FLOOR));
137
+ }
138
+ }
139
+ for (const name of ACCENT_NAMES) {
140
+ const scale = lightAccentScale(name);
141
+ rows.push(build('light', `light.${name}.solid`, scale.solid, 'page', lightNeutral.page, CONTRAST_FLOOR));
142
+ rows.push(build('light', `light.${name}.solid`, scale.solid, 'raised', lightNeutral.raised, CONTRAST_FLOOR));
143
+ rows.push(build('light', `light.${name}.solid`, scale.solid, `light.${name}.subtle`, scale.subtle, CONTRAST_FLOOR));
144
+ rows.push(build('light', `light.${name}.border`, scale.border, 'raised', lightNeutral.raised, NON_TEXT_FLOOR));
145
+ rows.push(build('light', `text on light.${name}.subtle`, lightNeutral.textPrimary, `light.${name}.subtle`, scale.subtle, CONTRAST_FLOOR));
146
+ }
147
+ for (const [name, scale] of Object.entries(statusLight)) {
148
+ rows.push(build('light', `light.status.${name}.text`, scale.text, 'page', lightNeutral.page, CONTRAST_FLOOR));
149
+ }
150
+ return rows;
151
+ }
152
+ export const failures = (rows = checks()) => rows.filter((r) => r.state === 'fail');
153
+ const ratio = (value) => value.toFixed(2);
154
+ const round = (value, places) => value.toFixed(places);
155
+ export function designTables() {
156
+ const against = (c) => ratio(contrastEmitted(c, neutral.editor));
157
+ const onPage = (c) => ratio(contrastEmitted(c, lightNeutral.page));
158
+ const neutralRows = Object.keys(NEUTRAL_LIGHTNESS)
159
+ .map((name, index) => {
160
+ const colour = neutral[name];
161
+ const shown = name === 'editor' ? '—' : against(colour);
162
+ return `| ${index + 1} | ${NEUTRAL_ROLE[name]} | ${round(colour[0], 3)} | \`${hex(colour)}\` | ${shown} |`;
163
+ }).join('\n');
164
+ const accentRows = ACCENT_NAMES.map((name) => {
165
+ const scale = accentScale(name);
166
+ const [, chroma, hue] = ACCENTS[name];
167
+ return `| ${name} | ${hue}° | ${round(chroma, 3)} | \`${hex(scale.solid)}\` | \`${hex(scale.border)}\` | \`${hex(scale.subtle)}\` | ${against(scale.solid)} |`;
168
+ }).join('\n');
169
+ const SYNTAX_LABEL = {
170
+ variable: 'variable, property, parameter', number: 'number, constant', constant: '',
171
+ type: 'type, class', string: 'string', operator: 'operator, escape', escape: '',
172
+ function: 'function, method', keyword: 'keyword',
173
+ };
174
+ const syntaxRows = Object.keys(SYNTAX)
175
+ .filter((role) => SYNTAX_LABEL[role] !== '')
176
+ .map((role) => {
177
+ const accent = SYNTAX[role];
178
+ const colour = ACCENTS[accent];
179
+ const drift = colour[2] - ONE_DARK_PRO_HUE[role];
180
+ const sign = drift >= 0 ? '+' : '−';
181
+ return `| ${SYNTAX_LABEL[role]} | ${accent} | \`${hex(colour)}\` | ${round(ONE_DARK_PRO_HUE[role], 1)}° | ${colour[2]}° | ${sign}${round(Math.abs(drift), 1)}° | ${against(colour)} |`;
182
+ })
183
+ .concat([
184
+ `| comment | — | \`${hex(comment)}\` | — | 264° | — | **${against(comment)}** |`,
185
+ `| punctuation | — | \`${hex(neutral.textSecondary)}\` | — | 264° | — | ${against(neutral.textSecondary)} |`,
186
+ ]).join('\n');
187
+ const OVERLAY_LABEL = {
188
+ selection: 'selection', findMatchOther: 'find match, other',
189
+ wordHighlight: 'word highlight', lineHighlight: 'current line',
190
+ };
191
+ const overlayRows = Object.entries(overlay)
192
+ .map(([name, value]) => `| ${OVERLAY_LABEL[name] ?? name} | \`${hexAlpha(value.color, value.alpha)}\` | ${Math.round(value.alpha * 100)}% |`)
193
+ .concat([`| find match, current | \`${hex(findMatch.current)}\` | solid |`])
194
+ .join('\n');
195
+ const DIFF_LABEL = {
196
+ addedLine: 'added, line wash', addedWord: 'added, word wash',
197
+ removedLine: 'removed, line wash', removedWord: 'removed, word wash',
198
+ };
199
+ const diffRows = Object.entries(diffWash)
200
+ .map(([name, value]) => `| ${DIFF_LABEL[name]} | \`${hexAlpha(value.color, value.alpha)}\` | ${Math.round(value.alpha * 100)}% |`)
201
+ .concat([
202
+ ['added, gutter strip', diff.addedStrip], ['added, change bar', diff.addedGutter],
203
+ ['removed, gutter strip', diff.removedStrip], ['removed, change bar', diff.removedGutter],
204
+ ].map(([label, colour]) => `| ${label} | \`${hex(colour)}\` | solid |`))
205
+ .join('\n');
206
+ const ANSI_LABEL = {
207
+ black: 'black', red: 'red', green: 'green', yellow: 'yellow', blue: 'blue',
208
+ magenta: 'magenta', cyan: 'cyan', white: 'white',
209
+ };
210
+ const label = (slot) => slot.startsWith('bright') ? `bright ${ANSI_LABEL[slot.slice(6).toLowerCase()]}` : ANSI_LABEL[slot];
211
+ const ansiRows = ANSI_ORDER.slice(0, 8).map((normal, index) => {
212
+ const brightSlot = ANSI_ORDER[index + 8];
213
+ const normalRatio = normal === 'black' ? 'exempt' : against(ansi[normal]);
214
+ return `| ${index} | ${label(normal)} | \`${hex(ansi[normal])}\` | ${normalRatio} | | ${index + 8} | ${label(brightSlot)} | \`${hex(ansi[brightSlot])}\` | ${against(ansi[brightSlot])} |`;
215
+ }).join('\n');
216
+ const LIGHT_LABEL = {
217
+ page: 'page', surface: 'surface', raised: 'raised', input: 'input', hover: 'hover',
218
+ hairline: 'hairline', divider: 'divider', border: 'border', muted: 'muted',
219
+ textSecondary: 'secondary text', textPrimary: 'primary text',
220
+ };
221
+ const lightNames = Object.keys(LIGHT_LIGHTNESS);
222
+ const lightRows = lightNames.map((name, index) => {
223
+ const accent = ACCENT_NAMES[index];
224
+ const left = `| ${LIGHT_LABEL[name]} | \`${hex(lightNeutral[name])}\` |`;
225
+ if (!accent)
226
+ return `${left} | | | |`;
227
+ const solid = lightAccentScale(accent).solid;
228
+ return `${left} | ${accent} | \`${hex(solid)}\` | ${onPage(solid)} |`;
229
+ }).join('\n');
230
+ const aionRatios = [
231
+ ...Object.keys(SYNTAX).map((role) => contrastEmitted(ACCENTS[SYNTAX[role]], neutral.editor)),
232
+ contrastEmitted(comment, neutral.editor),
233
+ contrastEmitted(neutral.textSecondary, neutral.editor),
234
+ ];
235
+ const rivalRows = [
236
+ { name: 'Aion', lowest: Math.min(...aionRatios), below: [], source: '—' },
237
+ ...RIVALS.map((rival) => ({
238
+ ...measure(rival),
239
+ source: `[${rival.variant}](${rival.source}) @ \`${rival.revision.slice(0, 7)}\``,
240
+ })),
241
+ ].map((r) => `| ${r.name} | ${ratio(r.lowest)} | ${r.below.length === 0 ? 'none' : r.below.join(', ')} | ${r.source} |`).join('\n');
242
+ const surfaceRows = RIVALS
243
+ .map((rival) => `| ${rival.variant} | \`${rival.background}\` | \`${rival.sideBar}\` | ${surfaceOrder(rival)} |`)
244
+ .join('\n');
245
+ // The listing tables. They quoted hand-typed hex until a palette change made every one
246
+ // of them wrong at once, so they are generated like everything else.
247
+ const LISTING_SURFACE = [
248
+ ['editor', neutral.editor], ['panel, terminal', neutral.terminal],
249
+ ['sidebar, status bar', neutral.sidebar], ['widget, menu', neutral.widget],
250
+ ['primary text', neutral.textPrimary], ['secondary text', neutral.textSecondary],
251
+ ['comment', comment],
252
+ ];
253
+ const LISTING_ACCENT = [
254
+ ['coral, variables', 'coral'], ['copper, numbers', 'copper'], ['gold, types', 'gold'],
255
+ ['green, strings', 'green'], ['teal, operators', 'teal'], ['blue, functions', 'blue'],
256
+ ['violet, keywords', 'violet'],
257
+ ];
258
+ const listingRows = LISTING_SURFACE.map(([label, colour], index) => {
259
+ const [accentLabel, accent] = LISTING_ACCENT[index];
260
+ return `| ${label} | \`${hex(colour)}\` | | ${accentLabel} | \`${hex(ACCENTS[accent])}\` |`;
261
+ }).join('\n');
262
+ // Windows Terminal calls the magenta slots "purple", and this table ships in its README.
263
+ const wt = (slot) => label(slot).replace('magenta', 'purple');
264
+ const slotRows = ANSI_ORDER.slice(0, 8).map((normal, index) => {
265
+ const bright = ANSI_ORDER[index + 8];
266
+ return `| ${index} | ${wt(normal)} | \`${hex(ansi[normal])}\` | ${index + 8} | ${wt(bright)} | \`${hex(ansi[bright])}\` |`;
267
+ }).join('\n');
268
+ // The covered set, written out. The documents used to say "twenty-five states" in prose
269
+ // while the function returned thirty-five, and a reader had no way to see which ones.
270
+ const foregrounds = readingForegrounds();
271
+ const stateRows = readingStates().map((state) => {
272
+ const worst = Object.entries(foregrounds)
273
+ .map(([role, colour]) => ({ role, value: contrastEmitted(colour, state.background) }))
274
+ .sort((a, b) => a.value - b.value)[0];
275
+ return `| ${state.name} | \`${hex(state.background)}\` | ${worst.role} | ${ratio(worst.value)} |`;
276
+ }).join('\n');
277
+ return { rivals: rivalRows, surfaces: surfaceRows, listing: listingRows, slots: slotRows, neutral: neutralRows, accent: accentRows, syntax: syntaxRows, overlay: overlayRows, diff: diffRows, ansi: ansiRows, light: lightRows, states: stateRows };
278
+ }
279
+ //# sourceMappingURL=report.js.map