@unfold-mdx/react 0.0.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.
- package/LICENSE +21 -0
- package/README.md +275 -0
- package/dist/chunk-OX5XKYQT.js +218 -0
- package/dist/codeDiff-BVwZfdt9.d.cts +19 -0
- package/dist/codeDiff-BVwZfdt9.d.ts +19 -0
- package/dist/diff/index.cjs +248 -0
- package/dist/diff/index.d.cts +37 -0
- package/dist/diff/index.d.ts +37 -0
- package/dist/diff/index.js +14 -0
- package/dist/index.cjs +626 -0
- package/dist/index.d.cts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +378 -0
- package/dist/shiki/index.cjs +125 -0
- package/dist/shiki/index.d.cts +14 -0
- package/dist/shiki/index.d.ts +14 -0
- package/dist/shiki/index.js +100 -0
- package/package.json +88 -0
- package/theme.css +212 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { SentenceDiff } from './diff/index.js';
|
|
2
|
+
export { sentenceDiff, tokenize, tokenizeCodeLine, tokenizeLines } from './diff/index.js';
|
|
3
|
+
import { C as CodeLineDiff } from './codeDiff-BVwZfdt9.js';
|
|
4
|
+
export { a as CodeToken, c as codeDiff } from './codeDiff-BVwZfdt9.js';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import React__default from 'react';
|
|
7
|
+
import { ShikiHighlighter } from './shiki/index.js';
|
|
8
|
+
|
|
9
|
+
interface DepthProps {
|
|
10
|
+
selectedIndex?: number;
|
|
11
|
+
defaultIndex?: number;
|
|
12
|
+
onChange?: (i: number) => void;
|
|
13
|
+
orientation?: "horizontal" | "vertical";
|
|
14
|
+
ratio?: number;
|
|
15
|
+
show?: "both" | "prose" | "code";
|
|
16
|
+
indicators?: boolean;
|
|
17
|
+
buttonVariant?: "text" | "arrow" | "chevron" | "minimal";
|
|
18
|
+
animate?: boolean;
|
|
19
|
+
highlight?: boolean;
|
|
20
|
+
highlighter?: ShikiHighlighter;
|
|
21
|
+
children: React__default.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
declare function Depth({ selectedIndex, defaultIndex, onChange, orientation, ratio, show, indicators, buttonVariant, animate, highlight, highlighter, children, }: DepthProps): React__default.JSX.Element;
|
|
24
|
+
|
|
25
|
+
interface DepthLevelProps {
|
|
26
|
+
label: string;
|
|
27
|
+
children: React__default.ReactNode;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* DepthLevel acts as a slot component to hold a level's label and prose snapshot.
|
|
32
|
+
*
|
|
33
|
+
* - Inside <Depth> (after mounting/hydration), it is parsed and not rendered directly.
|
|
34
|
+
* - Outside <Depth> (or during SSR / before hydration), it falls back to rendering
|
|
35
|
+
* its children wrapped in a container with a data-depth-level attribute.
|
|
36
|
+
*/
|
|
37
|
+
declare function DepthLevel({ label, children, ...props }: DepthLevelProps): React__default.JSX.Element;
|
|
38
|
+
declare namespace DepthLevel {
|
|
39
|
+
var unfoldType: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface DepthCodeProps extends React__default.HTMLAttributes<HTMLPreElement> {
|
|
43
|
+
lang?: string;
|
|
44
|
+
label?: string;
|
|
45
|
+
children: string;
|
|
46
|
+
}
|
|
47
|
+
declare function DepthCode({ lang, label, children, ...rest }: DepthCodeProps): React__default.JSX.Element;
|
|
48
|
+
declare namespace DepthCode {
|
|
49
|
+
var unfoldType: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface DepthPaneProps {
|
|
53
|
+
show: "both" | "prose" | "code";
|
|
54
|
+
proseDiff: SentenceDiff[];
|
|
55
|
+
codeDiff: CodeLineDiff[];
|
|
56
|
+
justEntered: boolean;
|
|
57
|
+
animate: boolean;
|
|
58
|
+
codeLang?: string;
|
|
59
|
+
codeText?: string;
|
|
60
|
+
highlighter?: ShikiHighlighter;
|
|
61
|
+
highlight?: boolean;
|
|
62
|
+
}
|
|
63
|
+
declare function DepthPane({ show, proseDiff, codeDiff, justEntered, animate, codeLang, codeText, highlighter, highlight, }: DepthPaneProps): React.JSX.Element;
|
|
64
|
+
|
|
65
|
+
export { CodeLineDiff, Depth, DepthCode, type DepthCodeProps, DepthLevel, type DepthLevelProps, DepthPane, type DepthProps, SentenceDiff };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codeDiff,
|
|
3
|
+
sentenceDiff,
|
|
4
|
+
tokenize,
|
|
5
|
+
tokenizeCodeLine,
|
|
6
|
+
tokenizeLines
|
|
7
|
+
} from "./chunk-OX5XKYQT.js";
|
|
8
|
+
|
|
9
|
+
// src/components/Depth.tsx
|
|
10
|
+
import React, { useState, useEffect, useMemo, useRef } from "react";
|
|
11
|
+
|
|
12
|
+
// src/context/DepthContext.tsx
|
|
13
|
+
import { createContext } from "react";
|
|
14
|
+
var DepthContext = createContext(null);
|
|
15
|
+
|
|
16
|
+
// src/components/DepthLevel.tsx
|
|
17
|
+
import { jsx } from "react/jsx-runtime";
|
|
18
|
+
function DepthLevel({ label, children, ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx("div", { "data-depth-level": true, ...props, children });
|
|
20
|
+
}
|
|
21
|
+
DepthLevel.unfoldType = "level";
|
|
22
|
+
|
|
23
|
+
// src/components/DepthCode.tsx
|
|
24
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
25
|
+
function DepthCode({ lang, label, children, ...rest }) {
|
|
26
|
+
return /* @__PURE__ */ jsx2("pre", { "data-lang": lang, ...rest, children: /* @__PURE__ */ jsx2("code", { children }) });
|
|
27
|
+
}
|
|
28
|
+
DepthCode.unfoldType = "code";
|
|
29
|
+
|
|
30
|
+
// src/components/DepthPane.tsx
|
|
31
|
+
import { useSyncExternalStore } from "react";
|
|
32
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
33
|
+
var noopSubscribe = () => () => {
|
|
34
|
+
};
|
|
35
|
+
function DepthPane({
|
|
36
|
+
show,
|
|
37
|
+
proseDiff,
|
|
38
|
+
codeDiff: codeDiff2,
|
|
39
|
+
justEntered,
|
|
40
|
+
animate,
|
|
41
|
+
codeLang,
|
|
42
|
+
codeText,
|
|
43
|
+
highlighter,
|
|
44
|
+
highlight = true
|
|
45
|
+
}) {
|
|
46
|
+
const showProse = show === "both" || show === "prose";
|
|
47
|
+
const showCode = show === "both" || show === "code";
|
|
48
|
+
const enterState = animate && justEntered ? "true" : void 0;
|
|
49
|
+
const getStatus = (itemKind) => {
|
|
50
|
+
if (!highlight || itemKind === "equal") return "equal";
|
|
51
|
+
return "added";
|
|
52
|
+
};
|
|
53
|
+
useSyncExternalStore(
|
|
54
|
+
highlighter?.subscribe ?? noopSubscribe,
|
|
55
|
+
() => highlighter?.getSnapshot() ?? 0,
|
|
56
|
+
() => highlighter?.getSnapshot() ?? 0
|
|
57
|
+
);
|
|
58
|
+
let finalCodeDiff = codeDiff2;
|
|
59
|
+
if (highlighter && codeText !== void 0 && codeLang !== void 0) {
|
|
60
|
+
const highlighted = highlighter.highlight(codeText, codeLang, codeDiff2);
|
|
61
|
+
if (highlighted) {
|
|
62
|
+
finalCodeDiff = highlighted;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return /* @__PURE__ */ jsxs("div", { "data-unfold-panes": true, children: [
|
|
66
|
+
showProse && /* @__PURE__ */ jsx3("div", { "data-unfold-pane": "prose", "aria-live": "polite", children: proseDiff.filter((item) => item.kind !== "removed").map((item, index) => {
|
|
67
|
+
const status = getStatus(item.kind);
|
|
68
|
+
const sentenceText = item.kind === "modified" ? item.after : item.sentence;
|
|
69
|
+
return /* @__PURE__ */ jsxs(
|
|
70
|
+
"span",
|
|
71
|
+
{
|
|
72
|
+
"data-sentence": status,
|
|
73
|
+
"data-enter": status === "added" ? enterState : void 0,
|
|
74
|
+
children: [
|
|
75
|
+
sentenceText,
|
|
76
|
+
" "
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
`prose-${index}`
|
|
80
|
+
);
|
|
81
|
+
}) }),
|
|
82
|
+
showCode && /* @__PURE__ */ jsx3("div", { "data-unfold-pane": "code", "aria-live": "polite", children: /* @__PURE__ */ jsx3("pre", { "data-lang": codeLang, children: /* @__PURE__ */ jsx3("code", { children: finalCodeDiff.map((lineDiff, lineIndex) => {
|
|
83
|
+
const lineText = lineDiff.tokens ? lineDiff.tokens.map((t) => t.text).join("") : lineDiff.line || "";
|
|
84
|
+
const isWhitespaceOnly = lineText.trim() === "";
|
|
85
|
+
const lineStatus = isWhitespaceOnly ? "equal" : getStatus(lineDiff.kind);
|
|
86
|
+
if (lineDiff.kind === "equal" && !lineDiff.tokens) {
|
|
87
|
+
return /* @__PURE__ */ jsxs("span", { "data-code-line": "equal", children: [
|
|
88
|
+
lineDiff.line,
|
|
89
|
+
"\n"
|
|
90
|
+
] }, `line-${lineIndex}`);
|
|
91
|
+
}
|
|
92
|
+
return /* @__PURE__ */ jsxs(
|
|
93
|
+
"span",
|
|
94
|
+
{
|
|
95
|
+
"data-code-line": lineStatus,
|
|
96
|
+
"data-enter": lineStatus === "added" ? enterState : void 0,
|
|
97
|
+
children: [
|
|
98
|
+
lineDiff.tokens?.map((token, tokenIndex) => {
|
|
99
|
+
const tokenStatus = getStatus(token.kind);
|
|
100
|
+
return /* @__PURE__ */ jsx3(
|
|
101
|
+
"span",
|
|
102
|
+
{
|
|
103
|
+
"data-code-token": isWhitespaceOnly ? "equal" : tokenStatus,
|
|
104
|
+
style: token.color ? { color: token.color } : void 0,
|
|
105
|
+
children: token.text
|
|
106
|
+
},
|
|
107
|
+
`token-${tokenIndex}`
|
|
108
|
+
);
|
|
109
|
+
}),
|
|
110
|
+
"\n"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
`line-${lineIndex}`
|
|
114
|
+
);
|
|
115
|
+
}) }) }) })
|
|
116
|
+
] });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/components/controls/Controls.tsx
|
|
120
|
+
import { useContext } from "react";
|
|
121
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
122
|
+
var BUTTON_LABELS = {
|
|
123
|
+
text: { prev: "Prev", next: "Next" },
|
|
124
|
+
arrow: { prev: "\u2190 Prev", next: "Next \u2192" },
|
|
125
|
+
chevron: { prev: "\u2039 Prev", next: "Next \u203A" },
|
|
126
|
+
minimal: { prev: "\u2039", next: "\u203A" }
|
|
127
|
+
};
|
|
128
|
+
function Controls({ buttonVariant, indicators }) {
|
|
129
|
+
const context = useContext(DepthContext);
|
|
130
|
+
if (!context) return null;
|
|
131
|
+
const { selectedIndex, totalSteps, advance, back, goTo, labels } = context;
|
|
132
|
+
const isFirst = selectedIndex === 0;
|
|
133
|
+
const isLast = selectedIndex === totalSteps - 1;
|
|
134
|
+
const btnText = BUTTON_LABELS[buttonVariant] || BUTTON_LABELS.text;
|
|
135
|
+
return /* @__PURE__ */ jsxs2("div", { "data-unfold-controls": true, "data-button-variant": buttonVariant, children: [
|
|
136
|
+
/* @__PURE__ */ jsx4(
|
|
137
|
+
"button",
|
|
138
|
+
{
|
|
139
|
+
"data-unfold-prev": true,
|
|
140
|
+
"aria-disabled": isFirst ? "true" : "false",
|
|
141
|
+
"aria-label": !isFirst ? `Go back to step: ${labels[selectedIndex - 1]}` : void 0,
|
|
142
|
+
onClick: back,
|
|
143
|
+
children: btnText.prev
|
|
144
|
+
}
|
|
145
|
+
),
|
|
146
|
+
indicators && /* @__PURE__ */ jsx4("div", { "data-unfold-indicators": true, role: "tablist", children: Array.from({ length: totalSteps }).map((_, i) => /* @__PURE__ */ jsx4(
|
|
147
|
+
"button",
|
|
148
|
+
{
|
|
149
|
+
role: "tab",
|
|
150
|
+
"aria-selected": i === selectedIndex,
|
|
151
|
+
"data-unfold-dot": true,
|
|
152
|
+
"data-active": i === selectedIndex ? "true" : void 0,
|
|
153
|
+
"aria-label": `Go to step: ${labels[i] || i + 1}`,
|
|
154
|
+
onClick: () => goTo(i)
|
|
155
|
+
},
|
|
156
|
+
i
|
|
157
|
+
)) }),
|
|
158
|
+
/* @__PURE__ */ jsx4(
|
|
159
|
+
"button",
|
|
160
|
+
{
|
|
161
|
+
"data-unfold-next": true,
|
|
162
|
+
"aria-disabled": isLast ? "true" : "false",
|
|
163
|
+
"aria-label": !isLast ? `Advance to step: ${labels[selectedIndex + 1]}` : void 0,
|
|
164
|
+
onClick: advance,
|
|
165
|
+
children: btnText.next
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
] });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/components/Depth.tsx
|
|
172
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
173
|
+
function getTextContent(node) {
|
|
174
|
+
if (node === null || node === void 0) {
|
|
175
|
+
return "";
|
|
176
|
+
}
|
|
177
|
+
if (typeof node === "string" || typeof node === "number") {
|
|
178
|
+
return String(node);
|
|
179
|
+
}
|
|
180
|
+
if (Array.isArray(node)) {
|
|
181
|
+
return node.map(getTextContent).join("");
|
|
182
|
+
}
|
|
183
|
+
if (React.isValidElement(node)) {
|
|
184
|
+
return getTextContent(node.props.children);
|
|
185
|
+
}
|
|
186
|
+
return "";
|
|
187
|
+
}
|
|
188
|
+
function Depth({
|
|
189
|
+
selectedIndex,
|
|
190
|
+
defaultIndex,
|
|
191
|
+
onChange,
|
|
192
|
+
orientation = "horizontal",
|
|
193
|
+
ratio = 0.5,
|
|
194
|
+
show = "both",
|
|
195
|
+
indicators = false,
|
|
196
|
+
buttonVariant = "text",
|
|
197
|
+
animate = true,
|
|
198
|
+
highlight = true,
|
|
199
|
+
highlighter,
|
|
200
|
+
children
|
|
201
|
+
}) {
|
|
202
|
+
const isControlled = selectedIndex !== void 0 && onChange !== void 0;
|
|
203
|
+
const [internalIndex, setInternalIndex] = useState(defaultIndex ?? 0);
|
|
204
|
+
const currentIndex = isControlled ? selectedIndex : internalIndex;
|
|
205
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
setIsMounted(true);
|
|
208
|
+
}, []);
|
|
209
|
+
const parsedChildren = useMemo(() => {
|
|
210
|
+
const proseLevels = [];
|
|
211
|
+
const codeLevels = [];
|
|
212
|
+
React.Children.forEach(children, (child) => {
|
|
213
|
+
if (React.isValidElement(child)) {
|
|
214
|
+
const type = child.type?.unfoldType || child.type;
|
|
215
|
+
if (type === "level" || type === DepthLevel) {
|
|
216
|
+
proseLevels.push({
|
|
217
|
+
label: child.props.label || "",
|
|
218
|
+
text: getTextContent(child.props.children)
|
|
219
|
+
});
|
|
220
|
+
} else if (type === "code" || type === DepthCode) {
|
|
221
|
+
codeLevels.push({
|
|
222
|
+
lang: child.props.lang || "",
|
|
223
|
+
label: child.props.label || "",
|
|
224
|
+
text: getTextContent(child.props.children)
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return { proseLevels, codeLevels };
|
|
230
|
+
}, [children]);
|
|
231
|
+
const proseDiffTable = useMemo(() => {
|
|
232
|
+
const table = [];
|
|
233
|
+
if (parsedChildren.proseLevels.length > 0) {
|
|
234
|
+
table.push(sentenceDiff("", parsedChildren.proseLevels[0].text));
|
|
235
|
+
for (let i = 1; i < parsedChildren.proseLevels.length; i++) {
|
|
236
|
+
table.push(sentenceDiff(parsedChildren.proseLevels[i - 1].text, parsedChildren.proseLevels[i].text));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return table;
|
|
240
|
+
}, [parsedChildren.proseLevels]);
|
|
241
|
+
const codeDiffTable = useMemo(() => {
|
|
242
|
+
const table = [];
|
|
243
|
+
if (parsedChildren.codeLevels.length > 0) {
|
|
244
|
+
table.push(codeDiff("", parsedChildren.codeLevels[0].text));
|
|
245
|
+
for (let i = 1; i < parsedChildren.codeLevels.length; i++) {
|
|
246
|
+
table.push(codeDiff(parsedChildren.codeLevels[i - 1].text, parsedChildren.codeLevels[i].text));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return table;
|
|
250
|
+
}, [parsedChildren.codeLevels]);
|
|
251
|
+
const [justEntered, setJustEntered] = useState(false);
|
|
252
|
+
const prevIndexRef = useRef(null);
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (prevIndexRef.current !== null && currentIndex !== prevIndexRef.current) {
|
|
255
|
+
setJustEntered(true);
|
|
256
|
+
}
|
|
257
|
+
prevIndexRef.current = currentIndex;
|
|
258
|
+
}, [currentIndex]);
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
if (justEntered) {
|
|
261
|
+
const animFrameId = requestAnimationFrame(() => {
|
|
262
|
+
setJustEntered(false);
|
|
263
|
+
});
|
|
264
|
+
return () => cancelAnimationFrame(animFrameId);
|
|
265
|
+
}
|
|
266
|
+
}, [justEntered]);
|
|
267
|
+
const totalSteps = Math.max(parsedChildren.proseLevels.length, parsedChildren.codeLevels.length, 1);
|
|
268
|
+
const goTo = (index) => {
|
|
269
|
+
if (index >= 0 && index < totalSteps) {
|
|
270
|
+
if (!isControlled) {
|
|
271
|
+
setInternalIndex(index);
|
|
272
|
+
}
|
|
273
|
+
onChange?.(index);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const handleBack = () => goTo(currentIndex - 1);
|
|
277
|
+
const handleNext = () => goTo(currentIndex + 1);
|
|
278
|
+
const maxLabels = Math.max(parsedChildren.proseLevels.length, parsedChildren.codeLevels.length);
|
|
279
|
+
const labels = Array.from({ length: maxLabels }).map((_, i) => {
|
|
280
|
+
return parsedChildren.proseLevels[i]?.label || parsedChildren.codeLevels[i]?.label || `Step ${i + 1}`;
|
|
281
|
+
});
|
|
282
|
+
const contextValue = {
|
|
283
|
+
selectedIndex: currentIndex,
|
|
284
|
+
totalSteps,
|
|
285
|
+
labels,
|
|
286
|
+
show,
|
|
287
|
+
orientation,
|
|
288
|
+
goTo,
|
|
289
|
+
advance: handleNext,
|
|
290
|
+
back: handleBack
|
|
291
|
+
};
|
|
292
|
+
if (!isMounted) {
|
|
293
|
+
const defaultLastIndex = totalSteps - 1;
|
|
294
|
+
let proseCount = 0;
|
|
295
|
+
let codeCount = 0;
|
|
296
|
+
const maxProse = parsedChildren.proseLevels.length;
|
|
297
|
+
const maxCode = parsedChildren.codeLevels.length;
|
|
298
|
+
return /* @__PURE__ */ jsx5(DepthContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx5(
|
|
299
|
+
"div",
|
|
300
|
+
{
|
|
301
|
+
"data-unfold-root": true,
|
|
302
|
+
"data-orientation": orientation,
|
|
303
|
+
"data-show": show,
|
|
304
|
+
style: { "--unfold-ratio": ratio },
|
|
305
|
+
"data-level": defaultLastIndex,
|
|
306
|
+
"data-total-levels": totalSteps,
|
|
307
|
+
role: "region",
|
|
308
|
+
"aria-label": labels[defaultLastIndex],
|
|
309
|
+
children: React.Children.map(children, (child) => {
|
|
310
|
+
if (React.isValidElement(child)) {
|
|
311
|
+
const type = child.type?.unfoldType || child.type;
|
|
312
|
+
if (type === "level" || type === DepthLevel) {
|
|
313
|
+
const isDeepest = proseCount === Math.max(0, maxProse - 1);
|
|
314
|
+
proseCount++;
|
|
315
|
+
return React.cloneElement(child, {
|
|
316
|
+
"data-unfold-active": isDeepest ? "true" : void 0
|
|
317
|
+
});
|
|
318
|
+
} else if (type === "code" || type === DepthCode) {
|
|
319
|
+
const isDeepest = codeCount === Math.max(0, maxCode - 1);
|
|
320
|
+
codeCount++;
|
|
321
|
+
return React.cloneElement(child, {
|
|
322
|
+
"data-unfold-active": isDeepest ? "true" : void 0
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return child;
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
) });
|
|
330
|
+
}
|
|
331
|
+
const clampedProseIndex = Math.min(currentIndex, proseDiffTable.length - 1);
|
|
332
|
+
const clampedCodeIndex = Math.min(currentIndex, codeDiffTable.length - 1);
|
|
333
|
+
const currentProseDiff = clampedProseIndex >= 0 ? proseDiffTable[clampedProseIndex] : [];
|
|
334
|
+
const currentCodeDiff = clampedCodeIndex >= 0 ? codeDiffTable[clampedCodeIndex] : [];
|
|
335
|
+
const currentCodeLang = clampedCodeIndex >= 0 ? parsedChildren.codeLevels[clampedCodeIndex]?.lang : void 0;
|
|
336
|
+
const currentCodeText = clampedCodeIndex >= 0 ? parsedChildren.codeLevels[clampedCodeIndex]?.text : void 0;
|
|
337
|
+
return /* @__PURE__ */ jsx5(DepthContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs3(
|
|
338
|
+
"div",
|
|
339
|
+
{
|
|
340
|
+
"data-unfold-root": true,
|
|
341
|
+
"data-orientation": orientation,
|
|
342
|
+
"data-show": show,
|
|
343
|
+
style: { "--unfold-ratio": ratio },
|
|
344
|
+
"data-level": currentIndex,
|
|
345
|
+
"data-total-levels": totalSteps,
|
|
346
|
+
role: "region",
|
|
347
|
+
"aria-label": labels[currentIndex],
|
|
348
|
+
children: [
|
|
349
|
+
/* @__PURE__ */ jsx5(
|
|
350
|
+
DepthPane,
|
|
351
|
+
{
|
|
352
|
+
show,
|
|
353
|
+
proseDiff: currentProseDiff,
|
|
354
|
+
codeDiff: currentCodeDiff,
|
|
355
|
+
justEntered,
|
|
356
|
+
animate,
|
|
357
|
+
codeLang: currentCodeLang,
|
|
358
|
+
codeText: currentCodeText,
|
|
359
|
+
highlighter,
|
|
360
|
+
highlight
|
|
361
|
+
}
|
|
362
|
+
),
|
|
363
|
+
/* @__PURE__ */ jsx5(Controls, { buttonVariant, indicators })
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
) });
|
|
367
|
+
}
|
|
368
|
+
export {
|
|
369
|
+
Depth,
|
|
370
|
+
DepthCode,
|
|
371
|
+
DepthLevel,
|
|
372
|
+
DepthPane,
|
|
373
|
+
codeDiff,
|
|
374
|
+
sentenceDiff,
|
|
375
|
+
tokenize,
|
|
376
|
+
tokenizeCodeLine,
|
|
377
|
+
tokenizeLines
|
|
378
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/shiki/index.ts
|
|
21
|
+
var shiki_exports = {};
|
|
22
|
+
__export(shiki_exports, {
|
|
23
|
+
createShikiHighlighter: () => createShikiHighlighter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(shiki_exports);
|
|
26
|
+
var import_shiki = require("shiki");
|
|
27
|
+
function mergeTokens(diffTokens, syntaxTokens) {
|
|
28
|
+
const chars = [];
|
|
29
|
+
for (const dt of diffTokens) {
|
|
30
|
+
for (const char of dt.text) {
|
|
31
|
+
chars.push({ char, kind: dt.kind });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let charIdx = 0;
|
|
35
|
+
for (const st of syntaxTokens) {
|
|
36
|
+
const len = st.content.length;
|
|
37
|
+
for (let i = 0; i < len; i++) {
|
|
38
|
+
if (charIdx < chars.length) {
|
|
39
|
+
chars[charIdx].color = st.color;
|
|
40
|
+
}
|
|
41
|
+
charIdx++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const merged = [];
|
|
45
|
+
if (chars.length === 0) return merged;
|
|
46
|
+
let currentKind = chars[0].kind;
|
|
47
|
+
let currentColor = chars[0].color;
|
|
48
|
+
let currentText = chars[0].char;
|
|
49
|
+
for (let i = 1; i < chars.length; i++) {
|
|
50
|
+
const c = chars[i];
|
|
51
|
+
if (c.kind === currentKind && c.color === currentColor) {
|
|
52
|
+
currentText += c.char;
|
|
53
|
+
} else {
|
|
54
|
+
merged.push({ kind: currentKind, color: currentColor, text: currentText });
|
|
55
|
+
currentKind = c.kind;
|
|
56
|
+
currentColor = c.color;
|
|
57
|
+
currentText = c.char;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
merged.push({ kind: currentKind, color: currentColor, text: currentText });
|
|
61
|
+
return merged;
|
|
62
|
+
}
|
|
63
|
+
function createShikiHighlighter(options = {}) {
|
|
64
|
+
let highlighter = null;
|
|
65
|
+
let isReady = false;
|
|
66
|
+
let tick = 0;
|
|
67
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
68
|
+
const notify = () => {
|
|
69
|
+
tick++;
|
|
70
|
+
listeners.forEach((fn) => fn());
|
|
71
|
+
};
|
|
72
|
+
const defaultTheme = options.theme || "vitesse-dark";
|
|
73
|
+
const defaultLangs = options.langs || ["javascript", "typescript", "jsx", "tsx", "css", "html", "json", "markdown"];
|
|
74
|
+
(0, import_shiki.getHighlighter)({
|
|
75
|
+
themes: [defaultTheme],
|
|
76
|
+
langs: defaultLangs
|
|
77
|
+
}).then((h) => {
|
|
78
|
+
highlighter = h;
|
|
79
|
+
isReady = true;
|
|
80
|
+
notify();
|
|
81
|
+
}).catch((err) => {
|
|
82
|
+
console.error("[unfold-mdx] Failed to initialize Shiki highlighter:", err);
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
subscribe: (onStoreChange) => {
|
|
86
|
+
listeners.add(onStoreChange);
|
|
87
|
+
return () => {
|
|
88
|
+
listeners.delete(onStoreChange);
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
getSnapshot: () => tick,
|
|
92
|
+
highlight: (code, lang, diffLines) => {
|
|
93
|
+
if (!isReady || !highlighter) {
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
let syntaxLines;
|
|
97
|
+
try {
|
|
98
|
+
syntaxLines = highlighter.codeToTokensBase(code, {
|
|
99
|
+
lang,
|
|
100
|
+
theme: defaultTheme
|
|
101
|
+
});
|
|
102
|
+
} catch (e) {
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
return diffLines.map((lineDiff, i) => {
|
|
106
|
+
const syntaxTokens = syntaxLines[i] || [];
|
|
107
|
+
let diffTokens;
|
|
108
|
+
if (lineDiff.kind === "equal" && !lineDiff.tokens) {
|
|
109
|
+
diffTokens = [{ kind: "equal", text: lineDiff.line }];
|
|
110
|
+
} else {
|
|
111
|
+
diffTokens = lineDiff.tokens || [];
|
|
112
|
+
}
|
|
113
|
+
const mergedTokens = mergeTokens(diffTokens, syntaxTokens);
|
|
114
|
+
return {
|
|
115
|
+
...lineDiff,
|
|
116
|
+
tokens: mergedTokens
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
123
|
+
0 && (module.exports = {
|
|
124
|
+
createShikiHighlighter
|
|
125
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { C as CodeLineDiff } from '../codeDiff-BVwZfdt9.cjs';
|
|
2
|
+
|
|
3
|
+
interface ShikiAdapterOptions {
|
|
4
|
+
theme?: string;
|
|
5
|
+
langs?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface ShikiHighlighter {
|
|
8
|
+
subscribe: (onStoreChange: () => void) => () => void;
|
|
9
|
+
getSnapshot: () => number;
|
|
10
|
+
highlight: (code: string, lang: string, diffLines: CodeLineDiff[]) => CodeLineDiff[] | undefined;
|
|
11
|
+
}
|
|
12
|
+
declare function createShikiHighlighter(options?: ShikiAdapterOptions): ShikiHighlighter;
|
|
13
|
+
|
|
14
|
+
export { type ShikiAdapterOptions, type ShikiHighlighter, createShikiHighlighter };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { C as CodeLineDiff } from '../codeDiff-BVwZfdt9.js';
|
|
2
|
+
|
|
3
|
+
interface ShikiAdapterOptions {
|
|
4
|
+
theme?: string;
|
|
5
|
+
langs?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface ShikiHighlighter {
|
|
8
|
+
subscribe: (onStoreChange: () => void) => () => void;
|
|
9
|
+
getSnapshot: () => number;
|
|
10
|
+
highlight: (code: string, lang: string, diffLines: CodeLineDiff[]) => CodeLineDiff[] | undefined;
|
|
11
|
+
}
|
|
12
|
+
declare function createShikiHighlighter(options?: ShikiAdapterOptions): ShikiHighlighter;
|
|
13
|
+
|
|
14
|
+
export { type ShikiAdapterOptions, type ShikiHighlighter, createShikiHighlighter };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/shiki/index.ts
|
|
2
|
+
import { getHighlighter } from "shiki";
|
|
3
|
+
function mergeTokens(diffTokens, syntaxTokens) {
|
|
4
|
+
const chars = [];
|
|
5
|
+
for (const dt of diffTokens) {
|
|
6
|
+
for (const char of dt.text) {
|
|
7
|
+
chars.push({ char, kind: dt.kind });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
let charIdx = 0;
|
|
11
|
+
for (const st of syntaxTokens) {
|
|
12
|
+
const len = st.content.length;
|
|
13
|
+
for (let i = 0; i < len; i++) {
|
|
14
|
+
if (charIdx < chars.length) {
|
|
15
|
+
chars[charIdx].color = st.color;
|
|
16
|
+
}
|
|
17
|
+
charIdx++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const merged = [];
|
|
21
|
+
if (chars.length === 0) return merged;
|
|
22
|
+
let currentKind = chars[0].kind;
|
|
23
|
+
let currentColor = chars[0].color;
|
|
24
|
+
let currentText = chars[0].char;
|
|
25
|
+
for (let i = 1; i < chars.length; i++) {
|
|
26
|
+
const c = chars[i];
|
|
27
|
+
if (c.kind === currentKind && c.color === currentColor) {
|
|
28
|
+
currentText += c.char;
|
|
29
|
+
} else {
|
|
30
|
+
merged.push({ kind: currentKind, color: currentColor, text: currentText });
|
|
31
|
+
currentKind = c.kind;
|
|
32
|
+
currentColor = c.color;
|
|
33
|
+
currentText = c.char;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
merged.push({ kind: currentKind, color: currentColor, text: currentText });
|
|
37
|
+
return merged;
|
|
38
|
+
}
|
|
39
|
+
function createShikiHighlighter(options = {}) {
|
|
40
|
+
let highlighter = null;
|
|
41
|
+
let isReady = false;
|
|
42
|
+
let tick = 0;
|
|
43
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
44
|
+
const notify = () => {
|
|
45
|
+
tick++;
|
|
46
|
+
listeners.forEach((fn) => fn());
|
|
47
|
+
};
|
|
48
|
+
const defaultTheme = options.theme || "vitesse-dark";
|
|
49
|
+
const defaultLangs = options.langs || ["javascript", "typescript", "jsx", "tsx", "css", "html", "json", "markdown"];
|
|
50
|
+
getHighlighter({
|
|
51
|
+
themes: [defaultTheme],
|
|
52
|
+
langs: defaultLangs
|
|
53
|
+
}).then((h) => {
|
|
54
|
+
highlighter = h;
|
|
55
|
+
isReady = true;
|
|
56
|
+
notify();
|
|
57
|
+
}).catch((err) => {
|
|
58
|
+
console.error("[unfold-mdx] Failed to initialize Shiki highlighter:", err);
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
subscribe: (onStoreChange) => {
|
|
62
|
+
listeners.add(onStoreChange);
|
|
63
|
+
return () => {
|
|
64
|
+
listeners.delete(onStoreChange);
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
getSnapshot: () => tick,
|
|
68
|
+
highlight: (code, lang, diffLines) => {
|
|
69
|
+
if (!isReady || !highlighter) {
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
let syntaxLines;
|
|
73
|
+
try {
|
|
74
|
+
syntaxLines = highlighter.codeToTokensBase(code, {
|
|
75
|
+
lang,
|
|
76
|
+
theme: defaultTheme
|
|
77
|
+
});
|
|
78
|
+
} catch (e) {
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
return diffLines.map((lineDiff, i) => {
|
|
82
|
+
const syntaxTokens = syntaxLines[i] || [];
|
|
83
|
+
let diffTokens;
|
|
84
|
+
if (lineDiff.kind === "equal" && !lineDiff.tokens) {
|
|
85
|
+
diffTokens = [{ kind: "equal", text: lineDiff.line }];
|
|
86
|
+
} else {
|
|
87
|
+
diffTokens = lineDiff.tokens || [];
|
|
88
|
+
}
|
|
89
|
+
const mergedTokens = mergeTokens(diffTokens, syntaxTokens);
|
|
90
|
+
return {
|
|
91
|
+
...lineDiff,
|
|
92
|
+
tokens: mergedTokens
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
createShikiHighlighter
|
|
100
|
+
};
|