@oversk7/pi-web-dsh-style 1.3.1 → 1.3.3

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.
Files changed (184) hide show
  1. package/README.md +2 -2
  2. package/THIRD_PARTY_NOTICES.md +32 -3
  3. package/package.json +4 -1
  4. package/server.ts +194 -18
  5. package/web/app.css +9 -1
  6. package/web/app.js +16 -4
  7. package/web/assets/fonts/KaTeX_AMS-Regular.ttf +0 -0
  8. package/web/assets/fonts/KaTeX_AMS-Regular.woff +0 -0
  9. package/web/assets/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  10. package/web/assets/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  11. package/web/assets/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  12. package/web/assets/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  13. package/web/assets/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  14. package/web/assets/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  15. package/web/assets/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  16. package/web/assets/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  17. package/web/assets/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  18. package/web/assets/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  19. package/web/assets/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  20. package/web/assets/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  21. package/web/assets/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  22. package/web/assets/fonts/KaTeX_Main-Bold.ttf +0 -0
  23. package/web/assets/fonts/KaTeX_Main-Bold.woff +0 -0
  24. package/web/assets/fonts/KaTeX_Main-Bold.woff2 +0 -0
  25. package/web/assets/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  26. package/web/assets/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  27. package/web/assets/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  28. package/web/assets/fonts/KaTeX_Main-Italic.ttf +0 -0
  29. package/web/assets/fonts/KaTeX_Main-Italic.woff +0 -0
  30. package/web/assets/fonts/KaTeX_Main-Italic.woff2 +0 -0
  31. package/web/assets/fonts/KaTeX_Main-Regular.ttf +0 -0
  32. package/web/assets/fonts/KaTeX_Main-Regular.woff +0 -0
  33. package/web/assets/fonts/KaTeX_Main-Regular.woff2 +0 -0
  34. package/web/assets/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  35. package/web/assets/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  36. package/web/assets/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  37. package/web/assets/fonts/KaTeX_Math-Italic.ttf +0 -0
  38. package/web/assets/fonts/KaTeX_Math-Italic.woff +0 -0
  39. package/web/assets/fonts/KaTeX_Math-Italic.woff2 +0 -0
  40. package/web/assets/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  41. package/web/assets/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  42. package/web/assets/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  43. package/web/assets/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  44. package/web/assets/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  45. package/web/assets/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  46. package/web/assets/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  47. package/web/assets/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  48. package/web/assets/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  49. package/web/assets/fonts/KaTeX_Script-Regular.ttf +0 -0
  50. package/web/assets/fonts/KaTeX_Script-Regular.woff +0 -0
  51. package/web/assets/fonts/KaTeX_Script-Regular.woff2 +0 -0
  52. package/web/assets/fonts/KaTeX_Size1-Regular.ttf +0 -0
  53. package/web/assets/fonts/KaTeX_Size1-Regular.woff +0 -0
  54. package/web/assets/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  55. package/web/assets/fonts/KaTeX_Size2-Regular.ttf +0 -0
  56. package/web/assets/fonts/KaTeX_Size2-Regular.woff +0 -0
  57. package/web/assets/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  58. package/web/assets/fonts/KaTeX_Size3-Regular.ttf +0 -0
  59. package/web/assets/fonts/KaTeX_Size3-Regular.woff +0 -0
  60. package/web/assets/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  61. package/web/assets/fonts/KaTeX_Size4-Regular.ttf +0 -0
  62. package/web/assets/fonts/KaTeX_Size4-Regular.woff +0 -0
  63. package/web/assets/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  64. package/web/assets/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  65. package/web/assets/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  66. package/web/assets/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  67. package/web/assets/katex.min.css +1 -0
  68. package/web/assets/markdown.LICENSE.txt +78 -0
  69. package/web/assets/markdown.js +536 -21
  70. package/web/assets/mermaid/chunks/abnfDiagram-YUKMZFIV-FZYDJU67.js +1 -0
  71. package/web/assets/mermaid/chunks/architecture-WOLXFQ4H-BZVX64AA.js +1 -0
  72. package/web/assets/mermaid/chunks/architectureDiagram-47P4ROYG-2BMC2Z7S.js +36 -0
  73. package/web/assets/mermaid/chunks/blockDiagram-E7TT5TSB-FH6ZULKF.js +140 -0
  74. package/web/assets/mermaid/chunks/c4Diagram-CRA5TL53-OG62EAHM.js +38 -0
  75. package/web/assets/mermaid/chunks/chunk-22D53RF2.js +2 -0
  76. package/web/assets/mermaid/chunks/chunk-25KVF5ZE.js +1 -0
  77. package/web/assets/mermaid/chunks/chunk-4HYFSIZT.js +1 -0
  78. package/web/assets/mermaid/chunks/chunk-4W4OQ3QY.js +1 -0
  79. package/web/assets/mermaid/chunks/chunk-5TQRX2ND.js +1 -0
  80. package/web/assets/mermaid/chunks/chunk-64OFLKGX.js +322 -0
  81. package/web/assets/mermaid/chunks/chunk-64OFLKGX.js.LEGAL.txt +17 -0
  82. package/web/assets/mermaid/chunks/chunk-6UFWKTSA.js +1 -0
  83. package/web/assets/mermaid/chunks/chunk-6ZAAIG6Q.js +10 -0
  84. package/web/assets/mermaid/chunks/chunk-76QEHT4T.js +1 -0
  85. package/web/assets/mermaid/chunks/chunk-A7665WVC.js +1 -0
  86. package/web/assets/mermaid/chunks/chunk-AECJS4LP.js +130 -0
  87. package/web/assets/mermaid/chunks/chunk-AECJS4LP.js.LEGAL.txt +5 -0
  88. package/web/assets/mermaid/chunks/chunk-ATQQWCXO.js +2 -0
  89. package/web/assets/mermaid/chunks/chunk-DQVO43GU.js +1 -0
  90. package/web/assets/mermaid/chunks/chunk-DSKRGV6V.js +1 -0
  91. package/web/assets/mermaid/chunks/chunk-EIBFAW37.js +1 -0
  92. package/web/assets/mermaid/chunks/chunk-FBC3PYSO.js +1 -0
  93. package/web/assets/mermaid/chunks/chunk-FQ5IIL54.js +88 -0
  94. package/web/assets/mermaid/chunks/chunk-GZYY6JMP.js +1 -0
  95. package/web/assets/mermaid/chunks/chunk-HIGPS4L2.js +1 -0
  96. package/web/assets/mermaid/chunks/chunk-HP75Y56I.js +2 -0
  97. package/web/assets/mermaid/chunks/chunk-JRAHLUQM.js +32 -0
  98. package/web/assets/mermaid/chunks/chunk-KNYDPLLK.js +1 -0
  99. package/web/assets/mermaid/chunks/chunk-L465623K.js +136 -0
  100. package/web/assets/mermaid/chunks/chunk-L7BMR7WL.js +1 -0
  101. package/web/assets/mermaid/chunks/chunk-LLGPMHB6.js +1 -0
  102. package/web/assets/mermaid/chunks/chunk-NBH7YOBD.js +15 -0
  103. package/web/assets/mermaid/chunks/chunk-NCOVM3YF.js +1 -0
  104. package/web/assets/mermaid/chunks/chunk-PHKIAKHS.js +1 -0
  105. package/web/assets/mermaid/chunks/chunk-PM2IFX5O.js +1 -0
  106. package/web/assets/mermaid/chunks/chunk-PN62QGJW.js +72 -0
  107. package/web/assets/mermaid/chunks/chunk-QHMFFOZZ.js +1 -0
  108. package/web/assets/mermaid/chunks/chunk-QQZBUVKZ.js +1 -0
  109. package/web/assets/mermaid/chunks/chunk-RV7WQMNL.js +1 -0
  110. package/web/assets/mermaid/chunks/chunk-STBOPOHX.js +1 -0
  111. package/web/assets/mermaid/chunks/chunk-T3FZWEYV.js +1 -0
  112. package/web/assets/mermaid/chunks/chunk-TMMWB6NQ.js +1 -0
  113. package/web/assets/mermaid/chunks/chunk-TPW5CADB.js +1 -0
  114. package/web/assets/mermaid/chunks/chunk-TSOLLCID.js +2 -0
  115. package/web/assets/mermaid/chunks/chunk-TWGPYWIY.js +1 -0
  116. package/web/assets/mermaid/chunks/chunk-WGMEMLED.js +1 -0
  117. package/web/assets/mermaid/chunks/chunk-XC2LKMMW.js +1 -0
  118. package/web/assets/mermaid/chunks/chunk-XHVVPFJ5.js +1 -0
  119. package/web/assets/mermaid/chunks/chunk-YIN5CSIF.js +2 -0
  120. package/web/assets/mermaid/chunks/chunk-YIN5CSIF.js.LEGAL.txt +12 -0
  121. package/web/assets/mermaid/chunks/chunk-YORIST7Z.js +1 -0
  122. package/web/assets/mermaid/chunks/chunk-YPXVLPXW.js +3 -0
  123. package/web/assets/mermaid/chunks/chunk-Z4WODCD2.js +213 -0
  124. package/web/assets/mermaid/chunks/chunk-ZAERIUBF.js +1 -0
  125. package/web/assets/mermaid/chunks/chunk-ZDOBOOWG.js +62 -0
  126. package/web/assets/mermaid/chunks/classDiagram-v2-K4WV4PDN-HXQD675C.js +217 -0
  127. package/web/assets/mermaid/chunks/cose-bilkent-JH36ORCC-3VHNHM7I.js +1 -0
  128. package/web/assets/mermaid/chunks/cynefin-EF2NZ3EQ-ZO5PPXJG.js +1 -0
  129. package/web/assets/mermaid/chunks/cynefinDiagram-3GCD6N5R-LFWDWE4E.js +62 -0
  130. package/web/assets/mermaid/chunks/dagre-W4DXFKR2-SDBBVBGG.js +4 -0
  131. package/web/assets/mermaid/chunks/diagram-2UJZ2QOL-YCKW3WNJ.js +200 -0
  132. package/web/assets/mermaid/chunks/diagram-OVF4WLC6-62NNPE34.js +24 -0
  133. package/web/assets/mermaid/chunks/diagram-PFPMY2P6-7RR2GTZ5.js +30 -0
  134. package/web/assets/mermaid/chunks/diagram-UMYRVEAY-Q4I25SMR.js +24 -0
  135. package/web/assets/mermaid/chunks/diagram-YEKJPTXX-VNUTOPRK.js +41 -0
  136. package/web/assets/mermaid/chunks/diagram-ZIFT7M5P-KALXX5CE.js +3 -0
  137. package/web/assets/mermaid/chunks/ebnfDiagram-VR2GEFS7-WIFBISHL.js +1 -0
  138. package/web/assets/mermaid/chunks/elk-IJKZMXRS-LGG232IL.js +27 -0
  139. package/web/assets/mermaid/chunks/erDiagram-O2IAPWRE-KWX2J2TX.js +99 -0
  140. package/web/assets/mermaid/chunks/eventmodeling-K75KTNOO-3JNV4VSP.js +1 -0
  141. package/web/assets/mermaid/chunks/flowDiagram-OXPTDLAJ-CUMOBDC3.js +1 -0
  142. package/web/assets/mermaid/chunks/ganttDiagram-R7TSEDQI-GCS3MRFX.js +292 -0
  143. package/web/assets/mermaid/chunks/gitGraph-VSP46ZUC-HSAXKLAG.js +1 -0
  144. package/web/assets/mermaid/chunks/gitGraphDiagram-XJZIOB7I-FB763ROZ.js +106 -0
  145. package/web/assets/mermaid/chunks/info-OHQRW6UA-C4R2B34T.js +1 -0
  146. package/web/assets/mermaid/chunks/infoDiagram-5W2HQ5XZ-XCQU5T5D.js +2 -0
  147. package/web/assets/mermaid/chunks/ishikawaDiagram-K3B6WC7H-UWYT66G7.js +70 -0
  148. package/web/assets/mermaid/chunks/journeyDiagram-COXZFDF6-DOW66RYW.js +139 -0
  149. package/web/assets/mermaid/chunks/kanban-definition-P3RFRI5V-K55BXDLO.js +89 -0
  150. package/web/assets/mermaid/chunks/katex-5AUD5AJN.js +257 -0
  151. package/web/assets/mermaid/chunks/mindmap-definition-LZFPQGKD-WG3FIZ5U.js +96 -0
  152. package/web/assets/mermaid/chunks/packet-JDAUHWVQ-SFUSDIUN.js +1 -0
  153. package/web/assets/mermaid/chunks/pegDiagram-BGZESJAR-MQOH3WII.js +1 -0
  154. package/web/assets/mermaid/chunks/pie-XZMESJXO-M76QC4CD.js +1 -0
  155. package/web/assets/mermaid/chunks/pieDiagram-CAPJLFHJ-V3WNR4Y2.js +39 -0
  156. package/web/assets/mermaid/chunks/quadrantDiagram-GDMTTRAM-DJLTATHS.js +7 -0
  157. package/web/assets/mermaid/chunks/radar-ABXABTNO-HSBJ6SSD.js +1 -0
  158. package/web/assets/mermaid/chunks/railroad-I3PHUGI6-4QJMJ7LG.js +1 -0
  159. package/web/assets/mermaid/chunks/railroad-abnf-LNEFI6M7-ZLNZ5ZRH.js +1 -0
  160. package/web/assets/mermaid/chunks/railroad-ebnf-DWYD2UWJ-EBV6ZOUW.js +1 -0
  161. package/web/assets/mermaid/chunks/railroad-peg-JR7BVNR7-X3E47TO7.js +1 -0
  162. package/web/assets/mermaid/chunks/railroadDiagram-SM67HX2B-ZHEERYJS.js +1 -0
  163. package/web/assets/mermaid/chunks/requirementDiagram-X7JNWC4A-TVHJNPPI.js +84 -0
  164. package/web/assets/mermaid/chunks/sankeyDiagram-UM26HJRW-FXHWY3MM.js +40 -0
  165. package/web/assets/mermaid/chunks/sequenceDiagram-ZO4K6R2Y-OP7DCVPK.js +169 -0
  166. package/web/assets/mermaid/chunks/sizeCapture-INFHLROL-2GUJIKNX.js +1 -0
  167. package/web/assets/mermaid/chunks/stateDiagram-v2-TBUQTH76-R26D6XHK.js +291 -0
  168. package/web/assets/mermaid/chunks/swimlanes-N4OXWK64-FPGACWHE.js +1 -0
  169. package/web/assets/mermaid/chunks/swimlanesDiagram-K3J5GTZL-YMNX34Q2.js +8 -0
  170. package/web/assets/mermaid/chunks/timeline-definition-YOQAKGHF-R2U2UR4K.js +120 -0
  171. package/web/assets/mermaid/chunks/treeView-D4JQ5SDB-3BQHZUCI.js +1 -0
  172. package/web/assets/mermaid/chunks/treemap-SAJKECNS-6NJPJ5NM.js +1 -0
  173. package/web/assets/mermaid/chunks/usecaseDiagram-VIAY4XPW-BCEIGXDX.js +370 -0
  174. package/web/assets/mermaid/chunks/vennDiagram-BLWOH2XV-DOV5W2IV.js +34 -0
  175. package/web/assets/mermaid/chunks/wardley-7MLQ67FV-ITOM4KQ2.js +1 -0
  176. package/web/assets/mermaid/chunks/wardleyDiagram-YMQ3BMBF-CGJ7AG4L.js +78 -0
  177. package/web/assets/mermaid/chunks/xychartDiagram-TAQBALBS-2MTP7IBV.js +7 -0
  178. package/web/assets/mermaid/mermaid.js +11 -0
  179. package/web/assets/mermaid/mermaid.js.LEGAL.txt +7 -0
  180. package/web/index.html +1 -0
  181. package/web/markdown-diagrams.js +147 -0
  182. package/web/markdown.ts +50 -4
  183. package/web/slash-commands.js +1 -0
  184. 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) {
@@ -60,7 +93,14 @@ parser.validateLink = (target) => Boolean(localFileLink(target)) || validateLink
60
93
  parser.renderer.rules.link_open = (tokens, index, options, env, renderer) => {
61
94
  const token = tokens[index];
62
95
  const target = String(token.attrGet("href") || "");
63
- const local = localFileLink(target, true);
96
+ let local = localFileLink(target, true);
97
+ if (local && typeof env?.cwd === "string" && !/^[A-Za-z]:[\\/]|^\\\\/.test(local.path)) {
98
+ const base = new URL("file:///");
99
+ base.pathname = `${env.cwd.replace(/\\/g, "/").replace(/%/g, "%25").replace(/\/+$/, "")}/`;
100
+ const relative = local.path.replace(/\\/g, "/").split("/").map(encodeURIComponent).join("/");
101
+ const resolved = new URL(relative, base);
102
+ local = localFileLink(`${resolved.href}${local.line ? `#L${local.line}` : ""}`) || local;
103
+ }
64
104
  if (local) {
65
105
  token.attrSet("href", local.href);
66
106
  token.attrJoin("class", "md-local-file");
@@ -84,9 +124,14 @@ parser.renderer.rules.image = (tokens, index, options, env, renderer) => {
84
124
  return renderImage(tokens, index, options, env, renderer);
85
125
  };
86
126
 
87
- parser.renderer.rules.fence = (tokens, index) => {
127
+ parser.renderer.rules.fence = (tokens, index, _options, env) => {
88
128
  const token = tokens[index];
89
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
+ }
90
135
  let code = escape(token.content);
91
136
  // Bound synchronous highlighting work while streaming large tool-generated snippets.
92
137
  if (language && hljs.getLanguage(language) && token.content.length <= 32 * 1024) {
@@ -121,6 +166,7 @@ parser.renderer.rules.task_checkbox = (tokens, index) => {
121
166
  return `<input type="checkbox" disabled${checked ? " checked" : ""} aria-label="${checked ? "已完成" : "未完成"}"/> `;
122
167
  };
123
168
 
124
- export function markdown(src: unknown): string {
125
- return parser.render(String(src ?? ""));
169
+ export function markdown(src: unknown, context: { cwd?: string } = {}): string {
170
+ const text = String(src ?? "");
171
+ return parser.render(text, { ...context, markdownLines: text.replace(/\r\n?/g, "\n").split("\n") });
126
172
  }
@@ -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)}