@markii/ansi 0.17.0-next.1 → 0.17.0-next.2
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 +2 -0
- package/dist/failure-presentation.js +4 -0
- package/dist/interactive.d.ts +19 -1
- package/dist/interactive.js +30 -0
- package/dist/pack-loader.js +3 -0
- package/dist/pack-script.d.ts +1 -1
- package/dist/pack-script.js +12 -2
- package/dist/registry.d.ts +10 -0
- package/dist/render.d.ts +7 -0
- package/dist/render.js +94 -7
- package/package.json +5 -5
|
@@ -39,6 +39,8 @@ export declare function invalidAttributeValueTitle(directive: string, attribute:
|
|
|
39
39
|
export declare function unsafeImageSrcTitle(directive: string): string;
|
|
40
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
41
|
export declare function codeRendererFailedTitle(): string;
|
|
42
|
+
/** The diagnostic text for a directive that names no registered component; the unknown-component box was shown. */
|
|
43
|
+
export declare function unknownDirectiveTitle(directive: string): string;
|
|
42
44
|
/** The diagnostic text for a registered block component written as an inline directive. Wording identical to `@markii/html`'s `formMismatchTitle`. */
|
|
43
45
|
export declare function formMismatchTitle(directive: string): string;
|
|
44
46
|
/**
|
|
@@ -103,6 +103,10 @@ export function unsafeImageSrcTitle(directive) {
|
|
|
103
103
|
export function codeRendererFailedTitle() {
|
|
104
104
|
return 'code block: the host code renderer failed, so the plain block was shown';
|
|
105
105
|
}
|
|
106
|
+
/** The diagnostic text for a directive that names no registered component; the unknown-component box was shown. */
|
|
107
|
+
export function unknownDirectiveTitle(directive) {
|
|
108
|
+
return `${directive}: unknown component, shown as a placeholder`;
|
|
109
|
+
}
|
|
106
110
|
/** The diagnostic text for a registered block component written as an inline directive. Wording identical to `@markii/html`'s `formMismatchTitle`. */
|
|
107
111
|
export function formMismatchTitle(directive) {
|
|
108
112
|
return `${directive}: block component was written inline and was not rendered`;
|
package/dist/interactive.d.ts
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import { useInput } from 'ink';
|
|
2
3
|
import type { DOMElement } from 'ink';
|
|
3
4
|
import type { AnsiTheme } from './theme.js';
|
|
4
5
|
import type { ColorLevel } from './ansi.js';
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a focusable pack component's live element. It draws a two-column
|
|
8
|
+
* gutter (the accent marker while the component has focus, blanks
|
|
9
|
+
* otherwise) and names the component's focus id for `usePackInput`. The
|
|
10
|
+
* pack draws inside the width that is left.
|
|
11
|
+
*/
|
|
12
|
+
export declare function PackFocusScope({ focusId, children, }: {
|
|
13
|
+
focusId: number;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}): ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Ink's `useInput`, for a terminal pack: the same handler and options, but
|
|
18
|
+
* keys reach the handler only while the pack component whose subtree calls
|
|
19
|
+
* it has focus. Outside a focusable pack component it never fires. A pack
|
|
20
|
+
* reads this as `__markiiInk.useInput`, so it never sees raw stdin.
|
|
21
|
+
*/
|
|
22
|
+
export declare function usePackInput(handler: Parameters<typeof useInput>[0], options?: Parameters<typeof useInput>[1]): void;
|
|
5
23
|
/** Whether the component identified by `focusId` is the currently focused one. `undefined` (a non-interactive render, or a component that never got a `focusId`) is never focused. */
|
|
6
24
|
export declare function useIsFocused(focusId: number | undefined): boolean;
|
|
7
25
|
/** Wraps `text` in the accent-colored focus marker when interactive AND `focusId` is the focused one; otherwise returns `text` unchanged. */
|
|
@@ -29,7 +47,7 @@ export declare function useFocusAnchor(focusId: number | undefined): (node: DOME
|
|
|
29
47
|
* yet (mount, or a reader who has not switched panels).
|
|
30
48
|
*/
|
|
31
49
|
export interface FocusableDescriptor {
|
|
32
|
-
kind: 'tabs' | 'details';
|
|
50
|
+
kind: 'tabs' | 'details' | 'pack';
|
|
33
51
|
label: string;
|
|
34
52
|
}
|
|
35
53
|
export interface InteractiveRootProps {
|
package/dist/interactive.js
CHANGED
|
@@ -11,6 +11,36 @@ const FocusContext = createContext({
|
|
|
11
11
|
reportActiveLabel: () => { },
|
|
12
12
|
registerAnchor: () => { },
|
|
13
13
|
});
|
|
14
|
+
/**
|
|
15
|
+
* The `focusId` of the pack component a subtree belongs to, or `undefined`
|
|
16
|
+
* outside one. Provided by `PackFocusScope`; read only by `usePackInput`.
|
|
17
|
+
*/
|
|
18
|
+
const PackFocusContext = createContext(undefined);
|
|
19
|
+
/**
|
|
20
|
+
* Wraps a focusable pack component's live element. It draws a two-column
|
|
21
|
+
* gutter (the accent marker while the component has focus, blanks
|
|
22
|
+
* otherwise) and names the component's focus id for `usePackInput`. The
|
|
23
|
+
* pack draws inside the width that is left.
|
|
24
|
+
*/
|
|
25
|
+
export function PackFocusScope({ focusId, children, }) {
|
|
26
|
+
const gutter = useFocusMarker('▌ ', focusId);
|
|
27
|
+
const focused = useIsFocused(focusId);
|
|
28
|
+
return (_jsx(PackFocusContext.Provider, { value: focusId, children: _jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: focused ? gutter : ' ' }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: children })] }) }));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Ink's `useInput`, for a terminal pack: the same handler and options, but
|
|
32
|
+
* keys reach the handler only while the pack component whose subtree calls
|
|
33
|
+
* it has focus. Outside a focusable pack component it never fires. A pack
|
|
34
|
+
* reads this as `__markiiInk.useInput`, so it never sees raw stdin.
|
|
35
|
+
*/
|
|
36
|
+
export function usePackInput(handler, options = {}) {
|
|
37
|
+
const focusId = useContext(PackFocusContext);
|
|
38
|
+
const focused = useIsFocused(focusId);
|
|
39
|
+
useInput(handler, {
|
|
40
|
+
...options,
|
|
41
|
+
isActive: focused && options.isActive !== false,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
14
44
|
/** Whether the component identified by `focusId` is the currently focused one. `undefined` (a non-interactive render, or a component that never got a `focusId`) is never focused. */
|
|
15
45
|
export function useIsFocused(focusId) {
|
|
16
46
|
const api = useContext(FocusContext);
|
package/dist/pack-loader.js
CHANGED
|
@@ -142,6 +142,9 @@ export function loadAnsiPack(manifest, componentModules) {
|
|
|
142
142
|
const inline = kind === undefined ? module.inline : kind === 'inline';
|
|
143
143
|
if (inline !== undefined)
|
|
144
144
|
entry.inline = inline;
|
|
145
|
+
// Focus only makes sense for a block view.
|
|
146
|
+
if (module.focusable === true && inline !== true)
|
|
147
|
+
entry.focusable = true;
|
|
145
148
|
entries[composed.name] = entry;
|
|
146
149
|
}
|
|
147
150
|
return { registry: createAnsiRegistry(entries) };
|
package/dist/pack-script.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { AnsiPackToInstall } from './pack-loader.js';
|
|
|
6
6
|
* globalThis.__markiiRegisterTerminalPack(manifestJson, componentModules)
|
|
7
7
|
*
|
|
8
8
|
* with the raw `pack.json` text and an own-property map of
|
|
9
|
-
* `{ component, inline? }` keyed by local component name, the same shape
|
|
9
|
+
* `{ component, inline?, focusable? }` keyed by local component name, the same shape
|
|
10
10
|
* `__markiiRegisterPack` takes for the webview. React and Ink are NOT
|
|
11
11
|
* bundled into the script. The host sets `globalThis.__markiiReactInk`
|
|
12
12
|
* (the React instance Ink uses) and `globalThis.__markiiInk` (Ink's
|
package/dist/pack-script.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as Ink from 'ink';
|
|
3
|
+
import { usePackInput } from './interactive.js';
|
|
3
4
|
import { parsePackManifest } from '@markii/pack';
|
|
4
5
|
/**
|
|
5
6
|
* The terminal pack registration contract (docs/packs.md). A pack's
|
|
@@ -8,7 +9,7 @@ import { parsePackManifest } from '@markii/pack';
|
|
|
8
9
|
* globalThis.__markiiRegisterTerminalPack(manifestJson, componentModules)
|
|
9
10
|
*
|
|
10
11
|
* with the raw `pack.json` text and an own-property map of
|
|
11
|
-
* `{ component, inline? }` keyed by local component name, the same shape
|
|
12
|
+
* `{ component, inline?, focusable? }` keyed by local component name, the same shape
|
|
12
13
|
* `__markiiRegisterPack` takes for the webview. React and Ink are NOT
|
|
13
14
|
* bundled into the script. The host sets `globalThis.__markiiReactInk`
|
|
14
15
|
* (the React instance Ink uses) and `globalThis.__markiiInk` (Ink's
|
|
@@ -32,7 +33,9 @@ export const TERMINAL_INK_GLOBAL = '__markiiInk';
|
|
|
32
33
|
export function installTerminalPackGlobals() {
|
|
33
34
|
const g = globalThis;
|
|
34
35
|
g[TERMINAL_REACT_GLOBAL] = React;
|
|
35
|
-
|
|
36
|
+
// Ink's exports with `useInput` replaced by the focus-gated one: a pack
|
|
37
|
+
// gets keys while its component has focus and never raw stdin.
|
|
38
|
+
g[TERMINAL_INK_GLOBAL] = { ...Ink, useInput: usePackInput };
|
|
36
39
|
}
|
|
37
40
|
function isPlainObject(value) {
|
|
38
41
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -52,9 +55,16 @@ function toComponentModules(value) {
|
|
|
52
55
|
if (Object.hasOwn(entry, 'inline') && typeof entry.inline !== 'boolean') {
|
|
53
56
|
return undefined;
|
|
54
57
|
}
|
|
58
|
+
if (Object.hasOwn(entry, 'focusable') &&
|
|
59
|
+
typeof entry.focusable !== 'boolean') {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
55
62
|
out[key] = {
|
|
56
63
|
component: entry.component,
|
|
57
64
|
...(typeof entry.inline === 'boolean' ? { inline: entry.inline } : {}),
|
|
65
|
+
...(typeof entry.focusable === 'boolean'
|
|
66
|
+
? { focusable: entry.focusable }
|
|
67
|
+
: {}),
|
|
58
68
|
};
|
|
59
69
|
}
|
|
60
70
|
return out;
|
package/dist/registry.d.ts
CHANGED
|
@@ -171,6 +171,16 @@ export interface AnsiRegistryEntry {
|
|
|
171
171
|
* Ink primitives Yoga can reflow.
|
|
172
172
|
*/
|
|
173
173
|
selfLayout?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Marks a BLOCK component whose returned Ink element the live viewer
|
|
176
|
+
* mounts and lets take focus, instead of drawing it as a static
|
|
177
|
+
* snapshot. It joins the focus order (up/down/j/k) and, while focused,
|
|
178
|
+
* its element's `useInput` calls receive keys (a pack reads
|
|
179
|
+
* `__markiiInk.useInput`, which is gated this way). In a render-once
|
|
180
|
+
* pass, or inside a framed body (a callout, a card) it renders as
|
|
181
|
+
* before: a snapshot with no focus.
|
|
182
|
+
*/
|
|
183
|
+
focusable?: boolean;
|
|
174
184
|
}
|
|
175
185
|
/** One alias: a second name for an existing component, optionally carrying preset attributes. */
|
|
176
186
|
export interface RegistryAlias {
|
package/dist/render.d.ts
CHANGED
|
@@ -57,6 +57,13 @@ export interface RenderMarkOptions {
|
|
|
57
57
|
readonly resolveImageSrc?: ResolveImageSrc;
|
|
58
58
|
readonly resolveHref?: ResolveHref;
|
|
59
59
|
readonly onDiagnostic?: OnDiagnostic;
|
|
60
|
+
/**
|
|
61
|
+
* Also report a directive that names no registered component as an
|
|
62
|
+
* `'unknown-directive'` event. Off by default: the fallback box already
|
|
63
|
+
* marks it in the output. A host that places findings in the note's
|
|
64
|
+
* source (a language server) turns it on.
|
|
65
|
+
*/
|
|
66
|
+
readonly reportUnknownDirectives?: boolean;
|
|
60
67
|
/**
|
|
61
68
|
* Components of installed packs that this render cannot draw, as plain
|
|
62
69
|
* data from the host. A directive named here renders the usual fallback
|
package/dist/render.js
CHANGED
|
@@ -15,13 +15,13 @@ 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, codeRendererFailedTitle, formMismatchTitle, NO_ENGINE_VIEW_LABEL, noEngineViewTitle, packLoadFailedLabel, packLoadFailedTitle, 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, unknownDirectiveTitle, 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';
|
|
22
22
|
import { defaultAnsiRegistry } from './components/index.js';
|
|
23
23
|
import { renderInkToString, renderInkToStringSync } from './ink-string.js';
|
|
24
|
-
import { InteractiveRoot } from './interactive.js';
|
|
24
|
+
import { InteractiveRoot, PackFocusScope } from './interactive.js';
|
|
25
25
|
import { glyphsFor } from './glyphs.js';
|
|
26
26
|
import { InteractiveTabs } from './components/tabs.js';
|
|
27
27
|
import { InteractiveDetails } from './components/details.js';
|
|
@@ -126,6 +126,29 @@ function nodeOrRootToHast(node) {
|
|
|
126
126
|
}
|
|
127
127
|
return { type: 'root', children };
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* A walk context whose `onDiagnostic` stamps each event with the source
|
|
131
|
+
* range of `element`, unless an inner directive already did. This is how a
|
|
132
|
+
* host (the language server) places a finding in the note without any
|
|
133
|
+
* component knowing about positions. A node with no position, or a render
|
|
134
|
+
* with no `onDiagnostic`, returns `wctx` itself.
|
|
135
|
+
*/
|
|
136
|
+
function withNodePosition(wctx, element) {
|
|
137
|
+
const downstream = wctx.onDiagnostic;
|
|
138
|
+
const range = element.position;
|
|
139
|
+
if (downstream === undefined || range === undefined)
|
|
140
|
+
return wctx;
|
|
141
|
+
const position = {
|
|
142
|
+
startLine: range.start.line,
|
|
143
|
+
startColumn: range.start.column,
|
|
144
|
+
endLine: range.end.line,
|
|
145
|
+
endColumn: range.end.column,
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
...wctx,
|
|
149
|
+
onDiagnostic: (event) => downstream(event.position === undefined ? { ...event, position } : event),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
129
152
|
function nextFocusId(wctx) {
|
|
130
153
|
const id = wctx.focusCounter.current;
|
|
131
154
|
wctx.focusCounter.current += 1;
|
|
@@ -389,6 +412,11 @@ function renderDirectiveContentString(name, kind, isBlock, attributes, childrenT
|
|
|
389
412
|
});
|
|
390
413
|
return unknownDirective(name, inline, safeChildrenText(childrenText), missingView.loadFailed ? 'pack-load-failed' : 'no-engine-view', width, wctx.color, wctx.glyphs, missingView.pack);
|
|
391
414
|
}
|
|
415
|
+
reportDiagnostic(wctx.onDiagnostic, {
|
|
416
|
+
kind: 'unknown-directive',
|
|
417
|
+
directive: name || '(unnamed)',
|
|
418
|
+
message: unknownDirectiveTitle(name || '(unnamed)'),
|
|
419
|
+
});
|
|
392
420
|
return unknownDirective(name || '(unnamed)', inline, safeChildrenText(childrenText), 'unregistered', width, wctx.color, wctx.glyphs);
|
|
393
421
|
}
|
|
394
422
|
if (isFormMismatch(entry, kind)) {
|
|
@@ -812,7 +840,8 @@ function renderBlocksString(nodes, width, indent, wctx) {
|
|
|
812
840
|
}
|
|
813
841
|
return blocks.join('\n\n');
|
|
814
842
|
}
|
|
815
|
-
function renderDirectiveString(element, width, indent,
|
|
843
|
+
function renderDirectiveString(element, width, indent, walk) {
|
|
844
|
+
const wctx = withNodePosition(walk, element);
|
|
816
845
|
const written = stringProperty(element, 'data-mk-name') ?? '';
|
|
817
846
|
const kind = stringProperty(element, 'data-mk-kind');
|
|
818
847
|
const rawAttributes = parseAttributes(stringProperty(element, 'data-mk-attrs'));
|
|
@@ -1241,7 +1270,8 @@ function renderFigureElement(element, attributes, width, indent, wctx) {
|
|
|
1241
1270
|
return (_jsxs(Box, { flexDirection: "column", children: [headerElement, body] }));
|
|
1242
1271
|
}
|
|
1243
1272
|
/** Renders one directive as a real Ink element (block position only). Dispatches `row`/`tabs`/`details`/`card`/`figure` to their real builders; every other directive falls back to the string engine, wrapped in one `<Text>`. */
|
|
1244
|
-
function renderDirectiveElement(element, width, indent,
|
|
1273
|
+
function renderDirectiveElement(element, width, indent, walk) {
|
|
1274
|
+
const wctx = withNodePosition(walk, element);
|
|
1245
1275
|
const written = stringProperty(element, 'data-mk-name') ?? '';
|
|
1246
1276
|
const kind = stringProperty(element, 'data-mk-kind');
|
|
1247
1277
|
const rawAttributes = parseAttributes(stringProperty(element, 'data-mk-attrs'));
|
|
@@ -1312,8 +1342,48 @@ function renderDirectiveElement(element, width, indent, wctx) {
|
|
|
1312
1342
|
return styledText(componentError(name || '(unnamed)', false, '', width, wctx.color, wctx.glyphs));
|
|
1313
1343
|
}
|
|
1314
1344
|
}
|
|
1345
|
+
if (entry?.focusable === true && !formMismatch && wctx.interactive) {
|
|
1346
|
+
const focusable = renderFocusablePackElement(element, name, aliased, width, indent, wctx);
|
|
1347
|
+
if (focusable !== undefined)
|
|
1348
|
+
return focusable;
|
|
1349
|
+
}
|
|
1315
1350
|
return styledText(renderDirectiveString(element, width, indent, wctx));
|
|
1316
1351
|
}
|
|
1352
|
+
/**
|
|
1353
|
+
* A `focusable` registry component in the live viewer: the component runs
|
|
1354
|
+
* as usual, but an Ink element it returns is mounted (not drawn once) in a
|
|
1355
|
+
* `PackFocusScope`, so it joins the focus order and receives keys while
|
|
1356
|
+
* focused. Returns `undefined` when the component returns a string or
|
|
1357
|
+
* throws, so the caller falls back to the string path (which reports the
|
|
1358
|
+
* failure the usual way). The component draws two columns narrower: the
|
|
1359
|
+
* scope's gutter carries the focus marker.
|
|
1360
|
+
*/
|
|
1361
|
+
function renderFocusablePackElement(element, name, attributes, width, indent, wctx) {
|
|
1362
|
+
const component = readRegistryComponent(wctx.registry[name]);
|
|
1363
|
+
if (component == null)
|
|
1364
|
+
return undefined;
|
|
1365
|
+
const innerWidth = Math.max(1, width - 2);
|
|
1366
|
+
const binding = resolveDataAttribute(attributes, wctx.scope);
|
|
1367
|
+
const focusId = nextFocusId(wctx);
|
|
1368
|
+
recordFocusable(wctx, { kind: 'pack', label: stripControlCharacters(name) });
|
|
1369
|
+
const ctx = buildComponentContext(innerWidth, indent, wctx, binding, undefined, focusId);
|
|
1370
|
+
let result;
|
|
1371
|
+
try {
|
|
1372
|
+
result = component({
|
|
1373
|
+
attributes: binding.attributes,
|
|
1374
|
+
children: renderDirectiveChildrenString(element.children, innerWidth, indent, wctx),
|
|
1375
|
+
ctx,
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
catch {
|
|
1379
|
+
// Same as the string path: the failure is contained to a neutral box.
|
|
1380
|
+
return styledText(componentError(name || '(unnamed)', false, '', width, wctx.color, wctx.glyphs));
|
|
1381
|
+
}
|
|
1382
|
+
if (!isValidElement(result)) {
|
|
1383
|
+
return styledText(typeof result === 'string' ? result : '');
|
|
1384
|
+
}
|
|
1385
|
+
return _jsx(PackFocusScope, { focusId: focusId, children: result });
|
|
1386
|
+
}
|
|
1317
1387
|
/** Renders one block-level hast node as a real Ink element, or `null` for a node that carries no content (whitespace-only text). */
|
|
1318
1388
|
function renderBlockElement(node, width, indent, wctx) {
|
|
1319
1389
|
if (node.type === 'text') {
|
|
@@ -1409,6 +1479,23 @@ function buildWalkContext(registry, scope, color, theme, glyphs, interactive, re
|
|
|
1409
1479
|
focusables: undefined,
|
|
1410
1480
|
};
|
|
1411
1481
|
}
|
|
1482
|
+
/**
|
|
1483
|
+
* The `onDiagnostic` a render hands its walk: the caller's own, minus
|
|
1484
|
+
* `'unknown-directive'` events unless `reportUnknownDirectives` asked for
|
|
1485
|
+
* them. The fallback box already marks an unknown directive in the output,
|
|
1486
|
+
* so a host that does not place findings in a source (the viewer, an
|
|
1487
|
+
* export) keeps the quiet diagnostics surface it always had.
|
|
1488
|
+
*/
|
|
1489
|
+
function diagnosticsFor(options) {
|
|
1490
|
+
const downstream = options?.onDiagnostic;
|
|
1491
|
+
if (downstream === undefined || options?.reportUnknownDirectives === true) {
|
|
1492
|
+
return downstream;
|
|
1493
|
+
}
|
|
1494
|
+
return (event) => {
|
|
1495
|
+
if (event.kind !== 'unknown-directive')
|
|
1496
|
+
downstream(event);
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1412
1499
|
/** The columns assumed when a caller supplies no `width` option. */
|
|
1413
1500
|
const DEFAULT_WIDTH = 80;
|
|
1414
1501
|
/** 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. */
|
|
@@ -1443,7 +1530,7 @@ async function renderRootToString(root, registry, scope, color, theme, glyphs, i
|
|
|
1443
1530
|
export async function renderMarkToAnsi(text, registry = defaultAnsiRegistry, store, vault, options) {
|
|
1444
1531
|
const color = resolveColorOption(options?.color);
|
|
1445
1532
|
try {
|
|
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
|
|
1533
|
+
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, diagnosticsFor(options), createUnavailableComponentLookup(options?.unavailableComponents), options?.width ?? DEFAULT_WIDTH, options?.renderCode);
|
|
1447
1534
|
}
|
|
1448
1535
|
catch (error) {
|
|
1449
1536
|
return renderFailureFallback(error, color);
|
|
@@ -1458,7 +1545,7 @@ export async function renderMarkToAnsi(text, registry = defaultAnsiRegistry, sto
|
|
|
1458
1545
|
export async function renderMarkNodeToAnsi(node, registry = defaultAnsiRegistry, store, vault, options) {
|
|
1459
1546
|
const color = resolveColorOption(options?.color);
|
|
1460
1547
|
try {
|
|
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
|
|
1548
|
+
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, diagnosticsFor(options), createUnavailableComponentLookup(options?.unavailableComponents), options?.width ?? DEFAULT_WIDTH, options?.renderCode);
|
|
1462
1549
|
}
|
|
1463
1550
|
catch (error) {
|
|
1464
1551
|
return renderFailureFallback(error, color);
|
|
@@ -1522,7 +1609,7 @@ export function buildMarkElement(document, registry = defaultAnsiRegistry, optio
|
|
|
1522
1609
|
root = { type: 'root', children: [] };
|
|
1523
1610
|
}
|
|
1524
1611
|
const unavailable = createUnavailableComponentLookup(options?.unavailableComponents);
|
|
1525
|
-
const content = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, options?.resolveImageSrc, options?.resolveHref, options
|
|
1612
|
+
const content = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, options?.resolveImageSrc, options?.resolveHref, diagnosticsFor(options), unavailable, width, options?.renderCode);
|
|
1526
1613
|
if (!interactive)
|
|
1527
1614
|
return content;
|
|
1528
1615
|
// 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.2",
|
|
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.2",
|
|
55
|
+
"@markii/pack": "^0.17.0-next.2",
|
|
56
|
+
"@markii/stdlib": "^0.17.0-next.2",
|
|
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.2",
|
|
63
63
|
"@types/react": "^19.2.0"
|
|
64
64
|
}
|
|
65
65
|
}
|