@oversk7/pi-web-dsh-style 1.3.2 → 1.3.4
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/README.md +10 -2
- package/THIRD_PARTY_NOTICES.md +32 -3
- package/package.json +4 -1
- package/server.ts +234 -21
- package/sse-channel.ts +37 -1
- package/web/app.css +9 -1
- package/web/app.js +96 -25
- package/web/assets/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/web/assets/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/web/assets/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/web/assets/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/web/assets/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/web/assets/fonts/KaTeX_Main-Bold.woff +0 -0
- package/web/assets/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/web/assets/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/web/assets/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/web/assets/fonts/KaTeX_Main-Italic.woff +0 -0
- package/web/assets/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Main-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/web/assets/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/web/assets/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/web/assets/fonts/KaTeX_Math-Italic.woff +0 -0
- package/web/assets/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Script-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/web/assets/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/web/assets/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/web/assets/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/web/assets/katex.min.css +1 -0
- package/web/assets/markdown.LICENSE.txt +78 -0
- package/web/assets/markdown.js +536 -21
- package/web/assets/mermaid/chunks/abnfDiagram-YUKMZFIV-FZYDJU67.js +1 -0
- package/web/assets/mermaid/chunks/architecture-WOLXFQ4H-BZVX64AA.js +1 -0
- package/web/assets/mermaid/chunks/architectureDiagram-47P4ROYG-2BMC2Z7S.js +36 -0
- package/web/assets/mermaid/chunks/blockDiagram-E7TT5TSB-FH6ZULKF.js +140 -0
- package/web/assets/mermaid/chunks/c4Diagram-CRA5TL53-OG62EAHM.js +38 -0
- package/web/assets/mermaid/chunks/chunk-22D53RF2.js +2 -0
- package/web/assets/mermaid/chunks/chunk-25KVF5ZE.js +1 -0
- package/web/assets/mermaid/chunks/chunk-4HYFSIZT.js +1 -0
- package/web/assets/mermaid/chunks/chunk-4W4OQ3QY.js +1 -0
- package/web/assets/mermaid/chunks/chunk-5TQRX2ND.js +1 -0
- package/web/assets/mermaid/chunks/chunk-64OFLKGX.js +322 -0
- package/web/assets/mermaid/chunks/chunk-64OFLKGX.js.LEGAL.txt +17 -0
- package/web/assets/mermaid/chunks/chunk-6UFWKTSA.js +1 -0
- package/web/assets/mermaid/chunks/chunk-6ZAAIG6Q.js +10 -0
- package/web/assets/mermaid/chunks/chunk-76QEHT4T.js +1 -0
- package/web/assets/mermaid/chunks/chunk-A7665WVC.js +1 -0
- package/web/assets/mermaid/chunks/chunk-AECJS4LP.js +130 -0
- package/web/assets/mermaid/chunks/chunk-AECJS4LP.js.LEGAL.txt +5 -0
- package/web/assets/mermaid/chunks/chunk-ATQQWCXO.js +2 -0
- package/web/assets/mermaid/chunks/chunk-DQVO43GU.js +1 -0
- package/web/assets/mermaid/chunks/chunk-DSKRGV6V.js +1 -0
- package/web/assets/mermaid/chunks/chunk-EIBFAW37.js +1 -0
- package/web/assets/mermaid/chunks/chunk-FBC3PYSO.js +1 -0
- package/web/assets/mermaid/chunks/chunk-FQ5IIL54.js +88 -0
- package/web/assets/mermaid/chunks/chunk-GZYY6JMP.js +1 -0
- package/web/assets/mermaid/chunks/chunk-HIGPS4L2.js +1 -0
- package/web/assets/mermaid/chunks/chunk-HP75Y56I.js +2 -0
- package/web/assets/mermaid/chunks/chunk-JRAHLUQM.js +32 -0
- package/web/assets/mermaid/chunks/chunk-KNYDPLLK.js +1 -0
- package/web/assets/mermaid/chunks/chunk-L465623K.js +136 -0
- package/web/assets/mermaid/chunks/chunk-L7BMR7WL.js +1 -0
- package/web/assets/mermaid/chunks/chunk-LLGPMHB6.js +1 -0
- package/web/assets/mermaid/chunks/chunk-NBH7YOBD.js +15 -0
- package/web/assets/mermaid/chunks/chunk-NCOVM3YF.js +1 -0
- package/web/assets/mermaid/chunks/chunk-PHKIAKHS.js +1 -0
- package/web/assets/mermaid/chunks/chunk-PM2IFX5O.js +1 -0
- package/web/assets/mermaid/chunks/chunk-PN62QGJW.js +72 -0
- package/web/assets/mermaid/chunks/chunk-QHMFFOZZ.js +1 -0
- package/web/assets/mermaid/chunks/chunk-QQZBUVKZ.js +1 -0
- package/web/assets/mermaid/chunks/chunk-RV7WQMNL.js +1 -0
- package/web/assets/mermaid/chunks/chunk-STBOPOHX.js +1 -0
- package/web/assets/mermaid/chunks/chunk-T3FZWEYV.js +1 -0
- package/web/assets/mermaid/chunks/chunk-TMMWB6NQ.js +1 -0
- package/web/assets/mermaid/chunks/chunk-TPW5CADB.js +1 -0
- package/web/assets/mermaid/chunks/chunk-TSOLLCID.js +2 -0
- package/web/assets/mermaid/chunks/chunk-TWGPYWIY.js +1 -0
- package/web/assets/mermaid/chunks/chunk-WGMEMLED.js +1 -0
- package/web/assets/mermaid/chunks/chunk-XC2LKMMW.js +1 -0
- package/web/assets/mermaid/chunks/chunk-XHVVPFJ5.js +1 -0
- package/web/assets/mermaid/chunks/chunk-YIN5CSIF.js +2 -0
- package/web/assets/mermaid/chunks/chunk-YIN5CSIF.js.LEGAL.txt +12 -0
- package/web/assets/mermaid/chunks/chunk-YORIST7Z.js +1 -0
- package/web/assets/mermaid/chunks/chunk-YPXVLPXW.js +3 -0
- package/web/assets/mermaid/chunks/chunk-Z4WODCD2.js +213 -0
- package/web/assets/mermaid/chunks/chunk-ZAERIUBF.js +1 -0
- package/web/assets/mermaid/chunks/chunk-ZDOBOOWG.js +62 -0
- package/web/assets/mermaid/chunks/classDiagram-v2-K4WV4PDN-HXQD675C.js +217 -0
- package/web/assets/mermaid/chunks/cose-bilkent-JH36ORCC-3VHNHM7I.js +1 -0
- package/web/assets/mermaid/chunks/cynefin-EF2NZ3EQ-ZO5PPXJG.js +1 -0
- package/web/assets/mermaid/chunks/cynefinDiagram-3GCD6N5R-LFWDWE4E.js +62 -0
- package/web/assets/mermaid/chunks/dagre-W4DXFKR2-SDBBVBGG.js +4 -0
- package/web/assets/mermaid/chunks/diagram-2UJZ2QOL-YCKW3WNJ.js +200 -0
- package/web/assets/mermaid/chunks/diagram-OVF4WLC6-62NNPE34.js +24 -0
- package/web/assets/mermaid/chunks/diagram-PFPMY2P6-7RR2GTZ5.js +30 -0
- package/web/assets/mermaid/chunks/diagram-UMYRVEAY-Q4I25SMR.js +24 -0
- package/web/assets/mermaid/chunks/diagram-YEKJPTXX-VNUTOPRK.js +41 -0
- package/web/assets/mermaid/chunks/diagram-ZIFT7M5P-KALXX5CE.js +3 -0
- package/web/assets/mermaid/chunks/ebnfDiagram-VR2GEFS7-WIFBISHL.js +1 -0
- package/web/assets/mermaid/chunks/elk-IJKZMXRS-LGG232IL.js +27 -0
- package/web/assets/mermaid/chunks/erDiagram-O2IAPWRE-KWX2J2TX.js +99 -0
- package/web/assets/mermaid/chunks/eventmodeling-K75KTNOO-3JNV4VSP.js +1 -0
- package/web/assets/mermaid/chunks/flowDiagram-OXPTDLAJ-CUMOBDC3.js +1 -0
- package/web/assets/mermaid/chunks/ganttDiagram-R7TSEDQI-GCS3MRFX.js +292 -0
- package/web/assets/mermaid/chunks/gitGraph-VSP46ZUC-HSAXKLAG.js +1 -0
- package/web/assets/mermaid/chunks/gitGraphDiagram-XJZIOB7I-FB763ROZ.js +106 -0
- package/web/assets/mermaid/chunks/info-OHQRW6UA-C4R2B34T.js +1 -0
- package/web/assets/mermaid/chunks/infoDiagram-5W2HQ5XZ-XCQU5T5D.js +2 -0
- package/web/assets/mermaid/chunks/ishikawaDiagram-K3B6WC7H-UWYT66G7.js +70 -0
- package/web/assets/mermaid/chunks/journeyDiagram-COXZFDF6-DOW66RYW.js +139 -0
- package/web/assets/mermaid/chunks/kanban-definition-P3RFRI5V-K55BXDLO.js +89 -0
- package/web/assets/mermaid/chunks/katex-5AUD5AJN.js +257 -0
- package/web/assets/mermaid/chunks/mindmap-definition-LZFPQGKD-WG3FIZ5U.js +96 -0
- package/web/assets/mermaid/chunks/packet-JDAUHWVQ-SFUSDIUN.js +1 -0
- package/web/assets/mermaid/chunks/pegDiagram-BGZESJAR-MQOH3WII.js +1 -0
- package/web/assets/mermaid/chunks/pie-XZMESJXO-M76QC4CD.js +1 -0
- package/web/assets/mermaid/chunks/pieDiagram-CAPJLFHJ-V3WNR4Y2.js +39 -0
- package/web/assets/mermaid/chunks/quadrantDiagram-GDMTTRAM-DJLTATHS.js +7 -0
- package/web/assets/mermaid/chunks/radar-ABXABTNO-HSBJ6SSD.js +1 -0
- package/web/assets/mermaid/chunks/railroad-I3PHUGI6-4QJMJ7LG.js +1 -0
- package/web/assets/mermaid/chunks/railroad-abnf-LNEFI6M7-ZLNZ5ZRH.js +1 -0
- package/web/assets/mermaid/chunks/railroad-ebnf-DWYD2UWJ-EBV6ZOUW.js +1 -0
- package/web/assets/mermaid/chunks/railroad-peg-JR7BVNR7-X3E47TO7.js +1 -0
- package/web/assets/mermaid/chunks/railroadDiagram-SM67HX2B-ZHEERYJS.js +1 -0
- package/web/assets/mermaid/chunks/requirementDiagram-X7JNWC4A-TVHJNPPI.js +84 -0
- package/web/assets/mermaid/chunks/sankeyDiagram-UM26HJRW-FXHWY3MM.js +40 -0
- package/web/assets/mermaid/chunks/sequenceDiagram-ZO4K6R2Y-OP7DCVPK.js +169 -0
- package/web/assets/mermaid/chunks/sizeCapture-INFHLROL-2GUJIKNX.js +1 -0
- package/web/assets/mermaid/chunks/stateDiagram-v2-TBUQTH76-R26D6XHK.js +291 -0
- package/web/assets/mermaid/chunks/swimlanes-N4OXWK64-FPGACWHE.js +1 -0
- package/web/assets/mermaid/chunks/swimlanesDiagram-K3J5GTZL-YMNX34Q2.js +8 -0
- package/web/assets/mermaid/chunks/timeline-definition-YOQAKGHF-R2U2UR4K.js +120 -0
- package/web/assets/mermaid/chunks/treeView-D4JQ5SDB-3BQHZUCI.js +1 -0
- package/web/assets/mermaid/chunks/treemap-SAJKECNS-6NJPJ5NM.js +1 -0
- package/web/assets/mermaid/chunks/usecaseDiagram-VIAY4XPW-BCEIGXDX.js +370 -0
- package/web/assets/mermaid/chunks/vennDiagram-BLWOH2XV-DOV5W2IV.js +34 -0
- package/web/assets/mermaid/chunks/wardley-7MLQ67FV-ITOM4KQ2.js +1 -0
- package/web/assets/mermaid/chunks/wardleyDiagram-YMQ3BMBF-CGJ7AG4L.js +78 -0
- package/web/assets/mermaid/chunks/xychartDiagram-TAQBALBS-2MTP7IBV.js +7 -0
- package/web/assets/mermaid/mermaid.js +11 -0
- package/web/assets/mermaid/mermaid.js.LEGAL.txt +7 -0
- package/web/index.html +1 -0
- package/web/markdown-diagrams.js +147 -0
- package/web/markdown.ts +41 -2
- package/web/session-sync.d.ts +12 -0
- package/web/session-sync.js +105 -0
- package/web/slash-commands.js +1 -0
- package/web/themes.css +46 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
let renderer;
|
|
2
|
+
let configuredTheme;
|
|
3
|
+
let sequence = 0;
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
const rendered = new WeakMap();
|
|
6
|
+
|
|
7
|
+
function diagramFragment(template) {
|
|
8
|
+
const content = template.content.cloneNode(true);
|
|
9
|
+
const prefix = `pi-diagram-${++sequence}`;
|
|
10
|
+
const ids = new Map();
|
|
11
|
+
for (const element of content.querySelectorAll("[id]")) {
|
|
12
|
+
const original = element.id;
|
|
13
|
+
const unique = `${prefix}-${ids.size}`;
|
|
14
|
+
ids.set(original, unique);
|
|
15
|
+
element.id = unique;
|
|
16
|
+
}
|
|
17
|
+
const replaceUrls = value => value.replace(/url\(\s*(["']?)#([^\s)"']+)\1\s*\)/g,
|
|
18
|
+
(match, quote, id) => ids.has(id) ? `url(${quote}#${ids.get(id)}${quote})` : match);
|
|
19
|
+
for (const element of content.querySelectorAll("*")) {
|
|
20
|
+
for (const attribute of [...element.attributes]) {
|
|
21
|
+
let value = replaceUrls(attribute.value);
|
|
22
|
+
if (["href", "xlink:href"].includes(attribute.name) && value.startsWith("#") && ids.has(value.slice(1))) {
|
|
23
|
+
value = `#${ids.get(value.slice(1))}`;
|
|
24
|
+
}
|
|
25
|
+
if (["aria-labelledby", "aria-describedby"].includes(attribute.name)) {
|
|
26
|
+
value = value.split(/\s+/).map(id => ids.get(id) || id).join(" ");
|
|
27
|
+
}
|
|
28
|
+
if (value !== attribute.value) element.setAttribute(attribute.name, value);
|
|
29
|
+
}
|
|
30
|
+
if (element.localName === "style") {
|
|
31
|
+
element.textContent = replaceUrls(element.textContent).replace(/#([\w:.-]+)/g,
|
|
32
|
+
(match, id) => ids.has(id) ? `#${ids.get(id)}` : match);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return content;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function observeMarkdownDiagrams(root = document.body) {
|
|
39
|
+
let frame;
|
|
40
|
+
let running = false;
|
|
41
|
+
let stopped = false;
|
|
42
|
+
const pending = new Set();
|
|
43
|
+
const selector = '.md-mermaid[data-mermaid-complete="true"]';
|
|
44
|
+
|
|
45
|
+
function collect(node) {
|
|
46
|
+
if (node.nodeType !== 1) return;
|
|
47
|
+
if (node.matches(selector)) pending.add(node);
|
|
48
|
+
for (const diagram of node.querySelectorAll(selector)) pending.add(diagram);
|
|
49
|
+
if (pending.size) schedule();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function update() {
|
|
53
|
+
frame = undefined;
|
|
54
|
+
if (running || stopped) return;
|
|
55
|
+
running = true;
|
|
56
|
+
try {
|
|
57
|
+
for (const node of [...pending]) {
|
|
58
|
+
pending.delete(node);
|
|
59
|
+
const dark = document.body.hasAttribute("data-ds-dark-theme");
|
|
60
|
+
const source = node.dataset.mermaidSource || "";
|
|
61
|
+
const key = `${dark}:${source}`;
|
|
62
|
+
if (rendered.get(node) === key || !node.isConnected) continue;
|
|
63
|
+
rendered.set(node, key);
|
|
64
|
+
if (source.length > 32 * 1024) continue;
|
|
65
|
+
try {
|
|
66
|
+
let result = cache.get(key);
|
|
67
|
+
if (!result) {
|
|
68
|
+
renderer ||= import("./assets/mermaid/mermaid.js").then(module => module.default).catch(error => {
|
|
69
|
+
renderer = undefined;
|
|
70
|
+
throw error;
|
|
71
|
+
});
|
|
72
|
+
const mermaid = await renderer;
|
|
73
|
+
if (!node.isConnected || stopped) continue;
|
|
74
|
+
if (dark !== document.body.hasAttribute("data-ds-dark-theme")) {
|
|
75
|
+
rendered.delete(node);
|
|
76
|
+
pending.add(node);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (configuredTheme !== dark) mermaid.initialize({
|
|
80
|
+
startOnLoad: false,
|
|
81
|
+
securityLevel: "strict",
|
|
82
|
+
theme: dark ? "dark" : "default",
|
|
83
|
+
layout: "dagre",
|
|
84
|
+
suppressErrorRendering: true,
|
|
85
|
+
maxTextSize: 32 * 1024,
|
|
86
|
+
flowchart: { htmlLabels: false },
|
|
87
|
+
});
|
|
88
|
+
configuredTheme = dark;
|
|
89
|
+
const id = `pi-mermaid-${++sequence}`;
|
|
90
|
+
let svg;
|
|
91
|
+
try {
|
|
92
|
+
({ svg } = await mermaid.render(id, source));
|
|
93
|
+
} catch {
|
|
94
|
+
result = { error: true };
|
|
95
|
+
}
|
|
96
|
+
if (!result) {
|
|
97
|
+
const template = document.createElement("template");
|
|
98
|
+
template.innerHTML = svg;
|
|
99
|
+
result = { template };
|
|
100
|
+
}
|
|
101
|
+
cache.set(key, result);
|
|
102
|
+
if (cache.size > 64) cache.delete(cache.keys().next().value);
|
|
103
|
+
}
|
|
104
|
+
if (!node.isConnected || stopped) continue;
|
|
105
|
+
if (dark !== document.body.hasAttribute("data-ds-dark-theme")) {
|
|
106
|
+
rendered.delete(node);
|
|
107
|
+
pending.add(node);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (result.error) {
|
|
111
|
+
node.dataset.mermaidState = "error";
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
node.replaceChildren(diagramFragment(result.template));
|
|
115
|
+
node.dataset.mermaidState = "rendered";
|
|
116
|
+
node.setAttribute("role", "img");
|
|
117
|
+
node.setAttribute("aria-label", "Mermaid diagram");
|
|
118
|
+
} catch {
|
|
119
|
+
// Keep the readable source when a diagram is invalid or still incomplete.
|
|
120
|
+
node.dataset.mermaidState = "error";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} finally {
|
|
124
|
+
running = false;
|
|
125
|
+
if (pending.size) schedule();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function schedule() {
|
|
130
|
+
if (!stopped && frame === undefined) frame = requestAnimationFrame(update);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const observer = new MutationObserver(records => {
|
|
134
|
+
for (const record of records) {
|
|
135
|
+
if (record.type === "attributes") collect(root);
|
|
136
|
+
else for (const node of record.addedNodes) collect(node);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
observer.observe(root, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-ds-dark-theme"] });
|
|
140
|
+
collect(root);
|
|
141
|
+
return () => {
|
|
142
|
+
stopped = true;
|
|
143
|
+
observer.disconnect();
|
|
144
|
+
if (frame !== undefined) cancelAnimationFrame(frame);
|
|
145
|
+
pending.clear();
|
|
146
|
+
};
|
|
147
|
+
}
|
package/web/markdown.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import MarkdownIt from "markdown-it";
|
|
2
|
+
import katex from "katex";
|
|
3
|
+
import texmath from "markdown-it-texmath";
|
|
2
4
|
import hljs from "highlight.js/lib/common";
|
|
3
5
|
import dockerfile from "highlight.js/lib/languages/dockerfile";
|
|
4
6
|
import powershell from "highlight.js/lib/languages/powershell";
|
|
@@ -35,7 +37,38 @@ function localFileLink(target: string, workspace = false) {
|
|
|
35
37
|
return { path: value, line, href };
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
const formulaCache = new Map<string, string>();
|
|
41
|
+
let formulaCacheBytes = 0;
|
|
42
|
+
|
|
43
|
+
function renderFormula(source: string, options: katex.KatexOptions) {
|
|
44
|
+
if (source.length > 16 * 1024) throw new Error("Formula exceeds rendering limit");
|
|
45
|
+
const key = `${options.displayMode ? "display" : "inline"}:${source}`;
|
|
46
|
+
const cached = formulaCache.get(key);
|
|
47
|
+
if (cached !== undefined) {
|
|
48
|
+
formulaCache.delete(key);
|
|
49
|
+
formulaCache.set(key, cached);
|
|
50
|
+
return cached;
|
|
51
|
+
}
|
|
52
|
+
const html = katex.renderToString(source, options);
|
|
53
|
+
const bytes = (key.length + html.length) * 2;
|
|
54
|
+
if (bytes <= 1024 * 1024) {
|
|
55
|
+
formulaCache.set(key, html);
|
|
56
|
+
formulaCacheBytes += bytes;
|
|
57
|
+
while (formulaCache.size > 128 || formulaCacheBytes > 1024 * 1024) {
|
|
58
|
+
const oldest = formulaCache.keys().next().value!;
|
|
59
|
+
formulaCacheBytes -= (oldest.length + formulaCache.get(oldest)!.length) * 2;
|
|
60
|
+
formulaCache.delete(oldest);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return html;
|
|
64
|
+
}
|
|
65
|
+
|
|
38
66
|
const parser = new MarkdownIt({ html: false, breaks: false, linkify: true });
|
|
67
|
+
parser.use(texmath, {
|
|
68
|
+
engine: { renderToString: renderFormula },
|
|
69
|
+
delimiters: ["dollars", "brackets"],
|
|
70
|
+
katexOptions: { trust: false, strict: "ignore", throwOnError: false, maxExpand: 1000, maxSize: 20 },
|
|
71
|
+
});
|
|
39
72
|
const InlineState = parser.inline.State;
|
|
40
73
|
parser.inline.State = class extends InlineState {
|
|
41
74
|
scanDelims(start: number, canSplitWord: boolean) {
|
|
@@ -91,9 +124,14 @@ parser.renderer.rules.image = (tokens, index, options, env, renderer) => {
|
|
|
91
124
|
return renderImage(tokens, index, options, env, renderer);
|
|
92
125
|
};
|
|
93
126
|
|
|
94
|
-
parser.renderer.rules.fence = (tokens, index) => {
|
|
127
|
+
parser.renderer.rules.fence = (tokens, index, _options, env) => {
|
|
95
128
|
const token = tokens[index];
|
|
96
129
|
const language = token.info.trim().split(/\s+/)[0];
|
|
130
|
+
if (language.toLowerCase() === "mermaid") {
|
|
131
|
+
const lastLine = (env?.markdownLines as string[] | undefined)?.[token.map![1] - 1] || "";
|
|
132
|
+
const closed = new RegExp(`^\\s*(?:>\\s*)*[${token.markup[0]}]{${token.markup.length},}\\s*$`).test(lastLine);
|
|
133
|
+
return `<div class="md-mermaid" data-mermaid-source="${escape(token.content)}" data-mermaid-complete="${closed}"><pre class="md-code-block"><code>${escape(token.content)}</code></pre></div>\n`;
|
|
134
|
+
}
|
|
97
135
|
let code = escape(token.content);
|
|
98
136
|
// Bound synchronous highlighting work while streaming large tool-generated snippets.
|
|
99
137
|
if (language && hljs.getLanguage(language) && token.content.length <= 32 * 1024) {
|
|
@@ -129,5 +167,6 @@ parser.renderer.rules.task_checkbox = (tokens, index) => {
|
|
|
129
167
|
};
|
|
130
168
|
|
|
131
169
|
export function markdown(src: unknown, context: { cwd?: string } = {}): string {
|
|
132
|
-
|
|
170
|
+
const text = String(src ?? "");
|
|
171
|
+
return parser.render(text, { ...context, markdownLines: text.replace(/\r\n?/g, "\n").split("\n") });
|
|
133
172
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
|
|
2
|
+
export type SessionPatch =
|
|
3
|
+
| { op: "set"; path: (string | number)[]; value: JsonValue }
|
|
4
|
+
| { op: "delete"; path: (string | number)[] }
|
|
5
|
+
| { op: "append"; path: (string | number)[]; value: string }
|
|
6
|
+
| { op: "truncate"; path: (string | number)[]; length: number };
|
|
7
|
+
/** Detached JSON normalization: undefined object fields disappear; array holes become null. */
|
|
8
|
+
export function canonicalizeJson(value: unknown): JsonValue | undefined;
|
|
9
|
+
/** Compare canonical JSON, emitting string suffixes and changes to individual paths. */
|
|
10
|
+
export function diffSessionSnapshots(previous: JsonValue, next: JsonValue): SessionPatch[];
|
|
11
|
+
/** Immutable application with structural sharing; invalid or unsafe paths throw. */
|
|
12
|
+
export function applySessionPatches<T extends JsonValue>(snapshot: T, patches: readonly SessionPatch[]): T;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Paths contain object keys or array indices. Operations are set, delete (object
|
|
2
|
+
// field), append (string suffix), and truncate (array length). A root set resets
|
|
3
|
+
// structure. Paths traverse only own properties, including ordinary JSON keys
|
|
4
|
+
// such as __proto__; assignments cannot invoke prototype setters.
|
|
5
|
+
const own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
6
|
+
function setOwn(value, key, item) {
|
|
7
|
+
Object.defineProperty(value, key, { value: item, enumerable: true, writable: true, configurable: true });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function canonicalizeJson(value) {
|
|
11
|
+
const ancestors = new Set();
|
|
12
|
+
function visit(input, key = "") {
|
|
13
|
+
if (input && typeof input.toJSON === "function") input = input.toJSON(key);
|
|
14
|
+
if (input === null || typeof input === "string" || typeof input === "boolean") return input;
|
|
15
|
+
if (typeof input === "number") return Number.isFinite(input) ? input : null;
|
|
16
|
+
if (typeof input === "bigint") throw new TypeError("BigInt is not JSON data");
|
|
17
|
+
if (typeof input !== "object") return undefined;
|
|
18
|
+
if (ancestors.has(input)) throw new TypeError("Circular JSON data");
|
|
19
|
+
ancestors.add(input);
|
|
20
|
+
let result;
|
|
21
|
+
if (Array.isArray(input)) {
|
|
22
|
+
result = Array.from({ length: input.length }, (_, i) => visit(input[i], String(i)) ?? null);
|
|
23
|
+
} else {
|
|
24
|
+
result = {};
|
|
25
|
+
for (const key of Object.keys(input)) {
|
|
26
|
+
const item = visit(input[key], key);
|
|
27
|
+
if (item !== undefined) setOwn(result, key, item);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
ancestors.delete(input);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
return visit(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Compare detached canonical JSON without serializing unchanged history strings.
|
|
37
|
+
export function diffSessionSnapshots(previous, next) {
|
|
38
|
+
const patches = [];
|
|
39
|
+
function walk(before, after, path) {
|
|
40
|
+
if (before === after) return;
|
|
41
|
+
if (typeof before === "string" && typeof after === "string" && after.startsWith(before)) {
|
|
42
|
+
patches.push({ op: "append", path, value: after.slice(before.length) });
|
|
43
|
+
} else if (Array.isArray(before) && Array.isArray(after)) {
|
|
44
|
+
const common = Math.min(before.length, after.length);
|
|
45
|
+
for (let i = 0; i < common; i++) walk(before[i], after[i], [...path, i]);
|
|
46
|
+
for (let i = common; i < after.length; i++) patches.push({ op: "set", path: [...path, i], value: after[i] });
|
|
47
|
+
if (after.length < before.length) patches.push({ op: "truncate", path, length: after.length });
|
|
48
|
+
} else if (before && after && typeof before === "object" && typeof after === "object" && !Array.isArray(before) && !Array.isArray(after)) {
|
|
49
|
+
for (const key of Object.keys(before)) {
|
|
50
|
+
if (!own(after, key)) patches.push({ op: "delete", path: [...path, key] });
|
|
51
|
+
}
|
|
52
|
+
for (const key of Object.keys(after)) {
|
|
53
|
+
if (own(before, key)) walk(before[key], after[key], [...path, key]);
|
|
54
|
+
else patches.push({ op: "set", path: [...path, key], value: after[key] });
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
patches.push({ op: "set", path, value: after });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
walk(previous, next, []);
|
|
61
|
+
return patches;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Rebuild only modified paths; the input snapshot and unchanged branches retain
|
|
65
|
+
// their identity. Invalid operations throw before any input can be mutated.
|
|
66
|
+
export function applySessionPatches(snapshot, patches) {
|
|
67
|
+
if (!Array.isArray(patches)) throw new TypeError("Expected patches array");
|
|
68
|
+
let result = snapshot;
|
|
69
|
+
for (const patch of patches) {
|
|
70
|
+
if (!patch || !["set", "delete", "append", "truncate"].includes(patch.op) || !Array.isArray(patch.path)) throw new TypeError("Invalid patch");
|
|
71
|
+
for (const key of patch.path) {
|
|
72
|
+
if (typeof key !== "string" && (!Number.isSafeInteger(key) || key < 0)) throw new TypeError("Invalid patch path");
|
|
73
|
+
}
|
|
74
|
+
function update(current, depth) {
|
|
75
|
+
if (depth === patch.path.length) {
|
|
76
|
+
if (patch.op === "set") {
|
|
77
|
+
if (!own(patch, "value")) throw new TypeError("Missing patch value");
|
|
78
|
+
const value = canonicalizeJson(patch.value);
|
|
79
|
+
if (value === undefined) throw new TypeError("Invalid patch value");
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
if (patch.op === "append" && typeof current === "string" && typeof patch.value === "string") return current + patch.value;
|
|
83
|
+
if (patch.op === "truncate" && Array.isArray(current) && Number.isSafeInteger(patch.length) && patch.length >= 0 && patch.length <= current.length) return current.slice(0, patch.length);
|
|
84
|
+
throw new TypeError("Invalid patch target");
|
|
85
|
+
}
|
|
86
|
+
const key = patch.path[depth];
|
|
87
|
+
if (!current || typeof current !== "object") throw new TypeError("Invalid patch parent");
|
|
88
|
+
const array = Array.isArray(current);
|
|
89
|
+
if (array ? typeof key !== "number" || key > current.length : typeof key !== "string") throw new TypeError("Invalid patch key");
|
|
90
|
+
const leaf = depth === patch.path.length - 1;
|
|
91
|
+
const exists = own(current, key);
|
|
92
|
+
if (!exists && !(leaf && patch.op === "set")) throw new TypeError("Missing patch path");
|
|
93
|
+
const copy = array ? current.slice() : { ...current };
|
|
94
|
+
if (leaf && patch.op === "delete") {
|
|
95
|
+
if (array) throw new TypeError("Use truncate for arrays");
|
|
96
|
+
delete copy[key];
|
|
97
|
+
} else {
|
|
98
|
+
setOwn(copy, key, update(exists ? current[key] : undefined, depth + 1));
|
|
99
|
+
}
|
|
100
|
+
return copy;
|
|
101
|
+
}
|
|
102
|
+
result = update(result, 0);
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
package/web/slash-commands.js
CHANGED
|
@@ -24,6 +24,7 @@ export const unsupportedSlashCommands = {
|
|
|
24
24
|
trust: "项目信任设置需要在 Pi 终端中通过 /trust 管理。",
|
|
25
25
|
changelog: "Web 暂不展示 Pi 更新日志,请在 Pi 终端执行 /changelog。",
|
|
26
26
|
hotkeys: "终端快捷键不适用于网页。网页输入框支持 Enter 发送、Shift+Enter 换行,命令菜单支持方向键选择和 Tab 补全。",
|
|
27
|
+
bug: "Web 暂不支持提交问题报告,请在 Pi 终端执行 /bug。",
|
|
27
28
|
debug: "此调试命令仅支持 Pi 终端。",
|
|
28
29
|
arminsayshi: "此彩蛋命令仅支持 Pi 终端。",
|
|
29
30
|
dementedelves: "此彩蛋命令仅支持 Pi 终端。",
|
package/web/themes.css
CHANGED
|
@@ -156,3 +156,49 @@ body:is([data-theme-style=sakura],[data-theme-style=starlight],[data-custom-back
|
|
|
156
156
|
body:is([data-theme-style=sakura],[data-theme-style=starlight],[data-custom-background]) [data-chat-flow-kind=assistant-text]{padding:14px;border-radius:16px}
|
|
157
157
|
body:is([data-theme-style=sakura],[data-theme-style=starlight],[data-custom-background]) .pXSMma_headline{padding:10px 12px;margin:0;box-sizing:border-box}
|
|
158
158
|
}
|
|
159
|
+
|
|
160
|
+
/* Claude Code UI + Tokyo Night chat colors apply only in dark mode. */
|
|
161
|
+
body[data-ds-dark-theme]{
|
|
162
|
+
--pi-chat-heading:#ff9e64;--pi-chat-code:#9ece6a;--pi-chat-link:#7dcfff;
|
|
163
|
+
--pi-chat-blue:#7aa2f7;--pi-chat-purple:#bb9af7;--pi-chat-orange:#ff9e64;
|
|
164
|
+
--pi-chat-green:#9ece6a;--pi-chat-red:#f7768e;--pi-chat-yellow:#e0af68;
|
|
165
|
+
--pi-chat-muted:#9aa5ce;--pi-chat-thinking:#a0a4b0;
|
|
166
|
+
--pi-chat-text:var(--dsw-alias-label-primary);
|
|
167
|
+
--pi-chat-strong:color-mix(in srgb,var(--pi-chat-heading) 28%,var(--dsw-alias-label-primary));
|
|
168
|
+
--pi-chat-border:color-mix(in srgb,var(--pi-chat-blue) 22%,var(--dsw-alias-border-l2));
|
|
169
|
+
--pi-chat-inline-bg:color-mix(in srgb,var(--pi-chat-code) 7%,var(--dsw-alias-markdown-inline-code));
|
|
170
|
+
--pi-chat-inline-border:color-mix(in srgb,var(--pi-chat-code) 20%,var(--dsw-alias-border-l2));
|
|
171
|
+
}
|
|
172
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5{color:var(--pi-chat-text)}
|
|
173
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(h1,h2,h3,h4,h5,h6){color:var(--pi-chat-heading)}
|
|
174
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 strong{color:var(--pi-chat-strong);font-weight:650}
|
|
175
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 em{color:var(--pi-chat-muted)}
|
|
176
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 a{color:var(--pi-chat-link);text-decoration-color:color-mix(in srgb,var(--pi-chat-link) 45%,transparent)}
|
|
177
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 a:is(:hover,:focus-visible){text-decoration-color:currentColor}
|
|
178
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 a:focus-visible{outline:2px solid var(--pi-chat-link);outline-offset:3px;border-radius:2px}
|
|
179
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :not(pre)>code{color:var(--pi-chat-code);background:var(--pi-chat-inline-bg);border-color:var(--pi-chat-inline-border)}
|
|
180
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(ul,ol)>li::marker{color:var(--pi-chat-link)}
|
|
181
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 ul>li:not(.md-task-item)::marker{content:"◉ ";font-size:.75em}
|
|
182
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 .md-task-item input{accent-color:var(--pi-chat-green)}
|
|
183
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 blockquote{color:var(--pi-chat-muted);border-left-color:var(--pi-chat-border)}
|
|
184
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 hr{background:var(--pi-chat-border)}
|
|
185
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.md-code-container,.md-code-block,.md-code-language){border-color:var(--pi-chat-border)}
|
|
186
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 .md-code-language{color:var(--pi-chat-muted);background:var(--dsw-alias-markdown-code-block-banner);font-size:11px;letter-spacing:.03em}
|
|
187
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 .md-code-block{color:var(--pi-chat-text)}
|
|
188
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 .md-table-wrap th{color:var(--pi-chat-heading)}
|
|
189
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-comment,.hljs-quote){color:var(--pi-chat-muted);font-style:italic}
|
|
190
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-operator){color:var(--pi-chat-purple)}
|
|
191
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-string,.hljs-regexp){color:var(--pi-chat-green)}
|
|
192
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-title,.hljs-section){color:var(--pi-chat-blue)}
|
|
193
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-number,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-attr){color:var(--pi-chat-orange)}
|
|
194
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-type,.hljs-built_in,.hljs-selector-class,.hljs-selector-id){color:var(--pi-chat-link)}
|
|
195
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 :is(.hljs-meta,.hljs-doctag,.hljs-attribute){color:var(--pi-chat-yellow)}
|
|
196
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 .hljs-addition{color:var(--pi-chat-green);background:color-mix(in srgb,var(--pi-chat-green) 10%,transparent)}
|
|
197
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] ._markdown_1nba0_5 .hljs-deletion{color:var(--pi-chat-red);background:color-mix(in srgb,var(--pi-chat-red) 10%,transparent)}
|
|
198
|
+
body[data-ds-dark-theme] [data-chat-flow-kind=assistant-step] :is(.QWLzlG_leading,.QWLzlG_title,.QWLzlG_summary){color:var(--pi-chat-thinking)}
|
|
199
|
+
body[data-ds-dark-theme] [data-chat-flow-kind=assistant-step] .Y0dWHa_thinkingQuote{border-left-color:var(--pi-chat-border)}
|
|
200
|
+
body[data-ds-dark-theme] [data-chat-flow-kind=assistant-step] .Y0dWHa_thinkingQuote .Y0dWHa_payload{color:var(--pi-chat-thinking);font-family:var(--dsw-font-family);line-height:1.7}
|
|
201
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] .CY-8Ka_root .CY-8Ka_title{color:var(--pi-chat-blue);font-weight:500}
|
|
202
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] .CY-8Ka_root[data-state=ok] .CY-8Ka_leading{color:var(--pi-chat-green)}
|
|
203
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] .CY-8Ka_root[data-state=running] :is(.CY-8Ka_leading,.CY-8Ka_title){color:var(--pi-chat-green)}
|
|
204
|
+
body[data-ds-dark-theme] [data-chat-flow-kind] .CY-8Ka_root[data-state=error] :is(.CY-8Ka_leading,.CY-8Ka_title,.CY-8Ka_errorSummary){color:var(--pi-chat-red)}
|