@valkyrianlabs/payload-markdown 1.3.1

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 (204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +222 -0
  3. package/dist/blocks/MarkdownBlock/Component.d.ts +2 -0
  4. package/dist/blocks/MarkdownBlock/Component.js +13 -0
  5. package/dist/blocks/MarkdownBlock/Component.js.map +1 -0
  6. package/dist/blocks/MarkdownBlock/config.d.ts +2 -0
  7. package/dist/blocks/MarkdownBlock/config.js +20 -0
  8. package/dist/blocks/MarkdownBlock/config.js.map +1 -0
  9. package/dist/blocks/MarkdownBlock/types.d.js +3 -0
  10. package/dist/blocks/MarkdownBlock/types.d.js.map +1 -0
  11. package/dist/components/MarkdownRenderer/Component.client.d.ts +5 -0
  12. package/dist/components/MarkdownRenderer/Component.client.js +163 -0
  13. package/dist/components/MarkdownRenderer/Component.client.js.map +1 -0
  14. package/dist/components/MarkdownRenderer/Component.d.ts +3 -0
  15. package/dist/components/MarkdownRenderer/Component.js +87 -0
  16. package/dist/components/MarkdownRenderer/Component.js.map +1 -0
  17. package/dist/components/MarkdownRenderer/index.css +69 -0
  18. package/dist/components/MarkdownRenderer/index.module.css +45 -0
  19. package/dist/components/MarkdownRenderer/index.module.scss +43 -0
  20. package/dist/components/MarkdownRenderer/index.scss +83 -0
  21. package/dist/components/MarkdownRenderer/types.d.js +5 -0
  22. package/dist/components/MarkdownRenderer/types.d.js.map +1 -0
  23. package/dist/core/codeConfig.d.ts +6 -0
  24. package/dist/core/codeConfig.js +45 -0
  25. package/dist/core/codeConfig.js.map +1 -0
  26. package/dist/core/codeToHtml.d.ts +5 -0
  27. package/dist/core/codeToHtml.js +180 -0
  28. package/dist/core/codeToHtml.js.map +1 -0
  29. package/dist/core/plugins/rehypeApplyLayoutClasses.d.ts +4 -0
  30. package/dist/core/plugins/rehypeApplyLayoutClasses.js +72 -0
  31. package/dist/core/plugins/rehypeApplyLayoutClasses.js.map +1 -0
  32. package/dist/core/plugins/rehypeStripAuthoredInlineStyles.d.ts +3 -0
  33. package/dist/core/plugins/rehypeStripAuthoredInlineStyles.js +17 -0
  34. package/dist/core/plugins/rehypeStripAuthoredInlineStyles.js.map +1 -0
  35. package/dist/core/plugins/remarkCompileLayouts.d.ts +3 -0
  36. package/dist/core/plugins/remarkCompileLayouts.js +169 -0
  37. package/dist/core/plugins/remarkCompileLayouts.js.map +1 -0
  38. package/dist/core/plugins/remarkHeadingAnchorsAndToc.d.ts +3 -0
  39. package/dist/core/plugins/remarkHeadingAnchorsAndToc.js +68 -0
  40. package/dist/core/plugins/remarkHeadingAnchorsAndToc.js.map +1 -0
  41. package/dist/core/plugins/remarkLayoutDirectives.d.ts +3 -0
  42. package/dist/core/plugins/remarkLayoutDirectives.js +26 -0
  43. package/dist/core/plugins/remarkLayoutDirectives.js.map +1 -0
  44. package/dist/core/plugins/remarkLayoutSentinels.d.ts +3 -0
  45. package/dist/core/plugins/remarkLayoutSentinels.js +184 -0
  46. package/dist/core/plugins/remarkLayoutSentinels.js.map +1 -0
  47. package/dist/core/plugins/remarkLiftLayoutDirectives.d.ts +3 -0
  48. package/dist/core/plugins/remarkLiftLayoutDirectives.js +93 -0
  49. package/dist/core/plugins/remarkLiftLayoutDirectives.js.map +1 -0
  50. package/dist/core/plugins/remarkNormalizeLayoutSyntax.d.ts +3 -0
  51. package/dist/core/plugins/remarkValidateDirectiveThemes.d.ts +4 -0
  52. package/dist/core/plugins/remarkValidateDirectiveThemes.js +28 -0
  53. package/dist/core/plugins/remarkValidateDirectiveThemes.js.map +1 -0
  54. package/dist/core/renderMarkdown.d.ts +2 -0
  55. package/dist/core/renderMarkdown.js +270 -0
  56. package/dist/core/renderMarkdown.js.map +1 -0
  57. package/dist/core/types.d.js +7 -0
  58. package/dist/core/types.d.js.map +1 -0
  59. package/dist/core/types.d.ts +238 -0
  60. package/dist/core/types.js +5 -0
  61. package/dist/core/types.js.map +1 -0
  62. package/dist/directives/attributes.d.ts +14 -0
  63. package/dist/directives/attributes.js +121 -0
  64. package/dist/directives/attributes.js.map +1 -0
  65. package/dist/directives/definitions/callout.d.ts +6 -0
  66. package/dist/directives/definitions/callout.js +107 -0
  67. package/dist/directives/definitions/callout.js.map +1 -0
  68. package/dist/directives/definitions/card.d.ts +4 -0
  69. package/dist/directives/definitions/card.js +120 -0
  70. package/dist/directives/definitions/card.js.map +1 -0
  71. package/dist/directives/definitions/cards.d.ts +7 -0
  72. package/dist/directives/definitions/cards.js +72 -0
  73. package/dist/directives/definitions/cards.js.map +1 -0
  74. package/dist/directives/definitions/cell.d.ts +4 -0
  75. package/dist/directives/definitions/cell.js +44 -0
  76. package/dist/directives/definitions/cell.js.map +1 -0
  77. package/dist/directives/definitions/columns.d.ts +4 -0
  78. package/dist/directives/definitions/columns.js +89 -0
  79. package/dist/directives/definitions/columns.js.map +1 -0
  80. package/dist/directives/definitions/details.d.ts +2 -0
  81. package/dist/directives/definitions/details.js +76 -0
  82. package/dist/directives/definitions/details.js.map +1 -0
  83. package/dist/directives/definitions/section.d.ts +2 -0
  84. package/dist/directives/definitions/section.js +32 -0
  85. package/dist/directives/definitions/section.js.map +1 -0
  86. package/dist/directives/definitions/steps.d.ts +2 -0
  87. package/dist/directives/definitions/steps.js +242 -0
  88. package/dist/directives/definitions/steps.js.map +1 -0
  89. package/dist/directives/definitions/tab.d.ts +8 -0
  90. package/dist/directives/definitions/tab.js +59 -0
  91. package/dist/directives/definitions/tab.js.map +1 -0
  92. package/dist/directives/definitions/tabs.d.ts +2 -0
  93. package/dist/directives/definitions/tabs.js +197 -0
  94. package/dist/directives/definitions/tabs.js.map +1 -0
  95. package/dist/directives/definitions/toc.d.ts +7 -0
  96. package/dist/directives/definitions/toc.js +59 -0
  97. package/dist/directives/definitions/toc.js.map +1 -0
  98. package/dist/directives/diagnostics.d.ts +8 -0
  99. package/dist/directives/diagnostics.js +167 -0
  100. package/dist/directives/diagnostics.js.map +1 -0
  101. package/dist/directives/headingAnchors.d.ts +9 -0
  102. package/dist/directives/headingAnchors.js +54 -0
  103. package/dist/directives/headingAnchors.js.map +1 -0
  104. package/dist/directives/index.d.ts +5 -0
  105. package/dist/directives/index.js +6 -0
  106. package/dist/directives/index.js.map +1 -0
  107. package/dist/directives/registry.d.ts +20 -0
  108. package/dist/directives/registry.js +152 -0
  109. package/dist/directives/registry.js.map +1 -0
  110. package/dist/directives/renderData.d.ts +3 -0
  111. package/dist/directives/renderData.js +11 -0
  112. package/dist/directives/renderData.js.map +1 -0
  113. package/dist/directives/themes.d.ts +25 -0
  114. package/dist/directives/themes.js +274 -0
  115. package/dist/directives/themes.js.map +1 -0
  116. package/dist/directives/types.d.ts +72 -0
  117. package/dist/directives/types.js +3 -0
  118. package/dist/directives/types.js.map +1 -0
  119. package/dist/editor/MarkdownCodeMirror/Component.client.d.ts +8 -0
  120. package/dist/editor/MarkdownCodeMirror/Component.client.js +78 -0
  121. package/dist/editor/MarkdownCodeMirror/Component.client.js.map +1 -0
  122. package/dist/editor/MarkdownCodeMirror/Component.d.ts +9 -0
  123. package/dist/editor/MarkdownCodeMirror/Component.js +10 -0
  124. package/dist/editor/MarkdownCodeMirror/Component.js.map +1 -0
  125. package/dist/editor/MarkdownCodeMirror.d.ts +8 -0
  126. package/dist/editor/MarkdownCodeMirror.js +74 -0
  127. package/dist/editor/MarkdownCodeMirror.js.map +1 -0
  128. package/dist/editor/MarkdownEditor.d.ts +10 -0
  129. package/dist/editor/MarkdownEditor.js +22 -0
  130. package/dist/editor/MarkdownEditor.js.map +1 -0
  131. package/dist/editor/directives/completions.d.ts +5 -0
  132. package/dist/editor/directives/completions.js +93 -0
  133. package/dist/editor/directives/completions.js.map +1 -0
  134. package/dist/editor/directives/diagnostics.d.ts +1 -0
  135. package/dist/editor/directives/diagnostics.js +12 -0
  136. package/dist/editor/directives/diagnostics.js.map +1 -0
  137. package/dist/editor/themes/payload.d.ts +27 -0
  138. package/dist/editor/themes/payload.js +269 -0
  139. package/dist/editor/themes/payload.js.map +1 -0
  140. package/dist/editor/themes/support/highlighters.d.ts +20 -0
  141. package/dist/editor/themes/support/highlighters.js +1141 -0
  142. package/dist/editor/themes/support/highlighters.js.map +1 -0
  143. package/dist/editor/themes/support/lang.d.ts +40 -0
  144. package/dist/editor/themes/support/lang.js +201 -0
  145. package/dist/editor/themes/support/lang.js.map +1 -0
  146. package/dist/exports/advanced.d.ts +3 -0
  147. package/dist/exports/advanced.js +5 -0
  148. package/dist/exports/advanced.js.map +1 -0
  149. package/dist/exports/client.d.ts +1 -0
  150. package/dist/exports/client.js +2 -0
  151. package/dist/exports/client.js.map +1 -0
  152. package/dist/exports/server.d.ts +3 -0
  153. package/dist/exports/server.js +5 -0
  154. package/dist/exports/server.js.map +1 -0
  155. package/dist/field/BlocksParams/config.d.ts +7 -0
  156. package/dist/field/BlocksParams/config.js +149 -0
  157. package/dist/field/BlocksParams/config.js.map +1 -0
  158. package/dist/field/CodeBlock/config.d.ts +7 -0
  159. package/dist/field/CodeBlock/config.js +321 -0
  160. package/dist/field/CodeBlock/config.js.map +1 -0
  161. package/dist/field/CodeBlockConfig/config.d.ts +7 -0
  162. package/dist/field/CodeBlockConfig/config.js +306 -0
  163. package/dist/field/CodeBlockConfig/config.js.map +1 -0
  164. package/dist/field/CodeBlockParams/config.d.ts +7 -0
  165. package/dist/field/CodeBlockParams/config.js +321 -0
  166. package/dist/field/CodeBlockParams/config.js.map +1 -0
  167. package/dist/field/Config/config.d.ts +7 -0
  168. package/dist/field/Config/config.js +149 -0
  169. package/dist/field/Config/config.js.map +1 -0
  170. package/dist/field/MarkdownField/Component.d.ts +2 -0
  171. package/dist/field/MarkdownField/Component.js +42 -0
  172. package/dist/field/MarkdownField/Component.js.map +1 -0
  173. package/dist/field/MarkdownField/config.d.ts +3 -0
  174. package/dist/field/MarkdownField/config.js +20 -0
  175. package/dist/field/MarkdownField/config.js.map +1 -0
  176. package/dist/field/Tailwind/config.d.ts +9 -0
  177. package/dist/field/Tailwind/config.js +13 -0
  178. package/dist/field/Tailwind/config.js.map +1 -0
  179. package/dist/field/TailwindField/config.d.ts +9 -0
  180. package/dist/field/TailwindField/config.js +13 -0
  181. package/dist/field/TailwindField/config.js.map +1 -0
  182. package/dist/index.d.ts +10 -0
  183. package/dist/index.js +91 -0
  184. package/dist/index.js.map +1 -0
  185. package/dist/runtime/index.d.ts +36 -0
  186. package/dist/runtime/index.js +124 -0
  187. package/dist/runtime/index.js.map +1 -0
  188. package/dist/styles/directiveSurface.d.ts +11 -0
  189. package/dist/styles/directiveSurface.js +38 -0
  190. package/dist/styles/directiveSurface.js.map +1 -0
  191. package/dist/types/core.d.ts +285 -0
  192. package/dist/types/core.js +5 -0
  193. package/dist/types/core.js.map +1 -0
  194. package/dist/types/layoutToken.d.ts +1 -0
  195. package/dist/types/layoutToken.js +3 -0
  196. package/dist/types/layoutToken.js.map +1 -0
  197. package/dist/types/mdast.d.js +5 -0
  198. package/dist/types/mdast.d.js.map +1 -0
  199. package/dist/types.d.js +3 -0
  200. package/dist/types.d.js.map +1 -0
  201. package/dist/types.d.ts +80 -0
  202. package/dist/types.js +3 -0
  203. package/dist/types.js.map +1 -0
  204. package/package.json +181 -0
@@ -0,0 +1,197 @@
1
+ import { DIRECTIVE_SURFACE_DIVIDER_CLASS } from '../../styles/directiveSurface.js';
2
+ import { resolveDirectiveTheme, slugThemeName } from '../themes.js';
3
+ import { getTabValueFromAttributes } from './tab.js';
4
+ function isContainerDirective(node) {
5
+ return Boolean(node && typeof node === 'object' && 'type' in node && node.type === 'containerDirective');
6
+ }
7
+ function isTabElement(node) {
8
+ return node.type === 'element' && node.properties?.dataVlLayout === 'tab';
9
+ }
10
+ function getStringProperty(properties, name) {
11
+ const value = properties?.[name];
12
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
13
+ }
14
+ function getBooleanProperty(properties, name) {
15
+ const value = properties?.[name];
16
+ return value === true || value === 'true';
17
+ }
18
+ function getTabLabel(node, index) {
19
+ return getStringProperty(node.properties, 'dataLabel') ?? getStringProperty(node.properties, 'dataValue') ?? `Tab ${index + 1}`;
20
+ }
21
+ function getRequestedDefault(node) {
22
+ return getStringProperty(node.properties, 'dataDefault');
23
+ }
24
+ function makeUniqueValue(rawValue, seen) {
25
+ const base = slugThemeName(rawValue);
26
+ const count = seen.get(base) ?? 0;
27
+ seen.set(base, count + 1);
28
+ return count === 0 ? base : `${base}-${count}`;
29
+ }
30
+ function collectTabs(node) {
31
+ const seen = new Map();
32
+ return node.children.filter(isTabElement).map((child, index)=>{
33
+ const rawValue = getStringProperty(child.properties, 'dataValue') ?? `tab-${index + 1}`;
34
+ const value = makeUniqueValue(rawValue, seen);
35
+ return {
36
+ disabled: getBooleanProperty(child.properties, 'dataDisabled'),
37
+ label: getTabLabel(child, index),
38
+ node: child,
39
+ panelId: `tabs-panel-${value}`,
40
+ themeName: getStringProperty(child.properties, 'dataTheme'),
41
+ triggerId: `tabs-trigger-${value}`,
42
+ value
43
+ };
44
+ });
45
+ }
46
+ function makeTabTrigger(tab, active) {
47
+ const className = [
48
+ 'vl-md-tabs-trigger',
49
+ active ? 'vl-md-tabs-trigger--active' : undefined,
50
+ 'rounded-lg px-3 py-2 text-sm font-medium text-foreground/70 transition-colors hover:text-foreground aria-selected:bg-white/10 aria-selected:text-foreground'
51
+ ].filter((value)=>Boolean(value));
52
+ return {
53
+ type: 'element',
54
+ children: [
55
+ {
56
+ type: 'text',
57
+ value: tab.label
58
+ }
59
+ ],
60
+ properties: {
61
+ id: tab.triggerId,
62
+ type: 'button',
63
+ ariaControls: tab.panelId,
64
+ ariaSelected: active ? 'true' : 'false',
65
+ className,
66
+ dataTabTrigger: '',
67
+ dataTabValue: tab.value,
68
+ disabled: tab.disabled || undefined,
69
+ role: 'tab',
70
+ tabIndex: active ? 0 : -1
71
+ },
72
+ tagName: 'button'
73
+ };
74
+ }
75
+ function makeTabPanel(tab, active, config, mergeClassNames, fallbackThemeName) {
76
+ const theme = resolveDirectiveTheme('tab', tab.themeName ?? fallbackThemeName, config.themes);
77
+ return {
78
+ type: 'element',
79
+ children: tab.node.children,
80
+ properties: {
81
+ id: tab.panelId,
82
+ ariaLabelledby: tab.triggerId,
83
+ className: mergeClassNames('vl-md-tabs-panel', theme.hookClassName, theme.modifierClassName, theme.classes, '[&>:first-child]:mt-0 [&>:last-child]:mb-0'),
84
+ dataTabPanel: '',
85
+ dataTabValue: tab.value,
86
+ dataTheme: theme.name,
87
+ hidden: active ? undefined : true,
88
+ role: 'tabpanel',
89
+ tabIndex: 0
90
+ },
91
+ tagName: 'div'
92
+ };
93
+ }
94
+ function makeTabList(tabs, activeValue) {
95
+ return {
96
+ type: 'element',
97
+ children: tabs.map((tab)=>makeTabTrigger(tab, tab.value === activeValue)),
98
+ properties: {
99
+ className: [
100
+ `vl-md-tabs-list flex flex-wrap gap-1 border-b ${DIRECTIVE_SURFACE_DIVIDER_CLASS} pb-2`
101
+ ],
102
+ dataTabsList: '',
103
+ role: 'tablist'
104
+ },
105
+ tagName: 'div'
106
+ };
107
+ }
108
+ function findActiveTab(tabs, requestedDefault) {
109
+ const requested = requestedDefault ? slugThemeName(requestedDefault) : undefined;
110
+ const requestedMatch = requested ? tabs.find((tab)=>tab.value === requested && !tab.disabled) ?? tabs.find((tab)=>tab.value === requested) : undefined;
111
+ return requestedMatch ?? tabs.find((tab)=>!tab.disabled) ?? tabs[0];
112
+ }
113
+ function getDirectTabDirectives(node) {
114
+ return node.children.filter((child)=>isContainerDirective(child) && child.name === 'tab');
115
+ }
116
+ export const tabsDirective = {
117
+ name: 'tabs',
118
+ allowedAttributes: [
119
+ 'default',
120
+ 'tabTheme',
121
+ 'theme'
122
+ ],
123
+ applyHast (node, config, { mergeClassNames }) {
124
+ const tabs = collectTabs(node);
125
+ const activeTab = findActiveTab(tabs, getRequestedDefault(node));
126
+ const theme = resolveDirectiveTheme('tabs', typeof node.properties.dataTheme === 'string' ? node.properties.dataTheme : undefined, config.themes);
127
+ const tabTheme = typeof node.properties.dataTabTheme === 'string' ? node.properties.dataTabTheme : undefined;
128
+ node.properties.dataTheme = theme.name;
129
+ node.properties.dataDefault = activeTab?.value;
130
+ node.properties.className = mergeClassNames('not-prose', theme.hookClassName, theme.modifierClassName, theme.classes);
131
+ if (!activeTab) {
132
+ node.children = [
133
+ {
134
+ type: 'element',
135
+ children: [
136
+ {
137
+ type: 'text',
138
+ value: 'No tabs configured.'
139
+ }
140
+ ],
141
+ properties: {
142
+ className: [
143
+ 'text-sm text-muted-foreground'
144
+ ]
145
+ },
146
+ tagName: 'p'
147
+ }
148
+ ];
149
+ return;
150
+ }
151
+ node.children = [
152
+ makeTabList(tabs, activeTab.value),
153
+ ...tabs.map((tab)=>makeTabPanel(tab, tab.value === activeTab.value, config, mergeClassNames, tabTheme))
154
+ ];
155
+ },
156
+ description: 'Accessible tabbed content block with server-rendered panels.',
157
+ editor: {
158
+ detail: 'Tabs directive',
159
+ label: 'Tabs',
160
+ snippet: ':::tabs {default="${pnpm}"}\n\n:::tab {label="${pnpm}" value="${pnpm}"}\n```bash\npnpm add ${package-name}\n```\n:::\n\n:::tab {label="${npm}" value="${npm}"}\n```bash\nnpm install ${package-name}\n```\n:::\n\n:::\n${}'
161
+ },
162
+ getMdastRenderProperties (node) {
163
+ return {
164
+ dataDefault: typeof node.attributes?.default === 'string' ? node.attributes.default : undefined,
165
+ dataDirective: 'tabs',
166
+ dataTabTheme: typeof node.attributes?.tabTheme === 'string' ? node.attributes.tabTheme : undefined,
167
+ dataTheme: typeof node.attributes?.theme === 'string' ? node.attributes.theme : 'default'
168
+ };
169
+ },
170
+ kind: 'tabs',
171
+ openMarker: ':::tabs',
172
+ public: true,
173
+ supportsAttributes: true,
174
+ tagName: 'section',
175
+ themeAttributes: {
176
+ tabTheme: 'tab',
177
+ theme: 'tabs'
178
+ },
179
+ validateMdast (node) {
180
+ const warnings = [];
181
+ const tabs = getDirectTabDirectives(node);
182
+ const values = new Map();
183
+ if (tabs.length === 0) warnings.push('Directive "tabs" has no child "tab" directives.');
184
+ tabs.forEach((tab, index)=>{
185
+ const value = getTabValueFromAttributes(tab.attributes, index);
186
+ values.set(value, (values.get(value) ?? 0) + 1);
187
+ });
188
+ for (const [value, count] of values)if (count > 1) warnings.push(`Duplicate tab value "${value}" in "tabs".`);
189
+ if (typeof node.attributes?.default === 'string') {
190
+ const requested = slugThemeName(node.attributes.default);
191
+ if (!values.has(requested)) warnings.push(`Invalid tabs default "${node.attributes.default}". Falling back to the first tab.`);
192
+ }
193
+ return warnings;
194
+ }
195
+ };
196
+
197
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/directives/definitions/tabs.ts"],"sourcesContent":["import type { Element, ElementContent } from 'hast'\nimport type { ContainerDirective } from 'mdast-util-directive'\n\nimport type { LayoutDirectiveDefinition } from '../types.js'\n\nimport { DIRECTIVE_SURFACE_DIVIDER_CLASS } from '../../styles/directiveSurface.js'\nimport { resolveDirectiveTheme, slugThemeName } from '../themes.js'\nimport { getTabValueFromAttributes } from './tab.js'\n\ntype TabModel = {\n disabled: boolean\n label: string\n node: Element\n panelId: string\n themeName?: string\n triggerId: string\n value: string\n}\n\nfunction isContainerDirective(node: unknown): node is ContainerDirective {\n return Boolean(\n node &&\n typeof node === 'object' &&\n 'type' in node &&\n (node as { type?: string }).type === 'containerDirective',\n )\n}\n\nfunction isTabElement(node: ElementContent): node is Element {\n return node.type === 'element' && node.properties?.dataVlLayout === 'tab'\n}\n\nfunction getStringProperty(\n properties: Element['properties'] | undefined,\n name: string,\n): string | undefined {\n const value = properties?.[name]\n\n return typeof value === 'string' && value.trim() ? value.trim() : undefined\n}\n\nfunction getBooleanProperty(properties: Element['properties'] | undefined, name: string): boolean {\n const value = properties?.[name]\n\n return value === true || value === 'true'\n}\n\nfunction getTabLabel(node: Element, index: number): string {\n return (\n getStringProperty(node.properties, 'dataLabel') ??\n getStringProperty(node.properties, 'dataValue') ??\n `Tab ${index + 1}`\n )\n}\n\nfunction getRequestedDefault(node: Element): string | undefined {\n return getStringProperty(node.properties, 'dataDefault')\n}\n\nfunction makeUniqueValue(rawValue: string, seen: Map<string, number>): string {\n const base = slugThemeName(rawValue)\n const count = seen.get(base) ?? 0\n seen.set(base, count + 1)\n\n return count === 0 ? base : `${base}-${count}`\n}\n\nfunction collectTabs(node: Element): TabModel[] {\n const seen = new Map<string, number>()\n\n return node.children.filter(isTabElement).map((child, index) => {\n const rawValue = getStringProperty(child.properties, 'dataValue') ?? `tab-${index + 1}`\n const value = makeUniqueValue(rawValue, seen)\n\n return {\n disabled: getBooleanProperty(child.properties, 'dataDisabled'),\n label: getTabLabel(child, index),\n node: child,\n panelId: `tabs-panel-${value}`,\n themeName: getStringProperty(child.properties, 'dataTheme'),\n triggerId: `tabs-trigger-${value}`,\n value,\n }\n })\n}\n\nfunction makeTabTrigger(tab: TabModel, active: boolean): Element {\n const className = [\n 'vl-md-tabs-trigger',\n active ? 'vl-md-tabs-trigger--active' : undefined,\n 'rounded-lg px-3 py-2 text-sm font-medium text-foreground/70 transition-colors hover:text-foreground aria-selected:bg-white/10 aria-selected:text-foreground',\n ].filter((value): value is string => Boolean(value))\n\n return {\n type: 'element',\n children: [{ type: 'text', value: tab.label }],\n properties: {\n id: tab.triggerId,\n type: 'button',\n ariaControls: tab.panelId,\n ariaSelected: active ? 'true' : 'false',\n className,\n dataTabTrigger: '',\n dataTabValue: tab.value,\n disabled: tab.disabled || undefined,\n role: 'tab',\n tabIndex: active ? 0 : -1,\n },\n tagName: 'button',\n }\n}\n\nfunction makeTabPanel(\n tab: TabModel,\n active: boolean,\n config: Parameters<NonNullable<LayoutDirectiveDefinition['applyHast']>>[1],\n mergeClassNames: Parameters<NonNullable<LayoutDirectiveDefinition['applyHast']>>[2]['mergeClassNames'],\n fallbackThemeName?: string,\n): Element {\n const theme = resolveDirectiveTheme('tab', tab.themeName ?? fallbackThemeName, config.themes)\n\n return {\n type: 'element',\n children: tab.node.children,\n properties: {\n id: tab.panelId,\n ariaLabelledby: tab.triggerId,\n className: mergeClassNames(\n 'vl-md-tabs-panel',\n theme.hookClassName,\n theme.modifierClassName,\n theme.classes,\n '[&>:first-child]:mt-0 [&>:last-child]:mb-0',\n ),\n dataTabPanel: '',\n dataTabValue: tab.value,\n dataTheme: theme.name,\n hidden: active ? undefined : true,\n role: 'tabpanel',\n tabIndex: 0,\n },\n tagName: 'div',\n }\n}\n\nfunction makeTabList(tabs: TabModel[], activeValue: string): Element {\n return {\n type: 'element',\n children: tabs.map((tab) => makeTabTrigger(tab, tab.value === activeValue)),\n properties: {\n className: [`vl-md-tabs-list flex flex-wrap gap-1 border-b ${DIRECTIVE_SURFACE_DIVIDER_CLASS} pb-2`],\n dataTabsList: '',\n role: 'tablist',\n },\n tagName: 'div',\n }\n}\n\nfunction findActiveTab(tabs: TabModel[], requestedDefault?: string): TabModel | undefined {\n const requested = requestedDefault ? slugThemeName(requestedDefault) : undefined\n const requestedMatch = requested\n ? tabs.find((tab) => tab.value === requested && !tab.disabled) ??\n tabs.find((tab) => tab.value === requested)\n : undefined\n\n return requestedMatch ?? tabs.find((tab) => !tab.disabled) ?? tabs[0]\n}\n\nfunction getDirectTabDirectives(node: ContainerDirective): ContainerDirective[] {\n return node.children.filter(\n (child): child is ContainerDirective => isContainerDirective(child) && child.name === 'tab',\n )\n}\n\nexport const tabsDirective: LayoutDirectiveDefinition = {\n name: 'tabs',\n allowedAttributes: ['default', 'tabTheme', 'theme'],\n applyHast(node, config, { mergeClassNames }) {\n const tabs = collectTabs(node)\n const activeTab = findActiveTab(tabs, getRequestedDefault(node))\n const theme = resolveDirectiveTheme(\n 'tabs',\n typeof node.properties.dataTheme === 'string' ? node.properties.dataTheme : undefined,\n config.themes,\n )\n const tabTheme =\n typeof node.properties.dataTabTheme === 'string' ? node.properties.dataTabTheme : undefined\n\n node.properties.dataTheme = theme.name\n node.properties.dataDefault = activeTab?.value\n node.properties.className = mergeClassNames(\n 'not-prose',\n theme.hookClassName,\n theme.modifierClassName,\n theme.classes,\n )\n\n if (!activeTab) {\n node.children = [\n {\n type: 'element',\n children: [{ type: 'text', value: 'No tabs configured.' }],\n properties: {\n className: ['text-sm text-muted-foreground'],\n },\n tagName: 'p',\n },\n ]\n return\n }\n\n node.children = [\n makeTabList(tabs, activeTab.value),\n ...tabs.map((tab) =>\n makeTabPanel(tab, tab.value === activeTab.value, config, mergeClassNames, tabTheme),\n ),\n ]\n },\n description: 'Accessible tabbed content block with server-rendered panels.',\n editor: {\n detail: 'Tabs directive',\n label: 'Tabs',\n snippet:\n ':::tabs {default=\"${pnpm}\"}\\n\\n:::tab {label=\"${pnpm}\" value=\"${pnpm}\"}\\n```bash\\npnpm add ${package-name}\\n```\\n:::\\n\\n:::tab {label=\"${npm}\" value=\"${npm}\"}\\n```bash\\nnpm install ${package-name}\\n```\\n:::\\n\\n:::\\n${}',\n },\n getMdastRenderProperties(node) {\n return {\n dataDefault:\n typeof node.attributes?.default === 'string' ? node.attributes.default : undefined,\n dataDirective: 'tabs',\n dataTabTheme:\n typeof node.attributes?.tabTheme === 'string' ? node.attributes.tabTheme : undefined,\n dataTheme: typeof node.attributes?.theme === 'string' ? node.attributes.theme : 'default',\n }\n },\n kind: 'tabs',\n openMarker: ':::tabs',\n public: true,\n supportsAttributes: true,\n tagName: 'section',\n themeAttributes: {\n tabTheme: 'tab',\n theme: 'tabs',\n },\n validateMdast(node) {\n const warnings: string[] = []\n const tabs = getDirectTabDirectives(node)\n const values = new Map<string, number>()\n\n if (tabs.length === 0) warnings.push('Directive \"tabs\" has no child \"tab\" directives.')\n\n tabs.forEach((tab, index) => {\n const value = getTabValueFromAttributes(tab.attributes, index)\n values.set(value, (values.get(value) ?? 0) + 1)\n })\n\n for (const [value, count] of values)\n if (count > 1) warnings.push(`Duplicate tab value \"${value}\" in \"tabs\".`)\n\n if (typeof node.attributes?.default === 'string') {\n const requested = slugThemeName(node.attributes.default)\n if (!values.has(requested))\n warnings.push(`Invalid tabs default \"${node.attributes.default}\". Falling back to the first tab.`)\n }\n\n return warnings\n },\n}\n"],"names":["DIRECTIVE_SURFACE_DIVIDER_CLASS","resolveDirectiveTheme","slugThemeName","getTabValueFromAttributes","isContainerDirective","node","Boolean","type","isTabElement","properties","dataVlLayout","getStringProperty","name","value","trim","undefined","getBooleanProperty","getTabLabel","index","getRequestedDefault","makeUniqueValue","rawValue","seen","base","count","get","set","collectTabs","Map","children","filter","map","child","disabled","label","panelId","themeName","triggerId","makeTabTrigger","tab","active","className","id","ariaControls","ariaSelected","dataTabTrigger","dataTabValue","role","tabIndex","tagName","makeTabPanel","config","mergeClassNames","fallbackThemeName","theme","themes","ariaLabelledby","hookClassName","modifierClassName","classes","dataTabPanel","dataTheme","hidden","makeTabList","tabs","activeValue","dataTabsList","findActiveTab","requestedDefault","requested","requestedMatch","find","getDirectTabDirectives","tabsDirective","allowedAttributes","applyHast","activeTab","tabTheme","dataTabTheme","dataDefault","description","editor","detail","snippet","getMdastRenderProperties","attributes","default","dataDirective","kind","openMarker","public","supportsAttributes","themeAttributes","validateMdast","warnings","values","length","push","forEach","has"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ContainerDirective } from 'mdast-util-directive';
2
+ import type { LayoutDirectiveDefinition } from '../types.js';
3
+ export declare const DEFAULT_TOC_DEPTH = 3;
4
+ export declare const DEFAULT_TOC_TITLE = "On this page";
5
+ export declare function resolveTocDepth(node: ContainerDirective): number;
6
+ export declare function resolveTocTitle(node: ContainerDirective): string;
7
+ export declare const tocDirective: LayoutDirectiveDefinition;
@@ -0,0 +1,59 @@
1
+ import { resolveDirectiveTheme } from '../themes.js';
2
+ export const DEFAULT_TOC_DEPTH = 3;
3
+ export const DEFAULT_TOC_TITLE = 'On this page';
4
+ export function resolveTocDepth(node) {
5
+ const value = node.attributes?.depth;
6
+ const parsed = typeof value === 'string' ? Number.parseInt(value, 10) : DEFAULT_TOC_DEPTH;
7
+ return Number.isInteger(parsed) && parsed >= 1 && parsed <= 6 ? parsed : DEFAULT_TOC_DEPTH;
8
+ }
9
+ export function resolveTocTitle(node) {
10
+ const title = node.attributes?.title;
11
+ return typeof title === 'string' && title.trim() ? title.trim() : DEFAULT_TOC_TITLE;
12
+ }
13
+ export const tocDirective = {
14
+ name: 'toc',
15
+ allowedAttributes: [
16
+ 'depth',
17
+ 'theme',
18
+ 'title'
19
+ ],
20
+ applyHast (node, config, { mergeClassNames }) {
21
+ const theme = resolveDirectiveTheme('toc', typeof node.properties.dataTheme === 'string' ? node.properties.dataTheme : undefined, config.themes);
22
+ node.properties.dataTheme = theme.name;
23
+ node.properties.className = mergeClassNames('not-prose', theme.hookClassName, theme.modifierClassName, theme.classes);
24
+ },
25
+ defaultAttributes: {
26
+ depth: String(DEFAULT_TOC_DEPTH),
27
+ title: DEFAULT_TOC_TITLE
28
+ },
29
+ description: 'Table of contents generated from document headings.',
30
+ editor: {
31
+ detail: 'Docs directive',
32
+ label: 'Table of contents',
33
+ snippet: ':::toc {title="${On this page}" depth="${3}"}\n:::\n${}'
34
+ },
35
+ getMdastRenderProperties (node) {
36
+ const title = resolveTocTitle(node);
37
+ return {
38
+ ariaLabel: title,
39
+ dataDirective: 'toc',
40
+ dataTheme: typeof node.attributes?.theme === 'string' ? node.attributes.theme : 'default',
41
+ dataTitle: title
42
+ };
43
+ },
44
+ kind: 'toc',
45
+ openMarker: ':::toc',
46
+ public: true,
47
+ supportsAttributes: true,
48
+ tagName: 'nav',
49
+ themeAttributes: {
50
+ theme: 'toc'
51
+ },
52
+ validateAttributes ({ attributes }) {
53
+ const warnings = [];
54
+ if (typeof attributes.depth === 'string' && (!Number.isInteger(Number.parseInt(attributes.depth, 10)) || Number.parseInt(attributes.depth, 10) < 1 || Number.parseInt(attributes.depth, 10) > 6)) warnings.push(`Invalid toc depth "${attributes.depth}". Falling back to "${DEFAULT_TOC_DEPTH}".`);
55
+ return warnings;
56
+ }
57
+ };
58
+
59
+ //# sourceMappingURL=toc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/directives/definitions/toc.ts"],"sourcesContent":["import type { ContainerDirective } from 'mdast-util-directive'\n\nimport type { LayoutDirectiveDefinition } from '../types.js'\n\nimport { resolveDirectiveTheme } from '../themes.js'\n\nexport const DEFAULT_TOC_DEPTH = 3\nexport const DEFAULT_TOC_TITLE = 'On this page'\n\nexport function resolveTocDepth(node: ContainerDirective): number {\n const value = node.attributes?.depth\n const parsed = typeof value === 'string' ? Number.parseInt(value, 10) : DEFAULT_TOC_DEPTH\n\n return Number.isInteger(parsed) && parsed >= 1 && parsed <= 6 ? parsed : DEFAULT_TOC_DEPTH\n}\n\nexport function resolveTocTitle(node: ContainerDirective): string {\n const title = node.attributes?.title\n\n return typeof title === 'string' && title.trim() ? title.trim() : DEFAULT_TOC_TITLE\n}\n\nexport const tocDirective: LayoutDirectiveDefinition = {\n name: 'toc',\n allowedAttributes: ['depth', 'theme', 'title'],\n applyHast(node, config, { mergeClassNames }) {\n const theme = resolveDirectiveTheme(\n 'toc',\n typeof node.properties.dataTheme === 'string' ? node.properties.dataTheme : undefined,\n config.themes,\n )\n\n node.properties.dataTheme = theme.name\n node.properties.className = mergeClassNames(\n 'not-prose',\n theme.hookClassName,\n theme.modifierClassName,\n theme.classes,\n )\n },\n defaultAttributes: {\n depth: String(DEFAULT_TOC_DEPTH),\n title: DEFAULT_TOC_TITLE,\n },\n description: 'Table of contents generated from document headings.',\n editor: {\n detail: 'Docs directive',\n label: 'Table of contents',\n snippet: ':::toc {title=\"${On this page}\" depth=\"${3}\"}\\n:::\\n${}',\n },\n getMdastRenderProperties(node) {\n const title = resolveTocTitle(node)\n\n return {\n ariaLabel: title,\n dataDirective: 'toc',\n dataTheme: typeof node.attributes?.theme === 'string' ? node.attributes.theme : 'default',\n dataTitle: title,\n }\n },\n kind: 'toc',\n openMarker: ':::toc',\n public: true,\n supportsAttributes: true,\n tagName: 'nav',\n themeAttributes: {\n theme: 'toc',\n },\n validateAttributes({ attributes }) {\n const warnings: string[] = []\n\n if (\n typeof attributes.depth === 'string' &&\n (!Number.isInteger(Number.parseInt(attributes.depth, 10)) ||\n Number.parseInt(attributes.depth, 10) < 1 ||\n Number.parseInt(attributes.depth, 10) > 6)\n )\n warnings.push(`Invalid toc depth \"${attributes.depth}\". Falling back to \"${DEFAULT_TOC_DEPTH}\".`)\n\n return warnings\n },\n}\n"],"names":["resolveDirectiveTheme","DEFAULT_TOC_DEPTH","DEFAULT_TOC_TITLE","resolveTocDepth","node","value","attributes","depth","parsed","Number","parseInt","isInteger","resolveTocTitle","title","trim","tocDirective","name","allowedAttributes","applyHast","config","mergeClassNames","theme","properties","dataTheme","undefined","themes","className","hookClassName","modifierClassName","classes","defaultAttributes","String","description","editor","detail","label","snippet","getMdastRenderProperties","ariaLabel","dataDirective","dataTitle","kind","openMarker","public","supportsAttributes","tagName","themeAttributes","validateAttributes","warnings","push"],"mappings":"AAIA,SAASA,qBAAqB,QAAQ,eAAc;AAEpD,OAAO,MAAMC,oBAAoB,EAAC;AAClC,OAAO,MAAMC,oBAAoB,eAAc;AAE/C,OAAO,SAASC,gBAAgBC,IAAwB;IACtD,MAAMC,QAAQD,KAAKE,UAAU,EAAEC;IAC/B,MAAMC,SAAS,OAAOH,UAAU,WAAWI,OAAOC,QAAQ,CAACL,OAAO,MAAMJ;IAExE,OAAOQ,OAAOE,SAAS,CAACH,WAAWA,UAAU,KAAKA,UAAU,IAAIA,SAASP;AAC3E;AAEA,OAAO,SAASW,gBAAgBR,IAAwB;IACtD,MAAMS,QAAQT,KAAKE,UAAU,EAAEO;IAE/B,OAAO,OAAOA,UAAU,YAAYA,MAAMC,IAAI,KAAKD,MAAMC,IAAI,KAAKZ;AACpE;AAEA,OAAO,MAAMa,eAA0C;IACrDC,MAAM;IACNC,mBAAmB;QAAC;QAAS;QAAS;KAAQ;IAC9CC,WAAUd,IAAI,EAAEe,MAAM,EAAE,EAAEC,eAAe,EAAE;QACzC,MAAMC,QAAQrB,sBACZ,OACA,OAAOI,KAAKkB,UAAU,CAACC,SAAS,KAAK,WAAWnB,KAAKkB,UAAU,CAACC,SAAS,GAAGC,WAC5EL,OAAOM,MAAM;QAGfrB,KAAKkB,UAAU,CAACC,SAAS,GAAGF,MAAML,IAAI;QACtCZ,KAAKkB,UAAU,CAACI,SAAS,GAAGN,gBAC1B,aACAC,MAAMM,aAAa,EACnBN,MAAMO,iBAAiB,EACvBP,MAAMQ,OAAO;IAEjB;IACAC,mBAAmB;QACjBvB,OAAOwB,OAAO9B;QACdY,OAAOX;IACT;IACA8B,aAAa;IACbC,QAAQ;QACNC,QAAQ;QACRC,OAAO;QACPC,SAAS;IACX;IACAC,0BAAyBjC,IAAI;QAC3B,MAAMS,QAAQD,gBAAgBR;QAE9B,OAAO;YACLkC,WAAWzB;YACX0B,eAAe;YACfhB,WAAW,OAAOnB,KAAKE,UAAU,EAAEe,UAAU,WAAWjB,KAAKE,UAAU,CAACe,KAAK,GAAG;YAChFmB,WAAW3B;QACb;IACF;IACA4B,MAAM;IACNC,YAAY;IACZC,QAAQ;IACRC,oBAAoB;IACpBC,SAAS;IACTC,iBAAiB;QACfzB,OAAO;IACT;IACA0B,oBAAmB,EAAEzC,UAAU,EAAE;QAC/B,MAAM0C,WAAqB,EAAE;QAE7B,IACE,OAAO1C,WAAWC,KAAK,KAAK,YAC3B,CAAA,CAACE,OAAOE,SAAS,CAACF,OAAOC,QAAQ,CAACJ,WAAWC,KAAK,EAAE,QACnDE,OAAOC,QAAQ,CAACJ,WAAWC,KAAK,EAAE,MAAM,KACxCE,OAAOC,QAAQ,CAACJ,WAAWC,KAAK,EAAE,MAAM,CAAA,GAE1CyC,SAASC,IAAI,CAAC,CAAC,mBAAmB,EAAE3C,WAAWC,KAAK,CAAC,oBAAoB,EAAEN,kBAAkB,EAAE,CAAC;QAElG,OAAO+C;IACT;AACF,EAAC"}
@@ -0,0 +1,8 @@
1
+ export type DirectiveDiagnostic = {
2
+ from: number;
3
+ line: number;
4
+ message: string;
5
+ severity: 'warning';
6
+ to: number;
7
+ };
8
+ export declare function lintMarkdownDirectives(markdown: string): DirectiveDiagnostic[];
@@ -0,0 +1,167 @@
1
+ import { layoutDirectiveRegistry } from './registry.js';
2
+ import { hasDirectiveTheme } from './themes.js';
3
+ function isFenceLine(trimmed) {
4
+ return trimmed.startsWith('```') || trimmed.startsWith('~~~');
5
+ }
6
+ function findNearestFrameIndex(stack, predicate) {
7
+ for(let index = stack.length - 1; index >= 0; --index){
8
+ const frame = stack[index];
9
+ if (predicate(frame)) return index;
10
+ }
11
+ return -1;
12
+ }
13
+ function getTabValue(attributes, index) {
14
+ const value = attributes?.value;
15
+ const label = attributes?.label;
16
+ const raw = typeof value === 'string' && value.trim() ? value.trim() : typeof label === 'string' && label.trim() ? label.trim() : `tab-${index + 1}`;
17
+ return raw.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || `tab-${index + 1}`;
18
+ }
19
+ function finalizeTabsFrame(frame) {
20
+ if (frame.name !== 'tabs') return [];
21
+ const diagnostics = [];
22
+ const tabValues = frame.tabValues ?? new Map();
23
+ if (!frame.tabCount) diagnostics.push('Directive "tabs" has no child "tab" directives.');
24
+ for (const [value, count] of tabValues)if (count > 1) diagnostics.push(`Duplicate tab value "${value}" in "tabs".`);
25
+ if (frame.defaultValue) {
26
+ const defaultValue = getTabValue({
27
+ value: frame.defaultValue
28
+ }, 0);
29
+ if (!tabValues.has(defaultValue)) diagnostics.push(`Invalid tabs default "${frame.defaultValue}". Falling back to the first tab.`);
30
+ }
31
+ return diagnostics;
32
+ }
33
+ function findNearestTabsFrame(stack) {
34
+ for(let index = stack.length - 1; index >= 0; --index){
35
+ const frame = stack[index];
36
+ if (frame.name === 'tabs') return frame;
37
+ }
38
+ return undefined;
39
+ }
40
+ function updateOpenStack(stack, text, line, from) {
41
+ const token = layoutDirectiveRegistry.parseMarkdownLineDetailed(text).token;
42
+ const diagnostics = [];
43
+ if (!token) return diagnostics;
44
+ if (token.action === 'open') {
45
+ if (token.name === 'tab') {
46
+ const tabsFrame = findNearestTabsFrame(stack);
47
+ if (!tabsFrame) diagnostics.push('Directive "tab" is usually intended inside "tabs".');
48
+ else {
49
+ const nextIndex = tabsFrame.tabCount ?? 0;
50
+ const value = getTabValue(token.attributes, nextIndex);
51
+ tabsFrame.tabCount = nextIndex + 1;
52
+ tabsFrame.tabValues ??= new Map();
53
+ tabsFrame.tabValues.set(value, (tabsFrame.tabValues.get(value) ?? 0) + 1);
54
+ }
55
+ }
56
+ stack.push({
57
+ name: token.name,
58
+ from,
59
+ line,
60
+ ...token.name === 'tabs' ? {
61
+ defaultValue: typeof token.attributes?.default === 'string' ? token.attributes.default : undefined,
62
+ tabCount: 0,
63
+ tabValues: new Map()
64
+ } : {}
65
+ });
66
+ return diagnostics;
67
+ }
68
+ if (token.action === 'close') {
69
+ const frame = stack.pop();
70
+ if (frame) diagnostics.push(...finalizeTabsFrame(frame));
71
+ return diagnostics;
72
+ }
73
+ if (token.action === 'closeGrid') {
74
+ const index = findNearestFrameIndex(stack, (frame)=>layoutDirectiveRegistry.isGridName(frame.name));
75
+ if (index >= 0) {
76
+ const frames = stack.splice(index);
77
+ for (const frame of frames)diagnostics.push(...finalizeTabsFrame(frame));
78
+ }
79
+ return diagnostics;
80
+ }
81
+ const index = findNearestFrameIndex(stack, (frame)=>frame.name === 'section');
82
+ if (index >= 0) {
83
+ const frames = stack.splice(index);
84
+ for (const frame of frames)diagnostics.push(...finalizeTabsFrame(frame));
85
+ }
86
+ return diagnostics;
87
+ }
88
+ function getThemeDiagnostics(text) {
89
+ const token = layoutDirectiveRegistry.parseMarkdownLineDetailed(text).token;
90
+ if (!token || token.action !== 'open') return [];
91
+ const definition = layoutDirectiveRegistry.get(token.name);
92
+ if (!definition?.themeAttributes) return [];
93
+ const diagnostics = [];
94
+ for (const [attribute, groupName] of Object.entries(definition.themeAttributes)){
95
+ if (!groupName) continue;
96
+ const value = token.attributes?.[attribute];
97
+ if (typeof value !== 'string' || !value.trim()) continue;
98
+ if (hasDirectiveTheme(groupName, value)) continue;
99
+ const label = attribute === 'theme' ? 'theme' : attribute;
100
+ diagnostics.push(`Unknown ${label} "${value}" on "${token.name}". Falling back to "default".`);
101
+ }
102
+ return diagnostics;
103
+ }
104
+ export function lintMarkdownDirectives(markdown) {
105
+ const diagnostics = [];
106
+ const stack = [];
107
+ const lines = markdown.split(/\r?\n/);
108
+ let offset = 0;
109
+ let inFence = false;
110
+ for(let index = 0; index < lines.length; ++index){
111
+ const line = lines[index];
112
+ const trimmed = line.trim();
113
+ const lineStart = offset;
114
+ const markerStart = line.indexOf(':::');
115
+ const markerFrom = markerStart >= 0 ? lineStart + markerStart : lineStart;
116
+ const markerTo = lineStart + line.length;
117
+ if (isFenceLine(trimmed)) {
118
+ inFence = !inFence;
119
+ offset += line.length + 1;
120
+ continue;
121
+ }
122
+ if (!inFence && trimmed.startsWith(':::')) {
123
+ const result = layoutDirectiveRegistry.parseMarkdownLineDetailed(trimmed);
124
+ for (const message of result.diagnostics)diagnostics.push({
125
+ from: markerFrom,
126
+ line: index + 1,
127
+ message,
128
+ severity: 'warning',
129
+ to: markerTo
130
+ });
131
+ for (const message of getThemeDiagnostics(trimmed))diagnostics.push({
132
+ from: markerFrom,
133
+ line: index + 1,
134
+ message,
135
+ severity: 'warning',
136
+ to: markerTo
137
+ });
138
+ for (const message of updateOpenStack(stack, trimmed, index + 1, markerFrom))diagnostics.push({
139
+ from: markerFrom,
140
+ line: index + 1,
141
+ message,
142
+ severity: 'warning',
143
+ to: markerTo
144
+ });
145
+ }
146
+ offset += line.length + 1;
147
+ }
148
+ for (const frame of stack){
149
+ for (const message of finalizeTabsFrame(frame))diagnostics.push({
150
+ from: frame.from,
151
+ line: frame.line,
152
+ message,
153
+ severity: 'warning',
154
+ to: frame.from + frame.name.length + 3
155
+ });
156
+ diagnostics.push({
157
+ from: frame.from,
158
+ line: frame.line,
159
+ message: `Unclosed directive "${frame.name}".`,
160
+ severity: 'warning',
161
+ to: frame.from + frame.name.length + 3
162
+ });
163
+ }
164
+ return diagnostics;
165
+ }
166
+
167
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/directives/diagnostics.ts"],"sourcesContent":["import { layoutDirectiveRegistry } from './registry.js'\nimport { hasDirectiveTheme } from './themes.js'\n\nexport type DirectiveDiagnostic = {\n from: number\n line: number\n message: string\n severity: 'warning'\n to: number\n}\n\ntype OpenFrame = {\n defaultValue?: string\n from: number\n line: number\n name: string\n tabCount?: number\n tabValues?: Map<string, number>\n}\n\nfunction isFenceLine(trimmed: string): boolean {\n return trimmed.startsWith('```') || trimmed.startsWith('~~~')\n}\n\nfunction findNearestFrameIndex(stack: OpenFrame[], predicate: (frame: OpenFrame) => boolean) {\n for (let index = stack.length - 1; index >= 0; --index) {\n const frame = stack[index]\n if (predicate(frame)) return index\n }\n\n return -1\n}\n\nfunction getTabValue(attributes: Record<string, boolean | string> | undefined, index: number): string {\n const value = attributes?.value\n const label = attributes?.label\n const raw =\n typeof value === 'string' && value.trim()\n ? value.trim()\n : typeof label === 'string' && label.trim()\n ? label.trim()\n : `tab-${index + 1}`\n\n return raw\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || `tab-${index + 1}`\n}\n\nfunction finalizeTabsFrame(frame: OpenFrame): string[] {\n if (frame.name !== 'tabs') return []\n\n const diagnostics: string[] = []\n const tabValues = frame.tabValues ?? new Map<string, number>()\n\n if (!frame.tabCount) diagnostics.push('Directive \"tabs\" has no child \"tab\" directives.')\n\n for (const [value, count] of tabValues)\n if (count > 1) diagnostics.push(`Duplicate tab value \"${value}\" in \"tabs\".`)\n\n if (frame.defaultValue) {\n const defaultValue = getTabValue({ value: frame.defaultValue }, 0)\n if (!tabValues.has(defaultValue))\n diagnostics.push(`Invalid tabs default \"${frame.defaultValue}\". Falling back to the first tab.`)\n }\n\n return diagnostics\n}\n\nfunction findNearestTabsFrame(stack: OpenFrame[]): OpenFrame | undefined {\n for (let index = stack.length - 1; index >= 0; --index) {\n const frame = stack[index]\n if (frame.name === 'tabs') return frame\n }\n\n return undefined\n}\n\nfunction updateOpenStack(\n stack: OpenFrame[],\n text: string,\n line: number,\n from: number,\n): string[] {\n const token = layoutDirectiveRegistry.parseMarkdownLineDetailed(text).token\n const diagnostics: string[] = []\n\n if (!token) return diagnostics\n\n if (token.action === 'open') {\n if (token.name === 'tab') {\n const tabsFrame = findNearestTabsFrame(stack)\n\n if (!tabsFrame) diagnostics.push('Directive \"tab\" is usually intended inside \"tabs\".')\n else {\n const nextIndex = tabsFrame.tabCount ?? 0\n const value = getTabValue(token.attributes, nextIndex)\n\n tabsFrame.tabCount = nextIndex + 1\n tabsFrame.tabValues ??= new Map<string, number>()\n tabsFrame.tabValues.set(value, (tabsFrame.tabValues.get(value) ?? 0) + 1)\n }\n }\n\n stack.push({\n name: token.name,\n from,\n line,\n ...(token.name === 'tabs'\n ? {\n defaultValue:\n typeof token.attributes?.default === 'string'\n ? token.attributes.default\n : undefined,\n tabCount: 0,\n tabValues: new Map<string, number>(),\n }\n : {}),\n })\n return diagnostics\n }\n\n if (token.action === 'close') {\n const frame = stack.pop()\n if (frame) diagnostics.push(...finalizeTabsFrame(frame))\n return diagnostics\n }\n\n if (token.action === 'closeGrid') {\n const index = findNearestFrameIndex(stack, (frame) =>\n layoutDirectiveRegistry.isGridName(frame.name),\n )\n\n if (index >= 0) {\n const frames = stack.splice(index)\n for (const frame of frames) diagnostics.push(...finalizeTabsFrame(frame))\n }\n return diagnostics\n }\n\n const index = findNearestFrameIndex(stack, (frame) => frame.name === 'section')\n if (index >= 0) {\n const frames = stack.splice(index)\n for (const frame of frames) diagnostics.push(...finalizeTabsFrame(frame))\n }\n return diagnostics\n}\n\nfunction getThemeDiagnostics(text: string): string[] {\n const token = layoutDirectiveRegistry.parseMarkdownLineDetailed(text).token\n if (!token || token.action !== 'open') return []\n\n const definition = layoutDirectiveRegistry.get(token.name)\n if (!definition?.themeAttributes) return []\n\n const diagnostics: string[] = []\n\n for (const [attribute, groupName] of Object.entries(definition.themeAttributes)) {\n if (!groupName) continue\n\n const value = token.attributes?.[attribute]\n if (typeof value !== 'string' || !value.trim()) continue\n if (hasDirectiveTheme(groupName, value)) continue\n\n const label = attribute === 'theme' ? 'theme' : attribute\n diagnostics.push(\n `Unknown ${label} \"${value}\" on \"${token.name}\". Falling back to \"default\".`,\n )\n }\n\n return diagnostics\n}\n\nexport function lintMarkdownDirectives(markdown: string): DirectiveDiagnostic[] {\n const diagnostics: DirectiveDiagnostic[] = []\n const stack: OpenFrame[] = []\n const lines = markdown.split(/\\r?\\n/)\n let offset = 0\n let inFence = false\n\n for (let index = 0; index < lines.length; ++index) {\n const line = lines[index]\n const trimmed = line.trim()\n const lineStart = offset\n const markerStart = line.indexOf(':::')\n const markerFrom = markerStart >= 0 ? lineStart + markerStart : lineStart\n const markerTo = lineStart + line.length\n\n if (isFenceLine(trimmed)) {\n inFence = !inFence\n offset += line.length + 1\n continue\n }\n\n if (!inFence && trimmed.startsWith(':::')) {\n const result = layoutDirectiveRegistry.parseMarkdownLineDetailed(trimmed)\n\n for (const message of result.diagnostics)\n diagnostics.push({\n from: markerFrom,\n line: index + 1,\n message,\n severity: 'warning',\n to: markerTo,\n })\n\n for (const message of getThemeDiagnostics(trimmed))\n diagnostics.push({\n from: markerFrom,\n line: index + 1,\n message,\n severity: 'warning',\n to: markerTo,\n })\n\n for (const message of updateOpenStack(stack, trimmed, index + 1, markerFrom))\n diagnostics.push({\n from: markerFrom,\n line: index + 1,\n message,\n severity: 'warning',\n to: markerTo,\n })\n }\n\n offset += line.length + 1\n }\n\n for (const frame of stack) {\n for (const message of finalizeTabsFrame(frame))\n diagnostics.push({\n from: frame.from,\n line: frame.line,\n message,\n severity: 'warning',\n to: frame.from + frame.name.length + 3,\n })\n\n diagnostics.push({\n from: frame.from,\n line: frame.line,\n message: `Unclosed directive \"${frame.name}\".`,\n severity: 'warning',\n to: frame.from + frame.name.length + 3,\n })\n }\n\n return diagnostics\n}\n"],"names":["layoutDirectiveRegistry","hasDirectiveTheme","isFenceLine","trimmed","startsWith","findNearestFrameIndex","stack","predicate","index","length","frame","getTabValue","attributes","value","label","raw","trim","replace","toLowerCase","finalizeTabsFrame","name","diagnostics","tabValues","Map","tabCount","push","count","defaultValue","has","findNearestTabsFrame","undefined","updateOpenStack","text","line","from","token","parseMarkdownLineDetailed","action","tabsFrame","nextIndex","set","get","default","pop","isGridName","frames","splice","getThemeDiagnostics","definition","themeAttributes","attribute","groupName","Object","entries","lintMarkdownDirectives","markdown","lines","split","offset","inFence","lineStart","markerStart","indexOf","markerFrom","markerTo","result","message","severity","to"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Heading, Root } from 'mdast';
2
+ export type HeadingAnchor = {
3
+ depth: number;
4
+ id: string;
5
+ text: string;
6
+ };
7
+ export declare function headingToText(node: Heading): string;
8
+ export declare function slugifyHeading(value: string): string;
9
+ export declare function applyHeadingAnchors(tree: Root): HeadingAnchor[];
@@ -0,0 +1,54 @@
1
+ function phrasingToText(node) {
2
+ if ('value' in node && typeof node.value === 'string') return node.value;
3
+ if ('children' in node && Array.isArray(node.children)) return node.children.map(phrasingToText).join('');
4
+ return '';
5
+ }
6
+ export function headingToText(node) {
7
+ return node.children.map(phrasingToText).join('').trim();
8
+ }
9
+ export function slugifyHeading(value) {
10
+ const slug = value.toLowerCase().trim().replace(/['"]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
11
+ return slug || 'section';
12
+ }
13
+ function makeUniqueSlug(base, counts) {
14
+ const count = counts.get(base) ?? 0;
15
+ counts.set(base, count + 1);
16
+ return count === 0 ? base : `${base}-${count}`;
17
+ }
18
+ function isHeading(node) {
19
+ return node.type === 'heading';
20
+ }
21
+ function isContainerDirective(node) {
22
+ return node.type === 'containerDirective';
23
+ }
24
+ function walkChildren(children, visitHeading) {
25
+ for (const child of children){
26
+ if (isHeading(child)) {
27
+ visitHeading(child);
28
+ continue;
29
+ }
30
+ if (isContainerDirective(child)) walkChildren(child.children, visitHeading);
31
+ }
32
+ }
33
+ export function applyHeadingAnchors(tree) {
34
+ const counts = new Map();
35
+ const headings = [];
36
+ walkChildren(tree.children, (heading)=>{
37
+ const text = headingToText(heading);
38
+ const id = makeUniqueSlug(slugifyHeading(text), counts);
39
+ const data = heading.data ??= {};
40
+ data.hProperties = {
41
+ ...data.hProperties ?? {},
42
+ id,
43
+ dataHeadingAnchor: id
44
+ };
45
+ headings.push({
46
+ id,
47
+ depth: heading.depth,
48
+ text
49
+ });
50
+ });
51
+ return headings;
52
+ }
53
+
54
+ //# sourceMappingURL=headingAnchors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/directives/headingAnchors.ts"],"sourcesContent":["import type { Content, Heading, PhrasingContent, Root } from 'mdast'\nimport type { ContainerDirective } from 'mdast-util-directive'\n\nexport type HeadingAnchor = {\n depth: number\n id: string\n text: string\n}\n\nfunction phrasingToText(node: PhrasingContent): string {\n if ('value' in node && typeof node.value === 'string') return node.value\n if ('children' in node && Array.isArray(node.children))\n return node.children.map(phrasingToText).join('')\n\n return ''\n}\n\nexport function headingToText(node: Heading): string {\n return node.children.map(phrasingToText).join('').trim()\n}\n\nexport function slugifyHeading(value: string): string {\n const slug = value\n .toLowerCase()\n .trim()\n .replace(/['\"]/g, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n\n return slug || 'section'\n}\n\nfunction makeUniqueSlug(base: string, counts: Map<string, number>): string {\n const count = counts.get(base) ?? 0\n counts.set(base, count + 1)\n\n return count === 0 ? base : `${base}-${count}`\n}\n\nfunction isHeading(node: Content): node is Heading {\n return node.type === 'heading'\n}\n\nfunction isContainerDirective(node: Content): node is ContainerDirective {\n return node.type === 'containerDirective'\n}\n\nfunction walkChildren(\n children: Content[],\n visitHeading: (heading: Heading) => void,\n) {\n for (const child of children) {\n if (isHeading(child)) {\n visitHeading(child)\n continue\n }\n\n if (isContainerDirective(child)) walkChildren(child.children, visitHeading)\n }\n}\n\nexport function applyHeadingAnchors(tree: Root): HeadingAnchor[] {\n const counts = new Map<string, number>()\n const headings: HeadingAnchor[] = []\n\n walkChildren(tree.children, (heading) => {\n const text = headingToText(heading)\n const id = makeUniqueSlug(slugifyHeading(text), counts)\n const data = (heading.data ??= {})\n\n data.hProperties = {\n ...(data.hProperties ?? {}),\n id,\n dataHeadingAnchor: id,\n }\n\n headings.push({\n id,\n depth: heading.depth,\n text,\n })\n })\n\n return headings\n}\n"],"names":["phrasingToText","node","value","Array","isArray","children","map","join","headingToText","trim","slugifyHeading","slug","toLowerCase","replace","makeUniqueSlug","base","counts","count","get","set","isHeading","type","isContainerDirective","walkChildren","visitHeading","child","applyHeadingAnchors","tree","Map","headings","heading","text","id","data","hProperties","dataHeadingAnchor","push","depth"],"mappings":"AASA,SAASA,eAAeC,IAAqB;IAC3C,IAAI,WAAWA,QAAQ,OAAOA,KAAKC,KAAK,KAAK,UAAU,OAAOD,KAAKC,KAAK;IACxE,IAAI,cAAcD,QAAQE,MAAMC,OAAO,CAACH,KAAKI,QAAQ,GACnD,OAAOJ,KAAKI,QAAQ,CAACC,GAAG,CAACN,gBAAgBO,IAAI,CAAC;IAEhD,OAAO;AACT;AAEA,OAAO,SAASC,cAAcP,IAAa;IACzC,OAAOA,KAAKI,QAAQ,CAACC,GAAG,CAACN,gBAAgBO,IAAI,CAAC,IAAIE,IAAI;AACxD;AAEA,OAAO,SAASC,eAAeR,KAAa;IAC1C,MAAMS,OAAOT,MACVU,WAAW,GACXH,IAAI,GACJI,OAAO,CAAC,SAAS,IACjBA,OAAO,CAAC,eAAe,KACvBA,OAAO,CAAC,YAAY;IAEvB,OAAOF,QAAQ;AACjB;AAEA,SAASG,eAAeC,IAAY,EAAEC,MAA2B;IAC/D,MAAMC,QAAQD,OAAOE,GAAG,CAACH,SAAS;IAClCC,OAAOG,GAAG,CAACJ,MAAME,QAAQ;IAEzB,OAAOA,UAAU,IAAIF,OAAO,GAAGA,KAAK,CAAC,EAAEE,OAAO;AAChD;AAEA,SAASG,UAAUnB,IAAa;IAC9B,OAAOA,KAAKoB,IAAI,KAAK;AACvB;AAEA,SAASC,qBAAqBrB,IAAa;IACzC,OAAOA,KAAKoB,IAAI,KAAK;AACvB;AAEA,SAASE,aACPlB,QAAmB,EACnBmB,YAAwC;IAExC,KAAK,MAAMC,SAASpB,SAAU;QAC5B,IAAIe,UAAUK,QAAQ;YACpBD,aAAaC;YACb;QACF;QAEA,IAAIH,qBAAqBG,QAAQF,aAAaE,MAAMpB,QAAQ,EAAEmB;IAChE;AACF;AAEA,OAAO,SAASE,oBAAoBC,IAAU;IAC5C,MAAMX,SAAS,IAAIY;IACnB,MAAMC,WAA4B,EAAE;IAEpCN,aAAaI,KAAKtB,QAAQ,EAAE,CAACyB;QAC3B,MAAMC,OAAOvB,cAAcsB;QAC3B,MAAME,KAAKlB,eAAeJ,eAAeqB,OAAOf;QAChD,MAAMiB,OAAQH,QAAQG,IAAI,KAAK,CAAC;QAEhCA,KAAKC,WAAW,GAAG;YACjB,GAAID,KAAKC,WAAW,IAAI,CAAC,CAAC;YAC1BF;YACAG,mBAAmBH;QACrB;QAEAH,SAASO,IAAI,CAAC;YACZJ;YACAK,OAAOP,QAAQO,KAAK;YACpBN;QACF;IACF;IAEA,OAAOF;AACT"}
@@ -0,0 +1,5 @@
1
+ export { type DirectiveAttributes, type DirectiveAttributeValue, type ParsedDirectiveLine, parseDirectiveAttributes, parseDirectiveAttributesDetailed, parseDirectiveLine, } from './attributes.js';
2
+ export { applyHeadingAnchors, headingToText, slugifyHeading } from './headingAnchors.js';
3
+ export { layoutDirectiveRegistry } from './registry.js';
4
+ export { DEFAULT_CALLOUT_THEMES, DEFAULT_CARD_THEMES, DEFAULT_CARDS_THEMES, DEFAULT_CELL_THEMES, DEFAULT_COLUMNS_THEMES, DEFAULT_DETAILS_THEMES, DEFAULT_SECTION_THEMES, DEFAULT_STEPS_THEMES, DEFAULT_TOC_THEMES, getDirectiveThemeNames, hasDirectiveTheme, mergeMarkdownDirectiveThemes, resolveDirectiveTheme, slugThemeName, } from './themes.js';
5
+ export type { GridDirectiveName, LayoutDirectiveDefinition, LayoutDirectiveEditorMetadata, LayoutDirectiveName, LayoutName, LayoutToken, MarkdownDirectiveName, StaticDirectiveName, } from './types.js';
@@ -0,0 +1,6 @@
1
+ export { parseDirectiveAttributes, parseDirectiveAttributesDetailed, parseDirectiveLine } from './attributes.js';
2
+ export { applyHeadingAnchors, headingToText, slugifyHeading } from './headingAnchors.js';
3
+ export { layoutDirectiveRegistry } from './registry.js';
4
+ export { DEFAULT_CALLOUT_THEMES, DEFAULT_CARD_THEMES, DEFAULT_CARDS_THEMES, DEFAULT_CELL_THEMES, DEFAULT_COLUMNS_THEMES, DEFAULT_DETAILS_THEMES, DEFAULT_SECTION_THEMES, DEFAULT_STEPS_THEMES, DEFAULT_TOC_THEMES, getDirectiveThemeNames, hasDirectiveTheme, mergeMarkdownDirectiveThemes, resolveDirectiveTheme, slugThemeName } from './themes.js';
5
+
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/directives/index.ts"],"sourcesContent":["export {\n type DirectiveAttributes,\n type DirectiveAttributeValue,\n type ParsedDirectiveLine,\n parseDirectiveAttributes,\n parseDirectiveAttributesDetailed,\n parseDirectiveLine,\n} from './attributes.js'\nexport { applyHeadingAnchors, headingToText, slugifyHeading } from './headingAnchors.js'\nexport { layoutDirectiveRegistry } from './registry.js'\nexport {\n DEFAULT_CALLOUT_THEMES,\n DEFAULT_CARD_THEMES,\n DEFAULT_CARDS_THEMES,\n DEFAULT_CELL_THEMES,\n DEFAULT_COLUMNS_THEMES,\n DEFAULT_DETAILS_THEMES,\n DEFAULT_SECTION_THEMES,\n DEFAULT_STEPS_THEMES,\n DEFAULT_TOC_THEMES,\n getDirectiveThemeNames,\n hasDirectiveTheme,\n mergeMarkdownDirectiveThemes,\n resolveDirectiveTheme,\n slugThemeName,\n} from './themes.js'\nexport type {\n GridDirectiveName,\n LayoutDirectiveDefinition,\n LayoutDirectiveEditorMetadata,\n LayoutDirectiveName,\n LayoutName,\n LayoutToken,\n MarkdownDirectiveName,\n StaticDirectiveName,\n} from './types.js'\n"],"names":["parseDirectiveAttributes","parseDirectiveAttributesDetailed","parseDirectiveLine","applyHeadingAnchors","headingToText","slugifyHeading","layoutDirectiveRegistry","DEFAULT_CALLOUT_THEMES","DEFAULT_CARD_THEMES","DEFAULT_CARDS_THEMES","DEFAULT_CELL_THEMES","DEFAULT_COLUMNS_THEMES","DEFAULT_DETAILS_THEMES","DEFAULT_SECTION_THEMES","DEFAULT_STEPS_THEMES","DEFAULT_TOC_THEMES","getDirectiveThemeNames","hasDirectiveTheme","mergeMarkdownDirectiveThemes","resolveDirectiveTheme","slugThemeName"],"mappings":"AAAA,SAIEA,wBAAwB,EACxBC,gCAAgC,EAChCC,kBAAkB,QACb,kBAAiB;AACxB,SAASC,mBAAmB,EAAEC,aAAa,EAAEC,cAAc,QAAQ,sBAAqB;AACxF,SAASC,uBAAuB,QAAQ,gBAAe;AACvD,SACEC,sBAAsB,EACtBC,mBAAmB,EACnBC,oBAAoB,EACpBC,mBAAmB,EACnBC,sBAAsB,EACtBC,sBAAsB,EACtBC,sBAAsB,EACtBC,oBAAoB,EACpBC,kBAAkB,EAClBC,sBAAsB,EACtBC,iBAAiB,EACjBC,4BAA4B,EAC5BC,qBAAqB,EACrBC,aAAa,QACR,cAAa"}