@markii/ansi 0.17.0-next.0 → 0.17.0-next.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.
- package/dist/failure-presentation.d.ts +13 -0
- package/dist/failure-presentation.js +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/render.d.ts +24 -0
- package/dist/render.js +52 -14
- package/package.json +5 -5
|
@@ -37,6 +37,8 @@ export declare function emptyInlineTitle(name: string): string;
|
|
|
37
37
|
export declare function invalidAttributeValueTitle(directive: string, attribute: string, value: string): string;
|
|
38
38
|
/** The marker text for a component-built image whose `src` was refused as unsafe. Wording identical to `@markii/html`'s `unsafeImageSrcTitle`. */
|
|
39
39
|
export declare function unsafeImageSrcTitle(directive: string): string;
|
|
40
|
+
/** The diagnostic text for a host `renderCode` callback that threw; the code block rendered as usual. Wording identical to `@markii/html`'s `codeRendererFailedTitle`. */
|
|
41
|
+
export declare function codeRendererFailedTitle(): string;
|
|
40
42
|
/** The diagnostic text for a registered block component written as an inline directive. Wording identical to `@markii/html`'s `formMismatchTitle`. */
|
|
41
43
|
export declare function formMismatchTitle(directive: string): string;
|
|
42
44
|
/**
|
|
@@ -71,3 +73,14 @@ export declare const NO_ENGINE_VIEW_LABEL = "no terminal view for";
|
|
|
71
73
|
* and is never printed in the note.
|
|
72
74
|
*/
|
|
73
75
|
export declare function noEngineViewTitle(directive: string, pack: string, engines: readonly string[]): string;
|
|
76
|
+
/**
|
|
77
|
+
* The fallback label for a directive of a pack whose terminal view script
|
|
78
|
+
* could not be loaded. Followed by the directive name in backticks, like the
|
|
79
|
+
* other fallback labels. `pack` is stripped by the caller's sanitizer.
|
|
80
|
+
*/
|
|
81
|
+
export declare function packLoadFailedLabel(pack: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* The diagnostic text for the same case. The loader's own line (what threw)
|
|
84
|
+
* is on the diagnostics surface already; this one names the directive.
|
|
85
|
+
*/
|
|
86
|
+
export declare function packLoadFailedTitle(directive: string, pack: string): string;
|
|
@@ -99,6 +99,10 @@ export function invalidAttributeValueTitle(directive, attribute, value) {
|
|
|
99
99
|
export function unsafeImageSrcTitle(directive) {
|
|
100
100
|
return `${directive}: image source was refused as unsafe and was not shown`;
|
|
101
101
|
}
|
|
102
|
+
/** The diagnostic text for a host `renderCode` callback that threw; the code block rendered as usual. Wording identical to `@markii/html`'s `codeRendererFailedTitle`. */
|
|
103
|
+
export function codeRendererFailedTitle() {
|
|
104
|
+
return 'code block: the host code renderer failed, so the plain block was shown';
|
|
105
|
+
}
|
|
102
106
|
/** The diagnostic text for a registered block component written as an inline directive. Wording identical to `@markii/html`'s `formMismatchTitle`. */
|
|
103
107
|
export function formMismatchTitle(directive) {
|
|
104
108
|
return `${directive}: block component was written inline and was not rendered`;
|
|
@@ -144,3 +148,18 @@ export function noEngineViewTitle(directive, pack, engines) {
|
|
|
144
148
|
: 'it has no views for any engine';
|
|
145
149
|
return `The ${pack} pack has no terminal view for this component; ${has}`;
|
|
146
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* The fallback label for a directive of a pack whose terminal view script
|
|
153
|
+
* could not be loaded. Followed by the directive name in backticks, like the
|
|
154
|
+
* other fallback labels. `pack` is stripped by the caller's sanitizer.
|
|
155
|
+
*/
|
|
156
|
+
export function packLoadFailedLabel(pack) {
|
|
157
|
+
return `the \`${pack}\` pack failed to load, no terminal view for`;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The diagnostic text for the same case. The loader's own line (what threw)
|
|
161
|
+
* is on the diagnostics surface already; this one names the directive.
|
|
162
|
+
*/
|
|
163
|
+
export function packLoadFailedTitle(directive, pack) {
|
|
164
|
+
return `The ${pack} pack failed to load in the terminal, so ${directive} has no view`;
|
|
165
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { renderMarkToAnsi, renderMarkNodeToAnsi, renderMarkInlineToAnsi, buildMarkElement, type RenderMarkOptions, } from './render.js';
|
|
1
|
+
export { renderMarkToAnsi, renderMarkNodeToAnsi, renderMarkInlineToAnsi, buildMarkElement, type RenderCode, type RenderCodeBlock, type RenderMarkOptions, } from './render.js';
|
|
2
2
|
export { fg, colorize, bold, dim, italic, underline, inverse, hyperlink, detectColorLevel, resolveColorOption, type ColorLevel, type ColorOption, type AnsiColor, } from './ansi.js';
|
|
3
3
|
export { defaultAnsiTheme, type AnsiTheme, type Tier1Token } from './theme.js';
|
|
4
4
|
export { BOX_STYLES, isBoxStyle, glyphsFor, type BoxStyle, type FrameEdges, type GlyphSet, } from './glyphs.js';
|
package/dist/render.d.ts
CHANGED
|
@@ -14,12 +14,36 @@ export interface ElementTabPanel {
|
|
|
14
14
|
label: string;
|
|
15
15
|
body: ReactNode;
|
|
16
16
|
}
|
|
17
|
+
/** A fenced code block as offered to `RenderMarkOptions.renderCode`. */
|
|
18
|
+
export interface RenderCodeBlock {
|
|
19
|
+
/** The info-string language, or `undefined` when the fence names none. */
|
|
20
|
+
readonly lang: string | undefined;
|
|
21
|
+
/** The code text, control characters removed and tabs expanded. */
|
|
22
|
+
readonly code: string;
|
|
23
|
+
}
|
|
24
|
+
/** The `renderCode` callback: a string for a code block, or `undefined` to draw it as usual. */
|
|
25
|
+
export type RenderCode = (code: RenderCodeBlock) => string | undefined;
|
|
17
26
|
/**
|
|
18
27
|
* Options for `renderMarkToAnsi`/`renderMarkNodeToAnsi`/`renderMarkInlineToAnsi`.
|
|
19
28
|
* `store`/`vault` are offered both here and as positional parameters; when
|
|
20
29
|
* both are given, the option wins.
|
|
21
30
|
*/
|
|
22
31
|
export interface RenderMarkOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Replaces the output for an ordinary fenced code block. Called with the
|
|
34
|
+
* info-string language (`undefined` when the fence names none) and the
|
|
35
|
+
* code text, already stripped of control characters and with tabs
|
|
36
|
+
* expanded (the engine's own sanitizer, so author escapes cannot reach
|
|
37
|
+
* the terminal through a host that echoes it). Return a string to use it,
|
|
38
|
+
* or `undefined` to draw the block as usual. The returned string is
|
|
39
|
+
* HOST-TRUSTED and inserted as-is, so the host may add its own SGR
|
|
40
|
+
* styling and owns keeping control characters out of anything it adds.
|
|
41
|
+
* Never called for script fences or inline code. A callback that throws
|
|
42
|
+
* draws the block as usual and reports a `'code-renderer-failed'` event
|
|
43
|
+
* through `onDiagnostic`. Element results are not supported: fenced blocks
|
|
44
|
+
* are drawn as strings in this engine.
|
|
45
|
+
*/
|
|
46
|
+
readonly renderCode?: RenderCode;
|
|
23
47
|
readonly width?: number;
|
|
24
48
|
readonly color?: ColorOption;
|
|
25
49
|
readonly theme?: AnsiTheme;
|
package/dist/render.js
CHANGED
|
@@ -15,7 +15,7 @@ import { sanitizeBlockText, sanitizeUrlText, stripControlCharacters, } from './s
|
|
|
15
15
|
import { readRegistryComponent, registryLayoutAxis, registrySelfLayout, resolveDirectiveAlias, } from './registry.js';
|
|
16
16
|
import { applyLayout, resolveLayoutAttributes, } from './layout.js';
|
|
17
17
|
import { resolveScopedPath } from './resolve.js';
|
|
18
|
-
import { dataStateSuffix, emptyInlineTitle, failureToken, formMismatchTitle, NO_ENGINE_VIEW_LABEL, noEngineViewTitle, invalidAttributeValueLabel, invalidAttributeValueTitle, unsafeImageSrcLabel, unsafeImageSrcTitle, } from './failure-presentation.js';
|
|
18
|
+
import { dataStateSuffix, emptyInlineTitle, failureToken, codeRendererFailedTitle, formMismatchTitle, NO_ENGINE_VIEW_LABEL, noEngineViewTitle, packLoadFailedLabel, packLoadFailedTitle, invalidAttributeValueLabel, invalidAttributeValueTitle, unsafeImageSrcLabel, unsafeImageSrcTitle, } from './failure-presentation.js';
|
|
19
19
|
import { createUnavailableComponentLookup, formatValue, getContract, reportDiagnostic, } from '@markii/stdlib';
|
|
20
20
|
import { resolveImageAttribute, } from './image-resolve.js';
|
|
21
21
|
import { resolveHrefAttribute } from './href-resolve.js';
|
|
@@ -208,8 +208,11 @@ function isRegisteredInline(entry) {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
/** The fallback label line, worded for the reason and the form the directive was written in. Plain text (no escapes). */
|
|
211
|
-
function fallbackLabel(name, inline, reason) {
|
|
211
|
+
function fallbackLabel(name, inline, reason, pack) {
|
|
212
212
|
const code = `\`${stripControlCharacters(name)}\``;
|
|
213
|
+
if (reason === 'pack-load-failed') {
|
|
214
|
+
return `${packLoadFailedLabel(stripControlCharacters(pack ?? ''))} ${code}`;
|
|
215
|
+
}
|
|
213
216
|
if (reason === 'no-engine-view')
|
|
214
217
|
return `${NO_ENGINE_VIEW_LABEL} ${code}`;
|
|
215
218
|
if (reason === 'form-mismatch') {
|
|
@@ -220,8 +223,8 @@ function fallbackLabel(name, inline, reason) {
|
|
|
220
223
|
return `unknown component ${code}`;
|
|
221
224
|
}
|
|
222
225
|
/** The unknown-directive fallback: dim inline text for a text directive, a dashed dim frame for a block directive. Wording matches `@markii/html`'s `fallbackLabel` exactly. */
|
|
223
|
-
function unknownDirective(name, inline, childrenText, reason, width, color, glyphs) {
|
|
224
|
-
const label = fallbackLabel(name, inline, reason);
|
|
226
|
+
function unknownDirective(name, inline, childrenText, reason, width, color, glyphs, pack) {
|
|
227
|
+
const label = fallbackLabel(name, inline, reason, pack);
|
|
225
228
|
if (inline) {
|
|
226
229
|
return childrenText === ''
|
|
227
230
|
? dim(label, color)
|
|
@@ -380,9 +383,11 @@ function renderDirectiveContentString(name, kind, isBlock, attributes, childrenT
|
|
|
380
383
|
reportDiagnostic(wctx.onDiagnostic, {
|
|
381
384
|
kind: 'no-engine-view',
|
|
382
385
|
directive: name,
|
|
383
|
-
message:
|
|
386
|
+
message: missingView.loadFailed
|
|
387
|
+
? packLoadFailedTitle(name, missingView.pack)
|
|
388
|
+
: noEngineViewTitle(name, missingView.pack, missingView.engines),
|
|
384
389
|
});
|
|
385
|
-
return unknownDirective(name, inline, safeChildrenText(childrenText), 'no-engine-view', width, wctx.color, wctx.glyphs);
|
|
390
|
+
return unknownDirective(name, inline, safeChildrenText(childrenText), missingView.loadFailed ? 'pack-load-failed' : 'no-engine-view', width, wctx.color, wctx.glyphs, missingView.pack);
|
|
386
391
|
}
|
|
387
392
|
return unknownDirective(name || '(unnamed)', inline, safeChildrenText(childrenText), 'unregistered', width, wctx.color, wctx.glyphs);
|
|
388
393
|
}
|
|
@@ -536,6 +541,9 @@ function renderBlockString(node, width, indent, wctx) {
|
|
|
536
541
|
const marker = renderScriptMarker(node, wctx.color, wctx.glyphs);
|
|
537
542
|
if (marker !== undefined)
|
|
538
543
|
return marker;
|
|
544
|
+
const custom = applyRenderCode(node, wctx);
|
|
545
|
+
if (custom !== undefined)
|
|
546
|
+
return custom;
|
|
539
547
|
return renderCodeBlock(node, wctx.color);
|
|
540
548
|
}
|
|
541
549
|
return wrapText(renderInlineChildrenString(node.children ?? [], width, indent, wctx), width).join('\n');
|
|
@@ -648,6 +656,35 @@ function renderScriptMarker(node, color, glyphs) {
|
|
|
648
656
|
return undefined;
|
|
649
657
|
}
|
|
650
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Offers one ordinary fenced code block to the host's `renderCode`. The
|
|
661
|
+
* text handed over is already run through `sanitizeBlockText` (control
|
|
662
|
+
* characters dropped, tabs expanded), so a host that echoes it cannot
|
|
663
|
+
* smuggle author escapes into the terminal. Returns the host's string, or
|
|
664
|
+
* `undefined` to draw the block as usual: no callback, a decline, a
|
|
665
|
+
* non-string result, or a throw (reported through `onDiagnostic`).
|
|
666
|
+
*/
|
|
667
|
+
function applyRenderCode(node, wctx) {
|
|
668
|
+
if (!wctx.renderCode)
|
|
669
|
+
return undefined;
|
|
670
|
+
const codeNode = findChildElement(node, 'code');
|
|
671
|
+
if (!codeNode)
|
|
672
|
+
return undefined;
|
|
673
|
+
try {
|
|
674
|
+
const result = wctx.renderCode({
|
|
675
|
+
lang: getLanguage(codeNode) || undefined,
|
|
676
|
+
code: sanitizeBlockText(getCodeText(codeNode)),
|
|
677
|
+
});
|
|
678
|
+
return typeof result === 'string' ? result : undefined;
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
reportDiagnostic(wctx.onDiagnostic, {
|
|
682
|
+
kind: 'code-renderer-failed',
|
|
683
|
+
message: codeRendererFailedTitle(),
|
|
684
|
+
});
|
|
685
|
+
return undefined;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
651
688
|
function renderCodeBlock(node, color) {
|
|
652
689
|
const codeNode = findChildElement(node, 'code');
|
|
653
690
|
const language = getLanguage(codeNode);
|
|
@@ -1355,7 +1392,7 @@ function renderFailureFallback(error, color) {
|
|
|
1355
1392
|
const message = error instanceof Error ? error.message : String(error);
|
|
1356
1393
|
return `${dim('failed to render document', color)}\n${stripControlCharacters(message)}\n`;
|
|
1357
1394
|
}
|
|
1358
|
-
function buildWalkContext(registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable) {
|
|
1395
|
+
function buildWalkContext(registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, renderCode) {
|
|
1359
1396
|
return {
|
|
1360
1397
|
registry,
|
|
1361
1398
|
scope,
|
|
@@ -1367,6 +1404,7 @@ function buildWalkContext(registry, scope, color, theme, glyphs, interactive, re
|
|
|
1367
1404
|
resolveHref,
|
|
1368
1405
|
onDiagnostic,
|
|
1369
1406
|
unavailable,
|
|
1407
|
+
renderCode,
|
|
1370
1408
|
focusCounter: { current: 0 },
|
|
1371
1409
|
focusables: undefined,
|
|
1372
1410
|
};
|
|
@@ -1374,13 +1412,13 @@ function buildWalkContext(registry, scope, color, theme, glyphs, interactive, re
|
|
|
1374
1412
|
/** The columns assumed when a caller supplies no `width` option. */
|
|
1375
1413
|
const DEFAULT_WIDTH = 80;
|
|
1376
1414
|
/** Builds the root Ink element for `root`, given every resolved render input. Exported for `buildMarkElement` (the live-viewer entry point, phase 3) as well as this module's own three string-producing entry points. */
|
|
1377
|
-
function buildRootElement(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width) {
|
|
1378
|
-
const wctx = buildWalkContext(registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable);
|
|
1415
|
+
function buildRootElement(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width, renderCode) {
|
|
1416
|
+
const wctx = buildWalkContext(registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, renderCode);
|
|
1379
1417
|
const body = renderBlocksElement(root.children, width, '', wctx);
|
|
1380
1418
|
return (_jsx(Box, { width: width, flexDirection: "column", children: body ?? _jsx(Text, { children: " " }) }));
|
|
1381
1419
|
}
|
|
1382
|
-
async function renderRootToString(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width) {
|
|
1383
|
-
const element = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width);
|
|
1420
|
+
async function renderRootToString(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width, renderCode) {
|
|
1421
|
+
const element = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width, renderCode);
|
|
1384
1422
|
const raw = await renderInkToString(element, width);
|
|
1385
1423
|
return finalizeOutput(raw);
|
|
1386
1424
|
}
|
|
@@ -1405,7 +1443,7 @@ async function renderRootToString(root, registry, scope, color, theme, glyphs, i
|
|
|
1405
1443
|
export async function renderMarkToAnsi(text, registry = defaultAnsiRegistry, store, vault, options) {
|
|
1406
1444
|
const color = resolveColorOption(options?.color);
|
|
1407
1445
|
try {
|
|
1408
|
-
return await renderRootToString(toHast(text), registry, { store: options?.store ?? store, vault: options?.vault ?? vault }, color, options?.theme ?? defaultAnsiTheme, glyphsFor(options?.box), options?.interactive ?? false, options?.resolveImageSrc, options?.resolveHref, options?.onDiagnostic, createUnavailableComponentLookup(options?.unavailableComponents), options?.width ?? DEFAULT_WIDTH);
|
|
1446
|
+
return await renderRootToString(toHast(text), registry, { store: options?.store ?? store, vault: options?.vault ?? vault }, color, options?.theme ?? defaultAnsiTheme, glyphsFor(options?.box), options?.interactive ?? false, options?.resolveImageSrc, options?.resolveHref, options?.onDiagnostic, createUnavailableComponentLookup(options?.unavailableComponents), options?.width ?? DEFAULT_WIDTH, options?.renderCode);
|
|
1409
1447
|
}
|
|
1410
1448
|
catch (error) {
|
|
1411
1449
|
return renderFailureFallback(error, color);
|
|
@@ -1420,7 +1458,7 @@ export async function renderMarkToAnsi(text, registry = defaultAnsiRegistry, sto
|
|
|
1420
1458
|
export async function renderMarkNodeToAnsi(node, registry = defaultAnsiRegistry, store, vault, options) {
|
|
1421
1459
|
const color = resolveColorOption(options?.color);
|
|
1422
1460
|
try {
|
|
1423
|
-
return await renderRootToString(nodeOrRootToHast(node), registry, { store: options?.store ?? store, vault: options?.vault ?? vault }, color, options?.theme ?? defaultAnsiTheme, glyphsFor(options?.box), options?.interactive ?? false, options?.resolveImageSrc, options?.resolveHref, options?.onDiagnostic, createUnavailableComponentLookup(options?.unavailableComponents), options?.width ?? DEFAULT_WIDTH);
|
|
1461
|
+
return await renderRootToString(nodeOrRootToHast(node), registry, { store: options?.store ?? store, vault: options?.vault ?? vault }, color, options?.theme ?? defaultAnsiTheme, glyphsFor(options?.box), options?.interactive ?? false, options?.resolveImageSrc, options?.resolveHref, options?.onDiagnostic, createUnavailableComponentLookup(options?.unavailableComponents), options?.width ?? DEFAULT_WIDTH, options?.renderCode);
|
|
1424
1462
|
}
|
|
1425
1463
|
catch (error) {
|
|
1426
1464
|
return renderFailureFallback(error, color);
|
|
@@ -1484,7 +1522,7 @@ export function buildMarkElement(document, registry = defaultAnsiRegistry, optio
|
|
|
1484
1522
|
root = { type: 'root', children: [] };
|
|
1485
1523
|
}
|
|
1486
1524
|
const unavailable = createUnavailableComponentLookup(options?.unavailableComponents);
|
|
1487
|
-
const content = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, options?.resolveImageSrc, options?.resolveHref, options?.onDiagnostic, unavailable, width);
|
|
1525
|
+
const content = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, options?.resolveImageSrc, options?.resolveHref, options?.onDiagnostic, unavailable, width, options?.renderCode);
|
|
1488
1526
|
if (!interactive)
|
|
1489
1527
|
return content;
|
|
1490
1528
|
// The walk above already assigned every focusable component's `focusId`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/ansi",
|
|
3
|
-
"version": "0.17.0-next.
|
|
3
|
+
"version": "0.17.0-next.1",
|
|
4
4
|
"description": "The terminal renderer for Markii (.mk.md): a registry-driven hast-to-terminal engine built on Ink. Renders a note once as a string with optional color, for a pipe or a log, or as a live Ink tree a host can mount.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -51,15 +51,15 @@
|
|
|
51
51
|
"regenerate:fixtures": "npm run build:dist && node scripts/regenerate-ansi-fixtures.ts"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@markii/core": "^0.17.0-next.
|
|
55
|
-
"@markii/pack": "^0.17.0-next.
|
|
56
|
-
"@markii/stdlib": "^0.17.0-next.
|
|
54
|
+
"@markii/core": "^0.17.0-next.1",
|
|
55
|
+
"@markii/pack": "^0.17.0-next.1",
|
|
56
|
+
"@markii/stdlib": "^0.17.0-next.1",
|
|
57
57
|
"ink": "^7.1.1",
|
|
58
58
|
"react": "^19.2.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/hast": "^3.0.4",
|
|
62
|
-
"@markii/runtime": "^0.17.0-next.
|
|
62
|
+
"@markii/runtime": "^0.17.0-next.1",
|
|
63
63
|
"@types/react": "^19.2.0"
|
|
64
64
|
}
|
|
65
65
|
}
|