@dominikcz/greg 0.9.27

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 (183) hide show
  1. package/README.md +397 -0
  2. package/bin/greg.js +241 -0
  3. package/bin/init.js +351 -0
  4. package/bin/templates/docs/getting-started.md +47 -0
  5. package/bin/templates/docs/index.md +11 -0
  6. package/bin/templates/greg.config.js +39 -0
  7. package/bin/templates/greg.config.ts +38 -0
  8. package/bin/templates/index.html +16 -0
  9. package/bin/templates/src/App.svelte +5 -0
  10. package/bin/templates/src/app.css +20 -0
  11. package/bin/templates/src/main.js +9 -0
  12. package/bin/templates/svelte.config.js +1 -0
  13. package/bin/templates/tsconfig.json +21 -0
  14. package/bin/templates/vite.config.js +23 -0
  15. package/docs/__partials/markdown/examples/basic.md +4 -0
  16. package/docs/__partials/markdown/examples/diff.md +10 -0
  17. package/docs/__partials/markdown/examples/focus.md +5 -0
  18. package/docs/__partials/markdown/examples/language-title.md +3 -0
  19. package/docs/__partials/markdown/examples/line-highlighting.md +5 -0
  20. package/docs/__partials/markdown/examples/line-numbers.md +5 -0
  21. package/docs/__partials/note.md +4 -0
  22. package/docs/guide/__shared-warning.md +4 -0
  23. package/docs/guide/asset-handling.md +88 -0
  24. package/docs/guide/deploying.md +162 -0
  25. package/docs/guide/getting-started.md +334 -0
  26. package/docs/guide/index.md +23 -0
  27. package/docs/guide/localization.md +290 -0
  28. package/docs/guide/markdown/code.md +95 -0
  29. package/docs/guide/markdown/components-and-mermaid.md +43 -0
  30. package/docs/guide/markdown/containers.md +110 -0
  31. package/docs/guide/markdown/header-anchors.md +34 -0
  32. package/docs/guide/markdown/includes.md +84 -0
  33. package/docs/guide/markdown/index.md +20 -0
  34. package/docs/guide/markdown/inline-attributes.md +21 -0
  35. package/docs/guide/markdown/links-and-toc.md +64 -0
  36. package/docs/guide/markdown/math.md +54 -0
  37. package/docs/guide/markdown/syntax-highlighting.md +75 -0
  38. package/docs/guide/routing.md +150 -0
  39. package/docs/guide/using-svelte.md +88 -0
  40. package/docs/guide/versioning.md +281 -0
  41. package/docs/incompatibilities.md +48 -0
  42. package/docs/index.md +43 -0
  43. package/docs/reference/badge.md +100 -0
  44. package/docs/reference/carbon-ads.md +46 -0
  45. package/docs/reference/code-group.md +126 -0
  46. package/docs/reference/home-page.md +232 -0
  47. package/docs/reference/index.md +18 -0
  48. package/docs/reference/markdowndocs.md +275 -0
  49. package/docs/reference/outline.md +79 -0
  50. package/docs/reference/search.md +263 -0
  51. package/docs/reference/steps.md +200 -0
  52. package/docs/reference/team-page.md +189 -0
  53. package/docs/reference/theme.md +150 -0
  54. package/fakeDocsGenerator/generate_docs.js +310 -0
  55. package/package.json +92 -0
  56. package/scripts/build-versions.js +609 -0
  57. package/scripts/generate-static.js +79 -0
  58. package/scripts/render-markdown.js +420 -0
  59. package/src/lib/MarkdownDocs/AiChat.svelte +936 -0
  60. package/src/lib/MarkdownDocs/BackToTop.svelte +68 -0
  61. package/src/lib/MarkdownDocs/Breadcrumb.svelte +68 -0
  62. package/src/lib/MarkdownDocs/DocsNavigation.svelte +149 -0
  63. package/src/lib/MarkdownDocs/DocsSiteHeader.svelte +758 -0
  64. package/src/lib/MarkdownDocs/DocsVersionSwitcher.svelte +103 -0
  65. package/src/lib/MarkdownDocs/MarkdownDocs.svelte +2115 -0
  66. package/src/lib/MarkdownDocs/MarkdownRenderer.svelte +487 -0
  67. package/src/lib/MarkdownDocs/Outline.svelte +238 -0
  68. package/src/lib/MarkdownDocs/PrevNext.svelte +115 -0
  69. package/src/lib/MarkdownDocs/SearchModal.svelte +1241 -0
  70. package/src/lib/MarkdownDocs/TreeView.svelte +32 -0
  71. package/src/lib/MarkdownDocs/TreeViewItem.svelte +219 -0
  72. package/src/lib/MarkdownDocs/VersionOutdatedNotice.svelte +72 -0
  73. package/src/lib/MarkdownDocs/__tests__/codeDirectives.test.js +54 -0
  74. package/src/lib/MarkdownDocs/__tests__/common.test.js +41 -0
  75. package/src/lib/MarkdownDocs/__tests__/docsExamplesLint.test.js +77 -0
  76. package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/__partial-basic.md +3 -0
  77. package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/snippet.js +9 -0
  78. package/src/lib/MarkdownDocs/__tests__/fixtures/includes/part.md +11 -0
  79. package/src/lib/MarkdownDocs/__tests__/fixtures/includes/wrapper.md +5 -0
  80. package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.js +8 -0
  81. package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.md +5 -0
  82. package/src/lib/MarkdownDocs/__tests__/helpers.js +67 -0
  83. package/src/lib/MarkdownDocs/__tests__/localeUtils.test.js +204 -0
  84. package/src/lib/MarkdownDocs/__tests__/markdown.test.js +704 -0
  85. package/src/lib/MarkdownDocs/__tests__/markdownRendererRuntime.test.js +65 -0
  86. package/src/lib/MarkdownDocs/__tests__/searchIndexBuilder.test.js +117 -0
  87. package/src/lib/MarkdownDocs/__tests__/sqliteStore.test.js +202 -0
  88. package/src/lib/MarkdownDocs/__tests__/useRouter.test.js +16 -0
  89. package/src/lib/MarkdownDocs/ai/adapters/customAdapter.js +14 -0
  90. package/src/lib/MarkdownDocs/ai/adapters/customAdapter.ts +43 -0
  91. package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.js +81 -0
  92. package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.ts +116 -0
  93. package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.js +92 -0
  94. package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.ts +137 -0
  95. package/src/lib/MarkdownDocs/ai/aiProvider.ts +31 -0
  96. package/src/lib/MarkdownDocs/ai/characters.js +52 -0
  97. package/src/lib/MarkdownDocs/ai/characters.ts +69 -0
  98. package/src/lib/MarkdownDocs/ai/chunkStore.ts +25 -0
  99. package/src/lib/MarkdownDocs/ai/chunker.js +85 -0
  100. package/src/lib/MarkdownDocs/ai/chunker.ts +135 -0
  101. package/src/lib/MarkdownDocs/ai/docLinker.js +26 -0
  102. package/src/lib/MarkdownDocs/ai/docLinker.ts +36 -0
  103. package/src/lib/MarkdownDocs/ai/promptBuilder.js +33 -0
  104. package/src/lib/MarkdownDocs/ai/promptBuilder.ts +53 -0
  105. package/src/lib/MarkdownDocs/ai/ragPipeline.js +54 -0
  106. package/src/lib/MarkdownDocs/ai/ragPipeline.ts +106 -0
  107. package/src/lib/MarkdownDocs/ai/stores/memoryStore.js +88 -0
  108. package/src/lib/MarkdownDocs/ai/stores/memoryStore.ts +112 -0
  109. package/src/lib/MarkdownDocs/ai/stores/sqliteStore.ts +372 -0
  110. package/src/lib/MarkdownDocs/ai/types.ts +71 -0
  111. package/src/lib/MarkdownDocs/aiServer.js +288 -0
  112. package/src/lib/MarkdownDocs/codeDirectives.js +191 -0
  113. package/src/lib/MarkdownDocs/codeFenceInfo.js +45 -0
  114. package/src/lib/MarkdownDocs/codeGroup.ts +46 -0
  115. package/src/lib/MarkdownDocs/common.ts +47 -0
  116. package/src/lib/MarkdownDocs/docsUtils.js +281 -0
  117. package/src/lib/MarkdownDocs/index.plugins.js +22 -0
  118. package/src/lib/MarkdownDocs/layouts/LayoutDoc.svelte +8 -0
  119. package/src/lib/MarkdownDocs/layouts/LayoutHome.svelte +58 -0
  120. package/src/lib/MarkdownDocs/layouts/LayoutPage.svelte +9 -0
  121. package/src/lib/MarkdownDocs/loadGregConfig.js +82 -0
  122. package/src/lib/MarkdownDocs/localeUtils.ts +682 -0
  123. package/src/lib/MarkdownDocs/markdownRendererRuntime.ts +314 -0
  124. package/src/lib/MarkdownDocs/mermaidThemes.js +319 -0
  125. package/src/lib/MarkdownDocs/navigationUtils.js +22 -0
  126. package/src/lib/MarkdownDocs/rehypeCodeGroup.js +326 -0
  127. package/src/lib/MarkdownDocs/rehypeCodeTitle.js +96 -0
  128. package/src/lib/MarkdownDocs/rehypeToc.js +170 -0
  129. package/src/lib/MarkdownDocs/remarkCodeMeta.js +22 -0
  130. package/src/lib/MarkdownDocs/remarkContainers.js +329 -0
  131. package/src/lib/MarkdownDocs/remarkCustomAnchors.js +42 -0
  132. package/src/lib/MarkdownDocs/remarkEscapeSvelte.js +33 -0
  133. package/src/lib/MarkdownDocs/remarkGlobalComponents.js +65 -0
  134. package/src/lib/MarkdownDocs/remarkImports.js +461 -0
  135. package/src/lib/MarkdownDocs/remarkImportsBrowser.js +349 -0
  136. package/src/lib/MarkdownDocs/remarkInlineAttrs.js +95 -0
  137. package/src/lib/MarkdownDocs/remarkMathToHtml.js +138 -0
  138. package/src/lib/MarkdownDocs/searchIndexBuilder.js +497 -0
  139. package/src/lib/MarkdownDocs/searchServer.js +263 -0
  140. package/src/lib/MarkdownDocs/treeViewTypes.ts +11 -0
  141. package/src/lib/MarkdownDocs/useRouter.svelte.ts +114 -0
  142. package/src/lib/MarkdownDocs/useSplitter.svelte.ts +33 -0
  143. package/src/lib/MarkdownDocs/versioningDefaults.js +20 -0
  144. package/src/lib/MarkdownDocs/vitePluginAiServer.js +204 -0
  145. package/src/lib/MarkdownDocs/vitePluginCopyDocs.js +153 -0
  146. package/src/lib/MarkdownDocs/vitePluginFrontmatter.js +109 -0
  147. package/src/lib/MarkdownDocs/vitePluginGregConfig.js +108 -0
  148. package/src/lib/MarkdownDocs/vitePluginSearchIndex.js +57 -0
  149. package/src/lib/MarkdownDocs/vitePluginSearchServer.js +190 -0
  150. package/src/lib/components/Badge.svelte +59 -0
  151. package/src/lib/components/Button.svelte +138 -0
  152. package/src/lib/components/CarbonAds.svelte +99 -0
  153. package/src/lib/components/CodeGroup.svelte +102 -0
  154. package/src/lib/components/Feature.svelte +209 -0
  155. package/src/lib/components/Features.svelte +123 -0
  156. package/src/lib/components/Hero.svelte +399 -0
  157. package/src/lib/components/Image.svelte +128 -0
  158. package/src/lib/components/Link.svelte +105 -0
  159. package/src/lib/components/SocialLink.svelte +84 -0
  160. package/src/lib/components/SocialLinks.svelte +33 -0
  161. package/src/lib/components/Steps.svelte +143 -0
  162. package/src/lib/components/TeamMember.svelte +273 -0
  163. package/src/lib/components/TeamMembers.svelte +81 -0
  164. package/src/lib/components/TeamPage.svelte +65 -0
  165. package/src/lib/components/TeamPageSection.svelte +108 -0
  166. package/src/lib/components/TeamPageTitle.svelte +89 -0
  167. package/src/lib/components/index.js +24 -0
  168. package/src/lib/portal/context.js +12 -0
  169. package/src/lib/portal/index.js +3 -0
  170. package/src/lib/portal/portal.svelte +14 -0
  171. package/src/lib/portal/slot.svelte +8 -0
  172. package/src/lib/scss/__code.scss +128 -0
  173. package/src/lib/scss/__containers.scss +99 -0
  174. package/src/lib/scss/__markdown.scss +447 -0
  175. package/src/lib/scss/__scrollbar.scss +60 -0
  176. package/src/lib/scss/__steps.scss +100 -0
  177. package/src/lib/scss/__theme.scss +238 -0
  178. package/src/lib/scss/__toc.scss +55 -0
  179. package/src/lib/scss/__utilities.scss +7 -0
  180. package/src/lib/scss/greg.scss +9 -0
  181. package/src/lib/spinner/spinner.svelte +42 -0
  182. package/svelte.config.js +146 -0
  183. package/types/index.d.ts +456 -0
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Remark + Rehype plugins for VitePress-style containers and GitHub-flavored alerts.
3
+ *
4
+ * Architecture: two-phase, no inner sub-pipeline needed.
5
+ *
6
+ * 1. remarkContainers (remark plugin)
7
+ * - Pass 0: converts GitHub-flavored alert blockquotes (`> [!NOTE]`) into
8
+ * containerBlock nodes.
9
+ * - Pass 1 & 2: converts `:::` fenced containers into containerBlock nodes.
10
+ * Inner content stays as real mdast so the normal pipeline processes it
11
+ * (rehype-shiki, rehype-slug, etc.)
12
+ *
13
+ * 2. rehypeContainers (rehype plugin)
14
+ * Visits `container-block` hast elements and emits the final
15
+ * <div class="custom-block …">…</div> or <details>…</details> HTML.
16
+ *
17
+ * ::: container shapes produced by remark:
18
+ *
19
+ * A) Single paragraph (no blank lines — remark collapses to one node):
20
+ * paragraph { text: "::: info\nContent\n:::" }
21
+ *
22
+ * B) Multi-node span (blank lines or block elements inside):
23
+ * paragraph { text: "::: info Title" }
24
+ * ... block nodes ...
25
+ * paragraph { text: ":::" }
26
+ *
27
+ * Does NOT require remark-directive or any sub-pipeline.
28
+ */
29
+
30
+ import { visit } from 'unist-util-visit';
31
+
32
+ // ─── Constants ────────────────────────────────────────────────────────────────
33
+
34
+ const defaultLabels = {
35
+ infoLabel: 'INFO',
36
+ tipLabel: 'TIP',
37
+ warningLabel: 'WARNING',
38
+ dangerLabel: 'DANGER',
39
+ detailsLabel: 'Details',
40
+ };
41
+
42
+ const knownTypes = ['info', 'tip', 'warning', 'danger', 'details'];
43
+
44
+ const OPEN_LINE_RE = /^:::[ \t]+(\w+)(?:[ \t]+(.+?))?[ \t]*$/;
45
+ const CLOSE_LINE_RE = /^:::[ \t]*$/;
46
+
47
+ /** Maps GitHub alert type names to VitePress container types. */
48
+ const githubAlertTypeMap = {
49
+ note: 'info',
50
+ tip: 'tip',
51
+ important: 'warning',
52
+ warning: 'warning',
53
+ caution: 'danger',
54
+ };
55
+
56
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
57
+
58
+ function getDefaultLabel(type, labels) {
59
+ return (
60
+ {
61
+ info: labels.infoLabel,
62
+ tip: labels.tipLabel,
63
+ warning: labels.warningLabel,
64
+ danger: labels.dangerLabel,
65
+ details: labels.detailsLabel,
66
+ }[type] ?? type.toUpperCase()
67
+ );
68
+ }
69
+
70
+ /** Creates a containerBlock mdast node carrying type/title + inner mdast children. */
71
+ function containerBlockNode(type, rawTitle, children) {
72
+ return {
73
+ type: 'containerBlock',
74
+ rawTitle,
75
+ data: {
76
+ hName: 'div',
77
+ hProperties: {
78
+ className: ['container-block'],
79
+ 'data-type': type,
80
+ 'data-title': rawTitle,
81
+ },
82
+ },
83
+ children,
84
+ };
85
+ }
86
+
87
+ /** Recursively extracts plain text from an mdast node. */
88
+ function nodeToText(node) {
89
+ if (node.type === 'text' || node.type === 'inlineCode') return node.value ?? '';
90
+ if (node.children) return node.children.map(nodeToText).join('');
91
+ return '';
92
+ }
93
+
94
+ /**
95
+ * Tries to parse a GitHub-flavored alert blockquote node.
96
+ *
97
+ * remark parses `[!NOTE]` as a `linkReference` (unresolved link), not plain
98
+ * text, so we handle both:
99
+ * - `text` node with value `[!NOTE]…`
100
+ * - `linkReference` node whose text content is `!NOTE`
101
+ *
102
+ * Returns `{ mappedType, title, innerNodes }` or `null`.
103
+ */
104
+ function parseGithubAlert(node) {
105
+ if (node?.type !== 'blockquote' || !node.children?.length) return null;
106
+
107
+ const firstPara = node.children[0];
108
+ if (firstPara?.type !== 'paragraph') return null;
109
+
110
+ const paraChildren = firstPara.children ?? [];
111
+ const first = paraChildren[0];
112
+ if (!first) return null;
113
+
114
+ let alertKey, remainderChildren;
115
+
116
+ if (first.type === 'text') {
117
+ // "[!NOTE] some text" collapsed into a text node
118
+ const m = first.value.match(/^\[!([a-zA-Z]+)\]\s*/);
119
+ if (!m) return null;
120
+ alertKey = m[1].toLowerCase();
121
+ const rest = first.value.slice(m[0].length);
122
+ remainderChildren = rest
123
+ ? [{ ...first, value: rest }, ...paraChildren.slice(1)]
124
+ : paraChildren.slice(1);
125
+ } else if (first.type === 'linkReference') {
126
+ // remark's normal parse: `[!NOTE]` → linkReference{ label: "!NOTE" }
127
+ const m = nodeToText(first).match(/^!([a-zA-Z]+)$/);
128
+ if (!m) return null;
129
+ alertKey = m[1].toLowerCase();
130
+ // Trim leading soft-break / newline between `[!TYPE]` and content
131
+ let rest = paraChildren.slice(1);
132
+ if (rest[0]?.type === 'break' || rest[0]?.type === 'softbreak') {
133
+ rest = rest.slice(1);
134
+ } else if (rest[0]?.type === 'text' && rest[0].value.startsWith('\n')) {
135
+ const trimmed = rest[0].value.slice(1);
136
+ rest = trimmed ? [{ ...rest[0], value: trimmed }, ...rest.slice(1)] : rest.slice(1);
137
+ }
138
+ remainderChildren = rest;
139
+ } else {
140
+ return null;
141
+ }
142
+
143
+ const mappedType = githubAlertTypeMap[alertKey];
144
+ if (!mappedType) return null;
145
+
146
+ const innerNodes = [
147
+ ...(remainderChildren.length ? [{ type: 'paragraph', children: remainderChildren }] : []),
148
+ ...node.children.slice(1),
149
+ ];
150
+
151
+ return { mappedType, title: alertKey.toUpperCase(), innerNodes };
152
+ }
153
+
154
+ // ─── Remark plugin ────────────────────────────────────────────────────────────
155
+
156
+ export function remarkContainers(userOptions = {}) {
157
+ return (tree) => {
158
+ const children = tree.children;
159
+
160
+ // Pass 0 — GitHub-flavored alerts: `> [!NOTE]`
161
+ for (let i = 0; i < children.length; i++) {
162
+ const parsed = parseGithubAlert(children[i]);
163
+ if (!parsed) continue;
164
+ children.splice(i, 1, containerBlockNode(parsed.mappedType, parsed.title, parsed.innerNodes));
165
+ }
166
+
167
+ // Apply ::: container syntax recursively (root + list items + blockquotes)
168
+ applyContainerSyntaxRecursive(tree);
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Walk the mdast bottom-up and apply Pass 1 + Pass 2 container syntax to
174
+ * every block-level children array — including those nested inside list items
175
+ * and blockquotes.
176
+ */
177
+ function applyContainerSyntaxRecursive(node) {
178
+ if (!node.children) return;
179
+
180
+ // Recurse into nested block containers first (bottom-up).
181
+ // Important: list items live under a `list` node, so recurse through any
182
+ // node that has children instead of whitelisting only a few node types.
183
+ for (const child of node.children) {
184
+ if (Array.isArray(child.children)) {
185
+ applyContainerSyntaxRecursive(child);
186
+ }
187
+ }
188
+
189
+ // Then process this level's children array
190
+ applyContainerPasses(node.children);
191
+ }
192
+
193
+ /**
194
+ * Apply Pass 1 (single-paragraph :::) and Pass 2 (multi-node :::) to a
195
+ * specific `children` array in-place. Extracted from the original inline
196
+ * implementation so it can be reused recursively.
197
+ */
198
+ function applyContainerPasses(children) {
199
+ // Pass 1 — single-paragraph ::: containers (no blank lines inside)
200
+ for (let i = 0; i < children.length; i++) {
201
+ const node = children[i];
202
+ if (node.type !== 'paragraph') continue;
203
+
204
+ const firstChild = node.children?.[0];
205
+ if (firstChild?.type !== 'text') continue;
206
+
207
+ const firstLine = firstChild.value.split('\n')[0].trim();
208
+ const openMatch = firstLine.match(OPEN_LINE_RE);
209
+ if (!openMatch) continue;
210
+
211
+ const lastChild = node.children[node.children.length - 1];
212
+ if (lastChild?.type !== 'text') continue;
213
+
214
+ const lastLine = lastChild.value.split('\n').at(-1).trim();
215
+ if (!CLOSE_LINE_RE.test(lastLine)) continue;
216
+
217
+ const type = openMatch[1].toLowerCase();
218
+ if (!knownTypes.includes(type)) continue;
219
+
220
+ // Strip the opening and closing ::: lines; collect the inline nodes in between.
221
+ const innerChildren = [];
222
+ for (let ci = 0; ci < node.children.length; ci++) {
223
+ const c = node.children[ci];
224
+ const isFirst = ci === 0;
225
+ const isLast = ci === node.children.length - 1;
226
+
227
+ if (isFirst && isLast) {
228
+ const afterOpen = c.value.slice(c.value.indexOf('\n') + 1);
229
+ const content = afterOpen.slice(0, afterOpen.lastIndexOf('\n'));
230
+ if (content.trim()) innerChildren.push({ ...c, value: content });
231
+ } else if (isFirst) {
232
+ const afterOpen = c.value.slice(c.value.indexOf('\n') + 1);
233
+ if (afterOpen) innerChildren.push({ ...c, value: afterOpen });
234
+ } else if (isLast) {
235
+ const beforeClose = c.value.slice(0, c.value.lastIndexOf('\n'));
236
+ if (beforeClose) innerChildren.push({ ...c, value: beforeClose });
237
+ } else {
238
+ innerChildren.push(c);
239
+ }
240
+ }
241
+
242
+ const innerNodes = innerChildren.length
243
+ ? [{ type: 'paragraph', children: innerChildren }]
244
+ : [];
245
+
246
+ children.splice(i, 1, containerBlockNode(type, openMatch[2] ?? '', innerNodes));
247
+ }
248
+
249
+ // Pass 2 — multi-node ::: containers (blank lines or block elements inside)
250
+ const spans = [];
251
+ const stack = [];
252
+ for (let i = 0; i < children.length; i++) {
253
+ const node = children[i];
254
+ if (node.type !== 'paragraph') continue;
255
+ const text = nodeToText(node).trim();
256
+ if (text.includes('\n')) continue; // already handled in Pass 1
257
+
258
+ const openMatch = text.match(OPEN_LINE_RE);
259
+ if (openMatch) {
260
+ stack.push({ openIdx: i, type: openMatch[1], title: openMatch[2] ?? '' });
261
+ continue;
262
+ }
263
+ if (CLOSE_LINE_RE.test(text) && stack.length > 0) {
264
+ const frame = stack.pop();
265
+ if (stack.length === 0) spans.push({ ...frame, closeIdx: i });
266
+ }
267
+ }
268
+ for (const { openIdx, closeIdx, type, title: rawTitle } of spans.reverse()) {
269
+ const lcType = type.toLowerCase();
270
+ if (!knownTypes.includes(lcType)) continue;
271
+ const innerNodes = children.slice(openIdx + 1, closeIdx);
272
+ children.splice(openIdx, closeIdx - openIdx + 1, containerBlockNode(lcType, rawTitle, innerNodes));
273
+ }
274
+ }
275
+
276
+ // ─── Rehype plugin ────────────────────────────────────────────────────────────
277
+ // Runs after rehype-shiki, rehype-slug, etc. have processed the inner content.
278
+ // Replaces <div class="container-block"> with the final custom-block markup.
279
+
280
+ export function rehypeContainers(userOptions = {}) {
281
+ const labels = { ...defaultLabels, ...userOptions };
282
+
283
+ return (tree) => {
284
+ visit(tree, 'element', (node, index, parent) => {
285
+ if (node.tagName !== 'div' || !node.properties?.className?.includes('container-block')) return;
286
+
287
+ const type = node.properties['data-type'];
288
+ const rawTitle = node.properties['data-title'] ?? '';
289
+
290
+ const isOpen = /\{open\}/i.test(rawTitle);
291
+ const title = rawTitle.replace(/\s*\{open\}\s*$/i, '').trim() || getDefaultLabel(type, labels);
292
+
293
+ if (type === 'details') {
294
+ parent.children.splice(index, 1, {
295
+ type: 'element',
296
+ tagName: 'details',
297
+ properties: {
298
+ className: ['custom-block', 'details'],
299
+ ...(isOpen ? { open: true } : {}),
300
+ },
301
+ children: [
302
+ {
303
+ type: 'element',
304
+ tagName: 'summary',
305
+ properties: { className: ['custom-block-title'] },
306
+ children: [{ type: 'text', value: title }],
307
+ },
308
+ ...node.children,
309
+ ],
310
+ });
311
+ } else {
312
+ parent.children.splice(index, 1, {
313
+ type: 'element',
314
+ tagName: 'div',
315
+ properties: { className: ['custom-block', type] },
316
+ children: [
317
+ {
318
+ type: 'element',
319
+ tagName: 'p',
320
+ properties: { className: ['custom-block-title'] },
321
+ children: [{ type: 'text', value: title }],
322
+ },
323
+ ...node.children,
324
+ ],
325
+ });
326
+ }
327
+ });
328
+ };
329
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * remarkCustomAnchors
3
+ *
4
+ * Supports VitePress-style custom heading IDs:
5
+ * ## My Heading {#custom-id}
6
+ *
7
+ * Strips the `{#id}` suffix from the heading text and sets
8
+ * `node.data.hProperties.id` so rehype-slug leaves it alone.
9
+ */
10
+ import { visit } from 'unist-util-visit';
11
+
12
+ const CUSTOM_ID_RE = /\s*\{#([\w-]+)\}\s*$/;
13
+
14
+ export function remarkCustomAnchors() {
15
+ return (tree) => {
16
+ visit(tree, 'heading', (node) => {
17
+ if (!node.children?.length) return;
18
+
19
+ // The custom id is always at the end — check the last child text node
20
+ const last = node.children[node.children.length - 1];
21
+ if (last?.type !== 'text') return;
22
+
23
+ const match = last.value.match(CUSTOM_ID_RE);
24
+ if (!match) return;
25
+
26
+ const id = match[1];
27
+
28
+ // Strip the {#id} suffix from the text
29
+ last.value = last.value.slice(0, last.value.length - match[0].length);
30
+
31
+ // Remove the node entirely if it became empty
32
+ if (last.value === '') {
33
+ node.children.pop();
34
+ }
35
+
36
+ // Set the id on the hast element — rehype-slug skips elements with existing id
37
+ node.data ??= {};
38
+ node.data.hProperties ??= {};
39
+ node.data.hProperties.id = id;
40
+ });
41
+ };
42
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * remarkEscapeSvelte
3
+ *
4
+ * Escapes `{` and `}` in plain Markdown *text* nodes so Svelte's compiler
5
+ * doesn't treat them as template expressions.
6
+ *
7
+ * Only TEXT nodes are touched. Raw HTML nodes (including Svelte component
8
+ * elements like `<Hero items={data} />`) are intentionally left unmodified so
9
+ * that Svelte prop-binding expressions `{…}` remain valid after compilation.
10
+ *
11
+ * Code nodes (inline code and fenced code blocks) are untouched — mdsvex
12
+ * wraps those in `{@html …}` itself.
13
+ */
14
+ import { visit } from 'unist-util-visit';
15
+
16
+ /** Characters that need escaping in raw Svelte template text */
17
+ const ESCAPE_RE = /[{}]/g;
18
+ const ESCAPE_MAP = { '{': '&#123;', '}': '&#125;' };
19
+
20
+ function escapeText(str) {
21
+ return str.replace(ESCAPE_RE, (ch) => ESCAPE_MAP[ch]);
22
+ }
23
+
24
+ export function remarkEscapeSvelte() {
25
+ return (tree) => {
26
+ // Only escape { } inside plain Markdown text nodes.
27
+ // HTML nodes are passed through unchanged so that Svelte component
28
+ // attributes like actions={[…]} compile correctly.
29
+ visit(tree, 'text', (node) => {
30
+ node.value = escapeText(node.value);
31
+ });
32
+ };
33
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * remarkGlobalComponents
3
+ *
4
+ * Injects a <script> block into every processed Markdown file that
5
+ * auto-imports all Greg UI components (Badge, Button, Hero, …).
6
+ *
7
+ * If the file already has a <script> block at or near the top, the
8
+ * import statement is merged into it instead of adding a second block.
9
+ *
10
+ * The import path uses the `$components` Vite alias which must be
11
+ * configured in vite.config.js → resolve.alias.
12
+ */
13
+
14
+ const COMPONENTS_IMPORT =
15
+ `import { Badge, Button, Image, Link, Feature, Features, Hero, ` +
16
+ `SocialLink, SocialLinks, TeamMember, TeamMembers, ` +
17
+ `TeamPage, TeamPageTitle, TeamPageSection, Steps } from '$components/index.js';`;
18
+
19
+ const SCRIPT_OPEN_RE = /^<script(\b[^>]*)>/i;
20
+ const SCRIPT_SETUP_ATTR_RE = /\bsetup\b/i;
21
+
22
+ /**
23
+ * Return the index of the first `html` node whose value begins with
24
+ * a <script> tag, searching all top-level nodes.
25
+ */
26
+ function findScriptNodeIndex(children) {
27
+ for (let i = 0; i < children.length; i++) {
28
+ const node = children[i];
29
+ if (node.type === 'html' && SCRIPT_OPEN_RE.test(node.value.trimStart())) {
30
+ return i;
31
+ }
32
+ }
33
+ return -1;
34
+ }
35
+
36
+ /**
37
+ * The remark plugin function.
38
+ */
39
+ export function remarkGlobalComponents() {
40
+ return (tree) => {
41
+ const { children } = tree;
42
+ const idx = findScriptNodeIndex(children);
43
+
44
+ if (idx !== -1) {
45
+ // Merge: inject the import after the opening <script …> tag.
46
+ // If the existing script has a `setup` attribute (Vue syntax), strip it
47
+ // so Svelte sees a standard <script> block.
48
+ const node = children[idx];
49
+ node.value = node.value.replace(
50
+ SCRIPT_OPEN_RE,
51
+ (match, attrs) => {
52
+ const cleanAttrs = attrs.replace(SCRIPT_SETUP_ATTR_RE, '').trim();
53
+ const openTag = cleanAttrs ? `<script ${cleanAttrs}>` : '<script>';
54
+ return `${openTag}\n ${COMPONENTS_IMPORT}`;
55
+ },
56
+ );
57
+ } else {
58
+ // Prepend a fresh <script> block
59
+ children.unshift({
60
+ type: 'html',
61
+ value: `<script>\n ${COMPONENTS_IMPORT}\n</script>`,
62
+ });
63
+ }
64
+ };
65
+ }