@refrakt-md/runes 0.14.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate-resolve.d.ts +4 -0
- package/dist/aggregate-resolve.d.ts.map +1 -0
- package/dist/aggregate-resolve.js +209 -0
- package/dist/aggregate-resolve.js.map +1 -0
- package/dist/collection-helpers.d.ts +101 -0
- package/dist/collection-helpers.d.ts.map +1 -0
- package/dist/collection-helpers.js +206 -0
- package/dist/collection-helpers.js.map +1 -0
- package/dist/collection-resolve.d.ts +5 -0
- package/dist/collection-resolve.d.ts.map +1 -0
- package/dist/collection-resolve.js +209 -0
- package/dist/collection-resolve.js.map +1 -0
- package/dist/config.d.ts +46 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +208 -88
- package/dist/config.js.map +1 -1
- package/dist/deferred-body.d.ts +19 -0
- package/dist/deferred-body.d.ts.map +1 -0
- package/dist/deferred-body.js +70 -0
- package/dist/deferred-body.js.map +1 -0
- package/dist/drawer-pipeline.d.ts +33 -0
- package/dist/drawer-pipeline.d.ts.map +1 -0
- package/dist/drawer-pipeline.js +206 -0
- package/dist/drawer-pipeline.js.map +1 -0
- package/dist/expand-pipeline.d.ts +51 -0
- package/dist/expand-pipeline.d.ts.map +1 -0
- package/dist/expand-pipeline.js +314 -0
- package/dist/expand-pipeline.js.map +1 -0
- package/dist/field-match.d.ts +43 -0
- package/dist/field-match.d.ts.map +1 -0
- package/dist/field-match.js +121 -0
- package/dist/field-match.js.map +1 -0
- package/dist/functions.d.ts +36 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +74 -0
- package/dist/functions.js.map +1 -0
- package/dist/index.d.ts +34 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -4
- package/dist/index.js.map +1 -1
- package/dist/lang-map.d.ts +32 -0
- package/dist/lang-map.d.ts.map +1 -0
- package/dist/lang-map.js +76 -0
- package/dist/lang-map.js.map +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/read-file.d.ts +78 -0
- package/dist/lib/read-file.d.ts.map +1 -0
- package/dist/lib/read-file.js +166 -0
- package/dist/lib/read-file.js.map +1 -0
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +18 -2
- package/dist/nodes.js.map +1 -1
- package/dist/outline-scope.d.ts +46 -0
- package/dist/outline-scope.d.ts.map +1 -0
- package/dist/outline-scope.js +208 -0
- package/dist/outline-scope.js.map +1 -0
- package/dist/plugins.d.ts +13 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +65 -1
- package/dist/plugins.js.map +1 -1
- package/dist/relationships-resolve.d.ts +4 -0
- package/dist/relationships-resolve.d.ts.map +1 -0
- package/dist/relationships-resolve.js +180 -0
- package/dist/relationships-resolve.js.map +1 -0
- package/dist/snippet-pipeline.d.ts +42 -0
- package/dist/snippet-pipeline.d.ts.map +1 -0
- package/dist/snippet-pipeline.js +219 -0
- package/dist/snippet-pipeline.js.map +1 -0
- package/dist/tags/aggregate.d.ts +19 -0
- package/dist/tags/aggregate.d.ts.map +1 -0
- package/dist/tags/aggregate.js +59 -0
- package/dist/tags/aggregate.js.map +1 -0
- package/dist/tags/card.d.ts +25 -0
- package/dist/tags/card.d.ts.map +1 -0
- package/dist/tags/card.js +138 -0
- package/dist/tags/card.js.map +1 -0
- package/dist/tags/collection.d.ts +10 -0
- package/dist/tags/collection.d.ts.map +1 -0
- package/dist/tags/collection.js +54 -0
- package/dist/tags/collection.js.map +1 -0
- package/dist/tags/conversation.d.ts.map +1 -1
- package/dist/tags/conversation.js +26 -6
- package/dist/tags/conversation.js.map +1 -1
- package/dist/tags/drawer.d.ts +26 -0
- package/dist/tags/drawer.d.ts.map +1 -0
- package/dist/tags/drawer.js +134 -0
- package/dist/tags/drawer.js.map +1 -0
- package/dist/tags/expand.d.ts +22 -0
- package/dist/tags/expand.d.ts.map +1 -0
- package/dist/tags/expand.js +76 -0
- package/dist/tags/expand.js.map +1 -0
- package/dist/tags/progress.d.ts +9 -0
- package/dist/tags/progress.d.ts.map +1 -0
- package/dist/tags/progress.js +96 -0
- package/dist/tags/progress.js.map +1 -0
- package/dist/tags/relationships.d.ts +12 -0
- package/dist/tags/relationships.d.ts.map +1 -0
- package/dist/tags/relationships.js +62 -0
- package/dist/tags/relationships.js.map +1 -0
- package/dist/tags/snippet.d.ts +19 -0
- package/dist/tags/snippet.d.ts.map +1 -0
- package/dist/tags/snippet.js +49 -0
- package/dist/tags/snippet.js.map +1 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +35 -0
- package/dist/util.js.map +1 -1
- package/dist/xref-patterns.d.ts +57 -0
- package/dist/xref-patterns.d.ts.map +1 -0
- package/dist/xref-patterns.js +140 -0
- package/dist/xref-patterns.js.map +1 -0
- package/dist/xref-resolve.d.ts +22 -5
- package/dist/xref-resolve.d.ts.map +1 -1
- package/dist/xref-resolve.js +203 -79
- package/dist/xref-resolve.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet pipeline hooks (SPEC-062).
|
|
3
|
+
*
|
|
4
|
+
* - **Preprocess** walks the parsed Markdoc AST, finds every `{% snippet %}`
|
|
5
|
+
* tag, resolves + slices its source file, and replaces the tag with a
|
|
6
|
+
* Markdoc `fence` node. The fence carries `content` and `language` (so
|
|
7
|
+
* the existing code-block transform syntax-highlights it identically to
|
|
8
|
+
* a triple-backtick fence) plus `data-snippet-source` / `data-snippet-lines`
|
|
9
|
+
* attributes for downstream tooling and the wrap step.
|
|
10
|
+
*
|
|
11
|
+
* - **PostProcess** walks the rendered renderable tree, finds every `<pre>`
|
|
12
|
+
* element carrying `data-snippet-source`, and — when not nested under a
|
|
13
|
+
* fence-consuming container (`data-rune="code-group"`, `data-rune="diff"`)
|
|
14
|
+
* — wraps it in `<figure class="rf-snippet">` so themes can style snippet
|
|
15
|
+
* blocks distinctly from regular code blocks (and tooling can find
|
|
16
|
+
* `data-source-path` on the figure).
|
|
17
|
+
*
|
|
18
|
+
* This separation is the SPEC-062 composition story: container runes see
|
|
19
|
+
* snippet output as a regular fence (because preprocess made it one) and
|
|
20
|
+
* consume it transparently; standalone snippets get the figure chrome via
|
|
21
|
+
* the wrap step. Captions / titles are intentionally not provided — wrap
|
|
22
|
+
* a snippet in `{% codegroup title="..." %}` if you want a labelled chrome.
|
|
23
|
+
*/
|
|
24
|
+
import type { Node } from '@markdoc/markdoc';
|
|
25
|
+
import type { PreprocessContext, PreprocessPage, PipelineContext, TransformedPage, AggregatedData } from '@refrakt-md/types';
|
|
26
|
+
/**
|
|
27
|
+
* Preprocess: replace every `{% snippet %}` tag in the AST with a `fence`
|
|
28
|
+
* node carrying the resolved file content. After this runs, no snippet
|
|
29
|
+
* tags remain in the AST — only fences (some "snippet-derived" identified
|
|
30
|
+
* by their `data-snippet-source` attribute).
|
|
31
|
+
*/
|
|
32
|
+
export declare function preprocessSnippets(ast: Node, page: PreprocessPage, ctx: PreprocessContext): Node | void;
|
|
33
|
+
/**
|
|
34
|
+
* PostProcess: wrap standalone snippet-derived `<pre>` elements in the
|
|
35
|
+
* snippet figure chrome. Run as part of `corePipelineHooks.postProcess`.
|
|
36
|
+
*
|
|
37
|
+
* Container-nested snippets (inside `<div data-rune="code-group">` or
|
|
38
|
+
* `<div data-rune="diff">` outputs) are left alone — the container's chrome
|
|
39
|
+
* is already there and the figure would be duplicate.
|
|
40
|
+
*/
|
|
41
|
+
export declare function wrapStandaloneSnippets(page: TransformedPage, _aggregated: AggregatedData, _ctx: PipelineContext): TransformedPage;
|
|
42
|
+
//# sourceMappingURL=snippet-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet-pipeline.d.ts","sourceRoot":"","sources":["../src/snippet-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAiC7H;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,IAAI,EACT,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,iBAAiB,GACpB,IAAI,GAAG,IAAI,CAYb;AAsGD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,eAAe,EACrB,WAAW,EAAE,cAAc,EAC3B,IAAI,EAAE,eAAe,GACnB,eAAe,CAIjB"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet pipeline hooks (SPEC-062).
|
|
3
|
+
*
|
|
4
|
+
* - **Preprocess** walks the parsed Markdoc AST, finds every `{% snippet %}`
|
|
5
|
+
* tag, resolves + slices its source file, and replaces the tag with a
|
|
6
|
+
* Markdoc `fence` node. The fence carries `content` and `language` (so
|
|
7
|
+
* the existing code-block transform syntax-highlights it identically to
|
|
8
|
+
* a triple-backtick fence) plus `data-snippet-source` / `data-snippet-lines`
|
|
9
|
+
* attributes for downstream tooling and the wrap step.
|
|
10
|
+
*
|
|
11
|
+
* - **PostProcess** walks the rendered renderable tree, finds every `<pre>`
|
|
12
|
+
* element carrying `data-snippet-source`, and — when not nested under a
|
|
13
|
+
* fence-consuming container (`data-rune="code-group"`, `data-rune="diff"`)
|
|
14
|
+
* — wraps it in `<figure class="rf-snippet">` so themes can style snippet
|
|
15
|
+
* blocks distinctly from regular code blocks (and tooling can find
|
|
16
|
+
* `data-source-path` on the figure).
|
|
17
|
+
*
|
|
18
|
+
* This separation is the SPEC-062 composition story: container runes see
|
|
19
|
+
* snippet output as a regular fence (because preprocess made it one) and
|
|
20
|
+
* consume it transparently; standalone snippets get the figure chrome via
|
|
21
|
+
* the wrap step. Captions / titles are intentionally not provided — wrap
|
|
22
|
+
* a snippet in `{% codegroup title="..." %}` if you want a labelled chrome.
|
|
23
|
+
*/
|
|
24
|
+
import Markdoc from '@markdoc/markdoc';
|
|
25
|
+
import { readSnippetFile, SnippetSandboxError } from './lib/read-file.js';
|
|
26
|
+
import { inferLanguage } from './lang-map.js';
|
|
27
|
+
const { Ast, Tag } = Markdoc;
|
|
28
|
+
/** Resolve a Markdoc attribute value to a string. Handles literal strings
|
|
29
|
+
* and Markdoc `Variable` AST nodes (e.g. `path=$file.path` parses as a
|
|
30
|
+
* Variable, not a string). Unresolvable references (variable missing from
|
|
31
|
+
* the context, or attribute is some other AST shape) return an empty
|
|
32
|
+
* string — matching transform-time variable-evaluation behaviour. */
|
|
33
|
+
function resolveAttributeValue(value, variables) {
|
|
34
|
+
if (value === undefined || value === null)
|
|
35
|
+
return '';
|
|
36
|
+
if (typeof value === 'string')
|
|
37
|
+
return value;
|
|
38
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
39
|
+
return String(value);
|
|
40
|
+
if (typeof value === 'object' && '$$mdtype' in value) {
|
|
41
|
+
const node = value;
|
|
42
|
+
if (node.$$mdtype === 'Variable' && Array.isArray(node.path)) {
|
|
43
|
+
let current = variables;
|
|
44
|
+
for (const segment of node.path) {
|
|
45
|
+
if (current === null || current === undefined)
|
|
46
|
+
return '';
|
|
47
|
+
current = current[segment];
|
|
48
|
+
}
|
|
49
|
+
return current === null || current === undefined ? '' : String(current);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
/** Containers whose output consumes their fence children — wrapping a
|
|
55
|
+
* snippet-derived `<pre>` inside them would be duplicate chrome. */
|
|
56
|
+
const FENCE_CONSUMING_CONTAINERS = new Set(['code-group', 'diff']);
|
|
57
|
+
/**
|
|
58
|
+
* Preprocess: replace every `{% snippet %}` tag in the AST with a `fence`
|
|
59
|
+
* node carrying the resolved file content. After this runs, no snippet
|
|
60
|
+
* tags remain in the AST — only fences (some "snippet-derived" identified
|
|
61
|
+
* by their `data-snippet-source` attribute).
|
|
62
|
+
*/
|
|
63
|
+
export function preprocessSnippets(ast, page, ctx) {
|
|
64
|
+
if (!ctx.projectRoot) {
|
|
65
|
+
// No project root configured — silently no-op. Snippet refs will fall
|
|
66
|
+
// through to the schema's transform, which throws a clear error.
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let mutated = false;
|
|
70
|
+
walkAndReplaceSnippets(ast, page, ctx, ctx.projectRoot, (didReplace) => {
|
|
71
|
+
if (didReplace)
|
|
72
|
+
mutated = true;
|
|
73
|
+
});
|
|
74
|
+
return mutated ? ast : undefined;
|
|
75
|
+
}
|
|
76
|
+
function walkAndReplaceSnippets(node, page, ctx, projectRoot, onReplaced) {
|
|
77
|
+
if (!node.children)
|
|
78
|
+
return;
|
|
79
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
80
|
+
const child = node.children[i];
|
|
81
|
+
if (child.type === 'tag' && child.tag === 'snippet') {
|
|
82
|
+
// Always produce a fence — successful resolution returns the
|
|
83
|
+
// real fence; sandbox / missing-file / variable-resolution errors
|
|
84
|
+
// produce an error fence so the snippet tag never reaches the
|
|
85
|
+
// schema's transform (which throws). The build keeps going and
|
|
86
|
+
// the failure is visible on the rendered page.
|
|
87
|
+
node.children[i] = resolveSnippetToFence(child, page, ctx, projectRoot);
|
|
88
|
+
onReplaced(true);
|
|
89
|
+
// Don't recurse — replaced.
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
walkAndReplaceSnippets(child, page, ctx, projectRoot, onReplaced);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Build a `fence` AST node that renders a clear error message in place of
|
|
96
|
+
* the snippet. Used when resolution fails (sandbox, missing file, malformed
|
|
97
|
+
* lines, unresolvable variable reference). The fence carries the original
|
|
98
|
+
* attempted path in `data-snippet-source` so tooling can still detect
|
|
99
|
+
* snippet provenance. */
|
|
100
|
+
function makeErrorFence(pathAttr, message) {
|
|
101
|
+
return new Ast.Node('fence', {
|
|
102
|
+
content: `snippet error: ${message}\n`,
|
|
103
|
+
language: 'text',
|
|
104
|
+
'data-snippet-source': pathAttr || '(unresolved)',
|
|
105
|
+
'data-snippet-error': message,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function resolveSnippetToFence(tag, page, ctx, projectRoot) {
|
|
109
|
+
const pathAttr = resolveAttributeValue(tag.attributes.path, ctx.variables);
|
|
110
|
+
const lines = tag.attributes.lines !== undefined
|
|
111
|
+
? resolveAttributeValue(tag.attributes.lines, ctx.variables)
|
|
112
|
+
: undefined;
|
|
113
|
+
const langAttr = tag.attributes.lang !== undefined
|
|
114
|
+
? resolveAttributeValue(tag.attributes.lang, ctx.variables)
|
|
115
|
+
: undefined;
|
|
116
|
+
if (!pathAttr) {
|
|
117
|
+
const msg = 'snippet `path` attribute is required (and an unresolvable variable reference resolves to empty)';
|
|
118
|
+
ctx.error(msg, page.url);
|
|
119
|
+
return makeErrorFence('', msg);
|
|
120
|
+
}
|
|
121
|
+
let result;
|
|
122
|
+
try {
|
|
123
|
+
result = readSnippetFile({
|
|
124
|
+
pathAttr,
|
|
125
|
+
projectRoot,
|
|
126
|
+
lines: lines || undefined,
|
|
127
|
+
referencingPage: page.relativePath,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (err instanceof SnippetSandboxError) {
|
|
132
|
+
ctx.error(err.message, page.url);
|
|
133
|
+
return makeErrorFence(pathAttr, err.message);
|
|
134
|
+
}
|
|
135
|
+
// Unexpected error type — still produce an error fence so the build
|
|
136
|
+
// doesn't crash on a single page.
|
|
137
|
+
const msg = err.message ?? String(err);
|
|
138
|
+
ctx.error(`snippet "${pathAttr}" failed unexpectedly: ${msg}`, page.url);
|
|
139
|
+
return makeErrorFence(pathAttr, msg);
|
|
140
|
+
}
|
|
141
|
+
for (const warning of result.warnings) {
|
|
142
|
+
ctx.warn(warning, page.url);
|
|
143
|
+
}
|
|
144
|
+
const language = (langAttr && langAttr.length > 0) ? langAttr : inferLanguage(result.relativePath);
|
|
145
|
+
const fenceAttrs = {
|
|
146
|
+
content: result.content,
|
|
147
|
+
language,
|
|
148
|
+
'data-snippet-source': result.relativePath,
|
|
149
|
+
};
|
|
150
|
+
if (lines)
|
|
151
|
+
fenceAttrs['data-snippet-lines'] = lines;
|
|
152
|
+
// Construct a fence Ast.Node. Markdoc parses ``` blocks as
|
|
153
|
+
// new Ast.Node('fence', { content, language }) — same shape here.
|
|
154
|
+
return new Ast.Node('fence', fenceAttrs);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* PostProcess: wrap standalone snippet-derived `<pre>` elements in the
|
|
158
|
+
* snippet figure chrome. Run as part of `corePipelineHooks.postProcess`.
|
|
159
|
+
*
|
|
160
|
+
* Container-nested snippets (inside `<div data-rune="code-group">` or
|
|
161
|
+
* `<div data-rune="diff">` outputs) are left alone — the container's chrome
|
|
162
|
+
* is already there and the figure would be duplicate.
|
|
163
|
+
*/
|
|
164
|
+
export function wrapStandaloneSnippets(page, _aggregated, _ctx) {
|
|
165
|
+
const wrapped = walkAndWrap(page.renderable, /* containerAncestors */ false);
|
|
166
|
+
if (wrapped === page.renderable)
|
|
167
|
+
return page;
|
|
168
|
+
return { ...page, renderable: wrapped };
|
|
169
|
+
}
|
|
170
|
+
function walkAndWrap(node, insideFenceContainer) {
|
|
171
|
+
if (Array.isArray(node)) {
|
|
172
|
+
let mutated = false;
|
|
173
|
+
const next = node.map((c) => {
|
|
174
|
+
const w = walkAndWrap(c, insideFenceContainer);
|
|
175
|
+
if (w !== c)
|
|
176
|
+
mutated = true;
|
|
177
|
+
return w;
|
|
178
|
+
});
|
|
179
|
+
return mutated ? next : node;
|
|
180
|
+
}
|
|
181
|
+
if (!Tag.isTag(node))
|
|
182
|
+
return node;
|
|
183
|
+
const tag = node;
|
|
184
|
+
const dataRune = tag.attributes?.['data-rune'];
|
|
185
|
+
const enteredFenceContainer = typeof dataRune === 'string' && FENCE_CONSUMING_CONTAINERS.has(dataRune);
|
|
186
|
+
const childAncestor = insideFenceContainer || enteredFenceContainer;
|
|
187
|
+
// Wrap matching <pre> elements that aren't inside a fence-consuming container.
|
|
188
|
+
if (tag.name === 'pre' &&
|
|
189
|
+
tag.attributes?.['data-snippet-source'] !== undefined &&
|
|
190
|
+
!insideFenceContainer) {
|
|
191
|
+
return wrapPreInFigure(tag);
|
|
192
|
+
}
|
|
193
|
+
if (!tag.children || tag.children.length === 0)
|
|
194
|
+
return tag;
|
|
195
|
+
let mutated = false;
|
|
196
|
+
const newChildren = tag.children.map((c) => {
|
|
197
|
+
const w = walkAndWrap(c, childAncestor);
|
|
198
|
+
if (w !== c)
|
|
199
|
+
mutated = true;
|
|
200
|
+
return w;
|
|
201
|
+
});
|
|
202
|
+
if (!mutated)
|
|
203
|
+
return tag;
|
|
204
|
+
return new Tag(tag.name, tag.attributes, newChildren);
|
|
205
|
+
}
|
|
206
|
+
function wrapPreInFigure(preTag) {
|
|
207
|
+
const attrs = preTag.attributes;
|
|
208
|
+
const source = String(attrs['data-snippet-source'] ?? '');
|
|
209
|
+
const linesAttr = attrs['data-snippet-lines'] !== undefined ? String(attrs['data-snippet-lines']) : undefined;
|
|
210
|
+
const figureAttrs = {
|
|
211
|
+
class: 'rf-snippet',
|
|
212
|
+
'data-rune': 'snippet',
|
|
213
|
+
'data-source-path': source,
|
|
214
|
+
};
|
|
215
|
+
if (linesAttr)
|
|
216
|
+
figureAttrs['data-lines'] = linesAttr;
|
|
217
|
+
return new Tag('figure', figureAttrs, [preTag]);
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=snippet-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet-pipeline.js","sourceRoot":"","sources":["../src/snippet-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Markdoc from '@markdoc/markdoc';
|
|
2
|
+
export declare const AGGREGATE_SENTINEL = "__aggregate-sentinel";
|
|
3
|
+
/**
|
|
4
|
+
* `aggregate` (SPEC-076) — the third post-process-resolved query rune, beside
|
|
5
|
+
* `collection` and `relationships`. Where they project entities and edges, this
|
|
6
|
+
* one projects **numbers** — counts over the same field-match query, optionally
|
|
7
|
+
* broken down by group. Two modes from the same rune:
|
|
8
|
+
*
|
|
9
|
+
* - **No-body**: emits a single inline integer (the count). Replaces the
|
|
10
|
+
* standalone `count` rune proposal.
|
|
11
|
+
* - **Body-zoned**: source splits on top-level `hr` into preamble / template /
|
|
12
|
+
* fallback. `$item` is bound differently per zone — preamble gets totals,
|
|
13
|
+
* template gets the per-group projection, fallback gets zeros.
|
|
14
|
+
*
|
|
15
|
+
* The body is captured via `deferBody` and reparsed per group in the resolver
|
|
16
|
+
* (the same path collection's per-item template uses).
|
|
17
|
+
*/
|
|
18
|
+
export declare const aggregate: Markdoc.Schema;
|
|
19
|
+
//# sourceMappingURL=aggregate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../src/tags/aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AAKvC,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,gBAsCpB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Markdoc from '@markdoc/markdoc';
|
|
2
|
+
const { Tag } = Markdoc;
|
|
3
|
+
import { createContentModelSchema, createComponentRenderable } from '../lib/index.js';
|
|
4
|
+
import { readDeferredBody } from '../deferred-body.js';
|
|
5
|
+
export const AGGREGATE_SENTINEL = '__aggregate-sentinel';
|
|
6
|
+
/**
|
|
7
|
+
* `aggregate` (SPEC-076) — the third post-process-resolved query rune, beside
|
|
8
|
+
* `collection` and `relationships`. Where they project entities and edges, this
|
|
9
|
+
* one projects **numbers** — counts over the same field-match query, optionally
|
|
10
|
+
* broken down by group. Two modes from the same rune:
|
|
11
|
+
*
|
|
12
|
+
* - **No-body**: emits a single inline integer (the count). Replaces the
|
|
13
|
+
* standalone `count` rune proposal.
|
|
14
|
+
* - **Body-zoned**: source splits on top-level `hr` into preamble / template /
|
|
15
|
+
* fallback. `$item` is bound differently per zone — preamble gets totals,
|
|
16
|
+
* template gets the per-group projection, fallback gets zeros.
|
|
17
|
+
*
|
|
18
|
+
* The body is captured via `deferBody` and reparsed per group in the resolver
|
|
19
|
+
* (the same path collection's per-item template uses).
|
|
20
|
+
*/
|
|
21
|
+
export const aggregate = createContentModelSchema({
|
|
22
|
+
attributes: {
|
|
23
|
+
type: { type: String, required: false, default: '', description: 'Entity type(s), comma-separated.' },
|
|
24
|
+
filter: { type: String, required: false, default: '', description: 'field:value clauses defining the primary set being measured (SPEC-070 grammar).' },
|
|
25
|
+
value: { type: String, required: false, default: '', description: 'Optional secondary field:value clause defining the achieved subset within `filter` (e.g. value="status:done"). When set, $item.value is the count matching both; $item.percent is the ratio. Without it, the rune is a pure count + breakdown — no progress-bar semantics.' },
|
|
26
|
+
group: { type: String, required: false, default: '', description: 'Group-by field; omit to render the body once with totals.' },
|
|
27
|
+
sort: { type: String, required: false, default: '', description: 'Sort groups by `key`, `count`, `value`, or `percent` (prefix `-` for descending). Honors SPEC-072 domain-aware ordering when the group field has one.' },
|
|
28
|
+
limit: { type: String, required: false, default: '', description: 'Cap the number of groups (after sort).' },
|
|
29
|
+
empty: { type: String, required: false, default: '', description: 'Fallback text shown when the query matches no entities (self-closing form; body form uses a fallback zone). Absent → render nothing.' },
|
|
30
|
+
},
|
|
31
|
+
deferBody: true,
|
|
32
|
+
contentModel: { type: 'sequence', fields: [] },
|
|
33
|
+
transform(_resolved, attrs) {
|
|
34
|
+
const meta = (field, content) => new Tag('meta', { 'data-field': field, content });
|
|
35
|
+
const bodySource = readDeferredBody(attrs) ?? '';
|
|
36
|
+
const hasBody = bodySource.length > 0;
|
|
37
|
+
const metas = [
|
|
38
|
+
meta('aggregate-type', String(attrs.type ?? '')),
|
|
39
|
+
meta('aggregate-filter', String(attrs.filter ?? '')),
|
|
40
|
+
meta('aggregate-value', String(attrs.value ?? '')),
|
|
41
|
+
meta('aggregate-group', String(attrs.group ?? '')),
|
|
42
|
+
meta('aggregate-sort', String(attrs.sort ?? '')),
|
|
43
|
+
meta('aggregate-limit', String(attrs.limit ?? '')),
|
|
44
|
+
meta('aggregate-empty', String(attrs.empty ?? '')),
|
|
45
|
+
meta(AGGREGATE_SENTINEL, 'true'),
|
|
46
|
+
];
|
|
47
|
+
if (bodySource)
|
|
48
|
+
metas.push(meta('aggregate-body', bodySource));
|
|
49
|
+
// Self-closing form must be inline-safe (no <section> mid-prose), so the
|
|
50
|
+
// outer tag is a span when there's no body. Body form is a <section>.
|
|
51
|
+
return createComponentRenderable({
|
|
52
|
+
rune: 'aggregate',
|
|
53
|
+
tag: hasBody ? 'section' : 'span',
|
|
54
|
+
properties: {},
|
|
55
|
+
children: metas,
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=aggregate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/tags/aggregate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Markdoc from '@markdoc/markdoc';
|
|
2
|
+
/**
|
|
3
|
+
* `card` (SPEC-070) — a generic, self-contained content card.
|
|
4
|
+
*
|
|
5
|
+
* The body is divided by `---` (hr) into up to three zones:
|
|
6
|
+
*
|
|
7
|
+
* [media] --- body [--- footer]
|
|
8
|
+
*
|
|
9
|
+
* - **media** (optional leading zone): any content — an image, a `{% codegroup %}`,
|
|
10
|
+
* a `{% sandbox %}`, etc. Reuses the shared split layout (`SplitLayoutModel`):
|
|
11
|
+
* side-by-side with the body on wide screens, a full-bleed header on mobile
|
|
12
|
+
* (`data-media-position="top"`).
|
|
13
|
+
* - **body**: the main content.
|
|
14
|
+
* - **footer** (optional trailing zone): a muted meta row (date · tags · …).
|
|
15
|
+
*
|
|
16
|
+
* Zone roles are positional by count: 1 → body; 2 → media + body; 3 → media +
|
|
17
|
+
* body + footer. A leading empty zone (`--- body --- footer`) means "no media".
|
|
18
|
+
*
|
|
19
|
+
* `href` makes the whole card a link (a stretched link overlay, so real links
|
|
20
|
+
* inside body/footer stay clickable). The card carries no `$item`/registry
|
|
21
|
+
* knowledge — it's plain and composable, standalone or fed by a collection
|
|
22
|
+
* body template.
|
|
23
|
+
*/
|
|
24
|
+
export declare const card: Markdoc.Schema;
|
|
25
|
+
//# sourceMappingURL=card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../src/tags/card.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AAOvC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,IAAI,gBAwHf,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import Markdoc from '@markdoc/markdoc';
|
|
2
|
+
const { Tag } = Markdoc;
|
|
3
|
+
import { createContentModelSchema, createComponentRenderable, asNodes } from '../lib/index.js';
|
|
4
|
+
import { RenderableNodeCursor } from '../lib/renderable.js';
|
|
5
|
+
import { SplitLayoutModel, buildLayoutMetas, extractMediaImage } from './common.js';
|
|
6
|
+
/**
|
|
7
|
+
* `card` (SPEC-070) — a generic, self-contained content card.
|
|
8
|
+
*
|
|
9
|
+
* The body is divided by `---` (hr) into up to three zones:
|
|
10
|
+
*
|
|
11
|
+
* [media] --- body [--- footer]
|
|
12
|
+
*
|
|
13
|
+
* - **media** (optional leading zone): any content — an image, a `{% codegroup %}`,
|
|
14
|
+
* a `{% sandbox %}`, etc. Reuses the shared split layout (`SplitLayoutModel`):
|
|
15
|
+
* side-by-side with the body on wide screens, a full-bleed header on mobile
|
|
16
|
+
* (`data-media-position="top"`).
|
|
17
|
+
* - **body**: the main content.
|
|
18
|
+
* - **footer** (optional trailing zone): a muted meta row (date · tags · …).
|
|
19
|
+
*
|
|
20
|
+
* Zone roles are positional by count: 1 → body; 2 → media + body; 3 → media +
|
|
21
|
+
* body + footer. A leading empty zone (`--- body --- footer`) means "no media".
|
|
22
|
+
*
|
|
23
|
+
* `href` makes the whole card a link (a stretched link overlay, so real links
|
|
24
|
+
* inside body/footer stay clickable). The card carries no `$item`/registry
|
|
25
|
+
* knowledge — it's plain and composable, standalone or fed by a collection
|
|
26
|
+
* body template.
|
|
27
|
+
*/
|
|
28
|
+
export const card = createContentModelSchema({
|
|
29
|
+
base: SplitLayoutModel,
|
|
30
|
+
attributes: {
|
|
31
|
+
href: { type: String, required: false, default: '', description: 'Optional link target; makes the whole card clickable.' },
|
|
32
|
+
},
|
|
33
|
+
contentModel: {
|
|
34
|
+
type: 'sequence',
|
|
35
|
+
fields: [{ name: 'body', match: 'any', optional: true, greedy: true }],
|
|
36
|
+
},
|
|
37
|
+
transform(resolved, attrs, config) {
|
|
38
|
+
// Split the body nodes on `hr` (`---`) into zones.
|
|
39
|
+
const nodes = asNodes(resolved.body);
|
|
40
|
+
const zones = [[]];
|
|
41
|
+
for (const n of nodes) {
|
|
42
|
+
if (n.type === 'hr')
|
|
43
|
+
zones.push([]);
|
|
44
|
+
else
|
|
45
|
+
zones[zones.length - 1].push(n);
|
|
46
|
+
}
|
|
47
|
+
let mediaNodes = [];
|
|
48
|
+
let bodyNodes = [];
|
|
49
|
+
let footerNodes = [];
|
|
50
|
+
if (zones.length === 1) {
|
|
51
|
+
bodyNodes = zones[0];
|
|
52
|
+
}
|
|
53
|
+
else if (zones.length === 2) {
|
|
54
|
+
mediaNodes = zones[0];
|
|
55
|
+
bodyNodes = zones[1];
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
mediaNodes = zones[0];
|
|
59
|
+
footerNodes = zones[zones.length - 1];
|
|
60
|
+
bodyNodes = zones.slice(1, -1).flat();
|
|
61
|
+
}
|
|
62
|
+
const { metas: layoutMetas, children: layoutChildren } = buildLayoutMetas(attrs);
|
|
63
|
+
const children = [...layoutChildren];
|
|
64
|
+
// Media zone — unwrap a bare image; otherwise render arbitrary content.
|
|
65
|
+
if (mediaNodes.length > 0) {
|
|
66
|
+
const mediaCursor = new RenderableNodeCursor(Markdoc.transform(mediaNodes, config));
|
|
67
|
+
const img = extractMediaImage(mediaCursor);
|
|
68
|
+
const inner = img ? [img] : mediaCursor.toArray();
|
|
69
|
+
children.push(new Tag('div', { 'data-section': 'media', 'data-name': 'media' }, inner));
|
|
70
|
+
}
|
|
71
|
+
// Eyebrow: a leading paragraph immediately followed by a heading becomes a
|
|
72
|
+
// kicker (the page-section / recipe pattern). The rest stays freeform body.
|
|
73
|
+
let eyebrowNode;
|
|
74
|
+
let mainBodyNodes = bodyNodes;
|
|
75
|
+
if (bodyNodes.length >= 2 &&
|
|
76
|
+
bodyNodes[0].type === 'paragraph' &&
|
|
77
|
+
bodyNodes[1].type === 'heading') {
|
|
78
|
+
eyebrowNode = bodyNodes[0];
|
|
79
|
+
mainBodyNodes = bodyNodes.slice(1);
|
|
80
|
+
}
|
|
81
|
+
// Content zone (body + optional footer) — one grid child so the split
|
|
82
|
+
// layout puts media | content; footer sits at the bottom of content.
|
|
83
|
+
const bodyInner = [];
|
|
84
|
+
let eyebrowTag;
|
|
85
|
+
if (eyebrowNode) {
|
|
86
|
+
const eyebrowCursor = new RenderableNodeCursor(Markdoc.transform([eyebrowNode], config));
|
|
87
|
+
const first = eyebrowCursor.toArray()[0];
|
|
88
|
+
if (Markdoc.Tag.isTag(first)) {
|
|
89
|
+
eyebrowTag = first;
|
|
90
|
+
bodyInner.push(eyebrowTag);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const bodyCursor = new RenderableNodeCursor(Markdoc.transform(mainBodyNodes, config));
|
|
94
|
+
const bodyRendered = bodyCursor.toArray();
|
|
95
|
+
// The body's leading heading is the card title — give it a hook so it can
|
|
96
|
+
// sit tight under an eyebrow instead of carrying default prose top margin.
|
|
97
|
+
let titleTag;
|
|
98
|
+
const firstBody = bodyRendered[0];
|
|
99
|
+
if (Markdoc.Tag.isTag(firstBody) && /^h[1-6]$/.test(firstBody.name)) {
|
|
100
|
+
titleTag = firstBody;
|
|
101
|
+
}
|
|
102
|
+
bodyInner.push(...bodyRendered);
|
|
103
|
+
const bodyDiv = new Tag('div', { 'data-name': 'body' }, bodyInner);
|
|
104
|
+
const contentChildren = [bodyDiv];
|
|
105
|
+
let footerTag;
|
|
106
|
+
if (footerNodes.length > 0) {
|
|
107
|
+
const footerCursor = new RenderableNodeCursor(Markdoc.transform(footerNodes, config));
|
|
108
|
+
footerTag = new Tag('footer', { 'data-name': 'footer' }, footerCursor.toArray());
|
|
109
|
+
contentChildren.push(footerTag);
|
|
110
|
+
}
|
|
111
|
+
const contentDiv = new Tag('div', { 'data-name': 'content' }, contentChildren);
|
|
112
|
+
children.push(contentDiv);
|
|
113
|
+
// Whole-card link as a stretched overlay (keeps nested links valid).
|
|
114
|
+
let linkTag;
|
|
115
|
+
const href = String(attrs.href ?? '');
|
|
116
|
+
if (href) {
|
|
117
|
+
linkTag = new Tag('a', { 'data-name': 'link', href, 'aria-hidden': 'true', tabindex: '-1' }, []);
|
|
118
|
+
children.push(linkTag);
|
|
119
|
+
}
|
|
120
|
+
const refs = { content: contentDiv, body: bodyDiv };
|
|
121
|
+
if (titleTag)
|
|
122
|
+
refs.title = titleTag;
|
|
123
|
+
if (eyebrowTag)
|
|
124
|
+
refs.eyebrow = eyebrowTag;
|
|
125
|
+
if (footerTag)
|
|
126
|
+
refs.footer = footerTag;
|
|
127
|
+
if (linkTag)
|
|
128
|
+
refs.link = linkTag;
|
|
129
|
+
return createComponentRenderable({
|
|
130
|
+
rune: 'card',
|
|
131
|
+
tag: 'div',
|
|
132
|
+
properties: { layout: layoutMetas.layout },
|
|
133
|
+
refs,
|
|
134
|
+
children,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
//# sourceMappingURL=card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card.js","sourceRoot":"","sources":["../../src/tags/card.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import Markdoc from '@markdoc/markdoc';
|
|
2
|
+
export declare const COLLECTION_SENTINEL = "__collection-sentinel";
|
|
3
|
+
/**
|
|
4
|
+
* `collection` (SPEC-070) — queries the entity registry for many entities and
|
|
5
|
+
* projects them into a layout. Emits a sentinel that the postProcess resolver
|
|
6
|
+
* (`resolveCollections`) fills with real entity data. The body, when present,
|
|
7
|
+
* is a per-entity template captured via the deferBody mechanism (WORK-262).
|
|
8
|
+
*/
|
|
9
|
+
export declare const collection: Markdoc.Schema;
|
|
10
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/tags/collection.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AAKvC,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,UAAU,gBA2CrB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import Markdoc from '@markdoc/markdoc';
|
|
2
|
+
const { Tag } = Markdoc;
|
|
3
|
+
import { createContentModelSchema, createComponentRenderable } from '../lib/index.js';
|
|
4
|
+
import { readDeferredBody } from '../deferred-body.js';
|
|
5
|
+
export const COLLECTION_SENTINEL = '__collection-sentinel';
|
|
6
|
+
/**
|
|
7
|
+
* `collection` (SPEC-070) — queries the entity registry for many entities and
|
|
8
|
+
* projects them into a layout. Emits a sentinel that the postProcess resolver
|
|
9
|
+
* (`resolveCollections`) fills with real entity data. The body, when present,
|
|
10
|
+
* is a per-entity template captured via the deferBody mechanism (WORK-262).
|
|
11
|
+
*/
|
|
12
|
+
export const collection = createContentModelSchema({
|
|
13
|
+
attributes: {
|
|
14
|
+
type: { type: String, required: false, default: '', description: 'Entity type(s), comma-separated.' },
|
|
15
|
+
filter: { type: String, required: false, default: '', description: 'field:value clauses (SPEC-070 grammar).' },
|
|
16
|
+
sort: { type: String, required: false, default: '', description: 'Sort field (prefix - for descending).' },
|
|
17
|
+
group: { type: String, required: false, default: '', description: 'Group-by field.' },
|
|
18
|
+
limit: { type: String, required: false, default: '', description: 'Max items.' },
|
|
19
|
+
show: { type: String, required: false, default: '', description: 'Alias for type (entity types to include).' },
|
|
20
|
+
fields: { type: String, required: false, default: '', description: 'Comma-separated data fields to project.' },
|
|
21
|
+
layout: { type: String, required: false, default: 'list', description: 'Arrangement: list (stacked) | grid (multi-column) | table (aligned columns). Item chrome comes from the item — the no-body built-in, or a rune like {% card %} in the body template.' },
|
|
22
|
+
'group-display': { type: String, required: false, default: 'headings', matches: ['headings', 'accordion'], description: 'How groups are presented when `group` is set: headings (default) or accordion (collapsible native <details> panels, styled like the accordion rune, with a per-group count).' },
|
|
23
|
+
empty: { type: String, required: false, default: '', description: 'Fallback text shown when the query yields nothing (no-body form; body-template form uses a fallback zone). Absent → render nothing.' },
|
|
24
|
+
},
|
|
25
|
+
deferBody: true,
|
|
26
|
+
contentModel: { type: 'sequence', fields: [] },
|
|
27
|
+
transform(_resolved, attrs) {
|
|
28
|
+
const meta = (field, content) => new Tag('meta', { 'data-field': field, content });
|
|
29
|
+
const bodySource = readDeferredBody(attrs) ?? '';
|
|
30
|
+
const metas = [
|
|
31
|
+
meta('collection-type', String(attrs.type ?? attrs.show ?? '')),
|
|
32
|
+
meta('collection-filter', String(attrs.filter ?? '')),
|
|
33
|
+
meta('collection-sort', String(attrs.sort ?? '')),
|
|
34
|
+
meta('collection-group', String(attrs.group ?? '')),
|
|
35
|
+
meta('collection-limit', String(attrs.limit ?? '')),
|
|
36
|
+
meta('collection-fields', String(attrs.fields ?? '')),
|
|
37
|
+
meta('collection-layout', String(attrs.layout ?? 'list')),
|
|
38
|
+
meta('collection-group-display', String(attrs['group-display'] ?? 'headings')),
|
|
39
|
+
meta('collection-empty', String(attrs.empty ?? '')),
|
|
40
|
+
meta(COLLECTION_SENTINEL, 'true'),
|
|
41
|
+
];
|
|
42
|
+
if (bodySource)
|
|
43
|
+
metas.push(meta('collection-body', bodySource));
|
|
44
|
+
const placeholder = new Tag('div', { 'data-name': 'items' }, []);
|
|
45
|
+
return createComponentRenderable({
|
|
46
|
+
rune: 'collection',
|
|
47
|
+
tag: 'section',
|
|
48
|
+
properties: {},
|
|
49
|
+
refs: { items: placeholder },
|
|
50
|
+
children: [...metas, placeholder],
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/tags/collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/tags/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AAMvC,eAAO,MAAM,mBAAmB,gBA8B9B,CAAC;
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/tags/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AAMvC,eAAO,MAAM,mBAAmB,gBA8B9B,CAAC;AAwEH,eAAO,MAAM,YAAY,gBA2BvB,CAAC"}
|