openclacky 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +28 -7
- data/lib/clacky/agent/llm_caller.rb +23 -1
- data/lib/clacky/agent/session_serializer.rb +6 -1
- data/lib/clacky/agent/skill_manager.rb +18 -5
- data/lib/clacky/agent.rb +14 -5
- data/lib/clacky/anthropic_stream_aggregator.rb +135 -0
- data/lib/clacky/bedrock_stream_aggregator.rb +137 -0
- data/lib/clacky/brand_config.rb +68 -15
- data/lib/clacky/cli.rb +18 -19
- data/lib/clacky/client.rb +146 -17
- data/lib/clacky/default_skills/onboard/SKILL.md +6 -2
- data/lib/clacky/default_skills/onboard/scripts/import_external_skills.rb +50 -6
- data/lib/clacky/openai_stream_aggregator.rb +130 -0
- data/lib/clacky/server/channel/adapters/weixin/adapter.rb +169 -6
- data/lib/clacky/server/channel/channel_ui_controller.rb +6 -0
- data/lib/clacky/server/http_server.rb +9 -3
- data/lib/clacky/server/web_ui_controller.rb +8 -4
- data/lib/clacky/tools/terminal.rb +11 -0
- data/lib/clacky/ui2/components/input_area.rb +10 -1
- data/lib/clacky/ui2/components/todo_area.rb +22 -2
- data/lib/clacky/ui2/layout_manager.rb +70 -14
- data/lib/clacky/ui2/progress_handle.rb +86 -15
- data/lib/clacky/ui2/ui_controller.rb +47 -7
- data/lib/clacky/utils/logger.rb +7 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +6 -4
- data/lib/clacky/web/i18n.js +21 -6
- data/lib/clacky/web/index.html +8 -6
- data/lib/clacky/web/sessions.js +171 -58
- data/lib/clacky/web/vendor/katex/auto-render.min.js +1 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- data/lib/clacky/web/vendor/katex/katex.min.css +1 -0
- data/lib/clacky/web/vendor/katex/katex.min.js +1 -0
- data/lib/clacky/web/ws-dispatcher.js +19 -4
- data/lib/clacky.rb +3 -0
- data/scripts/build/src/install.sh.cc +15 -5
- data/scripts/install.ps1 +14 -3
- data/scripts/install.sh +15 -5
- metadata +28 -2
data/lib/clacky/web/sessions.js
CHANGED
|
@@ -90,20 +90,67 @@ const Sessions = (() => {
|
|
|
90
90
|
// if the marked library is unavailable.
|
|
91
91
|
function _markedParse(text) {
|
|
92
92
|
if (!text) return "";
|
|
93
|
+
|
|
94
|
+
// Extract math BEFORE marked so backslashes / underscores survive intact.
|
|
95
|
+
const math = [];
|
|
96
|
+
const PLACEHOLDER = (i) => `\u0000KTX${i}\u0000`;
|
|
97
|
+
let prepared = _extractMath(text, math, PLACEHOLDER);
|
|
98
|
+
|
|
99
|
+
let html;
|
|
93
100
|
if (typeof marked !== "undefined") {
|
|
94
|
-
// Custom renderer: open all links in a new tab
|
|
95
101
|
const renderer = new marked.Renderer();
|
|
96
102
|
renderer.link = function({ href, title, text }) {
|
|
97
103
|
const titleAttr = title ? ` title="${title}"` : "";
|
|
98
104
|
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
|
|
99
105
|
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
html = marked.parse(prepared, { breaks: true, gfm: true, renderer });
|
|
107
|
+
} else {
|
|
108
|
+
html = escapeHtml(prepared).replace(/\n/g, "<br>");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (math.length) {
|
|
112
|
+
html = html.replace(/\u0000KTX(\d+)\u0000/g, (_, i) => _renderMath(math[+i]));
|
|
113
|
+
}
|
|
114
|
+
return html;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Pull $$...$$, \[...\], $...$, \(...\) out of `text` and replace each with a
|
|
118
|
+
// sentinel placeholder so marked won't mangle the LaTeX source. The matched
|
|
119
|
+
// segments are pushed (with display flag) onto `out` for later KaTeX rendering.
|
|
120
|
+
function _extractMath(text, out, placeholder) {
|
|
121
|
+
// Order matters: longest/most-specific delimiters first.
|
|
122
|
+
const patterns = [
|
|
123
|
+
{ re: /\$\$([\s\S]+?)\$\$/g, display: true },
|
|
124
|
+
{ re: /\\\[([\s\S]+?)\\\]/g, display: true },
|
|
125
|
+
{ re: /\\\(([\s\S]+?)\\\)/g, display: false },
|
|
126
|
+
// Inline $...$: avoid $$, escaped \$, and prevent crossing newlines/blanks.
|
|
127
|
+
{ re: /(^|[^\$])\$(?!\s)([^\$\n]+?)(?<!\s)\$(?!\d)/g, display: false, hasPrefix: true },
|
|
128
|
+
];
|
|
129
|
+
let result = text;
|
|
130
|
+
for (const { re, display, hasPrefix } of patterns) {
|
|
131
|
+
result = result.replace(re, (m, a, b) => {
|
|
132
|
+
const body = hasPrefix ? b : a;
|
|
133
|
+
const idx = out.length;
|
|
134
|
+
out.push({ body, display });
|
|
135
|
+
return (hasPrefix ? a : "") + placeholder(idx);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function _renderMath({ body, display }) {
|
|
142
|
+
if (typeof katex === "undefined") {
|
|
143
|
+
return `<code>${escapeHtml((display ? "$$" : "$") + body + (display ? "$$" : "$"))}</code>`;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
return katex.renderToString(body, {
|
|
147
|
+
displayMode: display,
|
|
148
|
+
throwOnError: false,
|
|
149
|
+
output: "html",
|
|
150
|
+
});
|
|
151
|
+
} catch (e) {
|
|
152
|
+
return `<code class="katex-error">${escapeHtml(body)}</code>`;
|
|
104
153
|
}
|
|
105
|
-
// Fallback: plain escaped text with newlines preserved
|
|
106
|
-
return escapeHtml(text).replace(/\n/g, "<br>");
|
|
107
154
|
}
|
|
108
155
|
|
|
109
156
|
// Build the collapsible thinking block HTML for a given rendered-HTML content string.
|
|
@@ -112,7 +159,7 @@ const Sessions = (() => {
|
|
|
112
159
|
return `<details class="thinking-block">` +
|
|
113
160
|
`<summary class="thinking-summary">` +
|
|
114
161
|
`<span class="thinking-chevron">›</span>` +
|
|
115
|
-
`<span class="thinking-label">
|
|
162
|
+
`<span class="thinking-label">Thoughts</span>` +
|
|
116
163
|
`</summary>` +
|
|
117
164
|
`<div class="thinking-body">${renderedHtml}</div>` +
|
|
118
165
|
`</details>`;
|
|
@@ -349,9 +396,7 @@ const Sessions = (() => {
|
|
|
349
396
|
document.getElementById("btn-welcome-new")
|
|
350
397
|
.addEventListener("click", () => Sessions.create("general"));
|
|
351
398
|
|
|
352
|
-
// Modal:
|
|
353
|
-
document.getElementById("new-session-modal-close")
|
|
354
|
-
.addEventListener("click", () => Sessions.closeNewSessionModal());
|
|
399
|
+
// Modal: cancel / create / overlay click
|
|
355
400
|
document.getElementById("new-session-cancel")
|
|
356
401
|
.addEventListener("click", () => Sessions.closeNewSessionModal());
|
|
357
402
|
document.getElementById("new-session-create")
|
|
@@ -2051,6 +2096,15 @@ const Sessions = (() => {
|
|
|
2051
2096
|
// Here we only update the interrupt button visibility.
|
|
2052
2097
|
const interrupt = $("btn-interrupt");
|
|
2053
2098
|
if (interrupt) interrupt.style.display = status === "running" ? "" : "none";
|
|
2099
|
+
|
|
2100
|
+
// Swap input placeholder so the user knows they can still send extra
|
|
2101
|
+
// info while the agent is working.
|
|
2102
|
+
const inp = $("user-input");
|
|
2103
|
+
if (inp) {
|
|
2104
|
+
const key = status === "running" ? "chat.input.placeholderRunning" : "chat.input.placeholder";
|
|
2105
|
+
inp.setAttribute("data-i18n-placeholder", key);
|
|
2106
|
+
inp.setAttribute("placeholder", I18n.t(key));
|
|
2107
|
+
}
|
|
2054
2108
|
},
|
|
2055
2109
|
|
|
2056
2110
|
/**
|
|
@@ -2065,6 +2119,7 @@ const Sessions = (() => {
|
|
|
2065
2119
|
|
|
2066
2120
|
/** Update the session info bar below the chat header with current session metadata. */
|
|
2067
2121
|
updateInfoBar(s) {
|
|
2122
|
+
this._lastSession = s;
|
|
2068
2123
|
if (!s) {
|
|
2069
2124
|
// Hide all spans when no session
|
|
2070
2125
|
["sib-id", "sib-status", "sib-dir", "sib-mode", "sib-model", "sib-tasks", "sib-cost"].forEach(id => {
|
|
@@ -2103,8 +2158,8 @@ const Sessions = (() => {
|
|
|
2103
2158
|
const sibDir = $("sib-dir");
|
|
2104
2159
|
if (sibDir && s.working_dir) {
|
|
2105
2160
|
sibDir.textContent = s.working_dir;
|
|
2106
|
-
sibDir.title = s.working_dir
|
|
2107
|
-
|
|
2161
|
+
sibDir.title = `${s.working_dir} (${I18n.t("sib.dir.tooltip")})`;
|
|
2162
|
+
sibDir.dataset.workingDir = s.working_dir;
|
|
2108
2163
|
sibDir.dataset.sessionId = s.id;
|
|
2109
2164
|
}
|
|
2110
2165
|
|
|
@@ -2138,7 +2193,7 @@ const Sessions = (() => {
|
|
|
2138
2193
|
|
|
2139
2194
|
// Tasks
|
|
2140
2195
|
const sibTasks = $("sib-tasks");
|
|
2141
|
-
if (sibTasks) sibTasks.textContent =
|
|
2196
|
+
if (sibTasks) sibTasks.textContent = I18n.t("sessions.metaTasks", { n: s.total_tasks || 0 });
|
|
2142
2197
|
|
|
2143
2198
|
// Cost — show N/A when pricing is unknown (estimated)
|
|
2144
2199
|
const sibCost = $("sib-cost");
|
|
@@ -2409,13 +2464,19 @@ const Sessions = (() => {
|
|
|
2409
2464
|
}
|
|
2410
2465
|
},
|
|
2411
2466
|
|
|
2412
|
-
appendInfo(text) {
|
|
2467
|
+
appendInfo(text, subline) {
|
|
2413
2468
|
Sessions.collapseToolGroup();
|
|
2414
2469
|
const messages = $("messages");
|
|
2415
2470
|
const el = document.createElement("div");
|
|
2416
|
-
el.className = "msg msg-info";
|
|
2471
|
+
el.className = subline ? "msg msg-info msg-info-main" : "msg msg-info";
|
|
2417
2472
|
el.textContent = text;
|
|
2418
2473
|
messages.appendChild(el);
|
|
2474
|
+
if (subline) {
|
|
2475
|
+
const sub = document.createElement("div");
|
|
2476
|
+
sub.className = "msg msg-info-sub";
|
|
2477
|
+
sub.textContent = subline;
|
|
2478
|
+
messages.appendChild(sub);
|
|
2479
|
+
}
|
|
2419
2480
|
_scrollToBottomIfNeeded(messages);
|
|
2420
2481
|
},
|
|
2421
2482
|
|
|
@@ -2489,11 +2550,58 @@ const Sessions = (() => {
|
|
|
2489
2550
|
_getProgressState(id) {
|
|
2490
2551
|
if (!id) return null;
|
|
2491
2552
|
if (!Sessions._sessionProgress[id]) {
|
|
2492
|
-
Sessions._sessionProgress[id] = { el: null, interval: null, startTime: null, type: null, displayText: null };
|
|
2553
|
+
Sessions._sessionProgress[id] = { el: null, interval: null, startTime: null, type: null, displayText: null, metadata: null, lastChunkAt: null };
|
|
2493
2554
|
}
|
|
2494
2555
|
return Sessions._sessionProgress[id];
|
|
2495
2556
|
},
|
|
2496
2557
|
|
|
2558
|
+
// Compact a token count: 1234 → "1.2k", 12345 → "12k", 1234567 → "1.2M".
|
|
2559
|
+
_compactTokenCount(n) {
|
|
2560
|
+
if (n < 1000) return String(n);
|
|
2561
|
+
if (n < 1_000_000) {
|
|
2562
|
+
const k = n / 1000;
|
|
2563
|
+
return k >= 10 ? `${Math.floor(k)}k` : `${k.toFixed(1)}k`;
|
|
2564
|
+
}
|
|
2565
|
+
const m = n / 1_000_000;
|
|
2566
|
+
return m >= 10 ? `${Math.floor(m)}M` : `${m.toFixed(1)}M`;
|
|
2567
|
+
},
|
|
2568
|
+
|
|
2569
|
+
// Render LLM streaming output token count as "↓ 234 tokens".
|
|
2570
|
+
// Returns null when no positive output_tokens — matches CLI behaviour
|
|
2571
|
+
// (input is hidden mid-stream because most providers only ship
|
|
2572
|
+
// input_tokens with the final usage frame).
|
|
2573
|
+
_formatTokenSuffix(metadata) {
|
|
2574
|
+
if (!metadata) return null;
|
|
2575
|
+
const output = metadata.output_tokens;
|
|
2576
|
+
if (output == null || output <= 0) return null;
|
|
2577
|
+
return `↓ ${Sessions._compactTokenCount(output)} tokens`;
|
|
2578
|
+
},
|
|
2579
|
+
|
|
2580
|
+
// Compose the live progress line:
|
|
2581
|
+
// "<text>… (Ns · ↓N tokens · reasoning…)"
|
|
2582
|
+
// The "reasoning" tail surfaces inter-chunk silence so users see
|
|
2583
|
+
// the model is in extended thinking, not stuck. Threshold mirrors
|
|
2584
|
+
// ProgressHandle::IDLE_HINT_THRESHOLD_SECONDS. Animated dots avoid
|
|
2585
|
+
// duplicating the elapsed counter.
|
|
2586
|
+
_composeProgressLine(displayText, startTime, metadata, lastChunkAt) {
|
|
2587
|
+
const now = Date.now();
|
|
2588
|
+
const elapsed = startTime ? Math.floor((now - startTime) / 1000) : 0;
|
|
2589
|
+
const tokenStr = Sessions._formatTokenSuffix(metadata);
|
|
2590
|
+
const parts = [];
|
|
2591
|
+
if (elapsed > 0) parts.push(`${elapsed}s`);
|
|
2592
|
+
if (tokenStr) parts.push(tokenStr);
|
|
2593
|
+
if (tokenStr && lastChunkAt) {
|
|
2594
|
+
const idle = Math.floor((now - lastChunkAt) / 1000);
|
|
2595
|
+
if (idle >= 2) {
|
|
2596
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
2597
|
+
const frame = frames[Math.floor(now / 250) % frames.length];
|
|
2598
|
+
parts.push(`reasoning ${frame} `);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
if (parts.length === 0) return displayText;
|
|
2602
|
+
return `${displayText}… (${parts.join(" · ")})`;
|
|
2603
|
+
},
|
|
2604
|
+
|
|
2497
2605
|
// Build the display label for a given progress type (pure — no side effects).
|
|
2498
2606
|
_buildDisplayText(text, progress_type, metadata) {
|
|
2499
2607
|
if (progress_type === "thinking") {
|
|
@@ -2529,23 +2637,19 @@ const Sessions = (() => {
|
|
|
2529
2637
|
|
|
2530
2638
|
const el = document.createElement("div");
|
|
2531
2639
|
el.className = "progress-msg";
|
|
2532
|
-
|
|
2533
|
-
// Show elapsed time immediately (not just after first setInterval tick)
|
|
2534
|
-
const initialElapsed = Math.floor((Date.now() - state.startTime) / 1000);
|
|
2535
|
-
el.textContent = initialElapsed > 0
|
|
2536
|
-
? `⟳ ${displayText}… (${initialElapsed}s)`
|
|
2537
|
-
: `⟳ ${displayText}`;
|
|
2640
|
+
el.textContent = Sessions._composeProgressLine(state.displayText, state.startTime, state.metadata, state.lastChunkAt);
|
|
2538
2641
|
messages.appendChild(el);
|
|
2539
2642
|
state.el = el;
|
|
2540
2643
|
_scrollToBottomIfNeeded(messages);
|
|
2541
2644
|
|
|
2542
|
-
//
|
|
2645
|
+
// Tick at 250ms so streaming token counts feel live. The elapsed
|
|
2646
|
+
// counter only displays whole seconds, but token numbers update at
|
|
2647
|
+
// sub-second cadence on fast streams.
|
|
2543
2648
|
state.interval = setInterval(() => {
|
|
2544
|
-
const elapsed = Math.floor((Date.now() - state.startTime) / 1000);
|
|
2545
2649
|
if (state.el) {
|
|
2546
|
-
state.el.textContent =
|
|
2650
|
+
state.el.textContent = Sessions._composeProgressLine(state.displayText, state.startTime, state.metadata, state.lastChunkAt);
|
|
2547
2651
|
}
|
|
2548
|
-
},
|
|
2652
|
+
}, 250);
|
|
2549
2653
|
},
|
|
2550
2654
|
|
|
2551
2655
|
// Detach only the DOM element and timer for a session, preserving logical state
|
|
@@ -2567,54 +2671,53 @@ const Sessions = (() => {
|
|
|
2567
2671
|
const sid = _activeId;
|
|
2568
2672
|
if (!sid) return;
|
|
2569
2673
|
|
|
2570
|
-
const newStartTime
|
|
2571
|
-
const newDisplayText = Sessions._buildDisplayText(text, progress_type, metadata);
|
|
2674
|
+
const newStartTime = startedAt || Date.now();
|
|
2572
2675
|
|
|
2573
|
-
// If this session already has a visible progress indicator (DOM element
|
|
2574
|
-
// attached), update it in-place instead of tear-down/rebuild. This avoids
|
|
2575
|
-
// the jarring flicker when replay_live_state arrives shortly after the
|
|
2576
|
-
// eager-attach on session switch.
|
|
2577
2676
|
const existing = Sessions._sessionProgress[sid];
|
|
2578
2677
|
if (existing && existing.el) {
|
|
2579
|
-
//
|
|
2580
|
-
//
|
|
2678
|
+
// Same start time → same progress phase. Most common case during LLM
|
|
2679
|
+
// streaming (token counts arriving every ~250ms with message: null).
|
|
2680
|
+
// Keep the existing displayText so the random "thinking" verb does
|
|
2681
|
+
// NOT churn on every chunk. Just refresh metadata; the interval tick
|
|
2682
|
+
// will repaint with fresh tokens.
|
|
2581
2683
|
if (existing.startTime === newStartTime) {
|
|
2582
|
-
existing.type
|
|
2684
|
+
existing.type = progress_type;
|
|
2685
|
+
existing.metadata = metadata || {};
|
|
2686
|
+
existing.lastChunkAt = Date.now();
|
|
2687
|
+
// Only adopt a new displayText if the server actually sent one.
|
|
2688
|
+
if (text) existing.displayText = Sessions._buildDisplayText(text, progress_type, metadata);
|
|
2583
2689
|
return;
|
|
2584
2690
|
}
|
|
2585
|
-
// Different start time → new progress phase.
|
|
2586
|
-
//
|
|
2587
|
-
// never sees the indicator disappear/reappear.
|
|
2691
|
+
// Different start time → new progress phase. Update state in-place
|
|
2692
|
+
// and reset the timer base, but reuse the existing DOM element so
|
|
2693
|
+
// the user never sees the indicator disappear/reappear.
|
|
2694
|
+
const newDisplayText = Sessions._buildDisplayText(text, progress_type, metadata);
|
|
2588
2695
|
existing.type = progress_type;
|
|
2589
2696
|
existing.startTime = newStartTime;
|
|
2590
2697
|
existing.displayText = newDisplayText;
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
existing.el.textContent =
|
|
2594
|
-
? `⟳ ${newDisplayText}… (${elapsed}s)`
|
|
2595
|
-
: `⟳ ${newDisplayText}`;
|
|
2596
|
-
// Restart interval with new startTime
|
|
2698
|
+
existing.metadata = metadata || {};
|
|
2699
|
+
existing.lastChunkAt = newStartTime;
|
|
2700
|
+
existing.el.textContent = Sessions._composeProgressLine(newDisplayText, newStartTime, metadata, existing.lastChunkAt);
|
|
2597
2701
|
if (existing.interval) clearInterval(existing.interval);
|
|
2598
2702
|
existing.interval = setInterval(() => {
|
|
2599
|
-
const e = Math.floor((Date.now() - existing.startTime) / 1000);
|
|
2600
2703
|
if (existing.el) {
|
|
2601
|
-
existing.el.textContent =
|
|
2704
|
+
existing.el.textContent = Sessions._composeProgressLine(existing.displayText, existing.startTime, existing.metadata, existing.lastChunkAt);
|
|
2602
2705
|
}
|
|
2603
|
-
},
|
|
2706
|
+
}, 250);
|
|
2604
2707
|
_scrollToBottomIfNeeded($("messages"));
|
|
2605
2708
|
return;
|
|
2606
2709
|
}
|
|
2607
2710
|
|
|
2608
2711
|
// No existing visible progress — create from scratch.
|
|
2609
|
-
// Clear any stale logical state first.
|
|
2610
2712
|
Sessions.clearProgress(sid);
|
|
2611
2713
|
|
|
2612
2714
|
const state = Sessions._getProgressState(sid);
|
|
2613
2715
|
state.type = progress_type;
|
|
2614
2716
|
state.startTime = newStartTime;
|
|
2615
|
-
state.displayText =
|
|
2717
|
+
state.displayText = Sessions._buildDisplayText(text, progress_type, metadata);
|
|
2718
|
+
state.metadata = metadata || {};
|
|
2719
|
+
state.lastChunkAt = newStartTime;
|
|
2616
2720
|
|
|
2617
|
-
// Attach DOM + timer
|
|
2618
2721
|
Sessions._attachProgressUI(sid);
|
|
2619
2722
|
},
|
|
2620
2723
|
|
|
@@ -2654,6 +2757,8 @@ const Sessions = (() => {
|
|
|
2654
2757
|
state.startTime = null;
|
|
2655
2758
|
state.type = null;
|
|
2656
2759
|
state.displayText = null;
|
|
2760
|
+
state.metadata = null;
|
|
2761
|
+
state.lastChunkAt = null;
|
|
2657
2762
|
},
|
|
2658
2763
|
|
|
2659
2764
|
// Delete all progress state for a session (used when session is removed).
|
|
@@ -3208,9 +3313,9 @@ const Sessions = (() => {
|
|
|
3208
3313
|
if (dirEl) {
|
|
3209
3314
|
e.stopPropagation();
|
|
3210
3315
|
const sessionId = dirEl.dataset.sessionId;
|
|
3211
|
-
const currentDir = dirEl.
|
|
3212
|
-
|
|
3213
|
-
const newDir = await Modal.prompt("
|
|
3316
|
+
const currentDir = dirEl.dataset.workingDir || dirEl.textContent;
|
|
3317
|
+
|
|
3318
|
+
const newDir = await Modal.prompt(I18n.t("sib.dir.changePrompt"), currentDir);
|
|
3214
3319
|
if (newDir && newDir !== currentDir) {
|
|
3215
3320
|
_changeWorkingDirectory(sessionId, newDir);
|
|
3216
3321
|
}
|
|
@@ -3280,7 +3385,10 @@ const Sessions = (() => {
|
|
|
3280
3385
|
}
|
|
3281
3386
|
|
|
3282
3387
|
function _populateSessionActionsDropdown(dd, sessionId) {
|
|
3283
|
-
const t = (key, fallback) =>
|
|
3388
|
+
const t = (key, fallback) => {
|
|
3389
|
+
const s = I18n.t(key);
|
|
3390
|
+
return (s && s !== key) ? s : fallback;
|
|
3391
|
+
};
|
|
3284
3392
|
dd.innerHTML = "";
|
|
3285
3393
|
|
|
3286
3394
|
// Download item
|
|
@@ -3320,7 +3428,7 @@ const Sessions = (() => {
|
|
|
3320
3428
|
if (!res.ok) {
|
|
3321
3429
|
let msg = `HTTP ${res.status}`;
|
|
3322
3430
|
try { const data = await res.json(); if (data.error) msg = data.error; } catch (_) {}
|
|
3323
|
-
alert(
|
|
3431
|
+
alert(I18n.t("sessions.export.failed") + ": " + msg);
|
|
3324
3432
|
return;
|
|
3325
3433
|
}
|
|
3326
3434
|
const blob = await res.blob();
|
|
@@ -3342,7 +3450,7 @@ const Sessions = (() => {
|
|
|
3342
3450
|
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
3343
3451
|
} catch (err) {
|
|
3344
3452
|
console.error("Session export failed:", err);
|
|
3345
|
-
alert(
|
|
3453
|
+
alert(I18n.t("sessions.export.failed") + ": " + err.message);
|
|
3346
3454
|
} finally {
|
|
3347
3455
|
if (btnEl) {
|
|
3348
3456
|
try { btnEl.disabled = wasDisabled; } catch (_) {}
|
|
@@ -3370,7 +3478,8 @@ const Sessions = (() => {
|
|
|
3370
3478
|
const sibDir = $("sib-dir");
|
|
3371
3479
|
if (sibDir) {
|
|
3372
3480
|
sibDir.textContent = newDir;
|
|
3373
|
-
sibDir.title = newDir
|
|
3481
|
+
sibDir.title = `${newDir} (${I18n.t("sib.dir.tooltip")})`;
|
|
3482
|
+
sibDir.dataset.workingDir = newDir;
|
|
3374
3483
|
}
|
|
3375
3484
|
|
|
3376
3485
|
console.log(`Changed session ${sessionId} directory to ${newDir}`);
|
|
@@ -3381,3 +3490,7 @@ const Sessions = (() => {
|
|
|
3381
3490
|
}
|
|
3382
3491
|
|
|
3383
3492
|
})();
|
|
3493
|
+
|
|
3494
|
+
document.addEventListener("langchange", () => {
|
|
3495
|
+
if (Sessions._lastSession) Sessions.updateInfoBar(Sessions._lastSession);
|
|
3496
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
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={771: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={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=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},i=/^\\begin{/;var a=function(e,t){let r;const o=[],a=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e<r.length;e++)if("text"===r[e].type)o.appendChild(document.createTextNode(r[e].data));else{const i=document.createElement("span");let a=r[e].data;n.displayMode=r[e].display;try{n.preProcess&&(a=n.preProcess(a)),t().render(a,i,n)}catch(i){if(!(i instanceof t().ParseError))throw i;n.errorCallback("KaTeX auto-render: Failed to parse `"+r[e].data+"` with ",i),o.appendChild(document.createTextNode(r[e].rawData));continue}o.appendChild(i)}return o},s=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 s=l(o,t);if(s){for(let e=0;e<a;e++)r.nextSibling.remove();n+=s.childNodes.length-1,e.replaceChild(s,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+" ")))&&s(r,t)}}};var d=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||{},s(e,n)}}(),o=o.default}()}));
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2")}@font-face{font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2")}.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.11"}.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 .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}
|