@particle-academy/react-fancy 5.67.0 → 5.68.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/CHANGELOG.md +39 -0
- package/dist/ContentRenderer.types-RO_l7_6N.d.cts +137 -0
- package/dist/ContentRenderer.types-RO_l7_6N.d.ts +137 -0
- package/dist/{chunk-FXOT4P4P.js → chunk-2LQR4EAN.js} +131 -55
- package/dist/chunk-2LQR4EAN.js.map +1 -0
- package/dist/{chunk-E5UU666U.cjs → chunk-464QSZ4A.cjs} +131 -55
- package/dist/chunk-464QSZ4A.cjs.map +1 -0
- package/dist/{chunk-4S5DFN5N.cjs → chunk-E4DXICU3.cjs} +17 -11
- package/dist/chunk-E4DXICU3.cjs.map +1 -0
- package/dist/{chunk-HEY4SMS2.js → chunk-TQ77ZWBD.js} +10 -4
- package/dist/chunk-TQ77ZWBD.js.map +1 -0
- package/dist/content-renderer.cjs +4 -4
- package/dist/content-renderer.d.cts +3 -72
- package/dist/content-renderer.d.ts +3 -72
- package/dist/content-renderer.js +1 -1
- package/dist/editor.cjs +4 -4
- package/dist/editor.d.cts +19 -2
- package/dist/editor.d.ts +19 -2
- package/dist/editor.js +2 -2
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/docs/ContentRenderer.md +59 -5
- package/docs/Editor.md +38 -0
- package/package.json +1 -1
- package/dist/chunk-4S5DFN5N.cjs.map +0 -1
- package/dist/chunk-E5UU666U.cjs.map +0 -1
- package/dist/chunk-FXOT4P4P.js.map +0 -1
- package/dist/chunk-HEY4SMS2.js.map +0 -1
- package/dist/extensions-DiByZIpD.d.cts +0 -61
- package/dist/extensions-DiByZIpD.d.ts +0 -61
|
@@ -878,7 +878,7 @@ function maskInlineCode(line) {
|
|
|
878
878
|
function scan(src, names, trackFences) {
|
|
879
879
|
const tokens = [];
|
|
880
880
|
let openFenceStart = -1;
|
|
881
|
-
if (names.length === 0 && !trackFences) return { tokens, openFenceStart };
|
|
881
|
+
if (names.length === 0 && !trackFences) return { tokens, openFenceStart, openFence: null };
|
|
882
882
|
const tagRe = names.length ? new RegExp(`<(\\/?)(${names.map(escapeRegExp).join("|")})(?=[\\s>/])([^>]*)>`, "gi") : null;
|
|
883
883
|
let fence = null;
|
|
884
884
|
let pos = 0;
|
|
@@ -929,7 +929,39 @@ function scan(src, names, trackFences) {
|
|
|
929
929
|
if (nl < 0) break;
|
|
930
930
|
pos = lineEnd + 1;
|
|
931
931
|
}
|
|
932
|
-
return { tokens, openFenceStart };
|
|
932
|
+
return { tokens, openFenceStart, openFence: fence };
|
|
933
|
+
}
|
|
934
|
+
function partialTagAt(lastLine, names, markdown) {
|
|
935
|
+
if (!names.length) return -1;
|
|
936
|
+
const masked = markdown ? maskInlineCode(lastLine) : lastLine;
|
|
937
|
+
const lt = masked.lastIndexOf("<");
|
|
938
|
+
if (lt < 0) return -1;
|
|
939
|
+
const m = /^<(\/?)([A-Za-z][\w-]*)?([^>]*)$/.exec(masked.slice(lt));
|
|
940
|
+
if (!m) return -1;
|
|
941
|
+
const name = (m[2] ?? "").toLowerCase();
|
|
942
|
+
const rest = m[3];
|
|
943
|
+
let partial = false;
|
|
944
|
+
if (!name) partial = rest === "";
|
|
945
|
+
else if (names.includes(name)) partial = rest === "" || /^\s/.test(rest);
|
|
946
|
+
else partial = rest === "" && names.some((n) => n.startsWith(name));
|
|
947
|
+
return partial ? lt : -1;
|
|
948
|
+
}
|
|
949
|
+
function openTagsBefore(tokens, cut) {
|
|
950
|
+
const stack = [];
|
|
951
|
+
for (const t of tokens) {
|
|
952
|
+
if (t.start >= cut) break;
|
|
953
|
+
if (t.kind === "open") {
|
|
954
|
+
stack.push(t);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
958
|
+
if (stack[k].name === t.name) {
|
|
959
|
+
stack.length = k;
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return stack;
|
|
933
965
|
}
|
|
934
966
|
function splitMarkdownBlocks(src, tags) {
|
|
935
967
|
const names = tags.map((t) => t.toLowerCase());
|
|
@@ -973,43 +1005,45 @@ function streamingCut(src, tags, markdown) {
|
|
|
973
1005
|
const { tokens, openFenceStart } = scan(src, names, markdown);
|
|
974
1006
|
let cut = src.length;
|
|
975
1007
|
if (openFenceStart >= 0) cut = Math.min(cut, openFenceStart);
|
|
976
|
-
const stack =
|
|
977
|
-
for (const t of tokens) {
|
|
978
|
-
if (t.start >= cut) break;
|
|
979
|
-
if (t.kind === "open") {
|
|
980
|
-
stack.push(t);
|
|
981
|
-
continue;
|
|
982
|
-
}
|
|
983
|
-
for (let k = stack.length - 1; k >= 0; k--) {
|
|
984
|
-
if (stack[k].name === t.name) {
|
|
985
|
-
stack.length = k;
|
|
986
|
-
break;
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
}
|
|
1008
|
+
const stack = openTagsBefore(tokens, cut);
|
|
990
1009
|
if (stack.length) cut = Math.min(cut, stack[0].start);
|
|
991
1010
|
const lastLineStart = src.lastIndexOf("\n", cut - 1) + 1;
|
|
992
1011
|
const lastLine = src.slice(lastLineStart, cut);
|
|
993
1012
|
if (markdown && /^ {0,3}(`{1,2}|~{1,2})$/.test(lastLine)) {
|
|
994
1013
|
return Math.min(cut, lastLineStart);
|
|
995
1014
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1015
|
+
const lt = partialTagAt(lastLine, names, markdown);
|
|
1016
|
+
if (lt >= 0) cut = Math.min(cut, lastLineStart + lt);
|
|
1017
|
+
return cut;
|
|
1018
|
+
}
|
|
1019
|
+
function streamingProgressive(src, tags, markdown) {
|
|
1020
|
+
const names = tags.map((t) => t.toLowerCase());
|
|
1021
|
+
const { tokens, openFenceStart, openFence } = scan(src, names, markdown);
|
|
1022
|
+
let cut = src.length;
|
|
1023
|
+
let fence = openFenceStart >= 0 ? openFence : null;
|
|
1024
|
+
const lastLineStart = src.lastIndexOf("\n", cut - 1) + 1;
|
|
1025
|
+
const lastLine = src.slice(lastLineStart);
|
|
1026
|
+
if (fence) {
|
|
1027
|
+
if (openFenceStart === lastLineStart) {
|
|
1028
|
+
cut = openFenceStart;
|
|
1029
|
+
fence = null;
|
|
1030
|
+
} else {
|
|
1031
|
+
const m = /^ {0,3}(`+|~+)$/.exec(lastLine);
|
|
1032
|
+
if (m && m[1][0] === fence.char && m[1].length < fence.len) cut = lastLineStart;
|
|
1010
1033
|
}
|
|
1034
|
+
} else if (markdown && /^ {0,3}(`{1,2}|~{1,2})$/.test(lastLine)) {
|
|
1035
|
+
cut = lastLineStart;
|
|
1036
|
+
} else {
|
|
1037
|
+
const lt = partialTagAt(lastLine, names, markdown);
|
|
1038
|
+
if (lt >= 0) cut = lastLineStart + lt;
|
|
1011
1039
|
}
|
|
1012
|
-
|
|
1040
|
+
let out = src.slice(0, cut);
|
|
1041
|
+
if (fence) {
|
|
1042
|
+
out += (out.endsWith("\n") ? "" : "\n") + fence.char.repeat(fence.len) + "\n";
|
|
1043
|
+
}
|
|
1044
|
+
const stack = openTagsBefore(tokens, fence ? openFenceStart : cut);
|
|
1045
|
+
for (let k = stack.length - 1; k >= 0; k--) out += `</${stack[k].name}>`;
|
|
1046
|
+
return { source: out, openFence: fence !== null };
|
|
1013
1047
|
}
|
|
1014
1048
|
var FEEDBACK_MS = 2e3;
|
|
1015
1049
|
var WRITE_TIMEOUT_MS = 5e3;
|
|
@@ -1067,12 +1101,17 @@ function cellText(cell) {
|
|
|
1067
1101
|
for (const br of Array.from(clone.querySelectorAll("br"))) br.replaceWith(" ");
|
|
1068
1102
|
return (clone.textContent ?? "").replace(/[\t\r\n]+/g, " ").replace(/ {2,}/g, " ").trim();
|
|
1069
1103
|
}
|
|
1070
|
-
function HtmlChunk({
|
|
1104
|
+
function HtmlChunk({
|
|
1105
|
+
html,
|
|
1106
|
+
fromMarkdown,
|
|
1107
|
+
tail = false
|
|
1108
|
+
}) {
|
|
1071
1109
|
const ref = react.useRef(null);
|
|
1072
1110
|
const ctx = react.useContext(CopyContext);
|
|
1073
1111
|
const [targets, setTargets] = react.useState([]);
|
|
1074
1112
|
const code = ctx?.code ?? false;
|
|
1075
1113
|
const table = ctx?.table ?? false;
|
|
1114
|
+
const holdLastCode = tail && (ctx?.holdTailCode ?? false);
|
|
1076
1115
|
const inner = react.useMemo(() => ({ __html: html }), [html]);
|
|
1077
1116
|
react.useLayoutEffect(() => {
|
|
1078
1117
|
const root = ref.current;
|
|
@@ -1082,7 +1121,9 @@ function HtmlChunk({ html, fromMarkdown }) {
|
|
|
1082
1121
|
}
|
|
1083
1122
|
const found = [];
|
|
1084
1123
|
const selector = [code && "pre", table && "table"].filter(Boolean).join(",");
|
|
1124
|
+
const inProgress = holdLastCode ? Array.from(root.querySelectorAll("pre")).pop() : void 0;
|
|
1085
1125
|
for (const el of Array.from(root.querySelectorAll(selector))) {
|
|
1126
|
+
if (el === inProgress) continue;
|
|
1086
1127
|
const kind = el.tagName === "PRE" ? "code" : "table";
|
|
1087
1128
|
const wrapper = document.createElement("div");
|
|
1088
1129
|
wrapper.setAttribute("data-react-fancy-content-copy-block", kind);
|
|
@@ -1102,7 +1143,7 @@ function HtmlChunk({ html, fromMarkdown }) {
|
|
|
1102
1143
|
if (wrapper?.parentNode) wrapper.replaceWith(t.el);
|
|
1103
1144
|
}
|
|
1104
1145
|
};
|
|
1105
|
-
}, [html, code, table]);
|
|
1146
|
+
}, [html, code, table, holdLastCode]);
|
|
1106
1147
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1107
1148
|
/* @__PURE__ */ jsxRuntime.jsx("div", { ref, dangerouslySetInnerHTML: inner }),
|
|
1108
1149
|
targets.map(
|
|
@@ -1172,7 +1213,11 @@ function useCopyPipeline({
|
|
|
1172
1213
|
);
|
|
1173
1214
|
}
|
|
1174
1215
|
var MAX_DEPTH = 8;
|
|
1175
|
-
function
|
|
1216
|
+
function lastIndexWith(items, renders) {
|
|
1217
|
+
for (let i = items.length - 1; i >= 0; i--) if (renders(items[i])) return i;
|
|
1218
|
+
return -1;
|
|
1219
|
+
}
|
|
1220
|
+
function ContentBody({ source, format, extensions, unsafe, depth = 0, tail = false }) {
|
|
1176
1221
|
const active = depth >= MAX_DEPTH ? [] : extensions;
|
|
1177
1222
|
const blocks = react.useMemo(() => {
|
|
1178
1223
|
if (format !== "markdown") return null;
|
|
@@ -1181,8 +1226,19 @@ function ContentBody({ source, format, extensions, unsafe, depth = 0 }) {
|
|
|
1181
1226
|
}, [source, format, active]);
|
|
1182
1227
|
if (!blocks) {
|
|
1183
1228
|
const html = unsafe ? source : chunk3U7PUZLI_cjs.sanitizeHtml(source);
|
|
1184
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1229
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1230
|
+
RenderedContent,
|
|
1231
|
+
{
|
|
1232
|
+
html,
|
|
1233
|
+
extensions: active,
|
|
1234
|
+
unsafe,
|
|
1235
|
+
fromMarkdown: false,
|
|
1236
|
+
depth,
|
|
1237
|
+
tail
|
|
1238
|
+
}
|
|
1239
|
+
);
|
|
1185
1240
|
}
|
|
1241
|
+
const last = tail ? lastIndexWith(blocks, (b) => b.type === "tag" || b.source.trim() !== "") : -1;
|
|
1186
1242
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: blocks.map((block, i) => {
|
|
1187
1243
|
if (block.type === "markdown") {
|
|
1188
1244
|
const raw = markdownToRawHtml(block.source);
|
|
@@ -1194,7 +1250,8 @@ function ContentBody({ source, format, extensions, unsafe, depth = 0 }) {
|
|
|
1194
1250
|
extensions: active,
|
|
1195
1251
|
unsafe,
|
|
1196
1252
|
fromMarkdown: true,
|
|
1197
|
-
depth
|
|
1253
|
+
depth,
|
|
1254
|
+
tail: i === last
|
|
1198
1255
|
},
|
|
1199
1256
|
i
|
|
1200
1257
|
);
|
|
@@ -1210,21 +1267,38 @@ function ContentBody({ source, format, extensions, unsafe, depth = 0 }) {
|
|
|
1210
1267
|
format: "markdown",
|
|
1211
1268
|
extensions: active,
|
|
1212
1269
|
unsafe,
|
|
1213
|
-
depth: depth + 1
|
|
1270
|
+
depth: depth + 1,
|
|
1271
|
+
tail: i === last
|
|
1214
1272
|
}
|
|
1215
1273
|
) }) }, i);
|
|
1216
1274
|
}) });
|
|
1217
1275
|
}
|
|
1218
|
-
function RenderedContent({
|
|
1276
|
+
function RenderedContent({
|
|
1277
|
+
html,
|
|
1278
|
+
extensions,
|
|
1279
|
+
unsafe,
|
|
1280
|
+
fromMarkdown,
|
|
1281
|
+
depth,
|
|
1282
|
+
tail = false
|
|
1283
|
+
}) {
|
|
1219
1284
|
const segments = react.useMemo(() => parseSegments(html, extensions), [html, extensions]);
|
|
1220
1285
|
const renderHtml = (content) => unsafe ? content : chunk3U7PUZLI_cjs.sanitizeHtml(content);
|
|
1221
1286
|
if (segments.length === 0) return null;
|
|
1222
1287
|
if (segments.length === 1 && segments[0].type === "html") {
|
|
1223
|
-
return /* @__PURE__ */ jsxRuntime.jsx(HtmlChunk, { html: renderHtml(segments[0].content), fromMarkdown });
|
|
1288
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HtmlChunk, { html: renderHtml(segments[0].content), fromMarkdown, tail });
|
|
1224
1289
|
}
|
|
1290
|
+
const last = tail ? lastIndexWith(segments, (s) => s.type === "extension" || s.content.trim() !== "") : -1;
|
|
1225
1291
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: segments.map((segment, i) => {
|
|
1226
1292
|
if (segment.type === "html") {
|
|
1227
|
-
return segment.content ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1293
|
+
return segment.content ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1294
|
+
HtmlChunk,
|
|
1295
|
+
{
|
|
1296
|
+
html: renderHtml(segment.content),
|
|
1297
|
+
fromMarkdown,
|
|
1298
|
+
tail: i === last
|
|
1299
|
+
},
|
|
1300
|
+
i
|
|
1301
|
+
) : null;
|
|
1228
1302
|
}
|
|
1229
1303
|
const ext = extensions.find((e) => e.tag.toLowerCase() === segment.tag);
|
|
1230
1304
|
if (!ext) return null;
|
|
@@ -1237,7 +1311,8 @@ function RenderedContent({ html, extensions, unsafe, fromMarkdown, depth }) {
|
|
|
1237
1311
|
format: "html",
|
|
1238
1312
|
extensions,
|
|
1239
1313
|
unsafe,
|
|
1240
|
-
depth: depth + 1
|
|
1314
|
+
depth: depth + 1,
|
|
1315
|
+
tail: i === last
|
|
1241
1316
|
}
|
|
1242
1317
|
) : void 0;
|
|
1243
1318
|
return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { "data-render-extension": segment.tag, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { content: segment.content, attributes: segment.attributes, children }) }, i);
|
|
@@ -1263,15 +1338,14 @@ function ContentRenderer({
|
|
|
1263
1338
|
);
|
|
1264
1339
|
const safe = value ?? "";
|
|
1265
1340
|
const resolvedFormat = format === "auto" ? detectFormat(safe) : format;
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
);
|
|
1273
|
-
|
|
1274
|
-
}, [safe, streaming, extensions, resolvedFormat]);
|
|
1341
|
+
const progressive = streaming === "progressive";
|
|
1342
|
+
const { source, openFence } = react.useMemo(() => {
|
|
1343
|
+
if (!streaming) return { source: safe, openFence: false };
|
|
1344
|
+
const tags = extensions.map((e) => e.tag);
|
|
1345
|
+
const markdown = resolvedFormat === "markdown";
|
|
1346
|
+
if (progressive) return streamingProgressive(safe, tags, markdown);
|
|
1347
|
+
return { source: safe.slice(0, streamingCut(safe, tags, markdown)), openFence: false };
|
|
1348
|
+
}, [safe, streaming, progressive, extensions, resolvedFormat]);
|
|
1275
1349
|
const codeOn = copyButtons === true || typeof copyButtons === "object" && copyButtons.code !== false;
|
|
1276
1350
|
const tableOn = copyButtons === true || typeof copyButtons === "object" && copyButtons.table !== false;
|
|
1277
1351
|
const copyOn = codeOn || tableOn;
|
|
@@ -1283,8 +1357,8 @@ function ContentRenderer({
|
|
|
1283
1357
|
}, []);
|
|
1284
1358
|
const copy = useCopyPipeline({ writer: clipboardWriter, onCopy, rootRef, announce });
|
|
1285
1359
|
const copyContext = react.useMemo(
|
|
1286
|
-
() => ({ code: codeOn, table: tableOn, copy }),
|
|
1287
|
-
[codeOn, tableOn, copy]
|
|
1360
|
+
() => ({ code: codeOn, table: tableOn, copy, holdTailCode: openFence }),
|
|
1361
|
+
[codeOn, tableOn, copy, openFence]
|
|
1288
1362
|
);
|
|
1289
1363
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1290
1364
|
"div",
|
|
@@ -1292,6 +1366,7 @@ function ContentRenderer({
|
|
|
1292
1366
|
ref: rootRef,
|
|
1293
1367
|
"data-react-fancy-content-renderer": "",
|
|
1294
1368
|
"data-streaming": streaming ? "true" : void 0,
|
|
1369
|
+
"data-streaming-mode": progressive ? "progressive" : void 0,
|
|
1295
1370
|
style: { lineHeight: lineSpacing },
|
|
1296
1371
|
className: chunkZU5Q77OT_cjs.cn("text-sm", proseClasses, className),
|
|
1297
1372
|
children: [
|
|
@@ -1301,7 +1376,8 @@ function ContentRenderer({
|
|
|
1301
1376
|
source,
|
|
1302
1377
|
format: resolvedFormat,
|
|
1303
1378
|
extensions,
|
|
1304
|
-
unsafe
|
|
1379
|
+
unsafe,
|
|
1380
|
+
tail: true
|
|
1305
1381
|
}
|
|
1306
1382
|
) }),
|
|
1307
1383
|
copyOn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1331,5 +1407,5 @@ exports.proseClasses = proseClasses;
|
|
|
1331
1407
|
exports.registerExtension = registerExtension;
|
|
1332
1408
|
exports.registerExtensions = registerExtensions;
|
|
1333
1409
|
exports.renderMarkdown = renderMarkdown;
|
|
1334
|
-
//# sourceMappingURL=chunk-
|
|
1335
|
-
//# sourceMappingURL=chunk-
|
|
1410
|
+
//# sourceMappingURL=chunk-464QSZ4A.cjs.map
|
|
1411
|
+
//# sourceMappingURL=chunk-464QSZ4A.cjs.map
|