@honeydeck/honeydeck 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +25 -0
- package/DEVELOPMENT.md +522 -0
- package/LICENSE +21 -0
- package/Readme.md +49 -0
- package/SPEC.md +88 -0
- package/docs/components.md +63 -0
- package/docs/configuration.md +91 -0
- package/docs/getting-started.md +116 -0
- package/docs/kit-authoring.md +207 -0
- package/docs/kits.md +387 -0
- package/docs/local-development.md +95 -0
- package/docs/mermaid.md +198 -0
- package/docs/mobile.md +108 -0
- package/docs/navigation.md +93 -0
- package/docs/next-steps.md +377 -0
- package/docs/pdf-export.md +91 -0
- package/docs/presenter-mode.md +104 -0
- package/docs/slides.md +130 -0
- package/docs/slidev-migration.md +42 -0
- package/docs/steps-and-reveals.md +171 -0
- package/package.json +134 -0
- package/skills/SPEC.md +21 -0
- package/skills/honeydeck/SKILL.md +65 -0
- package/skills/presentation-writing/SKILL.md +75 -0
- package/skills/slidev-migration/SKILL.md +153 -0
- package/src/SPEC.md +89 -0
- package/src/assets.d.ts +30 -0
- package/src/cli/SPEC.md +230 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/banner.ts +9 -0
- package/src/cli/bin.js +5 -0
- package/src/cli/build.ts +229 -0
- package/src/cli/deck-path.ts +32 -0
- package/src/cli/dev.ts +263 -0
- package/src/cli/index.ts +126 -0
- package/src/cli/init.ts +369 -0
- package/src/cli/pdf.ts +923 -0
- package/src/cli/skill.ts +75 -0
- package/src/cli/templates/SPEC.md +70 -0
- package/src/cli/templates/deck-mdx.ts +15 -0
- package/src/cli/templates/package-json.ts +36 -0
- package/src/cli/templates/sparkle-button.ts +15 -0
- package/src/cli/templates/starter/components/SparkleButton.tsx +84 -0
- package/src/cli/templates/starter/deck.mdx +153 -0
- package/src/cli/templates/starter/styles.css +14 -0
- package/src/cli/templates/styles-css.ts +14 -0
- package/src/defaults.ts +1 -0
- package/src/layouts/ColorModeImage.tsx +55 -0
- package/src/layouts/SPEC.md +393 -0
- package/src/layouts/SlideFrame.tsx +48 -0
- package/src/layouts/bee/Blank.tsx +12 -0
- package/src/layouts/bee/Cover.tsx +70 -0
- package/src/layouts/bee/Default.tsx +42 -0
- package/src/layouts/bee/Image/Image.tsx +151 -0
- package/src/layouts/bee/Image/placeholder-dark.webp +0 -0
- package/src/layouts/bee/Image/placeholder-vertical-dark.webp +0 -0
- package/src/layouts/bee/Image/placeholder-vertical.webp +0 -0
- package/src/layouts/bee/Image/placeholder.webp +0 -0
- package/src/layouts/bee/ImageLeft.tsx +27 -0
- package/src/layouts/bee/ImageRight.tsx +27 -0
- package/src/layouts/bee/ImageSide.tsx +107 -0
- package/src/layouts/bee/Section.tsx +40 -0
- package/src/layouts/bee/TwoCol.tsx +108 -0
- package/src/layouts/bee/index.ts +40 -0
- package/src/layouts/clean/Blank.tsx +12 -0
- package/src/layouts/clean/Cover.tsx +58 -0
- package/src/layouts/clean/Default.tsx +33 -0
- package/src/layouts/clean/Image/Image.tsx +103 -0
- package/src/layouts/clean/ImageLeft.tsx +27 -0
- package/src/layouts/clean/ImageRight.tsx +27 -0
- package/src/layouts/clean/ImageSide.tsx +113 -0
- package/src/layouts/clean/Section.tsx +35 -0
- package/src/layouts/clean/TwoCol.tsx +63 -0
- package/src/layouts/clean/index.ts +40 -0
- package/src/layouts/index.ts +60 -0
- package/src/layouts/placeholders.ts +9 -0
- package/src/layouts/utils.ts +13 -0
- package/src/remark/SPEC.md +49 -0
- package/src/remark/h1-extract.ts +124 -0
- package/src/remark/index.ts +4 -0
- package/src/remark/shiki-code-blocks.ts +325 -0
- package/src/remark/step-numbering.ts +412 -0
- package/src/runtime/Deck.tsx +533 -0
- package/src/runtime/SPEC.md +256 -0
- package/src/runtime/SlideCanvas.tsx +95 -0
- package/src/runtime/TimelineContext.tsx +122 -0
- package/src/runtime/app-shell/index.html +31 -0
- package/src/runtime/app-shell/main.tsx +42 -0
- package/src/runtime/aspectRatio.ts +34 -0
- package/src/runtime/colorMode.ts +23 -0
- package/src/runtime/components/BrowserFrame.tsx +233 -0
- package/src/runtime/components/Button.tsx +57 -0
- package/src/runtime/components/CodeBlock.tsx +210 -0
- package/src/runtime/components/ColorModeCycleButton.tsx +59 -0
- package/src/runtime/components/ErrorBoundary.tsx +125 -0
- package/src/runtime/components/Keyboard.tsx +87 -0
- package/src/runtime/components/ListStyle.tsx +203 -0
- package/src/runtime/components/NavBar.tsx +223 -0
- package/src/runtime/components/NavBarButton.tsx +47 -0
- package/src/runtime/components/NavBarDivider.tsx +3 -0
- package/src/runtime/components/Notes.tsx +171 -0
- package/src/runtime/components/Reveal.tsx +82 -0
- package/src/runtime/components/RevealGroup.tsx +193 -0
- package/src/runtime/components/SPEC.md +263 -0
- package/src/runtime/components/SlideNumberBadge.tsx +11 -0
- package/src/runtime/components/TimelineSteps.tsx +115 -0
- package/src/runtime/components/index.ts +55 -0
- package/src/runtime/index.ts +42 -0
- package/src/runtime/inputOwnership.ts +68 -0
- package/src/runtime/keyboardTarget.ts +7 -0
- package/src/runtime/lastSlideRoute.ts +56 -0
- package/src/runtime/navigation.ts +211 -0
- package/src/runtime/router.ts +157 -0
- package/src/runtime/slideData.ts +137 -0
- package/src/runtime/sync.ts +267 -0
- package/src/runtime/types.ts +182 -0
- package/src/runtime/useKeyboardNav.ts +138 -0
- package/src/runtime/useSwipeNav.ts +257 -0
- package/src/runtime/views/DocsView.tsx +74 -0
- package/src/runtime/views/OverviewView.tsx +386 -0
- package/src/runtime/views/PresenterNotesPanel.tsx +76 -0
- package/src/runtime/views/PresenterView.tsx +340 -0
- package/src/runtime/views/SPEC.md +152 -0
- package/src/runtime/views/docs/ComponentsTab.tsx +178 -0
- package/src/runtime/views/docs/DocsHeader.tsx +101 -0
- package/src/runtime/views/docs/Intro.tsx +20 -0
- package/src/runtime/views/docs/LayoutsTab.tsx +324 -0
- package/src/runtime/views/docs/ThemeTab.tsx +110 -0
- package/src/runtime/views/index.ts +7 -0
- package/src/runtime/views/overviewGrid.ts +106 -0
- package/src/runtime/views/presenterPreview.ts +27 -0
- package/src/runtime/virtual-modules.d.ts +98 -0
- package/src/theme/SPEC.md +179 -0
- package/src/theme/base.css +623 -0
- package/src/theme/bee.css +35 -0
- package/src/theme/clean.css +38 -0
- package/src/vite-plugin/SPEC.md +114 -0
- package/src/vite-plugin/component-doc-crawler.ts +350 -0
- package/src/vite-plugin/deck-loader.ts +148 -0
- package/src/vite-plugin/index.ts +373 -0
- package/src/vite-plugin/layout-demo-crawler.ts +802 -0
- package/src/vite-plugin/splitter.ts +353 -0
- package/src/vite-plugin/token-manifest.ts +163 -0
- package/src/vite-plugin/virtual-modules.ts +587 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remark plugin: replace fenced code blocks with `<HoneydeckCodeBlock>` JSX elements.
|
|
3
|
+
*
|
|
4
|
+
* ### Pipeline position
|
|
5
|
+
* Must run AFTER `remarkStepNumbering` so it can read the `honeydeckStartAt` value
|
|
6
|
+
* that step-numbering annotates onto code MDAST nodes.
|
|
7
|
+
*
|
|
8
|
+
* ### What it does
|
|
9
|
+
* 1. Collects all `code` MDAST nodes.
|
|
10
|
+
* 2. For each code node:
|
|
11
|
+
* a. Loads the language grammar lazily into the shared shiki singleton.
|
|
12
|
+
* b. Highlights the code with dual themes (github-light + github-dark) using
|
|
13
|
+
* CSS custom properties (`defaultColor: false`). Each `.line` span gets a
|
|
14
|
+
* `data-line` attribute for runtime dimming.
|
|
15
|
+
* c. Parses the fence meta string (`{2|4-5|all}`) into step groups.
|
|
16
|
+
* d. Replaces the code node with a `mdxJsxFlowElement` for `<HoneydeckCodeBlock>`
|
|
17
|
+
* carrying three props: `html` (highlighted HTML string), `stepsJson`
|
|
18
|
+
* (JSON-encoded step groups), and `startAt` (1-based timeline step where
|
|
19
|
+
* the second group activates, from `node.data.honeydeckStartAt`), plus
|
|
20
|
+
* `source` (the original fenced code text for clipboard copying).
|
|
21
|
+
* 3. Injects a single `import { HoneydeckCodeBlock } from '@honeydeck/honeydeck/components/code-block'`
|
|
22
|
+
* declaration into the MDAST root when at least one code block was transformed.
|
|
23
|
+
*
|
|
24
|
+
* ### CSS variable activation
|
|
25
|
+
* The highlighted HTML uses shiki's dual-theme variable names
|
|
26
|
+
* (`--shiki-light`, `--shiki-dark`, `--shiki-light-bg`, `--shiki-dark-bg`).
|
|
27
|
+
* `src/theme/base.css` activates the correct variable set based on the
|
|
28
|
+
* `[data-honeydeck-color-mode]` attribute set by the Deck component.
|
|
29
|
+
*
|
|
30
|
+
* ### Step meta syntax
|
|
31
|
+
* - `{2}` — highlight line 2 immediately; consumes no timeline steps
|
|
32
|
+
* - `{2|4-5|all}` — three step groups; first group is baseline, then later groups activate at `startAt`, `startAt+1`
|
|
33
|
+
*
|
|
34
|
+
* @see HoneydeckCodeBlock in `src/runtime/components/CodeBlock.tsx`
|
|
35
|
+
* @see remarkStepNumbering for the counter that provides `honeydeckStartAt`
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import type { Program } from "estree";
|
|
39
|
+
import type { Code, Root } from "mdast";
|
|
40
|
+
import type {
|
|
41
|
+
MdxJsxAttribute,
|
|
42
|
+
MdxJsxAttributeValueExpression,
|
|
43
|
+
MdxJsxFlowElement,
|
|
44
|
+
} from "mdast-util-mdx-jsx";
|
|
45
|
+
import type { MdxjsEsm } from "mdast-util-mdxjs-esm";
|
|
46
|
+
import { getSingletonHighlighter } from "shiki";
|
|
47
|
+
import type { Plugin } from "unified";
|
|
48
|
+
import { visit } from "unist-util-visit";
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Types
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/** A single step-through group: either an array of 1-based line numbers or 'all'. */
|
|
55
|
+
export type StepGroup = number[] | "all";
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Singleton shiki highlighter
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
let highlighterPromise: Promise<
|
|
62
|
+
Awaited<ReturnType<typeof getSingletonHighlighter>>
|
|
63
|
+
> | null = null;
|
|
64
|
+
|
|
65
|
+
function getHighlighter() {
|
|
66
|
+
if (!highlighterPromise) {
|
|
67
|
+
highlighterPromise = getSingletonHighlighter({
|
|
68
|
+
themes: ["github-light", "github-dark"],
|
|
69
|
+
langs: [], // languages loaded lazily per code block
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return highlighterPromise;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function loadLang(
|
|
76
|
+
highlighter: Awaited<ReturnType<typeof getSingletonHighlighter>>,
|
|
77
|
+
lang: string,
|
|
78
|
+
): Promise<string> {
|
|
79
|
+
if (!lang || lang === "text" || lang === "plaintext") return "text";
|
|
80
|
+
try {
|
|
81
|
+
await highlighter.loadLanguage(
|
|
82
|
+
lang as Parameters<typeof highlighter.loadLanguage>[0],
|
|
83
|
+
);
|
|
84
|
+
return lang;
|
|
85
|
+
} catch {
|
|
86
|
+
// Language not in shiki's bundle — fall back to plain text
|
|
87
|
+
return "text";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Step meta parsing
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parse a code fence meta string like `{2|4-5|all}` into an array of step groups.
|
|
97
|
+
*
|
|
98
|
+
* Returns `[]` when there is no `{…}` block in the meta string.
|
|
99
|
+
* Even a single group like `{2}` produces `[[2]]` — a baseline highlight that
|
|
100
|
+
* consumes no timeline steps.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* parseStepMeta('{2|4-5|all}') // → [[2], [4,5], 'all']
|
|
104
|
+
* parseStepMeta('{2}') // → [[2]] (baseline highlight, no timeline step)
|
|
105
|
+
* parseStepMeta(null) // → []
|
|
106
|
+
*/
|
|
107
|
+
export function parseStepMeta(meta: string | null | undefined): StepGroup[] {
|
|
108
|
+
if (!meta) return [];
|
|
109
|
+
|
|
110
|
+
const match = meta.match(/\{([^}]+)\}/);
|
|
111
|
+
if (!match?.[1]) return [];
|
|
112
|
+
|
|
113
|
+
const groupStrings = match[1].split("|").filter(Boolean);
|
|
114
|
+
if (groupStrings.length === 0) return [];
|
|
115
|
+
// Note: single groups are valid — they represent a baseline highlight and
|
|
116
|
+
// consume no timeline steps.
|
|
117
|
+
|
|
118
|
+
return groupStrings.map((group): StepGroup => {
|
|
119
|
+
const trimmed = group.trim();
|
|
120
|
+
if (trimmed === "all") return "all";
|
|
121
|
+
|
|
122
|
+
const lines: number[] = [];
|
|
123
|
+
for (const part of trimmed.split(",")) {
|
|
124
|
+
const dashIdx = part.indexOf("-");
|
|
125
|
+
if (dashIdx !== -1) {
|
|
126
|
+
const start = parseInt(part.slice(0, dashIdx).trim(), 10);
|
|
127
|
+
const end = parseInt(part.slice(dashIdx + 1).trim(), 10);
|
|
128
|
+
if (!Number.isNaN(start) && !Number.isNaN(end)) {
|
|
129
|
+
for (let i = start; i <= end; i++) lines.push(i);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
const n = parseInt(part.trim(), 10);
|
|
133
|
+
if (!Number.isNaN(n)) lines.push(n);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return lines;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// AST helpers
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
/** Build a string-valued JSX attribute (prop="value"). */
|
|
145
|
+
function makeStringAttr(name: string, value: string): MdxJsxAttribute {
|
|
146
|
+
return { type: "mdxJsxAttribute", name, value };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Build a numeric JSX attribute (prop={N}). */
|
|
150
|
+
function makeNumericAttr(name: string, value: number): MdxJsxAttribute {
|
|
151
|
+
const estree: Program = {
|
|
152
|
+
type: "Program",
|
|
153
|
+
sourceType: "module",
|
|
154
|
+
comments: [],
|
|
155
|
+
body: [
|
|
156
|
+
{
|
|
157
|
+
type: "ExpressionStatement",
|
|
158
|
+
expression: { type: "Literal", value, raw: String(value) },
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
const valueExpr: MdxJsxAttributeValueExpression = {
|
|
163
|
+
type: "mdxJsxAttributeValueExpression",
|
|
164
|
+
value: String(value),
|
|
165
|
+
data: { estree },
|
|
166
|
+
};
|
|
167
|
+
return { type: "mdxJsxAttribute", name, value: valueExpr };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Prepend an `import { specifier } from 'source'` declaration to the MDAST
|
|
172
|
+
* root, placed after any leading `yaml` / `mdxjsEsm` nodes so it sits with
|
|
173
|
+
* the other imports.
|
|
174
|
+
*
|
|
175
|
+
* Skips injection if an import for `specifier` already exists.
|
|
176
|
+
*/
|
|
177
|
+
function injectImport(tree: Root, specifier: string, source: string): void {
|
|
178
|
+
// Avoid duplicate imports
|
|
179
|
+
const alreadyImported = tree.children.some(
|
|
180
|
+
(n) =>
|
|
181
|
+
n.type === "mdxjsEsm" &&
|
|
182
|
+
(n as unknown as { value: string }).value?.includes(specifier),
|
|
183
|
+
);
|
|
184
|
+
if (alreadyImported) return;
|
|
185
|
+
|
|
186
|
+
// Find insertion point: after any leading yaml + mdxjsEsm nodes
|
|
187
|
+
let insertAt = 0;
|
|
188
|
+
for (let i = 0; i < tree.children.length; i++) {
|
|
189
|
+
const t = tree.children[i]?.type;
|
|
190
|
+
if (t === "yaml" || t === "mdxjsEsm") {
|
|
191
|
+
insertAt = i + 1;
|
|
192
|
+
} else {
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const importNode: MdxjsEsm = {
|
|
198
|
+
type: "mdxjsEsm",
|
|
199
|
+
value: `import { ${specifier} } from '${source}'`,
|
|
200
|
+
data: {
|
|
201
|
+
estree: {
|
|
202
|
+
type: "Program",
|
|
203
|
+
sourceType: "module",
|
|
204
|
+
comments: [],
|
|
205
|
+
body: [
|
|
206
|
+
{
|
|
207
|
+
type: "ImportDeclaration",
|
|
208
|
+
specifiers: [
|
|
209
|
+
{
|
|
210
|
+
type: "ImportSpecifier",
|
|
211
|
+
imported: { type: "Identifier", name: specifier },
|
|
212
|
+
local: { type: "Identifier", name: specifier },
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
source: { type: "Literal", value: source, raw: `'${source}'` },
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
} as unknown as Program,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
tree.children.splice(
|
|
223
|
+
insertAt,
|
|
224
|
+
0,
|
|
225
|
+
importNode as unknown as (typeof tree.children)[number],
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Plugin
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
const COMPONENT_NAME = "HoneydeckCodeBlock";
|
|
234
|
+
const IMPORT_SOURCE = "@honeydeck/honeydeck/components/code-block";
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Async remark plugin that transforms fenced code blocks to `<HoneydeckCodeBlock>`
|
|
238
|
+
* JSX elements with pre-highlighted HTML (via shiki dual themes).
|
|
239
|
+
*
|
|
240
|
+
* Plugin ordering: `[remarkFrontmatter, remarkH1Extract, remarkStepNumbering, remarkShikiCodeBlocks]`
|
|
241
|
+
*/
|
|
242
|
+
export const remarkShikiCodeBlocks: Plugin<[], Root> = () => async (tree) => {
|
|
243
|
+
// Collect code nodes before mutating the tree (avoids iterator invalidation).
|
|
244
|
+
type CodeEntry = {
|
|
245
|
+
node: Code;
|
|
246
|
+
index: number;
|
|
247
|
+
parent: { children: unknown[] };
|
|
248
|
+
};
|
|
249
|
+
const codeEntries: CodeEntry[] = [];
|
|
250
|
+
|
|
251
|
+
visit(tree, "code", (node, index, parent) => {
|
|
252
|
+
if (index !== null && index !== undefined && parent) {
|
|
253
|
+
codeEntries.push({
|
|
254
|
+
node: node as unknown as Code,
|
|
255
|
+
index: index as number,
|
|
256
|
+
parent: parent as unknown as { children: unknown[] },
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
if (codeEntries.length === 0) return;
|
|
262
|
+
|
|
263
|
+
const highlighter = await getHighlighter();
|
|
264
|
+
let didTransform = false;
|
|
265
|
+
|
|
266
|
+
for (const { node, index, parent } of codeEntries) {
|
|
267
|
+
const rawLang = node.lang ?? "text";
|
|
268
|
+
const meta = node.meta;
|
|
269
|
+
const steps = parseStepMeta(meta);
|
|
270
|
+
const startAt: number =
|
|
271
|
+
((node.data as Record<string, unknown> | undefined)
|
|
272
|
+
?.honeydeckStartAt as number) ?? 0;
|
|
273
|
+
|
|
274
|
+
// Load language grammar (no-op if already cached)
|
|
275
|
+
const lang = await loadLang(highlighter, rawLang);
|
|
276
|
+
|
|
277
|
+
// Highlight with dual themes (CSS variable approach)
|
|
278
|
+
let html: string;
|
|
279
|
+
try {
|
|
280
|
+
html = highlighter.codeToHtml(node.value, {
|
|
281
|
+
lang,
|
|
282
|
+
themes: { light: "github-light", dark: "github-dark" },
|
|
283
|
+
defaultColor: false,
|
|
284
|
+
transformers: [
|
|
285
|
+
{
|
|
286
|
+
line(lineNode, lineNumber) {
|
|
287
|
+
// Ensure properties bag exists, then stamp data-line
|
|
288
|
+
if (!lineNode.properties) lineNode.properties = {};
|
|
289
|
+
lineNode.properties["data-line"] = lineNumber;
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
});
|
|
294
|
+
} catch {
|
|
295
|
+
// Fallback: plain pre/code block without syntax colours
|
|
296
|
+
const escaped = node.value
|
|
297
|
+
.replace(/&/g, "&")
|
|
298
|
+
.replace(/</g, "<")
|
|
299
|
+
.replace(/>/g, ">");
|
|
300
|
+
html = `<pre class="honeydeck-code-plain"><code>${escaped}</code></pre>`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Build <HoneydeckCodeBlock html="..." stepsJson="..." startAt={N} source="..." />
|
|
304
|
+
const jsxNode: MdxJsxFlowElement = {
|
|
305
|
+
type: "mdxJsxFlowElement",
|
|
306
|
+
name: COMPONENT_NAME,
|
|
307
|
+
attributes: [
|
|
308
|
+
makeStringAttr("html", html),
|
|
309
|
+
makeStringAttr("stepsJson", JSON.stringify(steps)),
|
|
310
|
+
makeNumericAttr("startAt", startAt),
|
|
311
|
+
makeStringAttr("source", node.value),
|
|
312
|
+
],
|
|
313
|
+
children: [],
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Replace code node in-place (1 → 1, so sibling indices stay valid)
|
|
317
|
+
parent.children.splice(index, 1, jsxNode);
|
|
318
|
+
didTransform = true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Inject the import once for the whole slide
|
|
322
|
+
if (didTransform) {
|
|
323
|
+
injectImport(tree, COMPONENT_NAME, IMPORT_SOURCE);
|
|
324
|
+
}
|
|
325
|
+
};
|