pi-studio 0.9.38 → 0.9.40
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 +14 -0
- package/README.md +4 -1
- package/client/studio-client.js +553 -21
- package/client/studio.css +253 -0
- package/index.ts +472 -2
- package/package.json +1 -1
- package/shared/studio-quarto-preview.js +169 -0
- package/shared/studio-repl-tmux.js +28 -0
package/client/studio-client.js
CHANGED
|
@@ -180,11 +180,12 @@
|
|
|
180
180
|
: "full";
|
|
181
181
|
const isEditorOnlyMode = studioMode === "editor-only";
|
|
182
182
|
const isSshStudioSession = Boolean(document.body && document.body.dataset && document.body.dataset.sshSession === "1");
|
|
183
|
-
const EDITOR_ONLY_RIGHT_VIEW_ALLOWED = new Set(["editor-preview", "files", "changes", "repl"]);
|
|
183
|
+
const EDITOR_ONLY_RIGHT_VIEW_ALLOWED = new Set(["editor-preview", "editor-quarto-preview", "files", "changes", "repl"]);
|
|
184
184
|
const RIGHT_VIEW_LABELS = {
|
|
185
185
|
markdown: "Response (Raw)",
|
|
186
186
|
preview: "Response (Preview)",
|
|
187
187
|
"editor-preview": "Editor (Preview)",
|
|
188
|
+
"editor-quarto-preview": "Editor (Quarto Preview)",
|
|
188
189
|
trace: "Working",
|
|
189
190
|
changes: "Changes",
|
|
190
191
|
files: "Files",
|
|
@@ -276,17 +277,22 @@
|
|
|
276
277
|
? "preview"
|
|
277
278
|
: (raw === "editor-preview"
|
|
278
279
|
? "editor-preview"
|
|
279
|
-
: (raw === "
|
|
280
|
-
? "
|
|
281
|
-
: (raw === "
|
|
282
|
-
? "
|
|
283
|
-
: (raw === "
|
|
284
|
-
? "
|
|
285
|
-
: (
|
|
280
|
+
: (raw === "editor-quarto-preview"
|
|
281
|
+
? "editor-quarto-preview"
|
|
282
|
+
: (raw === "repl"
|
|
283
|
+
? "repl"
|
|
284
|
+
: (raw === "files"
|
|
285
|
+
? "files"
|
|
286
|
+
: (raw === "changes"
|
|
287
|
+
? "changes"
|
|
288
|
+
: ((raw === "trace" || raw === "thinking") ? "trace" : "markdown"))))));
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
function normalizeRightViewValue(nextView) {
|
|
289
292
|
const normalized = canonicalRightViewValue(nextView);
|
|
293
|
+
if (normalized === "editor-quarto-preview" && !isCurrentStudioQuartoDocument()) {
|
|
294
|
+
return "editor-preview";
|
|
295
|
+
}
|
|
290
296
|
if (isEditorOnlyMode && !EDITOR_ONLY_RIGHT_VIEW_ALLOWED.has(normalized)) {
|
|
291
297
|
return "editor-preview";
|
|
292
298
|
}
|
|
@@ -295,6 +301,7 @@
|
|
|
295
301
|
|
|
296
302
|
function isRightViewAvailableInCurrentMode(view) {
|
|
297
303
|
const normalized = canonicalRightViewValue(view);
|
|
304
|
+
if (normalized === "editor-quarto-preview" && !isCurrentStudioQuartoDocument()) return false;
|
|
298
305
|
return !isEditorOnlyMode || EDITOR_ONLY_RIGHT_VIEW_ALLOWED.has(normalized);
|
|
299
306
|
}
|
|
300
307
|
|
|
@@ -309,13 +316,16 @@
|
|
|
309
316
|
|
|
310
317
|
function syncRightViewModeOptions() {
|
|
311
318
|
if (!rightViewSelect || !rightViewSelect.options) return;
|
|
319
|
+
const quartoRelevant = isCurrentStudioQuartoDocument();
|
|
312
320
|
Array.from(rightViewSelect.options).forEach((option) => {
|
|
313
321
|
if (!option) return;
|
|
314
|
-
|
|
322
|
+
const isQuartoOption = option.value === "editor-quarto-preview";
|
|
323
|
+
if (isQuartoOption) option.hidden = !quartoRelevant;
|
|
324
|
+
option.disabled = (isEditorOnlyMode && !EDITOR_ONLY_RIGHT_VIEW_ALLOWED.has(option.value)) || (isQuartoOption && !quartoRelevant);
|
|
315
325
|
});
|
|
316
326
|
rightViewSelect.title = isEditorOnlyMode
|
|
317
|
-
? "Editor-only views: Editor Preview, Changes, Files, or REPL. F7 cycles; Cmd/Ctrl+Alt+3/5/6/7 switch directly to
|
|
318
|
-
: "Right pane view mode. F7 cycles; Cmd/Ctrl+Alt+1–7 switches directly between
|
|
327
|
+
? "Editor-only views: Editor Preview, contextual Quarto Preview for .qmd/.md/.markdown files, Changes, Files, or REPL. F7 cycles; Cmd/Ctrl+Alt+3/5/6/7 switch directly to numbered right-pane views."
|
|
328
|
+
: "Right pane view mode. F7 cycles, including contextual Quarto Preview for file-backed .qmd, .md, and .markdown documents; Cmd/Ctrl+Alt+1–7 switches directly between the numbered views. Cmd/Ctrl+Alt+P/E/W keep their mnemonic Preview/Editor Preview/Working shortcuts.";
|
|
319
329
|
}
|
|
320
330
|
|
|
321
331
|
function getInitialRightView(source) {
|
|
@@ -1200,11 +1210,21 @@
|
|
|
1200
1210
|
|
|
1201
1211
|
function getStudioShortcutLabel(kind) {
|
|
1202
1212
|
const mac = isMacShortcutPlatform();
|
|
1203
|
-
if (kind === "repl-send") return mac ? "
|
|
1213
|
+
if (kind === "repl-send") return mac ? "⌃⇧↵" : "Ctrl+Shift+Enter";
|
|
1204
1214
|
if (kind === "run") return mac ? "⌘↵" : "Ctrl+Enter";
|
|
1205
1215
|
return "";
|
|
1206
1216
|
}
|
|
1207
1217
|
|
|
1218
|
+
function getStudioShortcutDescription(kind) {
|
|
1219
|
+
if (kind === "repl-send") {
|
|
1220
|
+
return isMacShortcutPlatform()
|
|
1221
|
+
? "Ctrl+Shift+Enter (Cmd+Shift+Enter also works where available)"
|
|
1222
|
+
: "Ctrl+Shift+Enter";
|
|
1223
|
+
}
|
|
1224
|
+
if (kind === "run") return "Cmd/Ctrl+Enter";
|
|
1225
|
+
return "";
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1208
1228
|
function withStudioShortcutLabel(label, kind) {
|
|
1209
1229
|
const shortcut = getStudioShortcutLabel(kind);
|
|
1210
1230
|
return shortcut ? (label + " " + shortcut) : label;
|
|
@@ -2009,6 +2029,22 @@
|
|
|
2009
2029
|
path: initialSourceState.path,
|
|
2010
2030
|
draftId: initialSourceState.draftId,
|
|
2011
2031
|
};
|
|
2032
|
+
let quartoPreviewContext = null;
|
|
2033
|
+
let quartoPreviewCheckRequestId = null;
|
|
2034
|
+
let quartoPreviewCheckSourcePath = "";
|
|
2035
|
+
let quartoPreviewActionRequestId = null;
|
|
2036
|
+
let quartoPreviewLogVisible = false;
|
|
2037
|
+
let quartoPreviewState = {
|
|
2038
|
+
status: "idle",
|
|
2039
|
+
sourcePath: "",
|
|
2040
|
+
url: "",
|
|
2041
|
+
log: "",
|
|
2042
|
+
error: null,
|
|
2043
|
+
context: null,
|
|
2044
|
+
startedAt: null,
|
|
2045
|
+
updatedAt: 0,
|
|
2046
|
+
actionRequestId: null,
|
|
2047
|
+
};
|
|
2012
2048
|
let fileBackedBaselineText = null;
|
|
2013
2049
|
let activePane = "left";
|
|
2014
2050
|
let paneFocusTarget = "off";
|
|
@@ -3908,7 +3944,7 @@
|
|
|
3908
3944
|
function getSelectEnabledValues(selectEl) {
|
|
3909
3945
|
if (!selectEl || !selectEl.options) return [];
|
|
3910
3946
|
return Array.from(selectEl.options)
|
|
3911
|
-
.filter((option) => option && !option.disabled)
|
|
3947
|
+
.filter((option) => option && !option.disabled && !option.hidden)
|
|
3912
3948
|
.map((option) => option.value)
|
|
3913
3949
|
.filter((value) => typeof value === "string" && value);
|
|
3914
3950
|
}
|
|
@@ -4743,6 +4779,22 @@
|
|
|
4743
4779
|
return;
|
|
4744
4780
|
}
|
|
4745
4781
|
|
|
4782
|
+
if (rightView === "editor-quarto-preview") {
|
|
4783
|
+
const context = getCurrentStudioQuartoContext();
|
|
4784
|
+
const current = getStudioQuartoProcessStatusForCurrentSource();
|
|
4785
|
+
const label = context && context.projectLabel ? context.projectLabel : (sourceState.label || "Quarto document");
|
|
4786
|
+
const statusLabel = quartoPreviewCheckRequestId
|
|
4787
|
+
? "checking"
|
|
4788
|
+
: (context && !context.available
|
|
4789
|
+
? "unavailable"
|
|
4790
|
+
: (current.sameSource
|
|
4791
|
+
? (current.status === "running" ? "running" : (current.status === "starting" ? "starting" : (current.status === "error" ? "error" : "stopped")))
|
|
4792
|
+
: ((quartoPreviewState.status === "running" || quartoPreviewState.status === "starting") ? "another preview running" : "ready to start")));
|
|
4793
|
+
referenceBadgeEl.textContent = "Quarto: " + label + " · " + statusLabel + " · saved-file preview"
|
|
4794
|
+
+ (isStudioQuartoEditorDirty() ? " · unsaved changes excluded" : "");
|
|
4795
|
+
return;
|
|
4796
|
+
}
|
|
4797
|
+
|
|
4746
4798
|
if (rightView === "editor-preview") {
|
|
4747
4799
|
const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
|
|
4748
4800
|
if (hasResponse) {
|
|
@@ -8182,6 +8234,7 @@
|
|
|
8182
8234
|
critiqueViewEl.addEventListener("click", handleReplPaneClick);
|
|
8183
8235
|
critiqueViewEl.addEventListener("click", handleFilesPaneClick);
|
|
8184
8236
|
critiqueViewEl.addEventListener("click", handleGitChangesPaneClick);
|
|
8237
|
+
critiqueViewEl.addEventListener("click", handleStudioQuartoPreviewClick);
|
|
8185
8238
|
critiqueViewEl.addEventListener("change", handleReplPaneChange);
|
|
8186
8239
|
critiqueViewEl.addEventListener("change", (event) => {
|
|
8187
8240
|
void handleFilesPaneChange(event);
|
|
@@ -8207,7 +8260,7 @@
|
|
|
8207
8260
|
|
|
8208
8261
|
function applyPendingResponseScrollReset() {
|
|
8209
8262
|
if (!pendingResponseScrollReset || !critiqueViewEl) return false;
|
|
8210
|
-
if (rightView === "editor-preview") return false;
|
|
8263
|
+
if (rightView === "editor-preview" || rightView === "editor-quarto-preview") return false;
|
|
8211
8264
|
|
|
8212
8265
|
pendingResponseScrollReset = false;
|
|
8213
8266
|
let targetEl = replaceResponsePaneWithClone();
|
|
@@ -8216,7 +8269,7 @@
|
|
|
8216
8269
|
: (cb) => window.setTimeout(cb, 16);
|
|
8217
8270
|
const resetScroll = () => {
|
|
8218
8271
|
if (!targetEl || !targetEl.isConnected) return;
|
|
8219
|
-
if (rightView === "editor-preview") return;
|
|
8272
|
+
if (rightView === "editor-preview" || rightView === "editor-quarto-preview") return;
|
|
8220
8273
|
targetEl.scrollTop = 0;
|
|
8221
8274
|
targetEl.scrollLeft = 0;
|
|
8222
8275
|
};
|
|
@@ -10548,8 +10601,289 @@
|
|
|
10548
10601
|
}
|
|
10549
10602
|
}
|
|
10550
10603
|
|
|
10604
|
+
function getStudioQuartoProcessStatusForCurrentSource() {
|
|
10605
|
+
const sourcePath = getCurrentStudioQuartoSourcePath();
|
|
10606
|
+
return {
|
|
10607
|
+
sameSource: studioQuartoPathsMatch(quartoPreviewState.sourcePath, sourcePath)
|
|
10608
|
+
|| Boolean(quartoPreviewState.context && studioQuartoPathsMatch(quartoPreviewState.context.requestedSourcePath, sourcePath)),
|
|
10609
|
+
sourcePath,
|
|
10610
|
+
status: quartoPreviewState.status,
|
|
10611
|
+
};
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10614
|
+
function isStudioQuartoEditorDirty() {
|
|
10615
|
+
return Boolean(isCurrentStudioQuartoDocument() && editorDiffersFromFileBackedBaseline());
|
|
10616
|
+
}
|
|
10617
|
+
|
|
10618
|
+
function requestStudioQuartoPreviewCheck(force) {
|
|
10619
|
+
const sourcePath = getCurrentStudioQuartoSourcePath();
|
|
10620
|
+
if (!isCurrentStudioQuartoDocument()) return false;
|
|
10621
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return false;
|
|
10622
|
+
if (!force && getCurrentStudioQuartoContext()) return true;
|
|
10623
|
+
if (!force && quartoPreviewCheckRequestId && studioQuartoPathsMatch(quartoPreviewCheckSourcePath, sourcePath)) return true;
|
|
10624
|
+
const requestId = makeRequestId();
|
|
10625
|
+
quartoPreviewCheckRequestId = requestId;
|
|
10626
|
+
quartoPreviewCheckSourcePath = sourcePath;
|
|
10627
|
+
quartoPreviewContext = null;
|
|
10628
|
+
if (rightView === "editor-quarto-preview") renderQuartoPreviewView();
|
|
10629
|
+
const sent = sendMessage({ type: "quarto_preview_check_request", requestId, sourcePath });
|
|
10630
|
+
if (!sent) {
|
|
10631
|
+
quartoPreviewCheckRequestId = null;
|
|
10632
|
+
quartoPreviewCheckSourcePath = "";
|
|
10633
|
+
if (rightView === "editor-quarto-preview") renderQuartoPreviewView();
|
|
10634
|
+
}
|
|
10635
|
+
return sent;
|
|
10636
|
+
}
|
|
10637
|
+
|
|
10638
|
+
function requestStudioQuartoPreviewStart() {
|
|
10639
|
+
const sourcePath = getCurrentStudioQuartoSourcePath();
|
|
10640
|
+
if (!isCurrentStudioQuartoDocument()) {
|
|
10641
|
+
setStatus("Quarto preview requires a file-backed .qmd, .md, or .markdown document.", "warning");
|
|
10642
|
+
return false;
|
|
10643
|
+
}
|
|
10644
|
+
if (quartoPreviewActionRequestId) return false;
|
|
10645
|
+
const requestId = makeRequestId();
|
|
10646
|
+
quartoPreviewActionRequestId = requestId;
|
|
10647
|
+
const sent = sendMessage({ type: "quarto_preview_start_request", requestId, sourcePath });
|
|
10648
|
+
if (!sent) quartoPreviewActionRequestId = null;
|
|
10649
|
+
renderQuartoPreviewView();
|
|
10650
|
+
if (sent) setStatus("Starting Quarto preview with computational cell execution disabled…", "warning");
|
|
10651
|
+
return sent;
|
|
10652
|
+
}
|
|
10653
|
+
|
|
10654
|
+
function requestStudioQuartoPreviewStop() {
|
|
10655
|
+
if (quartoPreviewActionRequestId) return false;
|
|
10656
|
+
const requestId = makeRequestId();
|
|
10657
|
+
quartoPreviewActionRequestId = requestId;
|
|
10658
|
+
const sent = sendMessage({ type: "quarto_preview_stop_request", requestId });
|
|
10659
|
+
if (!sent) quartoPreviewActionRequestId = null;
|
|
10660
|
+
renderQuartoPreviewView();
|
|
10661
|
+
if (sent) setStatus("Stopping Quarto preview…", "warning");
|
|
10662
|
+
return sent;
|
|
10663
|
+
}
|
|
10664
|
+
|
|
10665
|
+
function openStudioQuartoPreviewInBrowser() {
|
|
10666
|
+
const rawUrl = String(quartoPreviewState.url || "").trim();
|
|
10667
|
+
if (!rawUrl) {
|
|
10668
|
+
setStatus("Quarto preview is not running yet.", "warning");
|
|
10669
|
+
return false;
|
|
10670
|
+
}
|
|
10671
|
+
try {
|
|
10672
|
+
const parsed = new URL(rawUrl);
|
|
10673
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
10674
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("Unsupported preview protocol.");
|
|
10675
|
+
if (hostname !== "127.0.0.1" && hostname !== "localhost" && hostname !== "::1") throw new Error("Preview URL is not loopback-only.");
|
|
10676
|
+
const opened = window.open(parsed.href, "_blank");
|
|
10677
|
+
if (!opened) {
|
|
10678
|
+
setStatus("Browser blocked the Quarto preview tab. Allow pop-ups and try again.", "warning");
|
|
10679
|
+
return false;
|
|
10680
|
+
}
|
|
10681
|
+
try { opened.opener = null; } catch {}
|
|
10682
|
+
setStatus("Opening Quarto preview in a browser tab.", "success");
|
|
10683
|
+
return true;
|
|
10684
|
+
} catch (error) {
|
|
10685
|
+
setStatus("Could not open Quarto preview: " + ((error && error.message) ? error.message : String(error)), "error");
|
|
10686
|
+
return false;
|
|
10687
|
+
}
|
|
10688
|
+
}
|
|
10689
|
+
|
|
10690
|
+
function openStudioQuartoInstallationInstructions() {
|
|
10691
|
+
try {
|
|
10692
|
+
const opened = window.open("https://quarto.org/docs/get-started/", "_blank");
|
|
10693
|
+
if (!opened) {
|
|
10694
|
+
setStatus("Browser blocked the Quarto installation page.", "warning");
|
|
10695
|
+
return;
|
|
10696
|
+
}
|
|
10697
|
+
try { opened.opener = null; } catch {}
|
|
10698
|
+
} catch (error) {
|
|
10699
|
+
setStatus("Could not open Quarto installation instructions.", "warning");
|
|
10700
|
+
}
|
|
10701
|
+
}
|
|
10702
|
+
|
|
10703
|
+
function buildStudioQuartoLogDetails(log, open) {
|
|
10704
|
+
const text = String(log || "").trim();
|
|
10705
|
+
if (!text) return "";
|
|
10706
|
+
return "<details class='quarto-preview-log-details'" + (open ? " open" : "") + ">"
|
|
10707
|
+
+ "<summary>Quarto log</summary>"
|
|
10708
|
+
+ "<pre>" + escapeHtml(text) + "</pre>"
|
|
10709
|
+
+ "</details>";
|
|
10710
|
+
}
|
|
10711
|
+
|
|
10712
|
+
function buildStudioQuartoContextSummary(context) {
|
|
10713
|
+
if (!context) return "Quarto document";
|
|
10714
|
+
const kind = context.isProject
|
|
10715
|
+
? (context.projectType ? (context.projectType.charAt(0).toUpperCase() + context.projectType.slice(1) + " project") : "Quarto project")
|
|
10716
|
+
: "Standalone document";
|
|
10717
|
+
return kind + (context.version ? " · Quarto " + context.version : "");
|
|
10718
|
+
}
|
|
10719
|
+
|
|
10720
|
+
function updateStudioQuartoRunningShell() {
|
|
10721
|
+
const context = getCurrentStudioQuartoContext() || quartoPreviewState.context;
|
|
10722
|
+
const url = String(quartoPreviewState.url || "");
|
|
10723
|
+
let shell = critiqueViewEl ? critiqueViewEl.querySelector(".quarto-preview-shell[data-preview-url]") : null;
|
|
10724
|
+
if (!shell || shell.getAttribute("data-preview-url") !== url) {
|
|
10725
|
+
const label = context && context.projectLabel ? context.projectLabel : (sourceState.label || "Quarto document");
|
|
10726
|
+
critiqueViewEl.innerHTML = ""
|
|
10727
|
+
+ "<div class='quarto-preview-shell' data-preview-url='" + escapeHtml(url) + "'>"
|
|
10728
|
+
+ "<div class='quarto-preview-toolbar'>"
|
|
10729
|
+
+ "<div class='quarto-preview-title-group'>"
|
|
10730
|
+
+ "<span class='quarto-preview-title'>" + escapeHtml(label) + "</span>"
|
|
10731
|
+
+ "<span class='quarto-preview-subtitle'>" + escapeHtml(buildStudioQuartoContextSummary(context)) + "</span>"
|
|
10732
|
+
+ "</div>"
|
|
10733
|
+
+ "<div class='quarto-preview-actions'>"
|
|
10734
|
+
+ "<button type='button' data-quarto-action='open'>Open in browser ↗</button>"
|
|
10735
|
+
+ "<button type='button' data-quarto-action='restart'>Restart</button>"
|
|
10736
|
+
+ "<button type='button' data-quarto-action='toggle-log'>Show log</button>"
|
|
10737
|
+
+ "<button type='button' data-quarto-action='stop'>Stop</button>"
|
|
10738
|
+
+ "</div>"
|
|
10739
|
+
+ "</div>"
|
|
10740
|
+
+ "<div class='quarto-preview-dirty-warning' hidden>Unsaved editor changes are not included. Save the editor to update Quarto.</div>"
|
|
10741
|
+
+ "<iframe class='quarto-preview-frame' src='" + escapeHtml(url) + "' title='Quarto preview for " + escapeHtml(label) + "' sandbox='allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads' allow='clipboard-write; fullscreen' allowfullscreen referrerpolicy='no-referrer'></iframe>"
|
|
10742
|
+
+ "<div class='quarto-preview-live-log' hidden><pre></pre></div>"
|
|
10743
|
+
+ "</div>";
|
|
10744
|
+
shell = critiqueViewEl.querySelector(".quarto-preview-shell[data-preview-url]");
|
|
10745
|
+
}
|
|
10746
|
+
const warningEl = shell ? shell.querySelector(".quarto-preview-dirty-warning") : null;
|
|
10747
|
+
if (warningEl) warningEl.hidden = !isStudioQuartoEditorDirty();
|
|
10748
|
+
const logPanelEl = shell ? shell.querySelector(".quarto-preview-live-log") : null;
|
|
10749
|
+
const logPreEl = logPanelEl ? logPanelEl.querySelector("pre") : null;
|
|
10750
|
+
if (logPanelEl) logPanelEl.hidden = !quartoPreviewLogVisible;
|
|
10751
|
+
if (logPreEl && logPreEl.textContent !== quartoPreviewState.log) logPreEl.textContent = quartoPreviewState.log;
|
|
10752
|
+
const toggleLogBtn = shell ? shell.querySelector("[data-quarto-action='toggle-log']") : null;
|
|
10753
|
+
if (toggleLogBtn) toggleLogBtn.textContent = quartoPreviewLogVisible ? "Hide log" : "Show log";
|
|
10754
|
+
if (shell) {
|
|
10755
|
+
shell.querySelectorAll("button[data-quarto-action]").forEach((button) => {
|
|
10756
|
+
if (button.getAttribute("data-quarto-action") === "open" || button.getAttribute("data-quarto-action") === "toggle-log") return;
|
|
10757
|
+
button.disabled = Boolean(quartoPreviewActionRequestId);
|
|
10758
|
+
});
|
|
10759
|
+
}
|
|
10760
|
+
}
|
|
10761
|
+
|
|
10762
|
+
function renderQuartoPreviewView() {
|
|
10763
|
+
if (!critiqueViewEl) return;
|
|
10764
|
+
finishPreviewRender(critiqueViewEl);
|
|
10765
|
+
if (!isCurrentStudioQuartoDocument()) {
|
|
10766
|
+
critiqueViewEl.innerHTML = "<div class='quarto-preview-card'><h2>Quarto preview unavailable</h2><p>Open a file-backed <code>.qmd</code>, <code>.md</code>, or <code>.markdown</code> document to use this view.</p></div>";
|
|
10767
|
+
return;
|
|
10768
|
+
}
|
|
10769
|
+
|
|
10770
|
+
const current = getStudioQuartoProcessStatusForCurrentSource();
|
|
10771
|
+
const context = getCurrentStudioQuartoContext();
|
|
10772
|
+
const checking = Boolean(quartoPreviewCheckRequestId && studioQuartoPathsMatch(quartoPreviewCheckSourcePath, current.sourcePath));
|
|
10773
|
+
const actionPending = Boolean(quartoPreviewActionRequestId);
|
|
10774
|
+
if (current.sameSource && current.status === "running" && quartoPreviewState.url) {
|
|
10775
|
+
updateStudioQuartoRunningShell();
|
|
10776
|
+
return;
|
|
10777
|
+
}
|
|
10778
|
+
|
|
10779
|
+
if (checking && !context) {
|
|
10780
|
+
critiqueViewEl.innerHTML = "<div class='quarto-preview-card quarto-preview-card-centered'>"
|
|
10781
|
+
+ "<div class='quarto-preview-spinner' aria-hidden='true'></div>"
|
|
10782
|
+
+ "<h2>Checking Quarto…</h2>"
|
|
10783
|
+
+ "<p>Inspecting the saved document and its project configuration.</p>"
|
|
10784
|
+
+ "</div>";
|
|
10785
|
+
return;
|
|
10786
|
+
}
|
|
10787
|
+
|
|
10788
|
+
if (!context) {
|
|
10789
|
+
critiqueViewEl.innerHTML = "<div class='quarto-preview-card quarto-preview-card-centered'>"
|
|
10790
|
+
+ "<h2>Editor (Quarto Preview)</h2>"
|
|
10791
|
+
+ "<p>Check the installed Quarto version and this saved document before starting a preview.</p>"
|
|
10792
|
+
+ "<div class='quarto-preview-card-actions'><button type='button' data-quarto-action='check'>Check Quarto</button></div>"
|
|
10793
|
+
+ "</div>";
|
|
10794
|
+
return;
|
|
10795
|
+
}
|
|
10796
|
+
|
|
10797
|
+
if (!context.available) {
|
|
10798
|
+
const missing = context.reason === "not-found";
|
|
10799
|
+
critiqueViewEl.innerHTML = "<div class='quarto-preview-card quarto-preview-card-centered'>"
|
|
10800
|
+
+ "<h2>" + (missing ? "Quarto not found" : "Quarto preview unavailable") + "</h2>"
|
|
10801
|
+
+ "<p>" + escapeHtml(context.error || "Studio could not inspect this Quarto document.") + "</p>"
|
|
10802
|
+
+ (missing ? "<p class='quarto-preview-note'>Studio could not find <code>quarto</code> on its PATH.</p>" : "")
|
|
10803
|
+
+ "<div class='quarto-preview-card-actions'>"
|
|
10804
|
+
+ "<button type='button' data-quarto-action='check'>Check again</button>"
|
|
10805
|
+
+ (missing ? "<button type='button' data-quarto-action='install'>Installation instructions ↗</button>" : "")
|
|
10806
|
+
+ "</div>"
|
|
10807
|
+
+ buildStudioQuartoLogDetails(context.inspectLog, true)
|
|
10808
|
+
+ "</div>";
|
|
10809
|
+
return;
|
|
10810
|
+
}
|
|
10811
|
+
|
|
10812
|
+
const runningAnother = !current.sameSource && (current.status === "running" || current.status === "starting" || current.status === "stopping");
|
|
10813
|
+
const dirtyWarning = isStudioQuartoEditorDirty()
|
|
10814
|
+
? "<div class='quarto-preview-dirty-warning'>Unsaved editor changes are not included. Save before starting or refreshing Quarto.</div>"
|
|
10815
|
+
: "";
|
|
10816
|
+
if (current.sameSource && (current.status === "starting" || current.status === "stopping")) {
|
|
10817
|
+
const stopping = current.status === "stopping";
|
|
10818
|
+
critiqueViewEl.innerHTML = "<div class='quarto-preview-card quarto-preview-card-centered'>"
|
|
10819
|
+
+ "<div class='quarto-preview-spinner' aria-hidden='true'></div>"
|
|
10820
|
+
+ "<h2>" + (stopping ? "Stopping Quarto preview…" : "Starting Quarto preview…") + "</h2>"
|
|
10821
|
+
+ "<p>" + escapeHtml(buildStudioQuartoContextSummary(context)) + " · computational cells will not be executed.</p>"
|
|
10822
|
+
+ dirtyWarning
|
|
10823
|
+
+ (!stopping ? "<div class='quarto-preview-card-actions'><button type='button' data-quarto-action='stop'" + (actionPending ? " disabled" : "") + ">Stop</button></div>" : "")
|
|
10824
|
+
+ buildStudioQuartoLogDetails(quartoPreviewState.log, true)
|
|
10825
|
+
+ "</div>";
|
|
10826
|
+
return;
|
|
10827
|
+
}
|
|
10828
|
+
|
|
10829
|
+
const sameSourceError = current.sameSource && current.status === "error";
|
|
10830
|
+
const label = context.projectLabel || sourceState.label || "Quarto document";
|
|
10831
|
+
critiqueViewEl.innerHTML = "<div class='quarto-preview-card quarto-preview-card-centered'>"
|
|
10832
|
+
+ "<h2>" + (sameSourceError ? "Quarto preview stopped with an error" : "Ready to preview with Quarto") + "</h2>"
|
|
10833
|
+
+ "<p><strong>" + escapeHtml(label) + "</strong><br>" + escapeHtml(buildStudioQuartoContextSummary(context)) + "</p>"
|
|
10834
|
+
+ (sameSourceError ? "<div class='quarto-preview-error'>" + escapeHtml(quartoPreviewState.error || "Quarto preview failed.") + "</div>" : "")
|
|
10835
|
+
+ (runningAnother ? "<div class='quarto-preview-note'>Another Quarto document is currently being previewed. Starting this one will replace it.</div>" : "")
|
|
10836
|
+
+ dirtyWarning
|
|
10837
|
+
+ "<div class='quarto-preview-safety-note'><strong>Saved-file preview.</strong> Studio starts Quarto on <code>127.0.0.1</code> with <code>--no-execute</code>, so computational cells are not run. Quarto still processes the project's trusted configuration, extensions, filters, and render hooks, controls the page styling, and may create or update its normal rendered output files.</div>"
|
|
10838
|
+
+ "<div class='quarto-preview-card-actions'>"
|
|
10839
|
+
+ "<button type='button' class='quarto-preview-primary' data-quarto-action='start'" + (actionPending ? " disabled" : "") + ">" + (sameSourceError ? "Retry Quarto preview" : (runningAnother ? "Preview this document" : "Start Quarto preview")) + "</button>"
|
|
10840
|
+
+ (runningAnother && quartoPreviewState.url ? "<button type='button' data-quarto-action='open'>Open running preview ↗</button>" : "")
|
|
10841
|
+
+ (runningAnother ? "<button type='button' data-quarto-action='stop'" + (actionPending ? " disabled" : "") + ">Stop running preview</button>" : "")
|
|
10842
|
+
+ "</div>"
|
|
10843
|
+
+ buildStudioQuartoLogDetails(sameSourceError ? quartoPreviewState.log : context.inspectLog, sameSourceError)
|
|
10844
|
+
+ "</div>";
|
|
10845
|
+
}
|
|
10846
|
+
|
|
10847
|
+
function syncStudioQuartoDirtyUi() {
|
|
10848
|
+
if (rightView !== "editor-quarto-preview") return;
|
|
10849
|
+
const warningEl = critiqueViewEl ? critiqueViewEl.querySelector(".quarto-preview-dirty-warning") : null;
|
|
10850
|
+
if (warningEl && warningEl.classList && warningEl.closest(".quarto-preview-shell")) {
|
|
10851
|
+
warningEl.hidden = !isStudioQuartoEditorDirty();
|
|
10852
|
+
}
|
|
10853
|
+
updateReferenceBadge();
|
|
10854
|
+
}
|
|
10855
|
+
|
|
10856
|
+
function handleStudioQuartoPreviewClick(event) {
|
|
10857
|
+
if (rightView !== "editor-quarto-preview") return;
|
|
10858
|
+
const target = event && event.target instanceof Element ? event.target.closest("[data-quarto-action]") : null;
|
|
10859
|
+
if (!target || !critiqueViewEl.contains(target)) return;
|
|
10860
|
+
event.preventDefault();
|
|
10861
|
+
const action = target.getAttribute("data-quarto-action");
|
|
10862
|
+
if (action === "check") {
|
|
10863
|
+
requestStudioQuartoPreviewCheck(true);
|
|
10864
|
+
} else if (action === "start" || action === "restart") {
|
|
10865
|
+
requestStudioQuartoPreviewStart();
|
|
10866
|
+
} else if (action === "stop") {
|
|
10867
|
+
requestStudioQuartoPreviewStop();
|
|
10868
|
+
} else if (action === "open") {
|
|
10869
|
+
openStudioQuartoPreviewInBrowser();
|
|
10870
|
+
} else if (action === "install") {
|
|
10871
|
+
openStudioQuartoInstallationInstructions();
|
|
10872
|
+
} else if (action === "toggle-log") {
|
|
10873
|
+
quartoPreviewLogVisible = !quartoPreviewLogVisible;
|
|
10874
|
+
updateStudioQuartoRunningShell();
|
|
10875
|
+
}
|
|
10876
|
+
}
|
|
10877
|
+
|
|
10551
10878
|
function renderActiveResult() {
|
|
10552
|
-
if (critiqueViewEl)
|
|
10879
|
+
if (critiqueViewEl) {
|
|
10880
|
+
critiqueViewEl.classList.toggle("git-changes-host", rightView === "changes");
|
|
10881
|
+
critiqueViewEl.classList.toggle("quarto-preview-host", rightView === "editor-quarto-preview");
|
|
10882
|
+
}
|
|
10883
|
+
if (rightView === "editor-quarto-preview") {
|
|
10884
|
+
renderQuartoPreviewView();
|
|
10885
|
+
return;
|
|
10886
|
+
}
|
|
10553
10887
|
if (rightView === "trace") {
|
|
10554
10888
|
renderTraceView();
|
|
10555
10889
|
return;
|
|
@@ -10648,7 +10982,7 @@
|
|
|
10648
10982
|
: normalizeForCompare(sourceTextEl.value);
|
|
10649
10983
|
const responseLoaded = hasResponse && normalizedEditor === latestResponseNormalized;
|
|
10650
10984
|
const isCritiqueResponse = hasResponse && latestResponseIsStructuredCritique;
|
|
10651
|
-
const showingAuxiliaryRightPane = rightView === "trace" || rightView === "repl" || rightView === "files" || rightView === "changes";
|
|
10985
|
+
const showingAuxiliaryRightPane = rightView === "trace" || rightView === "repl" || rightView === "files" || rightView === "changes" || rightView === "editor-quarto-preview";
|
|
10652
10986
|
|
|
10653
10987
|
if (responseWrapEl) {
|
|
10654
10988
|
responseWrapEl.hidden = showingAuxiliaryRightPane;
|
|
@@ -10732,6 +11066,7 @@
|
|
|
10732
11066
|
: (exportingReplJournal ? "Export the Studio REPL record as standalone HTML and open it in the default browser." : "Export the current right-pane preview as standalone HTML and open it in the default browser.");
|
|
10733
11067
|
}
|
|
10734
11068
|
if (exportPreviewControlsEl) {
|
|
11069
|
+
exportPreviewControlsEl.hidden = rightView === "editor-quarto-preview";
|
|
10735
11070
|
exportPreviewControlsEl.title = canExportPreview
|
|
10736
11071
|
? (exportingReplJournal
|
|
10737
11072
|
? "Choose a format and export destination for the Studio REPL record."
|
|
@@ -10746,6 +11081,7 @@
|
|
|
10746
11081
|
pullLatestBtn.textContent = queuedLatestResponse ? "Fetch latest response *" : "Fetch latest response";
|
|
10747
11082
|
|
|
10748
11083
|
updateSyncBadge(normalizedEditor);
|
|
11084
|
+
syncStudioQuartoDirtyUi();
|
|
10749
11085
|
}
|
|
10750
11086
|
|
|
10751
11087
|
function refreshResponseUi() {
|
|
@@ -10802,6 +11138,86 @@
|
|
|
10802
11138
|
return null;
|
|
10803
11139
|
}
|
|
10804
11140
|
|
|
11141
|
+
function normalizeStudioQuartoClientPath(value) {
|
|
11142
|
+
const normalized = String(value || "").trim().replace(/\\/g, "/").replace(/\/+$/, "");
|
|
11143
|
+
return /^[A-Za-z]:\//.test(normalized) ? normalized.toLowerCase() : normalized;
|
|
11144
|
+
}
|
|
11145
|
+
|
|
11146
|
+
function getCurrentStudioQuartoSourcePath() {
|
|
11147
|
+
return sourceState && typeof sourceState.path === "string" ? sourceState.path.trim() : "";
|
|
11148
|
+
}
|
|
11149
|
+
|
|
11150
|
+
function isCurrentStudioQuartoDocument() {
|
|
11151
|
+
return /\.(?:qmd|md|markdown)$/i.test(getCurrentStudioQuartoSourcePath());
|
|
11152
|
+
}
|
|
11153
|
+
|
|
11154
|
+
function studioQuartoPathsMatch(left, right) {
|
|
11155
|
+
const a = normalizeStudioQuartoClientPath(left);
|
|
11156
|
+
const b = normalizeStudioQuartoClientPath(right);
|
|
11157
|
+
return Boolean(a && b && a === b);
|
|
11158
|
+
}
|
|
11159
|
+
|
|
11160
|
+
function normalizeStudioQuartoContext(value) {
|
|
11161
|
+
if (!value || typeof value !== "object") return null;
|
|
11162
|
+
return {
|
|
11163
|
+
sourcePath: typeof value.sourcePath === "string" ? value.sourcePath : "",
|
|
11164
|
+
requestedSourcePath: typeof value.requestedSourcePath === "string" ? value.requestedSourcePath : (typeof value.sourcePath === "string" ? value.sourcePath : ""),
|
|
11165
|
+
available: value.available === true,
|
|
11166
|
+
reason: typeof value.reason === "string" ? value.reason : "inspect-error",
|
|
11167
|
+
version: typeof value.version === "string" ? value.version : "",
|
|
11168
|
+
projectRoot: typeof value.projectRoot === "string" ? value.projectRoot : "",
|
|
11169
|
+
projectType: typeof value.projectType === "string" ? value.projectType : "",
|
|
11170
|
+
projectLabel: typeof value.projectLabel === "string" ? value.projectLabel : "Quarto document",
|
|
11171
|
+
outputFile: typeof value.outputFile === "string" ? value.outputFile : "",
|
|
11172
|
+
isProject: value.isProject === true,
|
|
11173
|
+
error: typeof value.error === "string" && value.error ? value.error : null,
|
|
11174
|
+
inspectLog: typeof value.inspectLog === "string" ? value.inspectLog : "",
|
|
11175
|
+
};
|
|
11176
|
+
}
|
|
11177
|
+
|
|
11178
|
+
function normalizeStudioQuartoPreviewState(value) {
|
|
11179
|
+
if (!value || typeof value !== "object") return {
|
|
11180
|
+
status: "idle",
|
|
11181
|
+
sourcePath: "",
|
|
11182
|
+
url: "",
|
|
11183
|
+
log: "",
|
|
11184
|
+
error: null,
|
|
11185
|
+
context: null,
|
|
11186
|
+
startedAt: null,
|
|
11187
|
+
updatedAt: 0,
|
|
11188
|
+
actionRequestId: null,
|
|
11189
|
+
};
|
|
11190
|
+
const allowedStatuses = new Set(["idle", "starting", "running", "stopping", "stopped", "error"]);
|
|
11191
|
+
return {
|
|
11192
|
+
status: allowedStatuses.has(value.status) ? value.status : "idle",
|
|
11193
|
+
sourcePath: typeof value.sourcePath === "string" ? value.sourcePath : "",
|
|
11194
|
+
url: typeof value.url === "string" ? value.url : "",
|
|
11195
|
+
log: typeof value.log === "string" ? value.log : "",
|
|
11196
|
+
error: typeof value.error === "string" && value.error ? value.error : null,
|
|
11197
|
+
context: normalizeStudioQuartoContext(value.context),
|
|
11198
|
+
startedAt: typeof value.startedAt === "number" && Number.isFinite(value.startedAt) ? value.startedAt : null,
|
|
11199
|
+
updatedAt: typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) ? value.updatedAt : 0,
|
|
11200
|
+
actionRequestId: typeof value.actionRequestId === "string" ? value.actionRequestId : null,
|
|
11201
|
+
};
|
|
11202
|
+
}
|
|
11203
|
+
|
|
11204
|
+
function getCurrentStudioQuartoContext() {
|
|
11205
|
+
const sourcePath = getCurrentStudioQuartoSourcePath();
|
|
11206
|
+
if (quartoPreviewContext && (
|
|
11207
|
+
studioQuartoPathsMatch(quartoPreviewContext.sourcePath, sourcePath)
|
|
11208
|
+
|| studioQuartoPathsMatch(quartoPreviewContext.requestedSourcePath, sourcePath)
|
|
11209
|
+
)) {
|
|
11210
|
+
return quartoPreviewContext;
|
|
11211
|
+
}
|
|
11212
|
+
if (quartoPreviewState.context && (
|
|
11213
|
+
studioQuartoPathsMatch(quartoPreviewState.context.sourcePath, sourcePath)
|
|
11214
|
+
|| studioQuartoPathsMatch(quartoPreviewState.context.requestedSourcePath, sourcePath)
|
|
11215
|
+
)) {
|
|
11216
|
+
return quartoPreviewState.context;
|
|
11217
|
+
}
|
|
11218
|
+
return null;
|
|
11219
|
+
}
|
|
11220
|
+
|
|
10805
11221
|
function getHtmlPreviewResourceContextOptions() {
|
|
10806
11222
|
const sourcePath = getEffectiveSavePath() || sourceState.path || "";
|
|
10807
11223
|
const resourceDir = getCurrentResourceDirValue();
|
|
@@ -10917,6 +11333,7 @@
|
|
|
10917
11333
|
|
|
10918
11334
|
function setSourceState(next, options) {
|
|
10919
11335
|
const previousDescriptor = getCurrentStudioDocumentDescriptor();
|
|
11336
|
+
const previousQuartoPath = getCurrentStudioQuartoSourcePath();
|
|
10920
11337
|
const nextPath = next && next.path ? next.path : null;
|
|
10921
11338
|
sourceState = {
|
|
10922
11339
|
source: next && next.source ? next.source : "blank",
|
|
@@ -10926,9 +11343,24 @@
|
|
|
10926
11343
|
? null
|
|
10927
11344
|
: (next && next.draftId ? next.draftId : makeStudioDraftId()),
|
|
10928
11345
|
};
|
|
11346
|
+
const nextQuartoPath = getCurrentStudioQuartoSourcePath();
|
|
11347
|
+
const quartoSourceChanged = !studioQuartoPathsMatch(previousQuartoPath, nextQuartoPath);
|
|
11348
|
+
if (quartoSourceChanged) {
|
|
11349
|
+
quartoPreviewContext = null;
|
|
11350
|
+
quartoPreviewCheckRequestId = null;
|
|
11351
|
+
quartoPreviewCheckSourcePath = "";
|
|
11352
|
+
quartoPreviewActionRequestId = null;
|
|
11353
|
+
quartoPreviewLogVisible = false;
|
|
11354
|
+
}
|
|
10929
11355
|
if (!sourceState.path) {
|
|
10930
11356
|
clearFileBackedBaseline();
|
|
10931
11357
|
}
|
|
11358
|
+
syncRightViewModeOptions();
|
|
11359
|
+
const leavingUnavailableQuartoView = rightView === "editor-quarto-preview" && !isCurrentStudioQuartoDocument();
|
|
11360
|
+
if (leavingUnavailableQuartoView) {
|
|
11361
|
+
rightView = "editor-preview";
|
|
11362
|
+
rightViewSelect.value = rightView;
|
|
11363
|
+
}
|
|
10932
11364
|
updateStudioDocumentUrlState(sourceState);
|
|
10933
11365
|
updateSourceBadge();
|
|
10934
11366
|
syncActionButtons();
|
|
@@ -10942,6 +11374,9 @@
|
|
|
10942
11374
|
previousDescriptor: previousDescriptor,
|
|
10943
11375
|
carryCurrentMetadataToNewDocument: Boolean(options && options.carryCurrentMetadataToNewDocument),
|
|
10944
11376
|
});
|
|
11377
|
+
const refreshChangedQuartoView = rightView === "editor-quarto-preview" && quartoSourceChanged && isCurrentStudioQuartoDocument();
|
|
11378
|
+
if (refreshChangedQuartoView) requestStudioQuartoPreviewCheck(false);
|
|
11379
|
+
if (leavingUnavailableQuartoView || refreshChangedQuartoView) refreshResponseUi();
|
|
10945
11380
|
scheduleWorkspacePersistence();
|
|
10946
11381
|
}
|
|
10947
11382
|
|
|
@@ -11759,6 +12194,9 @@
|
|
|
11759
12194
|
if (rightView !== "editor-preview") {
|
|
11760
12195
|
clearPreviewJumpHighlight(critiqueViewEl);
|
|
11761
12196
|
}
|
|
12197
|
+
if (rightView === "editor-quarto-preview") {
|
|
12198
|
+
requestStudioQuartoPreviewCheck(false);
|
|
12199
|
+
}
|
|
11762
12200
|
|
|
11763
12201
|
refreshResponseUi();
|
|
11764
12202
|
syncActionButtons();
|
|
@@ -18760,6 +19198,7 @@
|
|
|
18760
19198
|
const canQueueSteering = studioRunChainActive && !critiqueIsStop;
|
|
18761
19199
|
const hasReplSession = Boolean(getActiveReplSessionForCurrentRuntime());
|
|
18762
19200
|
const showReplSend = rightView === "repl";
|
|
19201
|
+
const replSendShortcut = getStudioShortcutDescription("repl-send");
|
|
18763
19202
|
|
|
18764
19203
|
if (isEditorOnlyMode) {
|
|
18765
19204
|
if (sendRunBtn) {
|
|
@@ -18781,8 +19220,8 @@
|
|
|
18781
19220
|
sendReplBtn.textContent = showReplSend ? withStudioShortcutLabel(replSendMode === "literate" ? "Send selection/chunks" : "Send to REPL", "repl-send") : "Send to REPL";
|
|
18782
19221
|
sendReplBtn.title = hasReplSession
|
|
18783
19222
|
? (replSendMode === "literate"
|
|
18784
|
-
? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut:
|
|
18785
|
-
: "Raw send: selection, or full editor if no selection. Shortcut:
|
|
19223
|
+
? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut: " + replSendShortcut + "."
|
|
19224
|
+
: "Raw send: selection, or full editor if no selection. Shortcut: " + replSendShortcut + ".")
|
|
18786
19225
|
: "Start or select a REPL session in the right pane first.";
|
|
18787
19226
|
const replActionLine = sendReplBtn.closest(".repl-action-line");
|
|
18788
19227
|
if (replActionLine instanceof HTMLElement) replActionLine.hidden = !showReplSend;
|
|
@@ -18842,8 +19281,8 @@
|
|
|
18842
19281
|
sendReplBtn.textContent = showReplSend ? withStudioShortcutLabel(replSendMode === "literate" ? "Send selection/chunks" : "Send to REPL", "repl-send") : "Send to REPL";
|
|
18843
19282
|
sendReplBtn.title = hasReplSession
|
|
18844
19283
|
? (replSendMode === "literate"
|
|
18845
|
-
? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut:
|
|
18846
|
-
: "Raw send: selection, or full editor if no selection. Shortcut:
|
|
19284
|
+
? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut: " + replSendShortcut + "."
|
|
19285
|
+
: "Raw send: selection, or full editor if no selection. Shortcut: " + replSendShortcut + ".")
|
|
18847
19286
|
: "Start or select a REPL session in the right pane first.";
|
|
18848
19287
|
const replActionLine = sendReplBtn.closest(".repl-action-line");
|
|
18849
19288
|
if (replActionLine instanceof HTMLElement) replActionLine.hidden = !showReplSend;
|
|
@@ -19038,6 +19477,80 @@
|
|
|
19038
19477
|
return;
|
|
19039
19478
|
}
|
|
19040
19479
|
|
|
19480
|
+
if (message.type === "quarto_preview_context") {
|
|
19481
|
+
const requestId = typeof message.requestId === "string" ? message.requestId : "";
|
|
19482
|
+
const context = normalizeStudioQuartoContext(message.context);
|
|
19483
|
+
const matchesCheck = Boolean(requestId && quartoPreviewCheckRequestId === requestId);
|
|
19484
|
+
const matchesAction = Boolean(requestId && quartoPreviewActionRequestId === requestId);
|
|
19485
|
+
if (!matchesCheck && !matchesAction) return;
|
|
19486
|
+
if (matchesCheck) {
|
|
19487
|
+
quartoPreviewCheckRequestId = null;
|
|
19488
|
+
quartoPreviewCheckSourcePath = "";
|
|
19489
|
+
}
|
|
19490
|
+
if (context) quartoPreviewContext = context;
|
|
19491
|
+
if (matchesAction && (!context || !context.available)) {
|
|
19492
|
+
quartoPreviewActionRequestId = null;
|
|
19493
|
+
}
|
|
19494
|
+
if (rightView === "editor-quarto-preview") renderQuartoPreviewView();
|
|
19495
|
+
updateReferenceBadge();
|
|
19496
|
+
return;
|
|
19497
|
+
}
|
|
19498
|
+
|
|
19499
|
+
if (message.type === "quarto_preview_state") {
|
|
19500
|
+
const previousQuartoStatus = quartoPreviewState.status;
|
|
19501
|
+
quartoPreviewState = normalizeStudioQuartoPreviewState(message.preview);
|
|
19502
|
+
if (quartoPreviewState.actionRequestId && quartoPreviewActionRequestId === quartoPreviewState.actionRequestId) {
|
|
19503
|
+
quartoPreviewActionRequestId = null;
|
|
19504
|
+
}
|
|
19505
|
+
if (quartoPreviewState.context && (
|
|
19506
|
+
studioQuartoPathsMatch(quartoPreviewState.context.sourcePath, getCurrentStudioQuartoSourcePath())
|
|
19507
|
+
|| studioQuartoPathsMatch(quartoPreviewState.context.requestedSourcePath, getCurrentStudioQuartoSourcePath())
|
|
19508
|
+
)) {
|
|
19509
|
+
quartoPreviewContext = quartoPreviewState.context;
|
|
19510
|
+
}
|
|
19511
|
+
if (rightView === "editor-quarto-preview") renderQuartoPreviewView();
|
|
19512
|
+
updateReferenceBadge();
|
|
19513
|
+
syncActionButtons();
|
|
19514
|
+
if (quartoPreviewState.status !== previousQuartoStatus) {
|
|
19515
|
+
if (quartoPreviewState.status === "running") {
|
|
19516
|
+
setStatus("Quarto preview is running with computational cell execution disabled.", "success");
|
|
19517
|
+
} else if (quartoPreviewState.status === "error" && quartoPreviewState.error) {
|
|
19518
|
+
setStatus(quartoPreviewState.error, "error");
|
|
19519
|
+
} else if (quartoPreviewState.status === "stopped") {
|
|
19520
|
+
setStatus("Quarto preview stopped.");
|
|
19521
|
+
}
|
|
19522
|
+
}
|
|
19523
|
+
return;
|
|
19524
|
+
}
|
|
19525
|
+
|
|
19526
|
+
if (message.type === "quarto_preview_action_error") {
|
|
19527
|
+
const requestId = typeof message.requestId === "string" ? message.requestId : "";
|
|
19528
|
+
if (requestId && quartoPreviewCheckRequestId === requestId) {
|
|
19529
|
+
quartoPreviewCheckRequestId = null;
|
|
19530
|
+
quartoPreviewCheckSourcePath = "";
|
|
19531
|
+
}
|
|
19532
|
+
if (requestId && quartoPreviewActionRequestId === requestId) quartoPreviewActionRequestId = null;
|
|
19533
|
+
const messageText = typeof message.message === "string" ? message.message : "Quarto preview request failed.";
|
|
19534
|
+
quartoPreviewContext = {
|
|
19535
|
+
sourcePath: getCurrentStudioQuartoSourcePath(),
|
|
19536
|
+
requestedSourcePath: getCurrentStudioQuartoSourcePath(),
|
|
19537
|
+
available: false,
|
|
19538
|
+
reason: "inspect-error",
|
|
19539
|
+
version: "",
|
|
19540
|
+
projectRoot: "",
|
|
19541
|
+
projectType: "",
|
|
19542
|
+
projectLabel: sourceState.label || "Quarto document",
|
|
19543
|
+
outputFile: "",
|
|
19544
|
+
isProject: false,
|
|
19545
|
+
error: messageText,
|
|
19546
|
+
inspectLog: "",
|
|
19547
|
+
};
|
|
19548
|
+
if (rightView === "editor-quarto-preview") renderQuartoPreviewView();
|
|
19549
|
+
updateReferenceBadge();
|
|
19550
|
+
setStatus(messageText, "error");
|
|
19551
|
+
return;
|
|
19552
|
+
}
|
|
19553
|
+
|
|
19041
19554
|
if (message.type === "hello_ack") {
|
|
19042
19555
|
const busy = Boolean(message.busy);
|
|
19043
19556
|
agentBusyFromServer = Boolean(message.agentBusy);
|
|
@@ -19050,6 +19563,15 @@
|
|
|
19050
19563
|
}
|
|
19051
19564
|
updatePiSessionModelState(message);
|
|
19052
19565
|
updatePiThemeState(message);
|
|
19566
|
+
if (message.quartoPreview && typeof message.quartoPreview === "object") {
|
|
19567
|
+
quartoPreviewState = normalizeStudioQuartoPreviewState(message.quartoPreview);
|
|
19568
|
+
if (quartoPreviewState.context && (
|
|
19569
|
+
studioQuartoPathsMatch(quartoPreviewState.context.sourcePath, getCurrentStudioQuartoSourcePath())
|
|
19570
|
+
|| studioQuartoPathsMatch(quartoPreviewState.context.requestedSourcePath, getCurrentStudioQuartoSourcePath())
|
|
19571
|
+
)) {
|
|
19572
|
+
quartoPreviewContext = quartoPreviewState.context;
|
|
19573
|
+
}
|
|
19574
|
+
}
|
|
19053
19575
|
if (typeof message.terminalSessionLabel === "string") {
|
|
19054
19576
|
terminalSessionLabel = message.terminalSessionLabel;
|
|
19055
19577
|
}
|
|
@@ -19130,6 +19652,11 @@
|
|
|
19130
19652
|
handleIncomingResponse(lastMarkdown, lastResponseKind, message.lastResponse.timestamp, message.lastResponse.thinking);
|
|
19131
19653
|
}
|
|
19132
19654
|
|
|
19655
|
+
if (rightView === "editor-quarto-preview") {
|
|
19656
|
+
requestStudioQuartoPreviewCheck(false);
|
|
19657
|
+
renderQuartoPreviewView();
|
|
19658
|
+
}
|
|
19659
|
+
|
|
19133
19660
|
if (pendingRequestId) {
|
|
19134
19661
|
if (busy) {
|
|
19135
19662
|
setStatus(getStudioBusyStatus(pendingKind), "warning");
|
|
@@ -19840,6 +20367,10 @@
|
|
|
19840
20367
|
if (ws === socket) {
|
|
19841
20368
|
ws = null;
|
|
19842
20369
|
}
|
|
20370
|
+
quartoPreviewCheckRequestId = null;
|
|
20371
|
+
quartoPreviewCheckSourcePath = "";
|
|
20372
|
+
quartoPreviewActionRequestId = null;
|
|
20373
|
+
if (rightView === "editor-quarto-preview") renderQuartoPreviewView();
|
|
19843
20374
|
setBusy(true);
|
|
19844
20375
|
|
|
19845
20376
|
if (kind === "invalidated") {
|
|
@@ -21295,6 +21826,7 @@
|
|
|
21295
21826
|
resourceDirInput.value = normalizeStudioResourceDirValue(initialResourceDir);
|
|
21296
21827
|
}
|
|
21297
21828
|
setSourceState(initialSourceState);
|
|
21829
|
+
if (initialSourceState.path) markFileBackedBaseline(sourceTextEl.value);
|
|
21298
21830
|
refreshResponseUi();
|
|
21299
21831
|
updateAnnotatedReplyHeaderButton();
|
|
21300
21832
|
setActivePane("left");
|