@particle-academy/react-fancy 5.66.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 +91 -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/button.cjs +3 -4
- package/dist/button.js +1 -2
- package/dist/{chunk-J44EV6SP.js → chunk-23AVEYUP.js} +3 -3
- package/dist/{chunk-J44EV6SP.js.map → chunk-23AVEYUP.js.map} +1 -1
- package/dist/{chunk-L24LUSEE.js → chunk-2LQR4EAN.js} +553 -35
- package/dist/chunk-2LQR4EAN.js.map +1 -0
- package/dist/{chunk-C5YX5XVL.cjs → chunk-3U7PUZLI.cjs} +214 -4
- package/dist/chunk-3U7PUZLI.cjs.map +1 -0
- package/dist/{chunk-MCQJFMDP.cjs → chunk-464QSZ4A.cjs} +553 -35
- package/dist/chunk-464QSZ4A.cjs.map +1 -0
- package/dist/{chunk-QP35Z2WB.js → chunk-BL5IKSB3.js} +3 -3
- package/dist/{chunk-QP35Z2WB.js.map → chunk-BL5IKSB3.js.map} +1 -1
- package/dist/{chunk-4EUDW2WU.js → chunk-DCAD2JPP.js} +3 -3
- package/dist/{chunk-4EUDW2WU.js.map → chunk-DCAD2JPP.js.map} +1 -1
- package/dist/{chunk-ATIDUWY6.cjs → chunk-E4DXICU3.cjs} +19 -13
- package/dist/chunk-E4DXICU3.cjs.map +1 -0
- package/dist/{chunk-TLRRMYNV.js → chunk-OXERDFFF.js} +212 -4
- package/dist/chunk-OXERDFFF.js.map +1 -0
- package/dist/{chunk-SYEHWUM5.cjs → chunk-RALJMI7C.cjs} +4 -4
- package/dist/{chunk-SYEHWUM5.cjs.map → chunk-RALJMI7C.cjs.map} +1 -1
- package/dist/{chunk-UM4JXBRZ.cjs → chunk-S3NB6LDV.cjs} +4 -4
- package/dist/{chunk-UM4JXBRZ.cjs.map → chunk-S3NB6LDV.cjs.map} +1 -1
- package/dist/{chunk-MCQWTOTV.cjs → chunk-SAG6DHQK.cjs} +6 -6
- package/dist/{chunk-MCQWTOTV.cjs.map → chunk-SAG6DHQK.cjs.map} +1 -1
- package/dist/{chunk-CAUEKGMR.js → chunk-TQ77ZWBD.js} +11 -5
- package/dist/chunk-TQ77ZWBD.js.map +1 -0
- package/dist/{chunk-7HXFEYOV.cjs → chunk-U5LKNGHU.cjs} +15 -15
- package/dist/{chunk-7HXFEYOV.cjs.map → chunk-U5LKNGHU.cjs.map} +1 -1
- package/dist/{chunk-M52S3TFI.js → chunk-WFR7HKJL.js} +3 -3
- package/dist/{chunk-M52S3TFI.js.map → chunk-WFR7HKJL.js.map} +1 -1
- package/dist/content-renderer.cjs +7 -5
- package/dist/content-renderer.d.cts +4 -21
- package/dist/content-renderer.d.ts +4 -21
- package/dist/content-renderer.js +4 -2
- package/dist/editor.cjs +6 -5
- package/dist/editor.d.cts +19 -2
- package/dist/editor.d.ts +19 -2
- package/dist/editor.js +4 -3
- package/dist/index.cjs +37 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/json-editor.cjs +13 -14
- package/dist/json-editor.js +2 -3
- package/dist/magic-wand.cjs +3 -4
- package/dist/magic-wand.js +2 -3
- package/dist/prompt-input.cjs +5 -6
- package/dist/prompt-input.js +2 -3
- package/dist/reason-tag.cjs +3 -4
- package/dist/reason-tag.js +2 -3
- package/docs/ContentRenderer.md +192 -3
- package/docs/Editor.md +38 -0
- package/package.json +1 -1
- package/dist/chunk-6TBRBN7H.js +0 -212
- package/dist/chunk-6TBRBN7H.js.map +0 -1
- package/dist/chunk-ATIDUWY6.cjs.map +0 -1
- package/dist/chunk-C5YX5XVL.cjs.map +0 -1
- package/dist/chunk-CAUEKGMR.js.map +0 -1
- package/dist/chunk-L24LUSEE.js.map +0 -1
- package/dist/chunk-MCQJFMDP.cjs.map +0 -1
- package/dist/chunk-TLRRMYNV.js.map +0 -1
- package/dist/chunk-X2FC7ODX.cjs +0 -215
- package/dist/chunk-X2FC7ODX.cjs.map +0 -1
- package/dist/extensions-6PVd3-vB.d.cts +0 -38
- package/dist/extensions-6PVd3-vB.d.ts +0 -38
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk3U7PUZLI_cjs = require('./chunk-3U7PUZLI.cjs');
|
|
4
4
|
var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
|
|
5
5
|
var marked = require('marked');
|
|
6
6
|
var react = require('react');
|
|
7
|
+
var reactDom = require('react-dom');
|
|
7
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
9
|
|
|
9
10
|
// src/components/ContentRenderer/extensions.ts
|
|
11
|
+
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href"]);
|
|
12
|
+
function safeAttributes(attrs) {
|
|
13
|
+
const out = {};
|
|
14
|
+
for (const [name, value] of Object.entries(attrs)) {
|
|
15
|
+
const lower = name.toLowerCase();
|
|
16
|
+
if (lower.startsWith("on") || lower === "srcdoc") continue;
|
|
17
|
+
if (URL_ATTRS.has(lower)) {
|
|
18
|
+
const safe = chunk3U7PUZLI_cjs.sanitizeHref(value);
|
|
19
|
+
if (safe === void 0) continue;
|
|
20
|
+
out[name] = safe;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
out[name] = value;
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function tagAttributes(raw, unsafe) {
|
|
28
|
+
const attrs = raw ? parseAttributes(raw) : {};
|
|
29
|
+
return unsafe ? attrs : safeAttributes(attrs);
|
|
30
|
+
}
|
|
10
31
|
var globalRegistry = [];
|
|
11
32
|
function registerExtension(extension) {
|
|
12
33
|
const idx = globalRegistry.findIndex(
|
|
@@ -88,7 +109,7 @@ function markdownToRawHtml(markdown) {
|
|
|
88
109
|
return marked.marked.parse(markdown ?? "", { async: false });
|
|
89
110
|
}
|
|
90
111
|
function renderMarkdown(markdown, sanitize) {
|
|
91
|
-
return
|
|
112
|
+
return chunk3U7PUZLI_cjs.sanitizeHtml(markdownToRawHtml(markdown), sanitize);
|
|
92
113
|
}
|
|
93
114
|
var proseClasses = [
|
|
94
115
|
"[&_h1]:text-2xl [&_h1]:font-bold [&_h1]:mt-4 [&_h1]:mb-2",
|
|
@@ -813,67 +834,564 @@ function extensionEditorClasses(extensions) {
|
|
|
813
834
|
].join(" ");
|
|
814
835
|
}).join(" ");
|
|
815
836
|
}
|
|
816
|
-
|
|
837
|
+
|
|
838
|
+
// src/components/ContentRenderer/markdown-blocks.ts
|
|
839
|
+
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
840
|
+
function escapeRegExp(s) {
|
|
841
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
842
|
+
}
|
|
843
|
+
function maskInlineCode(line) {
|
|
844
|
+
let out = "";
|
|
845
|
+
let i = 0;
|
|
846
|
+
while (i < line.length) {
|
|
847
|
+
if (line[i] !== "`") {
|
|
848
|
+
out += line[i];
|
|
849
|
+
i++;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
let n = 0;
|
|
853
|
+
while (line[i + n] === "`") n++;
|
|
854
|
+
const fence = "`".repeat(n);
|
|
855
|
+
let search = i + n;
|
|
856
|
+
let close = -1;
|
|
857
|
+
while (search < line.length) {
|
|
858
|
+
const at = line.indexOf(fence, search);
|
|
859
|
+
if (at < 0) break;
|
|
860
|
+
let len = 0;
|
|
861
|
+
while (line[at + len] === "`") len++;
|
|
862
|
+
if (len === n) {
|
|
863
|
+
close = at;
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
search = at + len;
|
|
867
|
+
}
|
|
868
|
+
if (close < 0) {
|
|
869
|
+
out += fence;
|
|
870
|
+
i += n;
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
out += " ".repeat(close + n - i);
|
|
874
|
+
i = close + n;
|
|
875
|
+
}
|
|
876
|
+
return out;
|
|
877
|
+
}
|
|
878
|
+
function scan(src, names, trackFences) {
|
|
879
|
+
const tokens = [];
|
|
880
|
+
let openFenceStart = -1;
|
|
881
|
+
if (names.length === 0 && !trackFences) return { tokens, openFenceStart, openFence: null };
|
|
882
|
+
const tagRe = names.length ? new RegExp(`<(\\/?)(${names.map(escapeRegExp).join("|")})(?=[\\s>/])([^>]*)>`, "gi") : null;
|
|
883
|
+
let fence = null;
|
|
884
|
+
let pos = 0;
|
|
885
|
+
while (pos <= src.length) {
|
|
886
|
+
const nl = src.indexOf("\n", pos);
|
|
887
|
+
const lineEnd = nl < 0 ? src.length : nl;
|
|
888
|
+
const line = src.slice(pos, lineEnd);
|
|
889
|
+
if (trackFences) {
|
|
890
|
+
if (fence) {
|
|
891
|
+
const close = new RegExp(`^ {0,3}${fence.char === "`" ? "`" : "~"}{${fence.len},}\\s*$`);
|
|
892
|
+
if (close.test(line)) {
|
|
893
|
+
fence = null;
|
|
894
|
+
openFenceStart = -1;
|
|
895
|
+
}
|
|
896
|
+
pos = lineEnd + 1;
|
|
897
|
+
if (nl < 0) break;
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
const m = FENCE_OPEN.exec(line);
|
|
901
|
+
if (m && !(m[1][0] === "`" && m[2].includes("`"))) {
|
|
902
|
+
fence = { char: m[1][0], len: m[1].length };
|
|
903
|
+
openFenceStart = pos;
|
|
904
|
+
pos = lineEnd + 1;
|
|
905
|
+
if (nl < 0) break;
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (tagRe) {
|
|
910
|
+
const masked = trackFences ? maskInlineCode(line) : line;
|
|
911
|
+
const indent2 = /^ {0,3}/.exec(masked)[0].length;
|
|
912
|
+
tagRe.lastIndex = 0;
|
|
913
|
+
let t;
|
|
914
|
+
while ((t = tagRe.exec(masked)) !== null) {
|
|
915
|
+
const isClose = t[1] === "/";
|
|
916
|
+
const attrs = t[3];
|
|
917
|
+
if (isClose && attrs.trim() !== "") continue;
|
|
918
|
+
if (!isClose && attrs.trimEnd().endsWith("/")) continue;
|
|
919
|
+
tokens.push({
|
|
920
|
+
kind: isClose ? "close" : "open",
|
|
921
|
+
name: t[2].toLowerCase(),
|
|
922
|
+
start: pos + t.index,
|
|
923
|
+
end: pos + t.index + t[0].length,
|
|
924
|
+
attrs: isClose ? "" : attrs.trim(),
|
|
925
|
+
lineStart: t.index === indent2
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (nl < 0) break;
|
|
930
|
+
pos = lineEnd + 1;
|
|
931
|
+
}
|
|
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;
|
|
965
|
+
}
|
|
966
|
+
function splitMarkdownBlocks(src, tags) {
|
|
967
|
+
const names = tags.map((t) => t.toLowerCase());
|
|
968
|
+
if (names.length === 0) return src ? [{ type: "markdown", source: src }] : [];
|
|
969
|
+
const { tokens } = scan(src, names, true);
|
|
970
|
+
const blocks = [];
|
|
971
|
+
let cursor = 0;
|
|
972
|
+
let i = 0;
|
|
973
|
+
while (i < tokens.length) {
|
|
974
|
+
const open = tokens[i];
|
|
975
|
+
if (open.kind !== "open" || !open.lineStart || open.start < cursor) {
|
|
976
|
+
i++;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
let depth = 0;
|
|
980
|
+
let closeIdx = -1;
|
|
981
|
+
for (let j = i; j < tokens.length; j++) {
|
|
982
|
+
const t = tokens[j];
|
|
983
|
+
if (t.name !== open.name) continue;
|
|
984
|
+
depth += t.kind === "open" ? 1 : -1;
|
|
985
|
+
if (depth === 0) {
|
|
986
|
+
closeIdx = j;
|
|
987
|
+
break;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (closeIdx < 0) {
|
|
991
|
+
i++;
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const close = tokens[closeIdx];
|
|
995
|
+
if (open.start > cursor) blocks.push({ type: "markdown", source: src.slice(cursor, open.start) });
|
|
996
|
+
blocks.push({ type: "tag", tag: open.name, attrs: open.attrs, body: src.slice(open.end, close.start) });
|
|
997
|
+
cursor = close.end;
|
|
998
|
+
i = closeIdx + 1;
|
|
999
|
+
}
|
|
1000
|
+
if (cursor < src.length) blocks.push({ type: "markdown", source: src.slice(cursor) });
|
|
1001
|
+
return blocks;
|
|
1002
|
+
}
|
|
1003
|
+
function streamingCut(src, tags, markdown) {
|
|
1004
|
+
const names = tags.map((t) => t.toLowerCase());
|
|
1005
|
+
const { tokens, openFenceStart } = scan(src, names, markdown);
|
|
1006
|
+
let cut = src.length;
|
|
1007
|
+
if (openFenceStart >= 0) cut = Math.min(cut, openFenceStart);
|
|
1008
|
+
const stack = openTagsBefore(tokens, cut);
|
|
1009
|
+
if (stack.length) cut = Math.min(cut, stack[0].start);
|
|
1010
|
+
const lastLineStart = src.lastIndexOf("\n", cut - 1) + 1;
|
|
1011
|
+
const lastLine = src.slice(lastLineStart, cut);
|
|
1012
|
+
if (markdown && /^ {0,3}(`{1,2}|~{1,2})$/.test(lastLine)) {
|
|
1013
|
+
return Math.min(cut, lastLineStart);
|
|
1014
|
+
}
|
|
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;
|
|
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;
|
|
1039
|
+
}
|
|
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 };
|
|
1047
|
+
}
|
|
1048
|
+
var FEEDBACK_MS = 2e3;
|
|
1049
|
+
var WRITE_TIMEOUT_MS = 5e3;
|
|
1050
|
+
var CopyContext = react.createContext(null);
|
|
1051
|
+
async function defaultClipboardWriter(payload) {
|
|
1052
|
+
const clip = typeof navigator !== "undefined" ? navigator.clipboard : void 0;
|
|
1053
|
+
if (!clip) throw new Error("Clipboard API unavailable");
|
|
1054
|
+
const Item = globalThis.ClipboardItem;
|
|
1055
|
+
if (payload.html && Item && typeof clip.write === "function") {
|
|
1056
|
+
try {
|
|
1057
|
+
await clip.write([
|
|
1058
|
+
new Item({
|
|
1059
|
+
"text/html": new Blob([payload.html], { type: "text/html" }),
|
|
1060
|
+
"text/plain": new Blob([payload.text], { type: "text/plain" })
|
|
1061
|
+
})
|
|
1062
|
+
]);
|
|
1063
|
+
return;
|
|
1064
|
+
} catch {
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
if (typeof clip.writeText !== "function") throw new Error("Clipboard writeText unavailable");
|
|
1068
|
+
await clip.writeText(payload.text);
|
|
1069
|
+
}
|
|
1070
|
+
async function runWriter(writer, payload) {
|
|
1071
|
+
let timer;
|
|
1072
|
+
try {
|
|
1073
|
+
const result = await Promise.race([
|
|
1074
|
+
Promise.resolve().then(() => writer(payload)),
|
|
1075
|
+
new Promise((resolve) => {
|
|
1076
|
+
timer = setTimeout(() => resolve(false), WRITE_TIMEOUT_MS);
|
|
1077
|
+
})
|
|
1078
|
+
]);
|
|
1079
|
+
return result !== false;
|
|
1080
|
+
} catch {
|
|
1081
|
+
return false;
|
|
1082
|
+
} finally {
|
|
1083
|
+
if (timer) clearTimeout(timer);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
function codeText(pre, fromMarkdown) {
|
|
1087
|
+
const text = (pre.querySelector("code") ?? pre).textContent ?? "";
|
|
1088
|
+
return fromMarkdown && text.endsWith("\n") ? text.slice(0, -1) : text;
|
|
1089
|
+
}
|
|
1090
|
+
function tableTsv(table) {
|
|
1091
|
+
const rows = [];
|
|
1092
|
+
for (const tr of Array.from(table.querySelectorAll("tr"))) {
|
|
1093
|
+
if (tr.closest("table") !== table) continue;
|
|
1094
|
+
const cells = Array.from(tr.children).filter((c) => c.tagName === "TD" || c.tagName === "TH");
|
|
1095
|
+
rows.push(cells.map((c) => cellText(c)).join(" "));
|
|
1096
|
+
}
|
|
1097
|
+
return rows.join("\n");
|
|
1098
|
+
}
|
|
1099
|
+
function cellText(cell) {
|
|
1100
|
+
const clone = cell.cloneNode(true);
|
|
1101
|
+
for (const br of Array.from(clone.querySelectorAll("br"))) br.replaceWith(" ");
|
|
1102
|
+
return (clone.textContent ?? "").replace(/[\t\r\n]+/g, " ").replace(/ {2,}/g, " ").trim();
|
|
1103
|
+
}
|
|
1104
|
+
function HtmlChunk({
|
|
817
1105
|
html,
|
|
818
|
-
|
|
819
|
-
|
|
1106
|
+
fromMarkdown,
|
|
1107
|
+
tail = false
|
|
820
1108
|
}) {
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
1109
|
+
const ref = react.useRef(null);
|
|
1110
|
+
const ctx = react.useContext(CopyContext);
|
|
1111
|
+
const [targets, setTargets] = react.useState([]);
|
|
1112
|
+
const code = ctx?.code ?? false;
|
|
1113
|
+
const table = ctx?.table ?? false;
|
|
1114
|
+
const holdLastCode = tail && (ctx?.holdTailCode ?? false);
|
|
1115
|
+
const inner = react.useMemo(() => ({ __html: html }), [html]);
|
|
1116
|
+
react.useLayoutEffect(() => {
|
|
1117
|
+
const root = ref.current;
|
|
1118
|
+
if (!root || !code && !table) {
|
|
1119
|
+
setTargets([]);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const found = [];
|
|
1123
|
+
const selector = [code && "pre", table && "table"].filter(Boolean).join(",");
|
|
1124
|
+
const inProgress = holdLastCode ? Array.from(root.querySelectorAll("pre")).pop() : void 0;
|
|
1125
|
+
for (const el of Array.from(root.querySelectorAll(selector))) {
|
|
1126
|
+
if (el === inProgress) continue;
|
|
1127
|
+
const kind = el.tagName === "PRE" ? "code" : "table";
|
|
1128
|
+
const wrapper = document.createElement("div");
|
|
1129
|
+
wrapper.setAttribute("data-react-fancy-content-copy-block", kind);
|
|
1130
|
+
wrapper.className = kind === "code" ? "group relative" : "group relative flex flex-col";
|
|
1131
|
+
const host = document.createElement("div");
|
|
1132
|
+
host.setAttribute("data-react-fancy-content-copy-host", "");
|
|
1133
|
+
host.className = kind === "code" ? "absolute right-2 top-2 z-10" : "flex justify-end";
|
|
1134
|
+
el.replaceWith(wrapper);
|
|
1135
|
+
if (kind === "table") wrapper.append(host, el);
|
|
1136
|
+
else wrapper.append(el, host);
|
|
1137
|
+
found.push({ kind, el, host });
|
|
1138
|
+
}
|
|
1139
|
+
setTargets(found);
|
|
1140
|
+
return () => {
|
|
1141
|
+
for (const t of found) {
|
|
1142
|
+
const wrapper = t.host.parentElement;
|
|
1143
|
+
if (wrapper?.parentNode) wrapper.replaceWith(t.el);
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
}, [html, code, table, holdLastCode]);
|
|
1147
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1148
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref, dangerouslySetInnerHTML: inner }),
|
|
1149
|
+
targets.map(
|
|
1150
|
+
(t, i) => reactDom.createPortal(
|
|
1151
|
+
/* @__PURE__ */ jsxRuntime.jsx(CopyButton, { kind: t.kind, target: t.el, fromMarkdown }),
|
|
1152
|
+
t.host,
|
|
1153
|
+
`${i}`
|
|
1154
|
+
)
|
|
1155
|
+
)
|
|
1156
|
+
] });
|
|
1157
|
+
}
|
|
1158
|
+
function CopyButton({
|
|
1159
|
+
kind,
|
|
1160
|
+
target,
|
|
1161
|
+
fromMarkdown
|
|
1162
|
+
}) {
|
|
1163
|
+
const ctx = react.useContext(CopyContext);
|
|
1164
|
+
const [state, setState] = react.useState("idle");
|
|
1165
|
+
const timer = react.useRef(void 0);
|
|
1166
|
+
const btn = react.useRef(null);
|
|
1167
|
+
react.useEffect(() => () => clearTimeout(timer.current), []);
|
|
1168
|
+
const onClick = react.useCallback(async () => {
|
|
1169
|
+
if (!ctx || !btn.current) return;
|
|
1170
|
+
const text = kind === "code" ? codeText(target, fromMarkdown) : tableTsv(target);
|
|
1171
|
+
const ok = await ctx.copy({ kind, text, html: target.outerHTML }, btn.current);
|
|
1172
|
+
setState(ok ? "copied" : "failed");
|
|
1173
|
+
clearTimeout(timer.current);
|
|
1174
|
+
timer.current = setTimeout(() => setState("idle"), FEEDBACK_MS);
|
|
1175
|
+
}, [ctx, kind, target, fromMarkdown]);
|
|
1176
|
+
const idleLabel = kind === "code" ? "Copy code" : "Copy table";
|
|
1177
|
+
const label = state === "copied" ? "Copied" : state === "failed" ? "Copy failed" : idleLabel;
|
|
1178
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1179
|
+
chunk3U7PUZLI_cjs.Button,
|
|
1180
|
+
{
|
|
1181
|
+
ref: btn,
|
|
1182
|
+
type: "button",
|
|
1183
|
+
size: "xs",
|
|
1184
|
+
variant: "ghost",
|
|
1185
|
+
icon: state === "copied" ? "check" : state === "failed" ? "x" : "copy",
|
|
1186
|
+
"aria-label": label,
|
|
1187
|
+
title: label,
|
|
1188
|
+
"data-react-fancy-content-copy": kind,
|
|
1189
|
+
"data-copy-state": state,
|
|
1190
|
+
onClick,
|
|
1191
|
+
className: kind === "code" ? "text-zinc-300 hover:bg-zinc-800 hover:text-white opacity-70 group-hover:opacity-100 focus-visible:opacity-100" : "opacity-70 group-hover:opacity-100 focus-visible:opacity-100"
|
|
1192
|
+
}
|
|
824
1193
|
);
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1194
|
+
}
|
|
1195
|
+
function useCopyPipeline({
|
|
1196
|
+
writer,
|
|
1197
|
+
onCopy,
|
|
1198
|
+
rootRef,
|
|
1199
|
+
announce
|
|
1200
|
+
}) {
|
|
1201
|
+
const latest = react.useRef({ writer, onCopy, announce });
|
|
1202
|
+
latest.current = { writer, onCopy, announce };
|
|
1203
|
+
return react.useCallback(
|
|
1204
|
+
async (payload, button) => {
|
|
1205
|
+
const { writer: w, onCopy: report, announce: say } = latest.current;
|
|
1206
|
+
const ok = await runWriter(w ?? defaultClipboardWriter, payload);
|
|
1207
|
+
say(ok ? "Copied" : "Copy failed");
|
|
1208
|
+
const all = rootRef.current ? Array.from(rootRef.current.querySelectorAll("[data-react-fancy-content-copy]")) : [];
|
|
1209
|
+
report?.({ ...payload, index: all.indexOf(button), ok });
|
|
1210
|
+
return ok;
|
|
1211
|
+
},
|
|
1212
|
+
[rootRef]
|
|
828
1213
|
);
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1214
|
+
}
|
|
1215
|
+
var MAX_DEPTH = 8;
|
|
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 }) {
|
|
1221
|
+
const active = depth >= MAX_DEPTH ? [] : extensions;
|
|
1222
|
+
const blocks = react.useMemo(() => {
|
|
1223
|
+
if (format !== "markdown") return null;
|
|
1224
|
+
const mdTags = active.filter((e) => e.markdown).map((e) => e.tag);
|
|
1225
|
+
return splitMarkdownBlocks(source, mdTags);
|
|
1226
|
+
}, [source, format, active]);
|
|
1227
|
+
if (!blocks) {
|
|
1228
|
+
const html = unsafe ? source : chunk3U7PUZLI_cjs.sanitizeHtml(source);
|
|
1229
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1230
|
+
RenderedContent,
|
|
1231
|
+
{
|
|
1232
|
+
html,
|
|
1233
|
+
extensions: active,
|
|
1234
|
+
unsafe,
|
|
1235
|
+
fromMarkdown: false,
|
|
1236
|
+
depth,
|
|
1237
|
+
tail
|
|
1238
|
+
}
|
|
1239
|
+
);
|
|
832
1240
|
}
|
|
833
|
-
|
|
834
|
-
|
|
1241
|
+
const last = tail ? lastIndexWith(blocks, (b) => b.type === "tag" || b.source.trim() !== "") : -1;
|
|
1242
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: blocks.map((block, i) => {
|
|
1243
|
+
if (block.type === "markdown") {
|
|
1244
|
+
const raw = markdownToRawHtml(block.source);
|
|
1245
|
+
const html = unsafe ? raw : chunk3U7PUZLI_cjs.sanitizeHtml(raw);
|
|
1246
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1247
|
+
RenderedContent,
|
|
1248
|
+
{
|
|
1249
|
+
html,
|
|
1250
|
+
extensions: active,
|
|
1251
|
+
unsafe,
|
|
1252
|
+
fromMarkdown: true,
|
|
1253
|
+
depth,
|
|
1254
|
+
tail: i === last
|
|
1255
|
+
},
|
|
1256
|
+
i
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
const ext = active.find((e) => e.tag.toLowerCase() === block.tag);
|
|
1260
|
+
if (!ext) return null;
|
|
1261
|
+
const Component = ext.component;
|
|
1262
|
+
const Wrapper = ext.block !== false ? "div" : "span";
|
|
1263
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { "data-render-extension": block.tag, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { content: block.body, attributes: tagAttributes(block.attrs, unsafe), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1264
|
+
ContentBody,
|
|
1265
|
+
{
|
|
1266
|
+
source: block.body,
|
|
1267
|
+
format: "markdown",
|
|
1268
|
+
extensions: active,
|
|
1269
|
+
unsafe,
|
|
1270
|
+
depth: depth + 1,
|
|
1271
|
+
tail: i === last
|
|
1272
|
+
}
|
|
1273
|
+
) }) }, i);
|
|
1274
|
+
}) });
|
|
1275
|
+
}
|
|
1276
|
+
function RenderedContent({
|
|
1277
|
+
html,
|
|
1278
|
+
extensions,
|
|
1279
|
+
unsafe,
|
|
1280
|
+
fromMarkdown,
|
|
1281
|
+
depth,
|
|
1282
|
+
tail = false
|
|
1283
|
+
}) {
|
|
1284
|
+
const segments = react.useMemo(() => parseSegments(html, extensions), [html, extensions]);
|
|
1285
|
+
const renderHtml = (content) => unsafe ? content : chunk3U7PUZLI_cjs.sanitizeHtml(content);
|
|
1286
|
+
if (segments.length === 0) return null;
|
|
1287
|
+
if (segments.length === 1 && segments[0].type === "html") {
|
|
1288
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HtmlChunk, { html: renderHtml(segments[0].content), fromMarkdown, tail });
|
|
835
1289
|
}
|
|
1290
|
+
const last = tail ? lastIndexWith(segments, (s) => s.type === "extension" || s.content.trim() !== "") : -1;
|
|
836
1291
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: segments.map((segment, i) => {
|
|
837
1292
|
if (segment.type === "html") {
|
|
838
|
-
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;
|
|
839
1302
|
}
|
|
840
|
-
const ext = extensions.find(
|
|
841
|
-
(e) => e.tag.toLowerCase() === segment.tag
|
|
842
|
-
);
|
|
1303
|
+
const ext = extensions.find((e) => e.tag.toLowerCase() === segment.tag);
|
|
843
1304
|
if (!ext) return null;
|
|
844
1305
|
const Component = ext.component;
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
|
|
1306
|
+
const Wrapper = ext.block !== false ? "div" : "span";
|
|
1307
|
+
const children = ext.markdown ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1308
|
+
ContentBody,
|
|
1309
|
+
{
|
|
1310
|
+
source: segment.content,
|
|
1311
|
+
format: "html",
|
|
1312
|
+
extensions,
|
|
1313
|
+
unsafe,
|
|
1314
|
+
depth: depth + 1,
|
|
1315
|
+
tail: i === last
|
|
1316
|
+
}
|
|
1317
|
+
) : void 0;
|
|
1318
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { "data-render-extension": segment.tag, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { content: segment.content, attributes: segment.attributes, children }) }, i);
|
|
848
1319
|
}) });
|
|
849
1320
|
}
|
|
850
1321
|
RenderedContent.displayName = "RenderedContent";
|
|
1322
|
+
ContentBody.displayName = "ContentBody";
|
|
851
1323
|
function ContentRenderer({
|
|
852
1324
|
value,
|
|
853
1325
|
format = "auto",
|
|
854
1326
|
lineSpacing = 1.6,
|
|
855
1327
|
className,
|
|
856
1328
|
extensions: instanceExtensions,
|
|
857
|
-
unsafe = false
|
|
1329
|
+
unsafe = false,
|
|
1330
|
+
copyButtons = true,
|
|
1331
|
+
onCopy,
|
|
1332
|
+
clipboardWriter,
|
|
1333
|
+
streaming = false
|
|
858
1334
|
}) {
|
|
859
1335
|
const extensions = react.useMemo(
|
|
860
1336
|
() => mergeExtensions(instanceExtensions),
|
|
861
1337
|
[instanceExtensions]
|
|
862
1338
|
);
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
return
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1339
|
+
const safe = value ?? "";
|
|
1340
|
+
const resolvedFormat = format === "auto" ? detectFormat(safe) : format;
|
|
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]);
|
|
1349
|
+
const codeOn = copyButtons === true || typeof copyButtons === "object" && copyButtons.code !== false;
|
|
1350
|
+
const tableOn = copyButtons === true || typeof copyButtons === "object" && copyButtons.table !== false;
|
|
1351
|
+
const copyOn = codeOn || tableOn;
|
|
1352
|
+
const rootRef = react.useRef(null);
|
|
1353
|
+
const [announcement, setAnnouncement] = react.useState("");
|
|
1354
|
+
const announce = react.useCallback((message) => {
|
|
1355
|
+
setAnnouncement("");
|
|
1356
|
+
queueMicrotask(() => setAnnouncement(message));
|
|
1357
|
+
}, []);
|
|
1358
|
+
const copy = useCopyPipeline({ writer: clipboardWriter, onCopy, rootRef, announce });
|
|
1359
|
+
const copyContext = react.useMemo(
|
|
1360
|
+
() => ({ code: codeOn, table: tableOn, copy, holdTailCode: openFence }),
|
|
1361
|
+
[codeOn, tableOn, copy, openFence]
|
|
1362
|
+
);
|
|
1363
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
871
1364
|
"div",
|
|
872
1365
|
{
|
|
1366
|
+
ref: rootRef,
|
|
873
1367
|
"data-react-fancy-content-renderer": "",
|
|
1368
|
+
"data-streaming": streaming ? "true" : void 0,
|
|
1369
|
+
"data-streaming-mode": progressive ? "progressive" : void 0,
|
|
874
1370
|
style: { lineHeight: lineSpacing },
|
|
875
1371
|
className: chunkZU5Q77OT_cjs.cn("text-sm", proseClasses, className),
|
|
876
|
-
children:
|
|
1372
|
+
children: [
|
|
1373
|
+
/* @__PURE__ */ jsxRuntime.jsx(CopyContext.Provider, { value: copyContext, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1374
|
+
ContentBody,
|
|
1375
|
+
{
|
|
1376
|
+
source,
|
|
1377
|
+
format: resolvedFormat,
|
|
1378
|
+
extensions,
|
|
1379
|
+
unsafe,
|
|
1380
|
+
tail: true
|
|
1381
|
+
}
|
|
1382
|
+
) }),
|
|
1383
|
+
copyOn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1384
|
+
"span",
|
|
1385
|
+
{
|
|
1386
|
+
role: "status",
|
|
1387
|
+
"aria-live": "polite",
|
|
1388
|
+
"aria-atomic": "true",
|
|
1389
|
+
"data-react-fancy-content-renderer-live": "",
|
|
1390
|
+
className: "sr-only",
|
|
1391
|
+
children: announcement
|
|
1392
|
+
}
|
|
1393
|
+
)
|
|
1394
|
+
]
|
|
877
1395
|
}
|
|
878
1396
|
);
|
|
879
1397
|
}
|
|
@@ -889,5 +1407,5 @@ exports.proseClasses = proseClasses;
|
|
|
889
1407
|
exports.registerExtension = registerExtension;
|
|
890
1408
|
exports.registerExtensions = registerExtensions;
|
|
891
1409
|
exports.renderMarkdown = renderMarkdown;
|
|
892
|
-
//# sourceMappingURL=chunk-
|
|
893
|
-
//# sourceMappingURL=chunk-
|
|
1410
|
+
//# sourceMappingURL=chunk-464QSZ4A.cjs.map
|
|
1411
|
+
//# sourceMappingURL=chunk-464QSZ4A.cjs.map
|