@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.cjs
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
Depth: () => Depth,
|
|
34
|
+
DepthCode: () => DepthCode,
|
|
35
|
+
DepthLevel: () => DepthLevel,
|
|
36
|
+
DepthPane: () => DepthPane,
|
|
37
|
+
codeDiff: () => codeDiff,
|
|
38
|
+
sentenceDiff: () => sentenceDiff,
|
|
39
|
+
tokenize: () => tokenize,
|
|
40
|
+
tokenizeCodeLine: () => tokenizeCodeLine,
|
|
41
|
+
tokenizeLines: () => tokenizeLines
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
|
+
|
|
45
|
+
// src/diff/tokenize.ts
|
|
46
|
+
function tokenize(text) {
|
|
47
|
+
if (!text) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const splitRegex = /(?<=(?:(?<!\.)\.(?!\.)|[!?]))(?=\s|$)/;
|
|
51
|
+
return text.split(splitRegex).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/diff/sentenceDiff.ts
|
|
55
|
+
var import_diff_match_patch = require("diff-match-patch");
|
|
56
|
+
function sentenceDiff(prev, next) {
|
|
57
|
+
const prevSentences = tokenize(prev);
|
|
58
|
+
const nextSentences = tokenize(next);
|
|
59
|
+
const sentenceToChar = /* @__PURE__ */ new Map();
|
|
60
|
+
const charToSentence = [];
|
|
61
|
+
function getCharForSentence(sentence) {
|
|
62
|
+
let char = sentenceToChar.get(sentence);
|
|
63
|
+
if (char === void 0) {
|
|
64
|
+
const code = charToSentence.length;
|
|
65
|
+
char = String.fromCharCode(code);
|
|
66
|
+
sentenceToChar.set(sentence, char);
|
|
67
|
+
charToSentence.push(sentence);
|
|
68
|
+
}
|
|
69
|
+
return char;
|
|
70
|
+
}
|
|
71
|
+
const prevChars = prevSentences.map(getCharForSentence).join("");
|
|
72
|
+
const nextChars = nextSentences.map(getCharForSentence).join("");
|
|
73
|
+
const dmp = new import_diff_match_patch.diff_match_patch();
|
|
74
|
+
const diffs = dmp.diff_main(prevChars, nextChars);
|
|
75
|
+
dmp.diff_cleanupSemantic(diffs);
|
|
76
|
+
const result = [];
|
|
77
|
+
let i = 0;
|
|
78
|
+
while (i < diffs.length) {
|
|
79
|
+
const [op, text] = diffs[i];
|
|
80
|
+
if (op === 0) {
|
|
81
|
+
for (let j = 0; j < text.length; j++) {
|
|
82
|
+
result.push({
|
|
83
|
+
kind: "equal",
|
|
84
|
+
sentence: charToSentence[text.charCodeAt(j)]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
i++;
|
|
88
|
+
} else if (op === -1 && i + 1 < diffs.length && diffs[i + 1][0] === 1) {
|
|
89
|
+
const deleteText = text;
|
|
90
|
+
const insertText = diffs[i + 1][1];
|
|
91
|
+
const deleteLen = deleteText.length;
|
|
92
|
+
const insertLen = insertText.length;
|
|
93
|
+
const minLen = Math.min(deleteLen, insertLen);
|
|
94
|
+
for (let j = 0; j < minLen; j++) {
|
|
95
|
+
result.push({
|
|
96
|
+
kind: "modified",
|
|
97
|
+
before: charToSentence[deleteText.charCodeAt(j)],
|
|
98
|
+
after: charToSentence[insertText.charCodeAt(j)]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
for (let j = minLen; j < deleteLen; j++) {
|
|
102
|
+
result.push({
|
|
103
|
+
kind: "removed",
|
|
104
|
+
sentence: charToSentence[deleteText.charCodeAt(j)]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
for (let j = minLen; j < insertLen; j++) {
|
|
108
|
+
result.push({
|
|
109
|
+
kind: "added",
|
|
110
|
+
sentence: charToSentence[insertText.charCodeAt(j)]
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
i += 2;
|
|
114
|
+
} else if (op === -1) {
|
|
115
|
+
for (let j = 0; j < text.length; j++) {
|
|
116
|
+
result.push({
|
|
117
|
+
kind: "removed",
|
|
118
|
+
sentence: charToSentence[text.charCodeAt(j)]
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
i++;
|
|
122
|
+
} else if (op === 1) {
|
|
123
|
+
for (let j = 0; j < text.length; j++) {
|
|
124
|
+
result.push({
|
|
125
|
+
kind: "added",
|
|
126
|
+
sentence: charToSentence[text.charCodeAt(j)]
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
i++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/diff/codeTokenize.ts
|
|
136
|
+
function tokenizeLines(code) {
|
|
137
|
+
if (code === "") return [];
|
|
138
|
+
return code.split(/\r?\n/);
|
|
139
|
+
}
|
|
140
|
+
function tokenizeCodeLine(line) {
|
|
141
|
+
if (line === "") return [];
|
|
142
|
+
const regex = /([a-zA-Z0-9_]+|\s+|[^a-zA-Z0-9_\s])/g;
|
|
143
|
+
const tokens = [];
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = regex.exec(line)) !== null) {
|
|
146
|
+
if (match[0].length > 0) {
|
|
147
|
+
tokens.push(match[0]);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return tokens;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/diff/codeDiff.ts
|
|
154
|
+
var import_diff_match_patch2 = require("diff-match-patch");
|
|
155
|
+
function codeDiff(prev, next) {
|
|
156
|
+
if (prev === "" && next !== "") {
|
|
157
|
+
return tokenizeLines(next).map((line) => ({
|
|
158
|
+
kind: "added",
|
|
159
|
+
tokens: tokenizeCodeLine(line).map((t) => ({ kind: "added", text: t }))
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
const prevLines = tokenizeLines(prev);
|
|
163
|
+
const nextLines = tokenizeLines(next);
|
|
164
|
+
const lineToChar = /* @__PURE__ */ new Map();
|
|
165
|
+
const charToLine = [];
|
|
166
|
+
function getCharForLine(line) {
|
|
167
|
+
let char = lineToChar.get(line);
|
|
168
|
+
if (char === void 0) {
|
|
169
|
+
const code = charToLine.length;
|
|
170
|
+
char = String.fromCharCode(code);
|
|
171
|
+
lineToChar.set(line, char);
|
|
172
|
+
charToLine.push(line);
|
|
173
|
+
}
|
|
174
|
+
return char;
|
|
175
|
+
}
|
|
176
|
+
const prevChars = prevLines.map(getCharForLine).join("");
|
|
177
|
+
const nextChars = nextLines.map(getCharForLine).join("");
|
|
178
|
+
const dmp = new import_diff_match_patch2.diff_match_patch();
|
|
179
|
+
const diffs = dmp.diff_main(prevChars, nextChars);
|
|
180
|
+
dmp.diff_cleanupSemantic(diffs);
|
|
181
|
+
const result = [];
|
|
182
|
+
let i = 0;
|
|
183
|
+
while (i < diffs.length) {
|
|
184
|
+
const [op, text] = diffs[i];
|
|
185
|
+
if (op === 0) {
|
|
186
|
+
for (let j = 0; j < text.length; j++) {
|
|
187
|
+
result.push({
|
|
188
|
+
kind: "equal",
|
|
189
|
+
line: charToLine[text.charCodeAt(j)]
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
i++;
|
|
193
|
+
} else if (op === -1 && i + 1 < diffs.length && diffs[i + 1][0] === 1) {
|
|
194
|
+
const deleteText = text;
|
|
195
|
+
const insertText = diffs[i + 1][1];
|
|
196
|
+
const deleteLen = deleteText.length;
|
|
197
|
+
const insertLen = insertText.length;
|
|
198
|
+
const minLen = Math.min(deleteLen, insertLen);
|
|
199
|
+
for (let j = 0; j < minLen; j++) {
|
|
200
|
+
let getCharForToken2 = function(tok) {
|
|
201
|
+
let char = tokenToChar.get(tok);
|
|
202
|
+
if (char === void 0) {
|
|
203
|
+
const code = charToToken.length;
|
|
204
|
+
char = String.fromCharCode(code);
|
|
205
|
+
tokenToChar.set(tok, char);
|
|
206
|
+
charToToken.push(tok);
|
|
207
|
+
}
|
|
208
|
+
return char;
|
|
209
|
+
};
|
|
210
|
+
var getCharForToken = getCharForToken2;
|
|
211
|
+
const prevLine = charToLine[deleteText.charCodeAt(j)];
|
|
212
|
+
const nextLine = charToLine[insertText.charCodeAt(j)];
|
|
213
|
+
const prevTokens = tokenizeCodeLine(prevLine);
|
|
214
|
+
const nextTokens = tokenizeCodeLine(nextLine);
|
|
215
|
+
const tokenToChar = /* @__PURE__ */ new Map();
|
|
216
|
+
const charToToken = [];
|
|
217
|
+
const prevTokChars = prevTokens.map(getCharForToken2).join("");
|
|
218
|
+
const nextTokChars = nextTokens.map(getCharForToken2).join("");
|
|
219
|
+
const tokDiffs = dmp.diff_main(prevTokChars, nextTokChars);
|
|
220
|
+
dmp.diff_cleanupSemantic(tokDiffs);
|
|
221
|
+
const tokens = [];
|
|
222
|
+
for (let k = 0; k < tokDiffs.length; k++) {
|
|
223
|
+
const [tokOp, tokText] = tokDiffs[k];
|
|
224
|
+
if (tokOp === 0) {
|
|
225
|
+
for (let c = 0; c < tokText.length; c++) {
|
|
226
|
+
tokens.push({ kind: "equal", text: charToToken[tokText.charCodeAt(c)] });
|
|
227
|
+
}
|
|
228
|
+
} else if (tokOp === 1) {
|
|
229
|
+
for (let c = 0; c < tokText.length; c++) {
|
|
230
|
+
tokens.push({ kind: "added", text: charToToken[tokText.charCodeAt(c)] });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
result.push({ kind: "changed", tokens });
|
|
235
|
+
}
|
|
236
|
+
for (let j = minLen; j < insertLen; j++) {
|
|
237
|
+
const line = charToLine[insertText.charCodeAt(j)];
|
|
238
|
+
const tokens = tokenizeCodeLine(line).map((t) => ({ kind: "added", text: t }));
|
|
239
|
+
result.push({ kind: "added", tokens });
|
|
240
|
+
}
|
|
241
|
+
i += 2;
|
|
242
|
+
} else if (op === -1) {
|
|
243
|
+
i++;
|
|
244
|
+
} else if (op === 1) {
|
|
245
|
+
for (let j = 0; j < text.length; j++) {
|
|
246
|
+
const line = charToLine[text.charCodeAt(j)];
|
|
247
|
+
const tokens = tokenizeCodeLine(line).map((t) => ({ kind: "added", text: t }));
|
|
248
|
+
result.push({ kind: "added", tokens });
|
|
249
|
+
}
|
|
250
|
+
i++;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/components/Depth.tsx
|
|
257
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
258
|
+
|
|
259
|
+
// src/context/DepthContext.tsx
|
|
260
|
+
var import_react = require("react");
|
|
261
|
+
var DepthContext = (0, import_react.createContext)(null);
|
|
262
|
+
|
|
263
|
+
// src/components/DepthLevel.tsx
|
|
264
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
265
|
+
function DepthLevel({ label, children, ...props }) {
|
|
266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-depth-level": true, ...props, children });
|
|
267
|
+
}
|
|
268
|
+
DepthLevel.unfoldType = "level";
|
|
269
|
+
|
|
270
|
+
// src/components/DepthCode.tsx
|
|
271
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
272
|
+
function DepthCode({ lang, label, children, ...rest }) {
|
|
273
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { "data-lang": lang, ...rest, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children }) });
|
|
274
|
+
}
|
|
275
|
+
DepthCode.unfoldType = "code";
|
|
276
|
+
|
|
277
|
+
// src/components/DepthPane.tsx
|
|
278
|
+
var import_react2 = require("react");
|
|
279
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
280
|
+
var noopSubscribe = () => () => {
|
|
281
|
+
};
|
|
282
|
+
function DepthPane({
|
|
283
|
+
show,
|
|
284
|
+
proseDiff,
|
|
285
|
+
codeDiff: codeDiff2,
|
|
286
|
+
justEntered,
|
|
287
|
+
animate,
|
|
288
|
+
codeLang,
|
|
289
|
+
codeText,
|
|
290
|
+
highlighter,
|
|
291
|
+
highlight = true
|
|
292
|
+
}) {
|
|
293
|
+
const showProse = show === "both" || show === "prose";
|
|
294
|
+
const showCode = show === "both" || show === "code";
|
|
295
|
+
const enterState = animate && justEntered ? "true" : void 0;
|
|
296
|
+
const getStatus = (itemKind) => {
|
|
297
|
+
if (!highlight || itemKind === "equal") return "equal";
|
|
298
|
+
return "added";
|
|
299
|
+
};
|
|
300
|
+
(0, import_react2.useSyncExternalStore)(
|
|
301
|
+
highlighter?.subscribe ?? noopSubscribe,
|
|
302
|
+
() => highlighter?.getSnapshot() ?? 0,
|
|
303
|
+
() => highlighter?.getSnapshot() ?? 0
|
|
304
|
+
);
|
|
305
|
+
let finalCodeDiff = codeDiff2;
|
|
306
|
+
if (highlighter && codeText !== void 0 && codeLang !== void 0) {
|
|
307
|
+
const highlighted = highlighter.highlight(codeText, codeLang, codeDiff2);
|
|
308
|
+
if (highlighted) {
|
|
309
|
+
finalCodeDiff = highlighted;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-unfold-panes": true, children: [
|
|
313
|
+
showProse && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-unfold-pane": "prose", "aria-live": "polite", children: proseDiff.filter((item) => item.kind !== "removed").map((item, index) => {
|
|
314
|
+
const status = getStatus(item.kind);
|
|
315
|
+
const sentenceText = item.kind === "modified" ? item.after : item.sentence;
|
|
316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
317
|
+
"span",
|
|
318
|
+
{
|
|
319
|
+
"data-sentence": status,
|
|
320
|
+
"data-enter": status === "added" ? enterState : void 0,
|
|
321
|
+
children: [
|
|
322
|
+
sentenceText,
|
|
323
|
+
" "
|
|
324
|
+
]
|
|
325
|
+
},
|
|
326
|
+
`prose-${index}`
|
|
327
|
+
);
|
|
328
|
+
}) }),
|
|
329
|
+
showCode && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-unfold-pane": "code", "aria-live": "polite", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { "data-lang": codeLang, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { children: finalCodeDiff.map((lineDiff, lineIndex) => {
|
|
330
|
+
const lineText = lineDiff.tokens ? lineDiff.tokens.map((t) => t.text).join("") : lineDiff.line || "";
|
|
331
|
+
const isWhitespaceOnly = lineText.trim() === "";
|
|
332
|
+
const lineStatus = isWhitespaceOnly ? "equal" : getStatus(lineDiff.kind);
|
|
333
|
+
if (lineDiff.kind === "equal" && !lineDiff.tokens) {
|
|
334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-code-line": "equal", children: [
|
|
335
|
+
lineDiff.line,
|
|
336
|
+
"\n"
|
|
337
|
+
] }, `line-${lineIndex}`);
|
|
338
|
+
}
|
|
339
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
340
|
+
"span",
|
|
341
|
+
{
|
|
342
|
+
"data-code-line": lineStatus,
|
|
343
|
+
"data-enter": lineStatus === "added" ? enterState : void 0,
|
|
344
|
+
children: [
|
|
345
|
+
lineDiff.tokens?.map((token, tokenIndex) => {
|
|
346
|
+
const tokenStatus = getStatus(token.kind);
|
|
347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
348
|
+
"span",
|
|
349
|
+
{
|
|
350
|
+
"data-code-token": isWhitespaceOnly ? "equal" : tokenStatus,
|
|
351
|
+
style: token.color ? { color: token.color } : void 0,
|
|
352
|
+
children: token.text
|
|
353
|
+
},
|
|
354
|
+
`token-${tokenIndex}`
|
|
355
|
+
);
|
|
356
|
+
}),
|
|
357
|
+
"\n"
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
`line-${lineIndex}`
|
|
361
|
+
);
|
|
362
|
+
}) }) }) })
|
|
363
|
+
] });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/components/controls/Controls.tsx
|
|
367
|
+
var import_react3 = require("react");
|
|
368
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
369
|
+
var BUTTON_LABELS = {
|
|
370
|
+
text: { prev: "Prev", next: "Next" },
|
|
371
|
+
arrow: { prev: "\u2190 Prev", next: "Next \u2192" },
|
|
372
|
+
chevron: { prev: "\u2039 Prev", next: "Next \u203A" },
|
|
373
|
+
minimal: { prev: "\u2039", next: "\u203A" }
|
|
374
|
+
};
|
|
375
|
+
function Controls({ buttonVariant, indicators }) {
|
|
376
|
+
const context = (0, import_react3.useContext)(DepthContext);
|
|
377
|
+
if (!context) return null;
|
|
378
|
+
const { selectedIndex, totalSteps, advance, back, goTo, labels } = context;
|
|
379
|
+
const isFirst = selectedIndex === 0;
|
|
380
|
+
const isLast = selectedIndex === totalSteps - 1;
|
|
381
|
+
const btnText = BUTTON_LABELS[buttonVariant] || BUTTON_LABELS.text;
|
|
382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-unfold-controls": true, "data-button-variant": buttonVariant, children: [
|
|
383
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
384
|
+
"button",
|
|
385
|
+
{
|
|
386
|
+
"data-unfold-prev": true,
|
|
387
|
+
"aria-disabled": isFirst ? "true" : "false",
|
|
388
|
+
"aria-label": !isFirst ? `Go back to step: ${labels[selectedIndex - 1]}` : void 0,
|
|
389
|
+
onClick: back,
|
|
390
|
+
children: btnText.prev
|
|
391
|
+
}
|
|
392
|
+
),
|
|
393
|
+
indicators && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-unfold-indicators": true, role: "tablist", children: Array.from({ length: totalSteps }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
394
|
+
"button",
|
|
395
|
+
{
|
|
396
|
+
role: "tab",
|
|
397
|
+
"aria-selected": i === selectedIndex,
|
|
398
|
+
"data-unfold-dot": true,
|
|
399
|
+
"data-active": i === selectedIndex ? "true" : void 0,
|
|
400
|
+
"aria-label": `Go to step: ${labels[i] || i + 1}`,
|
|
401
|
+
onClick: () => goTo(i)
|
|
402
|
+
},
|
|
403
|
+
i
|
|
404
|
+
)) }),
|
|
405
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
406
|
+
"button",
|
|
407
|
+
{
|
|
408
|
+
"data-unfold-next": true,
|
|
409
|
+
"aria-disabled": isLast ? "true" : "false",
|
|
410
|
+
"aria-label": !isLast ? `Advance to step: ${labels[selectedIndex + 1]}` : void 0,
|
|
411
|
+
onClick: advance,
|
|
412
|
+
children: btnText.next
|
|
413
|
+
}
|
|
414
|
+
)
|
|
415
|
+
] });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/components/Depth.tsx
|
|
419
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
420
|
+
function getTextContent(node) {
|
|
421
|
+
if (node === null || node === void 0) {
|
|
422
|
+
return "";
|
|
423
|
+
}
|
|
424
|
+
if (typeof node === "string" || typeof node === "number") {
|
|
425
|
+
return String(node);
|
|
426
|
+
}
|
|
427
|
+
if (Array.isArray(node)) {
|
|
428
|
+
return node.map(getTextContent).join("");
|
|
429
|
+
}
|
|
430
|
+
if (import_react4.default.isValidElement(node)) {
|
|
431
|
+
return getTextContent(node.props.children);
|
|
432
|
+
}
|
|
433
|
+
return "";
|
|
434
|
+
}
|
|
435
|
+
function Depth({
|
|
436
|
+
selectedIndex,
|
|
437
|
+
defaultIndex,
|
|
438
|
+
onChange,
|
|
439
|
+
orientation = "horizontal",
|
|
440
|
+
ratio = 0.5,
|
|
441
|
+
show = "both",
|
|
442
|
+
indicators = false,
|
|
443
|
+
buttonVariant = "text",
|
|
444
|
+
animate = true,
|
|
445
|
+
highlight = true,
|
|
446
|
+
highlighter,
|
|
447
|
+
children
|
|
448
|
+
}) {
|
|
449
|
+
const isControlled = selectedIndex !== void 0 && onChange !== void 0;
|
|
450
|
+
const [internalIndex, setInternalIndex] = (0, import_react4.useState)(defaultIndex ?? 0);
|
|
451
|
+
const currentIndex = isControlled ? selectedIndex : internalIndex;
|
|
452
|
+
const [isMounted, setIsMounted] = (0, import_react4.useState)(false);
|
|
453
|
+
(0, import_react4.useEffect)(() => {
|
|
454
|
+
setIsMounted(true);
|
|
455
|
+
}, []);
|
|
456
|
+
const parsedChildren = (0, import_react4.useMemo)(() => {
|
|
457
|
+
const proseLevels = [];
|
|
458
|
+
const codeLevels = [];
|
|
459
|
+
import_react4.default.Children.forEach(children, (child) => {
|
|
460
|
+
if (import_react4.default.isValidElement(child)) {
|
|
461
|
+
const type = child.type?.unfoldType || child.type;
|
|
462
|
+
if (type === "level" || type === DepthLevel) {
|
|
463
|
+
proseLevels.push({
|
|
464
|
+
label: child.props.label || "",
|
|
465
|
+
text: getTextContent(child.props.children)
|
|
466
|
+
});
|
|
467
|
+
} else if (type === "code" || type === DepthCode) {
|
|
468
|
+
codeLevels.push({
|
|
469
|
+
lang: child.props.lang || "",
|
|
470
|
+
label: child.props.label || "",
|
|
471
|
+
text: getTextContent(child.props.children)
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
return { proseLevels, codeLevels };
|
|
477
|
+
}, [children]);
|
|
478
|
+
const proseDiffTable = (0, import_react4.useMemo)(() => {
|
|
479
|
+
const table = [];
|
|
480
|
+
if (parsedChildren.proseLevels.length > 0) {
|
|
481
|
+
table.push(sentenceDiff("", parsedChildren.proseLevels[0].text));
|
|
482
|
+
for (let i = 1; i < parsedChildren.proseLevels.length; i++) {
|
|
483
|
+
table.push(sentenceDiff(parsedChildren.proseLevels[i - 1].text, parsedChildren.proseLevels[i].text));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return table;
|
|
487
|
+
}, [parsedChildren.proseLevels]);
|
|
488
|
+
const codeDiffTable = (0, import_react4.useMemo)(() => {
|
|
489
|
+
const table = [];
|
|
490
|
+
if (parsedChildren.codeLevels.length > 0) {
|
|
491
|
+
table.push(codeDiff("", parsedChildren.codeLevels[0].text));
|
|
492
|
+
for (let i = 1; i < parsedChildren.codeLevels.length; i++) {
|
|
493
|
+
table.push(codeDiff(parsedChildren.codeLevels[i - 1].text, parsedChildren.codeLevels[i].text));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return table;
|
|
497
|
+
}, [parsedChildren.codeLevels]);
|
|
498
|
+
const [justEntered, setJustEntered] = (0, import_react4.useState)(false);
|
|
499
|
+
const prevIndexRef = (0, import_react4.useRef)(null);
|
|
500
|
+
(0, import_react4.useEffect)(() => {
|
|
501
|
+
if (prevIndexRef.current !== null && currentIndex !== prevIndexRef.current) {
|
|
502
|
+
setJustEntered(true);
|
|
503
|
+
}
|
|
504
|
+
prevIndexRef.current = currentIndex;
|
|
505
|
+
}, [currentIndex]);
|
|
506
|
+
(0, import_react4.useEffect)(() => {
|
|
507
|
+
if (justEntered) {
|
|
508
|
+
const animFrameId = requestAnimationFrame(() => {
|
|
509
|
+
setJustEntered(false);
|
|
510
|
+
});
|
|
511
|
+
return () => cancelAnimationFrame(animFrameId);
|
|
512
|
+
}
|
|
513
|
+
}, [justEntered]);
|
|
514
|
+
const totalSteps = Math.max(parsedChildren.proseLevels.length, parsedChildren.codeLevels.length, 1);
|
|
515
|
+
const goTo = (index) => {
|
|
516
|
+
if (index >= 0 && index < totalSteps) {
|
|
517
|
+
if (!isControlled) {
|
|
518
|
+
setInternalIndex(index);
|
|
519
|
+
}
|
|
520
|
+
onChange?.(index);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
const handleBack = () => goTo(currentIndex - 1);
|
|
524
|
+
const handleNext = () => goTo(currentIndex + 1);
|
|
525
|
+
const maxLabels = Math.max(parsedChildren.proseLevels.length, parsedChildren.codeLevels.length);
|
|
526
|
+
const labels = Array.from({ length: maxLabels }).map((_, i) => {
|
|
527
|
+
return parsedChildren.proseLevels[i]?.label || parsedChildren.codeLevels[i]?.label || `Step ${i + 1}`;
|
|
528
|
+
});
|
|
529
|
+
const contextValue = {
|
|
530
|
+
selectedIndex: currentIndex,
|
|
531
|
+
totalSteps,
|
|
532
|
+
labels,
|
|
533
|
+
show,
|
|
534
|
+
orientation,
|
|
535
|
+
goTo,
|
|
536
|
+
advance: handleNext,
|
|
537
|
+
back: handleBack
|
|
538
|
+
};
|
|
539
|
+
if (!isMounted) {
|
|
540
|
+
const defaultLastIndex = totalSteps - 1;
|
|
541
|
+
let proseCount = 0;
|
|
542
|
+
let codeCount = 0;
|
|
543
|
+
const maxProse = parsedChildren.proseLevels.length;
|
|
544
|
+
const maxCode = parsedChildren.codeLevels.length;
|
|
545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DepthContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
546
|
+
"div",
|
|
547
|
+
{
|
|
548
|
+
"data-unfold-root": true,
|
|
549
|
+
"data-orientation": orientation,
|
|
550
|
+
"data-show": show,
|
|
551
|
+
style: { "--unfold-ratio": ratio },
|
|
552
|
+
"data-level": defaultLastIndex,
|
|
553
|
+
"data-total-levels": totalSteps,
|
|
554
|
+
role: "region",
|
|
555
|
+
"aria-label": labels[defaultLastIndex],
|
|
556
|
+
children: import_react4.default.Children.map(children, (child) => {
|
|
557
|
+
if (import_react4.default.isValidElement(child)) {
|
|
558
|
+
const type = child.type?.unfoldType || child.type;
|
|
559
|
+
if (type === "level" || type === DepthLevel) {
|
|
560
|
+
const isDeepest = proseCount === Math.max(0, maxProse - 1);
|
|
561
|
+
proseCount++;
|
|
562
|
+
return import_react4.default.cloneElement(child, {
|
|
563
|
+
"data-unfold-active": isDeepest ? "true" : void 0
|
|
564
|
+
});
|
|
565
|
+
} else if (type === "code" || type === DepthCode) {
|
|
566
|
+
const isDeepest = codeCount === Math.max(0, maxCode - 1);
|
|
567
|
+
codeCount++;
|
|
568
|
+
return import_react4.default.cloneElement(child, {
|
|
569
|
+
"data-unfold-active": isDeepest ? "true" : void 0
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return child;
|
|
574
|
+
})
|
|
575
|
+
}
|
|
576
|
+
) });
|
|
577
|
+
}
|
|
578
|
+
const clampedProseIndex = Math.min(currentIndex, proseDiffTable.length - 1);
|
|
579
|
+
const clampedCodeIndex = Math.min(currentIndex, codeDiffTable.length - 1);
|
|
580
|
+
const currentProseDiff = clampedProseIndex >= 0 ? proseDiffTable[clampedProseIndex] : [];
|
|
581
|
+
const currentCodeDiff = clampedCodeIndex >= 0 ? codeDiffTable[clampedCodeIndex] : [];
|
|
582
|
+
const currentCodeLang = clampedCodeIndex >= 0 ? parsedChildren.codeLevels[clampedCodeIndex]?.lang : void 0;
|
|
583
|
+
const currentCodeText = clampedCodeIndex >= 0 ? parsedChildren.codeLevels[clampedCodeIndex]?.text : void 0;
|
|
584
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DepthContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
585
|
+
"div",
|
|
586
|
+
{
|
|
587
|
+
"data-unfold-root": true,
|
|
588
|
+
"data-orientation": orientation,
|
|
589
|
+
"data-show": show,
|
|
590
|
+
style: { "--unfold-ratio": ratio },
|
|
591
|
+
"data-level": currentIndex,
|
|
592
|
+
"data-total-levels": totalSteps,
|
|
593
|
+
role: "region",
|
|
594
|
+
"aria-label": labels[currentIndex],
|
|
595
|
+
children: [
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
597
|
+
DepthPane,
|
|
598
|
+
{
|
|
599
|
+
show,
|
|
600
|
+
proseDiff: currentProseDiff,
|
|
601
|
+
codeDiff: currentCodeDiff,
|
|
602
|
+
justEntered,
|
|
603
|
+
animate,
|
|
604
|
+
codeLang: currentCodeLang,
|
|
605
|
+
codeText: currentCodeText,
|
|
606
|
+
highlighter,
|
|
607
|
+
highlight
|
|
608
|
+
}
|
|
609
|
+
),
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Controls, { buttonVariant, indicators })
|
|
611
|
+
]
|
|
612
|
+
}
|
|
613
|
+
) });
|
|
614
|
+
}
|
|
615
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
616
|
+
0 && (module.exports = {
|
|
617
|
+
Depth,
|
|
618
|
+
DepthCode,
|
|
619
|
+
DepthLevel,
|
|
620
|
+
DepthPane,
|
|
621
|
+
codeDiff,
|
|
622
|
+
sentenceDiff,
|
|
623
|
+
tokenize,
|
|
624
|
+
tokenizeCodeLine,
|
|
625
|
+
tokenizeLines
|
|
626
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { SentenceDiff } from './diff/index.cjs';
|
|
2
|
+
export { sentenceDiff, tokenize, tokenizeCodeLine, tokenizeLines } from './diff/index.cjs';
|
|
3
|
+
import { C as CodeLineDiff } from './codeDiff-BVwZfdt9.cjs';
|
|
4
|
+
export { a as CodeToken, c as codeDiff } from './codeDiff-BVwZfdt9.cjs';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import React__default from 'react';
|
|
7
|
+
import { ShikiHighlighter } from './shiki/index.cjs';
|
|
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 };
|