Files changed (48) hide show
  1. static/app.js +0 -1020
  2. static/chat.js +0 -759
  3. static/css/_chat.css +0 -144
  4. static/css/_chatbot.css +0 -170
  5. static/css/_dark.css +0 -194
  6. static/css/_examples.css +0 -142
  7. static/css/_hide.css +0 -131
  8. static/css/_input.css +0 -114
  9. static/css/_layout.css +0 -41
  10. static/css/_legacy.css +0 -50
  11. static/css/_math.css +0 -132
  12. static/css/_misc.css +0 -111
  13. static/css/_modals.css +0 -266
  14. static/css/_perf.css +0 -50
  15. static/css/_thinking.css +0 -35
  16. static/css/_typing.css +0 -29
  17. static/css/_variables.css +0 -81
  18. static/vendor/highlight/LICENSE +0 -29
  19. static/vendor/highlight/github-dark.min.css +0 -10
  20. static/vendor/highlight/github.min.css +0 -18
  21. static/vendor/highlight/highlight.min.js +0 -0
  22. static/vendor/katex/LICENSE +0 -27
  23. static/vendor/katex/auto-render.min.js +0 -1
  24. static/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  25. static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  26. static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  27. static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  28. static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  29. static/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  30. static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  31. static/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  32. static/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  33. static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  34. static/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  35. static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  36. static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  37. static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  38. static/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  39. static/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  40. static/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  41. static/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  42. static/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  43. static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  44. static/vendor/katex/fonts/OFL.txt +0 -98
  45. static/vendor/katex/katex.min.css +0 -1
  46. static/vendor/katex/katex.min.js +0 -0
  47. static/vendor/marked/LICENSE +0 -60
  48. static/vendor/marked/marked.min.js +0 -8
static/app.js DELETED
@@ -1,1020 +0,0 @@
1
- /* eslint-disable */
2
- /**
3
- * Gradio app bootstrap.
4
- *
5
- * The Python side injects window.HY_EXAMPLES / window.HY_I18N before this
6
- * script runs. The custom chat renderer (static/chat.js) owns all DOM
7
- * mutations under #hy-chat — this file only deals with the surrounding
8
- * UX (overlay, scroll, send-button state, copy/preview buttons).
9
- *
10
- * Responsibilities
11
- * ────────────────
12
- * 1. Render the empty-state "What can I help you with?" overlay above the
13
- * chat surface, with example prompts that auto-send when clicked.
14
- * 2. Add a "▶ html preview" button below every Markdown HTML code block,
15
- * and render the code in a sandboxed iframe modal.
16
- * 3. Add a "Copy" button to the top-right of every <pre> code block.
17
- * 4. Own scroll behaviour: follow-the-tail while streaming, full user
18
- * control after streaming ends.
19
- * 5. OWN the Send-button busy state. The server NEVER toggles the
20
- * button. Instead, the streaming generators in chat.py drive a
21
- * dedicated hidden ``gr.HTML`` component (``#hy-busy-marker``):
22
- * while streaming → it contains a single
23
- * ``<span data-hy-streaming="1">``; on the final yield → it's
24
- * idle. We observe that one component and disable Send iff the
25
- * marker span is present.
26
- *
27
- * The marker lives in its OWN component (not inside the chat) so
28
- * unclosed ``<style>``/``<script>``/``<textarea>``/``<title>``/
29
- * ``<noscript>`` in model output cannot hijack it.
30
- */
31
- (() => {
32
- if (window.__HY_APP_INIT) return;
33
- window.__HY_APP_INIT = true;
34
-
35
- const EXAMPLES = (window.HY_EXAMPLES || []).slice();
36
- const BUSY_MSG =
37
- (window.HY_I18N && window.HY_I18N.model_busy) ||
38
- "Model is still responding. Please wait for it to finish.";
39
-
40
- // The chat surface lives at #hy-chat (built by static/chat.js into the
41
- // value of the gr.HTML at #hy-chat-host). Throughout this file we treat
42
- // #hy-chat as "the chatbot" — it's our custom replacement.
43
- const CHAT_SELECTOR = "#hy-chat";
44
-
45
- // CSS selector for the in-flight streaming marker. See chat.py
46
- // (HY_BUSY_HTML) for the producer side.
47
- const BUSY_MARKER_HOST_ID = "hy-busy-marker";
48
- const STREAM_MARKER_SELECTOR =
49
- '#' + BUSY_MARKER_HOST_ID + ' [data-hy-streaming="1"]';
50
- // Explicit "server says idle" marker. Distinct from "no marker yet"
51
- // (the host's initial empty value), so we can tell apart "still
52
- // waiting for the first server frame" from "server already responded
53
- // with idle" (e.g. validation short-circuit before any streaming).
54
- const IDLE_MARKER_SELECTOR =
55
- '#' + BUSY_MARKER_HOST_ID + ' [data-hy-busy="0"]';
56
-
57
- // Set to ``performance.now()`` whenever the user clicks Send / hits Enter.
58
- // Bridges the brief window between "user dispatched" and "first marker
59
- // frame arrived" so a fast double-click can't sneak through.
60
- let lastSendAt = 0;
61
- const SEND_DEBOUNCE_MS = 400;
62
-
63
- // Set to true the moment the user dispatches a send, cleared the moment
64
- // either (a) the server's first busy-marker frame actually arrives, or
65
- // (b) FIRST_FRAME_TIMEOUT_MS elapses (defensive timeout in case the
66
- // request never lands — e.g. WebSocket dropped before the first frame).
67
- //
68
- // SEND_DEBOUNCE_MS alone is NOT enough: on a slow link the round-trip
69
- // for the first frame can easily exceed 400ms and a second click slips
70
- // through, causing two parallel streams (server-side concurrency_limit
71
- // is 8 for the chat fn so the second request DOES run).
72
- let awaitingFirstFrame = false;
73
- const FIRST_FRAME_TIMEOUT_MS = 30 * 1000;
74
-
75
- // Soft watchdog: if the busy marker is set but the chat DOM has been
76
- // observably idle for this long AND it's been at least
77
- // CHAT_IDLE_MIN_SINCE_SEND_MS since the user clicked Send, we force-
78
- // clear the marker. Safety net for "the server's IDLE marker frame
79
- // got coalesced/dropped on the wire".
80
- //
81
- // CHAT_IDLE_MS MUST be comfortably greater than the server-side
82
- // ``_HEARTBEAT_INTERVAL`` (5s in core/chat.py). The watchdog is reset
83
- // on every applied delta — including heartbeats with empty ops — so
84
- // a healthy stream with periodic heartbeats keeps the timer well
85
- // below the threshold. The margin guards against jitter in heartbeat
86
- // arrival (proxy buffering, two-window contention slowing the
87
- // server's first-content yield, etc.) so the watchdog only fires
88
- // when the stream is actually dead.
89
- const CHAT_IDLE_MS = 12000;
90
- const CHAT_IDLE_MIN_SINCE_SEND_MS = 1500;
91
-
92
- // Hard absolute cap. Defence-in-depth backstop in case the soft
93
- // watchdog also fails for some reason.
94
- const ABSOLUTE_BUSY_TIMEOUT_MS = 5 * 60 * 1000;
95
-
96
- // Flipped to true on a real send dispatch so the scroll lock knows to
97
- // re-enter follow mode for the next streaming turn even if the user had
98
- // scrolled up during the previous turn.
99
- let justSentNewMessage = false;
100
-
101
- /* ── Chat mutation hub (rAF-batched) ──────────────────────��────────────
102
- *
103
- * The chat DOM gets HAMMERED during streaming: every new token may
104
- * mutate the trailing streaming segment. With each subsystem
105
- * (scroll lock, code-block injector, busy-marker timestamp,
106
- * empty-state visibility) installing its OWN MutationObserver,
107
- * the per-mutation work multiplied by the number of subsystems
108
- * was the dominant browser-side cost as conversations grew.
109
- *
110
- * We run a single MutationObserver and dispatch to subscribers via
111
- * requestAnimationFrame. This caps total dispatches at ≤60/sec
112
- * regardless of how many mutations the chat fires, and lets each
113
- * subscriber see the WHOLE batch of mutations in one call instead
114
- * of being woken N times in the same tick.
115
- *
116
- * Subscribers MUST be cheap — the rAF callback is on the critical
117
- * path of the next paint. */
118
- const chatSubscribers = [];
119
- let chatPending = null;
120
- let chatScheduled = false;
121
-
122
- function subscribeChatMutations(cb) {
123
- chatSubscribers.push(cb);
124
- }
125
-
126
- function startChatMutationHub(chatEl) {
127
- new MutationObserver((mutations) => {
128
- if (chatPending === null) chatPending = [];
129
- // Ref-share the records — they're owned by the observer and we
130
- // don't mutate them, just iterate.
131
- for (let i = 0; i < mutations.length; i++) {
132
- chatPending.push(mutations[i]);
133
- }
134
- if (chatScheduled) return;
135
- chatScheduled = true;
136
- requestAnimationFrame(() => {
137
- chatScheduled = false;
138
- const batch = chatPending;
139
- chatPending = null;
140
- for (let i = 0; i < chatSubscribers.length; i++) {
141
- try {
142
- chatSubscribers[i](batch);
143
- } catch (err) {
144
- console.error("[hy] chat subscriber error:", err);
145
- }
146
- }
147
- });
148
- }).observe(chatEl, {
149
- childList: true,
150
- subtree: true,
151
- characterData: true,
152
- });
153
- }
154
-
155
- /* ── Empty-state examples overlay ─────────────────────────────────────── */
156
- function createOverlay() {
157
- const chatEl = document.querySelector(CHAT_SELECTOR);
158
- if (!chatEl) {
159
- setTimeout(createOverlay, 500);
160
- return;
161
- }
162
- if (document.getElementById("examples-overlay")) return;
163
-
164
- const overlay = document.createElement("div");
165
- overlay.id = "examples-overlay";
166
-
167
- const wrapper = document.createElement("div");
168
- wrapper.className = "examples-wrapper";
169
-
170
- const heading = document.createElement("p");
171
- heading.className = "examples-heading";
172
- heading.textContent =
173
- (window.HY_I18N && window.HY_I18N.examples_heading) ||
174
- "What can I help you with?";
175
- wrapper.appendChild(heading);
176
-
177
- const grid = document.createElement("div");
178
- grid.className = "examples-grid";
179
-
180
- // Example-button label.
181
- // * Collapse internal whitespace/newlines to single spaces so each
182
- // card flows as one paragraph; the CSS 2-line clamp on
183
- // .example-btn handles the visual cut-off + ellipsis.
184
- // * Hard cap at LABEL_MAX_CHARS as defence-in-depth — the prompts
185
- // can be multi-kilobyte system instructions and we don't want
186
- // all of that in the DOM just to be invisibly clamped.
187
- // * ``title`` shows the full text on hover.
188
- // The FULL prompt is always what we dispatch to the model; this is
189
- // a display-only transform.
190
- const LABEL_MAX_CHARS = 200;
191
- function makeLabel(text) {
192
- const oneLine = String(text).replace(/\s+/g, " ").trim();
193
- if (oneLine.length <= LABEL_MAX_CHARS) return oneLine;
194
- return oneLine.slice(0, LABEL_MAX_CHARS).trimEnd() + "…";
195
- }
196
-
197
- EXAMPLES.forEach((text) => {
198
- // Two-layer structure:
199
- // .example-btn → flex container, owns size + centering
200
- // .example-btn-text → -webkit-box, owns the 2-line clamp
201
- // Splitting these is the only reliable way to get BOTH "vertical
202
- // centering" (needs display:flex) AND "clamp to 2 lines with auto
203
- // ellipsis" (needs display:-webkit-box) on the same card. Single-
204
- // element setups force a choice and we always lost one.
205
- const btn = document.createElement("div");
206
- btn.className = "example-btn";
207
- const inner = document.createElement("span");
208
- inner.className = "example-btn-text";
209
- inner.textContent = makeLabel(text);
210
- btn.appendChild(inner);
211
- btn.title = text;
212
- btn.addEventListener("click", () => {
213
- const textarea = document.querySelector(".msg-input textarea");
214
- if (!textarea) return;
215
- textarea.focus();
216
- textarea.select();
217
- document.execCommand("insertText", false, text);
218
- setTimeout(() => {
219
- const sendBtn = document.querySelector("#send-btn");
220
- if (sendBtn) sendBtn.click();
221
- }, 200);
222
- });
223
- grid.appendChild(btn);
224
- });
225
-
226
- wrapper.appendChild(grid);
227
- overlay.appendChild(wrapper);
228
-
229
- function positionOverlay() {
230
- const r = chatEl.getBoundingClientRect();
231
- overlay.style.top = `${r.top}px`;
232
- overlay.style.left = `${r.left}px`;
233
- overlay.style.width = `${r.width}px`;
234
- overlay.style.height = `${r.height}px`;
235
- }
236
- positionOverlay();
237
- document.body.appendChild(overlay);
238
-
239
- function syncVisibility() {
240
- const msgs = chatEl.querySelectorAll(".message-row");
241
- overlay.style.display = msgs.length > 0 ? "none" : "";
242
- }
243
- syncVisibility();
244
- subscribeChatMutations(syncVisibility);
245
-
246
- window.addEventListener("resize", positionOverlay);
247
- new ResizeObserver(positionOverlay).observe(chatEl);
248
- }
249
-
250
- /* ── HTML preview modal ───────────────────────────────────────────────── */
251
- function openHtmlPreview(htmlCode) {
252
- const existing = document.querySelector(".html-preview-overlay");
253
- if (existing) existing.remove();
254
-
255
- const overlay = document.createElement("div");
256
- overlay.className = "html-preview-overlay";
257
- overlay.addEventListener("click", (e) => {
258
- if (e.target === overlay) overlay.remove();
259
- });
260
-
261
- const modal = document.createElement("div");
262
- modal.className = "html-preview-modal";
263
-
264
- const header = document.createElement("div");
265
- header.className = "html-preview-header";
266
-
267
- const title = document.createElement("span");
268
- title.className = "html-preview-title";
269
- title.textContent = "HTML Preview";
270
-
271
- const closeBtn = document.createElement("button");
272
- closeBtn.className = "html-preview-close";
273
- closeBtn.innerHTML = "&times;";
274
- closeBtn.addEventListener("click", () => overlay.remove());
275
-
276
- header.appendChild(title);
277
- header.appendChild(closeBtn);
278
-
279
- const iframe = document.createElement("iframe");
280
- iframe.className = "html-preview-iframe";
281
- // Intentionally NOT including allow-same-origin: that would let the
282
- // user-supplied HTML reach into the parent page and steal cookies/state.
283
- iframe.sandbox = "allow-scripts allow-modals allow-forms allow-popups";
284
-
285
- modal.appendChild(header);
286
- modal.appendChild(iframe);
287
- overlay.appendChild(modal);
288
- document.body.appendChild(overlay);
289
-
290
- iframe.srcdoc = htmlCode;
291
-
292
- document.addEventListener("keydown", function escHandler(e) {
293
- if (e.key === "Escape") {
294
- overlay.remove();
295
- document.removeEventListener("keydown", escHandler);
296
- }
297
- });
298
- }
299
-
300
- const HTML_TAG_RE =
301
- /<\/(div|body|html|section|main|head|style|script|p|h[1-6]|span|table|form|ul|ol|nav|header|footer|article)>/i;
302
-
303
- function looksLikeHtml(text) {
304
- const trimmed = text.trim();
305
- if (!trimmed) return false;
306
- if (
307
- trimmed.startsWith("<!DOCTYPE") ||
308
- trimmed.startsWith("<!doctype") ||
309
- trimmed.startsWith("<html")
310
- ) {
311
- return true;
312
- }
313
- return (
314
- trimmed.startsWith("<") &&
315
- trimmed.endsWith(">") &&
316
- HTML_TAG_RE.test(trimmed)
317
- );
318
- }
319
-
320
- function injectPreviewButtonFor(codeEl) {
321
- // ``previewBtnInserted`` — set once on the codeEl after we attach a
322
- // button. Must NEVER be set on a code element living inside
323
- // ``.hy-streaming`` because that container's innerHTML is replaced
324
- // on every delta — the codeEl gets discarded and a fresh one (no
325
- // flag) appears, while the button (a sibling outside .hy-streaming)
326
- // would silently leak. We only inject for code blocks that have
327
- // settled into ``.hy-frozen`` (append-only, never re-rendered).
328
- if (codeEl.dataset.previewBtnInserted) {
329
- codeEl.__hyPreviewText = codeEl.textContent || "";
330
- return;
331
- }
332
-
333
- const pre = codeEl.closest("pre");
334
- if (!pre) return;
335
-
336
- // Skip code blocks still in the streaming half. They'll be
337
- // re-evaluated once the freeze boundary advances past them and the
338
- // pre is moved into ``.hy-frozen`` as a fresh DOM node — at which
339
- // point this MutationObserver fires again with the new pre as an
340
- // added node and we inject exactly once.
341
- const frozenAncestor = pre.closest(".hy-frozen");
342
- if (!frozenAncestor) return;
343
-
344
- const text = codeEl.textContent || "";
345
- if (!looksLikeHtml(text)) return;
346
-
347
- codeEl.dataset.previewBtnInserted = "true";
348
- codeEl.__hyPreviewText = text;
349
-
350
- const btn = document.createElement("button");
351
- btn.className = "html-preview-btn";
352
- btn.title = "Preview HTML";
353
- btn.addEventListener("click", (e) => {
354
- e.stopPropagation();
355
- e.preventDefault();
356
- openHtmlPreview(codeEl.__hyPreviewText || codeEl.textContent || "");
357
- });
358
-
359
- // Insert directly after the <pre> inside the same .hy-frozen
360
- // container. This binds the button's lifecycle to the specific
361
- // code block (instead of dumping all preview buttons into b.md as
362
- // siblings of .hy-frozen / .hy-streaming) and keeps its position
363
- // visually anchored to its source block.
364
- pre.parentElement.insertBefore(btn, pre.nextSibling);
365
- }
366
-
367
- /* ── Code-block chrome (header w/ lang label + copy button) ───────────── */
368
- function copyText(text) {
369
- if (navigator.clipboard && navigator.clipboard.writeText) {
370
- return navigator.clipboard.writeText(text).catch(() => fallbackCopy(text));
371
- }
372
- return fallbackCopy(text);
373
- }
374
-
375
- function fallbackCopy(text) {
376
- return new Promise((resolve, reject) => {
377
- try {
378
- const ta = document.createElement("textarea");
379
- ta.value = text;
380
- ta.setAttribute("readonly", "");
381
- ta.style.position = "absolute";
382
- ta.style.left = "-9999px";
383
- document.body.appendChild(ta);
384
- ta.select();
385
- document.execCommand("copy");
386
- document.body.removeChild(ta);
387
- resolve();
388
- } catch (err) {
389
- reject(err);
390
- }
391
- });
392
- }
393
-
394
- const COPY_ICON_SVG =
395
- '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" ' +
396
- 'stroke="currentColor" stroke-width="2" stroke-linecap="round" ' +
397
- 'stroke-linejoin="round" aria-hidden="true">' +
398
- '<rect x="9" y="9" width="11" height="11" rx="2" ry="2"></rect>' +
399
- '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>' +
400
- "</svg>";
401
- const CHECK_ICON_SVG =
402
- '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" ' +
403
- 'stroke="currentColor" stroke-width="2.5" stroke-linecap="round" ' +
404
- 'stroke-linejoin="round" aria-hidden="true">' +
405
- '<polyline points="4 12 10 18 20 6"></polyline>' +
406
- "</svg>";
407
-
408
- // Pull the language label off the <code> element. ``marked`` always
409
- // emits ``class="language-X"`` from the fence info string, and
410
- // ``hljs.highlightElement`` may also add ``language-X`` after detection.
411
- // Anything else (no fence info, hljs failed) → blank label so the
412
- // header still renders consistently.
413
- function detectCodeLang(codeEl) {
414
- const cls = codeEl.className || "";
415
- const m = cls.match(/(?:^|\s)language-([\w+#.-]+)/i);
416
- if (!m) return "";
417
- const lang = m[1].toLowerCase();
418
- // A few common aliases for nicer display.
419
- const ALIASES = {
420
- js: "javascript",
421
- ts: "typescript",
422
- sh: "shell",
423
- bash: "shell",
424
- zsh: "shell",
425
- yml: "yaml",
426
- md: "markdown",
427
- "c++": "cpp",
428
- };
429
- return ALIASES[lang] || lang;
430
- }
431
-
432
- const COPY_LABEL =
433
- (window.HY_I18N && window.HY_I18N.code_copy) || "Copy";
434
- const COPIED_LABEL =
435
- (window.HY_I18N && window.HY_I18N.code_copied) || "Copied";
436
-
437
- function injectCopyButtonFor(codeEl) {
438
- const pre = codeEl.closest("pre");
439
- if (!pre || pre.dataset.copyInjected) return;
440
- pre.dataset.copyInjected = "true";
441
-
442
- // ── Build the wrapper card ────────────────────────────────────────
443
- const wrapper = document.createElement("div");
444
- wrapper.className = "hy-codeblock";
445
-
446
- const header = document.createElement("div");
447
- header.className = "hy-codeblock-header";
448
-
449
- const lang = document.createElement("span");
450
- lang.className = "hy-codeblock-lang";
451
- lang.textContent = detectCodeLang(codeEl);
452
- header.appendChild(lang);
453
-
454
- const btn = document.createElement("button");
455
- btn.type = "button";
456
- btn.className = "code-copy-btn";
457
- btn.setAttribute("aria-label", COPY_LABEL);
458
- btn.title = COPY_LABEL;
459
- btn.innerHTML = COPY_ICON_SVG;
460
-
461
- let resetTimer = null;
462
- btn.addEventListener("click", (e) => {
463
- e.stopPropagation();
464
- e.preventDefault();
465
- const text = codeEl.textContent || "";
466
- copyText(text).then(
467
- () => {
468
- btn.innerHTML = CHECK_ICON_SVG;
469
- btn.classList.add("copied");
470
- btn.title = COPIED_LABEL;
471
- if (resetTimer) clearTimeout(resetTimer);
472
- resetTimer = setTimeout(() => {
473
- btn.innerHTML = COPY_ICON_SVG;
474
- btn.classList.remove("copied");
475
- btn.title = COPY_LABEL;
476
- }, 1400);
477
- },
478
- () => {
479
- btn.title = "Copy failed";
480
- if (resetTimer) clearTimeout(resetTimer);
481
- resetTimer = setTimeout(() => {
482
- btn.title = COPY_LABEL;
483
- }, 1400);
484
- }
485
- );
486
- });
487
- header.appendChild(btn);
488
-
489
- // ── Splice wrapper into the DOM around <pre> ─────────────────────
490
- // Insert the wrapper where <pre> lived, then move <pre> into it.
491
- // The mutation observer that drives this function will re-fire for
492
- // the wrapper's appearance, but ``dataset.copyInjected`` on <pre>
493
- // makes the second pass a no-op.
494
- const parent = pre.parentElement;
495
- if (!parent) return;
496
- parent.insertBefore(wrapper, pre);
497
- wrapper.appendChild(header);
498
- wrapper.appendChild(pre);
499
- }
500
-
501
- function processCodeBlock(codeEl) {
502
- injectCopyButtonFor(codeEl);
503
- injectPreviewButtonFor(codeEl);
504
- }
505
-
506
- function watchForHtmlBlocks() {
507
- const chatEl = document.querySelector(CHAT_SELECTOR);
508
- if (!chatEl) {
509
- setTimeout(watchForHtmlBlocks, 500);
510
- return;
511
- }
512
-
513
- chatEl.querySelectorAll("pre code").forEach(processCodeBlock);
514
-
515
- // Subscribers receive the whole rAF batch in one call. Within a
516
- // single batch the same code element may appear in many records
517
- // (one per streamed token); we de-dup with a Set so we only
518
- // re-evaluate ``injectPreviewButtonFor`` ONCE per affected code
519
- // element per frame instead of once per mutation.
520
- subscribeChatMutations((mutations) => {
521
- const codeElsToCheck = new Set();
522
- for (let i = 0; i < mutations.length; i++) {
523
- const m = mutations[i];
524
- if (m.type === "childList") {
525
- const added = m.addedNodes;
526
- for (let j = 0; j < added.length; j++) {
527
- const node = added[j];
528
- if (!(node instanceof HTMLElement)) continue;
529
- if (node.matches && node.matches("pre code")) {
530
- processCodeBlock(node);
531
- } else if (node.querySelectorAll) {
532
- const found = node.querySelectorAll("pre code");
533
- for (let k = 0; k < found.length; k++) {
534
- processCodeBlock(found[k]);
535
- }
536
- }
537
- }
538
- } else if (m.type === "characterData") {
539
- const el = m.target.parentElement;
540
- if (el && el.tagName === "CODE") codeElsToCheck.add(el);
541
- }
542
- }
543
- // Late-arriving HTML detection: a code block may not have looked
544
- // like HTML when first added but does now that more tokens have
545
- // streamed in. injectPreviewButtonFor is idempotent so calling it
546
- // again is safe and cheap.
547
- codeElsToCheck.forEach(injectPreviewButtonFor);
548
- });
549
- }
550
-
551
- /* ── Send button: helpers ─────────────────────────────────────────────── */
552
- function getSendButton() {
553
- const wrap = document.querySelector("#send-btn");
554
- if (!wrap) return null;
555
- if (wrap.tagName === "BUTTON") return wrap;
556
- return wrap.querySelector("button");
557
- }
558
-
559
- function setSendDisabled(disabled) {
560
- const btn = getSendButton();
561
- if (!btn) return;
562
- if (disabled) {
563
- if (!btn.disabled) {
564
- btn.setAttribute("disabled", "");
565
- btn.disabled = true;
566
- }
567
- } else {
568
- if (btn.disabled || btn.hasAttribute("disabled")) {
569
- btn.removeAttribute("disabled");
570
- btn.disabled = false;
571
- }
572
- }
573
- }
574
-
575
- function streamMarkerPresent() {
576
- return !!document.querySelector(STREAM_MARKER_SELECTOR);
577
- }
578
-
579
- function idleMarkerPresent() {
580
- return !!document.querySelector(IDLE_MARKER_SELECTOR);
581
- }
582
-
583
- function isModelBusy() {
584
- const now = performance.now();
585
- // Awaiting the server's first frame after a dispatch — must remain
586
- // busy regardless of marker state, with a hard timeout to avoid a
587
- // permanently-stuck button if the request silently failed.
588
- if (awaitingFirstFrame && now - lastSendAt < FIRST_FRAME_TIMEOUT_MS) {
589
- return true;
590
- }
591
- if (now - lastSendAt < SEND_DEBOUNCE_MS) return true;
592
- return streamMarkerPresent();
593
- }
594
-
595
- /* ── Busy toast ───────────────────────────────────────────────────────── */
596
- function ensureBusyToastHost() {
597
- let host = document.getElementById("hy-busy-toast-host");
598
- if (host) return host;
599
- host = document.createElement("div");
600
- host.id = "hy-busy-toast-host";
601
- Object.assign(host.style, {
602
- position: "fixed",
603
- top: "24px",
604
- left: "50%",
605
- transform: "translateX(-50%)",
606
- zIndex: "9999",
607
- display: "flex",
608
- flexDirection: "column",
609
- gap: "8px",
610
- pointerEvents: "none",
611
- });
612
- document.body.appendChild(host);
613
- return host;
614
- }
615
-
616
- let lastToastAt = 0;
617
- function showBusyToast() {
618
- const now = performance.now();
619
- if (now - lastToastAt < 800) return;
620
- lastToastAt = now;
621
-
622
- const host = ensureBusyToastHost();
623
- const toast = document.createElement("div");
624
- toast.className = "hy-busy-toast";
625
- toast.textContent = BUSY_MSG;
626
- Object.assign(toast.style, {
627
- background: "#1f2937",
628
- color: "#fff",
629
- padding: "10px 16px",
630
- borderRadius: "8px",
631
- boxShadow: "0 6px 20px rgba(0,0,0,0.25)",
632
- fontSize: "14px",
633
- maxWidth: "min(92vw, 520px)",
634
- lineHeight: "1.4",
635
- opacity: "0",
636
- transition: "opacity 180ms ease",
637
- pointerEvents: "none",
638
- });
639
- host.appendChild(toast);
640
- requestAnimationFrame(() => {
641
- toast.style.opacity = "1";
642
- });
643
- setTimeout(() => {
644
- toast.style.opacity = "0";
645
- setTimeout(() => toast.remove(), 250);
646
- }, 3200);
647
- }
648
-
649
- /* ── Send-button busy guard ───────────────────────────────────────────── */
650
- // Three-layer disable signal, in priority order:
651
- //
652
- // 1. SEND_DEBOUNCE_MS round-trip window after the user dispatched
653
- // → disabled (prevents fast double-click before first server
654
- // frame arrives).
655
- // 2. #hy-busy-marker contains [data-hy-streaming="1"]
656
- // → disabled (server is actively streaming).
657
- // 3. Chat DOM has been *completely* idle for CHAT_IDLE_MS AND
658
- // we're past CHAT_IDLE_MIN_SINCE_SEND_MS since dispatch
659
- // → soft override: force-clear the marker and enable. Safety
660
- // net for "the server's IDLE marker frame was lost on the
661
- // wire".
662
- let busyStartedAt = 0;
663
- let lastChatMutationAt = 0;
664
-
665
- function syncSendButtonState() {
666
- const marker = streamMarkerPresent();
667
- const now = performance.now();
668
-
669
- if (marker) {
670
- // First server frame has arrived — stop relying on the dispatch
671
- // timer and hand over to the marker as the source of truth.
672
- awaitingFirstFrame = false;
673
- if (busyStartedAt === 0) busyStartedAt = now;
674
-
675
- const sinceSend = now - lastSendAt;
676
- const sinceMutation = now - lastChatMutationAt;
677
- if (
678
- lastSendAt > 0 &&
679
- sinceSend > CHAT_IDLE_MIN_SINCE_SEND_MS &&
680
- sinceMutation > CHAT_IDLE_MS
681
- ) {
682
- console.warn(
683
- "[hy] Marker stuck while chat DOM idle for",
684
- (sinceMutation / 1000).toFixed(1),
685
- "s — force-clearing (server IDLE frame likely lost)."
686
- );
687
- document.querySelectorAll(STREAM_MARKER_SELECTOR).forEach((el) => {
688
- el.removeAttribute("data-hy-streaming");
689
- });
690
- busyStartedAt = 0;
691
- setSendDisabled(false);
692
- return;
693
- }
694
-
695
- if (now - busyStartedAt > ABSOLUTE_BUSY_TIMEOUT_MS) {
696
- console.warn(
697
- "[hy] Stream marker stuck >",
698
- ABSOLUTE_BUSY_TIMEOUT_MS / 1000,
699
- "s — force-enabling Send."
700
- );
701
- document.querySelectorAll(STREAM_MARKER_SELECTOR).forEach((el) => {
702
- el.removeAttribute("data-hy-streaming");
703
- });
704
- busyStartedAt = 0;
705
- setSendDisabled(false);
706
- return;
707
- }
708
-
709
- setSendDisabled(true);
710
- return;
711
- }
712
-
713
- busyStartedAt = 0;
714
-
715
- // Marker is idle. We may still be waiting for the FIRST frame after
716
- // a freshly-dispatched send: keep the button disabled so a second
717
- // click can't slip through during the initial request round-trip.
718
- if (awaitingFirstFrame) {
719
- // If the marker host explicitly contains the IDLE marker, the
720
- // server has already responded — typically a short-circuit path
721
- // like the functions-JSON validation failure in send_message,
722
- // which yields HY_IDLE_HTML without ever yielding HY_BUSY_HTML.
723
- // Without this branch, awaitingFirstFrame would stay set until
724
- // FIRST_FRAME_TIMEOUT_MS (30s), causing the next Enter/click to
725
- // wrongly trigger the "model is busy" toast.
726
- if (idleMarkerPresent()) {
727
- awaitingFirstFrame = false;
728
- } else if (now - lastSendAt > FIRST_FRAME_TIMEOUT_MS) {
729
- console.warn(
730
- "[hy] First-frame timeout — re-enabling Send (request likely failed)."
731
- );
732
- awaitingFirstFrame = false;
733
- setSendDisabled(false);
734
- return;
735
- } else {
736
- setSendDisabled(true);
737
- return;
738
- }
739
- }
740
-
741
- if (now - lastSendAt < SEND_DEBOUNCE_MS) {
742
- setSendDisabled(true);
743
- return;
744
- }
745
- setSendDisabled(false);
746
- }
747
-
748
- function installBusyGuard() {
749
- const sendBtn = document.querySelector("#send-btn");
750
- const textarea = document.querySelector(".msg-input textarea");
751
- const markerHost = document.getElementById(BUSY_MARKER_HOST_ID);
752
- const chatEl = document.querySelector(CHAT_SELECTOR);
753
- if (!sendBtn || !textarea || !markerHost || !chatEl) {
754
- setTimeout(installBusyGuard, 500);
755
- return;
756
- }
757
-
758
- // Track chat DOM activity for the soft watchdog above. ANY mutation
759
- // refreshes the timestamp — including post-stream KaTeX/highlight
760
- // injection — because if anything is still rendering the streaming
761
- // MIGHT not be over yet. The watchdog only fires after a long quiet
762
- // period.
763
- lastChatMutationAt = performance.now();
764
- subscribeChatMutations(() => {
765
- lastChatMutationAt = performance.now();
766
- });
767
-
768
- // Heartbeats — the server emits an empty-ops delta every ~5s during
769
- // long thinking pauses to keep the SSE/WebSocket connection alive
770
- // (see ``_HEARTBEAT_INTERVAL`` in core/chat.py). Empty-ops deltas
771
- // do NOT mutate ``#hy-chat`` so the MutationObserver above never
772
- // sees them, but they DO trigger the ``hy-chat:updated`` event that
773
- // applyDelta dispatches on every payload. Without this listener the
774
- // watchdog would mistakenly fire during legitimate thinking pauses
775
- // (especially with two windows in flight, where the per-request
776
- // first-content latency is higher) and unlock Send while the stream
777
- // is still running — letting the user dispatch a duplicate turn.
778
- chatEl.addEventListener("hy-chat:updated", () => {
779
- lastChatMutationAt = performance.now();
780
- });
781
-
782
- // Block clicks while busy. Capture phase + stopImmediatePropagation
783
- // ensures Gradio's own bubble-phase click handler never sees the
784
- // event when we want to reject it.
785
- //
786
- // CRITICAL: in the NOT-busy path we do NOT call setSendDisabled(true)
787
- // here. Gradio's Svelte Button checks ``this.disabled`` inside its
788
- // own click handler that runs AFTER ours; if we flip the disabled
789
- // attribute synchronously in capture, the actual send request is
790
- // silently swallowed.
791
- // Disable the Send button on the NEXT tick (after Gradio's own
792
- // bubble-phase click handler has run and dispatched the request).
793
- // We can't disable synchronously in capture or Gradio's Svelte
794
- // Button checks ``this.disabled`` and silently swallows the send.
795
- function lockSendAfterDispatch() {
796
- setTimeout(() => {
797
- setSendDisabled(true);
798
- }, 0);
799
- }
800
-
801
- sendBtn.addEventListener(
802
- "click",
803
- (e) => {
804
- if (isModelBusy()) {
805
- e.preventDefault();
806
- e.stopImmediatePropagation();
807
- e.stopPropagation();
808
- showBusyToast();
809
- return;
810
- }
811
- lastSendAt = performance.now();
812
- awaitingFirstFrame = true;
813
- justSentNewMessage = true;
814
- lockSendAfterDispatch();
815
- },
816
- true
817
- );
818
-
819
- // Same logic for Enter-to-submit on the textarea. Gradio's
820
- // Textbox.submit fires regardless of the Send button's disabled
821
- // state, so we ALSO need this gate.
822
- textarea.addEventListener(
823
- "keydown",
824
- (e) => {
825
- if (e.key !== "Enter" || e.shiftKey || e.isComposing) return;
826
- if (isModelBusy()) {
827
- e.preventDefault();
828
- e.stopImmediatePropagation();
829
- e.stopPropagation();
830
- showBusyToast();
831
- return;
832
- }
833
- lastSendAt = performance.now();
834
- awaitingFirstFrame = true;
835
- justSentNewMessage = true;
836
- lockSendAfterDispatch();
837
- },
838
- true
839
- );
840
-
841
- // Drive the busy state from mutations of the dedicated marker host.
842
- // Tiny subtree (one component, one inner span at most) so this is
843
- // essentially free.
844
- new MutationObserver(syncSendButtonState).observe(markerHost, {
845
- childList: true,
846
- subtree: true,
847
- characterData: true,
848
- attributes: true,
849
- attributeFilter: ["data-hy-streaming", "data-hy-busy"],
850
- });
851
-
852
- // Slow heartbeat in case the MutationObserver misses something.
853
- setInterval(syncSendButtonState, 500);
854
-
855
- // Initial sync.
856
- syncSendButtonState();
857
- }
858
-
859
- /* ── Follow-the-tail scroller ─────────────────────────────────────────── */
860
- // Design (simple & robust):
861
- // * ``follow`` mirrors "user is currently near the bottom of the
862
- // chat". It is updated on EVERY scroll event — both user-driven
863
- // and programmatic — because after our own ``scrollToBottom`` the
864
- // position IS at the bottom, so follow stays true automatically.
865
- // * When the user scrolls UP, the very next scroll event flips
866
- // follow to false; subsequent mutations don't re-pin the view.
867
- // * When the user is actively scrolling (wheel / touchmove /
868
- // scroll-key) we briefly pause programmatic scrolling so we don't
869
- // fight their fingers; once they let go (no gesture for
870
- // USER_QUIET_MS) auto-follow resumes if they ended near bottom.
871
- // * Two redundant triggers ensure we never miss a delta: the global
872
- // chat MutationObserver AND the explicit ``hy-chat:updated``
873
- // custom event dispatched by static/chat.js after each delta is
874
- // applied.
875
- function installScrollLock() {
876
- const chatEl = document.querySelector(CHAT_SELECTOR);
877
- if (!chatEl) {
878
- setTimeout(installScrollLock, 500);
879
- return;
880
- }
881
-
882
- const NEAR_BOTTOM_PX = 100;
883
- const USER_QUIET_MS = 120;
884
-
885
- let scrollable = null;
886
- let follow = true;
887
- let lastUserGestureAt = 0;
888
-
889
- function markUserGesture() {
890
- lastUserGestureAt = performance.now();
891
- }
892
- window.addEventListener("wheel", markUserGesture, {
893
- passive: true,
894
- capture: true,
895
- });
896
- window.addEventListener("touchmove", markUserGesture, {
897
- passive: true,
898
- capture: true,
899
- });
900
- const SCROLL_KEYS = new Set([
901
- "ArrowUp",
902
- "ArrowDown",
903
- "PageUp",
904
- "PageDown",
905
- "Home",
906
- "End",
907
- "Space",
908
- " ",
909
- ]);
910
- window.addEventListener(
911
- "keydown",
912
- (e) => {
913
- if (SCROLL_KEYS.has(e.key)) markUserGesture();
914
- },
915
- { capture: true }
916
- );
917
-
918
- function isNearBottom(el) {
919
- return el.scrollHeight - el.scrollTop - el.clientHeight < NEAR_BOTTOM_PX;
920
- }
921
-
922
- function onScroll() {
923
- if (!scrollable) return;
924
- // Always reflect the current viewport state. After programmatic
925
- // scrollToBottom the position IS at the bottom, so follow=true.
926
- // After user scrolls up, follow flips to false on the very next
927
- // scroll event. Simple and self-correcting.
928
- follow = isNearBottom(scrollable);
929
- }
930
-
931
- function scrollToBottom() {
932
- if (!scrollable) return;
933
- // Don't fight an active user gesture. Once they stop (>120ms of
934
- // silence) we'll catch up on the very next mutation OR custom
935
- // event — both of which fire for every streamed delta.
936
- if (performance.now() - lastUserGestureAt < USER_QUIET_MS) return;
937
- const target = scrollable.scrollHeight - scrollable.clientHeight;
938
- if (target > scrollable.scrollTop) scrollable.scrollTop = target;
939
- }
940
-
941
- function findScrollable() {
942
- // Our chat element IS itself the scrollable container (see
943
- // _chat.css). Attach to it eagerly even before content overflows
944
- // so we catch the first scroll event the moment overflow appears.
945
- const s = getComputedStyle(chatEl);
946
- if (s.overflowY === "auto" || s.overflowY === "scroll") return chatEl;
947
- // Fallback for unexpected layouts: walk the subtree.
948
- const candidates = chatEl.querySelectorAll("*");
949
- for (const el of candidates) {
950
- const cs = getComputedStyle(el);
951
- if (cs.overflowY === "auto" || cs.overflowY === "scroll") {
952
- if (el.scrollHeight > el.clientHeight + 4) return el;
953
- }
954
- }
955
- return chatEl;
956
- }
957
-
958
- function attach(el) {
959
- if (scrollable === el) return;
960
- if (scrollable) scrollable.removeEventListener("scroll", onScroll);
961
- scrollable = el;
962
- el.addEventListener("scroll", onScroll, { passive: true });
963
- if (follow) scrollToBottom();
964
- }
965
-
966
- // Attach immediately — chat.js may not have any content yet but the
967
- // element exists, so we can already wire the scroll listener.
968
- attach(findScrollable());
969
-
970
- function tick() {
971
- if (justSentNewMessage) {
972
- follow = true;
973
- justSentNewMessage = false;
974
- }
975
- if (!scrollable || !document.contains(scrollable)) {
976
- attach(findScrollable());
977
- }
978
- if (scrollable && follow) {
979
- scrollToBottom();
980
- }
981
- }
982
-
983
- // Trigger 1: the global chat MutationObserver hub. Catches every
984
- // DOM change under #hy-chat (delta-driven appends, frozen flush,
985
- // streaming innerHTML swaps, post-stream highlight injection).
986
- subscribeChatMutations(tick);
987
-
988
- // Trigger 2: the explicit ``hy-chat:updated`` event dispatched by
989
- // static/chat.js at the end of every applyDelta. Belt-and-braces
990
- // in case some pathological mutation pattern slips past the rAF
991
- // batcher above.
992
- chatEl.addEventListener("hy-chat:updated", tick);
993
-
994
- // Trigger 3: window resize can change clientHeight which makes the
995
- // previous "at bottom" position no longer at bottom. Re-pin.
996
- window.addEventListener("resize", () => {
997
- if (scrollable && follow) scrollToBottom();
998
- });
999
- }
1000
-
1001
- /* ── kick off ─────────────────────────────────────────────────────────── */
1002
- function bootstrap() {
1003
- const chatEl = document.querySelector(CHAT_SELECTOR);
1004
- if (!chatEl) {
1005
- // chat.js renders #hy-chat into the gr.HTML wrapper; until that
1006
- // happens we can't install observers. Retry until ready —
1007
- // subsystems below all guard for the same node themselves but
1008
- // the mutation hub MUST be running before they install their
1009
- // subscribers — otherwise early mutations would be missed.
1010
- setTimeout(bootstrap, 200);
1011
- return;
1012
- }
1013
- startChatMutationHub(chatEl);
1014
- createOverlay();
1015
- watchForHtmlBlocks();
1016
- installScrollLock();
1017
- installBusyGuard();
1018
- }
1019
- setTimeout(bootstrap, 800);
1020
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/chat.js DELETED
@@ -1,759 +0,0 @@
1
- /* eslint-disable */
2
- /**
3
- * Hy3 custom chat renderer.
4
- *
5
- * The DOM under ``#hy-chat`` is owned exclusively by this module —
6
- * nothing in Gradio touches it. The server pushes JSON deltas via the
7
- * hidden ``#hy-chat-delta`` ``gr.HTML`` component; we observe its
8
- * mutations, parse, and apply each op to the chat container.
9
- *
10
- * Design notes
11
- * ────────────
12
- * 1. Append-only DOM. Each new bubble is its own ``.message-row``.
13
- * The browser's per-row layout/paint isolation (``contain`` +
14
- * ``content-visibility`` from ``_perf.css``) keeps rendering cost
15
- * O(visible) regardless of conversation length.
16
- *
17
- * 2. Frozen / streaming split inside each assistant bubble. As content
18
- * streams in, every paragraph (or fenced code / display math) that
19
- * has CLOSED gets rendered ONCE into a stable ``.hy-frozen`` DOM
20
- * subtree and never touched again — KaTeX, ``marked``, and the
21
- * syntax highlighter all run exactly once per closed block. Only
22
- * the trailing in-progress paragraph re-renders on each delta,
23
- * and that block is small. As a result already-rendered formulas
24
- * never flicker / re-layout mid-stream.
25
- *
26
- * 3. Wire payload is ~just the new characters. A 100-char delta is
27
- * ~150 bytes on the wire, so WebSocket back-pressure essentially
28
- * goes away.
29
- *
30
- * Op contract — keep in sync with chat.py:
31
- *
32
- * {type: "reset"}
33
- * {type: "user", text: "..."}
34
- * {type: "assistant_begin", id: "a-N"}
35
- * {type: "reasoning_delta", id: "a-N", delta: "..."}
36
- * {type: "content_delta", id: "a-N", delta: "...", thinking_done?: bool}
37
- * {type: "assistant_end", id: "a-N"}
38
- * {type: "tool_call", id: "a-N", markdown: "..."}
39
- * {type: "remove_bubble", id: "a-N"}
40
- */
41
- (() => {
42
- if (window.__HY_CHAT_INIT) return;
43
- window.__HY_CHAT_INIT = true;
44
-
45
- // ─── State ─────────────────────────────────────────────────────────────
46
- const state = {
47
- host: null, // <div id="hy-chat">
48
- bubbles: new Map(), // bubble_id -> bubble record (see ensureBubble)
49
- lastSeq: 0, // de-dup gate against re-fires of the same payload
50
- // Current chat epoch — bumped server-side every time the user clicks
51
- // "+" / new_chat. Each delta payload carries the epoch it was
52
- // produced under; deltas whose epoch doesn't match ours are dropped
53
- // (they're tail bytes from a stream the user already abandoned).
54
- // Initialised lazily from the first payload that arrives so legacy
55
- // payloads without an epoch field still apply.
56
- currentEpoch: null,
57
- };
58
-
59
- // ─── DOM helpers ───────────────────────────────────────────────────────
60
- function ensureHost() {
61
- if (state.host && document.contains(state.host)) return state.host;
62
- state.host = document.getElementById("hy-chat");
63
- return state.host;
64
- }
65
-
66
- function escapeHtml(s) {
67
- return String(s)
68
- .replace(/&/g, "&amp;")
69
- .replace(/</g, "&lt;")
70
- .replace(/>/g, "&gt;")
71
- .replace(/"/g, "&quot;")
72
- .replace(/'/g, "&#39;");
73
- }
74
-
75
- // ─── Markdown + math renderer ──────────────────────────────────────────
76
- // We DELIBERATELY do not sanitize here. The rest of the app depends
77
- // on the model being able to emit raw HTML (e.g.
78
- // <details class="thinking-block">, the HTML-preview workflow on raw
79
- // HTML code blocks). The model is trusted output for this product
80
- // surface; if that ever changes, swap marked for a sanitizing parser
81
- // like markdown-it + DOMPurify.
82
- const MARKED_OPTS = { gfm: true, breaks: false };
83
-
84
- const KATEX_DELIMS = [
85
- { left: "$$", right: "$$", display: true },
86
- { left: "$", right: "$", display: false },
87
- // We pre-convert \[…\] / \(…\) → $$…$$ / $…$ via normalizeMathDelims
88
- // BEFORE marked.parse runs (see below) — so the auto-render scan
89
- // never needs to worry about them. They are NOT listed here on
90
- // purpose: marked treats ``\[`` and ``\(`` as backslash escapes
91
- // and silently drops the leading ``\`` in the rendered HTML, so by
92
- // the time KaTeX scans the DOM the original bracket delimiters are
93
- // already gone. Pre-conversion is the only reliable fix.
94
- ];
95
-
96
- // ─── Math handling: two-stage pipeline ────────────────────────────────
97
- //
98
- // The model emits LaTeX in any of four delimiter styles: ``$$…$$``,
99
- // ``\[…\]``, ``$…$``, ``\(…\)``. We need KaTeX (via auto-render) to
100
- // pick all of them up while still letting ``marked.parse`` handle the
101
- // surrounding markdown.
102
- //
103
- // Two problems block the naïve approach:
104
- //
105
- // 1. ``marked`` treats ``\[``, ``\]``, ``\(``, ``\)`` as backslash
106
- // escapes and silently drops the ``\``. By the time KaTeX scans
107
- // the rendered HTML the bracket delimiters are gone.
108
- //
109
- // 2. Even inside ``$$…$$``, ``marked`` strips backslashes before
110
- // ``\{``, ``\}``, ``\_``, etc. — so things like ``\{(0:0:1:0)\}``
111
- // lose their literal-brace escapes and KaTeX renders an empty
112
- // group instead of the intended literal braces.
113
- //
114
- // The fix is two passes that together keep marked away from the math
115
- // content:
116
- //
117
- // Stage A — ``normalizeMathDelims`` converts ``\[/\]`` → ``$$`` and
118
- // ``\(/\)`` → ``$`` OUTSIDE fenced/inline code blocks. After this
119
- // pass every math region is delimited with dollars.
120
- //
121
- // Stage B — ``protectMath`` extracts every ``$$…$$`` / ``$…$`` from
122
- // the text and replaces it with a unique placeholder token (a NUL
123
- // character that ``marked`` will leave alone). After ``marked.parse``
124
- // we substitute the original math source back in. ``ignoredTags``
125
- // already keeps KaTeX out of ``<pre>``/``<code>``, so the only
126
- // regions that end up containing dollars in the rendered HTML are
127
- // the math ones — exactly what we want.
128
- //
129
- // The placeholder uses NUL as a delimiter because ``marked`` cannot
130
- // produce a literal NUL on its own (every input source we'd see is
131
- // valid Unicode text without NULs), so the ``restoreMath`` regex is
132
- // unambiguous.
133
- function normalizeMathDelims(text) {
134
- if (!text) return text;
135
- let out = "";
136
- let i = 0;
137
- const n = text.length;
138
- while (i < n) {
139
- const ch = text[i];
140
-
141
- // Triple-backtick or triple-tilde fenced block — passthrough until
142
- // matching closing fence on its own (or anywhere on a) line.
143
- if ((ch === "`" && text.startsWith("```", i)) ||
144
- (ch === "~" && text.startsWith("~~~", i))) {
145
- const marker = text.substr(i, 3);
146
- out += marker;
147
- i += 3;
148
- const close = text.indexOf(marker, i);
149
- if (close < 0) {
150
- // Unclosed fence — emit rest verbatim and stop.
151
- out += text.slice(i);
152
- return out;
153
- }
154
- out += text.slice(i, close + 3);
155
- i = close + 3;
156
- continue;
157
- }
158
-
159
- // Inline code: single or double backticks, ended by the same run
160
- // length. Common case is a single backtick.
161
- if (ch === "`") {
162
- let runLen = 1;
163
- while (i + runLen < n && text[i + runLen] === "`") runLen++;
164
- const open = text.substr(i, runLen);
165
- out += open;
166
- i += runLen;
167
- const close = text.indexOf(open, i);
168
- if (close < 0) {
169
- out += text.slice(i);
170
- return out;
171
- }
172
- out += text.slice(i, close + runLen);
173
- i = close + runLen;
174
- continue;
175
- }
176
-
177
- // Backslash-bracket / backslash-paren delimiters. Must be the
178
- // FIRST conversion check after the code-fence skip so we don't
179
- // mistake e.g. ``\[`` inside a code fence for math.
180
- if (ch === "\\") {
181
- if (text[i + 1] === "[") { out += "$$"; i += 2; continue; }
182
- if (text[i + 1] === "]") { out += "$$"; i += 2; continue; }
183
- if (text[i + 1] === "(") { out += "$"; i += 2; continue; }
184
- if (text[i + 1] === ")") { out += "$"; i += 2; continue; }
185
- }
186
-
187
- out += ch;
188
- i++;
189
- }
190
- return out;
191
- }
192
-
193
- // Inline math regex requires the opening ``$`` to be IMMEDIATELY
194
- // followed by a non-space, non-``$`` char (avoids matching prices like
195
- // ``I have $5 and $10``) and the closing ``$`` to be IMMEDIATELY
196
- // preceded by a non-space, non-``$`` char. Display math (``$$…$$``)
197
- // is matched first (the alternative is left-most-greedy in a JS
198
- // regex, but with non-greedy bodies and ``$$`` being a longer prefix
199
- // it wins ties naturally).
200
- const MATH_RE = /\$\$[\s\S]+?\$\$|\$(?=[^\s$])[^\n$]*?(?<=[^\s$])\$/g;
201
-
202
- function protectMath(text) {
203
- const stash = [];
204
- const escaped = text.replace(MATH_RE, (m) => {
205
- const idx = stash.length;
206
- stash.push(m);
207
- return `\u0000M${idx}\u0000`;
208
- });
209
- return { escaped, stash };
210
- }
211
-
212
- function restoreMath(html, stash) {
213
- if (!stash.length) return html;
214
- return html.replace(/\u0000M(\d+)\u0000/g, (_, n) => stash[+n]);
215
- }
216
-
217
- function renderMarkdownInto(el, text) {
218
- if (!text) {
219
- el.innerHTML = "";
220
- return;
221
- }
222
- const normalized = normalizeMathDelims(text);
223
- const { escaped, stash } = protectMath(normalized);
224
- const rawMd = (window.marked && window.marked.parse)
225
- ? window.marked.parse(escaped, MARKED_OPTS)
226
- : escapeHtml(escaped);
227
- el.innerHTML = restoreMath(rawMd, stash);
228
-
229
- // Math
230
- if (window.renderMathInElement) {
231
- try {
232
- window.renderMathInElement(el, {
233
- delimiters: KATEX_DELIMS,
234
- throwOnError: false,
235
- // Skip math-detection inside elements that should never contain
236
- // math (matches KaTeX auto-render defaults).
237
- ignoredTags: ["script", "noscript", "style", "textarea", "pre", "code"],
238
- });
239
- } catch (err) {
240
- console.warn("[hy-chat] katex render error:", err);
241
- }
242
- }
243
-
244
- // Syntax highlighting (idempotent if hljs not loaded yet)
245
- if (window.hljs && window.hljs.highlightElement) {
246
- const codes = el.querySelectorAll("pre code");
247
- for (let i = 0; i < codes.length; i++) {
248
- const c = codes[i];
249
- if (c.dataset.hljsDone) continue;
250
- try {
251
- window.hljs.highlightElement(c);
252
- } catch (err) {
253
- // hljs throws if the language is unknown — harmless, ignore.
254
- }
255
- c.dataset.hljsDone = "1";
256
- }
257
- }
258
- }
259
-
260
- // ─── Frozen / streaming split ──────────────────────────────────────────
261
- // Find the highest "safe" boundary in the current content buffer up to
262
- // which we can freeze the rendered DOM and never touch it again. A
263
- // boundary is safe iff the markdown above it forms a complete set of
264
- // top-level blocks — i.e. we are NOT inside an open fenced code block
265
- // (``` / ~~~), display math ($$), or LaTeX env (\begin{…}).
266
- //
267
- // Returns the index in ``text`` immediately after the last safe block
268
- // separator (\n\n).
269
- function findFreezeBoundary(text) {
270
- const len = text.length;
271
- let i = 0;
272
- let inFence = false; // inside ``` … ``` or ~~~ … ~~~
273
- let fenceMarker = null;
274
- let inMath = false; // inside $$ … $$
275
- let inEnv = false; // inside \begin{…} … \end{…}
276
- let lastSafe = 0;
277
-
278
- while (i < len) {
279
- const ch = text[i];
280
-
281
- // Fence detection (must check before single-char paths)
282
- if (!inMath && !inEnv) {
283
- if (ch === "`" && text.startsWith("```", i)) {
284
- if (!inFence) { inFence = true; fenceMarker = "```"; }
285
- else if (fenceMarker === "```") { inFence = false; fenceMarker = null; }
286
- i += 3;
287
- continue;
288
- }
289
- if (ch === "~" && text.startsWith("~~~", i)) {
290
- if (!inFence) { inFence = true; fenceMarker = "~~~"; }
291
- else if (fenceMarker === "~~~") { inFence = false; fenceMarker = null; }
292
- i += 3;
293
- continue;
294
- }
295
- }
296
-
297
- if (inFence) { i++; continue; }
298
-
299
- // Display math $$ … $$
300
- if (!inEnv && ch === "$" && text[i + 1] === "$") {
301
- inMath = !inMath;
302
- i += 2;
303
- continue;
304
- }
305
-
306
- if (inMath) { i++; continue; }
307
-
308
- // \begin{env} … \end{env}
309
- if (ch === "\\") {
310
- if (text.startsWith("\\begin{", i)) { inEnv = true; i += 7; continue; }
311
- if (text.startsWith("\\end{", i)) { inEnv = false; i += 5; continue; }
312
- }
313
-
314
- if (inEnv) { i++; continue; }
315
-
316
- // Paragraph break outside any block — candidate freeze point.
317
- if (ch === "\n" && text[i + 1] === "\n") {
318
- // Skip past the entire run of blank lines so we don't freeze
319
- // mid-blank-paragraph.
320
- let j = i + 2;
321
- while (j < len && (text[j] === "\n" || text[j] === " " || text[j] === "\t")) j++;
322
- lastSafe = j;
323
- i = j;
324
- continue;
325
- }
326
-
327
- i++;
328
- }
329
-
330
- return lastSafe;
331
- }
332
-
333
- // ─── Bubble lifecycle ──────────────────────────────────────────────────
334
- function ensureBubble(id) {
335
- return state.bubbles.get(id);
336
- }
337
-
338
- // Re-fire ``hy-chat:updated`` so the scroll-lock in app.js reconsiders
339
- // whether to re-pin to the bottom now that geometry may have changed.
340
- // Coalesced via rAF so a flurry of layout changes (e.g. KaTeX +
341
- // hljs + table column-width pass all in the same frame) only
342
- // dispatches once.
343
- let _nudgePending = false;
344
- function nudgeScrollSoon() {
345
- if (_nudgePending) return;
346
- _nudgePending = true;
347
- requestAnimationFrame(() => {
348
- _nudgePending = false;
349
- if (!state.host) return;
350
- state.host.dispatchEvent(new CustomEvent("hy-chat:updated", {
351
- bubbles: false,
352
- }));
353
- });
354
- }
355
-
356
- // Forces ``row`` to flush any pending layout, then schedules a
357
- // single rAF-coalesced ``hy-chat:updated`` dispatch. Used both
358
- // when a ``<details>`` toggles and as the ``ResizeObserver``
359
- // callback for each row.
360
- function nudgeScrollAfterToggle(b) {
361
- if (!b || !b.row) return;
362
- void b.row.offsetHeight;
363
- nudgeScrollSoon();
364
- }
365
-
366
- // Per-row ResizeObserver. Many things that change a row's height
367
- // happen ASYNCHRONOUSLY after the streaming op that created the
368
- // content: KaTeX glyphs render once their fonts arrive, hljs
369
- // decorates code blocks on the next idle tick, browsers do a
370
- // second column-width pass on tables once all rows are in. None
371
- // of these go through ``applyDelta`` so the scroll-lock would
372
- // otherwise miss them, leaving the user stuck at a now-stale
373
- // ``scrollHeight - clientHeight``. Wiring a ResizeObserver per
374
- // row covers all of them with one mechanism.
375
- const _rowResizeObserver =
376
- typeof ResizeObserver !== "undefined"
377
- ? new ResizeObserver(() => nudgeScrollSoon())
378
- : null;
379
-
380
- function createAssistantBubble(id) {
381
- const row = document.createElement("div");
382
- row.className = "message-row bot-row";
383
- row.dataset.testid = "bot";
384
- row.dataset.role = "assistant";
385
- row.dataset.bubbleId = id;
386
-
387
- const msg = document.createElement("div");
388
- msg.className = "message bot";
389
-
390
- const md = document.createElement("div");
391
- md.className = "markdown hy-markdown";
392
-
393
- const typing = document.createElement("div");
394
- typing.className = "typing-indicator hy-typing";
395
- typing.innerHTML = "<span></span><span></span><span></span>";
396
- md.appendChild(typing);
397
-
398
- msg.appendChild(md);
399
- row.appendChild(msg);
400
- state.host.appendChild(row);
401
- if (_rowResizeObserver) _rowResizeObserver.observe(row);
402
-
403
- const record = {
404
- row, md, typing,
405
- // Reasoning (optional, lazily created)
406
- reasoningEl: null,
407
- reasoningSummaryEl: null,
408
- reasoningBodyEl: null,
409
- reasoningBuf: "",
410
- thinkingDone: false,
411
- // Content (lazily created on first content_delta)
412
- frozenEl: null,
413
- streamingEl: null,
414
- contentBuf: "",
415
- frozenLen: 0,
416
- // Tool-call append region (lazily created)
417
- toolCallContainer: null,
418
- ended: false,
419
- };
420
- state.bubbles.set(id, record);
421
- return record;
422
- }
423
-
424
- function ensureReasoning(b) {
425
- if (b.reasoningEl) return;
426
- const det = document.createElement("details");
427
- det.className = "thinking-block";
428
- det.open = true;
429
- const sum = document.createElement("summary");
430
- sum.textContent = (window.HY_I18N && window.HY_I18N.thinking) || "Thinking...";
431
- det.appendChild(sum);
432
- const body = document.createElement("div");
433
- body.className = "hy-reasoning-body";
434
- body.style.whiteSpace = "pre-wrap";
435
- body.style.wordBreak = "break-word";
436
- det.appendChild(body);
437
- b.reasoningEl = det;
438
- b.reasoningSummaryEl = sum;
439
- b.reasoningBodyEl = body;
440
- if (b.typing && b.typing.parentNode) b.typing.remove();
441
- // Reasoning always goes ABOVE content, even if content has already
442
- // started rendering (rare — most models stream reasoning first).
443
- b.md.insertBefore(det, b.md.firstChild);
444
- // Keep the parent chat's scroll geometry in sync whenever the user
445
- // (or markThinkingDone) toggles this block. See nudgeScrollAfterToggle.
446
- det.addEventListener("toggle", () => nudgeScrollAfterToggle(b));
447
- }
448
-
449
- function ensureContent(b) {
450
- if (b.frozenEl) return;
451
- if (b.typing && b.typing.parentNode) b.typing.remove();
452
- const frozen = document.createElement("div");
453
- frozen.className = "hy-frozen";
454
- const streaming = document.createElement("div");
455
- streaming.className = "hy-streaming";
456
- b.md.appendChild(frozen);
457
- b.md.appendChild(streaming);
458
- b.frozenEl = frozen;
459
- b.streamingEl = streaming;
460
- }
461
-
462
- function markThinkingDone(b) {
463
- if (!b.reasoningEl || b.thinkingDone) return;
464
- b.thinkingDone = true;
465
- b.reasoningSummaryEl.textContent =
466
- (window.HY_I18N && window.HY_I18N.thinking_done) || "Thought";
467
- b.reasoningEl.open = false;
468
- }
469
-
470
- // ─── Op handlers ───────────────────────────────────────────────────────
471
- function opReset() {
472
- if (_rowResizeObserver) {
473
- for (const b of state.bubbles.values()) {
474
- if (b.row) _rowResizeObserver.unobserve(b.row);
475
- }
476
- }
477
- state.host.innerHTML = "";
478
- state.bubbles.clear();
479
- state.lastSeq = 0;
480
- // currentEpoch is updated by applyDelta BEFORE op dispatch when a
481
- // reset op is in the payload — see the epoch handling there. We
482
- // intentionally do NOT clear it here, otherwise we'd reopen the
483
- // window for stale post-reset deltas.
484
- }
485
-
486
- function opUser(op) {
487
- const row = document.createElement("div");
488
- row.className = "message-row user-row";
489
- row.dataset.testid = "user";
490
- row.dataset.role = "user";
491
-
492
- const msg = document.createElement("div");
493
- msg.className = "message user user-message";
494
-
495
- const md = document.createElement("div");
496
- md.className = "markdown hy-markdown hy-user-text";
497
- // Render as plain text — never run user input through marked / KaTeX.
498
- md.style.whiteSpace = "pre-wrap";
499
- md.style.wordBreak = "break-word";
500
- md.textContent = op.text || "";
501
-
502
- msg.appendChild(md);
503
- row.appendChild(msg);
504
- state.host.appendChild(row);
505
- }
506
-
507
- function opAssistantBegin(op) {
508
- if (state.bubbles.has(op.id)) return; // idempotent on retry
509
- createAssistantBubble(op.id);
510
- }
511
-
512
- function opReasoningDelta(op) {
513
- let b = ensureBubble(op.id);
514
- if (!b) b = createAssistantBubble(op.id);
515
- ensureReasoning(b);
516
- const d = op.delta || "";
517
- if (!d) return;
518
- b.reasoningBuf += d;
519
- // Reasoning is shown as raw text — fast and never re-renders math.
520
- // Append-only via textContent means even very long reasoning stays
521
- // O(delta) per update; the DOM tree is just a single text node.
522
- b.reasoningBodyEl.textContent = b.reasoningBuf;
523
- // Keep the latest reasoning line visible inside the (max-height-
524
- // scrollable) details body.
525
- b.reasoningBodyEl.scrollTop = b.reasoningBodyEl.scrollHeight;
526
- }
527
-
528
- function opContentDelta(op) {
529
- let b = ensureBubble(op.id);
530
- if (!b) b = createAssistantBubble(op.id);
531
- if (op.thinking_done) markThinkingDone(b);
532
- ensureContent(b);
533
- const d = op.delta || "";
534
- if (!d) return;
535
- b.contentBuf += d;
536
-
537
- // Compute new freeze boundary. If it advanced past the last frozen
538
- // length, render JUST the newly-frozen chunk (text since last
539
- // boundary) ONCE and APPEND its DOM to the frozen container. The
540
- // already-frozen DOM is never touched — KaTeX, hljs, and marked
541
- // run exactly once per closed block.
542
- //
543
- // Why we can render the new chunk in isolation: the boundary is
544
- // always at a paragraph break (\n\n) outside any open code fence /
545
- // display math / LaTeX env, so each chunk is a complete set of
546
- // top-level markdown blocks that ``marked`` parses identically
547
- // whether or not it has the prior context.
548
- const text = b.contentBuf;
549
- const newBoundary = findFreezeBoundary(text);
550
- if (newBoundary > b.frozenLen) {
551
- const newChunk = text.slice(b.frozenLen, newBoundary);
552
- const temp = document.createElement("div");
553
- renderMarkdownInto(temp, newChunk);
554
- // Move children from temp into frozenEl. Moving (not cloning)
555
- // preserves the KaTeX/hljs work we just did, and crucially does
556
- // NOT re-trigger any rendering of the already-frozen DOM.
557
- while (temp.firstChild) {
558
- b.frozenEl.appendChild(temp.firstChild);
559
- }
560
- b.frozenLen = newBoundary;
561
- }
562
- // Streaming segment (everything after the boundary) is re-rendered
563
- // in full on each delta. It's bounded to roughly one paragraph, so
564
- // the KaTeX / marked / hljs work is O(paragraph) — and since
565
- // ``innerHTML = ...`` wipes the previous DOM, KaTeX never
566
- // double-renders the same formula here either.
567
- const streamingText = text.slice(b.frozenLen);
568
- renderMarkdownInto(b.streamingEl, streamingText);
569
- }
570
-
571
- function opAssistantEnd(op) {
572
- const b = ensureBubble(op.id);
573
- if (!b) return;
574
- b.ended = true;
575
- if (b.typing && b.typing.parentNode) b.typing.remove();
576
- // Defer one extra nudge to the next animation frame: post-stream
577
- // KaTeX/hljs decoration of the just-flushed tail can run after
578
- // applyDelta returns, growing the bubble's height beyond what
579
- // ``hy-chat:updated`` already saw at the end of this op.
580
- nudgeScrollSoon();
581
- // Final flush: take whatever is still in the streaming segment and
582
- // append it to the frozen container as one last chunk, then empty
583
- // streaming. Crucially we do NOT re-render the already-frozen DOM
584
- // — that would re-run KaTeX over every formula the user has been
585
- // looking at and produce a final visible flicker.
586
- if (b.contentBuf) {
587
- ensureContent(b);
588
- const tail = b.contentBuf.slice(b.frozenLen);
589
- if (tail) {
590
- const temp = document.createElement("div");
591
- renderMarkdownInto(temp, tail);
592
- while (temp.firstChild) {
593
- b.frozenEl.appendChild(temp.firstChild);
594
- }
595
- }
596
- b.frozenLen = b.contentBuf.length;
597
- b.streamingEl.innerHTML = "";
598
- } else if (b.streamingEl) {
599
- // No content at all (might be tool-call only) — clean up.
600
- b.streamingEl.innerHTML = "";
601
- }
602
- if (b.reasoningEl && !b.thinkingDone) markThinkingDone(b);
603
- }
604
-
605
- function opToolCall(op) {
606
- let b = ensureBubble(op.id);
607
- if (!b) b = createAssistantBubble(op.id);
608
- if (b.typing && b.typing.parentNode) b.typing.remove();
609
- if (!b.toolCallContainer) {
610
- const c = document.createElement("div");
611
- c.className = "hy-tool-calls";
612
- b.md.appendChild(c);
613
- b.toolCallContainer = c;
614
- }
615
- const tc = document.createElement("div");
616
- tc.className = "hy-tool-call";
617
- renderMarkdownInto(tc, op.markdown || "");
618
- b.toolCallContainer.appendChild(tc);
619
- }
620
-
621
- function opRemoveBubble(op) {
622
- const b = ensureBubble(op.id);
623
- if (!b) return;
624
- if (_rowResizeObserver && b.row) _rowResizeObserver.unobserve(b.row);
625
- if (b.row && b.row.parentNode) b.row.parentNode.removeChild(b.row);
626
- state.bubbles.delete(op.id);
627
- }
628
-
629
- function applyOp(op) {
630
- if (!op || !op.type) return;
631
- if (!ensureHost()) return;
632
- switch (op.type) {
633
- case "reset": opReset(); break;
634
- case "user": opUser(op); break;
635
- case "assistant_begin": opAssistantBegin(op); break;
636
- case "reasoning_delta": opReasoningDelta(op); break;
637
- case "content_delta": opContentDelta(op); break;
638
- case "assistant_end": opAssistantEnd(op); break;
639
- case "tool_call": opToolCall(op); break;
640
- case "remove_bubble": opRemoveBubble(op); break;
641
- default:
642
- console.warn("[hy-chat] unknown op type:", op.type);
643
- }
644
- }
645
-
646
- function applyDelta(payload) {
647
- if (!payload || typeof payload !== "object") return;
648
- const seq = typeof payload.seq === "number" ? payload.seq : null;
649
- // Dedup: Gradio occasionally re-fires the same component value on
650
- // reconnect. Since each delta has a fresh global seq, anything ≤
651
- // lastSeq is a re-fire we've already applied.
652
- if (seq !== null && seq <= state.lastSeq) return;
653
- if (seq !== null) state.lastSeq = seq;
654
- const ops = Array.isArray(payload.ops) ? payload.ops : [];
655
-
656
- // ── Stale-epoch drop ────────────────────────────────────────────
657
- // The user clicked "+" mid-stream. The reset payload carries a NEW
658
- // epoch; we adopt it and apply the reset. Any later payload from
659
- // the cancelled generator still carries the OLD epoch and is
660
- // dropped here — without this, late content_delta ops would
661
- // ``createAssistantBubble`` for the old bubble id and a ghost
662
- // bubble would appear in the freshly-cleared chat.
663
- const epoch = payload.epoch;
664
- if (epoch !== undefined && epoch !== null) {
665
- const hasReset = ops.some((op) => op && op.type === "reset");
666
- if (hasReset) {
667
- state.currentEpoch = epoch;
668
- } else if (state.currentEpoch === null) {
669
- // First payload of the session: adopt whatever epoch we see.
670
- state.currentEpoch = epoch;
671
- } else if (epoch !== state.currentEpoch) {
672
- return;
673
- }
674
- }
675
-
676
- for (let i = 0; i < ops.length; i++) {
677
- try {
678
- applyOp(ops[i]);
679
- } catch (err) {
680
- console.error("[hy-chat] op failed:", ops[i], err);
681
- }
682
- }
683
- // Notify subscribers (the scroll lock and code-block injector in
684
- // app.js) that the chat DOM changed. We dispatch a single CustomEvent
685
- // per applied delta — coarser than per-op so subscribers can do their
686
- // own coalescing on top.
687
- if (state.host) {
688
- state.host.dispatchEvent(new CustomEvent("hy-chat:updated", {
689
- bubbles: false,
690
- }));
691
- }
692
- }
693
-
694
- // ─── Wire up the delta channel ─────────────────────────────────────────
695
- function startDeltaObserver() {
696
- const deltaHost = document.getElementById("hy-chat-delta");
697
- if (!deltaHost) {
698
- setTimeout(startDeltaObserver, 200);
699
- return;
700
- }
701
- let lastValue = "";
702
- function handle() {
703
- // Gradio may put the value in textContent or in a nested element
704
- // depending on version. Walk the subtree to be defensive.
705
- //
706
- // While a generator is actively yielding, Gradio also injects a
707
- // status / elapsed-time text node ("0.0s", "0.1s", …) INSIDE the
708
- // output component's wrapper. That node bubbles into textContent
709
- // and ticks every ~100ms, which would (a) make JSON.parse choke
710
- // on the leading "0.0s" prefix and (b) re-fire the observer on
711
- // every timer tick. Slice from the first '{' to the last '}' to
712
- // skip the status overlay entirely.
713
- const raw = (deltaHost.textContent || "");
714
- const start = raw.indexOf("{");
715
- const end = raw.lastIndexOf("}");
716
- if (start < 0 || end < start) return;
717
- const v = raw.slice(start, end + 1);
718
- if (!v || v === lastValue) return;
719
- lastValue = v;
720
- let payload;
721
- try {
722
- payload = JSON.parse(v);
723
- } catch (err) {
724
- // Real malformed payload (rare): keep visibility, but at debug
725
- // level so the timer overlay can't ever spam the console.
726
- console.debug("[hy-chat] bad delta JSON:", v.slice(0, 200), err);
727
- return;
728
- }
729
- applyDelta(payload);
730
- }
731
- new MutationObserver(handle).observe(deltaHost, {
732
- childList: true,
733
- subtree: true,
734
- characterData: true,
735
- });
736
- handle(); // pick up initial value if any
737
- }
738
-
739
- function init() {
740
- if (!ensureHost()) {
741
- setTimeout(init, 200);
742
- return;
743
- }
744
- startDeltaObserver();
745
- }
746
-
747
- // Public surface for the rest of the page (app.js).
748
- window.HY_CHAT = {
749
- init,
750
- applyDelta,
751
- getHost: ensureHost,
752
- };
753
-
754
- if (document.readyState === "loading") {
755
- document.addEventListener("DOMContentLoaded", () => setTimeout(init, 200));
756
- } else {
757
- setTimeout(init, 200);
758
- }
759
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_chat.css DELETED
@@ -1,144 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────────────────
2
- * Custom chat surface
3
- *
4
- * Layout
5
- * ──────
6
- * #hy-chat-host → the gr.HTML wrapper component. Owns the
7
- * viewport-relative height; pure flex container.
8
- * #hy-chat → our scrollable chat surface. Append-only DOM
9
- * owned by static/chat.js; one .message-row per
10
- * bubble.
11
- *
12
- * The rest of the file is component styling for the renderer's emitted
13
- * DOM. The class names (``.message-row``, ``.message``, ``.bot``,
14
- * ``.user``, ``.markdown``) are deliberately shared with the rules in
15
- * _chatbot.css / _math.css / _thinking.css / _misc.css so those
16
- * rulesets apply transparently to our bubbles.
17
- * ───────────────────────────────────────────────────────────────────────── */
18
-
19
- /* ── Outer wrapper: takes up the available vertical space. ─────────────
20
- * We use absolute positioning on #hy-chat so it pins to all four edges
21
- * of the host regardless of how many intermediate wrapper divs Gradio
22
- * inserts (varies between versions). The host establishes the
23
- * positioning context with ``position: relative``. */
24
- #hy-chat-host {
25
- position: relative !important;
26
- height: calc(100vh - 200px) !important;
27
- padding: 0 !important;
28
- margin: 0 !important;
29
- background: var(--hy-bg) !important;
30
- border: none !important;
31
- box-shadow: none !important;
32
- min-height: 0 !important;
33
- overflow: hidden !important;
34
- }
35
- /* Strip styling and positioning from any wrappers Gradio inserts
36
- * between #hy-chat-host and #hy-chat — they would otherwise create new
37
- * containing blocks and break the absolute-positioned scroll surface. */
38
- #hy-chat-host > div,
39
- #hy-chat-host > div > div {
40
- position: static !important;
41
- padding: 0 !important;
42
- margin: 0 !important;
43
- background: transparent !important;
44
- border: none !important;
45
- box-shadow: none !important;
46
- height: auto !important;
47
- }
48
-
49
- /* ── Inner scrollable surface ──────────────────────────────────────────── */
50
- #hy-chat {
51
- position: absolute;
52
- inset: 0;
53
- overflow-y: auto;
54
- overflow-x: hidden;
55
- padding: 16px 24px 32px;
56
- background: var(--hy-bg);
57
- scroll-behavior: auto; /* programmatic scroll-to-bottom should be instant */
58
- }
59
-
60
- /* ── Message rows ──────────────────────────────────────────────────────── */
61
- #hy-chat .message-row {
62
- display: block;
63
- width: 100%;
64
- margin: 0 0 4px;
65
- padding: 0;
66
- }
67
- #hy-chat .message-row.user-row {
68
- /* Right-align user messages similar to Gradio's bubble layout. */
69
- display: flex;
70
- justify-content: flex-end;
71
- margin: 12px 0 8px;
72
- }
73
- #hy-chat .message-row.bot-row {
74
- margin: 0 0 18px;
75
- }
76
-
77
- /* ── Inner message containers ──────────────────────────────────────────── */
78
- #hy-chat .message {
79
- max-width: 95%;
80
- background: transparent;
81
- border: none;
82
- padding: 0;
83
- }
84
- #hy-chat .message.user {
85
- background: var(--hy-bg-muted, #f5f5f7);
86
- border-radius: 18px;
87
- padding: 10px 18px !important;
88
- max-width: min(720px, 75%);
89
- }
90
- .dark #hy-chat .message.user {
91
- background: #2a2a2a;
92
- }
93
- #hy-chat .message.bot {
94
- background: transparent;
95
- padding: 4px 0 !important;
96
- }
97
-
98
- /* ── User-text appearance (no markdown rendering — pre-wrap text). ────── */
99
- #hy-chat .hy-user-text {
100
- font-size: 15px;
101
- line-height: 1.7;
102
- color: var(--hy-text);
103
- }
104
-
105
- /* ── Frozen vs streaming segments inside the assistant bubble ──────────
106
- * Both are direct children of .markdown so the existing #hy-chat .bot
107
- * .markdown styling cascades into them transparently. The only thing
108
- * we add here is to mark the streaming segment so future debugging or
109
- * styling can target it (e.g. a subtle pulse to indicate "still
110
- * generating" without flickering already-rendered formulas). */
111
- #hy-chat .hy-frozen,
112
- #hy-chat .hy-streaming {
113
- display: block;
114
- }
115
-
116
- /* ── Tool-call display block (server-rendered Markdown HTML). ─────────── */
117
- #hy-chat .hy-tool-calls {
118
- margin-top: 10px;
119
- }
120
- #hy-chat .hy-tool-call + .hy-tool-call {
121
- margin-top: 8px;
122
- }
123
-
124
- /* ── Hide the delta channel completely ─────────────────────────────────
125
- * The bridge component for delta JSON. Must never occupy layout, must
126
- * never trap pointer / focus / scroll events — but its content has to
127
- * remain readable to the JS MutationObserver that drives the renderer
128
- * (so we cannot use display:none). */
129
- #hy-chat-delta,
130
- #hy-chat-delta * {
131
- position: absolute !important;
132
- width: 0 !important;
133
- height: 0 !important;
134
- margin: 0 !important;
135
- padding: 0 !important;
136
- border: 0 !important;
137
- overflow: hidden !important;
138
- clip: rect(0 0 0 0) !important;
139
- clip-path: inset(50%) !important;
140
- visibility: hidden !important;
141
- opacity: 0 !important;
142
- pointer-events: none !important;
143
- user-select: none !important;
144
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_chatbot.css DELETED
@@ -1,170 +0,0 @@
1
- /* ── chatbot container ── */
2
- #hy-chat {
3
- border: none !important;
4
- box-shadow: none !important;
5
- background: #ffffff !important;
6
- --border-color-primary: transparent !important;
7
- --color-accent-soft: #ffffff !important;
8
- --background-fill-secondary: #ffffff !important;
9
- }
10
- .dark #hy-chat {
11
- background: #171717 !important;
12
- --border-color-primary: transparent !important;
13
- --color-accent-soft: #1f1f1f !important;
14
- --background-fill-secondary: #171717 !important;
15
- }
16
-
17
- /* ── message panel: uniform style for both user and bot ── */
18
- #hy-chat .message-row .message-bubble-border,
19
- #hy-chat .bot .message-bubble-border,
20
- #hy-chat .user .message-bubble-border,
21
- #hy-chat [data-testid="bot"] .message-bubble-border,
22
- #hy-chat [data-testid="user"] .message-bubble-border {
23
- border-color: transparent !important;
24
- border-width: 0 !important;
25
- box-shadow: none !important;
26
- outline: none !important;
27
- background: #ffffff !important;
28
- padding: 4px 16px 4px 0 !important;
29
- max-width: 100% !important;
30
- }
31
- .dark #hy-chat .message-row .message-bubble-border,
32
- .dark #hy-chat .bot .message-bubble-border,
33
- .dark #hy-chat .user .message-bubble-border,
34
- .dark #hy-chat [data-testid="bot"] .message-bubble-border,
35
- .dark #hy-chat [data-testid="user"] .message-bubble-border {
36
- background: #171717 !important;
37
- }
38
-
39
- /* ── remove user message bubble border (all themes) ── */
40
- #hy-chat .user,
41
- #hy-chat .user .message,
42
- #hy-chat .user .bubble,
43
- #hy-chat .user > div,
44
- #hy-chat [data-testid="user"],
45
- #hy-chat [data-testid="user"] .message,
46
- #hy-chat [data-testid="user"] .bubble,
47
- #hy-chat [data-testid="user"] > div,
48
- #hy-chat .message-row.user-row,
49
- #hy-chat .message-row.user-row > div,
50
- #hy-chat .user-message,
51
- #hy-chat .message.user {
52
- border: none !important;
53
- border-width: 0 !important;
54
- border-color: transparent !important;
55
- box-shadow: none !important;
56
- outline: none !important;
57
- }
58
- #hy-chat .user .message,
59
- #hy-chat [data-testid="user"] .message,
60
- #hy-chat .user-message {
61
- background: transparent !important;
62
- padding: 4px 16px !important;
63
- }
64
- .dark #hy-chat .user .message,
65
- .dark #hy-chat [data-testid="user"] .message,
66
- .dark #hy-chat .user-message {
67
- background: transparent !important;
68
- }
69
-
70
- /* ── user text: match bot text style ── */
71
- #hy-chat .user .markdown,
72
- #hy-chat .user .message-content,
73
- #hy-chat .user p,
74
- #hy-chat [data-testid="user"] .markdown,
75
- #hy-chat [data-testid="user"] .message-content,
76
- #hy-chat [data-testid="user"] p {
77
- font-size: 15px !important;
78
- line-height: 1.8 !important;
79
- color: #222222 !important;
80
- letter-spacing: 0.01em !important;
81
- }
82
- .dark #hy-chat .user .markdown,
83
- .dark #hy-chat .user .message-content,
84
- .dark #hy-chat .user p,
85
- .dark #hy-chat [data-testid="user"] .markdown,
86
- .dark #hy-chat [data-testid="user"] .message-content,
87
- .dark #hy-chat [data-testid="user"] p {
88
- color: #e8e8e8 !important;
89
- }
90
-
91
- /* ── bot text styling ── */
92
- #hy-chat .bot .markdown,
93
- #hy-chat .bot .message-content,
94
- #hy-chat [data-testid="bot"] .markdown {
95
- font-size: 15px !important;
96
- line-height: 1.8 !important;
97
- color: #222222 !important;
98
- letter-spacing: 0.01em !important;
99
- }
100
- .dark #hy-chat .bot .markdown,
101
- .dark #hy-chat .bot .message-content,
102
- .dark #hy-chat [data-testid="bot"] .markdown {
103
- color: #e8e8e8 !important;
104
- }
105
- #hy-chat .bot p { margin: 0.6em 0 !important; }
106
- #hy-chat .bot strong, #hy-chat .bot b {
107
- font-weight: 600 !important;
108
- color: #111 !important;
109
- }
110
- .dark #hy-chat .bot strong, .dark #hy-chat .bot b {
111
- color: #fff !important;
112
- }
113
- #hy-chat .bot ul, #hy-chat .bot ol {
114
- padding-left: 1.5em !important;
115
- margin: 0.5em 0 !important;
116
- }
117
- #hy-chat .bot li {
118
- margin: 0.3em 0 !important;
119
- line-height: 1.8 !important;
120
- }
121
-
122
- /* ── headings ── */
123
- #hy-chat .bot h1 { font-size: 22px !important; font-weight: 700 !important; color: #111 !important; margin: 1em 0 0.5em !important; }
124
- #hy-chat .bot h2 { font-size: 19px !important; font-weight: 700 !important; color: #111 !important; margin: 0.9em 0 0.4em !important; }
125
- #hy-chat .bot h3 { font-size: 17px !important; font-weight: 600 !important; color: #111 !important; margin: 0.8em 0 0.35em !important; }
126
- #hy-chat .bot h4 { font-size: 15px !important; font-weight: 600 !important; color: #222 !important; margin: 0.7em 0 0.3em !important; }
127
- .dark #hy-chat .bot h1 { color: #f2f2f2 !important; }
128
- .dark #hy-chat .bot h2 { color: #ebebeb !important; }
129
- .dark #hy-chat .bot h3 { color: #e0e0e0 !important; }
130
- .dark #hy-chat .bot h4 { color: #d4d4d4 !important; }
131
-
132
- /* ── tables ── */
133
- #hy-chat table {
134
- border-collapse: collapse !important;
135
- width: auto !important;
136
- margin: 1em 0 !important;
137
- font-size: 14px !important;
138
- line-height: 1.6 !important;
139
- }
140
- #hy-chat th, #hy-chat td {
141
- border: 1px solid #e8e8e8 !important;
142
- padding: 10px 20px !important;
143
- text-align: left !important;
144
- }
145
- #hy-chat th {
146
- background: #fafafa !important;
147
- font-weight: 600 !important;
148
- color: #333 !important;
149
- }
150
- #hy-chat td {
151
- color: #444 !important;
152
- background: #fff !important;
153
- }
154
- #hy-chat tr:hover td {
155
- background: #fafcff !important;
156
- }
157
- .dark #hy-chat th, .dark #hy-chat td {
158
- border-color: #2a2a2a !important;
159
- }
160
- .dark #hy-chat th {
161
- background: #242424 !important;
162
- color: #e0e0e0 !important;
163
- }
164
- .dark #hy-chat td {
165
- color: #d0d0d0 !important;
166
- background: #1c1c1c !important;
167
- }
168
- .dark #hy-chat tr:hover td {
169
- background: #262626 !important;
170
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_dark.css DELETED
@@ -1,194 +0,0 @@
1
- /* ── Dark mode: sidebar ── */
2
- .dark .sidebar, .dark [class*="sidebar"] {
3
- background: #1a1a1a !important;
4
- border: none !important;
5
- box-shadow: none !important;
6
- }
7
- .dark .sidebar h1, .dark [class*="sidebar"] h1 {
8
- color: #e8e8e8 !important;
9
- }
10
- .dark .sidebar .block,
11
- .dark .sidebar .form,
12
- .dark .sidebar .panel,
13
- .dark .sidebar > div,
14
- .dark [class*="sidebar"] .block,
15
- .dark [class*="sidebar"] .form,
16
- .dark [class*="sidebar"] .panel,
17
- .dark [class*="sidebar"] > div {
18
- background: transparent !important;
19
- border: none !important;
20
- box-shadow: none !important;
21
- }
22
-
23
- /* ── Dark mode: global inputs ── */
24
- .dark textarea, .dark input[type="text"] {
25
- background: #2a2a2a !important;
26
- color: #e8e8e8 !important;
27
- border-color: #3a3a3a !important;
28
- }
29
- .dark textarea::placeholder, .dark input[type="text"]::placeholder {
30
- color: #666 !important;
31
- }
32
-
33
- /* ── Dark mode: slider tracks ── */
34
- .dark .gradio-container input[type="range"]::-moz-range-progress { background-color: #6b9fff !important; }
35
- .dark .gradio-container input[type="range"]::-moz-range-track { background: #2a2a2a !important; }
36
-
37
- /* ── Dark mode: dropdowns ── */
38
- .dark select, .dark .wrap[data-testid="dropdown"] {
39
- background: #2a2a2a !important;
40
- color: #e8e8e8 !important;
41
- border-color: #3a3a3a !important;
42
- }
43
-
44
- /* ── Dark mode: accordion ── */
45
- .dark .accordion {
46
- background: #1f1f1f !important;
47
- border-color: #2a2a2a !important;
48
- }
49
-
50
- /* ── Dark mode: forms & labels ── */
51
- .dark .form {
52
- border-color: #2a2a2a !important;
53
- }
54
- .dark label, .dark .label-wrap {
55
- color: #c0c0c0 !important;
56
- }
57
- .dark .info-text, .dark span[data-testid="block-info"] {
58
- color: #808080 !important;
59
- }
60
-
61
- /* ── Dark mode: scrollbars ── */
62
- .dark *::-webkit-scrollbar {
63
- width: 8px !important;
64
- height: 8px !important;
65
- background: transparent !important;
66
- }
67
- .dark *::-webkit-scrollbar-track {
68
- background: transparent !important;
69
- border: none !important;
70
- }
71
- .dark *::-webkit-scrollbar-thumb {
72
- background: #3a3a3a !important;
73
- border-radius: 4px !important;
74
- border: 2px solid transparent !important;
75
- background-clip: padding-box !important;
76
- }
77
- .dark *::-webkit-scrollbar-thumb:hover {
78
- background: #505050 !important;
79
- background-clip: padding-box !important;
80
- }
81
- .dark *::-webkit-scrollbar-corner {
82
- background: transparent !important;
83
- }
84
- .dark * {
85
- scrollbar-width: thin !important;
86
- scrollbar-color: #3a3a3a transparent !important;
87
- }
88
-
89
- /* ── Dark mode: sliders (track + thumb) ── */
90
- .dark input[type="range"] {
91
- -webkit-appearance: none !important;
92
- appearance: none !important;
93
- background: transparent !important;
94
- height: 4px !important;
95
- cursor: pointer !important;
96
- }
97
- .dark input[type="range"]::-webkit-slider-runnable-track {
98
- background: #2f2f2f !important;
99
- height: 4px !important;
100
- border-radius: 2px !important;
101
- border: none !important;
102
- }
103
- .dark input[type="range"]::-moz-range-track {
104
- background: #2f2f2f !important;
105
- height: 4px !important;
106
- border-radius: 2px !important;
107
- border: none !important;
108
- }
109
- .dark input[type="range"]::-webkit-slider-thumb {
110
- -webkit-appearance: none !important;
111
- appearance: none !important;
112
- width: 18px !important;
113
- height: 18px !important;
114
- border-radius: 50% !important;
115
- background: #6b9fff !important;
116
- border: 2px solid #1f1f1f !important;
117
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4) !important;
118
- margin-top: -7px !important;
119
- cursor: pointer !important;
120
- }
121
- .dark input[type="range"]::-moz-range-thumb {
122
- width: 18px !important;
123
- height: 18px !important;
124
- border-radius: 50% !important;
125
- background: #6b9fff !important;
126
- border: 2px solid #1f1f1f !important;
127
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4) !important;
128
- cursor: pointer !important;
129
- }
130
- .dark input[type="range"]::-moz-range-progress {
131
- background-color: #4b7fd6 !important;
132
- height: 4px !important;
133
- border-radius: 2px !important;
134
- }
135
-
136
- /* ── Dark mode: number/spin inputs (slider value boxes) ── */
137
- .dark input[type="number"] {
138
- background: #1f1f1f !important;
139
- color: #c8c8c8 !important;
140
- border: 1px solid #262626 !important;
141
- border-radius: 6px !important;
142
- box-shadow: none !important;
143
- }
144
- .dark input[type="number"]:focus {
145
- border-color: rgba(107, 159, 255, 0.35) !important;
146
- outline: none !important;
147
- }
148
- .dark input[type="number"]::-webkit-inner-spin-button,
149
- .dark input[type="number"]::-webkit-outer-spin-button {
150
- filter: invert(0.85) !important;
151
- opacity: 0.4 !important;
152
- }
153
- /* Number-input wrappers (the small editable boxes next to sliders).
154
- * Previously also targeted .wrap.svelte-1cl284s; dropped the hash since
155
- * Gradio regenerates it. Stable selectors below cover the same elements. */
156
- .dark .gradio-container [class*="number_input"],
157
- .dark .gradio-container [data-testid*="number"] input,
158
- .dark .gradio-container [data-testid*="number"] .wrap,
159
- .dark .gradio-container .head .wrap,
160
- .dark .gradio-container .head input {
161
- background: #1f1f1f !important;
162
- color: #c8c8c8 !important;
163
- border-color: #262626 !important;
164
- box-shadow: none !important;
165
- }
166
- .dark .gradio-container .head,
167
- .dark .gradio-container .head > div,
168
- .dark .gradio-container .tabular-nums {
169
- background: transparent !important;
170
- border-color: #262626 !important;
171
- }
172
-
173
- /* ── Dark mode: slider min/max range labels (e.g. "0", "1", "65536") ── */
174
- .dark .gradio-container .min,
175
- .dark .gradio-container .max,
176
- .dark .head .min,
177
- .dark .head .max,
178
- .dark span.tabular-nums {
179
- color: #707070 !important;
180
- }
181
-
182
- /* ── Dark mode: reset-to-default arrow button next to inputs ── */
183
- .dark button[aria-label="Reset to default value"],
184
- .dark button[title="Reset to default value"] {
185
- background: transparent !important;
186
- color: #707070 !important;
187
- border: none !important;
188
- box-shadow: none !important;
189
- }
190
- .dark button[aria-label="Reset to default value"]:hover,
191
- .dark button[title="Reset to default value"]:hover {
192
- color: #b0b0b0 !important;
193
- background: transparent !important;
194
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_examples.css DELETED
@@ -1,142 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────────────────
2
- * Empty-state "What can I help you with?" overlay.
3
- * Lives outside the Gradio container (appended to <body>) so we don't need
4
- * !important to win against framework styles.
5
- * ───────────────────────────────────────────────────────────────────────── */
6
-
7
- #examples-overlay {
8
- position: fixed;
9
- left: 0; right: 0;
10
- display: flex;
11
- align-items: center;
12
- justify-content: center;
13
- z-index: 100;
14
- background: var(--hy-bg);
15
- pointer-events: none;
16
- overflow-y: auto;
17
- }
18
-
19
- .examples-wrapper {
20
- display: flex;
21
- flex-direction: column;
22
- align-items: center;
23
- margin: auto;
24
- pointer-events: auto;
25
- padding: 40px 20px;
26
- }
27
-
28
- .examples-heading {
29
- font-size: 22px;
30
- font-weight: 600;
31
- color: var(--hy-text);
32
- margin: 0 0 28px;
33
- letter-spacing: -0.01em;
34
- }
35
-
36
- /* 1-2-1 "diamond" layout for exactly 4 example cards.
37
- * row 1: card #1 (centered, spans both columns)
38
- * row 2: card #2 (right-aligned in left col) | card #3 (left-aligned in right col)
39
- * row 3: card #4 (centered, spans both columns)
40
- * Using CSS grid with explicit nth-child placements rather than flex-
41
- * wrap, because flex-wrap on equal-width children can only produce
42
- * 1xN, 2x2, or 4x1 — never 1-2-1. */
43
- .examples-grid {
44
- display: grid;
45
- grid-template-columns: minmax(0, 480px) minmax(0, 480px);
46
- column-gap: 20px;
47
- row-gap: 14px;
48
- justify-content: center;
49
- width: min(1020px, 100%);
50
- }
51
-
52
- .example-btn:nth-child(1),
53
- .example-btn:nth-child(4) {
54
- grid-column: 1 / span 2;
55
- justify-self: center;
56
- }
57
- .example-btn:nth-child(2) {
58
- grid-column: 1;
59
- justify-self: end;
60
- }
61
- .example-btn:nth-child(3) {
62
- grid-column: 2;
63
- justify-self: start;
64
- }
65
-
66
- .example-btn {
67
- /* Outer card: flex container that vertically + horizontally centers
68
- * its single inner ``.example-btn-text`` child. We CANNOT also be
69
- * a ``-webkit-box`` here (that's the inner element's job, see
70
- * below) — line-clamp and flex centering need separate elements. */
71
- display: flex !important;
72
- align-items: center;
73
- justify-content: center;
74
- overflow: hidden !important;
75
- box-sizing: border-box !important;
76
-
77
- width: 460px;
78
- /* 14px font * 1.5 line-height = 21px per line. 2 lines = 42px.
79
- * + 14px*2 padding-y = 28px. + 1px*2 border = 2px. Total 72px. */
80
- height: 72px;
81
-
82
- background: var(--hy-bg);
83
- border: 1px solid var(--hy-border);
84
- border-radius: 18px;
85
- padding: 14px 20px;
86
- font-size: 14px;
87
- line-height: 1.5;
88
- color: var(--hy-text);
89
- cursor: pointer;
90
- transition: transform 0.15s ease, background 0.2s ease,
91
- border-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
92
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC",
93
- "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica,
94
- Arial, sans-serif;
95
- user-select: none;
96
- text-align: center;
97
- }
98
-
99
- /* Inner text element: owns the 2-line clamp + auto ellipsis. The
100
- * ``!important`` triplet defends against framework-level resets that
101
- * would otherwise silently change ``display`` and let a 3rd line
102
- * bleed into view. */
103
- .example-btn-text {
104
- display: -webkit-box !important;
105
- -webkit-line-clamp: 2 !important;
106
- -webkit-box-orient: vertical !important;
107
- overflow: hidden !important;
108
- word-break: break-word;
109
- width: 100%;
110
- text-align: center;
111
- }
112
-
113
- /* On narrow screens the diamond would overflow horizontally — collapse
114
- * to a single centered column instead. */
115
- @media (max-width: 1020px) {
116
- .examples-grid {
117
- grid-template-columns: minmax(0, 1fr);
118
- }
119
- .example-btn:nth-child(1),
120
- .example-btn:nth-child(2),
121
- .example-btn:nth-child(3),
122
- .example-btn:nth-child(4) {
123
- grid-column: 1;
124
- justify-self: center;
125
- }
126
- .example-btn {
127
- width: 100%;
128
- max-width: 520px;
129
- }
130
- }
131
- .example-btn:hover {
132
- background: var(--hy-bg-muted);
133
- border-color: var(--hy-border-strong);
134
- color: var(--hy-text-strong);
135
- box-shadow: var(--hy-shadow);
136
- transform: translateY(-1px);
137
- }
138
-
139
- .dark .example-btn:hover {
140
- border-color: rgba(107, 159, 255, 0.35);
141
- box-shadow: 0 0 8px rgba(107, 159, 255, 0.08);
142
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_hide.css DELETED
@@ -1,131 +0,0 @@
1
- /* ── hide all avatars ── */
2
- #hy-chat [class*="avatar"] {
3
- display: none !important;
4
- width: 0 !important;
5
- height: 0 !important;
6
- min-width: 0 !important;
7
- min-height: 0 !important;
8
- overflow: hidden !important;
9
- opacity: 0 !important;
10
- margin: 0 !important;
11
- padding: 0 !important;
12
- position: absolute !important;
13
- pointer-events: none !important;
14
- }
15
-
16
- /* ── hide Gradio default action buttons & icons ── */
17
- #hy-chat button[aria-label="Copy message"],
18
- #hy-chat button[title="Copy message"],
19
- #hy-chat button[aria-label="Share message"],
20
- #hy-chat button[title="Share message"],
21
- #hy-chat button[aria-label="Delete message"],
22
- #hy-chat button[title="Delete message"],
23
- #hy-chat button[aria-label="Retry"],
24
- #hy-chat button[title="Retry"],
25
- #hy-chat button[aria-label="Undo"],
26
- #hy-chat button[title="Undo"],
27
- #hy-chat .message-buttons-bot,
28
- #hy-chat .message-buttons-user,
29
- #hy-chat .bot .icon-button-wrapper,
30
- #hy-chat .user .icon-button-wrapper,
31
- #hy-chat .bot .icon-button,
32
- #hy-chat .user .icon-button,
33
- #hy-chat .likeable {
34
- display: none !important;
35
- }
36
-
37
- /*
38
- * NOTE: do NOT use a blanket `.message-row button:not(.html-preview-btn)`
39
- * rule here — it would also hide our per-code-block "Copy" button that
40
- * lives inside .hy-codeblock-header at the top-right of each fenced
41
- * code block. The specific aria-label/title selectors above already
42
- * cover the message-level action buttons we want to hide.
43
- */
44
-
45
- /* ── hide stray images/svgs inside messages ── */
46
- #hy-chat .message-row img:not([class*="markdown"]),
47
- #hy-chat .message-row svg {
48
- display: none !important;
49
- }
50
- /* …but keep the SVGs inside our custom code-copy-btn visible.
51
- * Specificity is boosted via duplicated #hy-chat to beat the hide rule. */
52
- #hy-chat#hy-chat .message-row .code-copy-btn,
53
- #hy-chat#hy-chat .message-row .code-copy-btn svg,
54
- #hy-chat#hy-chat .message-row .code-copy-btn svg * {
55
- display: inline-flex !important;
56
- }
57
- #hy-chat#hy-chat .message-row .code-copy-btn svg {
58
- display: block !important;
59
- }
60
- /* …and keep SVGs inside KaTeX math formulas visible.
61
- * KaTeX uses SVGs for radicals, stretchy delimiters, fraction lines, etc. */
62
- #hy-chat#hy-chat .message-row .katex svg,
63
- #hy-chat#hy-chat .message-row .katex-display svg {
64
- display: inline !important;
65
- }
66
-
67
- /* ── Hide scroll-to-bottom button (too jarring in both themes) ── */
68
- #hy-chat button.scroll-hide,
69
- #hy-chat button[aria-label*="scroll" i],
70
- #hy-chat button[aria-label*="bottom" i],
71
- #hy-chat .scroll-to-bottom,
72
- #hy-chat [class*="scroll-button"],
73
- #hy-chat [class*="scrollButton"],
74
- #hy-chat button[class*="scroll"] {
75
- display: none !important;
76
- visibility: hidden !important;
77
- opacity: 0 !important;
78
- pointer-events: none !important;
79
- }
80
-
81
- /* ── Hide the busy-marker component completely ──
82
- * Driven by chat.py: contains either "" (idle) or a single
83
- * <span data-hy-streaming="1"> (streaming). It is a pure machine signal
84
- * read by static/app.js; it must never occupy any visible layout
85
- * regardless of value, and must never trap pointer/scroll/focus events. */
86
- #hy-busy-marker,
87
- #hy-busy-marker * {
88
- position: absolute !important;
89
- width: 0 !important;
90
- height: 0 !important;
91
- margin: 0 !important;
92
- padding: 0 !important;
93
- border: 0 !important;
94
- overflow: hidden !important;
95
- clip: rect(0 0 0 0) !important;
96
- clip-path: inset(50%) !important;
97
- visibility: hidden !important;
98
- opacity: 0 !important;
99
- pointer-events: none !important;
100
- user-select: none !important;
101
- }
102
-
103
- /* ── Hide all Gradio progress / loading status indicators globally ── */
104
- .progress-bar-wrap,
105
- .progress-bar,
106
- .progress-text,
107
- .progress,
108
- .eta-bar,
109
- [data-testid="loader"],
110
- .loader,
111
- .wrap.default.minimal,
112
- .gradio-container .status-wrap,
113
- .gradio-container .status,
114
- .gradio-container .meta-text,
115
- .gradio-container .meta-text-center,
116
- .gradio-container [class*="progress"],
117
- .gradio-container [class*="Progress"],
118
- .gradio-container .generating,
119
- #hy-chat .generating,
120
- #hy-chat .progress,
121
- #hy-chat .progress-text,
122
- #hy-chat [class*="progress"],
123
- #hy-chat [class*="loading"],
124
- #hy-chat [class*="loader"] {
125
- display: none !important;
126
- visibility: hidden !important;
127
- opacity: 0 !important;
128
- pointer-events: none !important;
129
- height: 0 !important;
130
- width: 0 !important;
131
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_input.css DELETED
@@ -1,114 +0,0 @@
1
- /* ── input row ── */
2
- .msg-row {
3
- padding: 8px 0 !important;
4
- gap: 8px !important;
5
- border-top: none !important;
6
- background: #fff !important;
7
- align-items: center !important;
8
- height: 88px !important;
9
- min-height: 88px !important;
10
- max-height: 88px !important;
11
- flex-shrink: 0 !important;
12
- }
13
- .dark .msg-row {
14
- background: #171717 !important;
15
- }
16
- .dark .msg-row *:not(textarea):not(button):not(button *) {
17
- background: transparent !important;
18
- border-color: transparent !important;
19
- box-shadow: none !important;
20
- outline: none !important;
21
- }
22
- .msg-input textarea {
23
- height: 72px !important;
24
- min-height: 72px !important;
25
- max-height: 72px !important;
26
- overflow-y: auto !important;
27
- resize: none !important;
28
- border-radius: 20px !important;
29
- border: 1px solid #e0e0e0 !important;
30
- padding: 18px 22px !important;
31
- font-size: 15px !important;
32
- line-height: 1.5 !important;
33
- background: #fff !important;
34
- box-shadow: 0 1px 4px rgba(0,0,0,0.06) !important;
35
- color: #000 !important;
36
- }
37
- .dark .msg-input textarea {
38
- background: #2a2a2a !important;
39
- border-color: #3a3a3a !important;
40
- color: #e8e8e8 !important;
41
- box-shadow: 0 1px 4px rgba(0,0,0,0.3) !important;
42
- }
43
- .msg-input textarea:focus {
44
- border-color: #c0c0c0 !important;
45
- box-shadow: 0 2px 8px rgba(0,0,0,0.08) !important;
46
- outline: none !important;
47
- }
48
- .dark .msg-input textarea:focus {
49
- border-color: rgba(107, 159, 255, 0.4) !important;
50
- box-shadow: 0 0 0 2px rgba(107, 159, 255, 0.1), 0 2px 8px rgba(0,0,0,0.3) !important;
51
- }
52
-
53
- /* ── new chat (+) button ── */
54
- .new-chat-btn {
55
- width: 42px !important;
56
- height: 42px !important;
57
- min-width: 42px !important;
58
- max-width: 42px !important;
59
- border-radius: 50% !important;
60
- border: 1px solid #e5e7eb !important;
61
- background: #fff !important;
62
- color: #666 !important;
63
- font-size: 22px !important;
64
- font-weight: 300 !important;
65
- padding: 0 !important;
66
- display: flex !important;
67
- align-items: center !important;
68
- justify-content: center !important;
69
- cursor: pointer !important;
70
- flex-shrink: 0 !important;
71
- }
72
- .new-chat-btn:hover {
73
- background: #f5f5f5 !important;
74
- color: #333 !important;
75
- border-color: #d0d0d0 !important;
76
- }
77
- .dark .new-chat-btn {
78
- border-color: #3a3a3a !important;
79
- background: #2a2a2a !important;
80
- color: #999 !important;
81
- }
82
- .dark .new-chat-btn:hover {
83
- background: #333 !important;
84
- color: #e8e8e8 !important;
85
- border-color: #505050 !important;
86
- }
87
-
88
- /* ── buttons ── */
89
- button.primary { border-radius: 20px !important; font-weight: 500 !important; font-size: 14px !important; padding: 8px 20px !important; }
90
- button.secondary { border: 1px solid #e5e5ea !important; border-radius: 20px !important; font-size: 14px !important; color: #444 !important; padding: 8px 16px !important; }
91
- .dark button.secondary { border-color: #3a3a3a !important; color: #c0c0c0 !important; }
92
-
93
- /* ── send button ── */
94
- .send-btn, #send-btn {
95
- width: 42px !important;
96
- height: 42px !important;
97
- min-width: 42px !important;
98
- max-width: 42px !important;
99
- border-radius: 50% !important;
100
- padding: 0 !important;
101
- display: flex !important;
102
- align-items: center !important;
103
- justify-content: center !important;
104
- font-size: 18px !important;
105
- flex-shrink: 0 !important;
106
- }
107
- #send-btn button {
108
- width: 42px !important;
109
- height: 42px !important;
110
- min-width: 42px !important;
111
- border-radius: 50% !important;
112
- padding: 0 !important;
113
- font-size: 18px !important;
114
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_layout.css DELETED
@@ -1,41 +0,0 @@
1
- /* ── full-viewport layout ── */
2
- html, body {
3
- overflow: hidden !important;
4
- height: 100vh !important;
5
- background: #fff !important;
6
- }
7
- .dark html, .dark body, html.dark, body.dark {
8
- background: #171717 !important;
9
- }
10
- .gradio-container {
11
- height: 100vh !important;
12
- overflow: hidden !important;
13
- max-width: 100% !important;
14
- background: #fff !important;
15
- }
16
- .dark .gradio-container, .gradio-container.dark {
17
- background: #171717 !important;
18
- }
19
- .main {
20
- height: 100% !important;
21
- overflow: hidden !important;
22
- background: #fff !important;
23
- }
24
- .dark .main {
25
- background: #171717 !important;
26
- }
27
-
28
- /* ── global font ──
29
- * font-family is set on #hy-chat only (not #hy-chat *) so that KaTeX's
30
- * own math-font declarations (KaTeX_Main, KaTeX_Math, …) are not
31
- * overridden. Children inherit the system stack; KaTeX overrides it
32
- * via its own class-based rules. */
33
- #hy-chat {
34
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC",
35
- "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica,
36
- Arial, sans-serif !important;
37
- }
38
- #hy-chat, #hy-chat * {
39
- -webkit-font-smoothing: antialiased !important;
40
- -moz-osx-font-smoothing: grayscale !important;
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_legacy.css DELETED
@@ -1,50 +0,0 @@
1
- /* ── Form / accordion / label tweaks ──────────────────────────────────────
2
- *
3
- * Previously these rules targeted Svelte hash classes (e.g. .svelte-d5xbca,
4
- * .svelte-xzq5jh, .svelte-e5lyqv, .svelte-jdcl7l) which Gradio re-generates on
5
- * every release. We drop the hashes and use stable class/role selectors so a
6
- * Gradio bump doesn't silently break our styling.
7
- */
8
-
9
- /* hide the rectangular border Gradio puts around forms */
10
- .gradio-container .form {
11
- border: none !important;
12
- }
13
-
14
- /* small "Validate & Format" style accordion buttons inside forms */
15
- .gradio-container .form button.sm,
16
- .gradio-container .accordion button.sm {
17
- background: rgb(229, 231, 235);
18
- }
19
- .dark .gradio-container .form button.sm,
20
- .dark .gradio-container .accordion button.sm {
21
- background: #2a2a2a !important;
22
- color: #c0c0c0 !important;
23
- }
24
-
25
- /* accordion / dropdown / slider label colors */
26
- .gradio-container .label-wrap,
27
- .gradio-container .label-wrap > span {
28
- color: #000;
29
- }
30
- .dark .gradio-container .label-wrap,
31
- .dark .gradio-container .label-wrap > span {
32
- color: #c0c0c0 !important;
33
- }
34
-
35
- /* bot paragraph color (kept here because it's adjacent to label color logic) */
36
- #hy-chat .bot p {
37
- color: #000;
38
- }
39
- .dark #hy-chat .bot p {
40
- color: #e8e8e8 !important;
41
- }
42
-
43
- /* prose inside the Gradio container (markdown rendering); use attribute
44
- * selector that matches any container-version class instead of a pinned one */
45
- [class*="gradio-container"] .prose * {
46
- color: #000;
47
- }
48
- .dark [class*="gradio-container"] .prose * {
49
- color: #e8e8e8 !important;
50
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_math.css DELETED
@@ -1,132 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────────────────
2
- * KaTeX math rendering
3
- *
4
- * The chatbot CSS sets line-height: 1.8 + letter-spacing: 0.01em on
5
- * .bot .markdown. These cascade into KaTeX, where SVGs for stretchy
6
- * operators (like \not=) use height: inherit + width: 100%. An inflated
7
- * line-height turns those SVGs into solid-colour rectangles. We reset
8
- * typographic properties on all KaTeX descendants to restore correct
9
- * glyph and symbol layout.
10
- * ───────────────────────────────────────────────────────────────────────── */
11
-
12
- /* ── Restore KaTeX's own font ──
13
- * _layout.css sets font-family on #hy-chat; KaTeX children inherit it.
14
- * Override here so KaTeX uses its own math fonts for correct glyphs. */
15
- #hy-chat .katex {
16
- font: normal 1.21em KaTeX_Main, "Times New Roman", serif !important;
17
- text-indent: 0 !important;
18
- }
19
-
20
- /* ── Reset inherited typography ──
21
- * High-specificity selectors ensure we beat the #hy-chat .bot .markdown
22
- * rule that sets line-height: 1.8 and letter-spacing: 0.01em. */
23
- #hy-chat .bot .markdown .katex,
24
- #hy-chat .bot .markdown .katex *,
25
- #hy-chat .bot .markdown .katex-display,
26
- #hy-chat .bot .markdown .katex-display *,
27
- #hy-chat .katex,
28
- #hy-chat .katex *,
29
- #hy-chat .katex-display,
30
- #hy-chat .katex-display * {
31
- letter-spacing: normal !important;
32
- word-spacing: normal !important;
33
- line-height: 1.2 !important;
34
- }
35
-
36
- /* ── SVG rendering ──
37
- * KaTeX uses SVGs for radicals (√), stretchy delimiters, fraction lines,
38
- * and negation slashes. Ensure they inherit the current text colour and
39
- * are not distorted by the parent's line-height. */
40
- #hy-chat .katex svg {
41
- fill: currentColor;
42
- stroke: none;
43
- }
44
- #hy-chat .katex svg path {
45
- fill: currentColor;
46
- }
47
-
48
- /* ── SVG overlay fix ──
49
- * Constrain overlay containers (negation slashes, accents) so an
50
- * inflated line-height can never stretch SVGs into black blocks. */
51
- #hy-chat .katex .rlap,
52
- #hy-chat .katex .llap,
53
- #hy-chat .katex .clap {
54
- line-height: 0 !important;
55
- }
56
- #hy-chat .katex .stretchy,
57
- #hy-chat .katex .halfarrow-left,
58
- #hy-chat .katex .halfarrow-right,
59
- #hy-chat .katex .brace-left,
60
- #hy-chat .katex .brace-right {
61
- overflow: hidden !important;
62
- }
63
- #hy-chat .katex .rule,
64
- #hy-chat .katex .mfrac .frac-line,
65
- #hy-chat .katex .overline .overline-line,
66
- #hy-chat .katex .underline .underline-line,
67
- #hy-chat .katex .hline {
68
- overflow: hidden !important;
69
- }
70
-
71
- /* ── Text colour ──
72
- * Explicit colour in both themes so KaTeX text and SVG fills (via
73
- * currentColor) always match the surrounding prose. */
74
- #hy-chat .katex,
75
- #hy-chat .katex-display {
76
- color: var(--hy-text, #222222) !important;
77
- }
78
-
79
- /* ── Display math (block) ── */
80
- #hy-chat .katex-display {
81
- margin: 1em 0 !important;
82
- padding: 0.4em 0 !important;
83
- overflow-x: auto !important;
84
- overflow-y: hidden !important;
85
- -webkit-overflow-scrolling: touch;
86
- }
87
- #hy-chat .katex-display > .katex {
88
- text-align: center !important;
89
- }
90
-
91
- /* Subtle scrollbar for wide equations */
92
- #hy-chat .katex-display::-webkit-scrollbar {
93
- height: 3px;
94
- }
95
- #hy-chat .katex-display::-webkit-scrollbar-track {
96
- background: transparent;
97
- }
98
- #hy-chat .katex-display::-webkit-scrollbar-thumb {
99
- background: #d0d0d0;
100
- border-radius: 2px;
101
- }
102
-
103
- /* ── Inline math ── */
104
- #hy-chat p .katex,
105
- #hy-chat li .katex,
106
- #hy-chat td .katex {
107
- font-size: 1.05em;
108
- vertical-align: baseline;
109
- }
110
-
111
- /* ── KaTeX error fallback: show raw LaTeX legibly ── */
112
- #hy-chat .katex-error {
113
- color: var(--hy-text-muted, #666) !important;
114
- font-family: "SF Mono", "Fira Code", "JetBrains Mono", monospace !important;
115
- font-size: 0.88em !important;
116
- background: var(--hy-bg-muted, #f5f5f7) !important;
117
- padding: 1px 5px !important;
118
- border-radius: 3px !important;
119
- }
120
-
121
- /* ── Dark mode ── */
122
- .dark #hy-chat .katex,
123
- .dark #hy-chat .katex-display {
124
- color: var(--hy-text, #e8e8e8) !important;
125
- }
126
- .dark #hy-chat .katex-display::-webkit-scrollbar-thumb {
127
- background: #404040;
128
- }
129
- .dark #hy-chat .katex-error {
130
- color: var(--hy-text-muted, #909090) !important;
131
- background: var(--hy-bg-muted, #2a2a2a) !important;
132
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_misc.css DELETED
@@ -1,111 +0,0 @@
1
- /* ── inputs global ── */
2
- textarea, input[type="text"] { border-radius: 10px !important; font-size: 15px !important; }
3
-
4
- /* ── code blocks ── */
5
- #hy-chat pre {
6
- background: #f5f5f7 !important;
7
- border: 1px solid #ebebeb !important;
8
- border-radius: 8px !important;
9
- padding: 14px 18px !important;
10
- font-size: 13px !important;
11
- line-height: 1.6 !important;
12
- overflow-x: auto !important;
13
- margin: 0.8em 0 !important;
14
- }
15
- .dark #hy-chat pre {
16
- background: #111 !important;
17
- border-color: #2a2a2a !important;
18
- }
19
- #hy-chat code {
20
- font-family: "SF Mono", "Fira Code", "JetBrains Mono", "Consolas", "Monaco", monospace !important;
21
- font-size: 0.9em !important;
22
- }
23
- .dark #hy-chat code {
24
- color: #d4d4d4 !important;
25
- }
26
- #hy-chat :not(pre) > code {
27
- background: #f0f0f3 !important;
28
- padding: 2px 6px !important;
29
- border-radius: 4px !important;
30
- color: #333 !important;
31
- }
32
- .dark #hy-chat :not(pre) > code {
33
- background: #2a2a2a !important;
34
- color: #d4d4d4 !important;
35
- }
36
-
37
- /* ── blockquote ── */
38
- #hy-chat blockquote {
39
- border-left: 3px solid #d1d1d6 !important;
40
- margin: 0.8em 0 !important;
41
- padding: 4px 16px !important;
42
- color: #555 !important;
43
- background: transparent !important;
44
- }
45
- .dark #hy-chat blockquote {
46
- border-left-color: #3a3a3a !important;
47
- color: #909090 !important;
48
- }
49
-
50
- /* ── tables ──
51
- * Markdown tables otherwise render with ``display: table`` and
52
- * intrinsic auto sizing, which lets a wide table push past the
53
- * message's ``max-width: 95%`` and overflow ``#hy-chat`` horizontally.
54
- * Beyond the obvious horizontal-clip ugliness, the post-stream column-
55
- * width pass (browser re-measures cells once all rows are in) was
56
- * leaving ``#hy-chat``'s ``scrollHeight`` desynced from real layout —
57
- * the user could drag the scrollbar to the bottom of its track without
58
- * reaching the actual last row of the answer, and the wheel was dead
59
- * because they really were at the (stale) max scrollTop.
60
- *
61
- * The fix is to (a) keep the table inside the message bubble and
62
- * (b) make any too-wide table scroll horizontally inside ITS OWN
63
- * bounds rather than the row's:
64
- * - ``display: block`` + ``max-width: 100%`` + ``overflow-x: auto``
65
- * makes the ``<table>`` itself a horizontally-scrollable inline
66
- * box; column widths are still auto-computed but the layout
67
- * impact stays inside the table.
68
- * - ``border-collapse: collapse`` + light borders give it the
69
- * conventional spreadsheet look without adding cell-spacing
70
- * surprises that browsers handle inconsistently. */
71
- #hy-chat table {
72
- display: block !important;
73
- width: max-content !important;
74
- max-width: 100% !important;
75
- overflow-x: auto !important;
76
- border-collapse: collapse !important;
77
- margin: 0.8em 0 !important;
78
- font-size: 14px !important;
79
- }
80
- #hy-chat th,
81
- #hy-chat td {
82
- border: 1px solid #e5e5ea !important;
83
- padding: 6px 12px !important;
84
- text-align: left !important;
85
- vertical-align: top !important;
86
- }
87
- #hy-chat th {
88
- background: #f5f5f7 !important;
89
- font-weight: 600 !important;
90
- }
91
- .dark #hy-chat th,
92
- .dark #hy-chat td {
93
- border-color: #333 !important;
94
- }
95
- .dark #hy-chat th {
96
- background: #1f1f1f !important;
97
- }
98
-
99
- /* ── hr ── */
100
- #hy-chat hr {
101
- border: none !important;
102
- border-top: 1px solid #e5e5ea !important;
103
- margin: 1.2em 0 !important;
104
- }
105
- .dark #hy-chat hr {
106
- border-top-color: #2a2a2a !important;
107
- }
108
-
109
- /* ── slider progress ── */
110
- .gradio-container input[type="range"]::-moz-range-progress { background-color: #2563eb !important; }
111
- .gradio-container input[type="range"]::-moz-range-track { background: #e5e7eb !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_modals.css DELETED
@@ -1,266 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────────────────
2
- * Floating panels: tool-call input area + HTML preview modal.
3
- *
4
- * #tool-area lives inside the Gradio tree, so it still needs !important to
5
- * win the cascade against framework styles.
6
- *
7
- * .html-preview-* lives directly under <body> (appended by static/app.js)
8
- * so it can use plain specificity.
9
- * ───────────────────────────────────────────────────────────────────────── */
10
-
11
- /* ── tool area (inside Gradio) ─────────────────────────────────────────── */
12
- #tool-area {
13
- position: fixed !important;
14
- bottom: 120px !important;
15
- left: 50% !important;
16
- transform: translateX(-50%) !important;
17
- width: min(600px, 55%) !important;
18
- z-index: 999 !important;
19
- background: var(--hy-bg) !important;
20
- border: 1px solid var(--hy-border) !important;
21
- border-radius: 12px !important;
22
- padding: 16px !important;
23
- box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
24
- }
25
- .dark #tool-area {
26
- background: var(--hy-bg-soft) !important;
27
- border-color: var(--hy-border) !important;
28
- box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5) !important;
29
- }
30
- .dark #tool-area *:not(button):not(button *) {
31
- background: transparent !important;
32
- border-color: var(--hy-border-strong) !important;
33
- }
34
- .dark #tool-area textarea,
35
- .dark #tool-area input[type="text"] {
36
- background: var(--hy-bg-muted) !important;
37
- color: var(--hy-text) !important;
38
- border: 1px solid var(--hy-border-strong) !important;
39
- }
40
- .dark #tool-area textarea::placeholder,
41
- .dark #tool-area input[type="text"]::placeholder {
42
- color: var(--hy-text-muted) !important;
43
- }
44
-
45
- /* ── Code-block card (header w/ language label + copy button) ──────────
46
- *
47
- * Each fenced code block is wrapped by static/app.js:
48
- *
49
- * <div class="hy-codeblock">
50
- * <div class="hy-codeblock-header">
51
- * <span class="hy-codeblock-lang">python</span>
52
- * <button class="code-copy-btn">…</button>
53
- * </div>
54
- * <pre><code class="hljs language-…">…</code></pre>
55
- * </div>
56
- *
57
- * The wrapper owns the rounded border and background so the inner <pre>
58
- * can be flush with the header (no double border / gap). */
59
- #hy-chat .hy-codeblock {
60
- margin: 0.9em 0;
61
- border: 1px solid var(--hy-border);
62
- border-radius: 10px;
63
- overflow: hidden;
64
- background: #f6f7f9;
65
- }
66
- .dark #hy-chat .hy-codeblock {
67
- border-color: #2a2a2a;
68
- background: #0f1115;
69
- }
70
-
71
- #hy-chat .hy-codeblock-header {
72
- display: flex;
73
- align-items: center;
74
- justify-content: space-between;
75
- gap: 12px;
76
- padding: 6px 8px 6px 14px;
77
- background: #ececef;
78
- border-bottom: 1px solid var(--hy-border);
79
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text",
80
- "Segoe UI", Roboto, sans-serif;
81
- font-size: 12px;
82
- line-height: 1;
83
- color: #6b6b6b;
84
- user-select: none;
85
- }
86
- .dark #hy-chat .hy-codeblock-header {
87
- background: #1a1d22;
88
- border-bottom-color: #262a30;
89
- color: #9ca3af;
90
- }
91
-
92
- #hy-chat .hy-codeblock-lang {
93
- text-transform: lowercase;
94
- letter-spacing: 0.02em;
95
- font-weight: 500;
96
- }
97
- #hy-chat .hy-codeblock-lang:empty::before {
98
- content: "text";
99
- opacity: 0.7;
100
- }
101
-
102
- /* Strip the standalone <pre> chrome from _misc.css when nested in a
103
- * .hy-codeblock — the wrapper provides the border/radius/background. */
104
- #hy-chat .hy-codeblock pre {
105
- margin: 0 !important;
106
- border: none !important;
107
- border-radius: 0 !important;
108
- background: transparent !important;
109
- padding: 12px 16px !important;
110
- }
111
-
112
- /* Copy button — lives in the header, simple icon button. */
113
- #hy-chat .hy-codeblock-header .code-copy-btn {
114
- flex-shrink: 0;
115
- width: 26px;
116
- height: 26px;
117
- padding: 0;
118
- display: inline-flex;
119
- align-items: center;
120
- justify-content: center;
121
- color: inherit;
122
- background: transparent;
123
- border: none;
124
- border-radius: 6px;
125
- cursor: pointer;
126
- transition: background 0.15s ease, color 0.15s ease;
127
- }
128
- #hy-chat .hy-codeblock-header .code-copy-btn svg {
129
- display: block;
130
- width: 14px;
131
- height: 14px;
132
- }
133
- #hy-chat .hy-codeblock-header .code-copy-btn:hover {
134
- background: rgba(0, 0, 0, 0.06);
135
- color: #111;
136
- }
137
- #hy-chat .hy-codeblock-header .code-copy-btn:focus {
138
- outline: none;
139
- }
140
- #hy-chat .hy-codeblock-header .code-copy-btn:focus-visible {
141
- outline: 2px solid var(--hy-accent, #2563eb);
142
- outline-offset: 1px;
143
- }
144
- #hy-chat .hy-codeblock-header .code-copy-btn.copied,
145
- #hy-chat .hy-codeblock-header .code-copy-btn.copied:hover {
146
- color: #0a7f3f;
147
- background: transparent;
148
- }
149
- .dark #hy-chat .hy-codeblock-header .code-copy-btn:hover {
150
- background: rgba(255, 255, 255, 0.08);
151
- color: #f5f5f5;
152
- }
153
- .dark #hy-chat .hy-codeblock-header .code-copy-btn.copied,
154
- .dark #hy-chat .hy-codeblock-header .code-copy-btn.copied:hover {
155
- color: #4ade80;
156
- background: transparent;
157
- }
158
-
159
- /* ── HTML preview play button (under code blocks) ─────────────────────── */
160
- .html-preview-btn {
161
- display: inline-flex;
162
- align-items: center;
163
- background: none;
164
- border: none;
165
- padding: 2px 0;
166
- margin: 2px 0 6px;
167
- cursor: pointer;
168
- font-size: 0;
169
- line-height: 1;
170
- transition: opacity 0.15s ease;
171
- }
172
- .html-preview-btn::before {
173
- content: "▶ html preview";
174
- font-size: 12px;
175
- color: var(--hy-accent);
176
- font-weight: 400;
177
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
178
- }
179
- .html-preview-btn:hover {
180
- opacity: 0.7;
181
- }
182
-
183
- /* ── HTML preview modal (outside Gradio, under <body>) ────────────────── */
184
- .html-preview-overlay {
185
- position: fixed;
186
- inset: 0;
187
- z-index: 10000;
188
- background: rgba(0, 0, 0, 0.5);
189
- display: flex;
190
- align-items: center;
191
- justify-content: center;
192
- animation: fadeIn 0.2s ease;
193
- }
194
- @keyframes fadeIn {
195
- from { opacity: 0; }
196
- to { opacity: 1; }
197
- }
198
-
199
- .html-preview-modal {
200
- width: 90vw;
201
- height: 85vh;
202
- background: var(--hy-bg);
203
- border-radius: 12px;
204
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
205
- display: flex;
206
- flex-direction: column;
207
- overflow: hidden;
208
- animation: modalSlideUp 0.25s ease;
209
- }
210
- .dark .html-preview-modal {
211
- background: var(--hy-bg-soft);
212
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
213
- }
214
- @keyframes modalSlideUp {
215
- from { transform: translateY(30px); opacity: 0; }
216
- to { transform: translateY(0); opacity: 1; }
217
- }
218
-
219
- .html-preview-header {
220
- display: flex;
221
- align-items: center;
222
- justify-content: space-between;
223
- padding: 12px 20px;
224
- border-bottom: 1px solid var(--hy-border);
225
- background: var(--hy-bg-soft);
226
- flex-shrink: 0;
227
- }
228
- .dark .html-preview-header {
229
- background: var(--hy-bg-muted);
230
- }
231
-
232
- .html-preview-title {
233
- font-size: 14px;
234
- font-weight: 600;
235
- color: var(--hy-text);
236
- display: flex;
237
- align-items: center;
238
- gap: 8px;
239
- }
240
- .html-preview-title::before {
241
- content: "⬡";
242
- color: var(--hy-accent);
243
- }
244
-
245
- .html-preview-close {
246
- background: none;
247
- border: none;
248
- font-size: 20px;
249
- color: var(--hy-text-muted);
250
- cursor: pointer;
251
- padding: 4px 8px;
252
- border-radius: 6px;
253
- line-height: 1;
254
- transition: all 0.15s ease;
255
- }
256
- .html-preview-close:hover {
257
- background: #fee2e2;
258
- color: #dc2626;
259
- }
260
-
261
- .html-preview-iframe {
262
- flex: 1;
263
- border: none;
264
- width: 100%;
265
- background: var(--hy-bg);
266
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_perf.css DELETED
@@ -1,50 +0,0 @@
1
- /* ── Long-conversation rendering optimizations ─────────────────────────
2
- *
3
- * Three CSS optimizations have been tried on ``.message-row`` and all
4
- * three were ULTIMATELY REMOVED because each caused a real, repeatable
5
- * scroll-geometry bug:
6
- *
7
- * 1. ``contain: layout`` — when a child ``<details
8
- * class="thinking-block">`` collapsed (either via
9
- * ``markThinkingDone`` mid-stream OR via a user click), the row's
10
- * contribution to ``#hy-chat``'s ``scrollHeight`` got stranded at
11
- * the pre-collapse value on Chromium. The user would scroll up
12
- * to read history, drag the scrollbar back to the bottom, and
13
- * the thumb would hit the end of the track while the chat
14
- * stopped many screens above the latest message; the wheel did
15
- * nothing because they really were at ``scrollHeight -
16
- * clientHeight``. Manually toggling the collapsed Thinking block
17
- * open forced a relayout that fixed it — the diagnostic
18
- * fingerprint.
19
- *
20
- * 2. ``contain: paint`` — the same symptom recurred under a
21
- * narrower trigger: assistant turns containing wide elements
22
- * (most commonly markdown tables, also ``katex-display`` blocks,
23
- * and fenced code with long lines). ``paint`` containment makes
24
- * the row a stacking context AND clips overflow at the contain
25
- * box. Combined with the post-stream lazy-render passes
26
- * (``hljs.highlightElement``, KaTeX re-render of the streaming
27
- * tail, table column-width pass), the row's reported size in
28
- * ``#hy-chat``'s scroll metrics drifted away from its real
29
- * rendered size by hundreds-to-thousands of pixels. Toggling
30
- * Thinking no longer fixed this case because the stranded value
31
- * was on the post-table row, not the row containing Thinking.
32
- *
33
- * 3. ``content-visibility: auto`` — skipping render for off-screen
34
- * rows. Row heights vary wildly (a "你好" reply is ~40px, a
35
- * math derivation or table-heavy answer is 3000–5000px). With
36
- * ``contain-intrinsic-size: auto 240px`` as the placeholder,
37
- * scrolling up by a tiny amount caused off-screen rows to expand
38
- * from 240px to their real size, growing ``scrollHeight`` by
39
- * thousands of pixels in a single frame. Browser scroll
40
- * anchoring did not reliably compensate, so the view jumped far
41
- * up the conversation and felt broken.
42
- *
43
- * If we ever need any of these optimizations back, gate them behind a
44
- * row-count threshold (only apply to rows older than the last N) and,
45
- * for option 3, measure each row's real height before swapping it out
46
- * so ``contain-intrinsic-size`` is per-row rather than a single
47
- * global guess. For options 1 & 2, also wire a ``ResizeObserver`` on
48
- * each row that re-dispatches ``hy-chat:updated`` whenever the row's
49
- * height changes (the scroll-lock in ``static/app.js`` uses that
50
- * event to re-evaluate / re-pin). */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_thinking.css DELETED
@@ -1,35 +0,0 @@
1
- /* ── thinking toggle ── */
2
- #hy-chat .thinking-block {
3
- background: none !important;
4
- border: none !important;
5
- padding: 0 !important;
6
- margin: 6px 0 10px !important;
7
- }
8
- #hy-chat .thinking-block summary {
9
- cursor: pointer;
10
- font-size: 13px !important;
11
- font-weight: 400 !important;
12
- color: #8a8a8e !important;
13
- padding: 0 !important;
14
- list-style: none !important;
15
- display: inline-flex;
16
- align-items: center;
17
- gap: 4px;
18
- }
19
- #hy-chat .thinking-block summary:hover { color: #666 !important; }
20
- .dark #hy-chat .thinking-block summary { color: #808080 !important; }
21
- .dark #hy-chat .thinking-block summary:hover { color: #aaa !important; }
22
- #hy-chat .thinking-block summary::marker,
23
- #hy-chat .thinking-block summary::-webkit-details-marker { display: none !important; }
24
- #hy-chat .thinking-block summary::after { content: " ›" !important; font-size: 13px; color: #bbb; }
25
- #hy-chat .thinking-block[open] summary::after { content: " ⌄" !important; }
26
- #hy-chat .thinking-block[open] summary { margin-bottom: 8px !important; }
27
- #hy-chat .thinking-block[open] > :not(summary) {
28
- font-size: 13px; color: #666; line-height: 1.7;
29
- padding: 12px 16px; background: #f8f8fa !important; border-radius: 8px;
30
- max-height: 300px; overflow-y: auto; border-left: 2px solid #e5e5ea !important;
31
- }
32
- .dark #hy-chat .thinking-block[open] > :not(summary) {
33
- color: #909090 !important; background: #1c1c1c !important;
34
- border-left-color: #333 !important;
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_typing.css DELETED
@@ -1,29 +0,0 @@
1
- /* ── typing indicator (waiting for model response) ── */
2
- .typing-indicator {
3
- display: flex;
4
- align-items: center;
5
- gap: 5px;
6
- padding: 4px 0;
7
- height: 24px;
8
- }
9
- .typing-indicator span {
10
- width: 8px;
11
- height: 8px;
12
- background: var(--body-text-color-subdued, #999);
13
- border-radius: 50%;
14
- animation: typing-bounce 1.4s infinite ease-in-out both;
15
- }
16
- .typing-indicator span:nth-child(1) { animation-delay: 0s; }
17
- .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
18
- .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
19
-
20
- @keyframes typing-bounce {
21
- 0%, 80%, 100% {
22
- transform: scale(0.5);
23
- opacity: 0.35;
24
- }
25
- 40% {
26
- transform: scale(1);
27
- opacity: 1;
28
- }
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/css/_variables.css DELETED
@@ -1,81 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────────────────
2
- * Design tokens (custom properties)
3
- *
4
- * Two layers:
5
- * 1. ``--hy-*`` : our own palette — referenced from component CSS.
6
- * 2. ``--*-*-*`` : Gradio's built-in tokens — overridden so Gradio's
7
- * components match the rest of the UI.
8
- *
9
- * If you want to retheme HY3, change values here and (almost) nowhere else.
10
- * ───────────────────────────────────────────────────────────────────────── */
11
-
12
- :root, .gradio-container {
13
- /* Hy3 palette (light) */
14
- --hy-bg: #ffffff;
15
- --hy-bg-soft: #fafafa;
16
- --hy-bg-muted: #f5f5f7;
17
- --hy-text: #222222;
18
- --hy-text-strong: #111111;
19
- --hy-text-muted: #666666;
20
- --hy-border: #e5e7eb;
21
- --hy-border-strong: #d1d1d6;
22
- --hy-accent: #2563eb;
23
- --hy-accent-hover: #1d4ed8;
24
- --hy-accent-soft: #dbeafe;
25
- --hy-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
26
-
27
- /* Gradio framework overrides (light) */
28
- --body-background-fill: var(--hy-bg) !important;
29
- --block-background-fill: var(--hy-bg) !important;
30
- --block-shadow: none !important;
31
- --block-border-color: transparent !important;
32
- --input-border-width: 1px !important;
33
- --input-border-color: var(--hy-border) !important;
34
- --color-accent: var(--hy-accent) !important;
35
- --color-accent-soft: var(--hy-accent-soft) !important;
36
- --button-primary-background-fill: var(--hy-accent) !important;
37
- --button-primary-background-fill-hover: var(--hy-accent-hover) !important;
38
- --button-primary-text-color: #ffffff !important;
39
- --slider-color: var(--hy-accent) !important;
40
- --neutral-200: var(--hy-border) !important;
41
- }
42
-
43
- /* Hy3 palette (dark) + matching Gradio overrides */
44
- .dark {
45
- --hy-bg: #171717;
46
- --hy-bg-soft: #1f1f1f;
47
- --hy-bg-muted: #2a2a2a;
48
- --hy-text: #e8e8e8;
49
- --hy-text-strong: #ffffff;
50
- --hy-text-muted: #909090;
51
- --hy-border: #2a2a2a;
52
- --hy-border-strong: #3a3a3a;
53
- --hy-accent: #6b9fff;
54
- --hy-accent-hover: #4b8df8;
55
- --hy-accent-soft: #1a2a3d;
56
- --hy-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
57
-
58
- --body-background-fill: var(--hy-bg) !important;
59
- --block-background-fill: var(--hy-bg-soft) !important;
60
- --block-shadow: none !important;
61
- --block-border-color: var(--hy-border) !important;
62
- --input-border-width: 1px !important;
63
- --input-border-color: var(--hy-border-strong) !important;
64
- --color-accent: var(--hy-accent) !important;
65
- --color-accent-soft: var(--hy-accent-soft) !important;
66
- --button-primary-background-fill: var(--hy-accent-hover) !important;
67
- --button-primary-background-fill-hover: #3a7ce6 !important;
68
- --button-primary-text-color: #ffffff !important;
69
- --slider-color: var(--hy-accent) !important;
70
- --neutral-200: var(--hy-border) !important;
71
- --body-text-color: var(--hy-text) !important;
72
- --block-label-text-color: #c0c0c0 !important;
73
- --block-title-text-color: #e0e0e0 !important;
74
- --input-background-fill: var(--hy-bg-muted) !important;
75
- --background-fill-secondary: var(--hy-bg-soft) !important;
76
- --background-fill-primary: var(--hy-bg) !important;
77
- --border-color-primary: var(--hy-border) !important;
78
- --panel-background-fill: var(--hy-bg-soft) !important;
79
- --checkbox-background-color: var(--hy-bg-muted) !important;
80
- --checkbox-label-background-fill: var(--hy-bg-soft) !important;
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/vendor/highlight/LICENSE DELETED
@@ -1,29 +0,0 @@
1
- BSD 3-Clause License
2
-
3
- Copyright (c) 2006, Ivan Sagalaev.
4
- All rights reserved.
5
-
6
- Redistribution and use in source and binary forms, with or without
7
- modification, are permitted provided that the following conditions are met:
8
-
9
- * Redistributions of source code must retain the above copyright notice, this
10
- list of conditions and the following disclaimer.
11
-
12
- * Redistributions in binary form must reproduce the above copyright notice,
13
- this list of conditions and the following disclaimer in the documentation
14
- and/or other materials provided with the distribution.
15
-
16
- * Neither the name of the copyright holder nor the names of its
17
- contributors may be used to endorse or promote products derived from
18
- this software without specific prior written permission.
19
-
20
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/vendor/highlight/github-dark.min.css DELETED
@@ -1,10 +0,0 @@
1
- pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
2
- Theme: GitHub Dark
3
- Description: Dark theme as seen on github.com
4
- Author: github.com
5
- Maintainer: @Hirse
6
- Updated: 2021-05-15
7
-
8
- Outdated base version: https://github.com/primer/github-syntax-dark
9
- Current colors taken from GitHub's CSS
10
- */.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
 
 
 
 
 
 
 
 
 
 
 
static/vendor/highlight/github.min.css DELETED
@@ -1,18 +0,0 @@
1
- /**
2
- * Minified by jsDelivr using clean-css v5.3.3.
3
- * Original file: /gh/highlightjs/cdn-release@11.10.0/build/styles/github.css
4
- *
5
- * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
- */
7
- pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
8
- Theme: GitHub
9
- Description: Light theme as seen on github.com
10
- Author: github.com
11
- Maintainer: @Hirse
12
- Updated: 2021-05-15
13
-
14
- Outdated base version: https://github.com/primer/github-syntax-light
15
- Current colors taken from GitHub's CSS
16
- */
17
- .hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
18
- /*# sourceMappingURL=/sm/4af24df1ef2dd5fc126923c27abd2503f82240131784ba83446a7a23be61ec0f.map */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/vendor/highlight/highlight.min.js DELETED
The diff for this file is too large to render. See raw diff
 
static/vendor/katex/LICENSE DELETED
@@ -1,27 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2013-2020 Khan Academy and other contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
23
- ----------------------------------------------------------------------
24
-
25
- NOTE: The KaTeX font files (under ./fonts/) are NOT covered by this MIT
26
- license. They are licensed separately under the SIL Open Font License,
27
- Version 1.1 — see ./fonts/OFL.txt.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/vendor/katex/auto-render.min.js DELETED
@@ -1 +0,0 @@
1
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};r.d(o,{default:function(){return p}});var i=r(757),a=r.n(i);const l=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},s=/^\\begin{/;var d=function(e,t){let n;const r=[],o=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;n=e.search(o),-1!==n;){n>0&&(r.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const o=t.findIndex((t=>e.startsWith(t.left)));if(n=l(t[o].right,e,t[o].left.length),-1===n)break;const i=e.slice(0,n+t[o].right.length),a=s.test(i)?i:e.slice(t[o].left.length,n);r.push({type:"math",data:a,rawData:i,display:t[o].display}),e=e.slice(n+t[o].right.length)}return""!==e&&r.push({type:"text",data:e}),r};const c=function(e,t){const n=d(e,t.delimiters);if(1===n.length&&"text"===n[0].type)return null;const r=document.createDocumentFragment();for(let e=0;e<n.length;e++)if("text"===n[e].type)r.appendChild(document.createTextNode(n[e].data));else{const o=document.createElement("span");let i=n[e].data;t.displayMode=n[e].display;try{t.preProcess&&(i=t.preProcess(i)),a().render(i,o,t)}catch(o){if(!(o instanceof a().ParseError))throw o;t.errorCallback("KaTeX auto-render: Failed to parse `"+n[e].data+"` with ",o),r.appendChild(document.createTextNode(n[e].rawData));continue}r.appendChild(o)}return r},f=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const l=c(o,t);if(l){for(let e=0;e<a;e++)r.nextSibling.remove();n+=l.childNodes.length-1,e.replaceChild(l,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&f(r,t)}}};var p=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},f(e,n)};return o=o.default}()}));
 
 
static/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 DELETED
Binary file (28.1 kB)
 
static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 DELETED
Binary file (6.91 kB)
 
static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 DELETED
Binary file (6.91 kB)
 
static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 DELETED
Binary file (11.3 kB)
 
static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 DELETED
Binary file (11.3 kB)
 
static/vendor/katex/fonts/KaTeX_Main-Bold.woff2 DELETED
Binary file (25.3 kB)
 
static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 DELETED
Binary file (16.8 kB)
 
static/vendor/katex/fonts/KaTeX_Main-Italic.woff2 DELETED
Binary file (17 kB)
 
static/vendor/katex/fonts/KaTeX_Main-Regular.woff2 DELETED
Binary file (26.3 kB)
 
static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 DELETED
Binary file (16.4 kB)
 
static/vendor/katex/fonts/KaTeX_Math-Italic.woff2 DELETED
Binary file (16.4 kB)
 
static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 DELETED
Binary file (12.2 kB)
 
static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 DELETED
Binary file (12 kB)
 
static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 DELETED
Binary file (10.3 kB)
 
static/vendor/katex/fonts/KaTeX_Script-Regular.woff2 DELETED
Binary file (9.64 kB)
 
static/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 DELETED
Binary file (5.47 kB)
 
static/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 DELETED
Binary file (5.21 kB)
 
static/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 DELETED
Binary file (3.62 kB)
 
static/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 DELETED
Binary file (4.93 kB)
 
static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 DELETED
Binary file (13.6 kB)
 
static/vendor/katex/fonts/OFL.txt DELETED
@@ -1,98 +0,0 @@
1
- Copyright (c) 2009-2010, Design Science, Inc. (<www.mathjax.org>),
2
- with Reserved Font Name STIX-Web, Asana-Math, STIX, XITS, Latin Modern.
3
-
4
- Copyright (c) 2014-2020, Khan Academy (<www.khanacademy.org>),
5
- with Reserved Font Name KaTeX_AMS, KaTeX_Caligraphic, KaTeX_Fraktur,
6
- KaTeX_Main, KaTeX_Math, KaTeX_SansSerif, KaTeX_Script, KaTeX_Size1,
7
- KaTeX_Size2, KaTeX_Size3, KaTeX_Size4, KaTeX_Typewriter.
8
-
9
- This Font Software is licensed under the SIL Open Font License, Version 1.1.
10
- This license is copied below, and is also available with a FAQ at:
11
- https://openfontlicense.org
12
-
13
- -----------------------------------------------------------
14
- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
15
- -----------------------------------------------------------
16
-
17
- PREAMBLE
18
- The goals of the Open Font License (OFL) are to stimulate worldwide
19
- development of collaborative font projects, to support the font creation
20
- efforts of academic and linguistic communities, and to provide a free and
21
- open framework in which fonts may be shared and improved in partnership
22
- with others.
23
-
24
- The OFL allows the licensed fonts to be used, studied, modified and
25
- redistributed freely as long as they are not sold by themselves. The
26
- fonts, including any derivative works, can be bundled, embedded,
27
- redistributed and/or sold with any software provided that any reserved
28
- names are not used by derivative works. The fonts and derivatives,
29
- however, cannot be released under any other type of license. The
30
- requirement for fonts to remain under this license does not apply
31
- to any document created using the fonts or their derivatives.
32
-
33
- DEFINITIONS
34
- "Font Software" refers to the set of files released by the Copyright
35
- Holder(s) under this license and clearly marked as such. This may
36
- include source files, build scripts and documentation.
37
-
38
- "Reserved Font Name" refers to any names specified as such after the
39
- copyright statement(s).
40
-
41
- "Original Version" refers to the collection of Font Software components as
42
- distributed by the Copyright Holder(s).
43
-
44
- "Modified Version" refers to any derivative made by adding to, deleting,
45
- or substituting -- in part or in whole -- any of the components of the
46
- Original Version, by changing formats or by porting the Font Software to a
47
- new environment.
48
-
49
- "Author" refers to any designer, engineer, programmer, technical
50
- writer or other person who contributed to the Font Software.
51
-
52
- PERMISSION & CONDITIONS
53
- Permission is hereby granted, free of charge, to any person obtaining
54
- a copy of the Font Software, to use, study, copy, merge, embed, modify,
55
- redistribute, and sell modified and unmodified copies of the Font
56
- Software, subject to the following conditions:
57
-
58
- 1) Neither the Font Software nor any of its individual components,
59
- in Original or Modified Versions, may be sold by itself.
60
-
61
- 2) Original or Modified Versions of the Font Software may be bundled,
62
- redistributed and/or sold with any software, provided that each copy
63
- contains the above copyright notice and this license. These can be
64
- included either as stand-alone text files, human-readable headers or
65
- in the appropriate machine-readable metadata fields within text or
66
- binary files as long as those fields can be easily viewed by the user.
67
-
68
- 3) No Modified Version of the Font Software may use the Reserved Font
69
- Name(s) unless explicit written permission is granted by the corresponding
70
- Copyright Holder. This restriction only applies to the primary font name as
71
- presented to the users.
72
-
73
- 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
74
- Software shall not be used to promote, endorse or advertise any
75
- Modified Version, except to acknowledge the contribution(s) of the
76
- Copyright Holder(s) and the Author(s) or with their explicit written
77
- permission.
78
-
79
- 5) The Font Software, modified or unmodified, in part or in whole,
80
- must be distributed entirely under this license, and must not be
81
- distributed under any other license. The requirement for fonts to
82
- remain under this license does not apply to any document created
83
- using the Font Software.
84
-
85
- TERMINATION
86
- This license becomes null and void if any of the above conditions are
87
- not met.
88
-
89
- DISCLAIMER
90
- THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
91
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
92
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
93
- OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
94
- COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
95
- INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
96
- DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
97
- FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
98
- OTHER DEALINGS IN THE FONT SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/vendor/katex/katex.min.css DELETED
@@ -1 +0,0 @@
1
- @font-face{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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.21"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;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 .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;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;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.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}
 
 
static/vendor/katex/katex.min.js DELETED
The diff for this file is too large to render. See raw diff
 
static/vendor/marked/LICENSE DELETED
@@ -1,60 +0,0 @@
1
- # License information
2
-
3
- ## Contribution License Agreement
4
-
5
- If you contribute code to this project, you are implicitly allowing your code
6
- to be distributed under the MIT license. You are also implicitly verifying that
7
- all code is your original work.
8
-
9
- ## Marked
10
-
11
- Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
12
- Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
13
-
14
- Permission is hereby granted, free of charge, to any person obtaining a copy
15
- of this software and associated documentation files (the "Software"), to deal
16
- in the Software without restriction, including without limitation the rights
17
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
- copies of the Software, and to permit persons to whom the Software is
19
- furnished to do so, subject to the following conditions:
20
-
21
- The above copyright notice and this permission notice shall be included in
22
- all copies or substantial portions of the Software.
23
-
24
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
- THE SOFTWARE.
31
-
32
- ## Markdown
33
-
34
- Copyright (c) 2004, John Gruber
35
- http://daringfireball.net/
36
- All rights reserved.
37
-
38
- Redistribution and use in source and binary forms, with or without
39
- modification, are permitted provided that the following conditions are met:
40
-
41
- * Redistributions of source code must retain the above copyright notice,
42
- this list of conditions and the following disclaimer.
43
- * Redistributions in binary form must reproduce the above copyright notice,
44
- this list of conditions and the following disclaimer in the documentation
45
- and/or other materials provided with the distribution.
46
- * Neither the name "Markdown" nor the names of its contributors may be used
47
- to endorse or promote products derived from this software without specific
48
- prior written permission.
49
-
50
- This software is provided by the copyright holders and contributors "as is"
51
- and any express or implied warranties, including, but not limited to, the
52
- implied warranties of merchantability and fitness for a particular purpose
53
- are disclaimed. In no event shall the copyright owner or contributors be
54
- liable for any direct, indirect, incidental, special, exemplary, or
55
- consequential damages (including, but not limited to, procurement of
56
- substitute goods or services; loss of use, data, or profits; or business
57
- interruption) however caused and on any theory of liability, whether in
58
- contract, strict liability, or tort (including negligence or otherwise)
59
- arising in any way out of the use of this software, even if advised of the
60
- possibility of such damage.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/vendor/marked/marked.min.js DELETED
@@ -1,8 +0,0 @@
1
- /**
2
- * Minified by jsDelivr using Terser v5.37.0.
3
- * Original file: /npm/marked@15.0.7/lib/marked.umd.js
4
- *
5
- * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
- */
7
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s={exec:()=>null};function r(e,t=""){let n="string"==typeof e?e:e.source;const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(i.caret,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}const i={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[\t ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},l=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,o=/(?:[*+-]|\d{1,9}[.)])/,a=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,c=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),h=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),p=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,u=/(?!\s*\])(?:\\.|[^\[\]\\])+/,g=r(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",u).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),k=r(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,o).getRegex(),d="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",f=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,x=r("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",f).replace("tag",d).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),b=r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),w={blockquote:r(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",b).getRegex(),code:/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,def:g,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:l,html:x,lheading:c,list:k,newline:/^(?:[ \t]*(?:\n|$))+/,paragraph:b,table:s,text:/^[^\n]+/},m=r("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),y={...w,lheading:h,table:m,paragraph:r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",m).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex()},$={...w,html:r("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",f).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:s,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:r(p).replace("hr",l).replace("heading"," *#{1,6} *[^\n]").replace("lheading",c).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},R=/^( {2,}|\\)\n(?!\s*$)/,S=/[\p{P}\p{S}]/u,T=/[\s\p{P}\p{S}]/u,z=/[^\s\p{P}\p{S}]/u,A=r(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,T).getRegex(),_=/(?!~)[\p{P}\p{S}]/u,P=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,I=r(P,"u").replace(/punct/g,S).getRegex(),L=r(P,"u").replace(/punct/g,_).getRegex(),B="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",C=r(B,"gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),q=r(B,"gu").replace(/notPunctSpace/g,/(?:[^\s\p{P}\p{S}]|~)/u).replace(/punctSpace/g,/(?!~)[\s\p{P}\p{S}]/u).replace(/punct/g,_).getRegex(),E=r("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),Z=r(/\\(punct)/,"gu").replace(/punct/g,S).getRegex(),v=r(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),D=r(f).replace("(?:--\x3e|$)","--\x3e").getRegex(),M=r("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment",D).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),O=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Q=r(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",O).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),j=r(/^!?\[(label)\]\[(ref)\]/).replace("label",O).replace("ref",u).getRegex(),N=r(/^!?\[(ref)\](?:\[\])?/).replace("ref",u).getRegex(),G={_backpedal:s,anyPunctuation:Z,autolink:v,blockSkip:/\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g,br:R,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:s,emStrongLDelim:I,emStrongRDelimAst:C,emStrongRDelimUnd:E,escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,link:Q,nolink:N,punctuation:A,reflink:j,reflinkSearch:r("reflink|nolink(?!\\()","g").replace("reflink",j).replace("nolink",N).getRegex(),tag:M,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,url:s},H={...G,link:r(/^!?\[(label)\]\((.*?)\)/).replace("label",O).getRegex(),reflink:r(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",O).getRegex()},X={...G,emStrongRDelimAst:q,emStrongLDelim:L,url:r(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/},F={...X,br:r(R).replace("{2,}","*").getRegex(),text:r(X.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},U={normal:w,gfm:y,pedantic:$},J={normal:G,gfm:X,breaks:F,pedantic:H},K={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},V=e=>K[e];function W(e,t){if(t){if(i.escapeTest.test(e))return e.replace(i.escapeReplace,V)}else if(i.escapeTestNoEncode.test(e))return e.replace(i.escapeReplaceNoEncode,V);return e}function Y(e){try{e=encodeURI(e).replace(i.percentDecode,"%")}catch{return null}return e}function ee(e,t){const n=e.replace(i.findPipe,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(i.splitPipe);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;s<n.length;s++)n[s]=n[s].trim().replace(i.slashPipe,"|");return n}function te(e,t,n){const s=e.length;if(0===s)return"";let r=0;for(;r<s;){if(e.charAt(s-r-1)!==t)break;r++}return e.slice(0,s-r)}function ne(e,t,n,s,r){const i=t.href,l=t.title||null,o=e[1].replace(r.other.outputLinkReplace,"$1");if("!"!==e[0].charAt(0)){s.state.inLink=!0;const e={type:"link",raw:n,href:i,title:l,text:o,tokens:s.inlineTokens(o)};return s.state.inLink=!1,e}return{type:"image",raw:n,href:i,title:l,text:o}}class se{options;rules;lexer;constructor(t){this.options=t||e.defaults}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:te(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t,n){const s=e.match(n.other.indentCodeCompensation);if(null===s)return t;const r=s[1];return t.split("\n").map((e=>{const t=e.match(n.other.beginningSpace);if(null===t)return e;const[s]=t;return s.length>=r.length?e.slice(r.length):e})).join("\n")}(e,t[3]||"",this.rules);return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){const t=te(e,"#");this.options.pedantic?e=t.trim():t&&!this.rules.other.endingSpaceChar.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:te(t[0],"\n")}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=te(t[0],"\n").split("\n"),n="",s="";const r=[];for(;e.length>0;){let t=!1;const i=[];let l;for(l=0;l<e.length;l++)if(this.rules.other.blockquoteStart.test(e[l]))i.push(e[l]),t=!0;else{if(t)break;i.push(e[l])}e=e.slice(l);const o=i.join("\n"),a=o.replace(this.rules.other.blockquoteSetextReplace,"\n $1").replace(this.rules.other.blockquoteSetextReplace2,"");n=n?`${n}\n${o}`:o,s=s?`${s}\n${a}`:a;const c=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(a,r,!0),this.lexer.state.top=c,0===e.length)break;const h=r.at(-1);if("code"===h?.type)break;if("blockquote"===h?.type){const t=h,i=t.raw+"\n"+e.join("\n"),l=this.blockquote(i);r[r.length-1]=l,n=n.substring(0,n.length-t.raw.length)+l.raw,s=s.substring(0,s.length-t.text.length)+l.text;break}if("list"!==h?.type);else{const t=h,i=t.raw+"\n"+e.join("\n"),l=this.list(i);r[r.length-1]=l,n=n.substring(0,n.length-h.raw.length)+l.raw,s=s.substring(0,s.length-t.raw.length)+l.raw,e=i.substring(r.at(-1).raw.length).split("\n")}}return{type:"blockquote",raw:n,tokens:r,text:s}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=this.rules.other.listItemRegex(n);let l=!1;for(;e;){let n=!1,s="",o="";if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;s=t[0],e=e.substring(s.length);let a=t[2].split("\n",1)[0].replace(this.rules.other.listReplaceTabs,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=!a.trim(),p=0;if(this.options.pedantic?(p=2,o=a.trimStart()):h?p=t[1].length+1:(p=t[2].search(this.rules.other.nonSpaceChar),p=p>4?1:p,o=a.slice(p),p+=t[1].length),h&&this.rules.other.blankLine.test(c)&&(s+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=this.rules.other.nextBulletRegex(p),n=this.rules.other.hrRegex(p),r=this.rules.other.fencesBeginRegex(p),i=this.rules.other.headingBeginRegex(p),l=this.rules.other.htmlBeginRegex(p);for(;e;){const u=e.split("\n",1)[0];let g;if(c=u,this.options.pedantic?(c=c.replace(this.rules.other.listReplaceNesting," "),g=c):g=c.replace(this.rules.other.tabCharGlobal," "),r.test(c))break;if(i.test(c))break;if(l.test(c))break;if(t.test(c))break;if(n.test(c))break;if(g.search(this.rules.other.nonSpaceChar)>=p||!c.trim())o+="\n"+g.slice(p);else{if(h)break;if(a.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4)break;if(r.test(a))break;if(i.test(a))break;if(n.test(a))break;o+="\n"+c}h||c.trim()||(h=!0),s+=u+"\n",e=e.substring(u.length+1),a=g.slice(p)}}r.loose||(l?r.loose=!0:this.rules.other.doubleBlankLine.test(s)&&(l=!0));let u,g=null;this.options.gfm&&(g=this.rules.other.listIsTask.exec(o),g&&(u="[ ] "!==g[0],o=o.replace(this.rules.other.listReplaceTask,""))),r.items.push({type:"list_item",raw:s,task:!!g,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=s}const o=r.items.at(-1);if(!o)return;o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e<r.items.length;e++)if(this.lexer.state.top=!1,r.items[e].tokens=this.lexer.blockTokens(r.items[e].text,[]),!r.loose){const t=r.items[e].tokens.filter((e=>"space"===e.type)),n=t.length>0&&t.some((e=>this.rules.other.anyLine.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e<r.items.length;e++)r.items[e].loose=!0;return r}}html(e){const t=this.rules.block.html.exec(e);if(t){return{type:"html",block:!0,raw:t[0],pre:"pre"===t[1]||"script"===t[1]||"style"===t[1],text:t[0]}}}def(e){const t=this.rules.block.def.exec(e);if(t){const e=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),n=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!this.rules.other.tableDelimiter.test(t[2]))return;const n=ee(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),r=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)this.rules.other.tableAlignRight.test(e)?i.align.push("right"):this.rules.other.tableAlignCenter.test(e)?i.align.push("center"):this.rules.other.tableAlignLeft.test(e)?i.align.push("left"):i.align.push(null);for(let e=0;e<n.length;e++)i.header.push({text:n[e],tokens:this.lexer.inline(n[e]),header:!0,align:i.align[e]});for(const e of r)i.rows.push(ee(e,i.header.length).map(((e,t)=>({text:e,tokens:this.lexer.inline(e),header:!1,align:i.align[t]}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(e)){if(!this.rules.other.endAngleBracket.test(e))return;const t=te(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s<e.length;s++)if("\\"===e[s])s++;else if(e[s]===t[0])n++;else if(e[s]===t[1]&&(n--,n<0))return s;return-1}(t[2],"()");if(e>-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=this.rules.other.pedanticHrefTitle.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),this.rules.other.startAngleBracket.test(n)&&(n=this.options.pedantic&&!this.rules.other.endAngleBracket.test(e)?n.slice(1):n.slice(1,-1)),ne(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return ne(n,e,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal," ");const n=this.rules.other.nonSpaceChar.test(e),s=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&s&&(e=e.substring(1,e.length-1)),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=t[1],n="mailto:"+e):(e=t[1],n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=t[0],n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=t[0],n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){const e=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:e}}}}class re{tokens;options;state;tokenizer;inlineQueue;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||e.defaults,this.options.tokenizer=this.options.tokenizer||new se,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={other:i,block:U.normal,inline:J.normal};this.options.pedantic?(n.block=U.pedantic,n.inline=J.pedantic):this.options.gfm&&(n.block=U.gfm,this.options.breaks?n.inline=J.breaks:n.inline=J.gfm),this.tokenizer.rules=n}static get rules(){return{block:U,inline:J}}static lex(e,t){return new re(t).lex(e)}static lexInline(e,t){return new re(t).inlineTokens(e)}lex(e){e=e.replace(i.carriageReturn,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e<this.inlineQueue.length;e++){const t=this.inlineQueue[e];this.inlineTokens(t.src,t.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[],n=!1){for(this.options.pedantic&&(e=e.replace(i.tabCharGlobal," ").replace(i.spaceLine,""));e;){let s;if(this.options.extensions?.block?.some((n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);const n=t.at(-1);1===s.raw.length&&void 0!==n?n.raw+="\n":t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.at(-1).src=n.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title});continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let r=e;if(this.options.extensions?.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(s=this.tokenizer.paragraph(r))){const i=t.at(-1);n&&"paragraph"===i?.type?(i.raw+="\n"+s.raw,i.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=i.text):t.push(s),n=r.length!==e.length,e=e.substring(s.raw.length)}else if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,s=null;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(s=this.tokenizer.rules.inline.reflinkSearch.exec(n));)e.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(s=this.tokenizer.rules.inline.blockSkip.exec(n));)n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(s=this.tokenizer.rules.inline.anyPunctuation.exec(n));)n=n.slice(0,s.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let r=!1,i="";for(;e;){let s;if(r||(i=""),r=!1,this.options.extensions?.inline?.some((n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.escape(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.tag(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.link(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===s.type&&"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s);continue}if(s=this.tokenizer.emStrong(e,n,i)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.codespan(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.br(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.del(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.autolink(e)){e=e.substring(s.raw.length),t.push(s);continue}if(!this.state.inLink&&(s=this.tokenizer.url(e))){e=e.substring(s.raw.length),t.push(s);continue}let l=e;if(this.options.extensions?.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(l=e.substring(0,t+1))}if(s=this.tokenizer.inlineText(l)){e=e.substring(s.raw.length),"_"!==s.raw.slice(-1)&&(i=s.raw.slice(-1)),r=!0;const n=t.at(-1);"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return t}}class ie{options;parser;constructor(t){this.options=t||e.defaults}space(e){return""}code({text:e,lang:t,escaped:n}){const s=(t||"").match(i.notSpaceStart)?.[0],r=e.replace(i.endingNewline,"")+"\n";return s?'<pre><code class="language-'+W(s)+'">'+(n?r:W(r,!0))+"</code></pre>\n":"<pre><code>"+(n?r:W(r,!0))+"</code></pre>\n"}blockquote({tokens:e}){return`<blockquote>\n${this.parser.parse(e)}</blockquote>\n`}html({text:e}){return e}heading({tokens:e,depth:t}){return`<h${t}>${this.parser.parseInline(e)}</h${t}>\n`}hr(e){return"<hr>\n"}list(e){const t=e.ordered,n=e.start;let s="";for(let t=0;t<e.items.length;t++){const n=e.items[t];s+=this.listitem(n)}const r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+s+"</"+r+">\n"}listitem(e){let t="";if(e.task){const n=this.checkbox({checked:!!e.checked});e.loose?"paragraph"===e.tokens[0]?.type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+W(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`<li>${t}</li>\n`}checkbox({checked:e}){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox">'}paragraph({tokens:e}){return`<p>${this.parser.parseInline(e)}</p>\n`}table(e){let t="",n="";for(let t=0;t<e.header.length;t++)n+=this.tablecell(e.header[t]);t+=this.tablerow({text:n});let s="";for(let t=0;t<e.rows.length;t++){const r=e.rows[t];n="";for(let e=0;e<r.length;e++)n+=this.tablecell(r[e]);s+=this.tablerow({text:n})}return s&&(s=`<tbody>${s}</tbody>`),"<table>\n<thead>\n"+t+"</thead>\n"+s+"</table>\n"}tablerow({text:e}){return`<tr>\n${e}</tr>\n`}tablecell(e){const t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>\n`}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${W(e,!0)}</code>`}br(e){return"<br>"}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,tokens:n}){const s=this.parser.parseInline(n),r=Y(e);if(null===r)return s;let i='<a href="'+(e=r)+'"';return t&&(i+=' title="'+W(t)+'"'),i+=">"+s+"</a>",i}image({href:e,title:t,text:n}){const s=Y(e);if(null===s)return W(n);let r=`<img src="${e=s}" alt="${n}"`;return t&&(r+=` title="${W(t)}"`),r+=">",r}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:W(e.text)}}class le{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}}class oe{options;renderer;textRenderer;constructor(t){this.options=t||e.defaults,this.options.renderer=this.options.renderer||new ie,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new le}static parse(e,t){return new oe(t).parse(e)}static parseInline(e,t){return new oe(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions?.renderers?.[r.type]){const e=r,t=this.options.extensions.renderers[e.type].call({parser:this},e);if(!1!==t||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(e.type)){n+=t||"";continue}}const i=r;switch(i.type){case"space":n+=this.renderer.space(i);continue;case"hr":n+=this.renderer.hr(i);continue;case"heading":n+=this.renderer.heading(i);continue;case"code":n+=this.renderer.code(i);continue;case"table":n+=this.renderer.table(i);continue;case"blockquote":n+=this.renderer.blockquote(i);continue;case"list":n+=this.renderer.list(i);continue;case"html":n+=this.renderer.html(i);continue;case"paragraph":n+=this.renderer.paragraph(i);continue;case"text":{let r=i,l=this.renderer.text(r);for(;s+1<e.length&&"text"===e[s+1].type;)r=e[++s],l+="\n"+this.renderer.text(r);n+=t?this.renderer.paragraph({type:"paragraph",raw:l,text:l,tokens:[{type:"text",raw:l,text:l,escaped:!0}]}):l;continue}default:{const e='Token with "'+i.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}parseInline(e,t=this.renderer){let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions?.renderers?.[r.type]){const e=this.options.extensions.renderers[r.type].call({parser:this},r);if(!1!==e||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(r.type)){n+=e||"";continue}}const i=r;switch(i.type){case"escape":case"text":n+=t.text(i);break;case"html":n+=t.html(i);break;case"link":n+=t.link(i);break;case"image":n+=t.image(i);break;case"strong":n+=t.strong(i);break;case"em":n+=t.em(i);break;case"codespan":n+=t.codespan(i);break;case"br":n+=t.br(i);break;case"del":n+=t.del(i);break;default:{const e='Token with "'+i.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}}class ae{options;block;constructor(t){this.options=t||e.defaults}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}provideLexer(){return this.block?re.lex:re.lexInline}provideParser(){return this.block?oe.parse:oe.parseInline}}class ce{defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=oe;Renderer=ie;TextRenderer=le;Lexer=re;Tokenizer=se;Hooks=ae;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(const s of e)switch(n=n.concat(t.call(this,s)),s.type){case"table":{const e=s;for(const s of e.header)n=n.concat(this.walkTokens(s.tokens,t));for(const s of e.rows)for(const e of s)n=n.concat(this.walkTokens(e.tokens,t));break}case"list":{const e=s;n=n.concat(this.walkTokens(e.items,t));break}default:{const e=s;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach((s=>{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new ie(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new se(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new ae;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;const s=n,r=e.hooks[s],i=t[s];ae.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return re.lex(e,t??this.defaults)}parser(e,t){return oe.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{const s={...n},r={...this.defaults,...s},i=this.onError(!!r.silent,!!r.async);if(!0===this.defaults.async&&!1===s.async)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(null==t)return i(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));r.hooks&&(r.hooks.options=r,r.hooks.block=e);const l=r.hooks?r.hooks.provideLexer():e?re.lex:re.lexInline,o=r.hooks?r.hooks.provideParser():e?oe.parse:oe.parseInline;if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(t):t).then((e=>l(e,r))).then((e=>r.hooks?r.hooks.processAllTokens(e):e)).then((e=>r.walkTokens?Promise.all(this.walkTokens(e,r.walkTokens)).then((()=>e)):e)).then((e=>o(e,r))).then((e=>r.hooks?r.hooks.postprocess(e):e)).catch(i);try{r.hooks&&(t=r.hooks.preprocess(t));let e=l(t,r);r.hooks&&(e=r.hooks.processAllTokens(e)),r.walkTokens&&this.walkTokens(e,r.walkTokens);let n=o(e,r);return r.hooks&&(n=r.hooks.postprocess(n)),n}catch(e){return i(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="<p>An error occurred:</p><pre>"+W(n.message+"",!0)+"</pre>";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const he=new ce;function pe(e,t){return he.parse(e,t)}pe.options=pe.setOptions=function(e){return he.setOptions(e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.getDefaults=t,pe.defaults=e.defaults,pe.use=function(...e){return he.use(...e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.walkTokens=function(e,t){return he.walkTokens(e,t)},pe.parseInline=he.parseInline,pe.Parser=oe,pe.parser=oe.parse,pe.Renderer=ie,pe.TextRenderer=le,pe.Lexer=re,pe.lexer=re.lex,pe.Tokenizer=se,pe.Hooks=ae,pe.parse=pe;const ue=pe.options,ge=pe.setOptions,ke=pe.use,de=pe.walkTokens,fe=pe.parseInline,xe=pe,be=oe.parse,we=re.lex;e.Hooks=ae,e.Lexer=re,e.Marked=ce,e.Parser=oe,e.Renderer=ie,e.TextRenderer=le,e.Tokenizer=se,e.getDefaults=t,e.lexer=we,e.marked=pe,e.options=ue,e.parse=xe,e.parseInline=fe,e.parser=be,e.setOptions=ge,e.use=ke,e.walkTokens=de}));
8
- //# sourceMappingURL=/sm/77bc6b5397c95683db705e4167297ec0b2a93706a155641f348e5685060f3b6e.map