@refrakt-md/runes 0.33.0 → 0.35.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.
Files changed (305) hide show
  1. package/dist/aggregate-resolve.d.ts.map +1 -1
  2. package/dist/aggregate-resolve.js +18 -4
  3. package/dist/aggregate-resolve.js.map +1 -1
  4. package/dist/attributes.d.ts +15 -0
  5. package/dist/attributes.d.ts.map +1 -1
  6. package/dist/attributes.js +22 -7
  7. package/dist/attributes.js.map +1 -1
  8. package/dist/collection-helpers.d.ts.map +1 -1
  9. package/dist/collection-helpers.js +1 -1
  10. package/dist/collection-helpers.js.map +1 -1
  11. package/dist/collection-resolve.d.ts.map +1 -1
  12. package/dist/collection-resolve.js +31 -8
  13. package/dist/collection-resolve.js.map +1 -1
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/config.js +218 -77
  16. package/dist/config.js.map +1 -1
  17. package/dist/data-adapters.d.ts.map +1 -1
  18. package/dist/data-adapters.js +28 -11
  19. package/dist/data-adapters.js.map +1 -1
  20. package/dist/data-emit.d.ts.map +1 -1
  21. package/dist/data-emit.js.map +1 -1
  22. package/dist/data-pipeline.d.ts.map +1 -1
  23. package/dist/data-pipeline.js +45 -12
  24. package/dist/data-pipeline.js.map +1 -1
  25. package/dist/data-projection.d.ts.map +1 -1
  26. package/dist/data-projection.js +4 -3
  27. package/dist/data-projection.js.map +1 -1
  28. package/dist/data-resolve.d.ts.map +1 -1
  29. package/dist/data-resolve.js +5 -2
  30. package/dist/data-resolve.js.map +1 -1
  31. package/dist/documents/page.d.ts.map +1 -1
  32. package/dist/documents/page.js.map +1 -1
  33. package/dist/drawer-pipeline.d.ts.map +1 -1
  34. package/dist/drawer-pipeline.js +3 -2
  35. package/dist/drawer-pipeline.js.map +1 -1
  36. package/dist/expand-pipeline.d.ts.map +1 -1
  37. package/dist/expand-pipeline.js +3 -3
  38. package/dist/expand-pipeline.js.map +1 -1
  39. package/dist/fence-escape.d.ts.map +1 -1
  40. package/dist/fence-escape.js +1 -3
  41. package/dist/fence-escape.js.map +1 -1
  42. package/dist/field-match.d.ts.map +1 -1
  43. package/dist/field-match.js.map +1 -1
  44. package/dist/file-ref-resolve.js +6 -4
  45. package/dist/file-ref-resolve.js.map +1 -1
  46. package/dist/fixtures.js +2 -2
  47. package/dist/fixtures.js.map +1 -1
  48. package/dist/functions.d.ts.map +1 -1
  49. package/dist/functions.js +16 -3
  50. package/dist/functions.js.map +1 -1
  51. package/dist/include-pipeline.d.ts.map +1 -1
  52. package/dist/include-pipeline.js +3 -1
  53. package/dist/include-pipeline.js.map +1 -1
  54. package/dist/index.d.ts +95 -37
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +273 -60
  57. package/dist/index.js.map +1 -1
  58. package/dist/interfaces.d.ts.map +1 -1
  59. package/dist/interfaces.js.map +1 -1
  60. package/dist/layouts/grid.d.ts +2 -2
  61. package/dist/layouts/grid.d.ts.map +1 -1
  62. package/dist/layouts/grid.js +4 -6
  63. package/dist/layouts/grid.js.map +1 -1
  64. package/dist/lib/component.d.ts.map +1 -1
  65. package/dist/lib/component.js +7 -7
  66. package/dist/lib/component.js.map +1 -1
  67. package/dist/lib/icon-resolve.d.ts.map +1 -1
  68. package/dist/lib/icon-resolve.js.map +1 -1
  69. package/dist/lib/image-schemes.js +2 -2
  70. package/dist/lib/image-schemes.js.map +1 -1
  71. package/dist/lib/index.d.ts +29 -1
  72. package/dist/lib/index.d.ts.map +1 -1
  73. package/dist/lib/index.js +298 -51
  74. package/dist/lib/index.js.map +1 -1
  75. package/dist/lib/node.d.ts.map +1 -1
  76. package/dist/lib/node.js +3 -2
  77. package/dist/lib/node.js.map +1 -1
  78. package/dist/lib/placeholder.d.ts.map +1 -1
  79. package/dist/lib/placeholder.js +6 -9
  80. package/dist/lib/placeholder.js.map +1 -1
  81. package/dist/lib/read-file.d.ts.map +1 -1
  82. package/dist/lib/read-file.js.map +1 -1
  83. package/dist/lib/renderable.d.ts.map +1 -1
  84. package/dist/lib/renderable.js +5 -5
  85. package/dist/lib/renderable.js.map +1 -1
  86. package/dist/lib/resolver.d.ts.map +1 -1
  87. package/dist/lib/resolver.js +42 -36
  88. package/dist/lib/resolver.js.map +1 -1
  89. package/dist/lib/schema-table.d.ts +176 -0
  90. package/dist/lib/schema-table.d.ts.map +1 -0
  91. package/dist/lib/schema-table.js +427 -0
  92. package/dist/lib/schema-table.js.map +1 -0
  93. package/dist/lib/svg.d.ts.map +1 -1
  94. package/dist/lib/svg.js.map +1 -1
  95. package/dist/nodes.d.ts.map +1 -1
  96. package/dist/nodes.js +9 -11
  97. package/dist/nodes.js.map +1 -1
  98. package/dist/outline-scope.js +1 -3
  99. package/dist/outline-scope.js.map +1 -1
  100. package/dist/plugins.d.ts.map +1 -1
  101. package/dist/plugins.js +13 -10
  102. package/dist/plugins.js.map +1 -1
  103. package/dist/reference.d.ts +33 -0
  104. package/dist/reference.d.ts.map +1 -1
  105. package/dist/reference.js +60 -23
  106. package/dist/reference.js.map +1 -1
  107. package/dist/relationships-resolve.d.ts.map +1 -1
  108. package/dist/relationships-resolve.js +31 -7
  109. package/dist/relationships-resolve.js.map +1 -1
  110. package/dist/rune.d.ts +0 -2
  111. package/dist/rune.d.ts.map +1 -1
  112. package/dist/rune.js +0 -1
  113. package/dist/rune.js.map +1 -1
  114. package/dist/sandbox-sources.js +6 -6
  115. package/dist/sandbox-sources.js.map +1 -1
  116. package/dist/schema-row.d.ts +89 -0
  117. package/dist/schema-row.d.ts.map +1 -0
  118. package/dist/schema-row.js +78 -0
  119. package/dist/schema-row.js.map +1 -0
  120. package/dist/seo.d.ts +9 -0
  121. package/dist/seo.d.ts.map +1 -1
  122. package/dist/seo.js +88 -11
  123. package/dist/seo.js.map +1 -1
  124. package/dist/snippet-pipeline.d.ts.map +1 -1
  125. package/dist/snippet-pipeline.js +1 -1
  126. package/dist/snippet-pipeline.js.map +1 -1
  127. package/dist/tags/accordion.d.ts +58 -0
  128. package/dist/tags/accordion.d.ts.map +1 -1
  129. package/dist/tags/accordion.js +69 -23
  130. package/dist/tags/accordion.js.map +1 -1
  131. package/dist/tags/aggregate.d.ts.map +1 -1
  132. package/dist/tags/aggregate.js +65 -10
  133. package/dist/tags/aggregate.js.map +1 -1
  134. package/dist/tags/annotate.d.ts.map +1 -1
  135. package/dist/tags/annotate.js +12 -9
  136. package/dist/tags/annotate.js.map +1 -1
  137. package/dist/tags/badge.d.ts.map +1 -1
  138. package/dist/tags/badge.js.map +1 -1
  139. package/dist/tags/bar.d.ts.map +1 -1
  140. package/dist/tags/bar.js +3 -7
  141. package/dist/tags/bar.js.map +1 -1
  142. package/dist/tags/bg.d.ts.map +1 -1
  143. package/dist/tags/bg.js +60 -12
  144. package/dist/tags/bg.js.map +1 -1
  145. package/dist/tags/blog.d.ts.map +1 -1
  146. package/dist/tags/blog.js +43 -10
  147. package/dist/tags/blog.js.map +1 -1
  148. package/dist/tags/breadcrumb.d.ts +30 -0
  149. package/dist/tags/breadcrumb.d.ts.map +1 -1
  150. package/dist/tags/breadcrumb.js +57 -28
  151. package/dist/tags/breadcrumb.js.map +1 -1
  152. package/dist/tags/budget.d.ts.map +1 -1
  153. package/dist/tags/budget.js +60 -16
  154. package/dist/tags/budget.js.map +1 -1
  155. package/dist/tags/card.d.ts.map +1 -1
  156. package/dist/tags/card.js +24 -6
  157. package/dist/tags/card.js.map +1 -1
  158. package/dist/tags/chart.d.ts.map +1 -1
  159. package/dist/tags/chart.js +33 -11
  160. package/dist/tags/chart.js.map +1 -1
  161. package/dist/tags/codegroup.d.ts.map +1 -1
  162. package/dist/tags/codegroup.js +39 -21
  163. package/dist/tags/codegroup.js.map +1 -1
  164. package/dist/tags/collection.d.ts.map +1 -1
  165. package/dist/tags/collection.js +55 -9
  166. package/dist/tags/collection.js.map +1 -1
  167. package/dist/tags/common.d.ts.map +1 -1
  168. package/dist/tags/common.js +34 -9
  169. package/dist/tags/common.js.map +1 -1
  170. package/dist/tags/compare.d.ts.map +1 -1
  171. package/dist/tags/compare.js +26 -14
  172. package/dist/tags/compare.js.map +1 -1
  173. package/dist/tags/conversation.d.ts.map +1 -1
  174. package/dist/tags/conversation.js +19 -17
  175. package/dist/tags/conversation.js.map +1 -1
  176. package/dist/tags/data.d.ts.map +1 -1
  177. package/dist/tags/data.js +67 -13
  178. package/dist/tags/data.js.map +1 -1
  179. package/dist/tags/datatable.d.ts.map +1 -1
  180. package/dist/tags/datatable.js +27 -8
  181. package/dist/tags/datatable.js.map +1 -1
  182. package/dist/tags/deflist.d.ts.map +1 -1
  183. package/dist/tags/deflist.js +5 -11
  184. package/dist/tags/deflist.js.map +1 -1
  185. package/dist/tags/details.d.ts.map +1 -1
  186. package/dist/tags/details.js +8 -5
  187. package/dist/tags/details.js.map +1 -1
  188. package/dist/tags/diagram.d.ts.map +1 -1
  189. package/dist/tags/diagram.js +9 -5
  190. package/dist/tags/diagram.js.map +1 -1
  191. package/dist/tags/diff.d.ts.map +1 -1
  192. package/dist/tags/diff.js +43 -24
  193. package/dist/tags/diff.js.map +1 -1
  194. package/dist/tags/drawer.d.ts.map +1 -1
  195. package/dist/tags/drawer.js +4 -6
  196. package/dist/tags/drawer.js.map +1 -1
  197. package/dist/tags/embed.d.ts +8 -0
  198. package/dist/tags/embed.d.ts.map +1 -1
  199. package/dist/tags/embed.js +28 -10
  200. package/dist/tags/embed.js.map +1 -1
  201. package/dist/tags/expand.d.ts.map +1 -1
  202. package/dist/tags/expand.js.map +1 -1
  203. package/dist/tags/figure.d.ts +7 -0
  204. package/dist/tags/figure.d.ts.map +1 -1
  205. package/dist/tags/figure.js +42 -13
  206. package/dist/tags/figure.js.map +1 -1
  207. package/dist/tags/file-ref.d.ts.map +1 -1
  208. package/dist/tags/file-ref.js.map +1 -1
  209. package/dist/tags/form.d.ts.map +1 -1
  210. package/dist/tags/form.js +52 -25
  211. package/dist/tags/form.js.map +1 -1
  212. package/dist/tags/gallery.d.ts.map +1 -1
  213. package/dist/tags/gallery.js +33 -12
  214. package/dist/tags/gallery.js.map +1 -1
  215. package/dist/tags/grid.d.ts.map +1 -1
  216. package/dist/tags/grid.js +57 -14
  217. package/dist/tags/grid.js.map +1 -1
  218. package/dist/tags/hint.d.ts.map +1 -1
  219. package/dist/tags/hint.js +9 -5
  220. package/dist/tags/hint.js.map +1 -1
  221. package/dist/tags/icon.d.ts.map +1 -1
  222. package/dist/tags/icon.js +5 -1
  223. package/dist/tags/icon.js.map +1 -1
  224. package/dist/tags/include.d.ts.map +1 -1
  225. package/dist/tags/include.js +1 -1
  226. package/dist/tags/include.js.map +1 -1
  227. package/dist/tags/juxtapose.d.ts.map +1 -1
  228. package/dist/tags/juxtapose.js +32 -14
  229. package/dist/tags/juxtapose.js.map +1 -1
  230. package/dist/tags/layout.d.ts.map +1 -1
  231. package/dist/tags/layout.js +7 -4
  232. package/dist/tags/layout.js.map +1 -1
  233. package/dist/tags/mediatext.d.ts.map +1 -1
  234. package/dist/tags/mediatext.js +19 -8
  235. package/dist/tags/mediatext.js.map +1 -1
  236. package/dist/tags/nav.d.ts.map +1 -1
  237. package/dist/tags/nav.js +43 -23
  238. package/dist/tags/nav.js.map +1 -1
  239. package/dist/tags/pagination.d.ts.map +1 -1
  240. package/dist/tags/pagination.js +27 -5
  241. package/dist/tags/pagination.js.map +1 -1
  242. package/dist/tags/progress.d.ts.map +1 -1
  243. package/dist/tags/progress.js +25 -5
  244. package/dist/tags/progress.js.map +1 -1
  245. package/dist/tags/pullquote.d.ts.map +1 -1
  246. package/dist/tags/pullquote.js +16 -9
  247. package/dist/tags/pullquote.js.map +1 -1
  248. package/dist/tags/region.d.ts.map +1 -1
  249. package/dist/tags/region.js +9 -4
  250. package/dist/tags/region.js.map +1 -1
  251. package/dist/tags/relationships.d.ts.map +1 -1
  252. package/dist/tags/relationships.js +55 -9
  253. package/dist/tags/relationships.js.map +1 -1
  254. package/dist/tags/reveal.d.ts.map +1 -1
  255. package/dist/tags/reveal.js +16 -7
  256. package/dist/tags/reveal.js.map +1 -1
  257. package/dist/tags/sandbox.d.ts.map +1 -1
  258. package/dist/tags/sandbox.js +104 -44
  259. package/dist/tags/sandbox.js.map +1 -1
  260. package/dist/tags/section.d.ts.map +1 -1
  261. package/dist/tags/section.js +13 -3
  262. package/dist/tags/section.js.map +1 -1
  263. package/dist/tags/showcase.d.ts.map +1 -1
  264. package/dist/tags/showcase.js +33 -11
  265. package/dist/tags/showcase.js.map +1 -1
  266. package/dist/tags/sidenote.d.ts.map +1 -1
  267. package/dist/tags/sidenote.js +9 -5
  268. package/dist/tags/sidenote.js.map +1 -1
  269. package/dist/tags/snippet.d.ts.map +1 -1
  270. package/dist/tags/snippet.js.map +1 -1
  271. package/dist/tags/tabs.d.ts.map +1 -1
  272. package/dist/tags/tabs.js +16 -9
  273. package/dist/tags/tabs.js.map +1 -1
  274. package/dist/tags/textblock.d.ts.map +1 -1
  275. package/dist/tags/textblock.js +9 -5
  276. package/dist/tags/textblock.js.map +1 -1
  277. package/dist/tags/tint.d.ts +1 -1
  278. package/dist/tags/tint.d.ts.map +1 -1
  279. package/dist/tags/tint.js +14 -2
  280. package/dist/tags/tint.js.map +1 -1
  281. package/dist/tags/toc.d.ts.map +1 -1
  282. package/dist/tags/toc.js +16 -7
  283. package/dist/tags/toc.js.map +1 -1
  284. package/dist/tags/xref.d.ts.map +1 -1
  285. package/dist/tags/xref.js.map +1 -1
  286. package/dist/universal-attributes.d.ts.map +1 -1
  287. package/dist/universal-attributes.js +28 -5
  288. package/dist/universal-attributes.js.map +1 -1
  289. package/dist/util.d.ts.map +1 -1
  290. package/dist/util.js +4 -4
  291. package/dist/util.js.map +1 -1
  292. package/dist/xref-patterns.js +1 -1
  293. package/dist/xref-patterns.js.map +1 -1
  294. package/dist/xref-preview-resolve.js +3 -4
  295. package/dist/xref-preview-resolve.js.map +1 -1
  296. package/dist/xref-resolve.d.ts.map +1 -1
  297. package/dist/xref-resolve.js +4 -4
  298. package/dist/xref-resolve.js.map +1 -1
  299. package/i18n/de.json +67 -67
  300. package/i18n/en.json +67 -67
  301. package/package.json +3 -3
  302. package/dist/tags/error.d.ts +0 -3
  303. package/dist/tags/error.d.ts.map +0 -1
  304. package/dist/tags/error.js +0 -34
  305. package/dist/tags/error.js.map +0 -1
package/dist/config.js CHANGED
@@ -1,15 +1,15 @@
1
- import { isTag, readField, resolveGap, ratioToFr, resolveValign } from '@refrakt-md/transform';
1
+ import { isTag, readField, resolveGap, ratioToFr, resolveValign, } from '@refrakt-md/transform';
2
2
  import Markdoc from '@markdoc/markdoc';
3
3
  const { Tag } = Markdoc;
4
- import { createComponentRenderable } from './lib/index.js';
5
- import { BREADCRUMB_AUTO_SENTINEL } from './tags/breadcrumb.js';
4
+ import { applySchemaTable, createComponentRenderable } from './lib/index.js';
5
+ import { BREADCRUMB_AUTO_SENTINEL, breadcrumbSchema } from './tags/breadcrumb.js';
6
6
  import { NAV_AUTO_SENTINEL, NAV_COLLAPSED_AUTO } from './tags/nav.js';
7
7
  import { PAGINATION_AUTO_SENTINEL } from './tags/pagination.js';
8
8
  import { resolveXrefs } from './xref-resolve.js';
9
9
  import { preprocessSnippets, wrapStandaloneSnippets } from './snippet-pipeline.js';
10
10
  import { preprocessData } from './data-pipeline.js';
11
11
  import { preprocessIncludes } from './include-pipeline.js';
12
- import { registerDrawers, resolveAutoDrawerTitleLevels, hoistPreviewDrawers } from './drawer-pipeline.js';
12
+ import { registerDrawers, resolveAutoDrawerTitleLevels, hoistPreviewDrawers, } from './drawer-pipeline.js';
13
13
  import { resolveFileRefs } from './file-ref-resolve.js';
14
14
  import { resolveXrefPreviews } from './xref-preview-resolve.js';
15
15
  import { applyOutlineScopeWalkers, harvestHeadingsFromRenderable } from './outline-scope.js';
@@ -44,7 +44,12 @@ import { tabGroupSections } from './tags/tabs.js';
44
44
  /** Read text content from a property span child */
45
45
  function readPropText(node, prop) {
46
46
  for (const c of node.children) {
47
- if (isTag(c) && c.attributes?.['data-field'] === prop.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/([A-Z])([A-Z][a-z])/g, '$1-$2').toLowerCase()) {
47
+ if (isTag(c) &&
48
+ c.attributes?.['data-field'] ===
49
+ prop
50
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
51
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
52
+ .toLowerCase()) {
48
53
  return c.children.filter((ch) => typeof ch === 'string').join('');
49
54
  }
50
55
  }
@@ -66,22 +71,35 @@ export const coreConfig = {
66
71
  icons: {},
67
72
  runes: {
68
73
  // ─── Simple runes (block name only, engine adds BEM classes) ───
69
- Accordion: { block: 'accordion', defaultDensity: 'full', sections: accordionSections, autoLabel: pageSectionAutoLabel, editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline' } },
74
+ Accordion: {
75
+ block: 'accordion',
76
+ defaultDensity: 'full',
77
+ sections: accordionSections,
78
+ autoLabel: pageSectionAutoLabel,
79
+ editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline' },
80
+ },
70
81
  // SPEC-125 Phase 1 — the answer panel is the item's `body` role, so
71
82
  // `reading` / `dropcap` land on it. The header decision is recorded in
72
83
  // `sectionRoleExceptions` so it travels with the rune.
73
84
  AccordionItem: {
74
- block: 'accordion-item', parent: 'Accordion', requiresParent: 'Accordion',
85
+ block: 'accordion-item',
86
+ parent: 'Accordion',
87
+ requiresParent: 'Accordion',
75
88
  rootAttributes: { 'data-state': 'closed' },
76
89
  autoLabel: { name: 'header' },
77
90
  sections: accordionItemSections,
78
91
  provides: ['prose'],
79
92
  sectionRoleExceptions: {
80
- header: 'The `header` slot is the <summary> disclosure control, not a page-section header. `header` would inherit the chrome-row rhythm (a 3rem margin under every summary); `title` would tighten every summary\'s line-height while leaving `prominence` inert anyway, since `.rf-accordion-item__header` pins the control\'s font size on purpose. The parent Accordion already holds `title`.',
93
+ header: "The `header` slot is the <summary> disclosure control, not a page-section header. `header` would inherit the chrome-row rhythm (a 3rem margin under every summary); `title` would tighten every summary's line-height while leaving `prominence` inert anyway, since `.rf-accordion-item__header` pins the control's font size on purpose. The parent Accordion already holds `title`.",
81
94
  },
82
95
  editHints: { header: 'inline', body: 'none' },
83
96
  },
84
- Details: { block: 'details', defaultElevation: 'flush', autoLabel: { summary: 'summary' }, editHints: { summary: 'inline', body: 'none' } },
97
+ Details: {
98
+ block: 'details',
99
+ defaultElevation: 'flush',
100
+ autoLabel: { summary: 'summary' },
101
+ editHints: { summary: 'inline', body: 'none' },
102
+ },
85
103
  Grid: {
86
104
  block: 'grid',
87
105
  defaultDensity: 'full',
@@ -110,7 +128,10 @@ export const coreConfig = {
110
128
  interactive: true,
111
129
  defaultDensity: 'compact',
112
130
  defaultElevation: 'flat',
113
- modifiers: { title: { source: 'meta', noBemClass: true }, overflow: { source: 'meta', default: 'scroll' } },
131
+ modifiers: {
132
+ title: { source: 'meta', noBemClass: true },
133
+ overflow: { source: 'meta', default: 'scroll' },
134
+ },
114
135
  // The window chrome (three dots) is pure decoration — drawn in CSS
115
136
  // on `.rf-codegroup__topbar`. The only metadata is the optional
116
137
  // filename `title`, a bare monospace field in the topbar bar.
@@ -263,7 +284,11 @@ export const coreConfig = {
263
284
  separator: { prop: '--separator', template: '"{}"' },
264
285
  },
265
286
  },
266
- BreadcrumbItem: { block: 'breadcrumb-item', parent: 'Breadcrumb', requiresParent: 'Breadcrumb' },
287
+ BreadcrumbItem: {
288
+ block: 'breadcrumb-item',
289
+ parent: 'Breadcrumb',
290
+ requiresParent: 'Breadcrumb',
291
+ },
267
292
  Blog: {
268
293
  block: 'blog',
269
294
  defaultDensity: 'full',
@@ -327,7 +352,7 @@ export const coreConfig = {
327
352
  defaultDensity: 'compact',
328
353
  defaultElevation: 'sunken',
329
354
  modifiers: { hintType: { source: 'meta', default: 'note' } },
330
- contextModifiers: { 'hero': 'in-hero', 'feature': 'in-feature' },
355
+ contextModifiers: { hero: 'in-hero', feature: 'in-feature' },
331
356
  sections: hintSections,
332
357
  // SPEC-035 Zone 6 — the hintType value doubles as the visible title
333
358
  // (CSS capitalizes it). Declaring it as an enum lets `core.hint.<value>`
@@ -401,7 +426,12 @@ export const coreConfig = {
401
426
  modifiers: { layout: { source: 'meta', default: 'side-by-side' } },
402
427
  editHints: { panels: 'none' },
403
428
  },
404
- Conversation: { block: 'conversation', defaultDensity: 'compact', defaultElevation: 'flush', editHints: { messages: 'none' } },
429
+ Conversation: {
430
+ block: 'conversation',
431
+ defaultDensity: 'compact',
432
+ defaultElevation: 'flush',
433
+ editHints: { messages: 'none' },
434
+ },
405
435
  ConversationMessage: {
406
436
  block: 'conversation-message',
407
437
  parent: 'Conversation',
@@ -444,7 +474,7 @@ export const coreConfig = {
444
474
  // Extract slug from span[property="slug"] child → data-slug attribute
445
475
  // Keep slug text as visible fallback for SSR; web component replaces with <a> links
446
476
  let slug = '';
447
- const children = node.children.filter(child => {
477
+ const children = node.children.filter((child) => {
448
478
  if (isTag(child) && child.name === 'span' && child.attributes['data-field'] === 'slug') {
449
479
  slug = child.children.filter((c) => typeof c === 'string').join('');
450
480
  return false; // remove slug span from DOM
@@ -461,10 +491,16 @@ export const coreConfig = {
461
491
  };
462
492
  }
463
493
  // Explicit link item: add nav-item__link class to <a> tags for styling
464
- const styledChildren = children.map(child => {
494
+ const styledChildren = children.map((child) => {
465
495
  if (isTag(child) && child.name === 'a') {
466
496
  const existing = child.attributes.class || '';
467
- return { ...child, attributes: { ...child.attributes, class: ['rf-nav-item__link', existing].filter(Boolean).join(' ') } };
497
+ return {
498
+ ...child,
499
+ attributes: {
500
+ ...child.attributes,
501
+ class: ['rf-nav-item__link', existing].filter(Boolean).join(' '),
502
+ },
503
+ };
468
504
  }
469
505
  return child;
470
506
  });
@@ -474,7 +510,12 @@ export const coreConfig = {
474
510
  Diff: {
475
511
  block: 'diff',
476
512
  modifiers: { mode: { source: 'meta', default: 'unified' } },
477
- editHints: { line: 'none', 'gutter-num': 'none', 'gutter-prefix': 'none', 'line-content': 'none' },
513
+ editHints: {
514
+ line: 'none',
515
+ 'gutter-num': 'none',
516
+ 'gutter-prefix': 'none',
517
+ 'line-content': 'none',
518
+ },
478
519
  },
479
520
  Chart: {
480
521
  block: 'chart',
@@ -550,9 +591,27 @@ export const coreConfig = {
550
591
  editHints: { viewport: 'none' },
551
592
  },
552
593
  // ─── Interactive runes (still get BEM classes, components add behavior) ───
553
- TabGroup: { block: 'tabs', interactive: true, defaultDensity: 'full', sections: tabGroupSections, autoLabel: pageSectionAutoLabel, editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline' } },
554
- Tab: { block: 'tab', parent: 'TabGroup', requiresParent: 'TabGroup', rootAttributes: { 'data-state': 'inactive' }, editHints: { name: 'inline' } },
555
- TabPanel: { block: 'tab-panel', parent: 'TabGroup', requiresParent: 'TabGroup', rootAttributes: { 'data-state': 'inactive' } },
594
+ TabGroup: {
595
+ block: 'tabs',
596
+ interactive: true,
597
+ defaultDensity: 'full',
598
+ sections: tabGroupSections,
599
+ autoLabel: pageSectionAutoLabel,
600
+ editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline' },
601
+ },
602
+ Tab: {
603
+ block: 'tab',
604
+ parent: 'TabGroup',
605
+ requiresParent: 'TabGroup',
606
+ rootAttributes: { 'data-state': 'inactive' },
607
+ editHints: { name: 'inline' },
608
+ },
609
+ TabPanel: {
610
+ block: 'tab-panel',
611
+ parent: 'TabGroup',
612
+ requiresParent: 'TabGroup',
613
+ rootAttributes: { 'data-state': 'inactive' },
614
+ },
556
615
  DataTable: {
557
616
  block: 'datatable',
558
617
  interactive: true,
@@ -601,7 +660,12 @@ export const coreConfig = {
601
660
  autoLabel: pageSectionAutoLabel,
602
661
  editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline', steps: 'none' },
603
662
  },
604
- RevealStep: { block: 'reveal-step', parent: 'Reveal', rootAttributes: { 'data-state': 'closed' }, editHints: { body: 'none' } },
663
+ RevealStep: {
664
+ block: 'reveal-step',
665
+ parent: 'Reveal',
666
+ rootAttributes: { 'data-state': 'closed' },
667
+ editHints: { body: 'none' },
668
+ },
605
669
  Section: {
606
670
  block: 'section',
607
671
  modifiers: {
@@ -627,7 +691,13 @@ export const coreConfig = {
627
691
  },
628
692
  editHints: { panels: 'none' },
629
693
  },
630
- JuxtaposePanel: { block: 'juxtapose-panel', parent: 'Juxtapose', requiresParent: 'Juxtapose', rootAttributes: { 'data-state': 'inactive' }, editHints: { body: 'none' } },
694
+ JuxtaposePanel: {
695
+ block: 'juxtapose-panel',
696
+ parent: 'Juxtapose',
697
+ requiresParent: 'Juxtapose',
698
+ rootAttributes: { 'data-state': 'inactive' },
699
+ editHints: { body: 'none' },
700
+ },
631
701
  Diagram: {
632
702
  block: 'diagram',
633
703
  defaultDensity: 'compact',
@@ -712,7 +782,7 @@ function deriveParentUrl(url) {
712
782
  function resolveAutoBreadcrumbs(renderable, pageUrl, breadcrumbPaths, pagesByUrl, ctx) {
713
783
  if (!Tag.isTag(renderable)) {
714
784
  if (Array.isArray(renderable)) {
715
- const newChildren = renderable.map(c => resolveAutoBreadcrumbs(c, pageUrl, breadcrumbPaths, pagesByUrl, ctx));
785
+ const newChildren = renderable.map((c) => resolveAutoBreadcrumbs(c, pageUrl, breadcrumbPaths, pagesByUrl, ctx));
716
786
  // Return original if nothing changed
717
787
  if (newChildren.every((c, i) => c === renderable[i]))
718
788
  return renderable;
@@ -744,7 +814,19 @@ function buildAutoBreadcrumb(originalTag, pageUrl, breadcrumbPaths, pagesByUrl,
744
814
  // Find separator from existing meta child
745
815
  const separatorMeta = originalTag.children?.find((c) => Tag.isTag(c) && c.name === 'meta' && !c.attributes?.['data-field']);
746
816
  const separator = separatorMeta?.attributes?.content ?? '/';
747
- // Build breadcrumb items: ancestor pages + current page (no link)
817
+ // Build breadcrumb items: ancestor pages + current page (no link).
818
+ //
819
+ // WORK-563 — the schema is not decoration on this path. `collectJsonLd`
820
+ // nests a typed node into its parent only when that node carries *both*
821
+ // `typeof` and `property`. Without them this hook emitted a
822
+ // `BreadcrumbList` with no `itemListElement` and a `ListItem` floating up
823
+ // beside it as a detached, empty top-level entity — while rendering a
824
+ // perfectly correct trail.
825
+ //
826
+ // The explicit `{% breadcrumb %}` form (tags/breadcrumb.ts) has always got
827
+ // this right. This path is the same output contract and must match it: the
828
+ // bar is that an auto trail publishes what the same trail written by hand
829
+ // would.
748
830
  const listItems = [];
749
831
  for (const ancestorUrl of ancestorUrls) {
750
832
  const ancestorPage = pagesByUrl.get(ancestorUrl);
@@ -752,7 +834,8 @@ function buildAutoBreadcrumb(originalTag, pageUrl, breadcrumbPaths, pagesByUrl,
752
834
  continue;
753
835
  const nameSpan = new Tag('span', { hidden: true }, [ancestorPage.title]);
754
836
  const urlLink = new Tag('a', { href: ancestorUrl }, [ancestorPage.title]);
755
- listItems.push(createComponentRenderable({ rune: 'breadcrumb-item', schemaOrgType: 'ListItem',
837
+ listItems.push(createComponentRenderable({
838
+ rune: 'breadcrumb-item',
756
839
  tag: 'li',
757
840
  properties: { name: nameSpan, url: urlLink },
758
841
  children: [nameSpan, urlLink],
@@ -762,25 +845,38 @@ function buildAutoBreadcrumb(originalTag, pageUrl, breadcrumbPaths, pagesByUrl,
762
845
  const currentPage = pagesByUrl.get(pageUrl);
763
846
  const currentTitle = currentPage?.title ?? pageUrl;
764
847
  const currentSpan = new Tag('span', {}, [currentTitle]);
765
- listItems.push(createComponentRenderable({ rune: 'breadcrumb-item', schemaOrgType: 'ListItem',
848
+ listItems.push(createComponentRenderable({
849
+ rune: 'breadcrumb-item',
766
850
  tag: 'li',
767
851
  properties: { name: currentSpan },
768
852
  children: [currentSpan],
769
853
  }));
770
854
  const newSeparatorMeta = new Tag('meta', { content: separator });
771
855
  const itemsList = new Tag('ol', {}, listItems);
772
- return createComponentRenderable({ rune: 'breadcrumb', schemaOrgType: 'BreadcrumbList',
856
+ const node = createComponentRenderable({
857
+ rune: 'breadcrumb',
773
858
  tag: 'nav',
774
859
  properties: { separator: newSeparatorMeta },
775
860
  refs: { items: itemsList },
776
861
  children: [newSeparatorMeta, itemsList],
777
862
  });
863
+ // SPEC-130 D8 / WORK-571 — the hook calls the applier itself.
864
+ //
865
+ // `buildAutoBreadcrumb` runs from a `postProcess` hook, not from a Markdoc
866
+ // schema, so the `createContentModelSchema` wrapper that normally applies a
867
+ // rune's table cannot reach it. One extra call site keeps "the imperative
868
+ // form is fully migrated, not half of each" exceptionless rather than
869
+ // carving out the one emitter that happens to live in a pipeline phase —
870
+ // and, more to the point, it means both breadcrumb paths publish from the
871
+ // *same* table rather than from a declaration and a hand-written copy of it
872
+ // that can drift.
873
+ return applySchemaTable(node, breadcrumbSchema, {});
778
874
  }
779
875
  /** Walk a Markdoc renderable tree, resolving any auto-nav placeholders */
780
876
  function resolveAutoNavs(renderable, pageUrl, pagesByUrl, ctx) {
781
877
  if (!Tag.isTag(renderable)) {
782
878
  if (Array.isArray(renderable)) {
783
- const newChildren = renderable.map(c => resolveAutoNavs(c, pageUrl, pagesByUrl, ctx));
879
+ const newChildren = renderable.map((c) => resolveAutoNavs(c, pageUrl, pagesByUrl, ctx));
784
880
  if (newChildren.every((c, i) => c === renderable[i]))
785
881
  return renderable;
786
882
  return newChildren;
@@ -808,21 +904,23 @@ function resolveAutoNavs(renderable, pageUrl, pagesByUrl, ctx) {
808
904
  * declared. */
809
905
  function buildAutoNav(pageUrl, pagesByUrl, ctx, originalTag) {
810
906
  // Find direct children: pages whose parentUrl is this page's URL (excluding self)
811
- const children = Array.from(pagesByUrl.values()).filter(p => p.parentUrl === pageUrl && p.url !== pageUrl);
907
+ const children = Array.from(pagesByUrl.values()).filter((p) => p.parentUrl === pageUrl && p.url !== pageUrl);
812
908
  if (children.length === 0) {
813
909
  ctx.warn(`Nav auto: page '${pageUrl}' has no registered child pages`, pageUrl);
814
910
  }
815
- const listItems = children.map(child => {
911
+ const listItems = children.map((child) => {
816
912
  const titleSpan = new Tag('span', { 'data-field': 'slug' }, [child.title]);
817
913
  const link = new Tag('a', { href: child.url }, [titleSpan]);
818
- return createComponentRenderable({ rune: 'nav-item',
914
+ return createComponentRenderable({
915
+ rune: 'nav-item',
819
916
  tag: 'li',
820
917
  properties: { slug: titleSpan },
821
918
  children: [link],
822
919
  });
823
920
  });
824
921
  const itemsList = new Tag('ul', {}, listItems);
825
- const newNav = createComponentRenderable({ rune: 'nav',
922
+ const newNav = createComponentRenderable({
923
+ rune: 'nav',
826
924
  tag: 'nav',
827
925
  properties: {
828
926
  group: [],
@@ -832,7 +930,14 @@ function buildAutoNav(pageUrl, pagesByUrl, ctx, originalTag) {
832
930
  });
833
931
  // Preserve contextual attributes from the original nav so layout / auto /
834
932
  // source-path survive sentinel replacement.
835
- const carry = ['layout', 'data-layout', 'data-auto', 'data-source-path', 'data-collapsible', 'data-default-open'];
933
+ const carry = [
934
+ 'layout',
935
+ 'data-layout',
936
+ 'data-auto',
937
+ 'data-source-path',
938
+ 'data-collapsible',
939
+ 'data-default-open',
940
+ ];
836
941
  for (const k of carry) {
837
942
  const v = originalTag?.attributes?.[k];
838
943
  if (v !== undefined) {
@@ -920,7 +1025,7 @@ function findNavSlugSuggestions(slug, baseDir, pagesByUrl) {
920
1025
  }
921
1026
  sameSlug.sort();
922
1027
  typoMatches.sort((a, b) => a.distance - b.distance || a.url.localeCompare(b.url));
923
- const suggestions = [...sameSlug, ...typoMatches.map(t => t.url)];
1028
+ const suggestions = [...sameSlug, ...typoMatches.map((t) => t.url)];
924
1029
  return suggestions.slice(0, 3);
925
1030
  }
926
1031
  /** SPEC-055 nav-location-relative slug resolution.
@@ -960,15 +1065,14 @@ function sharedPrefixLength(a, b) {
960
1065
  * been migrated to use pre-resolved hrefs yet. SPEC-055's
961
1066
  * `resolveNavSlug` is the canonical resolver for new code. */
962
1067
  function resolveSlugToUrl(slug, pagesByUrl, currentUrl) {
963
- const candidates = Array.from(pagesByUrl.values()).filter(p => p.url.endsWith('/' + slug) || p.url === '/' + slug);
1068
+ const candidates = Array.from(pagesByUrl.values()).filter((p) => p.url.endsWith('/' + slug) || p.url === '/' + slug);
964
1069
  if (candidates.length === 0)
965
1070
  return null;
966
1071
  if (candidates.length === 1)
967
1072
  return candidates[0].url;
968
1073
  return candidates
969
1074
  .slice()
970
- .sort((a, b) => sharedPrefixLength(b.url, currentUrl) - sharedPrefixLength(a.url, currentUrl))[0]
971
- .url;
1075
+ .sort((a, b) => sharedPrefixLength(b.url, currentUrl) - sharedPrefixLength(a.url, currentUrl))[0].url;
972
1076
  }
973
1077
  /** Read a NavItem's slug from either `data-slug` (post-engine) or a nested
974
1078
  * `<span data-field="slug">` (pre-engine, e.g. layout regions). */
@@ -1084,7 +1188,7 @@ function urlMatchesGroup(currentUrl, itemUrls) {
1084
1188
  function resolveCollapsibleNavs(renderable, pageUrl, pagesByUrl) {
1085
1189
  if (!Tag.isTag(renderable)) {
1086
1190
  if (Array.isArray(renderable)) {
1087
- const newChildren = renderable.map(c => resolveCollapsibleNavs(c, pageUrl, pagesByUrl));
1191
+ const newChildren = renderable.map((c) => resolveCollapsibleNavs(c, pageUrl, pagesByUrl));
1088
1192
  if (newChildren.every((c, i) => c === renderable[i]))
1089
1193
  return renderable;
1090
1194
  return newChildren;
@@ -1092,11 +1196,13 @@ function resolveCollapsibleNavs(renderable, pageUrl, pagesByUrl) {
1092
1196
  return renderable;
1093
1197
  }
1094
1198
  const tag = renderable;
1095
- if (tag.attributes?.['data-rune'] === 'nav' &&
1096
- tag.attributes?.['data-collapsible'] === 'true') {
1199
+ if (tag.attributes?.['data-rune'] === 'nav' && tag.attributes?.['data-collapsible'] === 'true') {
1097
1200
  const defaultOpenRaw = String(tag.attributes?.['data-default-open'] ?? '');
1098
1201
  const defaultOpen = defaultOpenRaw
1099
- ? defaultOpenRaw.split(',').map(s => s.trim()).filter(Boolean)
1202
+ ? defaultOpenRaw
1203
+ .split(',')
1204
+ .map((s) => s.trim())
1205
+ .filter(Boolean)
1100
1206
  : [];
1101
1207
  const groups = [];
1102
1208
  const findGroups = (node) => {
@@ -1116,7 +1222,7 @@ function resolveCollapsibleNavs(renderable, pageUrl, pagesByUrl) {
1116
1222
  const inDefault = title && defaultOpen.includes(title);
1117
1223
  const itemUrls = collectGroupItemUrls(group, pagesByUrl, pageUrl);
1118
1224
  const matches = urlMatchesGroup(pageUrl, itemUrls);
1119
- group.attributes['data-collapsed'] = (inDefault || matches) ? 'false' : 'true';
1225
+ group.attributes['data-collapsed'] = inDefault || matches ? 'false' : 'true';
1120
1226
  }
1121
1227
  const newChildren = (tag.children ?? []).map((c) => resolveCollapsibleNavs(c, pageUrl, pagesByUrl));
1122
1228
  if (newChildren.every((c, i) => c === tag.children[i]))
@@ -1139,7 +1245,9 @@ function enrichNavItemAsCard(item, pageMeta, itemUrl) {
1139
1245
  }
1140
1246
  linkChildren.push(new Tag('span', { 'data-name': 'title', class: 'rf-nav-item__title' }, [titleText]));
1141
1247
  if (description) {
1142
- linkChildren.push(new Tag('span', { 'data-name': 'description', class: 'rf-nav-item__description' }, [description]));
1248
+ linkChildren.push(new Tag('span', { 'data-name': 'description', class: 'rf-nav-item__description' }, [
1249
+ description,
1250
+ ]));
1143
1251
  }
1144
1252
  const link = href
1145
1253
  ? new Tag('a', { href, class: 'rf-nav-item__link' }, linkChildren)
@@ -1206,7 +1314,9 @@ function augmentNavItemFromFrontmatter(item, pageMeta) {
1206
1314
  newLinkChildren.unshift(new Tag('rf-icon', { name: pageMeta.icon, 'data-name': 'icon', class: 'rf-nav-item__icon' }, []));
1207
1315
  }
1208
1316
  if (pageMeta.description && !hasDescription) {
1209
- newLinkChildren.push(new Tag('span', { 'data-name': 'description', class: 'rf-nav-item__description' }, [pageMeta.description]));
1317
+ newLinkChildren.push(new Tag('span', { 'data-name': 'description', class: 'rf-nav-item__description' }, [
1318
+ pageMeta.description,
1319
+ ]));
1210
1320
  }
1211
1321
  return { ...t, children: newLinkChildren };
1212
1322
  });
@@ -1215,7 +1325,7 @@ function augmentNavItemFromFrontmatter(item, pageMeta) {
1215
1325
  function resolveCardsNavs(renderable, pageUrl, pagesByUrl) {
1216
1326
  if (!Tag.isTag(renderable)) {
1217
1327
  if (Array.isArray(renderable)) {
1218
- const newChildren = renderable.map(c => resolveCardsNavs(c, pageUrl, pagesByUrl));
1328
+ const newChildren = renderable.map((c) => resolveCardsNavs(c, pageUrl, pagesByUrl));
1219
1329
  if (newChildren.every((c, i) => c === renderable[i]))
1220
1330
  return renderable;
1221
1331
  return newChildren;
@@ -1291,7 +1401,13 @@ function formatNavResolutionError(slug, sourcePath, attemptedUrl, suggestions) {
1291
1401
  const head = `Nav item \`${slug}\`${where} cannot be resolved (no page at \`${attemptedUrl}\`).`;
1292
1402
  if (suggestions.length === 0)
1293
1403
  return head;
1294
- const lines = ['', 'Did you mean one of:', ...suggestions.map(s => ` - ${s}`), '', 'Use a multi-segment slug (e.g. `section/page`) or an explicit `[Label](/path)` link.'];
1404
+ const lines = [
1405
+ '',
1406
+ 'Did you mean one of:',
1407
+ ...suggestions.map((s) => ` - ${s}`),
1408
+ '',
1409
+ 'Use a multi-segment slug (e.g. `section/page`) or an explicit `[Label](/path)` link.',
1410
+ ];
1295
1411
  return head + '\n' + lines.join('\n');
1296
1412
  }
1297
1413
  /** Walk a nav subtree, resolve every NavItem's `data-slug` to a real `<a href>`
@@ -1354,7 +1470,7 @@ function resolveNavItemsInSubtree(navTag, pagesByUrl, ctx, pageUrl) {
1354
1470
  function resolveNavSlugs(renderable, pagesByUrl, ctx, pageUrl) {
1355
1471
  if (!Tag.isTag(renderable)) {
1356
1472
  if (Array.isArray(renderable)) {
1357
- const next = renderable.map(c => resolveNavSlugs(c, pagesByUrl, ctx, pageUrl));
1473
+ const next = renderable.map((c) => resolveNavSlugs(c, pagesByUrl, ctx, pageUrl));
1358
1474
  return next.every((c, i) => c === renderable[i]) ? renderable : next;
1359
1475
  }
1360
1476
  return renderable;
@@ -1570,10 +1686,10 @@ function getSiblingPages(currentUrl, pagesByUrl, scope) {
1570
1686
  cursor = parent;
1571
1687
  }
1572
1688
  const prefix = sectionRoot.endsWith('/') ? sectionRoot : sectionRoot + '/';
1573
- candidates = pages.filter(p => p.url === sectionRoot || p.url.startsWith(prefix));
1689
+ candidates = pages.filter((p) => p.url === sectionRoot || p.url.startsWith(prefix));
1574
1690
  }
1575
1691
  else {
1576
- candidates = pages.filter(p => p.parentUrl === current.parentUrl);
1692
+ candidates = pages.filter((p) => p.parentUrl === current.parentUrl);
1577
1693
  }
1578
1694
  // Sort by frontmatter order (asc, missing → end), tie-break by URL.
1579
1695
  candidates.sort((a, b) => {
@@ -1583,7 +1699,7 @@ function getSiblingPages(currentUrl, pagesByUrl, scope) {
1583
1699
  return ao - bo;
1584
1700
  return a.url.localeCompare(b.url);
1585
1701
  });
1586
- return candidates.map(p => ({ url: p.url, title: p.title }));
1702
+ return candidates.map((p) => ({ url: p.url, title: p.title }));
1587
1703
  }
1588
1704
  function pickPrevNextFromSequence(sequence, currentUrl, pagesByUrl) {
1589
1705
  const idx = sequence.indexOf(currentUrl);
@@ -1620,7 +1736,7 @@ function buildPaginationLink(target, direction, label) {
1620
1736
  function resolveAutoPagination(renderable, pageUrl, pagesByUrl, rootRenderable) {
1621
1737
  if (!Tag.isTag(renderable)) {
1622
1738
  if (Array.isArray(renderable)) {
1623
- const newChildren = renderable.map(c => resolveAutoPagination(c, pageUrl, pagesByUrl, rootRenderable));
1739
+ const newChildren = renderable.map((c) => resolveAutoPagination(c, pageUrl, pagesByUrl, rootRenderable));
1624
1740
  if (newChildren.every((c, i) => c === renderable[i]))
1625
1741
  return renderable;
1626
1742
  return newChildren;
@@ -1638,10 +1754,14 @@ function resolveAutoPagination(renderable, pageUrl, pagesByUrl, rootRenderable)
1638
1754
  const scope = (scopeValue === 'section' ? 'section' : 'siblings');
1639
1755
  const prevLabelMeta = (tag.children ?? []).find((c) => Tag.isTag(c) && c.attributes?.['data-field'] === 'prev-label');
1640
1756
  const nextLabelMeta = (tag.children ?? []).find((c) => Tag.isTag(c) && c.attributes?.['data-field'] === 'next-label');
1641
- const prevLabel = prevLabelMeta ? String(prevLabelMeta.attributes.content ?? '') : undefined;
1642
- const nextLabel = nextLabelMeta ? String(nextLabelMeta.attributes.content ?? '') : undefined;
1757
+ const prevLabel = prevLabelMeta
1758
+ ? String(prevLabelMeta.attributes.content ?? '')
1759
+ : undefined;
1760
+ const nextLabel = nextLabelMeta
1761
+ ? String(nextLabelMeta.attributes.content ?? '')
1762
+ : undefined;
1643
1763
  // Skip when current page is a section index (has children)
1644
- const hasChildren = Array.from(pagesByUrl.values()).some(p => p.parentUrl === pageUrl && p.url !== pageUrl);
1764
+ const hasChildren = Array.from(pagesByUrl.values()).some((p) => p.parentUrl === pageUrl && p.url !== pageUrl);
1645
1765
  if (hasChildren) {
1646
1766
  return { ...tag, children: [] };
1647
1767
  }
@@ -1662,7 +1782,7 @@ function resolveAutoPagination(renderable, pageUrl, pagesByUrl, rootRenderable)
1662
1782
  else {
1663
1783
  // 2. Sibling pages by frontmatter order → directory order
1664
1784
  const siblings = getSiblingPages(pageUrl, pagesByUrl, scope);
1665
- const idx = siblings.findIndex(s => s.url === pageUrl);
1785
+ const idx = siblings.findIndex((s) => s.url === pageUrl);
1666
1786
  if (idx !== -1) {
1667
1787
  prev = idx > 0 ? siblings[idx - 1] : null;
1668
1788
  next = idx < siblings.length - 1 ? siblings[idx + 1] : null;
@@ -1689,7 +1809,9 @@ function resolveAutoPagination(renderable, pageUrl, pagesByUrl, rootRenderable)
1689
1809
  const newChildren = (tag.children ?? []).map((c) => {
1690
1810
  if (!Tag.isTag(c))
1691
1811
  return c;
1692
- if (c.name === 'a' && typeof c.attributes?.href === 'string' && c.attributes.href.startsWith('__slug:')) {
1812
+ if (c.name === 'a' &&
1813
+ typeof c.attributes?.href === 'string' &&
1814
+ c.attributes.href.startsWith('__slug:')) {
1693
1815
  const slug = c.attributes.href.slice('__slug:'.length);
1694
1816
  const resolvedUrl = resolveSlugToUrl(slug, pagesByUrl, pageUrl);
1695
1817
  if (resolvedUrl) {
@@ -1698,14 +1820,20 @@ function resolveAutoPagination(renderable, pageUrl, pagesByUrl, rootRenderable)
1698
1820
  // Replace label text if user didn't override (label child has same text as slug)
1699
1821
  const newChildren = (c.children ?? []).map((child) => {
1700
1822
  if (Tag.isTag(child) && child.attributes?.['data-name'] === 'label') {
1701
- const currentLabel = (child.children ?? []).filter((x) => typeof x === 'string').join('');
1823
+ const currentLabel = (child.children ?? [])
1824
+ .filter((x) => typeof x === 'string')
1825
+ .join('');
1702
1826
  if (currentLabel === slug) {
1703
1827
  return { ...child, children: [newLabel] };
1704
1828
  }
1705
1829
  }
1706
1830
  return child;
1707
1831
  });
1708
- return { ...c, attributes: { ...c.attributes, href: resolvedUrl }, children: newChildren };
1832
+ return {
1833
+ ...c,
1834
+ attributes: { ...c.attributes, href: resolvedUrl },
1835
+ children: newChildren,
1836
+ };
1709
1837
  }
1710
1838
  }
1711
1839
  return c;
@@ -1724,7 +1852,7 @@ function walkBlogTags(node, fn) {
1724
1852
  walkBlogTags(child, fn);
1725
1853
  }
1726
1854
  else if (Array.isArray(node)) {
1727
- node.forEach(n => walkBlogTags(n, fn));
1855
+ node.forEach((n) => walkBlogTags(n, fn));
1728
1856
  }
1729
1857
  }
1730
1858
  function mapBlogTags(node, fn) {
@@ -1732,12 +1860,12 @@ function mapBlogTags(node, fn) {
1732
1860
  const mapped = fn(node);
1733
1861
  if (mapped !== node)
1734
1862
  return mapped;
1735
- const newChildren = node.children.map(c => mapBlogTags(c, fn));
1863
+ const newChildren = node.children.map((c) => mapBlogTags(c, fn));
1736
1864
  const changed = newChildren.some((c, i) => c !== node.children[i]);
1737
1865
  return changed ? new Tag(node.name, node.attributes, newChildren) : node;
1738
1866
  }
1739
1867
  if (Array.isArray(node))
1740
- return node.map(n => mapBlogTags(n, fn));
1868
+ return node.map((n) => mapBlogTags(n, fn));
1741
1869
  return node;
1742
1870
  }
1743
1871
  /** Normalise folder path for prefix matching: ensure leading slash and trailing slash */
@@ -1761,7 +1889,7 @@ function isInFolder(pageUrl, folder) {
1761
1889
  function parseBlogFilter(filter) {
1762
1890
  if (!filter || !filter.trim())
1763
1891
  return [];
1764
- return filter.split(',').map(part => {
1892
+ return filter.split(',').map((part) => {
1765
1893
  const colonIdx = part.indexOf(':');
1766
1894
  if (colonIdx === -1)
1767
1895
  return { field: part.trim(), value: '' };
@@ -1780,7 +1908,7 @@ function matchesBlogFilter(post, filters) {
1780
1908
  return false;
1781
1909
  }
1782
1910
  else if (Array.isArray(fmValue)) {
1783
- if (!fmValue.some(v => String(v).toLowerCase() === value.toLowerCase()))
1911
+ if (!fmValue.some((v) => String(v).toLowerCase() === value.toLowerCase()))
1784
1912
  return false;
1785
1913
  }
1786
1914
  else {
@@ -1812,9 +1940,7 @@ function sortBlogPosts(posts, sort) {
1812
1940
  }
1813
1941
  /** Build an <article> Tag for a single blog post entry */
1814
1942
  function createBlogPostTag(post) {
1815
- const titleTag = new Tag('h3', {}, [
1816
- new Tag('a', { href: post.url }, [post.title]),
1817
- ]);
1943
+ const titleTag = new Tag('h3', {}, [new Tag('a', { href: post.url }, [post.title])]);
1818
1944
  const children = [titleTag];
1819
1945
  if (post.date) {
1820
1946
  children.push(new Tag('time', { datetime: post.date }, [post.date]));
@@ -1843,7 +1969,7 @@ function resolveBlogPosts(renderable, allPosts, ctx, pageUrl) {
1843
1969
  }
1844
1970
  const normalised = normaliseFolderPath(folder);
1845
1971
  const filters = parseBlogFilter(filterStr);
1846
- let posts = allPosts.filter(post => {
1972
+ let posts = allPosts.filter((post) => {
1847
1973
  if (post.draft)
1848
1974
  return false;
1849
1975
  if (!isInFolder(post.url, normalised))
@@ -1890,9 +2016,7 @@ navSearchScope) {
1890
2016
  result = resolveNavSlugs(result, coreData.pagesByUrl, ctx, pageUrl);
1891
2017
  result = resolveCollapsibleNavs(result, pageUrl, coreData.pagesByUrl);
1892
2018
  result = resolveCardsNavs(result, pageUrl, coreData.pagesByUrl);
1893
- const searchRoot = navSearchScope && navSearchScope.length > 0
1894
- ? [result, ...navSearchScope]
1895
- : result;
2019
+ const searchRoot = navSearchScope && navSearchScope.length > 0 ? [result, ...navSearchScope] : result;
1896
2020
  result = resolveAutoPagination(result, pageUrl, coreData.pagesByUrl, searchRoot);
1897
2021
  // SPEC-055 — mark aria-current / data-active="ancestor" on resolved nav
1898
2022
  // links per page. Runs after slug + auto-pagination resolution so the
@@ -1918,9 +2042,15 @@ navSearchScope) {
1918
2042
  * any custom field.
1919
2043
  */
1920
2044
  const RESERVED_PAGE_FRONTMATTER = new Set([
1921
- 'layout', 'tint', 'tint-mode', 'tint-lock', 'slug', 'redirect',
2045
+ 'layout',
2046
+ 'tint',
2047
+ 'tint-mode',
2048
+ 'tint-lock',
2049
+ 'slug',
2050
+ 'redirect',
1922
2051
  // entity-declaration keys (SPEC-092 Layer 2) — meta, not queryable content
1923
- 'type', 'id',
2052
+ 'type',
2053
+ 'id',
1924
2054
  ]);
1925
2055
  export function createCorePipelineHooks(opts = {}) {
1926
2056
  const xrefPatterns = opts.xrefPatterns ?? [];
@@ -1941,7 +2071,7 @@ export function createCorePipelineHooks(opts = {}) {
1941
2071
  const includeChanged = preprocessIncludes(ast, page, ctx);
1942
2072
  const snippetChanged = preprocessSnippets(ast, page, ctx);
1943
2073
  const dataChanged = preprocessData(ast, page, ctx);
1944
- return (includeChanged || snippetChanged || dataChanged) ? ast : undefined;
2074
+ return includeChanged || snippetChanged || dataChanged ? ast : undefined;
1945
2075
  },
1946
2076
  register(pages, registry, ctx) {
1947
2077
  for (const page of pages) {
@@ -1995,7 +2125,7 @@ export function createCorePipelineHooks(opts = {}) {
1995
2125
  },
1996
2126
  aggregate(registry, ctx) {
1997
2127
  const pageEntities = registry.getAll('page');
1998
- const pages = pageEntities.map(e => ({
2128
+ const pages = pageEntities.map((e) => ({
1999
2129
  url: e.data.url,
2000
2130
  title: e.data.title,
2001
2131
  parentUrl: e.data.parentUrl,
@@ -2006,14 +2136,14 @@ export function createCorePipelineHooks(opts = {}) {
2006
2136
  const pageTree = buildPageTree(pages);
2007
2137
  const breadcrumbPaths = buildBreadcrumbPaths(pages);
2008
2138
  // Quick lookup: url → { url, title } for postProcess use
2009
- const pagesByUrl = new Map(pages.map(p => [p.url, p]));
2139
+ const pagesByUrl = new Map(pages.map((p) => [p.url, p]));
2010
2140
  // Build heading index: "url#id" → heading data
2011
2141
  const headingIndex = new Map();
2012
2142
  for (const h of registry.getAll('heading')) {
2013
2143
  headingIndex.set(h.id, h.data);
2014
2144
  }
2015
2145
  // Blog: collect all pages as potential blog posts
2016
- const allPosts = pageEntities.map(e => ({
2146
+ const allPosts = pageEntities.map((e) => ({
2017
2147
  title: e.data.title || '',
2018
2148
  url: e.data.url || e.id,
2019
2149
  date: e.data.date || '',
@@ -2021,7 +2151,18 @@ export function createCorePipelineHooks(opts = {}) {
2021
2151
  draft: e.data.draft || false,
2022
2152
  frontmatter: e.data,
2023
2153
  }));
2024
- return { pageTree, breadcrumbPaths, pagesByUrl, headingIndex, allPosts, registry, xrefPatterns, embedConfig, repoUrl, repoBranch };
2154
+ return {
2155
+ pageTree,
2156
+ breadcrumbPaths,
2157
+ pagesByUrl,
2158
+ headingIndex,
2159
+ allPosts,
2160
+ registry,
2161
+ xrefPatterns,
2162
+ embedConfig,
2163
+ repoUrl,
2164
+ repoBranch,
2165
+ };
2025
2166
  },
2026
2167
  postProcess(page, aggregated, ctx) {
2027
2168
  const coreData = aggregated['__core__'];