@markii/ansi 0.17.0-next.1 → 0.17.0-next.3
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/ansi.d.ts +1 -2
- package/dist/ansi.js +1 -2
- package/dist/components/card.d.ts +1 -2
- package/dist/components/card.js +1 -2
- package/dist/components/row.d.ts +1 -1
- package/dist/components/row.js +1 -1
- package/dist/failure-presentation.d.ts +2 -0
- package/dist/failure-presentation.js +4 -0
- package/dist/ink-string.d.ts +1 -2
- package/dist/ink-string.js +1 -2
- package/dist/interactive.d.ts +21 -3
- package/dist/interactive.js +31 -1
- 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 +13 -6
- package/dist/registry.js +1 -1
- package/dist/render.d.ts +9 -3
- package/dist/render.js +108 -26
- package/dist/text-grid.d.ts +4 -6
- package/dist/text-grid.js +4 -6
- package/dist/value-types.d.ts +1 -1
- package/dist/value-types.js +1 -1
- package/package.json +5 -5
package/dist/ansi.d.ts
CHANGED
|
@@ -46,8 +46,7 @@ export declare const inverse: (text: string, level: ColorLevel) => string;
|
|
|
46
46
|
* at a line boundary are closed with a reset before the `\n` and reopened
|
|
47
47
|
* at the start of the next line.
|
|
48
48
|
*
|
|
49
|
-
* This is the
|
|
50
|
-
* batch 10's post-phase-2 review: Ink's `<Text>` does not treat an embedded
|
|
49
|
+
* This is the systematic fix for an Ink behavior: Ink's `<Text>` does not treat an embedded
|
|
51
50
|
* raw ANSI escape as transparent across an embedded newline the way a
|
|
52
51
|
* terminal receiving the same bytes directly would — it resets SGR state
|
|
53
52
|
* at every line boundary within one `<Text>` value and does not reopen it.
|
package/dist/ansi.js
CHANGED
|
@@ -72,8 +72,7 @@ const SGR_TOKEN = /\x1b\[([0-9;]*)m/g;
|
|
|
72
72
|
* at a line boundary are closed with a reset before the `\n` and reopened
|
|
73
73
|
* at the start of the next line.
|
|
74
74
|
*
|
|
75
|
-
* This is the
|
|
76
|
-
* batch 10's post-phase-2 review: Ink's `<Text>` does not treat an embedded
|
|
75
|
+
* This is the systematic fix for an Ink behavior: Ink's `<Text>` does not treat an embedded
|
|
77
76
|
* raw ANSI escape as transparent across an embedded newline the way a
|
|
78
77
|
* terminal receiving the same bytes directly would — it resets SGR state
|
|
79
78
|
* at every line boundary within one `<Text>` value and does not reopen it.
|
|
@@ -18,8 +18,7 @@ export declare function resolveCardBoxWidth(attributes: DirectiveAttributes, lay
|
|
|
18
18
|
* children are built ONCE, directly at the correct inner width, and never
|
|
19
19
|
* built wide and re-wrapped narrower afterward.
|
|
20
20
|
*
|
|
21
|
-
* That re-wrap-afterward shape was a real, shipped regression
|
|
22
|
-
* post-phase-2 review): re-wrapping an ALREADY-RENDERED child block with a
|
|
21
|
+
* That re-wrap-afterward shape was a real, shipped regression: re-wrapping an ALREADY-RENDERED child block with a
|
|
23
22
|
* word-based wrapper corrupts anything in it that draws its own fixed-width
|
|
24
23
|
* glyphs (a nested `card`/`callout`'s frame, a nested `row`'s columns) — the
|
|
25
24
|
* word-wrapper has no notion that a run of box-drawing characters is not
|
package/dist/components/card.js
CHANGED
|
@@ -31,8 +31,7 @@ export function resolveCardBoxWidth(attributes, layout, outerWidth) {
|
|
|
31
31
|
* children are built ONCE, directly at the correct inner width, and never
|
|
32
32
|
* built wide and re-wrapped narrower afterward.
|
|
33
33
|
*
|
|
34
|
-
* That re-wrap-afterward shape was a real, shipped regression
|
|
35
|
-
* post-phase-2 review): re-wrapping an ALREADY-RENDERED child block with a
|
|
34
|
+
* That re-wrap-afterward shape was a real, shipped regression: re-wrapping an ALREADY-RENDERED child block with a
|
|
36
35
|
* word-based wrapper corrupts anything in it that draws its own fixed-width
|
|
37
36
|
* glyphs (a nested `card`/`callout`'s frame, a nested `row`'s columns) — the
|
|
38
37
|
* word-wrapper has no notion that a run of box-drawing characters is not
|
package/dist/components/row.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnsiComponent, DirectiveAttributes } from '../registry.js';
|
|
2
2
|
declare const TEXT_ALIGNS: readonly ["left", "center", "right"];
|
|
3
3
|
export type RowTextAlign = (typeof TEXT_ALIGNS)[number];
|
|
4
|
-
/** Below this width, cells stack vertically instead of sitting side by side — a named constant
|
|
4
|
+
/** Below this width, cells stack vertically instead of sitting side by side — a named constant, not a magic number. */
|
|
5
5
|
export declare const ROW_COLUMN_THRESHOLD = 60;
|
|
6
6
|
/** The blank columns between two cells of a grid row. Shared by both emitters (the Ink flex `Box` and the flattened string). */
|
|
7
7
|
export declare const ROW_GUTTER = 1;
|
package/dist/components/row.js
CHANGED
|
@@ -6,7 +6,7 @@ const TEXT_ALIGNS = ['left', 'center', 'right'];
|
|
|
6
6
|
function isTextAlign(value) {
|
|
7
7
|
return TEXT_ALIGNS.includes(value);
|
|
8
8
|
}
|
|
9
|
-
/** Below this width, cells stack vertically instead of sitting side by side — a named constant
|
|
9
|
+
/** Below this width, cells stack vertically instead of sitting side by side — a named constant, not a magic number. */
|
|
10
10
|
export const ROW_COLUMN_THRESHOLD = 60;
|
|
11
11
|
/** The blank columns between two cells of a grid row. Shared by both emitters (the Ink flex `Box` and the flattened string). */
|
|
12
12
|
export const ROW_GUTTER = 1;
|
|
@@ -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/ink-string.d.ts
CHANGED
|
@@ -3,8 +3,7 @@ import type { ReactElement } from 'react';
|
|
|
3
3
|
* Mounts `element` with Ink at `columns` wide, captures the first (and only)
|
|
4
4
|
* frame as a plain string, and unmounts. `ink` is imported dynamically
|
|
5
5
|
* because `yoga-layout` (Ink's layout engine) resolves its WASM module via a
|
|
6
|
-
* top-level `await` at ES-module-evaluation time
|
|
7
|
-
* "Item 3"): the first call to this function pays that one-time cost, and
|
|
6
|
+
* top-level `await` at ES-module-evaluation time: the first call to this function pays that one-time cost, and
|
|
8
7
|
* every exported entry point that reaches here is therefore `async`. A
|
|
9
8
|
* later call in the same process reuses the already-evaluated module graph,
|
|
10
9
|
* the ordinary ESM module-cache guarantee.
|
package/dist/ink-string.js
CHANGED
|
@@ -20,8 +20,7 @@ function makeCollectingStream(columns) {
|
|
|
20
20
|
* Mounts `element` with Ink at `columns` wide, captures the first (and only)
|
|
21
21
|
* frame as a plain string, and unmounts. `ink` is imported dynamically
|
|
22
22
|
* because `yoga-layout` (Ink's layout engine) resolves its WASM module via a
|
|
23
|
-
* top-level `await` at ES-module-evaluation time
|
|
24
|
-
* "Item 3"): the first call to this function pays that one-time cost, and
|
|
23
|
+
* top-level `await` at ES-module-evaluation time: the first call to this function pays that one-time cost, and
|
|
25
24
|
* every exported entry point that reaches here is therefore `async`. A
|
|
26
25
|
* later call in the same process reuses the already-evaluated module graph,
|
|
27
26
|
* the ordinary ESM module-cache guarantee.
|
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,13 +47,13 @@ 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 {
|
|
36
54
|
/** Every focusable component the walk assigned a `focusId` to, in `focusId` order (empty for none). */
|
|
37
55
|
focusables: readonly FocusableDescriptor[];
|
|
38
|
-
/** Called when the reader presses `q`. The engine owns no process state: it never calls `process.exit` itself
|
|
56
|
+
/** Called when the reader presses `q`. The engine owns no process state: it never calls `process.exit` itself. */
|
|
39
57
|
onExit?: () => void;
|
|
40
58
|
color: ColorLevel;
|
|
41
59
|
theme: AnsiTheme;
|
|
@@ -55,7 +73,7 @@ export interface InteractiveRootProps {
|
|
|
55
73
|
/**
|
|
56
74
|
* The root of an interactive render: owns which `focusId` is focused, moves
|
|
57
75
|
* it on up/down/j/k, calls `onExit` on `q`, and renders the one-line status
|
|
58
|
-
* bar
|
|
76
|
+
* bar naming the focused block and the key legend. Rendered
|
|
59
77
|
* only when the caller actually wants a live, keyboard-driven view:
|
|
60
78
|
* `buildMarkElement` mounts this when `interactive` is true, but the
|
|
61
79
|
* render-ONCE string path (`ink-string.ts`, `renderRootToString`) never
|
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);
|
|
@@ -48,7 +78,7 @@ export function useFocusAnchor(focusId) {
|
|
|
48
78
|
/**
|
|
49
79
|
* The root of an interactive render: owns which `focusId` is focused, moves
|
|
50
80
|
* it on up/down/j/k, calls `onExit` on `q`, and renders the one-line status
|
|
51
|
-
* bar
|
|
81
|
+
* bar naming the focused block and the key legend. Rendered
|
|
52
82
|
* only when the caller actually wants a live, keyboard-driven view:
|
|
53
83
|
* `buildMarkElement` mounts this when `interactive` is true, but the
|
|
54
84
|
* render-ONCE string path (`ink-string.ts`, `renderRootToString`) never
|
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
|
@@ -8,14 +8,11 @@ import type { ResolvedLayoutAttributes } from './layout.js';
|
|
|
8
8
|
import type { ResolveImageSrc } from './image-resolve.js';
|
|
9
9
|
import type { ResolveHref } from './href-resolve.js';
|
|
10
10
|
/**
|
|
11
|
-
* The registry contract for the terminal engine
|
|
12
|
-
* rewrite): a component is a React function component receiving
|
|
11
|
+
* The registry contract for the terminal engine: a component is a React function component receiving
|
|
13
12
|
* `{ attributes, children, ctx }`, where `children` is ALREADY a built Ink
|
|
14
13
|
* element tree (a `ReactNode`) rather than the lazy string-producing
|
|
15
14
|
* `AnsiChildren` handle the pre-Ink engine used. Building children eagerly
|
|
16
|
-
* is
|
|
17
|
-
* section is silent on this; the brief is explicit: "Delete... the lazy-
|
|
18
|
-
* children machinery"): Ink/Yoga lays a tree out AFTER it is built, exactly
|
|
15
|
+
* is how this engine works: Ink/Yoga lays a tree out AFTER it is built, exactly
|
|
19
16
|
* like a browser reflows HTML, so a component no longer needs to ask for a
|
|
20
17
|
* narrower re-render of its own body the way the old string engine did —
|
|
21
18
|
* the one standard component that still needs a per-cell WIDTH NUMBER before
|
|
@@ -171,6 +168,16 @@ export interface AnsiRegistryEntry {
|
|
|
171
168
|
* Ink primitives Yoga can reflow.
|
|
172
169
|
*/
|
|
173
170
|
selfLayout?: boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Marks a BLOCK component whose returned Ink element the live viewer
|
|
173
|
+
* mounts and lets take focus, instead of drawing it as a static
|
|
174
|
+
* snapshot. It joins the focus order (up/down/j/k) and, while focused,
|
|
175
|
+
* its element's `useInput` calls receive keys (a pack reads
|
|
176
|
+
* `__markiiInk.useInput`, which is gated this way). In a render-once
|
|
177
|
+
* pass, or inside a framed body (a callout, a card) it renders as
|
|
178
|
+
* before: a snapshot with no focus.
|
|
179
|
+
*/
|
|
180
|
+
focusable?: boolean;
|
|
174
181
|
}
|
|
175
182
|
/** One alias: a second name for an existing component, optionally carrying preset attributes. */
|
|
176
183
|
export interface RegistryAlias {
|
|
@@ -223,7 +230,7 @@ export declare function resolveDirectiveAlias(registry: AnsiRegistry, name: stri
|
|
|
223
230
|
* top comment on why those three are the only ones that get a real Ink
|
|
224
231
|
* element tree instead. `children` is still typed `ReactNode` on
|
|
225
232
|
* `AnsiComponentProps` (the honest public contract: "an Ink element tree",
|
|
226
|
-
*
|
|
233
|
+
* and a string IS a valid `ReactNode`), so a
|
|
227
234
|
* component that needs its body AS TEXT (to measure it, frame it, indent
|
|
228
235
|
* it, or check whether it is empty) reads it through this helper rather
|
|
229
236
|
* than assuming the type; a non-string `children` degrades to `''` rather
|
package/dist/registry.js
CHANGED
|
@@ -117,7 +117,7 @@ export function resolveDirectiveAlias(registry, name, attributes) {
|
|
|
117
117
|
* top comment on why those three are the only ones that get a real Ink
|
|
118
118
|
* element tree instead. `children` is still typed `ReactNode` on
|
|
119
119
|
* `AnsiComponentProps` (the honest public contract: "an Ink element tree",
|
|
120
|
-
*
|
|
120
|
+
* and a string IS a valid `ReactNode`), so a
|
|
121
121
|
* component that needs its body AS TEXT (to measure it, frame it, indent
|
|
122
122
|
* it, or check whether it is empty) reads it through this helper rather
|
|
123
123
|
* than assuming the type; a non-string `children` degrades to `''` rather
|
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
|
|
@@ -92,8 +99,7 @@ export interface RenderMarkOptions {
|
|
|
92
99
|
* defaults to `defaultAnsiRegistry`.
|
|
93
100
|
*
|
|
94
101
|
* `async` since the first call in a process pays Ink/Yoga's one-time module-
|
|
95
|
-
* evaluation cost (`ink-string.ts`'s doc comment
|
|
96
|
-
* "Item 3"). Never throws: parsing is tolerant, unknown names always render
|
|
102
|
+
* evaluation cost (`ink-string.ts`'s doc comment). Never throws: parsing is tolerant, unknown names always render
|
|
97
103
|
* a fallback, and any unexpected internal error degrades to the "failed to
|
|
98
104
|
* render document" line.
|
|
99
105
|
*
|
|
@@ -118,7 +124,7 @@ export declare function renderMarkNodeToAnsi(node: MarkNode | MarkRoot, registry
|
|
|
118
124
|
*/
|
|
119
125
|
export declare function renderMarkInlineToAnsi(text: string, registry?: AnsiRegistry, store?: AnsiValueStore, vault?: AnsiVaultStore, options?: RenderMarkOptions): Promise<string>;
|
|
120
126
|
/**
|
|
121
|
-
* The live-viewer entry point
|
|
127
|
+
* The live-viewer entry point: builds the same Ink
|
|
122
128
|
* element tree `renderMarkToAnsi` would render to a string, but returns the
|
|
123
129
|
* element itself so a host (the CLI's `markii view`) can mount it directly
|
|
124
130
|
* with Ink's own `render()` instead of re-rendering a string on every
|
package/dist/render.js
CHANGED
|
@@ -15,23 +15,23 @@ 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';
|
|
28
28
|
/**
|
|
29
|
-
* The hast walk for the terminal engine (
|
|
29
|
+
* The hast walk for the terminal engine (the Ink rewrite). Every
|
|
30
30
|
* DECISION this walk makes is unchanged from the pre-Ink engine (alias
|
|
31
31
|
* resolution, layout resolution, data binding, form mismatch, invalid-enum
|
|
32
32
|
* notice, empty-inline notice, never-throw component containment, script-
|
|
33
33
|
* fence markers, link and image handling) — see AGENTS.md's architecture
|
|
34
|
-
* rules
|
|
34
|
+
* rules. The OUTPUT is a tree of Ink elements: instead of
|
|
35
35
|
* building one big plain string, the walk builds a tree of Ink elements
|
|
36
36
|
* (`Box`/`Text`) and hands it to `ink-string.ts` (the render-once path) or
|
|
37
37
|
* returns it directly (the new live-viewer export, `buildMarkElement`).
|
|
@@ -82,7 +82,7 @@ const ROW_NAME = 'row';
|
|
|
82
82
|
const TABS_NAME = 'tabs';
|
|
83
83
|
const DETAILS_NAME = 'details';
|
|
84
84
|
/**
|
|
85
|
-
* `card` and `figure` join this list
|
|
85
|
+
* `card` and `figure` join this list: both are
|
|
86
86
|
* containers whose body previously only ever reached STRING mode, so a
|
|
87
87
|
* `tabs`/`details` nested inside one could never become focusable. `card`
|
|
88
88
|
* draws its frame with an Ink `Box` border (`renderCardElement`); `figure`
|
|
@@ -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'));
|
|
@@ -848,7 +877,7 @@ function renderDirectiveString(element, width, indent, wctx) {
|
|
|
848
877
|
// own attributes (never from the body), so the walk resolves it here,
|
|
849
878
|
// BEFORE building children, and builds children exactly ONCE at that
|
|
850
879
|
// final width — never at the outer width followed by a re-wrap. A
|
|
851
|
-
// re-wrap-afterward shipped as a
|
|
880
|
+
// re-wrap-afterward shipped as a regression:
|
|
852
881
|
// it corrupts anything self-drawing nested inside (a card's frame, a
|
|
853
882
|
// row's columns), since a word-based re-wrapper has no notion that a run
|
|
854
883
|
// of box-drawing characters is not prose. See `resolveCardInnerWidth`'s
|
|
@@ -983,13 +1012,11 @@ function renderAlignedBodyElement(nodes, width, indent, wctx, align) {
|
|
|
983
1012
|
* Renders one `row` cell at `cellWidth`. `text=left` (the default) keeps
|
|
984
1013
|
* the cell a real Ink element tree (`renderBlockElement`), so anything
|
|
985
1014
|
* focusable inside it stays focusable. `text=center`/`text=right` instead
|
|
986
|
-
* falls back to a flattened, pre-justified STRING —
|
|
1015
|
+
* falls back to a flattened, pre-justified STRING — the justification
|
|
987
1016
|
* rule: exactly ONE implementation, `text-grid.ts`'s `padText`, never a
|
|
988
|
-
* second Ink-`alignItems`-based mechanism.
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* container, honored it); this closes that gap using the SAME helper the
|
|
992
|
-
* STRING mode already uses, rather than inventing a second mechanism.
|
|
1017
|
+
* second Ink-`alignItems`-based mechanism. The ELEMENT-mode grid and the STRING-mode fallback (reached when a `row`
|
|
1018
|
+
* is nested inside a self-drawing container) both honor `text=` through
|
|
1019
|
+
* that one helper.
|
|
993
1020
|
*/
|
|
994
1021
|
function renderRowCellElement(node, cellWidth, indent, wctx, align) {
|
|
995
1022
|
if (align === 'left')
|
|
@@ -1100,7 +1127,7 @@ function selfLayoutAlignElement(element, boxWidth, layout, width) {
|
|
|
1100
1127
|
}
|
|
1101
1128
|
/**
|
|
1102
1129
|
* `:::card{title="..." text=left|center|right} ... :::` as a real Ink
|
|
1103
|
-
* element (
|
|
1130
|
+
* element (card moves off `frameBlock`'s hand-drawn string frame
|
|
1104
1131
|
* onto Ink's own `Box` border, so a `tabs`/`details` nested in its body can
|
|
1105
1132
|
* become a real focusable — this function's `body` is built through
|
|
1106
1133
|
* `renderBlocksElement`, ELEMENT mode, whenever that is possible).
|
|
@@ -1112,9 +1139,7 @@ function selfLayoutAlignElement(element, boxWidth, layout, width) {
|
|
|
1112
1139
|
* `frameGlyphs`'s character set makes byte-identical to `frameBlock`'s own
|
|
1113
1140
|
* untitled output (proven against the committed fixtures).
|
|
1114
1141
|
*
|
|
1115
|
-
* `text=center`/`text=right`
|
|
1116
|
-
* implementation, `text-grid.ts`'s `padText`, pre-justifying a STRING before
|
|
1117
|
-
* Ink ever sees it, never a second Ink-`alignItems`-based mechanism) falls
|
|
1142
|
+
* `text=center`/`text=right` falls
|
|
1118
1143
|
* back to building the body as one flattened, pre-justified string, exactly
|
|
1119
1144
|
* like the pre-Ink engine did — an accepted, documented trade: a `tabs`/
|
|
1120
1145
|
* `details` nested inside a CENTERED or RIGHT-aligned card body is not
|
|
@@ -1197,7 +1222,7 @@ function renderCalloutElement(element, attributes, width, indent, wctx, layout)
|
|
|
1197
1222
|
}
|
|
1198
1223
|
/**
|
|
1199
1224
|
* `:::figure{src="..." alt="..."} caption markdown :::` as a real Ink
|
|
1200
|
-
* element (
|
|
1225
|
+
* element (`figure` never drew a hand-drawn frame — it is a plain
|
|
1201
1226
|
* dim alt/src line followed by its caption body — but its body still only
|
|
1202
1227
|
* ever reached STRING mode before this change, so a `tabs`/`details` in its
|
|
1203
1228
|
* caption could never become focusable. `body` here is built through
|
|
@@ -1241,7 +1266,8 @@ function renderFigureElement(element, attributes, width, indent, wctx) {
|
|
|
1241
1266
|
return (_jsxs(Box, { flexDirection: "column", children: [headerElement, body] }));
|
|
1242
1267
|
}
|
|
1243
1268
|
/** 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,
|
|
1269
|
+
function renderDirectiveElement(element, width, indent, walk) {
|
|
1270
|
+
const wctx = withNodePosition(walk, element);
|
|
1245
1271
|
const written = stringProperty(element, 'data-mk-name') ?? '';
|
|
1246
1272
|
const kind = stringProperty(element, 'data-mk-kind');
|
|
1247
1273
|
const rawAttributes = parseAttributes(stringProperty(element, 'data-mk-attrs'));
|
|
@@ -1312,8 +1338,48 @@ function renderDirectiveElement(element, width, indent, wctx) {
|
|
|
1312
1338
|
return styledText(componentError(name || '(unnamed)', false, '', width, wctx.color, wctx.glyphs));
|
|
1313
1339
|
}
|
|
1314
1340
|
}
|
|
1341
|
+
if (entry?.focusable === true && !formMismatch && wctx.interactive) {
|
|
1342
|
+
const focusable = renderFocusablePackElement(element, name, aliased, width, indent, wctx);
|
|
1343
|
+
if (focusable !== undefined)
|
|
1344
|
+
return focusable;
|
|
1345
|
+
}
|
|
1315
1346
|
return styledText(renderDirectiveString(element, width, indent, wctx));
|
|
1316
1347
|
}
|
|
1348
|
+
/**
|
|
1349
|
+
* A `focusable` registry component in the live viewer: the component runs
|
|
1350
|
+
* as usual, but an Ink element it returns is mounted (not drawn once) in a
|
|
1351
|
+
* `PackFocusScope`, so it joins the focus order and receives keys while
|
|
1352
|
+
* focused. Returns `undefined` when the component returns a string or
|
|
1353
|
+
* throws, so the caller falls back to the string path (which reports the
|
|
1354
|
+
* failure the usual way). The component draws two columns narrower: the
|
|
1355
|
+
* scope's gutter carries the focus marker.
|
|
1356
|
+
*/
|
|
1357
|
+
function renderFocusablePackElement(element, name, attributes, width, indent, wctx) {
|
|
1358
|
+
const component = readRegistryComponent(wctx.registry[name]);
|
|
1359
|
+
if (component == null)
|
|
1360
|
+
return undefined;
|
|
1361
|
+
const innerWidth = Math.max(1, width - 2);
|
|
1362
|
+
const binding = resolveDataAttribute(attributes, wctx.scope);
|
|
1363
|
+
const focusId = nextFocusId(wctx);
|
|
1364
|
+
recordFocusable(wctx, { kind: 'pack', label: stripControlCharacters(name) });
|
|
1365
|
+
const ctx = buildComponentContext(innerWidth, indent, wctx, binding, undefined, focusId);
|
|
1366
|
+
let result;
|
|
1367
|
+
try {
|
|
1368
|
+
result = component({
|
|
1369
|
+
attributes: binding.attributes,
|
|
1370
|
+
children: renderDirectiveChildrenString(element.children, innerWidth, indent, wctx),
|
|
1371
|
+
ctx,
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
catch {
|
|
1375
|
+
// Same as the string path: the failure is contained to a neutral box.
|
|
1376
|
+
return styledText(componentError(name || '(unnamed)', false, '', width, wctx.color, wctx.glyphs));
|
|
1377
|
+
}
|
|
1378
|
+
if (!isValidElement(result)) {
|
|
1379
|
+
return styledText(typeof result === 'string' ? result : '');
|
|
1380
|
+
}
|
|
1381
|
+
return _jsx(PackFocusScope, { focusId: focusId, children: result });
|
|
1382
|
+
}
|
|
1317
1383
|
/** Renders one block-level hast node as a real Ink element, or `null` for a node that carries no content (whitespace-only text). */
|
|
1318
1384
|
function renderBlockElement(node, width, indent, wctx) {
|
|
1319
1385
|
if (node.type === 'text') {
|
|
@@ -1409,9 +1475,26 @@ function buildWalkContext(registry, scope, color, theme, glyphs, interactive, re
|
|
|
1409
1475
|
focusables: undefined,
|
|
1410
1476
|
};
|
|
1411
1477
|
}
|
|
1478
|
+
/**
|
|
1479
|
+
* The `onDiagnostic` a render hands its walk: the caller's own, minus
|
|
1480
|
+
* `'unknown-directive'` events unless `reportUnknownDirectives` asked for
|
|
1481
|
+
* them. The fallback box already marks an unknown directive in the output,
|
|
1482
|
+
* so a host that does not place findings in a source (the viewer, an
|
|
1483
|
+
* export) keeps the quiet diagnostics surface it always had.
|
|
1484
|
+
*/
|
|
1485
|
+
function diagnosticsFor(options) {
|
|
1486
|
+
const downstream = options?.onDiagnostic;
|
|
1487
|
+
if (downstream === undefined || options?.reportUnknownDirectives === true) {
|
|
1488
|
+
return downstream;
|
|
1489
|
+
}
|
|
1490
|
+
return (event) => {
|
|
1491
|
+
if (event.kind !== 'unknown-directive')
|
|
1492
|
+
downstream(event);
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1412
1495
|
/** The columns assumed when a caller supplies no `width` option. */
|
|
1413
1496
|
const DEFAULT_WIDTH = 80;
|
|
1414
|
-
/** Builds the root Ink element for `root`, given every resolved render input. Exported for `buildMarkElement` (the live-viewer entry point
|
|
1497
|
+
/** Builds the root Ink element for `root`, given every resolved render input. Exported for `buildMarkElement` (the live-viewer entry point) as well as this module's own three string-producing entry points. */
|
|
1415
1498
|
function buildRootElement(root, registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, width, renderCode) {
|
|
1416
1499
|
const wctx = buildWalkContext(registry, scope, color, theme, glyphs, interactive, resolveImageSrc, resolveHref, onDiagnostic, unavailable, renderCode);
|
|
1417
1500
|
const body = renderBlocksElement(root.children, width, '', wctx);
|
|
@@ -1429,8 +1512,7 @@ async function renderRootToString(root, registry, scope, color, theme, glyphs, i
|
|
|
1429
1512
|
* defaults to `defaultAnsiRegistry`.
|
|
1430
1513
|
*
|
|
1431
1514
|
* `async` since the first call in a process pays Ink/Yoga's one-time module-
|
|
1432
|
-
* evaluation cost (`ink-string.ts`'s doc comment
|
|
1433
|
-
* "Item 3"). Never throws: parsing is tolerant, unknown names always render
|
|
1515
|
+
* evaluation cost (`ink-string.ts`'s doc comment). Never throws: parsing is tolerant, unknown names always render
|
|
1434
1516
|
* a fallback, and any unexpected internal error degrades to the "failed to
|
|
1435
1517
|
* render document" line.
|
|
1436
1518
|
*
|
|
@@ -1443,7 +1525,7 @@ async function renderRootToString(root, registry, scope, color, theme, glyphs, i
|
|
|
1443
1525
|
export async function renderMarkToAnsi(text, registry = defaultAnsiRegistry, store, vault, options) {
|
|
1444
1526
|
const color = resolveColorOption(options?.color);
|
|
1445
1527
|
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
|
|
1528
|
+
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
1529
|
}
|
|
1448
1530
|
catch (error) {
|
|
1449
1531
|
return renderFailureFallback(error, color);
|
|
@@ -1458,7 +1540,7 @@ export async function renderMarkToAnsi(text, registry = defaultAnsiRegistry, sto
|
|
|
1458
1540
|
export async function renderMarkNodeToAnsi(node, registry = defaultAnsiRegistry, store, vault, options) {
|
|
1459
1541
|
const color = resolveColorOption(options?.color);
|
|
1460
1542
|
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
|
|
1543
|
+
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
1544
|
}
|
|
1463
1545
|
catch (error) {
|
|
1464
1546
|
return renderFailureFallback(error, color);
|
|
@@ -1497,7 +1579,7 @@ export async function renderMarkInlineToAnsi(text, registry = defaultAnsiRegistr
|
|
|
1497
1579
|
return renderMarkToAnsi(text, registry, store, vault, options);
|
|
1498
1580
|
}
|
|
1499
1581
|
/**
|
|
1500
|
-
* The live-viewer entry point
|
|
1582
|
+
* The live-viewer entry point: builds the same Ink
|
|
1501
1583
|
* element tree `renderMarkToAnsi` would render to a string, but returns the
|
|
1502
1584
|
* element itself so a host (the CLI's `markii view`) can mount it directly
|
|
1503
1585
|
* with Ink's own `render()` instead of re-rendering a string on every
|
|
@@ -1522,7 +1604,7 @@ export function buildMarkElement(document, registry = defaultAnsiRegistry, optio
|
|
|
1522
1604
|
root = { type: 'root', children: [] };
|
|
1523
1605
|
}
|
|
1524
1606
|
const unavailable = createUnavailableComponentLookup(options?.unavailableComponents);
|
|
1525
|
-
const content = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, options?.resolveImageSrc, options?.resolveHref, options
|
|
1607
|
+
const content = buildRootElement(root, registry, scope, color, theme, glyphs, interactive, options?.resolveImageSrc, options?.resolveHref, diagnosticsFor(options), unavailable, width, options?.renderCode);
|
|
1526
1608
|
if (!interactive)
|
|
1527
1609
|
return content;
|
|
1528
1610
|
// The walk above already assigned every focusable component's `focusId`
|
package/dist/text-grid.d.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* handful of STANDARD COMPONENTS that draw their own fixed-width glyphs
|
|
4
4
|
* (`card`, `callout`, `divider`, `chart`, `table`, and the plain-markdown
|
|
5
5
|
* table grid in `render.tsx`). Everything else in this engine wraps prose
|
|
6
|
-
* through Ink's own layout (`ink-string.ts`'s doc comment
|
|
7
|
-
*
|
|
8
|
-
* them").
|
|
6
|
+
* through Ink's own layout (`ink-string.ts`'s doc comment): Ink's layout replaces
|
|
7
|
+
* wrap, pad, columns, frame and rule.
|
|
9
8
|
*
|
|
10
9
|
* A self-drawing component cannot lean on Ink for this: the string it builds
|
|
11
10
|
* (a box-drawing frame, a sparkline, a table grid) is a fixed block of
|
|
@@ -13,9 +12,8 @@
|
|
|
13
12
|
* `<Text>` renders it verbatim rather than reflowing pre-drawn glyphs. This
|
|
14
13
|
* module is therefore kept as a small, PRIVATE (not exported from
|
|
15
14
|
* `src/index.ts`) implementation detail of those few components, replacing
|
|
16
|
-
* the old public `measure.ts`/`box.ts` modules
|
|
17
|
-
*
|
|
18
|
-
* old `measure.ts`: Latin/CJK/emoji/combining-mark ranges, not a full
|
|
15
|
+
* the old public `measure.ts`/`box.ts` modules. The width-measurement
|
|
16
|
+
* approximation covers Latin/CJK/emoji/combining-mark ranges, not a full
|
|
19
17
|
* Unicode grapheme-segmentation table (see the doc comment below for the one
|
|
20
18
|
* documented limitation).
|
|
21
19
|
*/
|
package/dist/text-grid.js
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* handful of STANDARD COMPONENTS that draw their own fixed-width glyphs
|
|
4
4
|
* (`card`, `callout`, `divider`, `chart`, `table`, and the plain-markdown
|
|
5
5
|
* table grid in `render.tsx`). Everything else in this engine wraps prose
|
|
6
|
-
* through Ink's own layout (`ink-string.ts`'s doc comment
|
|
7
|
-
*
|
|
8
|
-
* them").
|
|
6
|
+
* through Ink's own layout (`ink-string.ts`'s doc comment): Ink's layout replaces
|
|
7
|
+
* wrap, pad, columns, frame and rule.
|
|
9
8
|
*
|
|
10
9
|
* A self-drawing component cannot lean on Ink for this: the string it builds
|
|
11
10
|
* (a box-drawing frame, a sparkline, a table grid) is a fixed block of
|
|
@@ -13,9 +12,8 @@
|
|
|
13
12
|
* `<Text>` renders it verbatim rather than reflowing pre-drawn glyphs. This
|
|
14
13
|
* module is therefore kept as a small, PRIVATE (not exported from
|
|
15
14
|
* `src/index.ts`) implementation detail of those few components, replacing
|
|
16
|
-
* the old public `measure.ts`/`box.ts` modules
|
|
17
|
-
*
|
|
18
|
-
* old `measure.ts`: Latin/CJK/emoji/combining-mark ranges, not a full
|
|
15
|
+
* the old public `measure.ts`/`box.ts` modules. The width-measurement
|
|
16
|
+
* approximation covers Latin/CJK/emoji/combining-mark ranges, not a full
|
|
19
17
|
* Unicode grapheme-segmentation table (see the doc comment below for the one
|
|
20
18
|
* documented limitation).
|
|
21
19
|
*/
|
package/dist/value-types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@markii/ansi`'s own structural view of `@markii/runtime`'s value-store
|
|
3
3
|
* contract. This package's dependency list is deliberately minimal (exactly
|
|
4
|
-
* `@markii/core` and `@markii/stdlib`; see AGENTS.md
|
|
4
|
+
* `@markii/core` and `@markii/stdlib`; see AGENTS.md),
|
|
5
5
|
* so it never imports `@markii/runtime` at runtime: declaring the shapes it
|
|
6
6
|
* needs here, rather than importing them, keeps a terminal render usable in
|
|
7
7
|
* a context that never pulled in the runtime package at all (a CLI that
|
package/dist/value-types.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@markii/ansi`'s own structural view of `@markii/runtime`'s value-store
|
|
3
3
|
* contract. This package's dependency list is deliberately minimal (exactly
|
|
4
|
-
* `@markii/core` and `@markii/stdlib`; see AGENTS.md
|
|
4
|
+
* `@markii/core` and `@markii/stdlib`; see AGENTS.md),
|
|
5
5
|
* so it never imports `@markii/runtime` at runtime: declaring the shapes it
|
|
6
6
|
* needs here, rather than importing them, keeps a terminal render usable in
|
|
7
7
|
* a context that never pulled in the runtime package at all (a CLI that
|
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.3",
|
|
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.3",
|
|
55
|
+
"@markii/pack": "^0.17.0-next.3",
|
|
56
|
+
"@markii/stdlib": "^0.17.0-next.3",
|
|
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.3",
|
|
63
63
|
"@types/react": "^19.2.0"
|
|
64
64
|
}
|
|
65
65
|
}
|