@vincemakes/kiso-tui-cells 0.46.2 → 0.47.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/dist/approval-panel.d.ts +149 -62
- package/dist/approval-panel.js +346 -227
- package/dist/components.d.ts +151 -21
- package/dist/components.js +1190 -821
- package/dist/diff.d.ts +40 -3
- package/dist/diff.js +142 -5
- package/dist/graphite.d.ts +123 -0
- package/dist/graphite.js +257 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/md.d.ts +18 -7
- package/dist/md.js +290 -37
- package/dist/render.d.ts +136 -27
- package/dist/render.js +381 -137
- package/dist/strings.d.ts +47 -46
- package/dist/strings.js +130 -171
- package/package.json +1 -1
package/dist/diff.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* v2e — the diff renderer: edit/write changes as inline ± lines, zero
|
|
3
|
-
* dependencies, no syntax highlighting (the spec's scope line).
|
|
4
|
-
* the approval moment
|
|
5
|
-
*
|
|
3
|
+
* dependencies, no syntax highlighting (the spec's scope line). v2e drew
|
|
4
|
+
* it at the approval moment only; Graphite §6 (R2a) draws it on every
|
|
5
|
+
* edit's card too, from the call's own hunks (`hunksDiff`) — DECLARED
|
|
6
|
+
* REVERSAL of "the frozen summary stays one line".
|
|
6
7
|
*
|
|
7
8
|
* edit_file diffs IN PLACE (the search→replace windows are known — no
|
|
8
9
|
* general engine needed); write_file does a row-level LCS over the old
|
|
@@ -17,7 +18,17 @@
|
|
|
17
18
|
export type DiffLine = {
|
|
18
19
|
kind: "-" | "+" | " " | "note";
|
|
19
20
|
text: string;
|
|
21
|
+
/** Graphite §6 (R2a): the changed WORDS of a line that replaced one
|
|
22
|
+
* other line — `[start, end)` offsets into `text`, sorted, disjoint.
|
|
23
|
+
* Absent everywhere else. */
|
|
24
|
+
marks?: readonly (readonly [number, number])[];
|
|
20
25
|
};
|
|
26
|
+
/** One change an edit_file call asks for: the text it looks for, and what
|
|
27
|
+
* replaces it. */
|
|
28
|
+
export interface Hunk {
|
|
29
|
+
readonly search: string;
|
|
30
|
+
readonly replace: string;
|
|
31
|
+
}
|
|
21
32
|
export interface DiffResult {
|
|
22
33
|
/** The FULL diff (with context, not truncated) — the display truncates. */
|
|
23
34
|
lines: DiffLine[];
|
|
@@ -44,6 +55,8 @@ export interface DiffResult {
|
|
|
44
55
|
* changes meaning. */
|
|
45
56
|
notFound?: true;
|
|
46
57
|
}
|
|
58
|
+
/** A line-level LCS diff — the classic two-row DP, ~small inputs. */
|
|
59
|
+
export declare function lcsDiff(oldLines: string[], newLines: string[]): DiffLine[];
|
|
47
60
|
/** The RENDERER's truncation: head + "… N lines (/last for full)" + tail. */
|
|
48
61
|
export declare function truncateDiff(diff: DiffLine[]): DiffLine[];
|
|
49
62
|
/** edit_file: the preview of a CHARACTER splice.
|
|
@@ -72,3 +85,27 @@ export declare function editFileDiff(oldContent: string, search: string, replace
|
|
|
72
85
|
/** write_file: a new file is all +; an existing file diffs row-level
|
|
73
86
|
* against its old content. */
|
|
74
87
|
export declare function writeFileDiff(oldContent: string | null, newContent: string): DiffResult;
|
|
88
|
+
/** The hunks an edit_file call carries: its `edits`, or its one
|
|
89
|
+
* `search`/`replace` pair. Null when the input has neither (the tool
|
|
90
|
+
* refuses such a call, so there is nothing to draw). */
|
|
91
|
+
export declare function hunksOf(input: Record<string, unknown>): Hunk[] | null;
|
|
92
|
+
/** Graphite §6 (R2a) — what an edit CHANGED, drawn from the call's own
|
|
93
|
+
* hunks: per hunk a line LCS of `search` against `replace`, the lines
|
|
94
|
+
* both keep as context (two around each change), a `···` note between
|
|
95
|
+
* hunks, and word marks where one line replaced one line. The file is
|
|
96
|
+
* never read, so the diff is a pure function of the durable log — the
|
|
97
|
+
* same live, after a reprint, after resume. */
|
|
98
|
+
export declare function hunksDiff(hunks: readonly Hunk[]): DiffResult;
|
|
99
|
+
/** The approval preview of a batch: the hunks applied IN ORDER, each to
|
|
100
|
+
* the text the ones before it left — the tool's own rule (ACI-3) — and
|
|
101
|
+
* the result diffed against the file. A hunk the tool would refuse (not
|
|
102
|
+
* there, or there more than once) is named, and nothing is drawn: the
|
|
103
|
+
* tool writes all of the batch or none of it. */
|
|
104
|
+
export declare function editFileHunksDiff(oldContent: string, hunks: readonly Hunk[], path?: string): DiffResult;
|
|
105
|
+
/** A line's tokens: runs of word characters, runs of spaces, and every
|
|
106
|
+
* other character on its own. Joined, they are the line. */
|
|
107
|
+
export declare function wordTokens(line: string): string[];
|
|
108
|
+
/** The token LCS of two lines: for each side, the `[start, end)` offsets
|
|
109
|
+
* of the tokens the other side does not keep, adjacent ones merged.
|
|
110
|
+
* Null when too little survives for marks to mean anything. */
|
|
111
|
+
export declare function tokenMarks(a: string, b: string): [Array<[number, number]> | null, Array<[number, number]> | null];
|
package/dist/diff.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* v2e — the diff renderer: edit/write changes as inline ± lines, zero
|
|
3
|
-
* dependencies, no syntax highlighting (the spec's scope line).
|
|
4
|
-
* the approval moment
|
|
5
|
-
*
|
|
3
|
+
* dependencies, no syntax highlighting (the spec's scope line). v2e drew
|
|
4
|
+
* it at the approval moment only; Graphite §6 (R2a) draws it on every
|
|
5
|
+
* edit's card too, from the call's own hunks (`hunksDiff`) — DECLARED
|
|
6
|
+
* REVERSAL of "the frozen summary stays one line".
|
|
6
7
|
*
|
|
7
8
|
* edit_file diffs IN PLACE (the search→replace windows are known — no
|
|
8
9
|
* general engine needed); write_file does a row-level LCS over the old
|
|
@@ -11,7 +12,7 @@
|
|
|
11
12
|
* from the full diff.
|
|
12
13
|
*/
|
|
13
14
|
/** A line-level LCS diff — the classic two-row DP, ~small inputs. */
|
|
14
|
-
function lcsDiff(oldLines, newLines) {
|
|
15
|
+
export function lcsDiff(oldLines, newLines) {
|
|
15
16
|
const n = oldLines.length;
|
|
16
17
|
const m = newLines.length;
|
|
17
18
|
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
@@ -138,7 +139,7 @@ export function editFileDiff(oldContent, search, replace, path) {
|
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
141
|
const result = oldContent.slice(0, at) + replace + oldContent.slice(at + search.length);
|
|
141
|
-
const lines = withContext(lcsDiff(oldContent.split("\n"), result.split("\n")));
|
|
142
|
+
const lines = withMarks(withContext(lcsDiff(oldContent.split("\n"), result.split("\n"))));
|
|
142
143
|
return { lines, ...stats(lines), outcome: "diff" };
|
|
143
144
|
}
|
|
144
145
|
/** write_file: a new file is all +; an existing file diffs row-level
|
|
@@ -151,3 +152,139 @@ export function writeFileDiff(oldContent, newContent) {
|
|
|
151
152
|
const lines = withContext(lcsDiff(oldContent.split("\n"), newContent.split("\n")));
|
|
152
153
|
return { lines, ...stats(lines), outcome: "diff" };
|
|
153
154
|
}
|
|
155
|
+
/** The hunks an edit_file call carries: its `edits`, or its one
|
|
156
|
+
* `search`/`replace` pair. Null when the input has neither (the tool
|
|
157
|
+
* refuses such a call, so there is nothing to draw). */
|
|
158
|
+
export function hunksOf(input) {
|
|
159
|
+
const pair = (x) => {
|
|
160
|
+
if (typeof x !== "object" || x === null)
|
|
161
|
+
return null;
|
|
162
|
+
const { search, replace } = x;
|
|
163
|
+
return typeof search === "string" && typeof replace === "string" ? { search, replace } : null;
|
|
164
|
+
};
|
|
165
|
+
if (Array.isArray(input.edits)) {
|
|
166
|
+
const hunks = input.edits.map(pair);
|
|
167
|
+
return hunks.length > 0 && hunks.every((h) => h !== null) ? hunks : null;
|
|
168
|
+
}
|
|
169
|
+
const one = pair(input);
|
|
170
|
+
return one === null ? null : [one];
|
|
171
|
+
}
|
|
172
|
+
/** Graphite §6 (R2a) — what an edit CHANGED, drawn from the call's own
|
|
173
|
+
* hunks: per hunk a line LCS of `search` against `replace`, the lines
|
|
174
|
+
* both keep as context (two around each change), a `···` note between
|
|
175
|
+
* hunks, and word marks where one line replaced one line. The file is
|
|
176
|
+
* never read, so the diff is a pure function of the durable log — the
|
|
177
|
+
* same live, after a reprint, after resume. */
|
|
178
|
+
export function hunksDiff(hunks) {
|
|
179
|
+
const lines = [];
|
|
180
|
+
for (const [i, h] of hunks.entries()) {
|
|
181
|
+
const one = withMarks(withContext(lcsDiff(h.search.split("\n"), h.replace.split("\n"))));
|
|
182
|
+
if (one.length === 0)
|
|
183
|
+
continue;
|
|
184
|
+
if (i > 0 && lines.length > 0)
|
|
185
|
+
lines.push({ kind: "note", text: "\u00b7\u00b7\u00b7" });
|
|
186
|
+
lines.push(...one);
|
|
187
|
+
}
|
|
188
|
+
return { lines, ...stats(lines), outcome: "diff" };
|
|
189
|
+
}
|
|
190
|
+
/** The approval preview of a batch: the hunks applied IN ORDER, each to
|
|
191
|
+
* the text the ones before it left — the tool's own rule (ACI-3) — and
|
|
192
|
+
* the result diffed against the file. A hunk the tool would refuse (not
|
|
193
|
+
* there, or there more than once) is named, and nothing is drawn: the
|
|
194
|
+
* tool writes all of the batch or none of it. */
|
|
195
|
+
export function editFileHunksDiff(oldContent, hunks, path) {
|
|
196
|
+
if (hunks.length === 1)
|
|
197
|
+
return editFileDiff(oldContent, hunks[0].search, hunks[0].replace, path);
|
|
198
|
+
const where = path ?? "the file";
|
|
199
|
+
let edited = oldContent;
|
|
200
|
+
for (const [i, h] of hunks.entries()) {
|
|
201
|
+
const at = edited.indexOf(h.search);
|
|
202
|
+
const which = i === 0 ? "hunk 1" : `hunk ${i + 1}, after ${i === 1 ? "hunk 1" : `hunks 1\u2013${i}`} applied`;
|
|
203
|
+
if (at < 0)
|
|
204
|
+
return { lines: [{ kind: "note", text: `pattern not found in ${where} (${which})` }], added: 0, removed: 0, outcome: "not-found", notFound: true };
|
|
205
|
+
if (h.search.length > 0 && edited.indexOf(h.search, at + 1) > at) {
|
|
206
|
+
return { lines: [{ kind: "note", text: `pattern matches more than one place in ${where} (${which})` }], added: 0, removed: 0, outcome: "ambiguous" };
|
|
207
|
+
}
|
|
208
|
+
edited = edited.slice(0, at) + h.replace + edited.slice(at + h.search.length);
|
|
209
|
+
}
|
|
210
|
+
const lines = withMarks(withContext(lcsDiff(oldContent.split("\n"), edited.split("\n"))));
|
|
211
|
+
return { lines, ...stats(lines), outcome: "diff" };
|
|
212
|
+
}
|
|
213
|
+
/** A line's tokens: runs of word characters, runs of spaces, and every
|
|
214
|
+
* other character on its own. Joined, they are the line. */
|
|
215
|
+
export function wordTokens(line) {
|
|
216
|
+
return line.match(/[\p{L}\p{N}_]+|\s+|[\s\S]/gu) ?? [];
|
|
217
|
+
}
|
|
218
|
+
/** The share of a replaced line's tokens (spaces aside) that must survive
|
|
219
|
+
* for word marks to help: below it the whole line changed, and marking
|
|
220
|
+
* nearly every word says less than the line's own colour does. */
|
|
221
|
+
const MARK_MIN_KEPT = 0.34;
|
|
222
|
+
/** Word marks on a diff: every `-` line directly followed by one `+` line,
|
|
223
|
+
* with no other change beside the pair, gets the offsets of the tokens
|
|
224
|
+
* the other side does not keep (a token LCS). */
|
|
225
|
+
function withMarks(diff) {
|
|
226
|
+
const changed = (k) => k >= 0 && k < diff.length && (diff[k].kind === "-" || diff[k].kind === "+");
|
|
227
|
+
return diff.map((d, k) => {
|
|
228
|
+
const pairAt = d.kind === "-" ? k : d.kind === "+" ? k - 1 : -1;
|
|
229
|
+
if (pairAt < 0 || diff[pairAt]?.kind !== "-" || diff[pairAt + 1]?.kind !== "+" || changed(pairAt - 1) || changed(pairAt + 2))
|
|
230
|
+
return d;
|
|
231
|
+
const [oldMarks, newMarks] = tokenMarks(diff[pairAt].text, diff[pairAt + 1].text);
|
|
232
|
+
if (oldMarks === null || newMarks === null)
|
|
233
|
+
return d;
|
|
234
|
+
const marks = d.kind === "-" ? oldMarks : newMarks;
|
|
235
|
+
return marks.length === 0 ? d : { ...d, marks };
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/** The token LCS of two lines: for each side, the `[start, end)` offsets
|
|
239
|
+
* of the tokens the other side does not keep, adjacent ones merged.
|
|
240
|
+
* Null when too little survives for marks to mean anything. */
|
|
241
|
+
export function tokenMarks(a, b) {
|
|
242
|
+
const ta = wordTokens(a);
|
|
243
|
+
const tb = wordTokens(b);
|
|
244
|
+
const n = ta.length;
|
|
245
|
+
const m = tb.length;
|
|
246
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
247
|
+
for (let i = n - 1; i >= 0; i -= 1) {
|
|
248
|
+
for (let j = m - 1; j >= 0; j -= 1)
|
|
249
|
+
dp[i][j] = ta[i] === tb[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
250
|
+
}
|
|
251
|
+
const keptA = new Array(n).fill(false);
|
|
252
|
+
const keptB = new Array(m).fill(false);
|
|
253
|
+
for (let i = 0, j = 0; i < n && j < m;) {
|
|
254
|
+
if (ta[i] === tb[j]) {
|
|
255
|
+
keptA[i] = true;
|
|
256
|
+
keptB[j] = true;
|
|
257
|
+
i += 1;
|
|
258
|
+
j += 1;
|
|
259
|
+
}
|
|
260
|
+
else if (dp[i + 1][j] >= dp[i][j + 1])
|
|
261
|
+
i += 1;
|
|
262
|
+
else
|
|
263
|
+
j += 1;
|
|
264
|
+
}
|
|
265
|
+
const solid = (t) => /\S/.test(t);
|
|
266
|
+
const words = ta.filter(solid).length + tb.filter(solid).length;
|
|
267
|
+
const kept = ta.filter((t, i) => keptA[i] && solid(t)).length + tb.filter((t, j) => keptB[j] && solid(t)).length;
|
|
268
|
+
if (words === 0 || kept / words < MARK_MIN_KEPT)
|
|
269
|
+
return [null, null];
|
|
270
|
+
return [spans(ta, keptA), spans(tb, keptB)];
|
|
271
|
+
}
|
|
272
|
+
/** The offsets of the tokens not kept, adjacent spans merged; a run of
|
|
273
|
+
* spaces between two changed tokens joins them. */
|
|
274
|
+
function spans(tokens, kept) {
|
|
275
|
+
const line = tokens.join("");
|
|
276
|
+
const out = [];
|
|
277
|
+
let at = 0;
|
|
278
|
+
for (const [i, t] of tokens.entries()) {
|
|
279
|
+
const end = at + t.length;
|
|
280
|
+
if (!kept[i] && /\S/.test(t)) {
|
|
281
|
+
const last = out[out.length - 1];
|
|
282
|
+
if (last !== undefined && /^\s*$/.test(line.slice(last[1], at)))
|
|
283
|
+
last[1] = end;
|
|
284
|
+
else
|
|
285
|
+
out.push([at, end]);
|
|
286
|
+
}
|
|
287
|
+
at = end;
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graphite (design.md §2, §3.4) — the colour tokens, the output tier, and
|
|
3
|
+
* the surfaces derived from the ground the terminal actually reported.
|
|
4
|
+
*
|
|
5
|
+
* PURE: no process state is read except where a caller passes it in, so
|
|
6
|
+
* every decision here is testable without a terminal. `render.ts` turns
|
|
7
|
+
* these colours into SGR bytes; this module only decides the colours.
|
|
8
|
+
*/
|
|
9
|
+
import { type Rgb } from "./ground.js";
|
|
10
|
+
export type Kind = "light" | "dark";
|
|
11
|
+
/** How colour reaches the terminal: 24-bit (`38;2;r;g;b`) where the
|
|
12
|
+
* terminal says it renders it, the nearest xterm-256 index otherwise. */
|
|
13
|
+
export type Tier = "24bit" | "256";
|
|
14
|
+
/** The Graphite table (design.md §2), on the two reference grounds. */
|
|
15
|
+
export declare const GRAPHITE: {
|
|
16
|
+
readonly light: {
|
|
17
|
+
readonly ground: "#ffffff";
|
|
18
|
+
readonly ink: "#111111";
|
|
19
|
+
readonly ink2: "#444444";
|
|
20
|
+
readonly dim: "#646464";
|
|
21
|
+
readonly rail: "#8c8c8c";
|
|
22
|
+
readonly line: "#e6e6e6";
|
|
23
|
+
readonly washRun: "#edf2fb";
|
|
24
|
+
readonly washDone: "#f1f1f1";
|
|
25
|
+
readonly washFail: "#fbecea";
|
|
26
|
+
readonly washAsk: "#f7f1e3";
|
|
27
|
+
readonly human: "#f7efdc";
|
|
28
|
+
readonly humanInk: "#171923";
|
|
29
|
+
readonly gold: "#8a5a00";
|
|
30
|
+
readonly goldMark: "#c9921f";
|
|
31
|
+
readonly blue: "#2456b5";
|
|
32
|
+
readonly code: "#e4ebf8";
|
|
33
|
+
readonly ok: "#2f7a3a";
|
|
34
|
+
readonly fail: "#b3261e";
|
|
35
|
+
readonly add: "#dff0e2";
|
|
36
|
+
readonly del: "#fadfdc";
|
|
37
|
+
readonly track: "#e2e2e2";
|
|
38
|
+
};
|
|
39
|
+
readonly dark: {
|
|
40
|
+
readonly ground: "#0b0b0b";
|
|
41
|
+
readonly ink: "#ededed";
|
|
42
|
+
readonly ink2: "#b5b5b5";
|
|
43
|
+
readonly dim: "#8e8e8e";
|
|
44
|
+
readonly rail: "#6b6b6b";
|
|
45
|
+
readonly line: "#212121";
|
|
46
|
+
readonly washRun: "#141b28";
|
|
47
|
+
readonly washDone: "#1b1b1b";
|
|
48
|
+
readonly washFail: "#2a1716";
|
|
49
|
+
readonly washAsk: "#211d13";
|
|
50
|
+
readonly human: "#efe6cf";
|
|
51
|
+
readonly humanInk: "#141620";
|
|
52
|
+
readonly gold: "#e3b04b";
|
|
53
|
+
readonly goldMark: "#e3b04b";
|
|
54
|
+
readonly blue: "#82a8f5";
|
|
55
|
+
readonly code: "#1a2438";
|
|
56
|
+
readonly ok: "#8fd19e";
|
|
57
|
+
readonly fail: "#f2877a";
|
|
58
|
+
readonly add: "#16301f";
|
|
59
|
+
readonly del: "#3a1b1a";
|
|
60
|
+
readonly track: "#2a2a2a";
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export type Token = Exclude<keyof (typeof GRAPHITE)["light"], "ground">;
|
|
64
|
+
export type Colours = {
|
|
65
|
+
readonly [K in Token | "ground"]: Rgb;
|
|
66
|
+
};
|
|
67
|
+
/** The tokens that are GROUNDS near the terminal's own (§3.4): each is
|
|
68
|
+
* derived from the reported ground. Every other token is fixed per kind. */
|
|
69
|
+
export declare const SURFACES: readonly ["line", "washRun", "washDone", "washFail", "washAsk", "human", "code", "add", "del", "track"];
|
|
70
|
+
/** The text tokens that read on the ground and on the card surfaces (§2.1). */
|
|
71
|
+
export declare const CARD_TEXT: readonly ["ink", "ink2", "dim", "gold", "blue", "ok", "fail"];
|
|
72
|
+
export declare const CARDS: readonly ["washRun", "washDone", "washFail", "washAsk"];
|
|
73
|
+
type Surface = Token | "ground";
|
|
74
|
+
/** Every text-on-surface pair a human reads (§2.1's floor applies to each). */
|
|
75
|
+
export declare const TEXT_PAIRS: readonly (readonly [Token, Surface])[];
|
|
76
|
+
export declare const FLOOR = 4.5;
|
|
77
|
+
/** The floor for a GRAPHIC — a mark, not text (WCAG 1.4.11). The breath
|
|
78
|
+
* never drops below it (§2.2). */
|
|
79
|
+
export declare const GRAPHIC_FLOOR = 3;
|
|
80
|
+
export declare function hexRgb(hex: string): Rgb;
|
|
81
|
+
export declare function rgbHex({ r, g, b }: Rgb): string;
|
|
82
|
+
/** The WCAG contrast ratio, the measure of §2's floor. */
|
|
83
|
+
export declare function contrast(a: Rgb, b: Rgb): number;
|
|
84
|
+
/** `t` of the way from `a` to `b`, per channel. */
|
|
85
|
+
export declare function mix(a: Rgb, b: Rgb, t: number): Rgb;
|
|
86
|
+
/**
|
|
87
|
+
* §3.4 — one surface for any ground. Per channel an affine map
|
|
88
|
+
* `s = a + k·g`, fitted so that the white reference ground gives the
|
|
89
|
+
* table's white value and the black one its black value: the two
|
|
90
|
+
* grounds reproduce the table exactly, and a terminal whose black is
|
|
91
|
+
* `#1e1e1e` gets a card that sits the same distance off ITS ground.
|
|
92
|
+
*/
|
|
93
|
+
export declare function deriveSurface(token: Token, ground: Rgb): Rgb;
|
|
94
|
+
/** A colour as the terminal will show it: itself in the 24-bit tier, the
|
|
95
|
+
* nearest xterm-256 entry in the 256 tier. The floor is checked on what
|
|
96
|
+
* is SHOWN — rounding to the nearest index can cost a pair its contrast. */
|
|
97
|
+
export declare function shown(c: Rgb, tier: Tier): Rgb;
|
|
98
|
+
/** The weakest text-on-surface pair of a colour set, as shown in `tier`. */
|
|
99
|
+
export declare function weakestPair(c: Colours, tier?: Tier): {
|
|
100
|
+
pair: readonly [Token, Surface];
|
|
101
|
+
ratio: number;
|
|
102
|
+
};
|
|
103
|
+
export declare function graphiteColours(kind: Kind, ground: Rgb | null, tier?: Tier): Colours;
|
|
104
|
+
/**
|
|
105
|
+
* The command breath (§5.2): seven steps from `gold` toward the running
|
|
106
|
+
* card's ground, peak → floor → peak. The floor is the deepest step that
|
|
107
|
+
* still meets the graphic floor on that ground (§2.2): the mark dims and
|
|
108
|
+
* never disappears.
|
|
109
|
+
*/
|
|
110
|
+
export declare function breathRamp(c: Colours): readonly Rgb[];
|
|
111
|
+
/** The xterm-256 palette's colour for an index 16–255 (the cube and the
|
|
112
|
+
* grey ramp; the first sixteen are the terminal's own and never chosen). */
|
|
113
|
+
export declare function xterm256(index: number): Rgb;
|
|
114
|
+
/** The nearest xterm-256 index (16–255) to a colour, by squared distance
|
|
115
|
+
* in RGB — the 256 tier's whole translation. */
|
|
116
|
+
export declare function nearest256(c: Rgb): number;
|
|
117
|
+
/** §2 — 24-bit where the terminal says it renders it. `COLORTERM` is the
|
|
118
|
+
* standard statement; anything else gets the 256 tier, which every
|
|
119
|
+
* terminal kiso docks in renders. */
|
|
120
|
+
export declare function colourTier(colorterm: string | undefined): Tier;
|
|
121
|
+
export declare function fg(c: Rgb, tier: Tier): string;
|
|
122
|
+
export declare function bg(c: Rgb, tier: Tier): string;
|
|
123
|
+
export {};
|
package/dist/graphite.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graphite (design.md §2, §3.4) — the colour tokens, the output tier, and
|
|
3
|
+
* the surfaces derived from the ground the terminal actually reported.
|
|
4
|
+
*
|
|
5
|
+
* PURE: no process state is read except where a caller passes it in, so
|
|
6
|
+
* every decision here is testable without a terminal. `render.ts` turns
|
|
7
|
+
* these colours into SGR bytes; this module only decides the colours.
|
|
8
|
+
*/
|
|
9
|
+
import { groundFrom, relativeLuminance } from "./ground.js";
|
|
10
|
+
/** The Graphite table (design.md §2), on the two reference grounds. */
|
|
11
|
+
export const GRAPHITE = {
|
|
12
|
+
light: {
|
|
13
|
+
ground: "#ffffff",
|
|
14
|
+
ink: "#111111",
|
|
15
|
+
ink2: "#444444",
|
|
16
|
+
dim: "#646464",
|
|
17
|
+
rail: "#8c8c8c",
|
|
18
|
+
line: "#e6e6e6",
|
|
19
|
+
washRun: "#edf2fb",
|
|
20
|
+
washDone: "#f1f1f1",
|
|
21
|
+
washFail: "#fbecea",
|
|
22
|
+
washAsk: "#f7f1e3",
|
|
23
|
+
human: "#f7efdc",
|
|
24
|
+
humanInk: "#171923",
|
|
25
|
+
gold: "#8a5a00",
|
|
26
|
+
goldMark: "#c9921f",
|
|
27
|
+
blue: "#2456b5",
|
|
28
|
+
code: "#e4ebf8",
|
|
29
|
+
ok: "#2f7a3a",
|
|
30
|
+
fail: "#b3261e",
|
|
31
|
+
add: "#dff0e2",
|
|
32
|
+
del: "#fadfdc",
|
|
33
|
+
track: "#e2e2e2",
|
|
34
|
+
},
|
|
35
|
+
dark: {
|
|
36
|
+
ground: "#0b0b0b",
|
|
37
|
+
ink: "#ededed",
|
|
38
|
+
ink2: "#b5b5b5",
|
|
39
|
+
dim: "#8e8e8e",
|
|
40
|
+
rail: "#6b6b6b",
|
|
41
|
+
line: "#212121",
|
|
42
|
+
washRun: "#141b28",
|
|
43
|
+
washDone: "#1b1b1b",
|
|
44
|
+
washFail: "#2a1716",
|
|
45
|
+
washAsk: "#211d13",
|
|
46
|
+
human: "#efe6cf",
|
|
47
|
+
humanInk: "#141620",
|
|
48
|
+
gold: "#e3b04b",
|
|
49
|
+
goldMark: "#e3b04b",
|
|
50
|
+
blue: "#82a8f5",
|
|
51
|
+
code: "#1a2438",
|
|
52
|
+
ok: "#8fd19e",
|
|
53
|
+
fail: "#f2877a",
|
|
54
|
+
add: "#16301f",
|
|
55
|
+
del: "#3a1b1a",
|
|
56
|
+
track: "#2a2a2a",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
/** The tokens that are GROUNDS near the terminal's own (§3.4): each is
|
|
60
|
+
* derived from the reported ground. Every other token is fixed per kind. */
|
|
61
|
+
export const SURFACES = ["line", "washRun", "washDone", "washFail", "washAsk", "human", "code", "add", "del", "track"];
|
|
62
|
+
/** The text tokens that read on the ground and on the card surfaces (§2.1). */
|
|
63
|
+
export const CARD_TEXT = ["ink", "ink2", "dim", "gold", "blue", "ok", "fail"];
|
|
64
|
+
export const CARDS = ["washRun", "washDone", "washFail", "washAsk"];
|
|
65
|
+
/** Every text-on-surface pair a human reads (§2.1's floor applies to each). */
|
|
66
|
+
export const TEXT_PAIRS = [
|
|
67
|
+
...["ground", ...CARDS].flatMap((surface) => CARD_TEXT.map((text) => [text, surface])),
|
|
68
|
+
["humanInk", "human"],
|
|
69
|
+
["blue", "code"],
|
|
70
|
+
["ink", "add"],
|
|
71
|
+
["ink", "del"],
|
|
72
|
+
];
|
|
73
|
+
export const FLOOR = 4.5;
|
|
74
|
+
/** The floor for a GRAPHIC — a mark, not text (WCAG 1.4.11). The breath
|
|
75
|
+
* never drops below it (§2.2). */
|
|
76
|
+
export const GRAPHIC_FLOOR = 3;
|
|
77
|
+
export function hexRgb(hex) {
|
|
78
|
+
const n = Number.parseInt(hex.slice(1), 16);
|
|
79
|
+
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
|
80
|
+
}
|
|
81
|
+
export function rgbHex({ r, g, b }) {
|
|
82
|
+
return `#${[r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
|
|
83
|
+
}
|
|
84
|
+
/** The WCAG contrast ratio, the measure of §2's floor. */
|
|
85
|
+
export function contrast(a, b) {
|
|
86
|
+
const [x, y] = [relativeLuminance(a), relativeLuminance(b)].sort((p, q) => q - p);
|
|
87
|
+
return (x + 0.05) / (y + 0.05);
|
|
88
|
+
}
|
|
89
|
+
/** `t` of the way from `a` to `b`, per channel. */
|
|
90
|
+
export function mix(a, b, t) {
|
|
91
|
+
const c = (x, y) => Math.round(x + (y - x) * t);
|
|
92
|
+
return { r: c(a.r, b.r), g: c(a.g, b.g), b: c(a.b, b.b) };
|
|
93
|
+
}
|
|
94
|
+
const REF_LIGHT = hexRgb(GRAPHITE.light.ground);
|
|
95
|
+
const REF_DARK = hexRgb(GRAPHITE.dark.ground);
|
|
96
|
+
/**
|
|
97
|
+
* §3.4 — one surface for any ground. Per channel an affine map
|
|
98
|
+
* `s = a + k·g`, fitted so that the white reference ground gives the
|
|
99
|
+
* table's white value and the black one its black value: the two
|
|
100
|
+
* grounds reproduce the table exactly, and a terminal whose black is
|
|
101
|
+
* `#1e1e1e` gets a card that sits the same distance off ITS ground.
|
|
102
|
+
*/
|
|
103
|
+
export function deriveSurface(token, ground) {
|
|
104
|
+
const w = hexRgb(GRAPHITE.light[token]);
|
|
105
|
+
const d = hexRgb(GRAPHITE.dark[token]);
|
|
106
|
+
const chan = (wc, dc, lw, ld, g) => {
|
|
107
|
+
const k = (wc - dc) / (lw - ld);
|
|
108
|
+
return Math.max(0, Math.min(255, Math.round(dc + k * (g - ld))));
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
r: chan(w.r, d.r, REF_LIGHT.r, REF_DARK.r, ground.r),
|
|
112
|
+
g: chan(w.g, d.g, REF_LIGHT.g, REF_DARK.g, ground.g),
|
|
113
|
+
b: chan(w.b, d.b, REF_LIGHT.b, REF_DARK.b, ground.b),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function table(kind) {
|
|
117
|
+
const t = GRAPHITE[kind];
|
|
118
|
+
return Object.fromEntries(Object.entries(t).map(([k, v]) => [k, hexRgb(v)]));
|
|
119
|
+
}
|
|
120
|
+
/** A colour as the terminal will show it: itself in the 24-bit tier, the
|
|
121
|
+
* nearest xterm-256 entry in the 256 tier. The floor is checked on what
|
|
122
|
+
* is SHOWN — rounding to the nearest index can cost a pair its contrast. */
|
|
123
|
+
export function shown(c, tier) {
|
|
124
|
+
return tier === "24bit" ? c : xterm256(nearest256(c));
|
|
125
|
+
}
|
|
126
|
+
/** One pair's ratio as shown. The ground is the terminal's own colour and
|
|
127
|
+
* is never rounded; every token kiso writes is. */
|
|
128
|
+
function pairRatio(c, text, surface, tier) {
|
|
129
|
+
const s = surface === "ground" ? c.ground : shown(c[surface], tier);
|
|
130
|
+
return contrast(shown(c[text], tier), s);
|
|
131
|
+
}
|
|
132
|
+
/** The weakest text-on-surface pair of a colour set, as shown in `tier`. */
|
|
133
|
+
export function weakestPair(c, tier = "24bit") {
|
|
134
|
+
let worst = { pair: TEXT_PAIRS[0], ratio: Number.POSITIVE_INFINITY };
|
|
135
|
+
for (const pair of TEXT_PAIRS) {
|
|
136
|
+
const ratio = pairRatio(c, pair[0], pair[1], tier);
|
|
137
|
+
if (ratio < worst.ratio)
|
|
138
|
+
worst = { pair, ratio };
|
|
139
|
+
}
|
|
140
|
+
return worst;
|
|
141
|
+
}
|
|
142
|
+
/** A text token moved toward the kind's extreme (white on a dark ground,
|
|
143
|
+
* black on a light one) just far enough that it clears the floor, as
|
|
144
|
+
* shown, on every surface it reads on. Unmoved when it already does. */
|
|
145
|
+
function lifted(c, token, kind, tier) {
|
|
146
|
+
const on = TEXT_PAIRS.filter(([t]) => t === token).map(([, s]) => s);
|
|
147
|
+
const extreme = kind === "dark" ? { r: 255, g: 255, b: 255 } : { r: 0, g: 0, b: 0 };
|
|
148
|
+
for (let t = 0; t <= 1.0001; t += 0.02) {
|
|
149
|
+
const cand = { ...c, [token]: mix(c[token], extreme, t) };
|
|
150
|
+
if (on.every((s) => pairRatio(cand, token, s, tier) >= FLOOR))
|
|
151
|
+
return cand[token];
|
|
152
|
+
}
|
|
153
|
+
return extreme;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The colours for one screen. `ground` is the RGB the terminal reported
|
|
157
|
+
* (OSC 11), or null when the ground was resolved without a colour — an
|
|
158
|
+
* explicit theme, the terminal's scheme report, COLORFGBG — in which case
|
|
159
|
+
* the table is used as it stands.
|
|
160
|
+
*
|
|
161
|
+
* A reported ground of the OTHER kind (a human who set `theme: light` on
|
|
162
|
+
* a black terminal) derives nothing: the answer the human gave wins, and
|
|
163
|
+
* surfaces fitted to a ground the text tokens were not chosen for would
|
|
164
|
+
* be wrong in both directions.
|
|
165
|
+
*
|
|
166
|
+
* On a reported ground the surfaces are derived (§3.4), and a text token
|
|
167
|
+
* that would fall under the floor — as shown in `tier` — on the ground or
|
|
168
|
+
* on a surface is moved toward the kind's extreme just far enough to clear
|
|
169
|
+
* it: a lighter dark theme lifts `dim`, never the other way. The table's
|
|
170
|
+
* own values pass on their reference grounds in both tiers, so there
|
|
171
|
+
* nothing moves. If a pair is still under the floor, the table is used.
|
|
172
|
+
*/
|
|
173
|
+
/** The last derivation, kept: the opening's rules and wordmark ask for
|
|
174
|
+
* the colours on every frame (the banner is a live cell), and the answer
|
|
175
|
+
* only moves with its three inputs (the render-perf pass, 2026-10-07).
|
|
176
|
+
* Callers read the table; none writes to it. */
|
|
177
|
+
let lastColours = null;
|
|
178
|
+
export function graphiteColours(kind, ground, tier = "24bit") {
|
|
179
|
+
const r = ground === null ? -1 : ground.r;
|
|
180
|
+
const g = ground === null ? -1 : ground.g;
|
|
181
|
+
const b = ground === null ? -1 : ground.b;
|
|
182
|
+
const k = lastColours;
|
|
183
|
+
if (k !== null && k.kind === kind && k.tier === tier && k.r === r && k.g === g && k.b === b)
|
|
184
|
+
return k.out;
|
|
185
|
+
const out = deriveColours(kind, ground, tier);
|
|
186
|
+
lastColours = { kind, r, g, b, tier, out };
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
function deriveColours(kind, ground, tier) {
|
|
190
|
+
const base = table(kind);
|
|
191
|
+
if (ground === null || groundFrom(ground) !== kind)
|
|
192
|
+
return base;
|
|
193
|
+
const derived = { ...base, ground };
|
|
194
|
+
for (const s of SURFACES)
|
|
195
|
+
derived[s] = deriveSurface(s, ground);
|
|
196
|
+
for (const t of new Set(TEXT_PAIRS.map(([text]) => text)))
|
|
197
|
+
derived[t] = lifted(derived, t, kind, tier);
|
|
198
|
+
const out = derived;
|
|
199
|
+
return weakestPair(out, tier).ratio >= FLOOR ? out : base;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The command breath (§5.2): seven steps from `gold` toward the running
|
|
203
|
+
* card's ground, peak → floor → peak. The floor is the deepest step that
|
|
204
|
+
* still meets the graphic floor on that ground (§2.2): the mark dims and
|
|
205
|
+
* never disappears.
|
|
206
|
+
*/
|
|
207
|
+
export function breathRamp(c) {
|
|
208
|
+
const card = c.washRun;
|
|
209
|
+
let deepest = 0;
|
|
210
|
+
for (let t = 0; t <= 1.0001; t += 0.01) {
|
|
211
|
+
if (contrast(mix(c.gold, card, t), card) >= GRAPHIC_FLOOR)
|
|
212
|
+
deepest = t;
|
|
213
|
+
else
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
const at = (f) => mix(c.gold, card, deepest * f);
|
|
217
|
+
return [at(0), at(1 / 3), at(2 / 3), at(1), at(2 / 3), at(1 / 3), at(0)];
|
|
218
|
+
}
|
|
219
|
+
/** The xterm-256 palette's colour for an index 16–255 (the cube and the
|
|
220
|
+
* grey ramp; the first sixteen are the terminal's own and never chosen). */
|
|
221
|
+
export function xterm256(index) {
|
|
222
|
+
if (index >= 232) {
|
|
223
|
+
const v = 8 + (index - 232) * 10;
|
|
224
|
+
return { r: v, g: v, b: v };
|
|
225
|
+
}
|
|
226
|
+
const i = index - 16;
|
|
227
|
+
const step = (n) => (n === 0 ? 0 : 55 + n * 40);
|
|
228
|
+
return { r: step(Math.floor(i / 36)), g: step(Math.floor(i / 6) % 6), b: step(i % 6) };
|
|
229
|
+
}
|
|
230
|
+
/** The nearest xterm-256 index (16–255) to a colour, by squared distance
|
|
231
|
+
* in RGB — the 256 tier's whole translation. */
|
|
232
|
+
export function nearest256(c) {
|
|
233
|
+
let best = 16;
|
|
234
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
235
|
+
for (let i = 16; i <= 255; i += 1) {
|
|
236
|
+
const x = xterm256(i);
|
|
237
|
+
const d = (x.r - c.r) ** 2 + (x.g - c.g) ** 2 + (x.b - c.b) ** 2;
|
|
238
|
+
if (d < bestD) {
|
|
239
|
+
bestD = d;
|
|
240
|
+
best = i;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return best;
|
|
244
|
+
}
|
|
245
|
+
/** §2 — 24-bit where the terminal says it renders it. `COLORTERM` is the
|
|
246
|
+
* standard statement; anything else gets the 256 tier, which every
|
|
247
|
+
* terminal kiso docks in renders. */
|
|
248
|
+
export function colourTier(colorterm) {
|
|
249
|
+
const v = colorterm?.trim().toLowerCase();
|
|
250
|
+
return v === "truecolor" || v === "24bit" ? "24bit" : "256";
|
|
251
|
+
}
|
|
252
|
+
export function fg(c, tier) {
|
|
253
|
+
return tier === "24bit" ? `\x1b[38;2;${c.r};${c.g};${c.b}m` : `\x1b[38;5;${nearest256(c)}m`;
|
|
254
|
+
}
|
|
255
|
+
export function bg(c, tier) {
|
|
256
|
+
return tier === "24bit" ? `\x1b[48;2;${c.r};${c.g};${c.b}m` : `\x1b[48;5;${nearest256(c)}m`;
|
|
257
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,14 +7,17 @@
|
|
|
7
7
|
* API-stability promise yet.
|
|
8
8
|
*/
|
|
9
9
|
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, foldTerms, elapsedLabel, settledLabel, statusLine, boxTop, boxBottom, terminalPipe, type FrameCtx, type RenderLine, type Component, type BodyCell, } from "./components.js";
|
|
10
|
-
export { editFileDiff, truncateDiff, writeFileDiff, type DiffLine, type DiffResult } from "./diff.js";
|
|
10
|
+
export { editFileDiff, editFileHunksDiff, hunksDiff, hunksOf, truncateDiff, writeFileDiff, type DiffLine, type DiffResult, type Hunk } from "./diff.js";
|
|
11
11
|
export { pendingQueueRows } from "./components.js";
|
|
12
12
|
export { charWidth, displayWidth, leadWidth, widthOf } from "./width.js";
|
|
13
13
|
export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, type AskAnswer, type AskOption, type AskQuestion, type AskResult, type AskRuntime, type AskSpec, } from "./approval-panel.js";
|
|
14
14
|
export { interactivePrompt, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, type TrustArtifact, } from "./strings.js";
|
|
15
|
-
export { coldResumeLine, coldResumeView, extensionsBannerText, helpRows, unansweredAskView, type BannerExtension } from "./strings.js";
|
|
15
|
+
export { coldResumeLine, coldResumeView, extensionsBannerText, extensionsFact, helpRows, unansweredAskView, type BannerExtension } from "./strings.js";
|
|
16
16
|
export { displayVerb } from "./strings.js";
|
|
17
|
-
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type ResumeMeta, } from "./render.js";
|
|
17
|
+
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, currentGroundRgb, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, paletteFor, relativeTime, renderResumeList, MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame, renderTerminalGap, renderToolSummary, TAGLINE, MOTTO, WORDMARK_W, type BannerFact, type BannerMeta, toolTarget, truncateRow, type Palette, type ResumeMeta, } from "./render.js";
|
|
18
18
|
/** DC-3 — the ground: is the terminal light or dark. Pure; see the
|
|
19
19
|
* module comment for why `unknown` is a result and not a failure. */
|
|
20
20
|
export { groundFrom, parseOscColor, relativeLuminance, resolveGround, type Ground, type GroundInputs, type Rgb } from "./ground.js";
|
|
21
|
+
/** Graphite (design.md §2, §3.4) — the tokens, the tier and the derived
|
|
22
|
+
* surfaces, as colours. Pure; `render.ts` writes them as SGR. */
|
|
23
|
+
export { GRAPHITE, breathRamp, colourTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair, type Colours, type Kind, type Tier, type Token } from "./graphite.js";
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* API-stability promise yet.
|
|
8
8
|
*/
|
|
9
9
|
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, foldTerms, elapsedLabel, settledLabel, statusLine, boxTop, boxBottom, terminalPipe, } from "./components.js";
|
|
10
|
-
export { editFileDiff, truncateDiff, writeFileDiff } from "./diff.js";
|
|
10
|
+
export { editFileDiff, editFileHunksDiff, hunksDiff, hunksOf, truncateDiff, writeFileDiff } from "./diff.js";
|
|
11
11
|
// W22 (the v8 input round): the pending-queue chips — the SAME
|
|
12
12
|
// UserMessage chip with the □ gutter, pre-rendered above the input
|
|
13
13
|
// row while turns wait in the queue.
|
|
@@ -24,11 +24,14 @@ export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWi
|
|
|
24
24
|
// the cli; what the human reads is presentation.
|
|
25
25
|
export { interactivePrompt, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, } from "./strings.js";
|
|
26
26
|
// KC3.5: the interrupted-ask copy and the extracted /help table.
|
|
27
|
-
export { coldResumeLine, coldResumeView, extensionsBannerText, helpRows, unansweredAskView } from "./strings.js";
|
|
27
|
+
export { coldResumeLine, coldResumeView, extensionsBannerText, extensionsFact, helpRows, unansweredAskView } from "./strings.js";
|
|
28
28
|
// TUI2-R2pre ④: the ONE display-verb table — the screen names the act,
|
|
29
29
|
// the tool table names the call.
|
|
30
30
|
export { displayVerb } from "./strings.js";
|
|
31
|
-
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./render.js";
|
|
31
|
+
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, currentGroundRgb, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, paletteFor, relativeTime, renderResumeList, MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame, renderTerminalGap, renderToolSummary, TAGLINE, MOTTO, WORDMARK_W, toolTarget, truncateRow, } from "./render.js";
|
|
32
32
|
/** DC-3 — the ground: is the terminal light or dark. Pure; see the
|
|
33
33
|
* module comment for why `unknown` is a result and not a failure. */
|
|
34
34
|
export { groundFrom, parseOscColor, relativeLuminance, resolveGround } from "./ground.js";
|
|
35
|
+
/** Graphite (design.md §2, §3.4) — the tokens, the tier and the derived
|
|
36
|
+
* surfaces, as colours. Pure; `render.ts` writes them as SGR. */
|
|
37
|
+
export { GRAPHITE, breathRamp, colourTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair } from "./graphite.js";
|