claude-simple 0.2.21 → 0.2.23

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 (77) hide show
  1. package/README.md +6 -0
  2. package/dist/{app.0.2.21.min.js → app.0.2.23.min.js} +104 -101
  3. package/dist/index.html +2 -2
  4. package/dist/{style.0.2.21.min.css → style.0.2.23.min.css} +9 -0
  5. package/package.json +2 -1
  6. package/public/agent-panel.js +2 -1
  7. package/public/chat.js +17 -2
  8. package/public/index.html +4 -2
  9. package/public/markdown.js +12 -0
  10. package/public/math.js +72 -0
  11. package/public/mermaid.js +52 -0
  12. package/public/shell/session-list.js +2 -1
  13. package/public/shell/templates.js +0 -1
  14. package/public/style.css +9 -0
  15. package/public/sw.js +5 -3
  16. package/public/vendor/fonts/KaTeX_AMS-Regular.ttf +0 -0
  17. package/public/vendor/fonts/KaTeX_AMS-Regular.woff +0 -0
  18. package/public/vendor/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  19. package/public/vendor/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  20. package/public/vendor/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  21. package/public/vendor/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  22. package/public/vendor/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  23. package/public/vendor/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  24. package/public/vendor/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  25. package/public/vendor/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  26. package/public/vendor/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  27. package/public/vendor/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  28. package/public/vendor/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  29. package/public/vendor/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  30. package/public/vendor/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  31. package/public/vendor/fonts/KaTeX_Main-Bold.ttf +0 -0
  32. package/public/vendor/fonts/KaTeX_Main-Bold.woff +0 -0
  33. package/public/vendor/fonts/KaTeX_Main-Bold.woff2 +0 -0
  34. package/public/vendor/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  35. package/public/vendor/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  36. package/public/vendor/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  37. package/public/vendor/fonts/KaTeX_Main-Italic.ttf +0 -0
  38. package/public/vendor/fonts/KaTeX_Main-Italic.woff +0 -0
  39. package/public/vendor/fonts/KaTeX_Main-Italic.woff2 +0 -0
  40. package/public/vendor/fonts/KaTeX_Main-Regular.ttf +0 -0
  41. package/public/vendor/fonts/KaTeX_Main-Regular.woff +0 -0
  42. package/public/vendor/fonts/KaTeX_Main-Regular.woff2 +0 -0
  43. package/public/vendor/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  44. package/public/vendor/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  45. package/public/vendor/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  46. package/public/vendor/fonts/KaTeX_Math-Italic.ttf +0 -0
  47. package/public/vendor/fonts/KaTeX_Math-Italic.woff +0 -0
  48. package/public/vendor/fonts/KaTeX_Math-Italic.woff2 +0 -0
  49. package/public/vendor/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  50. package/public/vendor/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  51. package/public/vendor/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  52. package/public/vendor/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  53. package/public/vendor/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  54. package/public/vendor/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  55. package/public/vendor/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  56. package/public/vendor/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  57. package/public/vendor/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  58. package/public/vendor/fonts/KaTeX_Script-Regular.ttf +0 -0
  59. package/public/vendor/fonts/KaTeX_Script-Regular.woff +0 -0
  60. package/public/vendor/fonts/KaTeX_Script-Regular.woff2 +0 -0
  61. package/public/vendor/fonts/KaTeX_Size1-Regular.ttf +0 -0
  62. package/public/vendor/fonts/KaTeX_Size1-Regular.woff +0 -0
  63. package/public/vendor/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  64. package/public/vendor/fonts/KaTeX_Size2-Regular.ttf +0 -0
  65. package/public/vendor/fonts/KaTeX_Size2-Regular.woff +0 -0
  66. package/public/vendor/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  67. package/public/vendor/fonts/KaTeX_Size3-Regular.ttf +0 -0
  68. package/public/vendor/fonts/KaTeX_Size3-Regular.woff +0 -0
  69. package/public/vendor/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  70. package/public/vendor/fonts/KaTeX_Size4-Regular.ttf +0 -0
  71. package/public/vendor/fonts/KaTeX_Size4-Regular.woff +0 -0
  72. package/public/vendor/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  73. package/public/vendor/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  74. package/public/vendor/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  75. package/public/vendor/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  76. package/public/vendor/katex.min.css +1 -0
  77. package/public/vendor/katex.min.js +1 -0
package/dist/index.html CHANGED
@@ -4,13 +4,13 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
6
6
  <title>Claude Code</title>
7
- <link rel="stylesheet" href="/style.0.2.21.min.css">
7
+ <link rel="stylesheet" href="/style.0.2.23.min.css">
8
8
  <link rel="stylesheet" href="/highlight.min.css">
9
9
  <script src="/marked.min.js"></script>
10
10
  <script src="/highlight.min.js"></script>
11
11
  </head>
12
12
  <body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
13
13
  <div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
14
- <script type="module" src="/app.0.2.21.min.js"></script>
14
+ <script type="module" src="/app.0.2.23.min.js"></script>
15
15
  </body>
16
16
  </html>
@@ -1159,9 +1159,18 @@ html.pwa-standalone .ma-panel {
1159
1159
  overflow-x: auto;
1160
1160
  padding: .75rem .25rem .25rem;
1161
1161
  text-align: center;
1162
+ cursor: zoom-in;
1162
1163
  }
1163
1164
  .mermaid-diagram svg { max-width: 100%; height: auto; }
1165
+ .mermaid-diagram:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: .5rem; }
1164
1166
  .mermaid-error { border-color: color-mix(in oklch, var(--color-error) 55%, var(--color-base-300)); }
1167
+ .mermaid-zoom-overlay { position: fixed; inset: 0; z-index: 100; padding: 1rem; background: color-mix(in srgb, #000 76%, transparent); display: flex; align-items: center; justify-content: center; }
1168
+ .mermaid-zoom-overlay.hidden { display: none; }
1169
+ .mermaid-zoom-canvas { width: min(96vw, 1200px); max-height: 92vh; overflow: auto; padding: 2rem; border-radius: .75rem; background: var(--color-base-100); cursor: zoom-out; }
1170
+ .mermaid-zoom-canvas svg { display: block; width: max-content; min-width: min(100%, 48rem); max-width: none; height: auto; margin: auto; }
1171
+ .mermaid-zoom-close { position: fixed; top: 1rem; right: 1rem; width: 2.5rem; height: 2.5rem; border-radius: 999px; background: var(--color-base-100); color: var(--color-base-content); font-size: 1.8rem; line-height: 1; }
1172
+ .math-display { display: block; overflow-x: auto; margin: .75rem 0; padding: .2rem 0; text-align: center; }
1173
+ .math-inline .katex { font-size: 1.05em; }
1165
1174
  @media (max-width: 640px) {
1166
1175
  #recent-menu {
1167
1176
  min-width: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-simple",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "claude-simple": "server.js",
@@ -38,6 +38,7 @@
38
38
  "express": "^4.18.2",
39
39
  "jsonwebtoken": "^9.0.2",
40
40
  "jszip": "^3.10.1",
41
+ "katex": "^0.16.47",
41
42
  "mammoth": "^1.8.0",
42
43
  "marked": "^9.1.6",
43
44
  "mermaid": "^11.17.1",
@@ -6,6 +6,7 @@ import { esc, $ } from './lib.js';
6
6
  import { api, authHeaders, currentMachineId } from './api.js';
7
7
  import { hubMode, ctx, currentAgent, currentProject } from './state.js';
8
8
  import { renderMermaid } from './mermaid.js';
9
+ import { normalizeMarkdownText } from './markdown.js';
9
10
 
10
11
  let panelOpen = false;
11
12
  let mode = 'chat'; // chat | report | history | config
@@ -29,7 +30,7 @@ const LAST_SESSION_KEY = 'ma_last_session_id';
29
30
  function mdHtml(text) {
30
31
  if (typeof marked !== 'undefined') {
31
32
  try {
32
- return marked.parse(String(text ?? ''), { breaks: true, gfm: true });
33
+ return marked.parse(normalizeMarkdownText(text), { breaks: true, gfm: true });
33
34
  } catch { /* fallthrough */ }
34
35
  }
35
36
  return esc(text).replace(/\n/g, '<br>');
package/public/chat.js CHANGED
@@ -7,6 +7,8 @@ import { sendWs, api, authHeaders, flushWsQueue, clearWsQueue } from './api.js';
7
7
  import { initWakeLock, setWakeLockDesired } from './wake-lock.js';
8
8
  import { pushNotification } from './notifications.js';
9
9
  import { renderMermaid } from './mermaid.js';
10
+ import { renderMath } from './math.js';
11
+ import { normalizeMarkdownText } from './markdown.js';
10
12
 
11
13
  function assistantLabel() {
12
14
  return AGENT_LABELS[currentAgent.peek()] || 'Assistant';
@@ -136,6 +138,11 @@ function bindSocketLifecycle() {
136
138
  if (e.persisted) ensureChatSocket({ force: true });
137
139
  else ensureChatSocket({ probe: true });
138
140
  });
141
+ window.addEventListener('focus', () => ensureChatSocket({ probe: true }));
142
+ navigator.connection?.addEventListener?.('change', () => {
143
+ reconnectDelay = 400;
144
+ ensureChatSocket({ probe: true });
145
+ });
139
146
  }
140
147
 
141
148
  function updateConnDot() {
@@ -1518,6 +1525,13 @@ function ensureMarked() {
1518
1525
  }
1519
1526
  return `<div class="table-wrap"><table><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
1520
1527
  },
1528
+ // Agent output is untrusted Markdown, not application HTML. In
1529
+ // particular agy's native transcript may contain `<truncated N bytes>`;
1530
+ // letting marked emit that raw tag can unbalance the surrounding message
1531
+ // DOM. Show HTML-like content literally instead.
1532
+ html(raw) {
1533
+ return escapeCode(raw);
1534
+ },
1521
1535
  };
1522
1536
 
1523
1537
  try {
@@ -1539,7 +1553,7 @@ function renderMarkdown(text) {
1539
1553
  if (typeof marked === 'undefined') return esc(text);
1540
1554
  try {
1541
1555
  ensureMarked();
1542
- return marked.parse(String(text ?? ''), { breaks: true, gfm: true });
1556
+ return marked.parse(normalizeMarkdownText(text), { breaks: true, gfm: true });
1543
1557
  } catch {
1544
1558
  return esc(text);
1545
1559
  }
@@ -1559,6 +1573,7 @@ function highlightCode(root) {
1559
1573
 
1560
1574
  function enhanceMarkdown(root) {
1561
1575
  highlightCode(root);
1576
+ renderMath(root);
1562
1577
  renderMermaid(root);
1563
1578
  }
1564
1579
 
@@ -1595,7 +1610,7 @@ export function initChat() {
1595
1610
 
1596
1611
  watch(currentProject, () => syncWakeLock());
1597
1612
 
1598
- delegate.on('click', '#btn-refresh-conn, #ws-banner-retry', e => {
1613
+ delegate.on('click', '#ws-banner-retry', e => {
1599
1614
  e.preventDefault();
1600
1615
  refreshConnection();
1601
1616
  });
package/public/index.html CHANGED
@@ -83,7 +83,9 @@
83
83
  <link id="hljs-theme" rel="stylesheet" href="/vendor/highlight-dark.min.css">
84
84
  <script src="/vendor/highlight.min.js"></script>
85
85
  <script src="/vendor/mermaid.min.js"></script>
86
- <link rel="stylesheet" href="style.css?v=ui-18">
86
+ <link rel="stylesheet" href="/vendor/katex.min.css">
87
+ <script src="/vendor/katex.min.js"></script>
88
+ <link rel="stylesheet" href="style.css?v=ui-20">
87
89
  </head>
88
90
  <body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
89
91
  <div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
@@ -99,6 +101,6 @@
99
101
  }
100
102
  }, 10000);
101
103
  </script>
102
- <script type="module" src="app.js?v=ui-18"></script>
104
+ <script type="module" src="app.js?v=ui-20"></script>
103
105
  </body>
104
106
  </html>
@@ -0,0 +1,12 @@
1
+ // Markdown input normalisation shared by chat, history and Meta Agent views.
2
+ // Some agent transcript formats use `<truncated N bytes>` as a transport
3
+ // placeholder. It is not HTML, but a Markdown parser will otherwise pass it
4
+ // through as an element and a malformed placeholder can corrupt the rest of a
5
+ // message's DOM.
6
+ const TRUNCATED_TRANSCRIPT_MARKER = /(?:<|\[)\s*(?:…\s*)?truncated\s+([\d,]+)\s+bytes\s*(?:>|\])/gi;
7
+
8
+ export function normalizeMarkdownText(value) {
9
+ return String(value ?? '').replace(TRUNCATED_TRANSCRIPT_MARKER, (_match, bytes) => {
10
+ return `\n\n> ⚠️ 原始记录已截断(${bytes} bytes)\n\n`;
11
+ });
12
+ }
package/public/math.js ADDED
@@ -0,0 +1,72 @@
1
+ // KaTeX enhancement for Markdown output. The parser deliberately runs after
2
+ // Marked so fenced code and Mermaid source stay literal.
3
+
4
+ function ignored(node) {
5
+ return node.parentElement?.closest('pre, code, .mermaid-diagram, .katex, script, style');
6
+ }
7
+
8
+ function nextToken(text, from) {
9
+ const patterns = [
10
+ { re: /\\\[([\s\S]+?)\\\]/g, display: true },
11
+ { re: /\$\$([\s\S]+?)\$\$/g, display: true },
12
+ { re: /\\\(([\s\S]+?)\\\)/g, display: false },
13
+ // A closing dollar must not be escaped or preceded by whitespace. This
14
+ // leaves ordinary currency such as "$5 and $6" untouched.
15
+ { re: /(?<!\\)\$([^$\n]|\\\$)+?(?<!\s)(?<!\\)\$/g, display: false },
16
+ ];
17
+ let best = null;
18
+ for (const pattern of patterns) {
19
+ pattern.re.lastIndex = from;
20
+ const match = pattern.re.exec(text);
21
+ if (!match || (best && match.index >= best.match.index)) continue;
22
+ const raw = match[0];
23
+ const body = raw.startsWith('\\[') || raw.startsWith('\\(')
24
+ ? raw.slice(2, -2)
25
+ : raw.startsWith('$$') ? raw.slice(2, -2) : raw.slice(1, -1);
26
+ if (!body.trim()) continue;
27
+ best = { match, body, display: pattern.display };
28
+ }
29
+ return best;
30
+ }
31
+
32
+ function replaceTextNode(node) {
33
+ const text = node.nodeValue || '';
34
+ let cursor = 0;
35
+ let token = nextToken(text, cursor);
36
+ if (!token || typeof globalThis.katex?.renderToString !== 'function') return;
37
+ const fragment = document.createDocumentFragment();
38
+ while (token) {
39
+ if (token.match.index > cursor) fragment.append(document.createTextNode(text.slice(cursor, token.match.index)));
40
+ let html;
41
+ try {
42
+ html = globalThis.katex.renderToString(token.body.trim(), {
43
+ displayMode: token.display,
44
+ throwOnError: false,
45
+ strict: 'warn',
46
+ });
47
+ } catch {
48
+ fragment.append(document.createTextNode(token.match[0]));
49
+ cursor = token.match.index + token.match[0].length;
50
+ token = nextToken(text, cursor);
51
+ continue;
52
+ }
53
+ const wrapper = document.createElement(token.display ? 'div' : 'span');
54
+ wrapper.className = token.display ? 'math-display' : 'math-inline';
55
+ wrapper.innerHTML = html;
56
+ fragment.append(wrapper);
57
+ cursor = token.match.index + token.match[0].length;
58
+ token = nextToken(text, cursor);
59
+ }
60
+ if (cursor < text.length) fragment.append(document.createTextNode(text.slice(cursor)));
61
+ node.replaceWith(fragment);
62
+ }
63
+
64
+ export function renderMath(root) {
65
+ if (!root || typeof globalThis.katex?.renderToString !== 'function') return;
66
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
67
+ const nodes = [];
68
+ while (walker.nextNode()) {
69
+ if (!ignored(walker.currentNode)) nodes.push(walker.currentNode);
70
+ }
71
+ nodes.forEach(replaceTextNode);
72
+ }
package/public/mermaid.js CHANGED
@@ -2,6 +2,45 @@
2
2
  // diagrams keep working when public CDNs are unavailable.
3
3
  let initialized = false;
4
4
  let sequence = 0;
5
+ let zoomBound = false;
6
+
7
+ function ensureZoomOverlay() {
8
+ let overlay = document.getElementById('mermaid-zoom-overlay');
9
+ if (overlay) return overlay;
10
+ overlay = document.createElement('div');
11
+ overlay.id = 'mermaid-zoom-overlay';
12
+ overlay.className = 'mermaid-zoom-overlay hidden';
13
+ overlay.setAttribute('role', 'dialog');
14
+ overlay.setAttribute('aria-modal', 'true');
15
+ overlay.setAttribute('aria-label', 'Mermaid diagram enlarged');
16
+ overlay.innerHTML = `<button type="button" class="mermaid-zoom-close" aria-label="关闭放大图">×</button><div class="mermaid-zoom-canvas"></div>`;
17
+ document.body.appendChild(overlay);
18
+ const close = () => overlay.classList.add('hidden');
19
+ overlay.addEventListener('click', event => {
20
+ if (event.target === overlay || event.target.closest('.mermaid-zoom-close')) close();
21
+ });
22
+ if (!zoomBound) {
23
+ zoomBound = true;
24
+ document.addEventListener('keydown', event => {
25
+ if (event.key === 'Escape') close();
26
+ });
27
+ }
28
+ return overlay;
29
+ }
30
+
31
+ function openZoom(host) {
32
+ const svg = host.querySelector('svg');
33
+ if (!svg) return;
34
+ const overlay = ensureZoomOverlay();
35
+ const canvas = overlay.querySelector('.mermaid-zoom-canvas');
36
+ // SVG is generated locally by Mermaid. Clone it into the modal so zooming
37
+ // never mutates the in-message diagram or its click bindings.
38
+ canvas.innerHTML = svg.outerHTML;
39
+ const expanded = canvas.querySelector('svg');
40
+ expanded?.removeAttribute('width');
41
+ expanded?.removeAttribute('height');
42
+ overlay.classList.remove('hidden');
43
+ }
5
44
 
6
45
  function setup() {
7
46
  const api = globalThis.mermaid;
@@ -53,6 +92,19 @@ export function renderMermaid(root) {
53
92
  Promise.resolve(api.parse(source)).then(() => api.render(id, source)).then(({ svg, bindFunctions }) => {
54
93
  host.innerHTML = svg;
55
94
  bindFunctions?.(host);
95
+ host.tabIndex = 0;
96
+ host.title = '点击放大图表';
97
+ host.addEventListener('click', event => {
98
+ // Mermaid can bind links/callbacks to diagram nodes; preserve those.
99
+ if (event.target.closest('a')) return;
100
+ openZoom(host);
101
+ });
102
+ host.addEventListener('keydown', event => {
103
+ if (event.key === 'Enter' || event.key === ' ') {
104
+ event.preventDefault();
105
+ openZoom(host);
106
+ }
107
+ });
56
108
  }).catch(error => {
57
109
  // Syntax errors remain inspectable/copyable as the original code block.
58
110
  host.replaceWith(pre);
@@ -13,6 +13,7 @@ import {
13
13
  import { api, NOT_MODIFIED } from '../api.js';
14
14
  import { getCachedSessions, setCachedSessions, getCachedWorkspaces, setCachedWorkspaces } from '../cache.js';
15
15
  import { formatTime, agentBadge, agentCountPills, shortPath } from './util.js';
16
+ import { normalizeMarkdownText } from '../markdown.js';
16
17
 
17
18
 
18
19
  function normalizeSessions(v) {
@@ -424,7 +425,7 @@ export function mdToHtml(text) {
424
425
  if (typeof marked === 'undefined') return `<pre class="whitespace-pre-wrap text-xs">${esc(text)}</pre>`;
425
426
  try {
426
427
  // Prefer shared renderer if chat has configured marked
427
- return marked.parse(String(text), { breaks: true, gfm: true });
428
+ return marked.parse(normalizeMarkdownText(text), { breaks: true, gfm: true });
428
429
  } catch {
429
430
  return `<pre class="whitespace-pre-wrap text-xs">${esc(text)}</pre>`;
430
431
  }
@@ -80,7 +80,6 @@ export const AppShell = (opts = {}) => {
80
80
  <span id="conn-dot" class="w-2 h-2 rounded-full bg-base-content/30 flex-shrink-0" title="连接状态"></span>
81
81
  <div class="flex gap-2 topbar-actions">
82
82
  <button id="btn-share-session" class="btn btn-ghost btn-xs border border-base-300" title="复制当前会话分享链接">分享</button>
83
- <button id="btn-refresh-conn" class="btn btn-ghost btn-xs border border-base-300" title="重新连接并同步">↻</button>
84
83
  <button id="btn-pwa-install" class="btn btn-ghost btn-xs border border-base-300 hidden" title="安装到主屏幕">安装</button>
85
84
  <button id="btn-meta-agent" class="btn btn-ghost btn-xs border border-primary/40 text-primary topbar-icon-btn" title="Meta Agent — 报告 / 问答 / VLM" aria-label="打开 Meta Agent">
86
85
  <svg class="topbar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m12 2 1.6 6.4L20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6L12 2Z"/><path d="m19 16 .7 2.3L22 19l-2.3.7L19 22l-.7-2.3L16 19l2.3-.7L19 16Z"/></svg><span class="topbar-icon-label">Agent</span>
package/public/style.css CHANGED
@@ -1157,9 +1157,18 @@ html.pwa-standalone .ma-panel {
1157
1157
  overflow-x: auto;
1158
1158
  padding: .75rem .25rem .25rem;
1159
1159
  text-align: center;
1160
+ cursor: zoom-in;
1160
1161
  }
1161
1162
  .mermaid-diagram svg { max-width: 100%; height: auto; }
1163
+ .mermaid-diagram:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: .5rem; }
1162
1164
  .mermaid-error { border-color: color-mix(in oklch, var(--color-error) 55%, var(--color-base-300)); }
1165
+ .mermaid-zoom-overlay { position: fixed; inset: 0; z-index: 100; padding: 1rem; background: color-mix(in srgb, #000 76%, transparent); display: flex; align-items: center; justify-content: center; }
1166
+ .mermaid-zoom-overlay.hidden { display: none; }
1167
+ .mermaid-zoom-canvas { width: min(96vw, 1200px); max-height: 92vh; overflow: auto; padding: 2rem; border-radius: .75rem; background: var(--color-base-100); cursor: zoom-out; }
1168
+ .mermaid-zoom-canvas svg { display: block; width: max-content; min-width: min(100%, 48rem); max-width: none; height: auto; margin: auto; }
1169
+ .mermaid-zoom-close { position: fixed; top: 1rem; right: 1rem; width: 2.5rem; height: 2.5rem; border-radius: 999px; background: var(--color-base-100); color: var(--color-base-content); font-size: 1.8rem; line-height: 1; }
1170
+ .math-display { display: block; overflow-x: auto; margin: .75rem 0; padding: .2rem 0; text-align: center; }
1171
+ .math-inline .katex { font-size: 1.05em; }
1163
1172
  @media (max-width: 640px) {
1164
1173
  #recent-menu {
1165
1174
  min-width: 0;
package/public/sw.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /* Agent UI service worker — cache shell assets only.
2
2
  * Never intercepts WebSocket or mutates API semantics beyond offline fallback.
3
3
  * Bump CACHE when shipping UI that must invalidate old shells. */
4
- const CACHE = 'agent-ui-shell-v23';
4
+ const CACHE = 'agent-ui-shell-v25';
5
5
  const PRECACHE = [
6
6
  '/',
7
7
  '/index.html',
8
8
  '/manifest.webmanifest',
9
- '/style.css?v=ui-18',
9
+ '/style.css?v=ui-20',
10
10
  '/style.css',
11
11
  '/ui.min.css',
12
12
  '/vendor/mini-react.min.js',
@@ -14,7 +14,9 @@ const PRECACHE = [
14
14
  '/vendor/highlight.min.js',
15
15
  '/vendor/highlight-dark.min.css',
16
16
  '/vendor/mermaid.min.js',
17
- '/app.js?v=ui-18',
17
+ '/vendor/katex.min.js',
18
+ '/vendor/katex.min.css',
19
+ '/app.js?v=ui-20',
18
20
  '/icons/icon-192.png',
19
21
  '/icons/icon-512.png',
20
22
  '/icons/icon-maskable-512.png',
@@ -0,0 +1 @@
1
+ @font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.47"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}