@ai-matrx/rich-content 0.0.0 → 0.2.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/CHANGELOG.md +80 -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/math-vendor-LICENSE-NOTICE.md +18 -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,23 @@
|
|
|
1
|
+
import { unescapeCellPipes } from "./gfm-cell-pipes.js";
|
|
2
|
+
function unescapeCodeIn(node) {
|
|
3
|
+
if (node.type === "inlineCode" && typeof node.value === "string") {
|
|
4
|
+
node.value = unescapeCellPipes(node.value);
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
node.children?.forEach(unescapeCodeIn);
|
|
8
|
+
}
|
|
9
|
+
function visit(node) {
|
|
10
|
+
if (node.type === "tableCell") {
|
|
11
|
+
unescapeCodeIn(node);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
node.children?.forEach(visit);
|
|
15
|
+
}
|
|
16
|
+
function remarkTableCodePipes() {
|
|
17
|
+
return (tree) => {
|
|
18
|
+
visit(tree);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
remarkTableCodePipes as default
|
|
23
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isWithinCodeBlock } from "remend";
|
|
2
|
+
import { hasUnclosedFrontmatter } from "./frontmatter.js";
|
|
3
|
+
import { unfinishedHeadingIdHead } from "./linear-scan.js";
|
|
4
|
+
function lastLineStart(text) {
|
|
5
|
+
return text.lastIndexOf("\n") + 1;
|
|
6
|
+
}
|
|
7
|
+
const pendingFrontmatter = {
|
|
8
|
+
name: "matrx-pending-frontmatter",
|
|
9
|
+
priority: 0,
|
|
10
|
+
handle: (text) => {
|
|
11
|
+
if (text === "-" || text === "--" || text === "---" || text === "+" || text === "++" || text === "+++") return "";
|
|
12
|
+
if (!hasUnclosedFrontmatter(text)) return text;
|
|
13
|
+
const [opener, ...rest] = text.split("\n");
|
|
14
|
+
const looks = opener === "+++" ? TOML_LINE : YAML_LINE;
|
|
15
|
+
return rest.every((line, i) => looks.test(line) || i === rest.length - 1 && /^[\w"'-]*$/.test(line)) ? "" : text;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const YAML_LINE = /^(?:[\w"'][^:\n]*:(?:\s.*)?|\s+.*|-(?:\s.*)?|#.*|)$/;
|
|
19
|
+
const TOML_LINE = /^\s*(?:[\w."-]+\s*=.*|\[.*\]|#.*|)$/;
|
|
20
|
+
const TRAILING_BLOCK_MARKER = /^(?:[ \t]{0,3}:{2,}[^\n]*|[ \t]{0,3}>[ \t]*\[!?[^\]\n]*\]?[+-]?|(?:!!!|\?\?\?\+?)[^\n]*|\*\[[^\n]*)$/;
|
|
21
|
+
const pendingBlockMarker = {
|
|
22
|
+
name: "matrx-pending-block-marker",
|
|
23
|
+
priority: 2,
|
|
24
|
+
handle: (text) => {
|
|
25
|
+
const start = lastLineStart(text);
|
|
26
|
+
const line = text.slice(start);
|
|
27
|
+
if (!line || !TRAILING_BLOCK_MARKER.test(line)) return text;
|
|
28
|
+
if (isWithinCodeBlock(text, start)) return text;
|
|
29
|
+
if (/^[ \t]{0,3}>[ \t]*\[![A-Za-z-]+\][+-]?[ \t]*$/.test(line)) return text;
|
|
30
|
+
return text.slice(0, start);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const TRAILING_WIKILINK = /(!?)\[\[([^[\]\n]*)\]?$/;
|
|
34
|
+
const TRAILING_TEXT_DIRECTIVE = /:(?:span|mark|kbd|abbr|sup|sub)\[([^\]\n]*)(?:\](\{[^}\n]*)?)?$/;
|
|
35
|
+
const TRAILING_FOOTNOTE_REF = /\[\^[^\]\s]*$/;
|
|
36
|
+
const TRAILING_EMOJI = /(^|[\s(])(:[a-z0-9_+-]+)$/;
|
|
37
|
+
const TRAILING_HIGHLIGHT = /(^|[^=\\])==(?=[^\s=])([^\n=]*)$/;
|
|
38
|
+
const pendingInline = {
|
|
39
|
+
name: "matrx-pending-inline-syntax",
|
|
40
|
+
priority: 3,
|
|
41
|
+
handle: (text) => {
|
|
42
|
+
const start = lastLineStart(text);
|
|
43
|
+
if (isWithinCodeBlock(text, start)) return text;
|
|
44
|
+
let out = text;
|
|
45
|
+
const wiki = TRAILING_WIKILINK.exec(out);
|
|
46
|
+
if (wiki && wiki.index >= start) {
|
|
47
|
+
out = out.slice(0, wiki.index) + (wiki[1] ? "" : wiki[2] ?? "");
|
|
48
|
+
}
|
|
49
|
+
const directive = TRAILING_TEXT_DIRECTIVE.exec(out);
|
|
50
|
+
if (directive && directive.index >= start && (directive[2] !== void 0 || !out.endsWith("]"))) {
|
|
51
|
+
out = out.slice(0, directive.index) + (directive[1] ?? "");
|
|
52
|
+
}
|
|
53
|
+
const fn = TRAILING_FOOTNOTE_REF.exec(out);
|
|
54
|
+
if (fn && fn.index >= start) out = out.slice(0, fn.index);
|
|
55
|
+
const emoji = TRAILING_EMOJI.exec(out);
|
|
56
|
+
if (emoji && emoji.index >= start) out = out.slice(0, emoji.index + (emoji[1] ?? "").length);
|
|
57
|
+
const hl = TRAILING_HIGHLIGHT.exec(out);
|
|
58
|
+
if (hl && hl.index >= start && (hl[2] ?? "").trim() && !/\s$/.test(out)) out = `${out}==`;
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const pendingHeadingId = {
|
|
63
|
+
name: "matrx-pending-heading-id",
|
|
64
|
+
priority: -2,
|
|
65
|
+
handle: (text) => {
|
|
66
|
+
const start = lastLineStart(text);
|
|
67
|
+
const head = unfinishedHeadingIdHead(text.slice(start));
|
|
68
|
+
if (head === null || isWithinCodeBlock(text, start)) return text;
|
|
69
|
+
return text.slice(0, start) + head;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
function openDisplayDollar(text) {
|
|
73
|
+
let at = -1;
|
|
74
|
+
let open = -1;
|
|
75
|
+
while ((at = text.indexOf("$$", at + 1)) !== -1) {
|
|
76
|
+
if (isWithinCodeBlock(text, at)) continue;
|
|
77
|
+
open = open === -1 ? at : -1;
|
|
78
|
+
at += 1;
|
|
79
|
+
}
|
|
80
|
+
return open;
|
|
81
|
+
}
|
|
82
|
+
const pendingMath = {
|
|
83
|
+
name: "matrx-pending-math",
|
|
84
|
+
priority: -1,
|
|
85
|
+
handle: (text) => {
|
|
86
|
+
let cut = openDisplayDollar(text);
|
|
87
|
+
for (const [open, close] of [
|
|
88
|
+
["\\[", "\\]"],
|
|
89
|
+
["\\(", "\\)"]
|
|
90
|
+
]) {
|
|
91
|
+
const o = text.lastIndexOf(open);
|
|
92
|
+
if (o !== -1 && text.indexOf(close, o) === -1 && !isWithinCodeBlock(text, o)) {
|
|
93
|
+
cut = cut === -1 ? o : Math.min(cut, o);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return cut === -1 ? text : text.slice(0, cut);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const SYNTAX_STREAM_HANDLERS = [
|
|
100
|
+
pendingFrontmatter,
|
|
101
|
+
pendingBlockMarker,
|
|
102
|
+
pendingInline,
|
|
103
|
+
pendingHeadingId,
|
|
104
|
+
pendingMath
|
|
105
|
+
];
|
|
106
|
+
export {
|
|
107
|
+
SYNTAX_STREAM_HANDLERS
|
|
108
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface TaskToggleRequest {
|
|
2
|
+
/** The item text as rendered. */
|
|
3
|
+
text: string;
|
|
4
|
+
/** Index among the rendered block's tasks — the tiebreaker for duplicates. */
|
|
5
|
+
index: number;
|
|
6
|
+
/** The state the person asked for. */
|
|
7
|
+
checked: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** The new source with that one task toggled, or null when no stored task matches. */
|
|
10
|
+
export declare function toggleTaskInSource(source: string, request: TaskToggleRequest): string | null;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { blockEdit, spliceSave, tokenizeSource } from "@ai-matrx/content-ir/source";
|
|
2
|
+
const TASK_LINE = /^([ \t]*(?:[-*+]|\d+[.)])[ \t]+)\[([ xX])\]([ \t]+|$)(.*)$/;
|
|
3
|
+
function normalize(text) {
|
|
4
|
+
return text.replace(/[*_~`=]|\[\[|\]\]/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\s+/g, " ").trim().toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
function toggleTaskInSource(source, request) {
|
|
7
|
+
const blocks = tokenizeSource(source);
|
|
8
|
+
const all = [];
|
|
9
|
+
blocks.forEach((block2, blockIndex) => {
|
|
10
|
+
if (block2.kind !== "prose") return;
|
|
11
|
+
let offset = 0;
|
|
12
|
+
for (const line of block2.raw.split("\n")) {
|
|
13
|
+
const m = TASK_LINE.exec(line);
|
|
14
|
+
if (m) {
|
|
15
|
+
all.push({ blockIndex, lineStart: offset, markerOffset: offset + (m[1] ?? "").length + 1, text: m[4] ?? "" });
|
|
16
|
+
}
|
|
17
|
+
offset += line.length + 1;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (all.length === 0) return null;
|
|
21
|
+
const wanted = normalize(request.text);
|
|
22
|
+
const matches = all.filter((c) => normalize(c.text) === wanted);
|
|
23
|
+
const pick = matches.length === 1 ? matches[0] : matches.length > 1 ? matches[Math.min(request.index, matches.length - 1)] ?? matches[0] : wanted === "" && all[request.index] ? all[request.index] : void 0;
|
|
24
|
+
if (!pick) return null;
|
|
25
|
+
const block = blocks[pick.blockIndex];
|
|
26
|
+
if (!block) return null;
|
|
27
|
+
const nextRaw = block.raw.slice(0, pick.markerOffset) + (request.checked ? "x" : " ") + block.raw.slice(pick.markerOffset + 1);
|
|
28
|
+
if (nextRaw === block.raw) return source;
|
|
29
|
+
const result = spliceSave(source, [blockEdit(block, nextRaw)], { blocks, requireIntegrity: true });
|
|
30
|
+
return result.text;
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
toggleTaskInSource
|
|
34
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Vendored math plugins
|
|
2
|
+
|
|
3
|
+
`remark-math`, `rehype-katex` and the syntax half of `micromark-extension-math` are copied here
|
|
4
|
+
verbatim (one import path changed each) under their MIT license:
|
|
5
|
+
|
|
6
|
+
> (The MIT License) Copyright (c) Titus Wormer <tituswormer@gmail.com>. Permission is hereby
|
|
7
|
+
> granted, free of charge, to any person obtaining a copy of this software and associated
|
|
8
|
+
> documentation files (the 'Software'), to deal in the Software without restriction, including
|
|
9
|
+
> without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
10
|
+
> and/or sell copies of the Software, and to permit persons to whom the Software is furnished to
|
|
11
|
+
> do so, subject to the following conditions: The above copyright notice and this permission
|
|
12
|
+
> notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS
|
|
13
|
+
> PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND.
|
|
14
|
+
|
|
15
|
+
Why: the upstream packages pin `katex ^0.16.0`, which carries GHSA-238p-pmpm-9mq7 (fixed in
|
|
16
|
+
0.18.2), so any npm consumer of this package installed a vulnerable KaTeX. Vendored, the only
|
|
17
|
+
KaTeX in the graph is this package's own `katex` dependency (^0.18.9). Drop the vendor copy the
|
|
18
|
+
day upstream depends on katex >= 0.18.2.
|
package/dist/styles.css
ADDED
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* addressWithoutNavigating — THE one way to change the address bar without a
|
|
3
|
+
* navigation (lane URL-STATE, 2026-09-24; born in lane PANEL-REMOUNT).
|
|
4
|
+
*
|
|
5
|
+
* Query state (`?view=`, `?panels=`, `?tab=`, a selected row, a filter) is
|
|
6
|
+
* bookkeeping about the page you are on, never a new page. `router.replace` /
|
|
7
|
+
* `router.push` are App Router NAVIGATIONS: each one fetches a fresh RSC
|
|
8
|
+
* payload for the route (a `?_rsc=` request, a server round trip) and, on
|
|
9
|
+
* commit, can remount the page — measured on /data/[tableId]: one `?_rsc=`
|
|
10
|
+
* request per layout switch, and before PANEL-REMOUNT the whole grid re-read.
|
|
11
|
+
*
|
|
12
|
+
* `history.pushState` / `history.replaceState` are patched by Next's app
|
|
13
|
+
* router: called with a state object that does NOT carry `__NA`, the patch
|
|
14
|
+
* copies Next's own tree state onto the new entry and dispatches a `restore`,
|
|
15
|
+
* so `useSearchParams` / `usePathname` hold the new URL with no server round
|
|
16
|
+
* trip and no remount. Back/forward still work (Next owns the entry).
|
|
17
|
+
*
|
|
18
|
+
* 🚨 This module always passes `null`. Passing `window.history.state` (the
|
|
19
|
+
* old habit) forwards Next's `__NA` marker, which SKIPS the patch: the address
|
|
20
|
+
* bar changes while `useSearchParams` keeps the old value.
|
|
21
|
+
*
|
|
22
|
+
* Use the router for a REAL route change (another page), or when the server
|
|
23
|
+
* component of the current route reads the changed param from `searchParams`
|
|
24
|
+
* — only a navigation re-renders server output. Everything else comes here.
|
|
25
|
+
* Guard: eslint `matrx/no-navigation-for-query-state`.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Replace the current history entry's address. No navigation, no RSC fetch.
|
|
29
|
+
*
|
|
30
|
+
* A HASH-ONLY change skips Next's patch on purpose: nothing Next exposes
|
|
31
|
+
* (`usePathname`, `useSearchParams`, `useParams`) reads the hash, yet the
|
|
32
|
+
* patch's `restore` hands every `useSearchParams` reader a new object, so each
|
|
33
|
+
* write re-rendered all of them. A canvas writing its camera to `#cam=` while
|
|
34
|
+
* panning paid that per write — over a second per pointer move on a board of
|
|
35
|
+
* fifteen heavy tiles, so the board froze while panning (2026-10-01). Forwarding
|
|
36
|
+
* the entry's own state keeps Next's `__NA` tree on it (Back still works).
|
|
37
|
+
*/
|
|
38
|
+
export declare function replaceAddressWithoutNavigating(href: string | URL): void;
|
|
39
|
+
/** Push a new history entry (Back returns to the old address). No navigation, no RSC fetch. */
|
|
40
|
+
export declare function pushAddressWithoutNavigating(href: string | URL): void;
|
|
41
|
+
/**
|
|
42
|
+
* The current path (and hash) carrying `search` as its query — the usual
|
|
43
|
+
* argument for the two writers above. An empty query drops the `?`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function currentPathWithSearch(search: URLSearchParams | string): string;
|
|
46
|
+
/** Just enough of Next's router for the two helpers below (and their tests). */
|
|
47
|
+
export interface AddressRouter {
|
|
48
|
+
push: (href: string, options?: {
|
|
49
|
+
scroll?: boolean;
|
|
50
|
+
}) => void;
|
|
51
|
+
replace: (href: string, options?: {
|
|
52
|
+
scroll?: boolean;
|
|
53
|
+
}) => void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* For a caller that names its page by a literal path (`/tasks?task=…`) but
|
|
57
|
+
* can also render somewhere else (a window panel over another page): on that
|
|
58
|
+
* page the change is query state, so no navigation; anywhere else it is a real
|
|
59
|
+
* route change, so the router carries it exactly as before (`options` are the
|
|
60
|
+
* router's, passed through unchanged).
|
|
61
|
+
*/
|
|
62
|
+
export declare function replaceAddressOrNavigate(router: AddressRouter, href: string, options?: {
|
|
63
|
+
scroll?: boolean;
|
|
64
|
+
}): void;
|
|
65
|
+
/** The push twin of {@link replaceAddressOrNavigate}. */
|
|
66
|
+
export declare function pushAddressOrNavigate(router: AddressRouter, href: string, options?: {
|
|
67
|
+
scroll?: boolean;
|
|
68
|
+
}): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function currentHref() {
|
|
2
|
+
return `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
3
|
+
}
|
|
4
|
+
function resolve(href) {
|
|
5
|
+
const url = new URL(String(href), window.location.href);
|
|
6
|
+
if (url.origin !== window.location.origin) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`addressWithoutNavigating: ${url.origin} is another origin; history writes are same-origin only. Navigate instead.`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
12
|
+
}
|
|
13
|
+
function replaceAddressWithoutNavigating(href) {
|
|
14
|
+
if (typeof window === "undefined") return;
|
|
15
|
+
const next = resolve(href);
|
|
16
|
+
if (next === currentHref()) return;
|
|
17
|
+
const sameDocument = next.split("#")[0] === `${window.location.pathname}${window.location.search}`;
|
|
18
|
+
const state = window.history.state;
|
|
19
|
+
if (sameDocument && isNextEntry(state)) {
|
|
20
|
+
window.history.replaceState(state, "", next);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
window.history.replaceState(null, "", next);
|
|
24
|
+
}
|
|
25
|
+
function isNextEntry(state) {
|
|
26
|
+
return typeof state === "object" && state !== null && ("__NA" in state || "_N" in state);
|
|
27
|
+
}
|
|
28
|
+
function pushAddressWithoutNavigating(href) {
|
|
29
|
+
if (typeof window === "undefined") return;
|
|
30
|
+
const next = resolve(href);
|
|
31
|
+
if (next === currentHref()) return;
|
|
32
|
+
window.history.pushState(null, "", next);
|
|
33
|
+
}
|
|
34
|
+
function currentPathWithSearch(search) {
|
|
35
|
+
const qs = (typeof search === "string" ? search : search.toString()).replace(
|
|
36
|
+
/^\?/,
|
|
37
|
+
""
|
|
38
|
+
);
|
|
39
|
+
if (typeof window === "undefined") return qs ? `?${qs}` : "";
|
|
40
|
+
return `${window.location.pathname}${qs ? `?${qs}` : ""}${window.location.hash}`;
|
|
41
|
+
}
|
|
42
|
+
function isCurrentPath(href) {
|
|
43
|
+
const url = new URL(String(href), window.location.href);
|
|
44
|
+
return url.origin === window.location.origin && url.pathname === window.location.pathname;
|
|
45
|
+
}
|
|
46
|
+
function replaceAddressOrNavigate(router, href, options) {
|
|
47
|
+
if (typeof window !== "undefined" && isCurrentPath(href)) replaceAddressWithoutNavigating(href);
|
|
48
|
+
else router.replace(href, options);
|
|
49
|
+
}
|
|
50
|
+
function pushAddressOrNavigate(router, href, options) {
|
|
51
|
+
if (typeof window !== "undefined" && isCurrentPath(href)) pushAddressWithoutNavigating(href);
|
|
52
|
+
else router.push(href, options);
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
currentPathWithSearch,
|
|
56
|
+
pushAddressOrNavigate,
|
|
57
|
+
pushAddressWithoutNavigating,
|
|
58
|
+
replaceAddressOrNavigate,
|
|
59
|
+
replaceAddressWithoutNavigating
|
|
60
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `clientLazy` — the package's replacement for `next/dynamic(..., { ssr: false })`: a lazily
|
|
3
|
+
* loaded component that renders only on the client. On the server and during hydration it
|
|
4
|
+
* renders `loading` (hydration-safe through `useSyncExternalStore`); on the client it renders
|
|
5
|
+
* the loaded component inside a Suspense boundary with the same `loading` as the fallback.
|
|
6
|
+
*/
|
|
7
|
+
import { type ComponentType } from "react";
|
|
8
|
+
export declare function useIsClient(): boolean;
|
|
9
|
+
export declare function clientLazy<P extends object>(load: () => Promise<{
|
|
10
|
+
default: ComponentType<P>;
|
|
11
|
+
}>, options?: {
|
|
12
|
+
loading?: ComponentType;
|
|
13
|
+
}): ComponentType<P>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createElement, lazy, Suspense, useSyncExternalStore } from "react";
|
|
3
|
+
const subscribe = () => () => {
|
|
4
|
+
};
|
|
5
|
+
const onClient = () => true;
|
|
6
|
+
const onServer = () => false;
|
|
7
|
+
function useIsClient() {
|
|
8
|
+
return useSyncExternalStore(subscribe, onClient, onServer);
|
|
9
|
+
}
|
|
10
|
+
function clientLazy(load, options = {}) {
|
|
11
|
+
const Lazy = lazy(load);
|
|
12
|
+
const Loading = options.loading;
|
|
13
|
+
function ClientLazy(props) {
|
|
14
|
+
const client = useIsClient();
|
|
15
|
+
const fallback = Loading ? createElement(Loading) : null;
|
|
16
|
+
if (!client) return fallback;
|
|
17
|
+
return createElement(Suspense, { fallback }, createElement(Lazy, props));
|
|
18
|
+
}
|
|
19
|
+
return ClientLazy;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
clientLazy,
|
|
23
|
+
useIsClient
|
|
24
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE place a `<decision>` body becomes options. Used by the splitter
|
|
3
|
+
* (content-splitter-core), the stream accumulator, and parseDecisionXml — the
|
|
4
|
+
* server twin is aidream matrx_ai/processing/blocks/parsers/decision_parser.py
|
|
5
|
+
* and MUST stay in lockstep with this file.
|
|
6
|
+
*
|
|
7
|
+
* Tolerant of real model output (captured 2026-10-04): extra attributes on
|
|
8
|
+
* <option> (`id` before/after `label`), single or double quotes, indentation
|
|
9
|
+
* and blank lines, and self-describing options with no `label` attribute
|
|
10
|
+
* (`<option id="x">Label text</option>` — the text names the choice).
|
|
11
|
+
* The choice id is the model's `id` attribute when present (unique), else
|
|
12
|
+
* `opt-N`. Only options whose closing tag has arrived count.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParsedDecisionOption {
|
|
15
|
+
/** The model's own `id` attribute when present and unique, else `opt-N`. */
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function parseOptionAttributes(attrString: string): Record<string, string>;
|
|
21
|
+
export declare function parseDecisionOptionsFromBody(body: string): ParsedDecisionOption[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const OPTION_RE = /<option\b([^>]*)>([\s\S]*?)<\/option\s*>/gi;
|
|
2
|
+
const ATTR_RE = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
3
|
+
function parseOptionAttributes(attrString) {
|
|
4
|
+
const attrs = {};
|
|
5
|
+
ATTR_RE.lastIndex = 0;
|
|
6
|
+
let m;
|
|
7
|
+
while ((m = ATTR_RE.exec(attrString)) !== null) {
|
|
8
|
+
attrs[m[1]] = m[2] ?? m[3] ?? "";
|
|
9
|
+
}
|
|
10
|
+
return attrs;
|
|
11
|
+
}
|
|
12
|
+
function parseDecisionOptionsFromBody(body) {
|
|
13
|
+
const options = [];
|
|
14
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15
|
+
const re = new RegExp(OPTION_RE.source, OPTION_RE.flags);
|
|
16
|
+
let m;
|
|
17
|
+
while ((m = re.exec(body)) !== null) {
|
|
18
|
+
const attrs = parseOptionAttributes(m[1]);
|
|
19
|
+
const text = m[2].trim();
|
|
20
|
+
const label = (attrs.label ?? attrs.title ?? attrs.name ?? "").trim() || text.split("\n")[0].trim();
|
|
21
|
+
if (label === "") continue;
|
|
22
|
+
const own = (attrs.id ?? "").trim();
|
|
23
|
+
const id = own !== "" && !seen.has(own) ? own : `opt-${options.length}`;
|
|
24
|
+
seen.add(id);
|
|
25
|
+
options.push({ id, label, text });
|
|
26
|
+
}
|
|
27
|
+
return options;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
parseDecisionOptionsFromBody,
|
|
31
|
+
parseOptionAttributes
|
|
32
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Token, type Tokens } from "marked";
|
|
2
|
+
export { Lexer, Tokenizer } from "marked";
|
|
3
|
+
export type { Token, Tokens };
|
|
4
|
+
/** GFM block tokens of `text` (each with its exact `raw`), blank-line tokens dropped. */
|
|
5
|
+
export declare function lexGfmBlocks(text: string): Token[];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON value types + narrowing guards.
|
|
3
|
+
*
|
|
4
|
+
* ─── Why this exists ───────────────────────────────────────────────────────
|
|
5
|
+
* This repo patches the Supabase-generated `Json` alias to `unknown`
|
|
6
|
+
* (scripts/patch-db-types.sh) so that typed objects are freely assignable INTO
|
|
7
|
+
* JSONB columns on the write side. The cost lands on the READ side: a JSONB
|
|
8
|
+
* column comes back as bare `unknown`, and to do anything with it you must
|
|
9
|
+
* narrow it.
|
|
10
|
+
*
|
|
11
|
+
* The lazy/cheating reaction (the #1 source of `as unknown as` in the codebase)
|
|
12
|
+
* is to cast the WHOLE row — `row as unknown as MyTypedRow` — which re-asserts
|
|
13
|
+
* every already-correctly-typed column just to reach the one `unknown` JSON
|
|
14
|
+
* field. That silently discards real type information and is exactly the
|
|
15
|
+
* "type cheating" this system exists to kill.
|
|
16
|
+
*
|
|
17
|
+
* The honest move is to narrow ONLY the JSON field, and to have a real name for
|
|
18
|
+
* "this is just a JSON object, accept it" that is NOT `any` and NOT a bare
|
|
19
|
+
* `unknown`:
|
|
20
|
+
*
|
|
21
|
+
* import { type JsonObject, isJsonObject } from "@/types/json";
|
|
22
|
+
*
|
|
23
|
+
* // 1. "It's just an object" — type the ONE field, leave the row typed:
|
|
24
|
+
* interface MyRow { id: string; name: string; config: JsonObject }
|
|
25
|
+
*
|
|
26
|
+
* // 2. Narrowing a bare `unknown` DB field honestly (runtime-checked):
|
|
27
|
+
* const cfg = isJsonObject(row.config) ? row.config : undefined;
|
|
28
|
+
* // cfg is JsonObject | undefined — no cast, no `any`, no whole-row nuke.
|
|
29
|
+
*
|
|
30
|
+
* // 3. Reading a leaf — JsonObject values are JsonValue, narrow as needed:
|
|
31
|
+
* const label = typeof cfg?.label === "string" ? cfg.label : null;
|
|
32
|
+
*
|
|
33
|
+
* For a concrete, known shape (an API/DB frame), prefer a Zod parse at the
|
|
34
|
+
* boundary (see TYPESCRIPT_STANDARDS.md §4). These types are for the genuinely
|
|
35
|
+
* open / loosely-shaped JSON the standards doc calls "just an object" — they
|
|
36
|
+
* give it an honest name so nobody reaches for `any` or a whole-row cast.
|
|
37
|
+
*/
|
|
38
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
39
|
+
/**
|
|
40
|
+
* A JSON object. Values are `JsonValue | undefined` so optional keys read
|
|
41
|
+
* cleanly (mirrors Supabase's original generated `Json` object member).
|
|
42
|
+
*/
|
|
43
|
+
export interface JsonObject {
|
|
44
|
+
[key: string]: JsonValue | undefined;
|
|
45
|
+
}
|
|
46
|
+
export type JsonArray = JsonValue[];
|
|
47
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
48
|
+
/**
|
|
49
|
+
* Narrow an `unknown` (e.g. a bare JSONB column) to a `JsonObject`.
|
|
50
|
+
* Plain object only — arrays and `null` return false.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isJsonObject(value: unknown): value is JsonObject;
|
|
53
|
+
/**
|
|
54
|
+
* Convert a `JsonObject` (values `JsonValue | undefined`) into the strict
|
|
55
|
+
* `Record<string, JsonValue>` shape generated API request bodies demand,
|
|
56
|
+
* dropping `undefined` entries (they don't exist in real JSON — `undefined`
|
|
57
|
+
* only appears via optional-key reads). Cast-free by construction.
|
|
58
|
+
*/
|
|
59
|
+
export declare function toJsonRecord(obj: JsonObject): Record<string, JsonValue>;
|
|
60
|
+
/** Narrow an `unknown` to a `JsonArray`. */
|
|
61
|
+
export declare function isJsonArray(value: unknown): value is JsonArray;
|
|
62
|
+
/** Narrow an `unknown` to a JSON primitive (string | number | boolean | null). */
|
|
63
|
+
export declare function isJsonPrimitive(value: unknown): value is JsonPrimitive;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function isJsonObject(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function toJsonRecord(obj) {
|
|
5
|
+
const out = {};
|
|
6
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
7
|
+
if (value !== void 0) out[key] = value;
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
function isJsonArray(value) {
|
|
12
|
+
return Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
function isJsonPrimitive(value) {
|
|
15
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
isJsonArray,
|
|
19
|
+
isJsonObject,
|
|
20
|
+
isJsonPrimitive,
|
|
21
|
+
toJsonRecord
|
|
22
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Strip markdown syntax from ONE line, keeping its readable text. */
|
|
2
|
+
export declare function plainTextOfMarkdownLine(line: string): string;
|
|
3
|
+
export interface PlainTitleOptions {
|
|
4
|
+
/** Longest title; longer titles are cut at a word boundary with "…". */
|
|
5
|
+
maxLength?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The plain-text title of markdown content: its first heading or line with
|
|
9
|
+
* the syntax removed. Returns "" when the content has no nameable line.
|
|
10
|
+
*/
|
|
11
|
+
export declare function plainTitleFromMarkdown(source: string | null | undefined, { maxLength }?: PlainTitleOptions): string;
|
|
12
|
+
/**
|
|
13
|
+
* Display form of a STORED title that may still carry syntax (rows written
|
|
14
|
+
* before the projection existed). Clean titles come back unchanged; a title
|
|
15
|
+
* that projects to nothing keeps its original text rather than going blank.
|
|
16
|
+
*/
|
|
17
|
+
export declare function displayTitle(title: string | null | undefined): string;
|
|
18
|
+
/**
|
|
19
|
+
* A read-boundary copy of `row` whose `key` title is in display form.
|
|
20
|
+
* Services that return rows whose title may have been derived from markdown
|
|
21
|
+
* (flashcard sets, assessments, study media) pass them through this, so every
|
|
22
|
+
* list, header, picker and export shows the clean name. Storage is untouched;
|
|
23
|
+
* a person who edits and saves the name saves what they see.
|
|
24
|
+
*/
|
|
25
|
+
export declare function withDisplayTitle<T, K extends keyof T>(row: T, key: K): T;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fenceLineKinds } from "@ai-matrx/content-ir/source";
|
|
2
|
+
import { hasJsonKindKey } from "@ai-matrx/content-ir/surfaces";
|
|
3
|
+
import { kindTextLabel } from "@ai-matrx/content-ir/surfaces";
|
|
4
|
+
function firstContentLine(source) {
|
|
5
|
+
const lines = source.split(/\r?\n/);
|
|
6
|
+
let i = 0;
|
|
7
|
+
if (lines[0]?.trim() === "---") {
|
|
8
|
+
const end = lines.findIndex((l, k) => k > 0 && /^(---|\.\.\.)\s*$/.test(l));
|
|
9
|
+
if (end > 0) i = end + 1;
|
|
10
|
+
}
|
|
11
|
+
const kinds = fenceLineKinds(source);
|
|
12
|
+
for (; i < lines.length; i += 1) {
|
|
13
|
+
const line = lines[i];
|
|
14
|
+
if (kinds[i] !== "prose") continue;
|
|
15
|
+
if (/^\s*([-=*_])(\s*\1){2,}\s*$/.test(line)) continue;
|
|
16
|
+
if (line.trim()) return line;
|
|
17
|
+
}
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
function plainTextOfMarkdownLine(line) {
|
|
21
|
+
let text = line.replace(/^\s{0,3}#{1,6}(\s+|$)/, "").replace(/^\s*(>\s?)+/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/^\s*\[[ xX]\]\s+/, "").replace(/\s+#+\s*$/, "");
|
|
22
|
+
text = text.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\[[^\]]*\]/g, "$1").replace(/<(https?:\/\/[^>\s]+)>/g, "$1").replace(/<\/?[A-Za-z][\w:-]*(\s[^<>]*)?\/?>/g, "").replace(/(`+)([\s\S]*?)\1/g, "$2").replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "$2").replace(/\*(?=\S)([^*]*?\S)\*/g, "$1").replace(/(^|\W)_(?=\S)([^_]*?\S)_(?=\W|$)/g, "$1$2").replace(/~~(?=\S)([\s\S]*?\S)~~/g, "$1").replace(/\\([\\`*_{}#+\-.!>~|])/g, "$1");
|
|
23
|
+
return text.replace(/\s+/g, " ").trim();
|
|
24
|
+
}
|
|
25
|
+
function plainTitleFromMarkdown(source, { maxLength } = {}) {
|
|
26
|
+
const text = source ?? "";
|
|
27
|
+
const holdsKind = hasJsonKindKey(text);
|
|
28
|
+
const title = plainTextOfMarkdownLine(holdsKind ? kindTextLabel(text, 200) : firstContentLine(text)) || // A kind always names its record (an `<artifact>`-wrapped kind used to
|
|
29
|
+
// project to "") — R4, round 6.
|
|
30
|
+
(holdsKind ? kindTextLabel(text, 200) : "");
|
|
31
|
+
if (!maxLength || title.length <= maxLength) return title;
|
|
32
|
+
const cut = title.slice(0, maxLength - 1);
|
|
33
|
+
const space = cut.lastIndexOf(" ");
|
|
34
|
+
return `${(space > maxLength * 0.6 ? cut.slice(0, space) : cut).trimEnd()}\u2026`;
|
|
35
|
+
}
|
|
36
|
+
function displayTitle(title) {
|
|
37
|
+
const raw = title ?? "";
|
|
38
|
+
return plainTitleFromMarkdown(raw) || raw.trim();
|
|
39
|
+
}
|
|
40
|
+
function withDisplayTitle(row, key) {
|
|
41
|
+
const value = row[key];
|
|
42
|
+
if (typeof value !== "string") return row;
|
|
43
|
+
const clean = displayTitle(value);
|
|
44
|
+
return clean === value ? row : { ...row, [key]: clean };
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
displayTitle,
|
|
48
|
+
plainTextOfMarkdownLine,
|
|
49
|
+
plainTitleFromMarkdown,
|
|
50
|
+
withDisplayTitle
|
|
51
|
+
};
|