@vincemakes/kiso-tui-cells 0.46.2 → 0.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,311 @@
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
+ // 0.47.1 — the dark table re-adapted (owner, 2026-10-07: preset D on
36
+ // the dark palette page): the surfaces were a few levels above the
37
+ // ground and read as heavy on a black terminal; they rise a step, and
38
+ // dim and ink2 rise with them to keep the floor. washAsk and code sit
39
+ // just under D's values (#524222, #30406a) so every pair of
40
+ // TEXT_PAIRS still clears 4.5, in the 256 tier too.
41
+ dark: {
42
+ ground: "#0b0b0b",
43
+ ink: "#ededed",
44
+ ink2: "#c2c2c2",
45
+ dim: "#a4a4a4",
46
+ rail: "#6b6b6b",
47
+ // the owner, 2026-10-08: the composer's rules read too grey on Apple
48
+ // Terminal's Pro profile (its transparency lightens the black) — the
49
+ // hairlines rise to #656565
50
+ line: "#656565",
51
+ washRun: "#2b3854",
52
+ washDone: "#333333",
53
+ washFail: "#4d2b28",
54
+ washAsk: "#483818",
55
+ human: "#e8dfc6",
56
+ humanInk: "#141620",
57
+ gold: "#e3b04b",
58
+ goldMark: "#e3b04b",
59
+ blue: "#82a8f5",
60
+ code: "#283658",
61
+ ok: "#8fd19e",
62
+ fail: "#f2877a",
63
+ add: "#2a5034",
64
+ del: "#5e302d",
65
+ track: "#474747",
66
+ },
67
+ };
68
+ /** The tokens that are GROUNDS near the terminal's own (§3.4): each is
69
+ * derived from the reported ground. Every other token is fixed per kind. */
70
+ export const SURFACES = ["line", "washRun", "washDone", "washFail", "washAsk", "human", "code", "add", "del", "track"];
71
+ /** The text tokens that read on the ground and on the card surfaces (§2.1). */
72
+ export const CARD_TEXT = ["ink", "ink2", "dim", "gold", "blue", "ok", "fail"];
73
+ export const CARDS = ["washRun", "washDone", "washFail", "washAsk"];
74
+ /** Every text-on-surface pair a human reads (§2.1's floor applies to each). */
75
+ export const TEXT_PAIRS = [
76
+ ...["ground", ...CARDS].flatMap((surface) => CARD_TEXT.map((text) => [text, surface])),
77
+ ["humanInk", "human"],
78
+ ["blue", "code"],
79
+ ["ink", "add"],
80
+ ["ink", "del"],
81
+ ];
82
+ export const FLOOR = 4.5;
83
+ /** The floor for a GRAPHIC — a mark, not text (WCAG 1.4.11). The breath
84
+ * never drops below it (§2.2). */
85
+ export const GRAPHIC_FLOOR = 3;
86
+ export function hexRgb(hex) {
87
+ const n = Number.parseInt(hex.slice(1), 16);
88
+ return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
89
+ }
90
+ export function rgbHex({ r, g, b }) {
91
+ return `#${[r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
92
+ }
93
+ /** The WCAG contrast ratio, the measure of §2's floor. */
94
+ export function contrast(a, b) {
95
+ const [x, y] = [relativeLuminance(a), relativeLuminance(b)].sort((p, q) => q - p);
96
+ return (x + 0.05) / (y + 0.05);
97
+ }
98
+ /** `t` of the way from `a` to `b`, per channel. */
99
+ export function mix(a, b, t) {
100
+ const c = (x, y) => Math.round(x + (y - x) * t);
101
+ return { r: c(a.r, b.r), g: c(a.g, b.g), b: c(a.b, b.b) };
102
+ }
103
+ const REF_LIGHT = hexRgb(GRAPHITE.light.ground);
104
+ const REF_DARK = hexRgb(GRAPHITE.dark.ground);
105
+ /**
106
+ * §3.4 — one surface for any ground. A DARK ground: per channel an affine
107
+ * map `s = a + k·g`, fitted so that the white reference ground gives the
108
+ * table's white value and the black one its black value — a terminal
109
+ * whose black is `#1e1e1e` gets a card that sits the same distance off
110
+ * ITS ground, and the distance narrows as a dark ground lightens, which
111
+ * keeps the text above the floor on the lighter dark themes. A LIGHT
112
+ * ground: the light table, moved by how far the ground sits from white.
113
+ * The two reference grounds reproduce the table exactly.
114
+ *
115
+ * 0.47.1 (the dark table's re-adaptation): DECLARED REVERSAL for the
116
+ * light grounds only. The one map through both tables tied them to the
117
+ * dark table — lifting the dark cards pulled a card on `#eeeeee` to within
118
+ * 1.09:1 of its ground. A light ground now answers to the light table
119
+ * alone; on the light grounds measured the cards move by a channel step
120
+ * or two at most.
121
+ */
122
+ export function deriveSurface(token, ground) {
123
+ if (groundFrom(ground) === "light") {
124
+ const s = hexRgb(GRAPHITE.light[token]);
125
+ const shift = (sc, rc, g) => Math.max(0, Math.min(255, Math.round(sc + (g - rc))));
126
+ return { r: shift(s.r, REF_LIGHT.r, ground.r), g: shift(s.g, REF_LIGHT.g, ground.g), b: shift(s.b, REF_LIGHT.b, ground.b) };
127
+ }
128
+ const w = hexRgb(GRAPHITE.light[token]);
129
+ const d = hexRgb(GRAPHITE.dark[token]);
130
+ const chan = (wc, dc, lw, ld, g) => {
131
+ const k = (wc - dc) / (lw - ld);
132
+ return Math.max(0, Math.min(255, Math.round(dc + k * (g - ld))));
133
+ };
134
+ return {
135
+ r: chan(w.r, d.r, REF_LIGHT.r, REF_DARK.r, ground.r),
136
+ g: chan(w.g, d.g, REF_LIGHT.g, REF_DARK.g, ground.g),
137
+ b: chan(w.b, d.b, REF_LIGHT.b, REF_DARK.b, ground.b),
138
+ };
139
+ }
140
+ function table(kind) {
141
+ const t = GRAPHITE[kind];
142
+ return Object.fromEntries(Object.entries(t).map(([k, v]) => [k, hexRgb(v)]));
143
+ }
144
+ /** A colour as the terminal will show it: itself in the 24-bit tier, the
145
+ * nearest xterm-256 entry in the 256 tier. The floor is checked on what
146
+ * is SHOWN — rounding to the nearest index can cost a pair its contrast. */
147
+ export function shown(c, tier) {
148
+ return tier === "24bit" ? c : xterm256(nearest256(c));
149
+ }
150
+ /** One pair's ratio as shown. The ground is the terminal's own colour and
151
+ * is never rounded; every token kiso writes is. */
152
+ function pairRatio(c, text, surface, tier) {
153
+ const s = surface === "ground" ? c.ground : shown(c[surface], tier);
154
+ return contrast(shown(c[text], tier), s);
155
+ }
156
+ /** The weakest text-on-surface pair of a colour set, as shown in `tier`. */
157
+ export function weakestPair(c, tier = "24bit") {
158
+ let worst = { pair: TEXT_PAIRS[0], ratio: Number.POSITIVE_INFINITY };
159
+ for (const pair of TEXT_PAIRS) {
160
+ const ratio = pairRatio(c, pair[0], pair[1], tier);
161
+ if (ratio < worst.ratio)
162
+ worst = { pair, ratio };
163
+ }
164
+ return worst;
165
+ }
166
+ /** A text token moved toward the kind's extreme (white on a dark ground,
167
+ * black on a light one) just far enough that it clears the floor, as
168
+ * shown, on every surface it reads on. Unmoved when it already does. */
169
+ function lifted(c, token, kind, tier) {
170
+ const on = TEXT_PAIRS.filter(([t]) => t === token).map(([, s]) => s);
171
+ const extreme = kind === "dark" ? { r: 255, g: 255, b: 255 } : { r: 0, g: 0, b: 0 };
172
+ for (let t = 0; t <= 1.0001; t += 0.02) {
173
+ const cand = { ...c, [token]: mix(c[token], extreme, t) };
174
+ if (on.every((s) => pairRatio(cand, token, s, tier) >= FLOOR))
175
+ return cand[token];
176
+ }
177
+ return extreme;
178
+ }
179
+ /**
180
+ * The colours for one screen. `ground` is the RGB the terminal reported
181
+ * (OSC 11), or null when the ground was resolved without a colour — an
182
+ * explicit theme, the terminal's scheme report, COLORFGBG — in which case
183
+ * the table is used as it stands.
184
+ *
185
+ * A reported ground of the OTHER kind (a human who set `theme: light` on
186
+ * a black terminal) derives nothing: the answer the human gave wins, and
187
+ * surfaces fitted to a ground the text tokens were not chosen for would
188
+ * be wrong in both directions.
189
+ *
190
+ * On a reported ground the surfaces are derived (§3.4), and a text token
191
+ * that would fall under the floor — as shown in `tier` — on the ground or
192
+ * on a surface is moved toward the kind's extreme just far enough to clear
193
+ * it: a lighter dark theme lifts `dim`, never the other way. The table's
194
+ * own values pass on their reference grounds in both tiers, so there
195
+ * nothing moves. If a pair is still under the floor, the table is used.
196
+ */
197
+ /** The last derivation, kept: the opening's rules and wordmark ask for
198
+ * the colours on every frame (the banner is a live cell), and the answer
199
+ * only moves with its three inputs (the render-perf pass, 2026-10-07).
200
+ * Callers read the table; none writes to it. */
201
+ let lastColours = null;
202
+ export function graphiteColours(kind, ground, tier = "24bit") {
203
+ const r = ground === null ? -1 : ground.r;
204
+ const g = ground === null ? -1 : ground.g;
205
+ const b = ground === null ? -1 : ground.b;
206
+ const k = lastColours;
207
+ if (k !== null && k.kind === kind && k.tier === tier && k.r === r && k.g === g && k.b === b)
208
+ return k.out;
209
+ const out = deriveColours(kind, ground, tier);
210
+ lastColours = { kind, r, g, b, tier, out };
211
+ return out;
212
+ }
213
+ function deriveColours(kind, ground, tier) {
214
+ const base = table(kind);
215
+ if (ground === null || groundFrom(ground) !== kind)
216
+ return base;
217
+ const derived = { ...base, ground };
218
+ for (const s of SURFACES)
219
+ derived[s] = deriveSurface(s, ground);
220
+ for (const t of new Set(TEXT_PAIRS.map(([text]) => text)))
221
+ derived[t] = lifted(derived, t, kind, tier);
222
+ const out = derived;
223
+ return weakestPair(out, tier).ratio >= FLOOR ? out : base;
224
+ }
225
+ /**
226
+ * The command breath (§5.2): seven steps from `gold` toward the running
227
+ * card's ground, peak → floor → peak. The floor is the deepest step that
228
+ * still meets the graphic floor on that ground (§2.2): the mark dims and
229
+ * never disappears.
230
+ */
231
+ export function breathRamp(c) {
232
+ const card = c.washRun;
233
+ let deepest = 0;
234
+ for (let t = 0; t <= 1.0001; t += 0.01) {
235
+ if (contrast(mix(c.gold, card, t), card) >= GRAPHIC_FLOOR)
236
+ deepest = t;
237
+ else
238
+ break;
239
+ }
240
+ const at = (f) => mix(c.gold, card, deepest * f);
241
+ return [at(0), at(1 / 3), at(2 / 3), at(1), at(2 / 3), at(1 / 3), at(0)];
242
+ }
243
+ /** The xterm-256 palette's colour for an index 16–255 (the cube and the
244
+ * grey ramp; the first sixteen are the terminal's own and never chosen). */
245
+ export function xterm256(index) {
246
+ if (index >= 232) {
247
+ const v = 8 + (index - 232) * 10;
248
+ return { r: v, g: v, b: v };
249
+ }
250
+ const i = index - 16;
251
+ const step = (n) => (n === 0 ? 0 : 55 + n * 40);
252
+ return { r: step(Math.floor(i / 36)), g: step(Math.floor(i / 6) % 6), b: step(i % 6) };
253
+ }
254
+ /** The nearest xterm-256 index (16–255) to a colour, by squared distance
255
+ * in RGB — the 256 tier's whole translation. */
256
+ export function nearest256(c) {
257
+ let best = 16;
258
+ let bestD = Number.POSITIVE_INFINITY;
259
+ for (let i = 16; i <= 255; i += 1) {
260
+ const x = xterm256(i);
261
+ const d = (x.r - c.r) ** 2 + (x.g - c.g) ** 2 + (x.b - c.b) ** 2;
262
+ if (d < bestD) {
263
+ bestD = d;
264
+ best = i;
265
+ }
266
+ }
267
+ return best;
268
+ }
269
+ /** §2 — 24-bit where the terminal says it renders it. `COLORTERM` is the
270
+ * standard statement; anything else gets the 256 tier, which every
271
+ * terminal kiso docks in renders. */
272
+ export function colourTier(colorterm) {
273
+ const v = colorterm?.trim().toLowerCase();
274
+ return v === "truecolor" || v === "24bit" ? "24bit" : "256";
275
+ }
276
+ /** 0.47.1 (owner, 2026-10-07) — 24-bit where the terminal is KNOWN to
277
+ * render it, though it does not say so in COLORTERM. Windows Terminal is
278
+ * the case that found it: drawn in the 256 tier, the person's cream block
279
+ * became pink (index 224) and its gold edge olive (186). Each terminal is
280
+ * recognised by what it sets; the list is the reference implementation's.
281
+ * Inside tmux or screen only COLORTERM counts — the outer terminal's
282
+ * variables are inherited, its rendering is not. Apple Terminal says it
283
+ * in COLORTERM where it renders 24-bit (§2), and keeps the 256 tier
284
+ * otherwise. PURE: the environment and the platform are passed in. */
285
+ export function terminalTier(env, platform) {
286
+ if (colourTier(env.COLORTERM) === "24bit")
287
+ return "24bit";
288
+ const term = (env.TERM ?? "").toLowerCase();
289
+ if (env.TMUX !== undefined || term.startsWith("tmux") || term.startsWith("screen"))
290
+ return "256";
291
+ const program = (env.TERM_PROGRAM ?? "").toLowerCase();
292
+ const known = env.WT_SESSION !== undefined ||
293
+ env.ITERM_SESSION_ID !== undefined ||
294
+ env.WEZTERM_PANE !== undefined ||
295
+ env.KITTY_WINDOW_ID !== undefined ||
296
+ env.GHOSTTY_RESOURCES_DIR !== undefined ||
297
+ ["iterm.app", "wezterm", "ghostty", "kitty", "vscode", "warpterminal", "alacritty"].includes(program) ||
298
+ term.includes("ghostty") ||
299
+ term === "alacritty" ||
300
+ (env.TERMINAL_EMULATOR ?? "").toLowerCase() === "jetbrains-jediterm" ||
301
+ // a Windows console renders 24-bit even where Windows Terminal hosts
302
+ // it without WT_SESSION (cmd.exe from Win+R)
303
+ platform === "win32";
304
+ return known ? "24bit" : "256";
305
+ }
306
+ export function fg(c, tier) {
307
+ return tier === "24bit" ? `\x1b[38;2;${c.r};${c.g};${c.b}m` : `\x1b[38;5;${nearest256(c)}m`;
308
+ }
309
+ export function bg(c, tier) {
310
+ return tier === "24bit" ? `\x1b[48;2;${c.r};${c.g};${c.b}m` : `\x1b[48;5;${nearest256(c)}m`;
311
+ }
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, terminalTier, 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, terminalTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair } from "./graphite.js";
package/dist/md.d.ts CHANGED
@@ -39,7 +39,7 @@
39
39
  /** The block kinds. `fence-open`/`fence-line` are separate kinds on
40
40
  * purpose: a fence's rows must be able to freeze ONE AT A TIME, and
41
41
  * MD-1.4's `code-line` is the same shape for the same reason. */
42
- export type MdKind = "para" | "heading" | "list" | "table" | "quote" | "rule" | "fence-open" | "fence-line" | "fence-close" | "code-line";
42
+ export type MdKind = "para" | "heading" | "list" | "table" | "quote" | "rule" | "fence-open" | "fence-line" | "fence-close" | "code-line" | "refdef";
43
43
  /** One block: its SOURCE lines, never a rendered form. The render is a
44
44
  * pure function of (block, width), which is what makes the freeze
45
45
  * property a property of the scanner alone. */
@@ -70,10 +70,15 @@ export declare class MdStream {
70
70
  }
71
71
  /** The whole message at once — the freeze property's oracle, and the
72
72
  * path a non-streaming caller takes. */
73
- export declare function renderMarkdown(text: string, W: number): string[];
73
+ export declare function renderMarkdown(text: string, W: number, opts?: {
74
+ readonly hardBreaks?: boolean;
75
+ }): string[];
74
76
  /** One block's screen rows. Pure in (block, W) — this is the whole
75
77
  * freeze guarantee: same source, same width, same bytes, forever. */
76
- export declare function renderBlock(b: MdBlock, W: number): string[];
78
+ export declare function renderBlock(b: MdBlock, W: number, hardBreaks?: boolean): string[];
79
+ /** Graphite §5 (R2b): a heading block's level, or null — the prose column
80
+ * puts `§` in the mark column beside a `##`, which is outside the block. */
81
+ export declare function headingLevel(b: MdBlock): number | null;
77
82
  /**
78
83
  * The inline pass — the mono style table applied to one block's text.
79
84
  *
@@ -90,12 +95,18 @@ export declare function renderBlock(b: MdBlock, W: number): string[];
90
95
  * out, so a nested span can never strand it. Italic is the one span
91
96
  * that closes surgically (SGR 23), because it can.
92
97
  *
98
+ * Graphite R2f (owner, 2026-09-30) adds `***bold italic***`, code spans
99
+ * of any backtick run, a link's title (read and dropped), `<autolinks>`,
100
+ * `![images]` (named: `image` dim, the alt as a link, the url dim),
101
+ * `[reference][links]` (the text as a link, the label dim) and `<br>` (a
102
+ * line break).
103
+ *
93
104
  * Documented deviations from CommonMark: `_` never emphasizes (it is a
94
105
  * character in identifiers far more often than a marker in prose);
95
- * emphasis does not nest across a code span; `~~` is not a construct at
96
- * all — the markers are content (the strict tokenizer both reference
97
- * implementations converged on, taken to its honest conclusion, since
98
- * SGR 9's terminal support is too fragmented to promise).
106
+ * emphasis does not nest across a code span; `~~` makes its text dim
107
+ * (R2b), never SGR 9, whose terminal support is too fragmented to
108
+ * promise — Apple Terminal draws none; a shortcut reference `[label]`
109
+ * stays literal.
99
110
  */
100
111
  export declare function inlineSpans(text: string, base: string): string;
101
112
  /** Split a table line into cells. The `|` walls are found on the RAW