@refrakt-md/runes 0.16.1 → 0.18.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/config.d.ts.map +1 -1
- package/dist/config.js +74 -377
- package/dist/config.js.map +1 -1
- package/dist/drawer-pipeline.d.ts.map +1 -1
- package/dist/drawer-pipeline.js +7 -22
- package/dist/drawer-pipeline.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/component.d.ts.map +1 -1
- package/dist/lib/component.js +50 -3
- package/dist/lib/component.js.map +1 -1
- package/dist/tags/badge.d.ts +5 -6
- package/dist/tags/badge.d.ts.map +1 -1
- package/dist/tags/badge.js +7 -18
- package/dist/tags/badge.js.map +1 -1
- package/dist/tags/bar.d.ts +23 -0
- package/dist/tags/bar.d.ts.map +1 -0
- package/dist/tags/bar.js +64 -0
- package/dist/tags/bar.js.map +1 -0
- package/dist/tags/budget.d.ts.map +1 -1
- package/dist/tags/budget.js +99 -9
- package/dist/tags/budget.js.map +1 -1
- package/dist/tags/chart.d.ts.map +1 -1
- package/dist/tags/chart.js +31 -64
- package/dist/tags/chart.js.map +1 -1
- package/dist/tags/codegroup.d.ts.map +1 -1
- package/dist/tags/codegroup.js +4 -1
- package/dist/tags/codegroup.js.map +1 -1
- package/dist/tags/deflist.d.ts +20 -0
- package/dist/tags/deflist.d.ts.map +1 -0
- package/dist/tags/deflist.js +173 -0
- package/dist/tags/deflist.js.map +1 -0
- package/dist/tags/diagram.d.ts.map +1 -1
- package/dist/tags/diagram.js +33 -7
- package/dist/tags/diagram.js.map +1 -1
- package/dist/tags/embed.d.ts.map +1 -1
- package/dist/tags/embed.js +28 -10
- package/dist/tags/embed.js.map +1 -1
- package/dist/tags/sandbox.d.ts.map +1 -1
- package/dist/tags/sandbox.js +32 -50
- package/dist/tags/sandbox.js.map +1 -1
- package/package.json +3 -3
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAA+B,MAAM,uBAAuB,CAAC;AAEtF,OAAO,KAAK,EAAE,mBAAmB,EAAmB,cAAc,EAAkB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAS/H,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAA+B,MAAM,uBAAuB,CAAC;AAEtF,OAAO,KAAK,EAAE,mBAAmB,EAAmB,cAAc,EAAkB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAS/H,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AA8B9D;gFACgF;AAChF,eAAO,MAAM,UAAU,EAAE,WAsfxB,CAAC;AAEF,sGAAsG;AACtG,eAAO,MAAM,UAAU,aAAa,CAAC;AAIrC,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,EAAE,CAAC;CACzB;AA0vCD,UAAU,YAAY;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAsKD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,OAAO,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;IACT,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChI,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IACnC;;;uBAGmB;IACnB,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACrC,EACD,GAAG,EAAE,eAAe;AACpB;;yEAEyE;AACzE,cAAc,CAAC,EAAE,OAAO,EAAE,GACxB,OAAO,CAqBT;AAED,2DAA2D;AAC3D,MAAM,WAAW,wBAAwB;IACxC;;8DAE0D;IAC1D,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC;;kFAE8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;6CACyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;2EAIuE;IACvE,WAAW,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC;;;yEAGiE;QACjE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,GAAE,wBAA6B,GAAG,mBAAmB,CA0ShG;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,EAAE,mBAA+C,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isTag,
|
|
1
|
+
import { isTag, readField, resolveGap, ratioToFr, resolveOffset, resolveValign, parsePlacement } from '@refrakt-md/transform';
|
|
2
2
|
import Markdoc from '@markdoc/markdoc';
|
|
3
3
|
const { Tag } = Markdoc;
|
|
4
4
|
import { createComponentRenderable } from './lib/index.js';
|
|
@@ -15,58 +15,6 @@ import { resolveExpands } from './expand-pipeline.js';
|
|
|
15
15
|
import { resolveCollections } from './collection-resolve.js';
|
|
16
16
|
import { resolveRelationships } from './relationships-resolve.js';
|
|
17
17
|
import { resolveAggregates } from './aggregate-resolve.js';
|
|
18
|
-
// ─── Budget postTransform helpers ───
|
|
19
|
-
const BUDGET_CURRENCY_SYMBOLS = {
|
|
20
|
-
USD: '$', EUR: '€', GBP: '£', JPY: '¥', CNY: '¥',
|
|
21
|
-
AUD: 'A$', CAD: 'C$', CHF: 'CHF ', SEK: 'kr', NOK: 'kr', DKK: 'kr',
|
|
22
|
-
INR: '₹', KRW: '₩', BRL: 'R$', MXN: 'MX$', ZAR: 'R',
|
|
23
|
-
};
|
|
24
|
-
function formatBudgetAmount(amount, symbol) {
|
|
25
|
-
const parts = (amount % 1 === 0 ? String(amount) : amount.toFixed(2)).split('.');
|
|
26
|
-
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
27
|
-
return symbol + parts.join('.');
|
|
28
|
-
}
|
|
29
|
-
function parseBudgetDays(duration) {
|
|
30
|
-
let days = 0;
|
|
31
|
-
const dayMatch = duration.match(/(\d+)\s*day/i);
|
|
32
|
-
const weekMatch = duration.match(/(\d+)\s*week/i);
|
|
33
|
-
const monthMatch = duration.match(/(\d+)\s*month/i);
|
|
34
|
-
if (dayMatch)
|
|
35
|
-
days += parseInt(dayMatch[1]);
|
|
36
|
-
if (weekMatch)
|
|
37
|
-
days += parseInt(weekMatch[1]) * 7;
|
|
38
|
-
if (monthMatch)
|
|
39
|
-
days += parseInt(monthMatch[1]) * 30;
|
|
40
|
-
if (days === 0) {
|
|
41
|
-
const num = parseInt(duration);
|
|
42
|
-
if (!isNaN(num))
|
|
43
|
-
days = num;
|
|
44
|
-
}
|
|
45
|
-
return days;
|
|
46
|
-
}
|
|
47
|
-
function parseBudgetAmount(str) {
|
|
48
|
-
const cleaned = str.replace(/[€$£¥₹₩\s]/g, '').replace(/,/g, '');
|
|
49
|
-
const range = cleaned.match(/^([\d.]+)\s*[-–]\s*([\d.]+)/);
|
|
50
|
-
if (range)
|
|
51
|
-
return (parseFloat(range[1]) + parseFloat(range[2])) / 2;
|
|
52
|
-
const num = parseFloat(cleaned);
|
|
53
|
-
return isNaN(num) ? 0 : num;
|
|
54
|
-
}
|
|
55
|
-
/** Recursively find all nodes with a specific data-rune attribute */
|
|
56
|
-
function collectByRune(children, typeName) {
|
|
57
|
-
const results = [];
|
|
58
|
-
for (const c of children) {
|
|
59
|
-
if (isTag(c)) {
|
|
60
|
-
if (c.attributes?.['data-rune'] === typeName) {
|
|
61
|
-
results.push(c);
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
results.push(...collectByRune(c.children, typeName));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return results;
|
|
69
|
-
}
|
|
70
18
|
/** Read text content from a property span child */
|
|
71
19
|
function readPropText(node, prop) {
|
|
72
20
|
for (const c of node.children) {
|
|
@@ -122,24 +70,26 @@ export const coreConfig = {
|
|
|
122
70
|
block: 'codegroup',
|
|
123
71
|
defaultDensity: 'compact',
|
|
124
72
|
modifiers: { title: { source: 'meta', noBemClass: true }, overflow: { source: 'meta', default: 'scroll' } },
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
{ tag: 'span', ref: 'title', metaText: 'title', condition: 'title' },
|
|
133
|
-
],
|
|
134
|
-
},
|
|
73
|
+
// The window chrome (three dots) is pure decoration — drawn in CSS
|
|
74
|
+
// on `.rf-codegroup__topbar`. The only metadata is the optional
|
|
75
|
+
// filename `title`, a bare monospace field in the topbar bar.
|
|
76
|
+
// `renderWhenEmpty` lets `title=""` still project the topbar (window
|
|
77
|
+
// chrome, no filename); an absent title renders no topbar.
|
|
78
|
+
metaFields: {
|
|
79
|
+
title: { metaType: 'code', condition: 'title', renderWhenEmpty: true },
|
|
135
80
|
},
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
postTransform(node) {
|
|
139
|
-
// Opt in to the highlight transform's `theme.code.colorScheme`
|
|
140
|
-
// cascade so the topbar + tab chrome flip with the inner code.
|
|
141
|
-
return { ...node, attributes: { ...node.attributes, 'data-code-host': true } };
|
|
81
|
+
blocks: {
|
|
82
|
+
topbar: { fields: ['title'], layout: 'bar' },
|
|
142
83
|
},
|
|
84
|
+
layout: { root: ['topbar'] },
|
|
85
|
+
sections: { topbar: 'header' },
|
|
86
|
+
// Opt in to the highlight transform's `theme.code.colorScheme` cascade
|
|
87
|
+
// (topbar + tab chrome flip with the inner code). Static flag → declared
|
|
88
|
+
// via rootAttributes rather than a postTransform. The `data-code-host`
|
|
89
|
+
// consumer reads it truthily, so `"true"` is equivalent to the old
|
|
90
|
+
// valueless boolean.
|
|
91
|
+
rootAttributes: { 'data-code-host': 'true' },
|
|
92
|
+
editHints: { panel: 'code' },
|
|
143
93
|
},
|
|
144
94
|
PageSection: { block: 'page-section' },
|
|
145
95
|
TableOfContents: { block: 'toc' },
|
|
@@ -160,6 +110,12 @@ export const coreConfig = {
|
|
|
160
110
|
* still needs an entry in the theme config so `computeUsedCssBlocks`
|
|
161
111
|
* includes `badge.css` in CSS tree-shaking when a badge is rendered. */
|
|
162
112
|
Badge: { block: 'badge' },
|
|
113
|
+
/* SPEC-079 composable rune handles — render the same DOM as the
|
|
114
|
+
* engine's `split` / `definition-list` layout primitives. CSS comes
|
|
115
|
+
* from the universal `[data-zone-layout=…]` selectors; per-rune
|
|
116
|
+
* blocks exist so CSS tree-shaking includes them. */
|
|
117
|
+
Bar: { block: 'bar' },
|
|
118
|
+
Deflist: { block: 'deflist' },
|
|
163
119
|
/* Collection emits a sentinel during transform; the postProcess hook
|
|
164
120
|
* (`resolveCollections`) fills it with queried entities. Engine config
|
|
165
121
|
* provides the block name for CSS tree-shaking. */
|
|
@@ -195,45 +151,13 @@ export const coreConfig = {
|
|
|
195
151
|
Embed: {
|
|
196
152
|
block: 'embed',
|
|
197
153
|
defaultDensity: 'compact',
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const aspect = readMeta(node, 'aspect') || '16:9';
|
|
204
|
-
const provider = readMeta(node, 'provider') || '';
|
|
205
|
-
const [w, h] = aspect.split(':').map(Number);
|
|
206
|
-
const paddingPercent = h && w ? (h / w) * 100 : 56.25;
|
|
207
|
-
// Filter out consumed meta tags
|
|
208
|
-
const contentChildren = node.children.filter(child => {
|
|
209
|
-
if (!isTag(child) || child.name !== 'meta')
|
|
210
|
-
return true;
|
|
211
|
-
const prop = child.attributes['data-field'];
|
|
212
|
-
return !['embedUrl', 'url', 'title', 'aspect', 'provider', 'type'].includes(prop);
|
|
213
|
-
});
|
|
214
|
-
const children = [];
|
|
215
|
-
if (embedUrl) {
|
|
216
|
-
children.push(makeTag('div', { class: `${block}__wrapper`, style: `padding-bottom: ${paddingPercent}%` }, [
|
|
217
|
-
makeTag('iframe', {
|
|
218
|
-
src: embedUrl,
|
|
219
|
-
title,
|
|
220
|
-
frameborder: '0',
|
|
221
|
-
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
|
222
|
-
allowfullscreen: '',
|
|
223
|
-
loading: 'lazy',
|
|
224
|
-
}, []),
|
|
225
|
-
]));
|
|
226
|
-
}
|
|
227
|
-
children.push(makeTag('div', { class: `${block}__fallback` }, contentChildren));
|
|
228
|
-
return {
|
|
229
|
-
...node,
|
|
230
|
-
attributes: {
|
|
231
|
-
...node.attributes,
|
|
232
|
-
...(provider ? { 'data-provider': provider } : {}),
|
|
233
|
-
},
|
|
234
|
-
children,
|
|
235
|
-
};
|
|
154
|
+
// SPEC-081: the rune transform builds the wrapper/iframe/fallback
|
|
155
|
+
// structure directly; `provider` is a bag-only modifier that surfaces
|
|
156
|
+
// as `data-provider`. No postTransform.
|
|
157
|
+
modifiers: {
|
|
158
|
+
provider: { source: 'meta', default: 'generic', noBemClass: true },
|
|
236
159
|
},
|
|
160
|
+
editHints: { fallback: 'none' },
|
|
237
161
|
},
|
|
238
162
|
Breadcrumb: {
|
|
239
163
|
block: 'breadcrumb',
|
|
@@ -265,75 +189,30 @@ export const coreConfig = {
|
|
|
265
189
|
Budget: {
|
|
266
190
|
block: 'budget',
|
|
267
191
|
defaultDensity: 'full',
|
|
268
|
-
sections: {
|
|
269
|
-
editHints: { headline: 'inline'
|
|
192
|
+
sections: { preamble: 'preamble', headline: 'title', footer: 'footer' },
|
|
193
|
+
editHints: { headline: 'inline' },
|
|
270
194
|
modifiers: {
|
|
271
195
|
currency: { source: 'meta', default: 'USD' },
|
|
272
196
|
duration: { source: 'meta' },
|
|
273
197
|
showPerDay: { source: 'meta', default: 'true' },
|
|
274
198
|
variant: { source: 'meta', default: 'detailed' },
|
|
275
199
|
},
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
tag: 'div', ref: 'meta',
|
|
283
|
-
children: [
|
|
284
|
-
{ tag: 'span', ref: 'meta-item', metaText: 'currency', condition: 'currency', metaType: 'category', metaRank: 'primary' },
|
|
285
|
-
{ tag: 'span', ref: 'meta-item', metaText: 'duration', label: 'Duration:', condition: 'duration', metaType: 'temporal', metaRank: 'secondary' },
|
|
286
|
-
],
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
},
|
|
200
|
+
// Duration reads first as a bare chip (self-evident, no label);
|
|
201
|
+
// currency is pushed to the right edge where it reads naturally
|
|
202
|
+
// against the budget breakdown below.
|
|
203
|
+
metaFields: {
|
|
204
|
+
duration: { metaType: 'category', condition: 'duration' },
|
|
205
|
+
currency: { metaType: 'category', condition: 'currency' },
|
|
290
206
|
},
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const categories = collectByRune(node.children, 'budget-category');
|
|
301
|
-
let grandTotal = 0;
|
|
302
|
-
for (const cat of categories) {
|
|
303
|
-
// Read from data attributes set by engine from label/subtotal modifiers
|
|
304
|
-
const label = cat.attributes['data-label'] || '';
|
|
305
|
-
const subtotalStr = cat.attributes['data-subtotal'] || '0';
|
|
306
|
-
const subtotal = parseFloat(subtotalStr) || 0;
|
|
307
|
-
grandTotal += subtotal;
|
|
308
|
-
// Inject category header with label and formatted subtotal
|
|
309
|
-
const catHeader = makeTag('div', { class: `${catBlock}__header` }, [
|
|
310
|
-
makeTag('span', { class: `${catBlock}__label` }, [label]),
|
|
311
|
-
makeTag('span', { class: `${catBlock}__subtotal` }, [formatBudgetAmount(subtotal, symbol)]),
|
|
312
|
-
]);
|
|
313
|
-
cat.children.unshift(catHeader);
|
|
314
|
-
}
|
|
315
|
-
// Build footer with totals
|
|
316
|
-
const footerChildren = [
|
|
317
|
-
makeTag('div', { class: `${block}__total` }, [
|
|
318
|
-
makeTag('span', { class: `${block}__total-label` }, ['Total']),
|
|
319
|
-
makeTag('span', { class: `${block}__total-amount` }, [formatBudgetAmount(grandTotal, symbol)]),
|
|
320
|
-
]),
|
|
321
|
-
];
|
|
322
|
-
if (duration && showPerDay) {
|
|
323
|
-
const days = parseBudgetDays(duration);
|
|
324
|
-
if (days > 0) {
|
|
325
|
-
const perDay = grandTotal / days;
|
|
326
|
-
footerChildren.push(makeTag('div', { class: `${block}__per-day` }, [
|
|
327
|
-
makeTag('span', { class: `${block}__per-day-label` }, ['Per day']),
|
|
328
|
-
makeTag('span', { class: `${block}__per-day-amount` }, [formatBudgetAmount(perDay, symbol)]),
|
|
329
|
-
]));
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
const footer = makeTag('div', { class: `${block}__footer` }, footerChildren);
|
|
333
|
-
return {
|
|
334
|
-
...node,
|
|
335
|
-
children: [...node.children, footer],
|
|
336
|
-
};
|
|
207
|
+
blocks: {
|
|
208
|
+
meta: { fields: ['duration', { field: 'currency', align: 'end' }], layout: 'bar' },
|
|
209
|
+
},
|
|
210
|
+
// SPEC-081: the transform emits flat header slots and derives the
|
|
211
|
+
// totals (footer + category headers built there); `layout` builds the
|
|
212
|
+
// preamble <header>, and the categories / footer append after it.
|
|
213
|
+
layout: {
|
|
214
|
+
root: ['meta', 'preamble'],
|
|
215
|
+
preamble: { tag: 'header', children: ['headline', 'blurb', 'image'] },
|
|
337
216
|
},
|
|
338
217
|
},
|
|
339
218
|
BudgetCategory: {
|
|
@@ -354,16 +233,16 @@ export const coreConfig = {
|
|
|
354
233
|
modifiers: { hintType: { source: 'meta', default: 'note' } },
|
|
355
234
|
contextModifiers: { 'hero': 'in-hero', 'feature': 'in-feature' },
|
|
356
235
|
sections: { header: 'header' },
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
},
|
|
236
|
+
// Header is a single `hintType` field, icon-decorated: the value
|
|
237
|
+
// (note/warning/caution/check) selects both the glyph and the
|
|
238
|
+
// label text.
|
|
239
|
+
metaFields: {
|
|
240
|
+
hintType: { icon: { group: 'hint' } },
|
|
241
|
+
},
|
|
242
|
+
blocks: {
|
|
243
|
+
header: { fields: ['hintType'], layout: 'bar' },
|
|
366
244
|
},
|
|
245
|
+
layout: { root: ['header'] },
|
|
367
246
|
},
|
|
368
247
|
Drawer: {
|
|
369
248
|
block: 'drawer',
|
|
@@ -490,115 +369,14 @@ export const coreConfig = {
|
|
|
490
369
|
Chart: {
|
|
491
370
|
block: 'chart',
|
|
492
371
|
defaultDensity: 'compact',
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
let chartData = { headers: [], rows: [] };
|
|
500
|
-
try {
|
|
501
|
-
chartData = JSON.parse(dataJson);
|
|
502
|
-
}
|
|
503
|
-
catch { /* fallback */ }
|
|
504
|
-
const colors = [
|
|
505
|
-
'var(--rf-color-info)', 'var(--rf-color-success)',
|
|
506
|
-
'var(--rf-color-warning)', 'var(--rf-color-danger)',
|
|
507
|
-
'#7c3aed', '#0891b2',
|
|
508
|
-
];
|
|
509
|
-
const svgW = 600, svgH = 300;
|
|
510
|
-
const pad = { top: 30, right: 20, bottom: 40, left: 50 };
|
|
511
|
-
const cw = svgW - pad.left - pad.right;
|
|
512
|
-
const ch = svgH - pad.top - pad.bottom;
|
|
513
|
-
const labels = chartData.rows.map(r => r[0] || '');
|
|
514
|
-
const series = chartData.headers.slice(1);
|
|
515
|
-
const values = chartData.rows.map(r => r.slice(1).map(v => parseFloat(v) || 0));
|
|
516
|
-
const maxVal = Math.max(...values.flat(), 1);
|
|
517
|
-
const bgw = cw / Math.max(labels.length, 1);
|
|
518
|
-
const bw = bgw / Math.max(series.length + 1, 2);
|
|
519
|
-
// Build SVG children
|
|
520
|
-
const svgChildren = [];
|
|
521
|
-
// Axes
|
|
522
|
-
svgChildren.push(makeTag('line', {
|
|
523
|
-
x1: String(pad.left), y1: String(pad.top),
|
|
524
|
-
x2: String(pad.left), y2: String(svgH - pad.bottom),
|
|
525
|
-
stroke: 'var(--rf-color-border)', 'stroke-width': '1',
|
|
526
|
-
}, []));
|
|
527
|
-
svgChildren.push(makeTag('line', {
|
|
528
|
-
x1: String(pad.left), y1: String(svgH - pad.bottom),
|
|
529
|
-
x2: String(svgW - pad.right), y2: String(svgH - pad.bottom),
|
|
530
|
-
stroke: 'var(--rf-color-border)', 'stroke-width': '1',
|
|
531
|
-
}, []));
|
|
532
|
-
if (chartType === 'bar') {
|
|
533
|
-
for (let i = 0; i < labels.length; i++) {
|
|
534
|
-
for (let si = 0; si < series.length; si++) {
|
|
535
|
-
const h = (values[i][si] / maxVal) * ch;
|
|
536
|
-
svgChildren.push(makeTag('rect', {
|
|
537
|
-
x: String(pad.left + i * bgw + si * bw + bw * 0.25),
|
|
538
|
-
y: String(pad.top + ch - h),
|
|
539
|
-
width: String(bw * 0.75),
|
|
540
|
-
height: String(h),
|
|
541
|
-
style: `fill: ${colors[si % colors.length]}`,
|
|
542
|
-
rx: '2',
|
|
543
|
-
}, []));
|
|
544
|
-
}
|
|
545
|
-
svgChildren.push(makeTag('text', {
|
|
546
|
-
x: String(pad.left + i * bgw + bgw / 2),
|
|
547
|
-
y: String(svgH - pad.bottom + 20),
|
|
548
|
-
'text-anchor': 'middle', 'font-size': '12',
|
|
549
|
-
fill: 'var(--rf-color-muted)',
|
|
550
|
-
}, [labels[i]]));
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
else if (chartType === 'line') {
|
|
554
|
-
for (let si = 0; si < series.length; si++) {
|
|
555
|
-
const pts = labels.map((_, i) => `${pad.left + i * bgw + bgw / 2},${pad.top + ch - (values[i][si] / maxVal) * ch}`).join(' ');
|
|
556
|
-
svgChildren.push(makeTag('polyline', {
|
|
557
|
-
points: pts, fill: 'none',
|
|
558
|
-
style: `stroke: ${colors[si % colors.length]}`,
|
|
559
|
-
'stroke-width': '2',
|
|
560
|
-
}, []));
|
|
561
|
-
for (let i = 0; i < labels.length; i++) {
|
|
562
|
-
svgChildren.push(makeTag('circle', {
|
|
563
|
-
cx: String(pad.left + i * bgw + bgw / 2),
|
|
564
|
-
cy: String(pad.top + ch - (values[i][si] / maxVal) * ch),
|
|
565
|
-
r: '4',
|
|
566
|
-
style: `fill: ${colors[si % colors.length]}`,
|
|
567
|
-
}, []));
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
for (let i = 0; i < labels.length; i++) {
|
|
571
|
-
svgChildren.push(makeTag('text', {
|
|
572
|
-
x: String(pad.left + i * bgw + bgw / 2),
|
|
573
|
-
y: String(svgH - pad.bottom + 20),
|
|
574
|
-
'text-anchor': 'middle', 'font-size': '12',
|
|
575
|
-
fill: 'var(--rf-color-muted)',
|
|
576
|
-
}, [labels[i]]));
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
const children = [];
|
|
580
|
-
if (title) {
|
|
581
|
-
children.push(makeTag('figcaption', { class: `${block}__title` }, [title]));
|
|
582
|
-
}
|
|
583
|
-
children.push(makeTag('div', { class: `${block}__container` }, [
|
|
584
|
-
makeTag('svg', {
|
|
585
|
-
viewBox: `0 0 ${svgW} ${svgH}`,
|
|
586
|
-
class: `${block}__svg`,
|
|
587
|
-
}, svgChildren),
|
|
588
|
-
]));
|
|
589
|
-
// Legend
|
|
590
|
-
if (series.length > 1) {
|
|
591
|
-
const legendItems = series.map((name, i) => makeTag('span', { class: `${block}__legend-item` }, [
|
|
592
|
-
makeTag('span', {
|
|
593
|
-
class: `${block}__legend-color`,
|
|
594
|
-
style: `background: ${colors[i % colors.length]};`,
|
|
595
|
-
}, []),
|
|
596
|
-
name,
|
|
597
|
-
]));
|
|
598
|
-
children.push(makeTag('div', { class: `${block}__legend` }, legendItems));
|
|
599
|
-
}
|
|
600
|
-
return { ...node, children };
|
|
372
|
+
// SPEC-083: the transform emits the rf-chart element wrapping the data
|
|
373
|
+
// `<table>`; `type` / `stacked` are bag-only modifiers (→ data-type /
|
|
374
|
+
// data-stacked) the web component reads. No postTransform.
|
|
375
|
+
modifiers: {
|
|
376
|
+
type: { source: 'meta', default: 'bar', noBemClass: true },
|
|
377
|
+
stacked: { source: 'meta', noBemClass: true },
|
|
601
378
|
},
|
|
379
|
+
editHints: { data: 'none' },
|
|
602
380
|
},
|
|
603
381
|
// ─── Text formatting & layout runes ───
|
|
604
382
|
PullQuote: {
|
|
@@ -732,31 +510,10 @@ export const coreConfig = {
|
|
|
732
510
|
block: 'diagram',
|
|
733
511
|
defaultDensity: 'compact',
|
|
734
512
|
editHints: { source: 'code' },
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const sourceMeta = findByDataName(node, 'source');
|
|
740
|
-
const source = sourceMeta?.attributes?.content || '';
|
|
741
|
-
// Build fallback HTML (visible in SSR, replaced by web component)
|
|
742
|
-
const children = [];
|
|
743
|
-
if (title) {
|
|
744
|
-
children.push(makeTag('figcaption', { class: `${block}__title` }, [title]));
|
|
745
|
-
}
|
|
746
|
-
const containerChildren = source
|
|
747
|
-
? [makeTag('pre', { class: `${block}__source` }, [makeTag('code', {}, [source])])]
|
|
748
|
-
: [];
|
|
749
|
-
children.push(makeTag('div', { class: `${block}__container` }, containerChildren));
|
|
750
|
-
// Hidden source for web component to read
|
|
751
|
-
if (source) {
|
|
752
|
-
children.push(makeTag('div', { 'data-content': 'source', style: 'display:none' }, [source]));
|
|
753
|
-
}
|
|
754
|
-
return {
|
|
755
|
-
...node,
|
|
756
|
-
name: 'rf-diagram',
|
|
757
|
-
attributes: { ...node.attributes, 'data-language': language },
|
|
758
|
-
children,
|
|
759
|
-
};
|
|
513
|
+
// SPEC-081: the rune transform emits the `rf-diagram` element + SSR
|
|
514
|
+
// fallback; `language` is a bag-only modifier (→ data-language).
|
|
515
|
+
modifiers: {
|
|
516
|
+
language: { source: 'meta', default: 'mermaid', noBemClass: true },
|
|
760
517
|
},
|
|
761
518
|
},
|
|
762
519
|
Tint: { block: 'tint', parent: '*' },
|
|
@@ -766,64 +523,6 @@ export const coreConfig = {
|
|
|
766
523
|
block: 'sandbox',
|
|
767
524
|
defaultDensity: 'compact',
|
|
768
525
|
editHints: { source: 'code' },
|
|
769
|
-
postTransform(node) {
|
|
770
|
-
// Read meta values
|
|
771
|
-
const content = readMeta(node, 'content') || '';
|
|
772
|
-
const framework = readMeta(node, 'framework') || '';
|
|
773
|
-
const dependencies = readMeta(node, 'dependencies') || '';
|
|
774
|
-
const label = readMeta(node, 'label') || '';
|
|
775
|
-
const height = readMeta(node, 'height') || 'auto';
|
|
776
|
-
const designTokens = readMeta(node, 'design-tokens') || '';
|
|
777
|
-
const securityMode = readMeta(node, 'security-mode') || 'trusted';
|
|
778
|
-
const allowJs = readMeta(node, 'allow-js') || 'true';
|
|
779
|
-
const sandboxOrigin = readMeta(node, 'sandbox-origin') || '';
|
|
780
|
-
// Keep non-meta children (fallback pre) and extract source panels
|
|
781
|
-
const fallbackChildren = [];
|
|
782
|
-
const sourcePanelOrigins = [];
|
|
783
|
-
for (const child of node.children) {
|
|
784
|
-
if (!isTag(child)) {
|
|
785
|
-
fallbackChildren.push(child);
|
|
786
|
-
continue;
|
|
787
|
-
}
|
|
788
|
-
if (child.name === 'meta') {
|
|
789
|
-
// Collect origin data from source panels
|
|
790
|
-
if (child.attributes?.['data-field'] === 'source-panel' && child.attributes?.['data-origin']) {
|
|
791
|
-
sourcePanelOrigins.push(`${child.attributes['data-label'] || ''}\t${child.attributes['data-origin']}`);
|
|
792
|
-
}
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
fallbackChildren.push(child);
|
|
796
|
-
}
|
|
797
|
-
// Wrap fallback and source in <template> tags (inert/invisible).
|
|
798
|
-
// Using <template> instead of <div> avoids HTML parser issues:
|
|
799
|
-
// when <rf-sandbox> is inside <p>, block elements like <pre> or
|
|
800
|
-
// <div> cause <p> to auto-close, pushing children out of the
|
|
801
|
-
// custom element. <template> is parsed but never rendered.
|
|
802
|
-
const children = [
|
|
803
|
-
...(fallbackChildren.length > 0
|
|
804
|
-
? [makeTag('template', { 'data-content': 'fallback' }, fallbackChildren)]
|
|
805
|
-
: []),
|
|
806
|
-
makeTag('template', { 'data-content': 'source' }, [content]),
|
|
807
|
-
];
|
|
808
|
-
return {
|
|
809
|
-
...node,
|
|
810
|
-
name: 'rf-sandbox',
|
|
811
|
-
attributes: {
|
|
812
|
-
...node.attributes,
|
|
813
|
-
'data-source-content': content,
|
|
814
|
-
...(framework ? { 'data-framework': framework } : {}),
|
|
815
|
-
...(dependencies ? { 'data-dependencies': dependencies } : {}),
|
|
816
|
-
...(label ? { 'data-label': label } : {}),
|
|
817
|
-
'data-height': height,
|
|
818
|
-
...(designTokens ? { 'data-design-tokens': designTokens } : {}),
|
|
819
|
-
...(sourcePanelOrigins.length > 0 ? { 'data-source-origins': sourcePanelOrigins.join('\n') } : {}),
|
|
820
|
-
'data-security-mode': securityMode,
|
|
821
|
-
'data-allow-js': allowJs,
|
|
822
|
-
...(sandboxOrigin ? { 'data-sandbox-origin': sandboxOrigin } : {}),
|
|
823
|
-
},
|
|
824
|
-
children,
|
|
825
|
-
};
|
|
826
|
-
},
|
|
827
526
|
},
|
|
828
527
|
},
|
|
829
528
|
};
|
|
@@ -2004,14 +1703,12 @@ function resolveBlogPosts(renderable, allPosts, ctx, pageUrl) {
|
|
|
2004
1703
|
const result = mapBlogTags(renderable, (tag) => {
|
|
2005
1704
|
if (tag.attributes['data-rune'] !== 'blog')
|
|
2006
1705
|
return tag;
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
const
|
|
2010
|
-
const
|
|
2011
|
-
const
|
|
2012
|
-
const
|
|
2013
|
-
const filterStr = Tag.isTag(filterMeta) ? filterMeta.attributes.content : '';
|
|
2014
|
-
const limitStr = Tag.isTag(limitMeta) ? limitMeta.attributes.content : '';
|
|
1706
|
+
// SPEC-082: read field values from the bag (bag-first, meta-fallback).
|
|
1707
|
+
// The cross-page tree still carries the `data-rune-fields` attribute.
|
|
1708
|
+
const folder = readField(tag, 'folder') ?? '';
|
|
1709
|
+
const sort = readField(tag, 'sort') || 'date-desc';
|
|
1710
|
+
const filterStr = readField(tag, 'filter') ?? '';
|
|
1711
|
+
const limitStr = readField(tag, 'limit') ?? '';
|
|
2015
1712
|
const limit = limitStr ? parseInt(limitStr, 10) : undefined;
|
|
2016
1713
|
if (!folder) {
|
|
2017
1714
|
ctx.warn('Blog rune missing folder attribute', pageUrl);
|