pi-studio 0.9.57 → 0.9.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +12 -5
- package/ROADMAP.md +10 -1
- package/client/studio-client.js +524 -52
- package/client/studio.css +171 -0
- package/index.ts +17 -5
- package/package.json +1 -1
package/client/studio-client.js
CHANGED
|
@@ -148,6 +148,8 @@
|
|
|
148
148
|
const stripAnnotationsBtn = document.getElementById("stripAnnotationsBtn");
|
|
149
149
|
const highlightSelect = document.getElementById("highlightSelect");
|
|
150
150
|
const lineNumbersSelect = document.getElementById("lineNumbersSelect");
|
|
151
|
+
const studioPaneLayoutSelect = document.getElementById("studioPaneLayoutSelect");
|
|
152
|
+
const activityTrackingSelect = document.getElementById("activityTrackingSelect");
|
|
151
153
|
const editorFontSizeSelect = document.getElementById("editorFontSizeSelect");
|
|
152
154
|
const annotationModeSelect = document.getElementById("annotationModeSelect");
|
|
153
155
|
const compactBtn = document.getElementById("compactBtn");
|
|
@@ -427,8 +429,8 @@
|
|
|
427
429
|
rightViewSelect.title = isWatchedFilePreview
|
|
428
430
|
? "Read-only watched preview follows this file on disk."
|
|
429
431
|
: (isEditorOnlyMode
|
|
430
|
-
? "Editor-only views: Editor Preview, contextual Quarto Preview for .qmd/.md/.markdown files, Changes, Files, REPL, or Side questions. F7 cycles; Cmd/Ctrl+Alt+3/5/6/7/8 switch directly to numbered right-pane views, and Cmd/Ctrl+Alt+F/Q open Files/Side questions."
|
|
431
|
-
: "Right pane view mode. F7 cycles, including contextual Quarto Preview for file-backed .qmd, .md, and .markdown documents; Cmd/Ctrl+Alt+1–8 switches directly between the numbered views. Cmd/Ctrl+Alt+P/E/W/F/Q keep mnemonic shortcuts for Preview, Editor Preview, Working, Files, and Side questions.");
|
|
432
|
+
? "Editor-only views: Editor Preview, contextual Quarto Preview for .qmd/.md/.markdown files, Changes, Files, REPL, or Side questions. F7 cycles; Cmd/Ctrl+Alt+3/5/6/7/8 switch directly to numbered right-pane views, and Cmd/Ctrl+Alt+F/R/Q open Files/REPL/Side questions."
|
|
433
|
+
: "Right pane view mode. F7 cycles, including contextual Quarto Preview for file-backed .qmd, .md, and .markdown documents; Cmd/Ctrl+Alt+1–8 switches directly between the numbered views. Cmd/Ctrl+Alt+P/E/W/F/R/Q keep mnemonic shortcuts for Preview, Editor Preview, Working, Files, REPL, and Side questions.");
|
|
432
434
|
}
|
|
433
435
|
|
|
434
436
|
function getInitialRightView(source) {
|
|
@@ -481,6 +483,8 @@
|
|
|
481
483
|
const REPL_TRANSCRIPT_MAX_CHARS = 200_000;
|
|
482
484
|
const REPL_JOURNAL_OUTPUT_MAX_CHARS = 80_000;
|
|
483
485
|
const REPL_JOURNAL_MAX_ENTRIES = 80;
|
|
486
|
+
const REPL_QUICK_DRAFT_MAX_CHARS = 20_000;
|
|
487
|
+
const REPL_QUICK_DRAFT_MAX_SESSIONS = 12;
|
|
484
488
|
const PDF_EXPORT_FETCH_TIMEOUT_MS = 180_000;
|
|
485
489
|
const HTML_EXPORT_FETCH_TIMEOUT_MS = 180_000;
|
|
486
490
|
const HTML_ARTIFACT_MATH_RENDER_FETCH_TIMEOUT_MS = 30_000;
|
|
@@ -588,6 +592,10 @@
|
|
|
588
592
|
let replFollow = true;
|
|
589
593
|
let replPollTimer = null;
|
|
590
594
|
let replBusy = false;
|
|
595
|
+
let replPendingRequestId = "";
|
|
596
|
+
const replQuickDrafts = new Map();
|
|
597
|
+
let replQuickPending = null;
|
|
598
|
+
let replQuickFocusRequested = false;
|
|
591
599
|
let replSendMode = (() => {
|
|
592
600
|
try {
|
|
593
601
|
const stored = window.localStorage && window.localStorage.getItem("piStudio.replSendMode");
|
|
@@ -622,7 +630,7 @@
|
|
|
622
630
|
mode: typeof entry.mode === "string" ? entry.mode : "raw",
|
|
623
631
|
prose: typeof entry.prose === "string" ? entry.prose : "",
|
|
624
632
|
code: typeof entry.code === "string" ? entry.code : "",
|
|
625
|
-
output: typeof entry.output === "string" ? entry.output : "",
|
|
633
|
+
output: typeof entry.output === "string" ? stripStudioReplSubmissionEcho(entry.output) : "",
|
|
626
634
|
beforeTranscript: "",
|
|
627
635
|
status: typeof entry.status === "string" ? entry.status : "sent",
|
|
628
636
|
skippedChunks: Math.max(0, Math.floor(Number(entry.skippedChunks) || 0)),
|
|
@@ -1302,6 +1310,10 @@
|
|
|
1302
1310
|
return {
|
|
1303
1311
|
sessionName,
|
|
1304
1312
|
target: typeof session.target === "string" ? session.target : (sessionName + ":0.0"),
|
|
1313
|
+
tmuxSessionId: typeof session.tmuxSessionId === "string" ? session.tmuxSessionId : "",
|
|
1314
|
+
tmuxSessionCreatedAt: typeof session.tmuxSessionCreatedAt === "number" && Number.isFinite(session.tmuxSessionCreatedAt)
|
|
1315
|
+
? session.tmuxSessionCreatedAt
|
|
1316
|
+
: 0,
|
|
1305
1317
|
runtime: typeof session.runtime === "string" ? session.runtime : "unknown",
|
|
1306
1318
|
label: typeof session.label === "string" && session.label.trim() ? session.label.trim() : sessionName,
|
|
1307
1319
|
source: typeof session.source === "string" ? session.source : "tmux",
|
|
@@ -1430,6 +1442,63 @@
|
|
|
1430
1442
|
: "Display short submissions in full, truncating after 6 lines or 600 characters, with compact anchors and a plain output divider.");
|
|
1431
1443
|
}
|
|
1432
1444
|
|
|
1445
|
+
function getReplQuickDraftKey(session) {
|
|
1446
|
+
const normalized = normalizeReplSession(session);
|
|
1447
|
+
if (!normalized || !normalized.tmuxSessionId || normalized.tmuxSessionCreatedAt <= 0) return "";
|
|
1448
|
+
return [normalizeReplRuntime(normalized.runtime), normalized.tmuxSessionId, normalized.tmuxSessionCreatedAt].join("\u001f");
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
function getReplQuickDraft(session) {
|
|
1452
|
+
const key = getReplQuickDraftKey(session);
|
|
1453
|
+
return key ? String(replQuickDrafts.get(key) || "") : "";
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function setReplQuickDraft(session, value) {
|
|
1457
|
+
const key = getReplQuickDraftKey(session);
|
|
1458
|
+
if (!key) return "";
|
|
1459
|
+
const draft = String(value || "").slice(0, REPL_QUICK_DRAFT_MAX_CHARS);
|
|
1460
|
+
if (!draft) {
|
|
1461
|
+
replQuickDrafts.delete(key);
|
|
1462
|
+
return "";
|
|
1463
|
+
}
|
|
1464
|
+
if (!replQuickDrafts.has(key) && replQuickDrafts.size >= REPL_QUICK_DRAFT_MAX_SESSIONS) {
|
|
1465
|
+
const oldestKey = replQuickDrafts.keys().next().value;
|
|
1466
|
+
if (oldestKey) replQuickDrafts.delete(oldestKey);
|
|
1467
|
+
}
|
|
1468
|
+
replQuickDrafts.set(key, draft);
|
|
1469
|
+
return draft;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function settleReplQuickPending(requestId, submitted) {
|
|
1473
|
+
if (!replQuickPending || !requestId || replQuickPending.requestId !== requestId) return false;
|
|
1474
|
+
const pending = replQuickPending;
|
|
1475
|
+
replQuickPending = null;
|
|
1476
|
+
if (replPendingRequestId === requestId) replPendingRequestId = "";
|
|
1477
|
+
replBusy = Boolean(replPendingRequestId);
|
|
1478
|
+
if (submitted && replQuickDrafts.get(pending.key) === pending.text) {
|
|
1479
|
+
replQuickDrafts.delete(pending.key);
|
|
1480
|
+
}
|
|
1481
|
+
return true;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
function reconcileReplQuickPending() {
|
|
1485
|
+
if (!replQuickPending) return false;
|
|
1486
|
+
const entry = replJournalEntries.find((candidate) => candidate.requestId === replQuickPending.requestId);
|
|
1487
|
+
if (!entry || entry.status === "sending") return false;
|
|
1488
|
+
return settleReplQuickPending(entry.requestId, entry.status !== "error");
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
function updateReplQuickComposerActions() {
|
|
1492
|
+
if (!critiqueViewEl || rightView !== "repl") return;
|
|
1493
|
+
const session = getActiveReplSessionForCurrentRuntime();
|
|
1494
|
+
const draft = getReplQuickDraft(session);
|
|
1495
|
+
const pending = Boolean(replQuickPending && replQuickPending.key === getReplQuickDraftKey(session));
|
|
1496
|
+
const sendButton = critiqueViewEl.querySelector("[data-repl-action='quick-send']");
|
|
1497
|
+
const clearButton = critiqueViewEl.querySelector("[data-repl-action='quick-clear']");
|
|
1498
|
+
if (sendButton) sendButton.disabled = !session || replTmuxAvailable === false || pending || replBusy || wsState === "Disconnected" || !draft.trim();
|
|
1499
|
+
if (clearButton) clearButton.disabled = pending || !draft;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1433
1502
|
function setReplJournalCollapsed(collapsed) {
|
|
1434
1503
|
replJournalCollapsed = Boolean(collapsed);
|
|
1435
1504
|
try {
|
|
@@ -1460,6 +1529,8 @@
|
|
|
1460
1529
|
runtime: session.runtime,
|
|
1461
1530
|
source: session.source,
|
|
1462
1531
|
target: session.target,
|
|
1532
|
+
tmuxSessionId: session.tmuxSessionId,
|
|
1533
|
+
tmuxSessionCreatedAt: session.tmuxSessionCreatedAt,
|
|
1463
1534
|
})));
|
|
1464
1535
|
}
|
|
1465
1536
|
|
|
@@ -1579,7 +1650,53 @@
|
|
|
1579
1650
|
|
|
1580
1651
|
function isReplControlFocused() {
|
|
1581
1652
|
const activeEl = document.activeElement;
|
|
1582
|
-
return activeEl instanceof Element && Boolean(activeEl.closest(".repl-controls"));
|
|
1653
|
+
return activeEl instanceof Element && Boolean(activeEl.closest(".repl-controls, .repl-quick-composer"));
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
function focusReplQuickComposer() {
|
|
1657
|
+
replQuickFocusRequested = true;
|
|
1658
|
+
window.setTimeout(() => {
|
|
1659
|
+
if (rightView !== "repl" || !critiqueViewEl) {
|
|
1660
|
+
replQuickFocusRequested = false;
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
const composer = critiqueViewEl.querySelector("[data-repl-quick-draft]");
|
|
1664
|
+
if (composer instanceof HTMLTextAreaElement) {
|
|
1665
|
+
if (!composer.disabled) {
|
|
1666
|
+
try {
|
|
1667
|
+
composer.focus({ preventScroll: true });
|
|
1668
|
+
} catch {
|
|
1669
|
+
try { composer.focus(); } catch {}
|
|
1670
|
+
}
|
|
1671
|
+
if (document.activeElement === composer) replQuickFocusRequested = false;
|
|
1672
|
+
}
|
|
1673
|
+
if (typeof composer.scrollIntoView === "function") {
|
|
1674
|
+
try {
|
|
1675
|
+
composer.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
1676
|
+
} catch {
|
|
1677
|
+
try { composer.scrollIntoView(); } catch {}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}, 0);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
function focusReplSessionControls() {
|
|
1685
|
+
if (rightView !== "repl" || !critiqueViewEl) return;
|
|
1686
|
+
critiqueViewEl.scrollTop = 0;
|
|
1687
|
+
const sessionSelect = critiqueViewEl.querySelector("[data-repl-session]");
|
|
1688
|
+
const runtimeSelect = critiqueViewEl.querySelector("[data-repl-runtime]");
|
|
1689
|
+
const target = sessionSelect instanceof HTMLSelectElement && !sessionSelect.disabled
|
|
1690
|
+
? sessionSelect
|
|
1691
|
+
: runtimeSelect;
|
|
1692
|
+
if (target instanceof HTMLElement && typeof target.focus === "function") {
|
|
1693
|
+
try {
|
|
1694
|
+
target.focus({ preventScroll: true });
|
|
1695
|
+
} catch {
|
|
1696
|
+
try { target.focus(); } catch {}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
setStatus("REPL session controls focused.");
|
|
1583
1700
|
}
|
|
1584
1701
|
|
|
1585
1702
|
function renderReplViewIfActive(options) {
|
|
@@ -1589,6 +1706,7 @@
|
|
|
1589
1706
|
traceRenderRaf = window.requestAnimationFrame(() => {
|
|
1590
1707
|
traceRenderRaf = null;
|
|
1591
1708
|
refreshResponseUi();
|
|
1709
|
+
if (replQuickFocusRequested) focusReplQuickComposer();
|
|
1592
1710
|
});
|
|
1593
1711
|
}
|
|
1594
1712
|
|
|
@@ -2020,9 +2138,11 @@
|
|
|
2020
2138
|
let value = String(delta || "").replace(/^\s+/, "");
|
|
2021
2139
|
// The raw mirror below remains raw; Studio record cards hide only the
|
|
2022
2140
|
// temp-file wrapper used to submit multiline snippets safely. Match
|
|
2023
|
-
// both legacy long Studio roots and compact private control roots.
|
|
2141
|
+
// both legacy long Studio roots and compact private control roots. Use
|
|
2142
|
+
// the Python wrapper's final double close because IPython may wrap in
|
|
2143
|
+
// the middle of the word `globals` and add continuation prompts.
|
|
2024
2144
|
const submissionEchoPatterns = [
|
|
2025
|
-
/^.*exec\(open\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]
|
|
2145
|
+
/^.*exec\(open\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?\)\)\s*$/gm,
|
|
2026
2146
|
/^.*include\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?\.jl"\)\s*$/gm,
|
|
2027
2147
|
/^.*source\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?local\s*=\s*\.GlobalEnv\)\s*$/gm,
|
|
2028
2148
|
/^.*:script\s+[\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?\.ghci"?\s*$/gm,
|
|
@@ -2218,11 +2338,15 @@
|
|
|
2218
2338
|
const session = getActiveReplSessionForCurrentRuntime();
|
|
2219
2339
|
if (!session) {
|
|
2220
2340
|
setStatus("Start or select a " + getReplRuntimeLabel(replRuntime) + " REPL session first.", "warning");
|
|
2221
|
-
return;
|
|
2341
|
+
return null;
|
|
2342
|
+
}
|
|
2343
|
+
if (replBusy) {
|
|
2344
|
+
setStatus("A REPL request is already in progress.", "warning");
|
|
2345
|
+
return null;
|
|
2222
2346
|
}
|
|
2223
2347
|
if (!payload || payload.error) {
|
|
2224
2348
|
setStatus(payload && payload.error ? payload.error : "Nothing to send to REPL.", "warning");
|
|
2225
|
-
return;
|
|
2349
|
+
return null;
|
|
2226
2350
|
}
|
|
2227
2351
|
if (payload.noteOnly) {
|
|
2228
2352
|
if (String(payload.prose || "").trim()) {
|
|
@@ -2241,12 +2365,12 @@
|
|
|
2241
2365
|
} else {
|
|
2242
2366
|
setStatus("No code or prose found to send.", "warning");
|
|
2243
2367
|
}
|
|
2244
|
-
return;
|
|
2368
|
+
return null;
|
|
2245
2369
|
}
|
|
2246
2370
|
const text = String(payload.text || "");
|
|
2247
2371
|
if (!text.trim()) {
|
|
2248
|
-
setStatus("
|
|
2249
|
-
return;
|
|
2372
|
+
setStatus("REPL submission is empty.", "warning");
|
|
2373
|
+
return null;
|
|
2250
2374
|
}
|
|
2251
2375
|
const requestId = makeRequestId();
|
|
2252
2376
|
const journalEntry = addReplJournalEntry({
|
|
@@ -2262,6 +2386,7 @@
|
|
|
2262
2386
|
skippedChunks: payload.skippedChunks,
|
|
2263
2387
|
});
|
|
2264
2388
|
activeReplJournalEntryId = journalEntry.id;
|
|
2389
|
+
replPendingRequestId = requestId;
|
|
2265
2390
|
replBusy = true;
|
|
2266
2391
|
syncActionButtons();
|
|
2267
2392
|
renderReplViewIfActive({ force: true });
|
|
@@ -2280,11 +2405,15 @@
|
|
|
2280
2405
|
prose: journalEntry.prose,
|
|
2281
2406
|
skippedChunks: journalEntry.skippedChunks,
|
|
2282
2407
|
})) {
|
|
2283
|
-
|
|
2408
|
+
if (replPendingRequestId === requestId) replPendingRequestId = "";
|
|
2409
|
+
replBusy = Boolean(replPendingRequestId);
|
|
2284
2410
|
replJournalEntries = replJournalEntries.map((entry) => entry.id === journalEntry.id ? { ...entry, status: "error" } : entry);
|
|
2285
2411
|
persistReplJournalEntries();
|
|
2286
2412
|
syncActionButtons();
|
|
2413
|
+
renderReplViewIfActive({ force: true });
|
|
2414
|
+
return null;
|
|
2287
2415
|
}
|
|
2416
|
+
return requestId;
|
|
2288
2417
|
}
|
|
2289
2418
|
|
|
2290
2419
|
function sendEditorTextToRepl(options) {
|
|
@@ -2300,6 +2429,36 @@
|
|
|
2300
2429
|
sendReplPayload(replSendMode === "literate" ? buildLiterateReplSendPayload() : buildRawReplSendPayload());
|
|
2301
2430
|
}
|
|
2302
2431
|
|
|
2432
|
+
function submitReplQuickDraft() {
|
|
2433
|
+
const session = getActiveReplSessionForCurrentRuntime();
|
|
2434
|
+
if (!session) {
|
|
2435
|
+
setStatus("Start or select a " + getReplRuntimeLabel(replRuntime) + " REPL session first.", "warning");
|
|
2436
|
+
return false;
|
|
2437
|
+
}
|
|
2438
|
+
const key = getReplQuickDraftKey(session);
|
|
2439
|
+
const text = getReplQuickDraft(session);
|
|
2440
|
+
if (!text.trim()) {
|
|
2441
|
+
setStatus("Quick send is empty.", "warning");
|
|
2442
|
+
return false;
|
|
2443
|
+
}
|
|
2444
|
+
if (replQuickPending && replQuickPending.key === key) {
|
|
2445
|
+
setStatus("Quick send is already being submitted to this REPL.", "warning");
|
|
2446
|
+
return false;
|
|
2447
|
+
}
|
|
2448
|
+
const requestId = sendReplPayload({
|
|
2449
|
+
text,
|
|
2450
|
+
prose: "",
|
|
2451
|
+
label: "quick send",
|
|
2452
|
+
mode: "raw",
|
|
2453
|
+
noteOnly: false,
|
|
2454
|
+
skippedChunks: 0,
|
|
2455
|
+
});
|
|
2456
|
+
if (!requestId) return false;
|
|
2457
|
+
replQuickPending = { requestId, key, text };
|
|
2458
|
+
renderReplViewIfActive({ force: true });
|
|
2459
|
+
return true;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2303
2462
|
function renderTraceViewIfActive() {
|
|
2304
2463
|
if (rightView !== "trace") return;
|
|
2305
2464
|
if (traceRenderRaf !== null) return;
|
|
@@ -2341,9 +2500,15 @@
|
|
|
2341
2500
|
let paneFocusTarget = initialPaneFocusTarget;
|
|
2342
2501
|
let paneSplitPercent = 50;
|
|
2343
2502
|
const PANE_SPLIT_STORAGE_KEY = "piStudio.paneSplitPercent";
|
|
2503
|
+
const PANE_LAYOUT_STORAGE_KEY = "piStudio.paneLayout";
|
|
2504
|
+
const ACTIVITY_TRACKING_STORAGE_KEY = "piStudio.trackActivity";
|
|
2344
2505
|
const PANE_SPLIT_MIN_PERCENT = 20;
|
|
2345
2506
|
const PANE_SPLIT_MAX_PERCENT = 80;
|
|
2346
2507
|
const PANE_SPLIT_SNAP_TO_CENTER_PERCENT = 1;
|
|
2508
|
+
let studioPaneLayout = readStudioPaneLayout();
|
|
2509
|
+
let activityTrackingEnabled = readActivityTrackingEnabled();
|
|
2510
|
+
let activityTrackingOwnsWorkingView = false;
|
|
2511
|
+
let activityTrackingRequestId = "";
|
|
2347
2512
|
const STUDIO_WORKSPACE_MAX_TEXT_CHARS = 900_000;
|
|
2348
2513
|
const STUDIO_WORKSPACE_PERSIST_INTERVAL_MS = 300;
|
|
2349
2514
|
const STUDIO_WORKSPACE_RECOVERY_FETCH_TIMEOUT_MS = 1_500;
|
|
@@ -2879,7 +3044,11 @@
|
|
|
2879
3044
|
: "Off";
|
|
2880
3045
|
const lineLabel = lineNumbersEnabled ? "Lines on" : "Lines off";
|
|
2881
3046
|
const editorSizeLabel = formatStudioFontSizeLabel(editorFontSize);
|
|
2882
|
-
|
|
3047
|
+
const extras = [];
|
|
3048
|
+
if (studioPaneLayout === "editor-top") extras.push("Editor above");
|
|
3049
|
+
if (studioPaneLayout === "response-top") extras.push("Response above");
|
|
3050
|
+
if (activityTrackingEnabled) extras.push("Following activity");
|
|
3051
|
+
setStudioUiRefreshButtonText(studioUiRefreshUi.viewButton, "View: " + syntaxLabel + " · " + lineLabel + " · " + editorSizeLabel + (extras.length ? " · " + extras.join(" · ") : ""));
|
|
2883
3052
|
}
|
|
2884
3053
|
syncStudioUiRefreshReviewTrigger();
|
|
2885
3054
|
}
|
|
@@ -3150,6 +3319,8 @@
|
|
|
3150
3319
|
const viewButton = makeStudioUiRefreshElement("button", "", "View");
|
|
3151
3320
|
const viewMenu = makeStudioUiRefreshMenu(viewButton, "view", "studio-refresh-view-anchor");
|
|
3152
3321
|
appendStudioUiRefreshMenuSection(viewMenu.menu, "Display", [highlightSelect, lineNumbersSelect, editorFontSizeSelect]);
|
|
3322
|
+
if (activityTrackingSelect) activityTrackingSelect.hidden = isEditorOnlyMode || isWatchedFilePreview;
|
|
3323
|
+
appendStudioUiRefreshMenuSection(viewMenu.menu, "Workspace", [studioPaneLayoutSelect, (isEditorOnlyMode || isWatchedFilePreview) ? null : activityTrackingSelect]);
|
|
3153
3324
|
stateEl.appendChild(annotationsMenu.anchor);
|
|
3154
3325
|
stateEl.appendChild(viewMenu.anchor);
|
|
3155
3326
|
|
|
@@ -4390,6 +4561,127 @@
|
|
|
4390
4561
|
setStatus(descriptor.fileBacked ? "Detached editor from file origin into a new draft." : "Reset editor origin to a new draft.", "success");
|
|
4391
4562
|
}
|
|
4392
4563
|
|
|
4564
|
+
function normalizeStudioPaneLayout(value) {
|
|
4565
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
4566
|
+
return normalized === "editor-top" || normalized === "response-top" ? normalized : "side-by-side";
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4569
|
+
function readStudioPaneLayout() {
|
|
4570
|
+
try {
|
|
4571
|
+
return normalizeStudioPaneLayout(window.localStorage && window.localStorage.getItem(PANE_LAYOUT_STORAGE_KEY));
|
|
4572
|
+
} catch {
|
|
4573
|
+
return "side-by-side";
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4577
|
+
function readActivityTrackingEnabled() {
|
|
4578
|
+
try {
|
|
4579
|
+
return Boolean(window.localStorage && window.localStorage.getItem(ACTIVITY_TRACKING_STORAGE_KEY) === "on");
|
|
4580
|
+
} catch {
|
|
4581
|
+
return false;
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
|
|
4585
|
+
function isStackedStudioPaneLayout() {
|
|
4586
|
+
return studioPaneLayout === "editor-top" || studioPaneLayout === "response-top";
|
|
4587
|
+
}
|
|
4588
|
+
|
|
4589
|
+
function isResponseFirstStudioPaneLayout() {
|
|
4590
|
+
return studioPaneLayout === "response-top";
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
function getStudioPaneLayoutLabel(value) {
|
|
4594
|
+
if (value === "editor-top") return "Editor above response";
|
|
4595
|
+
if (value === "response-top") return "Response above editor";
|
|
4596
|
+
return "Side by side";
|
|
4597
|
+
}
|
|
4598
|
+
|
|
4599
|
+
function setStudioPaneLayout(value, options) {
|
|
4600
|
+
studioPaneLayout = normalizeStudioPaneLayout(value);
|
|
4601
|
+
document.body.dataset.studioLayout = studioPaneLayout;
|
|
4602
|
+
if (studioPaneLayoutSelect) studioPaneLayoutSelect.value = studioPaneLayout;
|
|
4603
|
+
const mainEl = paneResizeHandleEl && typeof paneResizeHandleEl.closest === "function"
|
|
4604
|
+
? paneResizeHandleEl.closest("main")
|
|
4605
|
+
: null;
|
|
4606
|
+
if (mainEl && leftPaneEl && rightPaneEl && paneResizeHandleEl) {
|
|
4607
|
+
if (isResponseFirstStudioPaneLayout()) {
|
|
4608
|
+
mainEl.append(rightPaneEl, paneResizeHandleEl, leftPaneEl);
|
|
4609
|
+
} else {
|
|
4610
|
+
mainEl.append(leftPaneEl, paneResizeHandleEl, rightPaneEl);
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
applyPaneSplitPercent(paneSplitPercent, { persist: false });
|
|
4614
|
+
if (!options || options.persist !== false) {
|
|
4615
|
+
try {
|
|
4616
|
+
if (window.localStorage) window.localStorage.setItem(PANE_LAYOUT_STORAGE_KEY, studioPaneLayout);
|
|
4617
|
+
} catch {
|
|
4618
|
+
// Ignore localStorage failures.
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
syncStudioUiRefreshSummaries();
|
|
4622
|
+
if (!options || !options.silent) {
|
|
4623
|
+
setStatus("Pane layout: " + getStudioPaneLayoutLabel(studioPaneLayout) + ".");
|
|
4624
|
+
}
|
|
4625
|
+
return studioPaneLayout;
|
|
4626
|
+
}
|
|
4627
|
+
|
|
4628
|
+
function setActivityTrackingEnabled(enabled, options) {
|
|
4629
|
+
activityTrackingEnabled = Boolean(enabled) && !isEditorOnlyMode && !isWatchedFilePreview;
|
|
4630
|
+
if (activityTrackingSelect) {
|
|
4631
|
+
activityTrackingSelect.value = activityTrackingEnabled ? "on" : "off";
|
|
4632
|
+
activityTrackingSelect.hidden = isEditorOnlyMode || isWatchedFilePreview;
|
|
4633
|
+
}
|
|
4634
|
+
if (!activityTrackingEnabled) {
|
|
4635
|
+
activityTrackingOwnsWorkingView = false;
|
|
4636
|
+
activityTrackingRequestId = "";
|
|
4637
|
+
}
|
|
4638
|
+
if (!options || options.persist !== false) {
|
|
4639
|
+
try {
|
|
4640
|
+
if (window.localStorage) window.localStorage.setItem(ACTIVITY_TRACKING_STORAGE_KEY, activityTrackingEnabled ? "on" : "off");
|
|
4641
|
+
} catch {
|
|
4642
|
+
// Ignore localStorage failures.
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
syncStudioUiRefreshSummaries();
|
|
4646
|
+
if (activityTrackingEnabled && agentBusyFromServer) {
|
|
4647
|
+
beginTrackedStudioActivity(pendingRequestId || "active");
|
|
4648
|
+
}
|
|
4649
|
+
if (!options || !options.silent) {
|
|
4650
|
+
setStatus(activityTrackingEnabled
|
|
4651
|
+
? "Studio will show Working during main Pi activity, then return to Response Preview."
|
|
4652
|
+
: "Activity following disabled.");
|
|
4653
|
+
}
|
|
4654
|
+
return activityTrackingEnabled;
|
|
4655
|
+
}
|
|
4656
|
+
|
|
4657
|
+
function beginTrackedStudioActivity(requestId) {
|
|
4658
|
+
if (!activityTrackingEnabled || isEditorOnlyMode || isWatchedFilePreview) return false;
|
|
4659
|
+
const normalizedRequestId = String(requestId || "active");
|
|
4660
|
+
if (!activityTrackingOwnsWorkingView && activityTrackingRequestId
|
|
4661
|
+
&& (activityTrackingRequestId === normalizedRequestId || activityTrackingRequestId === "active")) {
|
|
4662
|
+
if (activityTrackingRequestId === "active" && normalizedRequestId !== "active") {
|
|
4663
|
+
activityTrackingRequestId = normalizedRequestId;
|
|
4664
|
+
}
|
|
4665
|
+
return false;
|
|
4666
|
+
}
|
|
4667
|
+
if (activityTrackingOwnsWorkingView && activityTrackingRequestId === normalizedRequestId && rightView === "trace") return false;
|
|
4668
|
+
activityTrackingRequestId = normalizedRequestId;
|
|
4669
|
+
activityTrackingOwnsWorkingView = true;
|
|
4670
|
+
if (rightView !== "trace") setRightView("trace", { activityTracking: true });
|
|
4671
|
+
return true;
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4674
|
+
function finishTrackedStudioActivity(requestId) {
|
|
4675
|
+
if (!activityTrackingRequestId && !activityTrackingOwnsWorkingView) return false;
|
|
4676
|
+
const normalizedRequestId = String(requestId || "");
|
|
4677
|
+
if (normalizedRequestId && activityTrackingRequestId !== normalizedRequestId) return false;
|
|
4678
|
+
const shouldReturn = activityTrackingEnabled && activityTrackingOwnsWorkingView && rightView === "trace";
|
|
4679
|
+
activityTrackingOwnsWorkingView = false;
|
|
4680
|
+
activityTrackingRequestId = "";
|
|
4681
|
+
if (shouldReturn) setRightView("preview", { activityTracking: true });
|
|
4682
|
+
return shouldReturn;
|
|
4683
|
+
}
|
|
4684
|
+
|
|
4393
4685
|
function clampPaneSplitPercent(value) {
|
|
4394
4686
|
const numeric = Number(value);
|
|
4395
4687
|
if (!Number.isFinite(numeric)) return 50;
|
|
@@ -4400,13 +4692,19 @@
|
|
|
4400
4692
|
function applyPaneSplitPercent(percent, options) {
|
|
4401
4693
|
paneSplitPercent = clampPaneSplitPercent(percent);
|
|
4402
4694
|
const rightPercent = Math.round((100 - paneSplitPercent) * 10) / 10;
|
|
4695
|
+
const dividerPercent = isResponseFirstStudioPaneLayout() ? rightPercent : paneSplitPercent;
|
|
4403
4696
|
document.documentElement.style.setProperty("--studio-left-pane-fr", paneSplitPercent + "fr");
|
|
4404
4697
|
document.documentElement.style.setProperty("--studio-right-pane-fr", rightPercent + "fr");
|
|
4405
4698
|
if (paneResizeHandleEl) {
|
|
4699
|
+
const stacked = isStackedStudioPaneLayout();
|
|
4700
|
+
paneResizeHandleEl.setAttribute("aria-orientation", stacked ? "horizontal" : "vertical");
|
|
4406
4701
|
paneResizeHandleEl.setAttribute("aria-valuemin", String(PANE_SPLIT_MIN_PERCENT));
|
|
4407
4702
|
paneResizeHandleEl.setAttribute("aria-valuemax", String(PANE_SPLIT_MAX_PERCENT));
|
|
4408
|
-
paneResizeHandleEl.setAttribute("aria-valuenow", String(Math.round(
|
|
4703
|
+
paneResizeHandleEl.setAttribute("aria-valuenow", String(Math.round(dividerPercent)));
|
|
4409
4704
|
paneResizeHandleEl.setAttribute("aria-valuetext", "Editor " + Math.round(paneSplitPercent) + " percent, response " + Math.round(rightPercent) + " percent");
|
|
4705
|
+
paneResizeHandleEl.title = stacked
|
|
4706
|
+
? "Drag to resize the stacked panes. Double-click to reset."
|
|
4707
|
+
: "Drag to resize the panes. Double-click to reset.";
|
|
4410
4708
|
}
|
|
4411
4709
|
if (!options || options.persist !== false) {
|
|
4412
4710
|
try {
|
|
@@ -4438,17 +4736,23 @@
|
|
|
4438
4736
|
: null;
|
|
4439
4737
|
if (!mainEl || typeof mainEl.getBoundingClientRect !== "function") return paneSplitPercent;
|
|
4440
4738
|
const rect = mainEl.getBoundingClientRect();
|
|
4441
|
-
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4739
|
+
const stacked = isStackedStudioPaneLayout();
|
|
4740
|
+
const extent = stacked ? rect.height : rect.width;
|
|
4741
|
+
if (!extent) return paneSplitPercent;
|
|
4742
|
+
const coordinate = stacked
|
|
4743
|
+
? ((typeof event.clientY === "number" ? event.clientY : (rect.top + rect.height / 2)) - rect.top)
|
|
4744
|
+
: ((typeof event.clientX === "number" ? event.clientX : (rect.left + rect.width / 2)) - rect.left);
|
|
4745
|
+
const dividerPercent = (coordinate / extent) * 100;
|
|
4746
|
+
return isResponseFirstStudioPaneLayout() ? 100 - dividerPercent : dividerPercent;
|
|
4444
4747
|
}
|
|
4445
4748
|
|
|
4446
4749
|
function setupPaneResizeHandle() {
|
|
4447
4750
|
if (!paneResizeHandleEl) return;
|
|
4751
|
+
setStudioPaneLayout(studioPaneLayout, { persist: false, silent: true });
|
|
4448
4752
|
loadPaneSplitPercent();
|
|
4449
4753
|
let dragging = false;
|
|
4450
4754
|
let movedDuringDrag = false;
|
|
4451
|
-
let
|
|
4755
|
+
let pointerStartPosition = 0;
|
|
4452
4756
|
let activePaneResizePointerId = null;
|
|
4453
4757
|
const finishDrag = () => {
|
|
4454
4758
|
if (!dragging) return;
|
|
@@ -4473,7 +4777,9 @@
|
|
|
4473
4777
|
event.stopPropagation();
|
|
4474
4778
|
dragging = true;
|
|
4475
4779
|
movedDuringDrag = false;
|
|
4476
|
-
|
|
4780
|
+
pointerStartPosition = isStackedStudioPaneLayout()
|
|
4781
|
+
? (typeof event.clientY === "number" ? event.clientY : 0)
|
|
4782
|
+
: (typeof event.clientX === "number" ? event.clientX : 0);
|
|
4477
4783
|
activePaneResizePointerId = event.pointerId;
|
|
4478
4784
|
if (document.body && document.body.classList) document.body.classList.add("pane-resizing");
|
|
4479
4785
|
try {
|
|
@@ -4489,7 +4795,10 @@
|
|
|
4489
4795
|
});
|
|
4490
4796
|
paneResizeHandleEl.addEventListener("pointermove", (event) => {
|
|
4491
4797
|
if (!dragging) return;
|
|
4492
|
-
const
|
|
4798
|
+
const currentPosition = isStackedStudioPaneLayout()
|
|
4799
|
+
? (typeof event.clientY === "number" ? event.clientY : pointerStartPosition)
|
|
4800
|
+
: (typeof event.clientX === "number" ? event.clientX : pointerStartPosition);
|
|
4801
|
+
const movement = Math.abs(currentPosition - pointerStartPosition);
|
|
4493
4802
|
if (!movedDuringDrag && movement < 3) return;
|
|
4494
4803
|
movedDuringDrag = true;
|
|
4495
4804
|
event.preventDefault();
|
|
@@ -4503,25 +4812,26 @@
|
|
|
4503
4812
|
resetPaneSplitPercent();
|
|
4504
4813
|
});
|
|
4505
4814
|
paneResizeHandleEl.addEventListener("keydown", (event) => {
|
|
4815
|
+
const stacked = isStackedStudioPaneLayout();
|
|
4816
|
+
const decreaseKey = stacked ? "ArrowUp" : "ArrowLeft";
|
|
4817
|
+
const increaseKey = stacked ? "ArrowDown" : "ArrowRight";
|
|
4818
|
+
let dividerPercent = isResponseFirstStudioPaneLayout() ? 100 - paneSplitPercent : paneSplitPercent;
|
|
4506
4819
|
if (event.key === "Home") {
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
}
|
|
4511
|
-
if (event.key === "End") {
|
|
4512
|
-
event.preventDefault();
|
|
4513
|
-
applyPaneSplitPercent(PANE_SPLIT_MAX_PERCENT);
|
|
4514
|
-
return;
|
|
4515
|
-
}
|
|
4516
|
-
if (event.key === "Enter" || event.key === " " || event.key === "Spacebar") {
|
|
4820
|
+
dividerPercent = PANE_SPLIT_MIN_PERCENT;
|
|
4821
|
+
} else if (event.key === "End") {
|
|
4822
|
+
dividerPercent = PANE_SPLIT_MAX_PERCENT;
|
|
4823
|
+
} else if (event.key === "Enter" || event.key === " " || event.key === "Spacebar") {
|
|
4517
4824
|
event.preventDefault();
|
|
4518
4825
|
resetPaneSplitPercent();
|
|
4519
4826
|
return;
|
|
4827
|
+
} else if (event.key === decreaseKey || event.key === increaseKey) {
|
|
4828
|
+
const step = event.shiftKey ? 10 : 5;
|
|
4829
|
+
dividerPercent += event.key === decreaseKey ? -step : step;
|
|
4830
|
+
} else {
|
|
4831
|
+
return;
|
|
4520
4832
|
}
|
|
4521
|
-
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
4522
4833
|
event.preventDefault();
|
|
4523
|
-
|
|
4524
|
-
applyPaneSplitPercent(paneSplitPercent + (event.key === "ArrowLeft" ? -step : step));
|
|
4834
|
+
applyPaneSplitPercent(isResponseFirstStudioPaneLayout() ? 100 - dividerPercent : dividerPercent);
|
|
4525
4835
|
});
|
|
4526
4836
|
}
|
|
4527
4837
|
|
|
@@ -4598,7 +4908,7 @@
|
|
|
4598
4908
|
function shouldPreserveScrollForPaneActivationEvent(event) {
|
|
4599
4909
|
const target = event && event.target;
|
|
4600
4910
|
if (!(target instanceof Element)) return true;
|
|
4601
|
-
if (target.closest("button, select, input, a, [role='button'], .studio-copy-block-btn, .preview-comment-add, .preview-comment-jump")) {
|
|
4911
|
+
if (target.closest("button, select, input, textarea, a, [role='button'], .studio-copy-block-btn, .preview-comment-add, .preview-comment-jump")) {
|
|
4602
4912
|
return false;
|
|
4603
4913
|
}
|
|
4604
4914
|
return true;
|
|
@@ -4702,8 +5012,10 @@
|
|
|
4702
5012
|
setStatus(label + " is unavailable in editor-only Studio views.", "warning");
|
|
4703
5013
|
return false;
|
|
4704
5014
|
}
|
|
4705
|
-
const snapshot = snapshotStudioScrollablePositions()
|
|
4706
|
-
|
|
5015
|
+
const snapshot = snapshotStudioScrollablePositions().filter((entry) => (
|
|
5016
|
+
!(options && options.focusReplComposer && entry.el === critiqueViewEl)
|
|
5017
|
+
));
|
|
5018
|
+
setRightView(requestedView, options);
|
|
4707
5019
|
scheduleStudioScrollablePositionRestore(snapshot);
|
|
4708
5020
|
if (!options || options.announce !== false) {
|
|
4709
5021
|
setStatus("Right pane view: " + label + ".");
|
|
@@ -5089,6 +5401,16 @@
|
|
|
5089
5401
|
return;
|
|
5090
5402
|
}
|
|
5091
5403
|
|
|
5404
|
+
const isReplShortcut = (key.toLowerCase() === "r" || code === "KeyR")
|
|
5405
|
+
&& (event.metaKey || event.ctrlKey)
|
|
5406
|
+
&& event.altKey
|
|
5407
|
+
&& !event.shiftKey;
|
|
5408
|
+
if (isReplShortcut) {
|
|
5409
|
+
event.preventDefault();
|
|
5410
|
+
switchRightPaneToView("repl", { focusReplComposer: true });
|
|
5411
|
+
return;
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5092
5414
|
const isSideQuestionsShortcut = (key.toLowerCase() === "q" || code === "KeyQ")
|
|
5093
5415
|
&& (event.metaKey || event.ctrlKey)
|
|
5094
5416
|
&& event.altKey
|
|
@@ -7585,7 +7907,7 @@
|
|
|
7585
7907
|
refreshBtn.type = "button";
|
|
7586
7908
|
refreshBtn.className = "studio-pdf-focus-btn studio-pdf-focus-refresh";
|
|
7587
7909
|
refreshBtn.textContent = "Refresh";
|
|
7588
|
-
refreshBtn.title = "Reload this PDF preview from disk. Shortcut: Cmd/Ctrl+Alt+R.";
|
|
7910
|
+
refreshBtn.title = "Reload this PDF preview from disk. Shortcut: Cmd/Ctrl+Alt+Shift+R.";
|
|
7589
7911
|
refreshBtn.setAttribute("aria-label", "Refresh PDF preview from disk");
|
|
7590
7912
|
refreshBtn.addEventListener("click", () => refreshStudioPdfFocusViewer());
|
|
7591
7913
|
actions.appendChild(refreshBtn);
|
|
@@ -8148,7 +8470,7 @@
|
|
|
8148
8470
|
const matches = (key === "r" || code === "KeyR")
|
|
8149
8471
|
&& (event.metaKey || event.ctrlKey)
|
|
8150
8472
|
&& event.altKey
|
|
8151
|
-
&&
|
|
8473
|
+
&& event.shiftKey;
|
|
8152
8474
|
if (!matches) return false;
|
|
8153
8475
|
if (!refreshVisibleStudioPdfPreviews()) return false;
|
|
8154
8476
|
event.preventDefault();
|
|
@@ -8904,7 +9226,7 @@
|
|
|
8904
9226
|
refreshBtn.type = "button";
|
|
8905
9227
|
refreshBtn.className = "studio-pdf-card-action studio-pdf-card-refresh";
|
|
8906
9228
|
refreshBtn.textContent = "Refresh";
|
|
8907
|
-
refreshBtn.title = "Reload this PDF preview from disk. Shortcut: Cmd/Ctrl+Alt+R.";
|
|
9229
|
+
refreshBtn.title = "Reload this PDF preview from disk. Shortcut: Cmd/Ctrl+Alt+Shift+R.";
|
|
8908
9230
|
refreshBtn.addEventListener("click", (event) => {
|
|
8909
9231
|
event.preventDefault();
|
|
8910
9232
|
event.stopPropagation();
|
|
@@ -9870,10 +10192,26 @@
|
|
|
9870
10192
|
if (!actionBtn) return;
|
|
9871
10193
|
event.preventDefault();
|
|
9872
10194
|
const action = actionBtn.getAttribute("data-repl-action") || "";
|
|
10195
|
+
if (action === "quick-send") {
|
|
10196
|
+
submitReplQuickDraft();
|
|
10197
|
+
return;
|
|
10198
|
+
}
|
|
10199
|
+
if (action === "jump-controls") {
|
|
10200
|
+
focusReplSessionControls();
|
|
10201
|
+
return;
|
|
10202
|
+
}
|
|
10203
|
+
if (action === "quick-clear") {
|
|
10204
|
+
const session = getActiveReplSessionForCurrentRuntime();
|
|
10205
|
+
if (session) setReplQuickDraft(session, "");
|
|
10206
|
+
renderReplViewIfActive({ force: true });
|
|
10207
|
+
focusReplQuickComposer();
|
|
10208
|
+
return;
|
|
10209
|
+
}
|
|
9873
10210
|
if (action === "start" || action === "new-session") {
|
|
9874
10211
|
const requestId = makeRequestId();
|
|
9875
10212
|
const command = getCurrentReplStartCommandFromDom();
|
|
9876
10213
|
setReplCommandOverride(replRuntime, command);
|
|
10214
|
+
replPendingRequestId = requestId;
|
|
9877
10215
|
replBusy = true;
|
|
9878
10216
|
replError = "";
|
|
9879
10217
|
replMessage = (action === "new-session" ? "Starting new " : "Starting ") + getReplRuntimeLabel(replRuntime) + " session" + (command ? " with custom command" : "") + "…";
|
|
@@ -9882,8 +10220,10 @@
|
|
|
9882
10220
|
const message = { type: "repl_start_request", requestId, runtime: replRuntime, newSession: action === "new-session" };
|
|
9883
10221
|
if (command) message.command = command;
|
|
9884
10222
|
if (!sendMessage(message)) {
|
|
9885
|
-
|
|
10223
|
+
if (replPendingRequestId === requestId) replPendingRequestId = "";
|
|
10224
|
+
replBusy = Boolean(replPendingRequestId);
|
|
9886
10225
|
syncActionButtons();
|
|
10226
|
+
renderReplViewIfActive({ force: true });
|
|
9887
10227
|
}
|
|
9888
10228
|
return;
|
|
9889
10229
|
}
|
|
@@ -9894,14 +10234,17 @@
|
|
|
9894
10234
|
return;
|
|
9895
10235
|
}
|
|
9896
10236
|
const requestId = makeRequestId();
|
|
10237
|
+
replPendingRequestId = requestId;
|
|
9897
10238
|
replBusy = true;
|
|
9898
10239
|
replError = "";
|
|
9899
10240
|
replMessage = "Stopping " + session.sessionName + "…";
|
|
9900
10241
|
syncActionButtons();
|
|
9901
10242
|
renderReplViewIfActive({ force: true });
|
|
9902
10243
|
if (!sendMessage({ type: "repl_stop_request", requestId, sessionName: session.sessionName })) {
|
|
9903
|
-
|
|
10244
|
+
if (replPendingRequestId === requestId) replPendingRequestId = "";
|
|
10245
|
+
replBusy = Boolean(replPendingRequestId);
|
|
9904
10246
|
syncActionButtons();
|
|
10247
|
+
renderReplViewIfActive({ force: true });
|
|
9905
10248
|
}
|
|
9906
10249
|
return;
|
|
9907
10250
|
}
|
|
@@ -9912,14 +10255,17 @@
|
|
|
9912
10255
|
return;
|
|
9913
10256
|
}
|
|
9914
10257
|
const requestId = makeRequestId();
|
|
10258
|
+
replPendingRequestId = requestId;
|
|
9915
10259
|
replBusy = true;
|
|
9916
10260
|
replError = "";
|
|
9917
10261
|
replMessage = "Interrupting REPL…";
|
|
9918
10262
|
syncActionButtons();
|
|
9919
10263
|
renderReplViewIfActive({ force: true });
|
|
9920
10264
|
if (!sendMessage({ type: "repl_interrupt_request", requestId, sessionName: session.sessionName })) {
|
|
9921
|
-
|
|
10265
|
+
if (replPendingRequestId === requestId) replPendingRequestId = "";
|
|
10266
|
+
replBusy = Boolean(replPendingRequestId);
|
|
9922
10267
|
syncActionButtons();
|
|
10268
|
+
renderReplViewIfActive({ force: true });
|
|
9923
10269
|
}
|
|
9924
10270
|
return;
|
|
9925
10271
|
}
|
|
@@ -9979,6 +10325,39 @@
|
|
|
9979
10325
|
}
|
|
9980
10326
|
}
|
|
9981
10327
|
|
|
10328
|
+
function handleReplPaneFocusIn(event) {
|
|
10329
|
+
if (!replQuickFocusRequested || rightView !== "repl") return;
|
|
10330
|
+
const target = event && event.target;
|
|
10331
|
+
if (target instanceof Element && !target.hasAttribute("data-repl-quick-draft")) {
|
|
10332
|
+
replQuickFocusRequested = false;
|
|
10333
|
+
}
|
|
10334
|
+
}
|
|
10335
|
+
|
|
10336
|
+
function handleReplPaneInput(event) {
|
|
10337
|
+
if (rightView !== "repl") return;
|
|
10338
|
+
const target = event && event.target;
|
|
10339
|
+
if (!(target instanceof HTMLTextAreaElement) || !target.hasAttribute("data-repl-quick-draft")) return;
|
|
10340
|
+
const session = getActiveReplSessionForCurrentRuntime();
|
|
10341
|
+
if (!session) return;
|
|
10342
|
+
const draft = setReplQuickDraft(session, target.value);
|
|
10343
|
+
if (target.value !== draft) target.value = draft;
|
|
10344
|
+
updateReplQuickComposerActions();
|
|
10345
|
+
}
|
|
10346
|
+
|
|
10347
|
+
function handleReplPaneKeydown(event) {
|
|
10348
|
+
if (rightView !== "repl" || !event || event.isComposing) return;
|
|
10349
|
+
const target = event.target;
|
|
10350
|
+
if (!(target instanceof HTMLTextAreaElement) || !target.hasAttribute("data-repl-quick-draft")) return;
|
|
10351
|
+
const submitShortcut = event.key === "Enter"
|
|
10352
|
+
&& (event.metaKey || event.ctrlKey)
|
|
10353
|
+
&& !event.altKey
|
|
10354
|
+
&& !event.shiftKey;
|
|
10355
|
+
if (!submitShortcut) return;
|
|
10356
|
+
event.preventDefault();
|
|
10357
|
+
event.stopPropagation();
|
|
10358
|
+
submitReplQuickDraft();
|
|
10359
|
+
}
|
|
10360
|
+
|
|
9982
10361
|
function handleReplPaneChange(event) {
|
|
9983
10362
|
if (rightView !== "repl") return;
|
|
9984
10363
|
const target = event.target;
|
|
@@ -10026,8 +10405,11 @@
|
|
|
10026
10405
|
critiqueViewEl.addEventListener("click", handleGitChangesPaneClick);
|
|
10027
10406
|
critiqueViewEl.addEventListener("click", handleStudioQuartoPreviewClick);
|
|
10028
10407
|
critiqueViewEl.addEventListener("click", (event) => { void handleSideQuestionClick(event); });
|
|
10408
|
+
critiqueViewEl.addEventListener("focusin", handleReplPaneFocusIn);
|
|
10029
10409
|
critiqueViewEl.addEventListener("input", handleSideQuestionInput);
|
|
10410
|
+
critiqueViewEl.addEventListener("input", handleReplPaneInput);
|
|
10030
10411
|
critiqueViewEl.addEventListener("keydown", handleSideQuestionKeydown);
|
|
10412
|
+
critiqueViewEl.addEventListener("keydown", handleReplPaneKeydown);
|
|
10031
10413
|
critiqueViewEl.addEventListener("change", handleSideQuestionChange);
|
|
10032
10414
|
critiqueViewEl.addEventListener("change", handleReplPaneChange);
|
|
10033
10415
|
critiqueViewEl.addEventListener("change", (event) => {
|
|
@@ -11763,6 +12145,28 @@
|
|
|
11763
12145
|
return "tmux attach -t " + String(session.sessionName || "");
|
|
11764
12146
|
}
|
|
11765
12147
|
|
|
12148
|
+
function buildReplQuickComposerHtml(activeSession, canSendToActiveSession) {
|
|
12149
|
+
const key = getReplQuickDraftKey(activeSession);
|
|
12150
|
+
const draft = getReplQuickDraft(activeSession);
|
|
12151
|
+
const pending = Boolean(replQuickPending && replQuickPending.key === key);
|
|
12152
|
+
const runtimeLabel = activeSession ? getReplRuntimeLabel(activeSession.runtime) : getReplRuntimeLabel(replRuntime);
|
|
12153
|
+
const tmuxTargetLabel = activeSession && activeSession.target ? ("tmux " + activeSession.target) : "";
|
|
12154
|
+
const targetDetails = activeSession
|
|
12155
|
+
? ([runtimeLabel, activeSession.sessionName, tmuxTargetLabel].filter(Boolean).join(" · "))
|
|
12156
|
+
: (runtimeLabel + " · no session selected");
|
|
12157
|
+
const visibleTargetLabel = activeSession ? activeSession.sessionName : "No session selected";
|
|
12158
|
+
const inputDisabled = !activeSession || !key || pending || replBusy;
|
|
12159
|
+
const sendDisabled = !key || !canSendToActiveSession || wsState === "Disconnected" || pending || !draft.trim();
|
|
12160
|
+
return "<section class='repl-quick-composer' aria-labelledby='replQuickComposerTitle'>"
|
|
12161
|
+
+ "<div class='repl-quick-header'><div><strong id='replQuickComposerTitle'>Quick send</strong><button type='button' class='repl-quick-target' data-repl-action='jump-controls' title='Target: " + escapeHtml(targetDetails) + ". Jump to REPL session controls.' aria-label='Target: " + escapeHtml(targetDetails) + ". Jump to REPL session controls.'><span class='repl-quick-target-label'>" + escapeHtml(visibleTargetLabel) + "</span><span class='repl-quick-target-arrow' aria-hidden='true'>↑</span></button></div>"
|
|
12162
|
+
+ "<span class='repl-quick-hint'>Enter adds a line · Cmd/Ctrl+Enter sends</span></div>"
|
|
12163
|
+
+ "<textarea data-repl-quick-draft rows='3' maxlength='" + REPL_QUICK_DRAFT_MAX_CHARS + "' aria-label='Quick code for " + escapeHtml(targetDetails) + "' aria-keyshortcuts='Meta+Enter Control+Enter' placeholder='" + (activeSession ? (key ? "Type a short command or snippet…" : "Exact tmux identity unavailable; refresh the session list") : "Start or select a REPL session first") + "'" + (inputDisabled ? " disabled" : "") + ">" + escapeHtml(draft) + "</textarea>"
|
|
12164
|
+
+ "<div class='repl-quick-actions'><span>Raw send through Studio’s safe submission and Shared REPL Record path.</span><div>"
|
|
12165
|
+
+ "<button type='button' data-repl-action='quick-clear'" + (pending || !draft ? " disabled" : "") + ">Clear</button>"
|
|
12166
|
+
+ "<button class='repl-quick-send-btn' type='button' data-repl-action='quick-send'" + (sendDisabled ? " disabled" : "") + ">" + (pending ? "Sending…" : "Send") + "</button>"
|
|
12167
|
+
+ "</div></div></section>";
|
|
12168
|
+
}
|
|
12169
|
+
|
|
11766
12170
|
function buildReplPanelHtml() {
|
|
11767
12171
|
const runtimeOptions = [
|
|
11768
12172
|
["shell", "Shell"],
|
|
@@ -11814,6 +12218,7 @@
|
|
|
11814
12218
|
+ (replError ? "<div class='repl-notice repl-notice-error'>" + escapeHtml(replError) + "</div>" : "")
|
|
11815
12219
|
+ buildReplJournalHtml(transcript)
|
|
11816
12220
|
+ buildReplStudioActionsHtml()
|
|
12221
|
+
+ buildReplQuickComposerHtml(activeSession, canSendToActiveSession)
|
|
11817
12222
|
+ buildReplMirrorHtml(body, transcript)
|
|
11818
12223
|
+ "</div>";
|
|
11819
12224
|
}
|
|
@@ -15062,9 +15467,14 @@
|
|
|
15062
15467
|
scheduleWorkspacePersistence();
|
|
15063
15468
|
}
|
|
15064
15469
|
|
|
15065
|
-
function setRightView(nextView) {
|
|
15470
|
+
function setRightView(nextView, options) {
|
|
15471
|
+
const automatedActivityChange = Boolean(options && options.activityTracking);
|
|
15472
|
+
if (activityTrackingOwnsWorkingView && !automatedActivityChange) {
|
|
15473
|
+
activityTrackingOwnsWorkingView = false;
|
|
15474
|
+
}
|
|
15066
15475
|
const previousView = rightView;
|
|
15067
15476
|
rightView = normalizeRightViewValue(nextView);
|
|
15477
|
+
if (rightView !== "repl") replQuickFocusRequested = false;
|
|
15068
15478
|
syncRightViewModeOptions();
|
|
15069
15479
|
rightViewSelect.value = rightView;
|
|
15070
15480
|
if (rightView === "trace" && previousView !== "trace") {
|
|
@@ -15103,6 +15513,9 @@
|
|
|
15103
15513
|
if (composer instanceof HTMLTextAreaElement) composer.focus({ preventScroll: true });
|
|
15104
15514
|
}, 0);
|
|
15105
15515
|
}
|
|
15516
|
+
if (rightView === "repl" && (previousView !== "repl" || (options && options.focusReplComposer))) {
|
|
15517
|
+
focusReplQuickComposer();
|
|
15518
|
+
}
|
|
15106
15519
|
scheduleWorkspacePersistence();
|
|
15107
15520
|
}
|
|
15108
15521
|
|
|
@@ -22978,6 +23391,11 @@
|
|
|
22978
23391
|
}
|
|
22979
23392
|
if (rightView === "side-questions") renderSideQuestionView();
|
|
22980
23393
|
syncAskAsideButton();
|
|
23394
|
+
if (agentBusyFromServer) {
|
|
23395
|
+
beginTrackedStudioActivity(pendingRequestId || "active");
|
|
23396
|
+
} else {
|
|
23397
|
+
finishTrackedStudioActivity();
|
|
23398
|
+
}
|
|
22981
23399
|
|
|
22982
23400
|
if (pendingRequestId) {
|
|
22983
23401
|
if (busy) {
|
|
@@ -23054,18 +23472,25 @@
|
|
|
23054
23472
|
const previousError = replError;
|
|
23055
23473
|
const previousMessage = replMessage;
|
|
23056
23474
|
const wasBusy = replBusy;
|
|
23475
|
+
const responseRequestId = typeof message.requestId === "string" ? message.requestId : "";
|
|
23476
|
+
const settlesPendingRequest = Boolean(responseRequestId && responseRequestId === replPendingRequestId);
|
|
23477
|
+
const previousActiveLifetimeKey = getReplQuickDraftKey(getActiveReplSession());
|
|
23057
23478
|
replTmuxAvailable = typeof message.tmuxAvailable === "boolean" ? message.tmuxAvailable : replTmuxAvailable;
|
|
23058
23479
|
const sessionsChanged = setReplSessions(message.sessions);
|
|
23059
23480
|
if (typeof message.activeSessionName === "string" && message.activeSessionName.trim()) {
|
|
23060
23481
|
setActiveReplSessionForCurrentRuntime(message.activeSessionName);
|
|
23061
23482
|
}
|
|
23062
23483
|
const journalChanged = mergeReplJournalEntries(message.journalEntries);
|
|
23484
|
+
reconcileReplQuickPending();
|
|
23063
23485
|
maybeImportLegacyReplJournalEntries(replActiveSessionName);
|
|
23064
23486
|
if (typeof message.transcript === "string") replTranscript = trimReplTranscript(message.transcript);
|
|
23065
23487
|
if (typeof message.capturedAt === "number") replCapturedAt = message.capturedAt;
|
|
23066
23488
|
replError = typeof message.replError === "string" ? message.replError : (typeof message.captureError === "string" ? message.captureError : "");
|
|
23067
23489
|
replMessage = typeof message.replMessage === "string" ? message.replMessage : "";
|
|
23068
|
-
|
|
23490
|
+
if (settlesPendingRequest && replPendingRequestId === responseRequestId) replPendingRequestId = "";
|
|
23491
|
+
replBusy = Boolean(replPendingRequestId);
|
|
23492
|
+
const activeSessionIdentityChanged = Boolean(previousActiveLifetimeKey)
|
|
23493
|
+
&& previousActiveLifetimeKey !== getReplQuickDraftKey(getActiveReplSession());
|
|
23069
23494
|
const controlsChanged = wasBusy
|
|
23070
23495
|
|| sessionsChanged
|
|
23071
23496
|
|| previousTmuxAvailable !== replTmuxAvailable
|
|
@@ -23077,7 +23502,7 @@
|
|
|
23077
23502
|
|| previousMessage !== replMessage
|
|
23078
23503
|
|| journalChanged
|
|
23079
23504
|
|| (!previousCapturedAt && replCapturedAt);
|
|
23080
|
-
if (viewChanged) renderReplViewIfActive();
|
|
23505
|
+
if (viewChanged) renderReplViewIfActive({ force: activeSessionIdentityChanged });
|
|
23081
23506
|
updateReferenceBadge();
|
|
23082
23507
|
return;
|
|
23083
23508
|
}
|
|
@@ -23102,18 +23527,28 @@
|
|
|
23102
23527
|
const previousError = replError;
|
|
23103
23528
|
const previousMessage = replMessage;
|
|
23104
23529
|
const wasBusy = replBusy;
|
|
23530
|
+
const responseRequestId = typeof message.requestId === "string" ? message.requestId : "";
|
|
23531
|
+
const settlesPendingRequest = Boolean(responseRequestId && responseRequestId === replPendingRequestId);
|
|
23532
|
+
const previousActiveLifetimeKey = getReplQuickDraftKey(getActiveReplSession());
|
|
23105
23533
|
let sessionsChanged = false;
|
|
23106
23534
|
if (message.session) {
|
|
23107
23535
|
const session = normalizeReplSession(message.session);
|
|
23108
|
-
if (session
|
|
23109
|
-
|
|
23110
|
-
|
|
23536
|
+
if (session) {
|
|
23537
|
+
const existingIndex = replSessions.findIndex((candidate) => candidate.sessionName === session.sessionName);
|
|
23538
|
+
if (existingIndex < 0) {
|
|
23539
|
+
replSessions = [...replSessions, session];
|
|
23540
|
+
sessionsChanged = true;
|
|
23541
|
+
} else if (serializeReplSessionsForCompare([replSessions[existingIndex]]) !== serializeReplSessionsForCompare([session])) {
|
|
23542
|
+
replSessions = replSessions.map((candidate, index) => index === existingIndex ? session : candidate);
|
|
23543
|
+
sessionsChanged = true;
|
|
23544
|
+
}
|
|
23111
23545
|
}
|
|
23112
23546
|
}
|
|
23113
23547
|
if (typeof message.activeSessionName === "string" && message.activeSessionName.trim()) {
|
|
23114
23548
|
setActiveReplSessionForCurrentRuntime(message.activeSessionName);
|
|
23115
23549
|
}
|
|
23116
23550
|
let journalChanged = mergeReplJournalEntries(message.journalEntries);
|
|
23551
|
+
reconcileReplQuickPending();
|
|
23117
23552
|
maybeImportLegacyReplJournalEntries(replActiveSessionName);
|
|
23118
23553
|
if (typeof message.transcript === "string") {
|
|
23119
23554
|
replTranscript = trimReplTranscript(message.transcript);
|
|
@@ -23125,7 +23560,10 @@
|
|
|
23125
23560
|
if (typeof message.capturedAt === "number") replCapturedAt = message.capturedAt;
|
|
23126
23561
|
replError = typeof message.replError === "string" ? message.replError : "";
|
|
23127
23562
|
if (typeof message.replMessage === "string") replMessage = message.replMessage;
|
|
23128
|
-
|
|
23563
|
+
if (settlesPendingRequest && replPendingRequestId === responseRequestId) replPendingRequestId = "";
|
|
23564
|
+
replBusy = Boolean(replPendingRequestId);
|
|
23565
|
+
const activeSessionIdentityChanged = Boolean(previousActiveLifetimeKey)
|
|
23566
|
+
&& previousActiveLifetimeKey !== getReplQuickDraftKey(getActiveReplSession());
|
|
23129
23567
|
const controlsChanged = wasBusy || sessionsChanged || previousActiveSessionName !== replActiveSessionName;
|
|
23130
23568
|
if (controlsChanged) syncActionButtons();
|
|
23131
23569
|
const viewChanged = controlsChanged
|
|
@@ -23134,7 +23572,7 @@
|
|
|
23134
23572
|
|| previousMessage !== replMessage
|
|
23135
23573
|
|| journalChanged
|
|
23136
23574
|
|| (!previousCapturedAt && replCapturedAt);
|
|
23137
|
-
if (viewChanged) renderReplViewIfActive();
|
|
23575
|
+
if (viewChanged) renderReplViewIfActive({ force: activeSessionIdentityChanged });
|
|
23138
23576
|
updateReferenceBadge();
|
|
23139
23577
|
return;
|
|
23140
23578
|
}
|
|
@@ -23153,9 +23591,12 @@
|
|
|
23153
23591
|
}
|
|
23154
23592
|
|
|
23155
23593
|
if (message.type === "repl_send_ack") {
|
|
23156
|
-
|
|
23594
|
+
const responseRequestId = typeof message.requestId === "string" ? message.requestId : "";
|
|
23595
|
+
if (responseRequestId && replPendingRequestId === responseRequestId) replPendingRequestId = "";
|
|
23596
|
+
replBusy = Boolean(replPendingRequestId);
|
|
23157
23597
|
replMessage = "";
|
|
23158
23598
|
replError = "";
|
|
23599
|
+
if (responseRequestId) settleReplQuickPending(responseRequestId, true);
|
|
23159
23600
|
mergeReplJournalEntries(message.journalEntries);
|
|
23160
23601
|
if (typeof message.requestId === "string") {
|
|
23161
23602
|
replJournalEntries = replJournalEntries.map((entry) => entry.requestId === message.requestId ? { ...entry, status: "sent", updatedAt: Date.now() } : entry);
|
|
@@ -23171,6 +23612,8 @@
|
|
|
23171
23612
|
pendingRequestId = typeof message.requestId === "string" ? message.requestId : pendingRequestId;
|
|
23172
23613
|
pendingKind = typeof message.kind === "string" ? message.kind : "unknown";
|
|
23173
23614
|
stickyStudioKind = pendingKind;
|
|
23615
|
+
agentBusyFromServer = true;
|
|
23616
|
+
beginTrackedStudioActivity(pendingRequestId || "active");
|
|
23174
23617
|
if (pendingKind === "direct") {
|
|
23175
23618
|
studioRunChainActive = true;
|
|
23176
23619
|
}
|
|
@@ -23201,6 +23644,7 @@
|
|
|
23201
23644
|
const busy = Boolean(message.busy);
|
|
23202
23645
|
setBusy(busy);
|
|
23203
23646
|
setWsState(busy ? "Submitting" : "Ready");
|
|
23647
|
+
finishTrackedStudioActivity(typeof message.requestId === "string" ? message.requestId : "");
|
|
23204
23648
|
setStatus(typeof message.message === "string" ? message.message : "Compaction completed.", "success");
|
|
23205
23649
|
return;
|
|
23206
23650
|
}
|
|
@@ -23215,6 +23659,7 @@
|
|
|
23215
23659
|
const busy = Boolean(message.busy);
|
|
23216
23660
|
setBusy(busy);
|
|
23217
23661
|
setWsState(busy ? "Submitting" : "Ready");
|
|
23662
|
+
finishTrackedStudioActivity(typeof message.requestId === "string" ? message.requestId : "");
|
|
23218
23663
|
setStatus(typeof message.message === "string" ? message.message : "Compaction failed.", "error");
|
|
23219
23664
|
return;
|
|
23220
23665
|
}
|
|
@@ -23234,6 +23679,7 @@
|
|
|
23234
23679
|
pendingRequestId = null;
|
|
23235
23680
|
pendingKind = null;
|
|
23236
23681
|
queuedLatestResponse = null;
|
|
23682
|
+
agentBusyFromServer = false;
|
|
23237
23683
|
setBusy(false);
|
|
23238
23684
|
setWsState("Ready");
|
|
23239
23685
|
|
|
@@ -23250,6 +23696,7 @@
|
|
|
23250
23696
|
if (!appliedFromHistory && typeof message.markdown === "string") {
|
|
23251
23697
|
handleIncomingResponse(message.markdown, responseKind, message.timestamp, message.thinking);
|
|
23252
23698
|
}
|
|
23699
|
+
finishTrackedStudioActivity(completedRequestId || "");
|
|
23253
23700
|
|
|
23254
23701
|
if (responseKind === "critique") {
|
|
23255
23702
|
setStatus("Critique ready.", "success");
|
|
@@ -23552,6 +23999,11 @@
|
|
|
23552
23999
|
|
|
23553
24000
|
setBusy(busy);
|
|
23554
24001
|
setWsState(busy ? "Submitting" : "Ready");
|
|
24002
|
+
if (agentBusyFromServer) {
|
|
24003
|
+
beginTrackedStudioActivity(pendingRequestId || "active");
|
|
24004
|
+
} else {
|
|
24005
|
+
finishTrackedStudioActivity();
|
|
24006
|
+
}
|
|
23555
24007
|
|
|
23556
24008
|
if (pendingRequestId) {
|
|
23557
24009
|
if (busy) {
|
|
@@ -23594,6 +24046,7 @@
|
|
|
23594
24046
|
clearArmedTitleAttention(message.requestId);
|
|
23595
24047
|
}
|
|
23596
24048
|
stickyStudioKind = null;
|
|
24049
|
+
finishTrackedStudioActivity(typeof message.requestId === "string" ? message.requestId : "");
|
|
23597
24050
|
setBusy(false);
|
|
23598
24051
|
setWsState("Ready");
|
|
23599
24052
|
setStatus(typeof message.message === "string" ? message.message : "Studio is busy.", "warning");
|
|
@@ -23615,9 +24068,14 @@
|
|
|
23615
24068
|
if (typeof message.requestId === "string") {
|
|
23616
24069
|
clearArmedTitleAttention(message.requestId);
|
|
23617
24070
|
}
|
|
23618
|
-
|
|
24071
|
+
const failedReplRequestId = typeof message.requestId === "string" && message.requestId === replPendingRequestId
|
|
24072
|
+
? message.requestId
|
|
24073
|
+
: "";
|
|
24074
|
+
if (failedReplRequestId) {
|
|
24075
|
+
replPendingRequestId = "";
|
|
23619
24076
|
replBusy = false;
|
|
23620
24077
|
replError = typeof message.message === "string" ? message.message : "REPL request failed.";
|
|
24078
|
+
settleReplQuickPending(failedReplRequestId, false);
|
|
23621
24079
|
if (typeof message.requestId === "string") {
|
|
23622
24080
|
replJournalEntries = replJournalEntries.map((entry) => entry.requestId === message.requestId ? { ...entry, status: "error", output: replError, updatedAt: Date.now() } : entry);
|
|
23623
24081
|
persistReplJournalEntries();
|
|
@@ -23625,6 +24083,7 @@
|
|
|
23625
24083
|
renderReplViewIfActive({ force: true });
|
|
23626
24084
|
}
|
|
23627
24085
|
stickyStudioKind = null;
|
|
24086
|
+
finishTrackedStudioActivity(typeof message.requestId === "string" ? message.requestId : "");
|
|
23628
24087
|
setBusy(false);
|
|
23629
24088
|
setWsState("Ready");
|
|
23630
24089
|
setStatus(typeof message.message === "string" ? message.message : "Request failed.", "error");
|
|
@@ -24160,6 +24619,18 @@
|
|
|
24160
24619
|
});
|
|
24161
24620
|
}
|
|
24162
24621
|
|
|
24622
|
+
if (studioPaneLayoutSelect) {
|
|
24623
|
+
studioPaneLayoutSelect.addEventListener("change", () => {
|
|
24624
|
+
setStudioPaneLayout(studioPaneLayoutSelect.value);
|
|
24625
|
+
});
|
|
24626
|
+
}
|
|
24627
|
+
|
|
24628
|
+
if (activityTrackingSelect) {
|
|
24629
|
+
activityTrackingSelect.addEventListener("change", () => {
|
|
24630
|
+
setActivityTrackingEnabled(activityTrackingSelect.value === "on");
|
|
24631
|
+
});
|
|
24632
|
+
}
|
|
24633
|
+
|
|
24163
24634
|
if (annotationModeSelect) {
|
|
24164
24635
|
annotationModeSelect.addEventListener("change", () => {
|
|
24165
24636
|
setAnnotationsEnabled(annotationModeSelect.value !== "off");
|
|
@@ -25628,6 +26099,7 @@
|
|
|
25628
26099
|
const storedLineNumbersEnabled = readStoredEditorLineNumbersEnabled();
|
|
25629
26100
|
const initialLineNumbersEnabled = storedLineNumbersEnabled ?? Boolean(lineNumbersSelect && lineNumbersSelect.value === "on");
|
|
25630
26101
|
setLineNumbersEnabled(initialLineNumbersEnabled);
|
|
26102
|
+
setActivityTrackingEnabled(activityTrackingEnabled, { persist: false, silent: true });
|
|
25631
26103
|
|
|
25632
26104
|
const storedResponseHighlightEnabled = readStoredResponseHighlightEnabled();
|
|
25633
26105
|
const initialResponseHighlightEnabled = storedResponseHighlightEnabled ?? Boolean(responseHighlightSelect && responseHighlightSelect.value === "on");
|