@ai-matrx/rich-content 0.0.0 → 0.2.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/CHANGELOG.md +72 -0
- package/LICENSE +1 -1
- package/README.md +58 -3
- package/dist/display/blocks/csv/CsvBlock.d.ts +10 -0
- package/dist/display/blocks/csv/CsvBlock.js +245 -0
- package/dist/display/blocks/table/parseMarkdownTable.d.ts +20 -0
- package/dist/display/blocks/table/parseMarkdownTable.js +35 -0
- package/dist/display/chat-markdown/citations/remarkMatrxCite.d.ts +33 -0
- package/dist/display/chat-markdown/citations/remarkMatrxCite.js +71 -0
- package/dist/display/chat-markdown/matrx-variables/remarkMatrxVariable.d.ts +29 -0
- package/dist/display/chat-markdown/matrx-variables/remarkMatrxVariable.js +71 -0
- package/dist/display/chat-markdown/page-break/remarkMatrxPageBreak.d.ts +55 -0
- package/dist/display/chat-markdown/page-break/remarkMatrxPageBreak.js +108 -0
- package/dist/display/chat-markdown/rehypeSafeRawHtml.d.ts +59 -0
- package/dist/display/chat-markdown/rehypeSafeRawHtml.js +140 -0
- package/dist/display/markdown-classification/processors/bock-processors/parse-markdown-table.d.ts +14 -0
- package/dist/display/markdown-classification/processors/bock-processors/parse-markdown-table.js +171 -0
- package/dist/display/markdown-classification/processors/types.d.ts +120 -0
- package/dist/display/markdown-classification/processors/types.js +10 -0
- package/dist/display/markdown-classification/processors/utils/client-blocks.d.ts +169 -0
- package/dist/display/markdown-classification/processors/utils/client-blocks.js +0 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-core.d.ts +223 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-core.js +1791 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-v2.d.ts +15 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-v2.js +19 -0
- package/dist/display/markdown-classification/processors/utils/expand-text-blocks.d.ts +9 -0
- package/dist/display/markdown-classification/processors/utils/expand-text-blocks.js +36 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-lines.d.ts +5 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-lines.js +40 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vector-inputs.d.ts +16 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vector-inputs.js +385 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vectors.d.ts +43 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vectors.js +33 -0
- package/dist/display/markdown-classification/processors/utils/table-data-parser.d.ts +9 -0
- package/dist/display/markdown-classification/processors/utils/table-data-parser.js +30 -0
- package/dist/display/markdown-classification/processors/utils/thinking-parser.d.ts +7 -0
- package/dist/display/markdown-classification/processors/utils/thinking-parser.js +70 -0
- package/dist/display/render-block.d.ts +23 -0
- package/dist/display/render-block.js +0 -0
- package/dist/display/themes.d.ts +1023 -0
- package/dist/display/themes.js +1105 -0
- package/dist/display/types.d.ts +32 -0
- package/dist/display/types.js +0 -0
- package/dist/host/ContentIrHostBoundary.d.ts +8 -0
- package/dist/host/ContentIrHostBoundary.js +15 -0
- package/dist/host/ErrorActions.d.ts +4 -0
- package/dist/host/ErrorActions.js +11 -0
- package/dist/host/NestedContent.d.ts +3 -0
- package/dist/host/NestedContent.js +12 -0
- package/dist/host/ResourcePeek.d.ts +6 -0
- package/dist/host/ResourcePeek.js +14 -0
- package/dist/host/component-registry.d.ts +2 -0
- package/dist/host/component-registry.js +25 -0
- package/dist/host/diagnostics.d.ts +2 -0
- package/dist/host/diagnostics.js +15 -0
- package/dist/host/index.d.ts +3 -0
- package/dist/host/index.js +18 -0
- package/dist/host/kind-incident.d.ts +3 -0
- package/dist/host/kind-incident.js +12 -0
- package/dist/host/kind-registry.d.ts +2 -0
- package/dist/host/kind-registry.js +35 -0
- package/dist/host/people.d.ts +3 -0
- package/dist/host/people.js +12 -0
- package/dist/host/registry.d.ts +27 -0
- package/dist/host/registry.js +55 -0
- package/dist/host/splitter-envelopes.d.ts +2 -0
- package/dist/host/splitter-envelopes.js +7 -0
- package/dist/host/tables.d.ts +6 -0
- package/dist/host/tables.js +8 -0
- package/dist/host/toast.d.ts +2 -0
- package/dist/host/toast.js +57 -0
- package/dist/host/types.d.ts +157 -0
- package/dist/host/types.js +0 -0
- package/dist/host/wikilinks.d.ts +15 -0
- package/dist/host/wikilinks.js +23 -0
- package/dist/kinds/host/route-env.d.ts +33 -0
- package/dist/kinds/host/route-env.js +16 -0
- package/dist/kinds/react/KindEscapedNotice.d.ts +14 -0
- package/dist/kinds/react/KindEscapedNotice.js +72 -0
- package/dist/kinds/react/ProvisionalKindBoundary.d.ts +19 -0
- package/dist/kinds/react/ProvisionalKindBoundary.js +17 -0
- package/dist/kinds/react/ensure-kind-renderable.d.ts +14 -0
- package/dist/kinds/react/ensure-kind-renderable.js +45 -0
- package/dist/kinds/react/kind-problems.d.ts +62 -0
- package/dist/kinds/react/kind-problems.js +134 -0
- package/dist/kinds/react/kind-route.d.ts +23 -0
- package/dist/kinds/react/kind-route.js +24 -0
- package/dist/kinds/react/loading/infer-loading-slug.d.ts +58 -0
- package/dist/kinds/react/loading/infer-loading-slug.js +100 -0
- package/dist/kinds/react/loading/kind-loading-components.d.ts +48 -0
- package/dist/kinds/react/loading/kind-loading-components.js +447 -0
- package/dist/kinds/react/loading/kind-loading-registry.d.ts +21 -0
- package/dist/kinds/react/loading/kind-loading-registry.js +62 -0
- package/dist/kinds/react/loading/kind-loading-slugs.d.ts +18 -0
- package/dist/kinds/react/loading/kind-loading-slugs.js +34 -0
- package/dist/kinds/react/loading/kind-loading.types.d.ts +79 -0
- package/dist/kinds/react/loading/kind-loading.types.js +31 -0
- package/dist/kinds/react/loading/resolve-loading-slug.d.ts +29 -0
- package/dist/kinds/react/loading/resolve-loading-slug.js +51 -0
- package/dist/kinds/react/partial-kind-route.d.ts +32 -0
- package/dist/kinds/react/partial-kind-route.js +27 -0
- package/dist/kinds/react/registry-versioned.d.ts +54 -0
- package/dist/kinds/react/registry-versioned.js +18 -0
- package/dist/kinds/react/route-at-version.d.ts +38 -0
- package/dist/kinds/react/route-at-version.js +78 -0
- package/dist/kinds/react/slot/KindSlot.d.ts +87 -0
- package/dist/kinds/react/slot/KindSlot.js +65 -0
- package/dist/kinds/react/use-registry-repaint.d.ts +6 -0
- package/dist/kinds/react/use-registry-repaint.js +36 -0
- package/dist/kinds/react/useIrNode.d.ts +9 -0
- package/dist/kinds/react/useIrNode.js +46 -0
- package/dist/kinds/react/useLiveJsonRegion.d.ts +15 -0
- package/dist/kinds/react/useLiveJsonRegion.js +84 -0
- package/dist/kinds/redux/render-block-envelope.d.ts +14 -0
- package/dist/kinds/redux/render-block-envelope.js +26 -0
- package/dist/kinds/registry/kind-correctors.d.ts +63 -0
- package/dist/kinds/registry/kind-correctors.js +94 -0
- package/dist/kinds/registry/legacy-quiz-envelope.d.ts +1 -0
- package/dist/kinds/registry/legacy-quiz-envelope.js +1 -0
- package/dist/kinds/registry/region-envelope-memo.d.ts +45 -0
- package/dist/kinds/registry/region-envelope-memo.js +94 -0
- package/dist/kinds/registry/registry-debug.d.ts +38 -0
- package/dist/kinds/registry/registry-debug.js +39 -0
- package/dist/markdown-core/MarkdownCore.d.ts +6 -0
- package/dist/markdown-core/MarkdownCore.js +32 -0
- package/dist/markdown-core/MarkdownCoreImpl.d.ts +3 -0
- package/dist/markdown-core/MarkdownCoreImpl.js +27 -0
- package/dist/markdown-core/MarkdownCoreServer.d.ts +4 -0
- package/dist/markdown-core/MarkdownCoreServer.js +26 -0
- package/dist/markdown-core/heading-anchors-context.d.ts +2 -0
- package/dist/markdown-core/heading-anchors-context.js +11 -0
- package/dist/markdown-core/heading-anchors-props.d.ts +2 -0
- package/dist/markdown-core/heading-anchors-props.js +6 -0
- package/dist/markdown-core/markdown-core-html.d.ts +3 -0
- package/dist/markdown-core/markdown-core-html.js +33 -0
- package/dist/markdown-core/markdown-core-presets.d.ts +22 -0
- package/dist/markdown-core/markdown-core-presets.js +118 -0
- package/dist/markdown-core/markdown-core-types.d.ts +43 -0
- package/dist/markdown-core/markdown-core-types.js +0 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-flow.d.ts +15 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-flow.js +143 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-text.d.ts +24 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-text.js +127 -0
- package/dist/markdown-core/math/vendor/micromark-math-syntax.d.ts +30 -0
- package/dist/markdown-core/math/vendor/micromark-math-syntax.js +15 -0
- package/dist/markdown-core/math/vendor/rehype-katex.d.ts +15 -0
- package/dist/markdown-core/math/vendor/rehype-katex.js +83 -0
- package/dist/markdown-core/math/vendor/remark-math.d.ts +9 -0
- package/dist/markdown-core/math/vendor/remark-math.js +20 -0
- package/dist/markdown-core/math-normalizer.d.ts +57 -0
- package/dist/markdown-core/math-normalizer.js +315 -0
- package/dist/markdown-core/plain-title.d.ts +2 -0
- package/dist/markdown-core/plain-title.js +1 -0
- package/dist/markdown-core/stream-heal.d.ts +2 -0
- package/dist/markdown-core/stream-heal.js +184 -0
- package/dist/markdown-core/streaming-context.d.ts +2 -0
- package/dist/markdown-core/streaming-context.js +11 -0
- package/dist/markdown-core/syntax/callout-types.d.ts +9 -0
- package/dist/markdown-core/syntax/callout-types.js +101 -0
- package/dist/markdown-core/syntax/containers.d.ts +17 -0
- package/dist/markdown-core/syntax/containers.js +353 -0
- package/dist/markdown-core/syntax/document-numbering.d.ts +52 -0
- package/dist/markdown-core/syntax/document-numbering.js +184 -0
- package/dist/markdown-core/syntax/elements/CalloutIcon.d.ts +3 -0
- package/dist/markdown-core/syntax/elements/CalloutIcon.js +41 -0
- package/dist/markdown-core/syntax/elements/CrossRef.d.ts +12 -0
- package/dist/markdown-core/syntax/elements/CrossRef.js +57 -0
- package/dist/markdown-core/syntax/elements/CsvFence.d.ts +4 -0
- package/dist/markdown-core/syntax/elements/CsvFence.js +18 -0
- package/dist/markdown-core/syntax/elements/DetailsElements.d.ts +12 -0
- package/dist/markdown-core/syntax/elements/DetailsElements.js +38 -0
- package/dist/markdown-core/syntax/elements/DocumentNumbering.d.ts +20 -0
- package/dist/markdown-core/syntax/elements/DocumentNumbering.js +37 -0
- package/dist/markdown-core/syntax/elements/DocumentPropertiesPanel.d.ts +4 -0
- package/dist/markdown-core/syntax/elements/DocumentPropertiesPanel.js +46 -0
- package/dist/markdown-core/syntax/elements/InDocAnchor.d.ts +11 -0
- package/dist/markdown-core/syntax/elements/InDocAnchor.js +87 -0
- package/dist/markdown-core/syntax/elements/MarkdownSourceEdit.d.ts +24 -0
- package/dist/markdown-core/syntax/elements/MarkdownSourceEdit.js +28 -0
- package/dist/markdown-core/syntax/elements/MatrxTabs.d.ts +10 -0
- package/dist/markdown-core/syntax/elements/MatrxTabs.js +59 -0
- package/dist/markdown-core/syntax/elements/MentionChip.d.ts +7 -0
- package/dist/markdown-core/syntax/elements/MentionChip.js +69 -0
- package/dist/markdown-core/syntax/elements/NestedBody.d.ts +3 -0
- package/dist/markdown-core/syntax/elements/NestedBody.js +13 -0
- package/dist/markdown-core/syntax/elements/TableOfContents.d.ts +5 -0
- package/dist/markdown-core/syntax/elements/TableOfContents.js +44 -0
- package/dist/markdown-core/syntax/elements/TaskCheckbox.d.ts +7 -0
- package/dist/markdown-core/syntax/elements/TaskCheckbox.js +63 -0
- package/dist/markdown-core/syntax/elements/WikiEmbed.d.ts +7 -0
- package/dist/markdown-core/syntax/elements/WikiEmbed.js +68 -0
- package/dist/markdown-core/syntax/elements/WikiLink.d.ts +10 -0
- package/dist/markdown-core/syntax/elements/WikiLink.js +114 -0
- package/dist/markdown-core/syntax/elements/core-syntax-elements.d.ts +22 -0
- package/dist/markdown-core/syntax/elements/core-syntax-elements.js +70 -0
- package/dist/markdown-core/syntax/elements/find-in-document.d.ts +3 -0
- package/dist/markdown-core/syntax/elements/find-in-document.js +20 -0
- package/dist/markdown-core/syntax/elements/wiki-target.d.ts +5 -0
- package/dist/markdown-core/syntax/elements/wiki-target.js +8 -0
- package/dist/markdown-core/syntax/frontmatter.d.ts +26 -0
- package/dist/markdown-core/syntax/frontmatter.js +72 -0
- package/dist/markdown-core/syntax/gfm-cell-pipes.d.ts +11 -0
- package/dist/markdown-core/syntax/gfm-cell-pipes.js +4 -0
- package/dist/markdown-core/syntax/inline-syntax.d.ts +26 -0
- package/dist/markdown-core/syntax/inline-syntax.js +163 -0
- package/dist/markdown-core/syntax/linear-scan.d.ts +46 -0
- package/dist/markdown-core/syntax/linear-scan.js +129 -0
- package/dist/markdown-core/syntax/mdast-helpers.d.ts +48 -0
- package/dist/markdown-core/syntax/mdast-helpers.js +48 -0
- package/dist/markdown-core/syntax/mentions.d.ts +2 -0
- package/dist/markdown-core/syntax/mentions.js +42 -0
- package/dist/markdown-core/syntax/names.d.ts +6 -0
- package/dist/markdown-core/syntax/names.js +18 -0
- package/dist/markdown-core/syntax/prepare-syntax-source.d.ts +1 -0
- package/dist/markdown-core/syntax/prepare-syntax-source.js +78 -0
- package/dist/markdown-core/syntax/rehype-matrx-syntax.d.ts +9 -0
- package/dist/markdown-core/syntax/rehype-matrx-syntax.js +180 -0
- package/dist/markdown-core/syntax/remark-inline-only.d.ts +23 -0
- package/dist/markdown-core/syntax/remark-inline-only.js +25 -0
- package/dist/markdown-core/syntax/remark-matrx-syntax.d.ts +7 -0
- package/dist/markdown-core/syntax/remark-matrx-syntax.js +351 -0
- package/dist/markdown-core/syntax/remark-table-code-pipes.d.ts +7 -0
- package/dist/markdown-core/syntax/remark-table-code-pipes.js +23 -0
- package/dist/markdown-core/syntax/stream-heal-syntax.d.ts +2 -0
- package/dist/markdown-core/syntax/stream-heal-syntax.js +108 -0
- package/dist/markdown-core/syntax/task-source.d.ts +10 -0
- package/dist/markdown-core/syntax/task-source.js +34 -0
- package/dist/markdown-core/syntax/useDocumentProperties.d.ts +3 -0
- package/dist/markdown-core/syntax/useDocumentProperties.js +8 -0
- package/dist/styles.css +0 -0
- package/dist/utils/addressWithoutNavigating.d.ts +68 -0
- package/dist/utils/addressWithoutNavigating.js +60 -0
- package/dist/utils/client-lazy.d.ts +13 -0
- package/dist/utils/client-lazy.js +24 -0
- package/dist/utils/decision-options.d.ts +21 -0
- package/dist/utils/decision-options.js +32 -0
- package/dist/utils/gfm-lexer.d.ts +5 -0
- package/dist/utils/gfm-lexer.js +10 -0
- package/dist/utils/json.d.ts +63 -0
- package/dist/utils/json.js +22 -0
- package/dist/utils/plain-title.d.ts +25 -0
- package/dist/utils/plain-title.js +51 -0
- package/dist/utils/table-source.d.ts +62 -0
- package/dist/utils/table-source.js +191 -0
- package/dist/utils/youtube.d.ts +52 -0
- package/dist/utils/youtube.js +137 -0
- package/package.json +132 -5
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kind loading-component contract — the EARLY-KEY interface between a
|
|
3
|
+
* streaming `__kind` payload and the hardcoded loading library.
|
|
4
|
+
*
|
|
5
|
+
* THE EARLY-KEY CONTRACT (teach emitters to send these FIRST):
|
|
6
|
+
* While a kind instance streams — and especially while its schema/component
|
|
7
|
+
* are still cold-fetching — the parser surfaces the region's top-level
|
|
8
|
+
* SCALAR values as they arrive. The loading layer reads this default key set
|
|
9
|
+
* from the envelope's early value:
|
|
10
|
+
*
|
|
11
|
+
* - `title` string — the instance's display title
|
|
12
|
+
* - `description` string — one-line summary of the content
|
|
13
|
+
* - `loading_message` string — what to show while the rest streams
|
|
14
|
+
* (e.g. "Pouring your wine tasting…")
|
|
15
|
+
* - `loading_subtext` string — secondary line under the message
|
|
16
|
+
* - `icon` string — icon hint (a slug from ICON_HINTS; unknown
|
|
17
|
+
* hints fall back to the component's default)
|
|
18
|
+
* - `count` number — expected item count (drives how many
|
|
19
|
+
* skeleton rows/cells render)
|
|
20
|
+
*
|
|
21
|
+
* A kind that wants a meaningful loading state should order these keys FIRST
|
|
22
|
+
* in its schema/sample so they stream in before the heavy fields. All keys
|
|
23
|
+
* are optional — every loader renders sensibly with none of them.
|
|
24
|
+
*
|
|
25
|
+
* WHICH LOADER RENDERS: `kind_definition.metadata.loading_component` names a
|
|
26
|
+
* slug from the library (see kind-loading-registry.ts). Missing/unknown slug
|
|
27
|
+
* → the `generic` skeleton. The library is hardcoded (zero fetch delay) and
|
|
28
|
+
* intentionally light — no heavy deps, semantic tokens, Lucide only.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* THE TWO PHASES of a loading silhouette (Arman, 2026-08-25).
|
|
32
|
+
*
|
|
33
|
+
* - `reserved` — the PLACEHOLDER. The slot exists and holds the footprint its
|
|
34
|
+
* content will need, but nothing has happened yet: no spinner, no shimmer,
|
|
35
|
+
* one slow breath on the icon. "It looks like a loading one, but it's
|
|
36
|
+
* fairly steady… without looking like it's actively, instantly loading."
|
|
37
|
+
* Modelled on `PodcastCompositionPlaceholder`, which proved the look.
|
|
38
|
+
* - `arriving` — the LOADING state. Same silhouette, now visibly working:
|
|
39
|
+
* spinner, shimmering bars, live early keys filling in as they stream.
|
|
40
|
+
*
|
|
41
|
+
* Same component, same footprint, so the placeholder→loading transition moves
|
|
42
|
+
* nothing on screen. Defaults to `arriving`, which is what every pre-2026-08-25
|
|
43
|
+
* call site meant.
|
|
44
|
+
*/
|
|
45
|
+
export type KindLoadingPhase = "reserved" | "arriving";
|
|
46
|
+
export interface KindLoadingProps {
|
|
47
|
+
/** The identified kind slug (shown as a subtle chip when no title yet). */
|
|
48
|
+
kind?: string;
|
|
49
|
+
/** Placeholder (footprint only) vs actively loading. Default `arriving`. */
|
|
50
|
+
phase?: KindLoadingPhase;
|
|
51
|
+
/**
|
|
52
|
+
* `full` (default) draws the loader's own frame and header — the right
|
|
53
|
+
* thing when it stands alone in a chat stream. `bare` draws ONLY the
|
|
54
|
+
* skeleton body, for a host that already renders the frame, icon, and
|
|
55
|
+
* title: a component never repeats chrome its host provides. Mirrors
|
|
56
|
+
* `LiveRunDisplay variant="bare"`.
|
|
57
|
+
*/
|
|
58
|
+
chrome?: "full" | "bare";
|
|
59
|
+
title?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
loadingMessage?: string;
|
|
62
|
+
loadingSubtext?: string;
|
|
63
|
+
/** Icon hint slug (ICON_HINTS key); unknown → the loader's default. */
|
|
64
|
+
icon?: string;
|
|
65
|
+
/** Expected item count — clamped by each loader to a sane skeleton count. */
|
|
66
|
+
count?: number;
|
|
67
|
+
/**
|
|
68
|
+
* THE SMART-LOADER FEED: the region's full partial value, exactly as it
|
|
69
|
+
* stands this frame (grows monotonically, never rewrites). Library
|
|
70
|
+
* skeletons ignore it; a data-fed loader reads it to PERFORM the arrival —
|
|
71
|
+
* surface real text as it lands, step counts up, soft-fade each piece in
|
|
72
|
+
* (Arman, 2026-08-24: animated, intelligent, never generic). Always
|
|
73
|
+
* provided by the pending render path; optional so static previews can
|
|
74
|
+
* render a loader with no stream behind it.
|
|
75
|
+
*/
|
|
76
|
+
value?: Record<string, unknown> | null;
|
|
77
|
+
}
|
|
78
|
+
/** Read the early-key set out of an envelope's (partial) root value. */
|
|
79
|
+
export declare function earlyKeysFromValue(value: Record<string, unknown> | null | undefined, kind?: string): KindLoadingProps;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function earlyKeysFromValue(value, kind) {
|
|
2
|
+
const v = value ?? {};
|
|
3
|
+
const str = (k) => typeof v[k] === "string" && v[k].length > 0 ? v[k] : void 0;
|
|
4
|
+
const num = (k) => typeof v[k] === "number" && Number.isFinite(v[k]) ? v[k] : void 0;
|
|
5
|
+
const props = {};
|
|
6
|
+
if (kind) props.kind = kind;
|
|
7
|
+
const title = str("title");
|
|
8
|
+
if (title) props.title = title;
|
|
9
|
+
const description = str("description");
|
|
10
|
+
if (description) props.description = description;
|
|
11
|
+
const loadingMessage = str("loading_message");
|
|
12
|
+
if (loadingMessage) props.loadingMessage = loadingMessage;
|
|
13
|
+
const loadingSubtext = str("loading_subtext");
|
|
14
|
+
if (loadingSubtext) props.loadingSubtext = loadingSubtext;
|
|
15
|
+
const icon = str("icon");
|
|
16
|
+
if (icon) props.icon = icon;
|
|
17
|
+
const count = num("count");
|
|
18
|
+
if (count !== void 0) {
|
|
19
|
+
props.count = count;
|
|
20
|
+
} else {
|
|
21
|
+
let largest = 0;
|
|
22
|
+
for (const item of Object.values(v)) {
|
|
23
|
+
if (Array.isArray(item) && item.length > largest) largest = item.length;
|
|
24
|
+
}
|
|
25
|
+
if (largest > 0) props.count = largest;
|
|
26
|
+
}
|
|
27
|
+
return props;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
earlyKeysFromValue
|
|
31
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type KindLoadingSlug } from "./kind-loading-slugs.js";
|
|
2
|
+
/** Where the answer came from — surfaced so the Stream tab / doctor can say. */
|
|
3
|
+
export type LoadingSlugOrigin = "declared" | "derived" | "generic";
|
|
4
|
+
export interface LoadingSlugResolution {
|
|
5
|
+
/** The library slug, or null when nothing beat the generic skeleton. */
|
|
6
|
+
slug: KindLoadingSlug | null;
|
|
7
|
+
origin: LoadingSlugOrigin;
|
|
8
|
+
/**
|
|
9
|
+
* The declared value when it was NOT a library slug — a data defect that
|
|
10
|
+
* resolution routed around. Present means "this kind declares a lie".
|
|
11
|
+
*/
|
|
12
|
+
invalidDeclared?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Test seam — resets the once-per-kind scream dedupe. */
|
|
15
|
+
export declare function resetInvalidLoadingDeclarationReports(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Which loading silhouette does this kind get?
|
|
18
|
+
*
|
|
19
|
+
* 1. DECLARED — `metadata.loading_component` (read through the registry, not
|
|
20
|
+
* off the definition: a Python-owned kind never produces a definition, so
|
|
21
|
+
* its owner's choice lives only in the catalog side map) when it names a
|
|
22
|
+
* real library slug.
|
|
23
|
+
* 2. DERIVED — from the kind's own parser schema, else from its emitted JSON
|
|
24
|
+
* Schema (the only shape description most kinds carry). Costs nothing to
|
|
25
|
+
* author and gives 857-of-868 undeclared kinds a shaped loader.
|
|
26
|
+
* 3. GENERIC — nothing distinctive enough; `null` lets the registry's own
|
|
27
|
+
* default answer.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveLoadingSlugForKind(kind: string | null | undefined): LoadingSlugResolution;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { captureError } from "../../../host/diagnostics.js";
|
|
3
|
+
import { kindRegistry } from "../../../host/kind-registry.js";
|
|
4
|
+
import {
|
|
5
|
+
inferLoadingSlug,
|
|
6
|
+
inferLoadingSlugFromJsonSchema
|
|
7
|
+
} from "./infer-loading-slug.js";
|
|
8
|
+
import { isKnownKindLoadingSlug } from "./kind-loading-slugs.js";
|
|
9
|
+
const GENERIC = { slug: null, origin: "generic" };
|
|
10
|
+
const reportedInvalidDeclarations = /* @__PURE__ */ new Set();
|
|
11
|
+
function reportInvalidDeclaration(kind, declared) {
|
|
12
|
+
const seenKey = `${kind}\0${declared}`;
|
|
13
|
+
if (reportedInvalidDeclarations.has(seenKey)) return;
|
|
14
|
+
reportedInvalidDeclarations.add(seenKey);
|
|
15
|
+
const message = `[content-ir] kind "${kind}" declares loading_component "${declared}", which is not in the loading library (kind-loading-slugs.ts). Falling back to the DERIVED loader so the declaration cannot make the render worse \u2014 but the declaration is a defect: fix the row or add "${declared}" to the library.`;
|
|
16
|
+
console.error(message);
|
|
17
|
+
try {
|
|
18
|
+
captureError({
|
|
19
|
+
source: "content-ir",
|
|
20
|
+
message,
|
|
21
|
+
relation: kind,
|
|
22
|
+
callSite: "resolveLoadingSlugForKind",
|
|
23
|
+
hint: "kind_definition.metadata.loading_component must name a slug from KIND_LOADING_SLUGS.",
|
|
24
|
+
raw: { kind, declared }
|
|
25
|
+
});
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function resetInvalidLoadingDeclarationReports() {
|
|
30
|
+
reportedInvalidDeclarations.clear();
|
|
31
|
+
}
|
|
32
|
+
function resolveLoadingSlugForKind(kind) {
|
|
33
|
+
if (!kind) return GENERIC;
|
|
34
|
+
const raw = kindRegistry.getDeclaredLoadingComponent(kind)?.trim() ?? "";
|
|
35
|
+
if (raw && isKnownKindLoadingSlug(raw)) {
|
|
36
|
+
return { slug: raw, origin: "declared" };
|
|
37
|
+
}
|
|
38
|
+
const invalidDeclared = raw || void 0;
|
|
39
|
+
if (invalidDeclared !== void 0) {
|
|
40
|
+
reportInvalidDeclaration(kind, invalidDeclared);
|
|
41
|
+
}
|
|
42
|
+
const derived = inferLoadingSlug(kindRegistry.getDefinition(kind)?.schema) ?? inferLoadingSlugFromJsonSchema(kindRegistry.getEmittedJsonSchema(kind));
|
|
43
|
+
if (derived) {
|
|
44
|
+
return invalidDeclared === void 0 ? { slug: derived, origin: "derived" } : { slug: derived, origin: "derived", invalidDeclared };
|
|
45
|
+
}
|
|
46
|
+
return invalidDeclared === void 0 ? GENERIC : { slug: null, origin: "generic", invalidDeclared };
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
resetInvalidLoadingDeclarationReports,
|
|
50
|
+
resolveLoadingSlugForKind
|
|
51
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Matrix binding of the PROVISIONAL (streaming partial kinds) route.
|
|
3
|
+
*
|
|
4
|
+
* Contract: `common-docs/systems/architecture/content-ir/FEATURE.md`.
|
|
5
|
+
* The reader/validator half is `@ai-matrx/content-ir` (`wire/partial-kind`);
|
|
6
|
+
* the ROUTE half — withhold-by-default, the per-kind opt-in, the
|
|
7
|
+
* anti-stuck-skeleton backstop, the terminal rules — is
|
|
8
|
+
* `@ai-matrx/content-ir-react` (`route/partial-kind-route.ts`), and since
|
|
9
|
+
* 0.8.0 the per-host partial application is `createKindRouteBinding`. Read
|
|
10
|
+
* the semantics there; this module only injects our registries.
|
|
11
|
+
*
|
|
12
|
+
* The wire gate runs in the execution system's `process-stream.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export { IR_PROVISIONAL_KEY, envelopeFromPartialKind, isProvisionalBlock, markKindPartialUnsafe, resetPartialUnsafeKinds, resolveAnnouncedKindLoading, type ProvisionalKindRender, } from "@ai-matrx/content-ir-react";
|
|
15
|
+
/** Has this kind opted in to being handed a provisional value? */
|
|
16
|
+
export declare const isPartialReadyKind: (kind: string) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a block's provisional render, or null when there is nothing to show
|
|
19
|
+
* provisionally (no event, a terminal event, a withheld kind, a kind nothing
|
|
20
|
+
* can route, or a verified envelope that already won).
|
|
21
|
+
*/
|
|
22
|
+
export declare const resolveProvisionalKindRender: <T extends import("@ai-matrx/content-ir-react").IrRoutableBlock & {
|
|
23
|
+
metadata?: Record<string, unknown>;
|
|
24
|
+
}>(block: T, options?: Pick<import("@ai-matrx/content-ir-react").PartialRenderOptions, "streamActive">) => import("@ai-matrx/content-ir-react").ProvisionalKindRender<T> | null;
|
|
25
|
+
/**
|
|
26
|
+
* Route the completed JSON already present on a workflow block when its
|
|
27
|
+
* partial-kind channel closes before the canonical envelope is attached.
|
|
28
|
+
*/
|
|
29
|
+
export declare const resolveSupersededKindRender: <T extends import("@ai-matrx/content-ir-react").IrRoutableBlock & {
|
|
30
|
+
content: string;
|
|
31
|
+
metadata?: Record<string, unknown>;
|
|
32
|
+
}>(block: T) => import("@ai-matrx/content-ir-react").SupersededKindRender<T> | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createKindRouteBinding } from "@ai-matrx/content-ir-react";
|
|
2
|
+
import { MATRX_OWNED_BLOCK_TYPES, matrxKindRouteEnv } from "../host/route-env.js";
|
|
3
|
+
import {
|
|
4
|
+
IR_PROVISIONAL_KEY,
|
|
5
|
+
envelopeFromPartialKind,
|
|
6
|
+
isProvisionalBlock,
|
|
7
|
+
markKindPartialUnsafe,
|
|
8
|
+
resetPartialUnsafeKinds,
|
|
9
|
+
resolveAnnouncedKindLoading
|
|
10
|
+
} from "@ai-matrx/content-ir-react";
|
|
11
|
+
const binding = createKindRouteBinding(matrxKindRouteEnv, {
|
|
12
|
+
ownedTypes: MATRX_OWNED_BLOCK_TYPES
|
|
13
|
+
});
|
|
14
|
+
const isPartialReadyKind = binding.isPartialReadyKind;
|
|
15
|
+
const resolveProvisionalKindRender = binding.resolveProvisionalKindRender;
|
|
16
|
+
const resolveSupersededKindRender = binding.resolveSupersededKindRender;
|
|
17
|
+
export {
|
|
18
|
+
IR_PROVISIONAL_KEY,
|
|
19
|
+
envelopeFromPartialKind,
|
|
20
|
+
isPartialReadyKind,
|
|
21
|
+
isProvisionalBlock,
|
|
22
|
+
markKindPartialUnsafe,
|
|
23
|
+
resetPartialUnsafeKinds,
|
|
24
|
+
resolveAnnouncedKindLoading,
|
|
25
|
+
resolveProvisionalKindRender,
|
|
26
|
+
resolveSupersededKindRender
|
|
27
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE REGISTRY-VERSIONED READ — the primitive that survives the React Compiler.
|
|
3
|
+
*
|
|
4
|
+
* 🚨 THE DEFECT THIS CLOSES (DD-215c, observed on production 2026-09-14).
|
|
5
|
+
*
|
|
6
|
+
* Kind rendering reads MODULE SINGLETONS during render (`componentRegistry`,
|
|
7
|
+
* `kindRegistry`). Nothing in React state changes when a row lands, so every
|
|
8
|
+
* such reader pairs its read with `useContentIrKindVersion(kind)`, which
|
|
9
|
+
* subscribes to that kind's counter and returns it. The counter was then used
|
|
10
|
+
* as a memo INVALIDATION KEY in the only way a number can be used when the
|
|
11
|
+
* computation does not need it:
|
|
12
|
+
*
|
|
13
|
+
* const block = useMemo(() => {
|
|
14
|
+
* void kindRouteVersion; // registry-arrival invalidation key
|
|
15
|
+
* return applyIrKindRoute(rawBlock);
|
|
16
|
+
* }, [rawBlock, kindRouteVersion]);
|
|
17
|
+
*
|
|
18
|
+
* That idiom is correct without the React Compiler and WRONG with it — and
|
|
19
|
+
* `next.config.js` sets `reactCompiler: true`. The compiler re-infers
|
|
20
|
+
* memoization from data flow: `void kindRouteVersion;` produces nothing, so the
|
|
21
|
+
* version is not an input, and the emitted cache is keyed on the block alone:
|
|
22
|
+
*
|
|
23
|
+
* if ($[13] !== rawBlock) { t6 = applyIrKindRoute(rawBlock); $[13] = rawBlock; ... }
|
|
24
|
+
*
|
|
25
|
+
* The subscription still fires and the component still re-renders — the route
|
|
26
|
+
* simply comes back out of the cache, so a block that mounted before its
|
|
27
|
+
* organization's component row landed keeps the platform's bundled component
|
|
28
|
+
* for the life of the mount. Jest does not run the compiler, so every test of
|
|
29
|
+
* that code passes on source semantics production does not have. Three lanes
|
|
30
|
+
* shipped fixes proven that way and production disproved each one.
|
|
31
|
+
*
|
|
32
|
+
* THE RULE, therefore: a registry read that must follow a registry version
|
|
33
|
+
* takes that version AS AN ARGUMENT, through this module. The version is a real
|
|
34
|
+
* input to a real cache key here, so no compiler pass can decide it is dead —
|
|
35
|
+
* and if the compiler memoizes the call site too, it memoizes on the same two
|
|
36
|
+
* values and the answer is identical. Guard: `pnpm check:registry-repaint`.
|
|
37
|
+
*/
|
|
38
|
+
interface VersionedEntry<T> {
|
|
39
|
+
version: number;
|
|
40
|
+
value: T;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read `compute()` for an OBJECT subject, re-running it whenever the registry
|
|
44
|
+
* version moves. One entry per subject; the WeakMap lets the subject (a block,
|
|
45
|
+
* a node) be collected with its answer.
|
|
46
|
+
*/
|
|
47
|
+
export declare function readAtVersionFor<K extends object, T>(cache: WeakMap<K, VersionedEntry<T>>, subject: K, version: number, compute: () => T): T;
|
|
48
|
+
/**
|
|
49
|
+
* Read `compute()` for a STRING subject (a kind slug), re-running it whenever
|
|
50
|
+
* the registry version moves. One entry per key — the map is bounded by the
|
|
51
|
+
* number of distinct kinds a tab renders, never by version churn.
|
|
52
|
+
*/
|
|
53
|
+
export declare function readAtVersionForKey<T>(cache: Map<string, VersionedEntry<T>>, key: string, version: number, compute: () => T): T;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function readAtVersionFor(cache, subject, version, compute) {
|
|
2
|
+
const hit = cache.get(subject);
|
|
3
|
+
if (hit !== void 0 && hit.version === version) return hit.value;
|
|
4
|
+
const value = compute();
|
|
5
|
+
cache.set(subject, { version, value });
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
function readAtVersionForKey(cache, key, version, compute) {
|
|
9
|
+
const hit = cache.get(key);
|
|
10
|
+
if (hit !== void 0 && hit.version === version) return hit.value;
|
|
11
|
+
const value = compute();
|
|
12
|
+
cache.set(key, { version, value });
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
readAtVersionFor,
|
|
17
|
+
readAtVersionForKey
|
|
18
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ROUTE, KEYED ON THE REGISTRY STATE THAT PRODUCED IT (DD-215c).
|
|
3
|
+
*
|
|
4
|
+
* A block's route is not a function of the block alone — it is a function of
|
|
5
|
+
* the block AND of what the component resolver held when the decision was
|
|
6
|
+
* made. Writing it as `f(block)` and hanging the registry version off a memo
|
|
7
|
+
* dependency array is what froze `/shapes/<kind>/instances` on the platform's
|
|
8
|
+
* bundled component for a reader whose organization had authored its own: the
|
|
9
|
+
* React Compiler re-infers memo inputs from data flow, the version was only
|
|
10
|
+
* ever `void`-ed, and the emitted cache was keyed on the block alone. The whole
|
|
11
|
+
* story, and the rule, are in `registry-versioned.ts`.
|
|
12
|
+
*
|
|
13
|
+
* So the version is an ARGUMENT here. Call it with
|
|
14
|
+
* `useContentIrKindVersion(envelopeKind)` and the route follows every arrival;
|
|
15
|
+
* there is no way to call it and silently lose the invalidation.
|
|
16
|
+
*/
|
|
17
|
+
import { type IrRoutableBlock } from "./kind-route.js";
|
|
18
|
+
/**
|
|
19
|
+
* Route one block at a known registry version — the seam every render path
|
|
20
|
+
* calls instead of `applyIrKindRoute` directly.
|
|
21
|
+
*
|
|
22
|
+
* `resolveSupersededKindRender` runs first for the same reason it always has:
|
|
23
|
+
* a block whose partial channel was superseded routes off the completed value,
|
|
24
|
+
* not off the partial envelope.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The route itself, uncached — what `routeBlockAtRegistryVersion` computes and
|
|
28
|
+
* memoises. For pure callers that ask the renderer's question without a
|
|
29
|
+
* registry version (the frame judge, `draws-raw-kind-json.ts`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function routeBlockNow<T extends IrRoutableBlock & {
|
|
32
|
+
content: string;
|
|
33
|
+
}>(block: T): T;
|
|
34
|
+
export declare function routeBlockAtRegistryVersion<T extends IrRoutableBlock & {
|
|
35
|
+
content: string;
|
|
36
|
+
}>(block: T, registryVersion: number): T;
|
|
37
|
+
/** Test seam — drops the per-session dedupe. */
|
|
38
|
+
export declare function resetStaleRouteReports(): void;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
applyIrKindRoute,
|
|
4
|
+
DB_KIND_COMPONENT_KEY,
|
|
5
|
+
readIrRouteMarker
|
|
6
|
+
} from "./kind-route.js";
|
|
7
|
+
import { resolveSupersededKindRender } from "./partial-kind-route.js";
|
|
8
|
+
import { readAtVersionFor } from "./registry-versioned.js";
|
|
9
|
+
import { readEnvelope } from "../redux/render-block-envelope.js";
|
|
10
|
+
import { componentRegistry } from "../../host/component-registry.js";
|
|
11
|
+
import {
|
|
12
|
+
MATRX_CONTENT_IR_PLATFORM,
|
|
13
|
+
MATRX_OWNED_BLOCK_TYPES
|
|
14
|
+
} from "../host/route-env.js";
|
|
15
|
+
const routed = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
function routeBlockNow(block) {
|
|
17
|
+
return resolveSupersededKindRender(block)?.block ?? applyIrKindRoute(block);
|
|
18
|
+
}
|
|
19
|
+
function routeBlockAtRegistryVersion(block, registryVersion) {
|
|
20
|
+
const answer = readAtVersionFor(
|
|
21
|
+
routed,
|
|
22
|
+
block,
|
|
23
|
+
registryVersion,
|
|
24
|
+
() => routeBlockNow(block)
|
|
25
|
+
);
|
|
26
|
+
announceStaleRoute(answer);
|
|
27
|
+
return answer;
|
|
28
|
+
}
|
|
29
|
+
const announced = /* @__PURE__ */ new Set();
|
|
30
|
+
function resetStaleRouteReports() {
|
|
31
|
+
announced.clear();
|
|
32
|
+
}
|
|
33
|
+
function announceStaleRoute(block) {
|
|
34
|
+
const marker = readIrRouteMarker(block.metadata);
|
|
35
|
+
if (marker && (marker.by === "generic" || marker.by === "directive")) return;
|
|
36
|
+
const envelope = readEnvelope(block.metadata);
|
|
37
|
+
const kind = envelope?.root.kind;
|
|
38
|
+
if (!kind) return;
|
|
39
|
+
const resolution = componentRegistry.resolve(
|
|
40
|
+
kind,
|
|
41
|
+
MATRX_CONTENT_IR_PLATFORM,
|
|
42
|
+
"output"
|
|
43
|
+
);
|
|
44
|
+
if (!resolution) return;
|
|
45
|
+
if (marker && marker.by === resolution.resolvedBy && marker.key === resolution.componentKey) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!marker) {
|
|
49
|
+
const routable = resolution.resolvedBy === "db" && resolution.source === "db" && resolution.isActive && resolution.hasComponentSource;
|
|
50
|
+
const exempt = MATRX_OWNED_BLOCK_TYPES.includes(block.type) || envelope?.root.kindState === "raw" || envelope?.root.kindState === "pending_schema";
|
|
51
|
+
if (!routable || exempt || block.type === DB_KIND_COMPONENT_KEY) return;
|
|
52
|
+
}
|
|
53
|
+
const renderedBy = marker?.by ?? "no route marker";
|
|
54
|
+
const renderedKey = marker?.key ?? block.type;
|
|
55
|
+
const seen = `${kind}|${renderedBy}:${renderedKey}|${resolution.resolvedBy}:${resolution.componentKey}`;
|
|
56
|
+
if (announced.has(seen)) return;
|
|
57
|
+
announced.add(seen);
|
|
58
|
+
void (async () => {
|
|
59
|
+
try {
|
|
60
|
+
const { reportKindComponentIncident } = await import("../../host/kind-incident.js");
|
|
61
|
+
reportKindComponentIncident({
|
|
62
|
+
kind,
|
|
63
|
+
errorType: "stale_route_render",
|
|
64
|
+
platform: MATRX_CONTENT_IR_PLATFORM,
|
|
65
|
+
role: "output",
|
|
66
|
+
componentKey: resolution.componentKey,
|
|
67
|
+
componentUpdatedAt: resolution.updatedAt,
|
|
68
|
+
message: `The reader is being shown "${renderedKey}" (resolved by ${renderedBy}) for kind "${kind}", but this browser's resolver already answers "${resolution.componentKey}" (resolved by ${resolution.resolvedBy}${resolution.hasComponentSource ? ", body present" : ", no body"}). The rendered block is a STALE route decision: the component row arrived after the block mounted and the route was not recomputed. Nothing on screen says so \u2014 the wrong component looks like the right one. Rendered at ${block.type}.`
|
|
69
|
+
});
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
})();
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
resetStaleRouteReports,
|
|
76
|
+
routeBlockAtRegistryVersion,
|
|
77
|
+
routeBlockNow
|
|
78
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE KIND SLOT — one reserved place on a surface, from first paint to final
|
|
3
|
+
* content, without ever moving anything above it.
|
|
4
|
+
*
|
|
5
|
+
* Ratified by Arman 2026-08-25, from the podcast run page: a slot "takes up
|
|
6
|
+
* the space… it's sort of animated, but it's fairly steady, and it tries to
|
|
7
|
+
* look a little bit like it's gonna look eventually, but without looking like
|
|
8
|
+
* it's actively, instantly loading. And then when the content actually starts
|
|
9
|
+
* coming, boom — that's when it switches to the loading component."
|
|
10
|
+
*
|
|
11
|
+
* ─── The four phases ────────────────────────────────────────────────────────
|
|
12
|
+
* reserved the kind's silhouette, STILL — holds the footprint, nothing has
|
|
13
|
+
* started. (`PodcastCompositionPlaceholder` is the reference look.)
|
|
14
|
+
* arriving the same silhouette, now working — spinner, shimmer, live early
|
|
15
|
+
* keys. The switch moves nothing: same component, same footprint.
|
|
16
|
+
* settled the real kind component, growing downward from its first
|
|
17
|
+
* renderable unit (THE ONE LOADING SEQUENCE).
|
|
18
|
+
* failed an honest failure, inside the same reserved footprint.
|
|
19
|
+
*
|
|
20
|
+
* ─── Why the placeholder is a PHASE, not a third component ──────────────────
|
|
21
|
+
* A separately-authored loader would mean a second artifact per kind to keep
|
|
22
|
+
* in sync with the schema (868 of them), and — worse — swapping between two
|
|
23
|
+
* components unmounts and remounts, so the layout jumps and animations
|
|
24
|
+
* restart at exactly the moment we are trying to keep calm. One silhouette in
|
|
25
|
+
* two moods keeps the transition continuous, and every kind gets a
|
|
26
|
+
* shape-appropriate placeholder for free via the declared-or-derived slug
|
|
27
|
+
* (`resolveLoadingSlugForKind`) — including the 857 that declare nothing.
|
|
28
|
+
*
|
|
29
|
+
* ─── Who owns the footprint ─────────────────────────────────────────────────
|
|
30
|
+
* The slot owns the SHAPE; the HOST owns the floor. A surface that knows how
|
|
31
|
+
* much room a region deserves already applies it to its own box — see
|
|
32
|
+
* `ReadoutTile`'s authored `minHeight` in `RunSurfaceView.tsx` ("the zero
|
|
33
|
+
* page-shift law") — and a second floor inside the slot would fight it, or
|
|
34
|
+
* silently lose to an `overflow-y-auto` ancestor. So there is deliberately no
|
|
35
|
+
* `minHeight` prop here: the silhouette's own height does the reserving, and
|
|
36
|
+
* content longer than it grows DOWNWARD, the one direction that disturbs
|
|
37
|
+
* nothing the reader is already looking at.
|
|
38
|
+
*
|
|
39
|
+
* ─── Identity ───────────────────────────────────────────────────────────────
|
|
40
|
+
* `slotKey` is the slot's OWN identity and must be stable from first paint —
|
|
41
|
+
* it exists before any producer does, which is exactly why it cannot be a
|
|
42
|
+
* request id (there is no request yet when the slot is reserved). A producer
|
|
43
|
+
* id (a streaming lane's requestId, a workflow `invocationKeyOf(...)`) is a
|
|
44
|
+
* BINDING the caller resolves later and feeds in as `phase` + `children`.
|
|
45
|
+
*/
|
|
46
|
+
import React from "react";
|
|
47
|
+
import type { KindLoadingProps } from "../loading/kind-loading.types.js";
|
|
48
|
+
export type KindSlotPhase = "reserved" | "arriving" | "settled" | "failed";
|
|
49
|
+
export interface KindSlotProps {
|
|
50
|
+
/**
|
|
51
|
+
* Stable identity for this slot, known at first paint and never changing.
|
|
52
|
+
* Surfaces derive it from their own structure (a workflow invocation key, a
|
|
53
|
+
* podcast asset index, an authored slot id) — never from a producer id.
|
|
54
|
+
*/
|
|
55
|
+
slotKey: string;
|
|
56
|
+
/** The kind expected here, when known. Drives which silhouette reserves. */
|
|
57
|
+
kind?: string | null;
|
|
58
|
+
phase: KindSlotPhase;
|
|
59
|
+
/**
|
|
60
|
+
* Early keys for the arriving phase (title / count / loading_message …) and
|
|
61
|
+
* the partial value a data-fed smart loader performs. Ignored when settled.
|
|
62
|
+
*/
|
|
63
|
+
early?: KindLoadingProps;
|
|
64
|
+
/** The settled content — the real kind component. */
|
|
65
|
+
children?: React.ReactNode;
|
|
66
|
+
/** Rendered in the `failed` phase. */
|
|
67
|
+
error?: React.ReactNode;
|
|
68
|
+
/**
|
|
69
|
+
* `bare` when the host already draws the frame/icon/title around this slot
|
|
70
|
+
* (a workflow deliverable card does) — the silhouette then contributes only
|
|
71
|
+
* its body, never a second header.
|
|
72
|
+
*/
|
|
73
|
+
chrome?: "full" | "bare";
|
|
74
|
+
className?: string;
|
|
75
|
+
}
|
|
76
|
+
export declare function KindSlot({ slotKey, kind, phase, early, children, error, chrome, className, }: KindSlotProps): React.JSX.Element;
|
|
77
|
+
/**
|
|
78
|
+
* The phase a slot is in, from the two facts every producer surface has: has
|
|
79
|
+
* anything started, and has it finished. Kept here so a surface never invents
|
|
80
|
+
* its own phase vocabulary — the workflow run board, a podcast slot, and an
|
|
81
|
+
* authored page all map into the SAME four phases.
|
|
82
|
+
*/
|
|
83
|
+
export declare function kindSlotPhase(input: {
|
|
84
|
+
started: boolean;
|
|
85
|
+
settled: boolean;
|
|
86
|
+
failed?: boolean;
|
|
87
|
+
}): KindSlotPhase;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { resolveKindLoadingComponent } from "../loading/kind-loading-registry.js";
|
|
5
|
+
import { resolveLoadingSlugForKind } from "../loading/resolve-loading-slug.js";
|
|
6
|
+
import { useContentIrKindVersion } from "../use-registry-repaint.js";
|
|
7
|
+
import { readAtVersionForKey } from "../registry-versioned.js";
|
|
8
|
+
import { useEnsureKindRenderable } from "../ensure-kind-renderable.js";
|
|
9
|
+
const loadingSlugCache = /* @__PURE__ */ new Map();
|
|
10
|
+
function KindSlot({
|
|
11
|
+
slotKey,
|
|
12
|
+
kind,
|
|
13
|
+
phase,
|
|
14
|
+
early,
|
|
15
|
+
children,
|
|
16
|
+
error,
|
|
17
|
+
chrome,
|
|
18
|
+
className
|
|
19
|
+
}) {
|
|
20
|
+
useEnsureKindRenderable(kind ?? null);
|
|
21
|
+
const kindVersion = useContentIrKindVersion(kind ?? null);
|
|
22
|
+
const slug = readAtVersionForKey(
|
|
23
|
+
loadingSlugCache,
|
|
24
|
+
kind ?? "",
|
|
25
|
+
kindVersion,
|
|
26
|
+
() => resolveLoadingSlugForKind(kind).slug
|
|
27
|
+
);
|
|
28
|
+
return /* @__PURE__ */ jsx(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
"data-kind-slot": slotKey,
|
|
32
|
+
"data-kind-slot-phase": phase,
|
|
33
|
+
"data-kind-slot-kind": kind ?? void 0,
|
|
34
|
+
className,
|
|
35
|
+
children: phase === "settled" ? children : phase === "failed" ? error : /* @__PURE__ */ jsx(
|
|
36
|
+
PendingSilhouette,
|
|
37
|
+
{
|
|
38
|
+
slug,
|
|
39
|
+
kind,
|
|
40
|
+
phase,
|
|
41
|
+
early,
|
|
42
|
+
chrome
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const PendingSilhouette = ({ slug, kind, phase, early, chrome }) => {
|
|
49
|
+
const Loader = resolveKindLoadingComponent(slug);
|
|
50
|
+
return React.createElement(Loader, {
|
|
51
|
+
...early,
|
|
52
|
+
kind: kind ?? early?.kind,
|
|
53
|
+
phase,
|
|
54
|
+
chrome
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
function kindSlotPhase(input) {
|
|
58
|
+
if (input.failed) return "failed";
|
|
59
|
+
if (input.settled) return "settled";
|
|
60
|
+
return input.started ? "arriving" : "reserved";
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
KindSlot,
|
|
64
|
+
kindSlotPhase
|
|
65
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscribes the caller to registry changes FOR ONE KIND and returns that
|
|
3
|
+
* kind's combined version. Pass null for blocks with no envelope kind —
|
|
4
|
+
* they never repaint from registries (nothing to learn about them).
|
|
5
|
+
*/
|
|
6
|
+
export declare function useContentIrKindVersion(kind: string | null): number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
3
|
+
import { kindRegistry } from "../../host/kind-registry.js";
|
|
4
|
+
import { componentRegistry } from "../../host/component-registry.js";
|
|
5
|
+
const noopSubscribe = () => () => {
|
|
6
|
+
};
|
|
7
|
+
const zero = () => 0;
|
|
8
|
+
function useContentIrKindVersion(kind) {
|
|
9
|
+
const subscribe = useCallback(
|
|
10
|
+
(onStoreChange) => {
|
|
11
|
+
if (!kind) return noopSubscribe();
|
|
12
|
+
const unsubKinds = kindRegistry.subscribeKind(kind, onStoreChange);
|
|
13
|
+
const unsubComponents = componentRegistry.subscribeKind(
|
|
14
|
+
kind,
|
|
15
|
+
onStoreChange
|
|
16
|
+
);
|
|
17
|
+
return () => {
|
|
18
|
+
unsubKinds();
|
|
19
|
+
unsubComponents();
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
[kind]
|
|
23
|
+
);
|
|
24
|
+
const getSnapshot = useCallback(
|
|
25
|
+
() => kind ? (
|
|
26
|
+
// Both counters are monotonic, so the sum is monotonic — a change
|
|
27
|
+
// in either registry produces a new snapshot value for this kind.
|
|
28
|
+
kindRegistry.getKindVersion(kind) + componentRegistry.getKindVersion(kind)
|
|
29
|
+
) : 0,
|
|
30
|
+
[kind]
|
|
31
|
+
);
|
|
32
|
+
return useSyncExternalStore(subscribe, getSnapshot, zero);
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
useContentIrKindVersion
|
|
36
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type IrPath } from "@ai-matrx/content-ir";
|
|
2
|
+
import type { IrTreeNode } from "@ai-matrx/content-ir";
|
|
3
|
+
export declare function useIrNode(identity: string | null, path: IrPath | string): IrTreeNode | null;
|
|
4
|
+
/**
|
|
5
|
+
* Structural subscription: the list of node pathKeys for a session, updated
|
|
6
|
+
* when nodes are added/removed/changed. Containers use this to know which
|
|
7
|
+
* children to mount without subscribing to every child's data.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useIrNodePaths(identity: string | null): string[];
|