pi-studio 0.9.21 → 0.9.22
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 +12 -0
- package/client/studio-client.js +147 -35
- package/client/studio.css +9 -0
- package/index.ts +14 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `pi-studio` are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.9.22] — 2026-05-29
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added **Open current file in new editor tab** and **Open current text as copy in new editor tab** to Source & context for explicit file-backed vs detached-copy tab opening.
|
|
9
|
+
- Added `Cmd/Ctrl+Alt+P` to switch the right pane directly back to Preview without cycling through Files, REPL, or Working.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Changed the editor toolbar's detached-copy action into **New editor tab** for opening a blank editor tab, moved **Send current text to Pi editor** into Source & context, separated the passive origin summary from explicit **Detach from file** / **Reset origin** actions, and aligned local link menus with Files-view wording (**Open file tab** / **Convert tab**).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- New Studio tabs opened from Files, local preview links, preview exports, or editor-tab actions now skip cloned browser-tab workspace restore on first load, preventing inherited Files/preview state from briefly flashing and replacing the requested document preview.
|
|
16
|
+
|
|
5
17
|
## [0.9.21] — 2026-05-28
|
|
6
18
|
|
|
7
19
|
### Added
|
package/client/studio-client.js
CHANGED
|
@@ -177,6 +177,7 @@
|
|
|
177
177
|
const isSshStudioSession = Boolean(document.body && document.body.dataset && document.body.dataset.sshSession === "1");
|
|
178
178
|
|
|
179
179
|
const initialQueryParams = new URLSearchParams(window.location.search || "");
|
|
180
|
+
const skipInitialWorkspaceRestore = initialQueryParams.get("skipWorkspaceRestore") === "1";
|
|
180
181
|
const explicitDocumentIdentityFromUrl = initialQueryParams.has("docId")
|
|
181
182
|
|| initialQueryParams.has("docSource")
|
|
182
183
|
|| initialQueryParams.has("docLabel")
|
|
@@ -232,6 +233,10 @@
|
|
|
232
233
|
let pendingKind = null;
|
|
233
234
|
let stickyStudioKind = null;
|
|
234
235
|
const pendingCompanionWindows = new Map();
|
|
236
|
+
let sourceOriginSummaryEl = null;
|
|
237
|
+
let sourceResetOriginBtn = null;
|
|
238
|
+
let sourceOpenCurrentFileTabBtn = null;
|
|
239
|
+
let sourceOpenCurrentTextCopyTabBtn = null;
|
|
235
240
|
let initialDocumentApplied = false;
|
|
236
241
|
function normalizeRightViewValue(nextView) {
|
|
237
242
|
const raw = String(nextView || "").trim();
|
|
@@ -258,8 +263,8 @@
|
|
|
258
263
|
option.disabled = isEditorOnlyMode && !editorOnlyAllowed.has(option.value);
|
|
259
264
|
});
|
|
260
265
|
rightViewSelect.title = isEditorOnlyMode
|
|
261
|
-
? "Editor-only views: editor preview, Files, or REPL.
|
|
262
|
-
: "Right pane view mode.
|
|
266
|
+
? "Editor-only views: editor preview, Files, or REPL. F7 cycles when the right pane is active; Cmd/Ctrl+Alt+P switches directly to Preview."
|
|
267
|
+
: "Right pane view mode. F7 cycles when the right pane is active; Cmd/Ctrl+Alt+P switches directly to Preview.";
|
|
263
268
|
}
|
|
264
269
|
|
|
265
270
|
function getInitialRightView(source) {
|
|
@@ -2405,8 +2410,66 @@
|
|
|
2405
2410
|
suggestCompletionOptionsBtn.hidden = false;
|
|
2406
2411
|
if (completionContextSelect) completionContextSelect.hidden = true;
|
|
2407
2412
|
contextMenu = makeStudioUiRefreshMenu(suggestCompletionOptionsBtn, "context", "studio-refresh-context-anchor");
|
|
2408
|
-
|
|
2413
|
+
sourceOriginSummaryEl = makeStudioUiRefreshElement("div", "source-badge source-origin-summary", "Origin: blank");
|
|
2414
|
+
sourceOriginSummaryEl.setAttribute("aria-label", "Current editor origin");
|
|
2415
|
+
sourceResetOriginBtn = makeStudioUiRefreshElement("button", "source-reset-origin-btn", "Reset origin");
|
|
2416
|
+
sourceResetOriginBtn.type = "button";
|
|
2417
|
+
sourceResetOriginBtn.title = "Reset the editor origin and keep the current text in a new draft.";
|
|
2418
|
+
sourceResetOriginBtn.addEventListener("click", (event) => {
|
|
2419
|
+
event.preventDefault();
|
|
2420
|
+
event.stopPropagation();
|
|
2421
|
+
closeStudioUiRefreshMenus();
|
|
2422
|
+
resetEditorOrigin();
|
|
2423
|
+
});
|
|
2424
|
+
sourceOpenCurrentFileTabBtn = makeStudioUiRefreshElement("button", "source-open-file-tab-btn", "Open current file in new editor tab");
|
|
2425
|
+
sourceOpenCurrentFileTabBtn.type = "button";
|
|
2426
|
+
sourceOpenCurrentFileTabBtn.title = "Open this file-backed document in a new refreshable editor-only Studio tab.";
|
|
2427
|
+
sourceOpenCurrentFileTabBtn.addEventListener("click", (event) => {
|
|
2428
|
+
event.preventDefault();
|
|
2429
|
+
event.stopPropagation();
|
|
2430
|
+
const path = sourceState && sourceState.path ? String(sourceState.path) : "";
|
|
2431
|
+
if (!path) {
|
|
2432
|
+
setStatus("Open current file in new editor tab is only available for file-backed documents.", "warning");
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
closeStudioUiRefreshMenus();
|
|
2436
|
+
const targetUrl = buildAuthedStudioUrl("/", {
|
|
2437
|
+
mode: "editor-only",
|
|
2438
|
+
docSource: "file",
|
|
2439
|
+
docLabel: sourceState && sourceState.label ? sourceState.label : basenameForStudioPath(path),
|
|
2440
|
+
docPath: path,
|
|
2441
|
+
resourceDir: getCurrentResourceDirValue() || dirnameForDisplayPath(path),
|
|
2442
|
+
skipWorkspaceRestore: "1",
|
|
2443
|
+
});
|
|
2444
|
+
try {
|
|
2445
|
+
window.open(targetUrl, "_blank", "noopener");
|
|
2446
|
+
setStatus("Opening current file in a new editor tab.", "success");
|
|
2447
|
+
} catch (error) {
|
|
2448
|
+
setStatus((error && error.message) ? error.message : String(error || "Could not open file tab."), "warning");
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
sourceOpenCurrentTextCopyTabBtn = makeStudioUiRefreshElement("button", "source-open-text-copy-tab-btn", "Open current text as copy in new editor tab");
|
|
2452
|
+
sourceOpenCurrentTextCopyTabBtn.type = "button";
|
|
2453
|
+
sourceOpenCurrentTextCopyTabBtn.title = "Open a detached copy of the current editor text in a new editor-only Studio tab.";
|
|
2454
|
+
sourceOpenCurrentTextCopyTabBtn.addEventListener("click", (event) => {
|
|
2455
|
+
event.preventDefault();
|
|
2456
|
+
event.stopPropagation();
|
|
2457
|
+
const content = String(sourceTextEl.value || "");
|
|
2458
|
+
if (!content.trim()) {
|
|
2459
|
+
setStatus("Editor is empty. Use New editor tab for a blank editor.", "warning");
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
closeStudioUiRefreshMenus();
|
|
2463
|
+
requestOpenEditorOnlyDocument(content, {
|
|
2464
|
+
label: sourceState && sourceState.label ? sourceState.label : "current editor",
|
|
2465
|
+
path: sourceState && sourceState.path ? sourceState.path : undefined,
|
|
2466
|
+
resourceDir: getCurrentResourceDirValue() || undefined,
|
|
2467
|
+
});
|
|
2468
|
+
});
|
|
2469
|
+
if (sendEditorBtn) sendEditorBtn.textContent = "Send current text to Pi editor";
|
|
2470
|
+
appendStudioUiRefreshMenuSection(contextMenu.menu, "Document", [sourceOriginSummaryEl, sourceResetOriginBtn, sourceOpenCurrentFileTabBtn, sourceOpenCurrentTextCopyTabBtn]);
|
|
2409
2471
|
appendStudioUiRefreshMenuSection(contextMenu.menu, "Working directory", [resourceDirBtn, resourceDirLabel, resourceDirInputWrap]);
|
|
2472
|
+
if (!isEditorOnlyMode && sendEditorBtn) appendStudioUiRefreshMenuSection(contextMenu.menu, "Pi editor", [sendEditorBtn]);
|
|
2410
2473
|
const cursorContextBtn = makeStudioUiRefreshElement("button", "completion-context-option", "Editor only");
|
|
2411
2474
|
cursorContextBtn.type = "button";
|
|
2412
2475
|
cursorContextBtn.setAttribute("data-completion-context-mode", "cursor");
|
|
@@ -2484,7 +2547,6 @@
|
|
|
2484
2547
|
actionLineTwoEl.appendChild(copyDraftBtn);
|
|
2485
2548
|
if (suggestCompletionBtn) actionLineTwoEl.appendChild(suggestCompletionBtn);
|
|
2486
2549
|
if (openCompanionBtn) actionLineTwoEl.appendChild(openCompanionBtn);
|
|
2487
|
-
if (!isEditorOnlyMode && sendEditorBtn) actionLineTwoEl.appendChild(sendEditorBtn);
|
|
2488
2550
|
const replActionLineEl = makeStudioUiRefreshElement("div", "studio-refresh-action-line repl-action-line");
|
|
2489
2551
|
replActionLineEl.hidden = true;
|
|
2490
2552
|
if (sendReplBtn) replActionLineEl.appendChild(sendReplBtn);
|
|
@@ -3213,13 +3275,26 @@
|
|
|
3213
3275
|
|
|
3214
3276
|
function updateSourceBadge() {
|
|
3215
3277
|
const label = sourceState && sourceState.label ? sourceState.label : "blank";
|
|
3216
|
-
|
|
3278
|
+
const originText = (studioUiRefreshEnabled ? "Origin: " : "Editor origin: ") + label + (hasRefreshableFilePath() ? " · file" : "");
|
|
3217
3279
|
const descriptor = getCurrentStudioDocumentDescriptor();
|
|
3218
3280
|
if (sourceBadgeEl) {
|
|
3281
|
+
sourceBadgeEl.textContent = originText;
|
|
3219
3282
|
sourceBadgeEl.title = descriptor.fileBacked
|
|
3220
3283
|
? ("Editor origin: " + label + "\nClick to reset origin and detach the current editor text into a new draft. The file on disk will not be changed.")
|
|
3221
3284
|
: ("Editor origin: " + label + "\nClick to reset origin and start a new independent draft while keeping the current text and local notes.");
|
|
3222
3285
|
}
|
|
3286
|
+
if (sourceOriginSummaryEl) {
|
|
3287
|
+
sourceOriginSummaryEl.textContent = originText;
|
|
3288
|
+
sourceOriginSummaryEl.title = descriptor.fileBacked
|
|
3289
|
+
? ("File-backed editor: " + (descriptor.path || label))
|
|
3290
|
+
: ("Detached editor origin: " + label);
|
|
3291
|
+
}
|
|
3292
|
+
if (sourceResetOriginBtn) {
|
|
3293
|
+
sourceResetOriginBtn.textContent = descriptor.fileBacked ? "Detach from file" : "Reset origin";
|
|
3294
|
+
sourceResetOriginBtn.title = descriptor.fileBacked
|
|
3295
|
+
? "Detach the current editor text from this file and keep it in a new draft. The file on disk will not be changed."
|
|
3296
|
+
: "Reset the editor origin and keep the current text in a new draft.";
|
|
3297
|
+
}
|
|
3223
3298
|
// Show "Set working dir" button when not file-backed
|
|
3224
3299
|
var isFileBacked = hasRefreshableFilePath();
|
|
3225
3300
|
if (isFileBacked) {
|
|
@@ -3560,6 +3635,17 @@
|
|
|
3560
3635
|
setStatus("Right pane content focused.");
|
|
3561
3636
|
}
|
|
3562
3637
|
|
|
3638
|
+
function switchRightPaneToPrimaryPreview() {
|
|
3639
|
+
const targetView = isEditorOnlyMode ? "editor-preview" : "preview";
|
|
3640
|
+
const snapshot = snapshotStudioScrollablePositions();
|
|
3641
|
+
setRightView(targetView);
|
|
3642
|
+
scheduleStudioScrollablePositionRestore(snapshot);
|
|
3643
|
+
const label = rightViewSelect && rightViewSelect.selectedOptions && rightViewSelect.selectedOptions[0]
|
|
3644
|
+
? rightViewSelect.selectedOptions[0].textContent
|
|
3645
|
+
: (isEditorOnlyMode ? "Editor (Preview)" : "Response (Preview)");
|
|
3646
|
+
setStatus("Right pane view: " + String(label || "Preview") + ".");
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3563
3649
|
function cycleActivePaneView(direction) {
|
|
3564
3650
|
if (activePane === "right") {
|
|
3565
3651
|
if (!rightViewSelect || rightViewSelect.disabled) {
|
|
@@ -3858,6 +3944,16 @@
|
|
|
3858
3944
|
return;
|
|
3859
3945
|
}
|
|
3860
3946
|
|
|
3947
|
+
const isPreviewShortcut = (key.toLowerCase() === "p" || code === "KeyP")
|
|
3948
|
+
&& (event.metaKey || event.ctrlKey)
|
|
3949
|
+
&& event.altKey
|
|
3950
|
+
&& !event.shiftKey;
|
|
3951
|
+
if (isPreviewShortcut) {
|
|
3952
|
+
event.preventDefault();
|
|
3953
|
+
switchRightPaneToPrimaryPreview();
|
|
3954
|
+
return;
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3861
3957
|
const isContentFocusShortcut = key === "F8" && !event.metaKey && !event.ctrlKey && !event.altKey;
|
|
3862
3958
|
if (isContentFocusShortcut) {
|
|
3863
3959
|
event.preventDefault();
|
|
@@ -9908,6 +10004,14 @@
|
|
|
9908
10004
|
|
|
9909
10005
|
fileInput.disabled = uiBusy;
|
|
9910
10006
|
if (sourceBadgeEl) sourceBadgeEl.disabled = uiBusy;
|
|
10007
|
+
if (sourceResetOriginBtn) sourceResetOriginBtn.disabled = uiBusy;
|
|
10008
|
+
if (sourceOpenCurrentFileTabBtn) {
|
|
10009
|
+
sourceOpenCurrentFileTabBtn.disabled = uiBusy || !hasRefreshableFilePath();
|
|
10010
|
+
sourceOpenCurrentFileTabBtn.title = hasRefreshableFilePath()
|
|
10011
|
+
? "Open this file-backed document in a new refreshable editor-only Studio tab."
|
|
10012
|
+
: "Available after opening a file-backed document.";
|
|
10013
|
+
}
|
|
10014
|
+
if (sourceOpenCurrentTextCopyTabBtn) sourceOpenCurrentTextCopyTabBtn.disabled = uiBusy || wsState !== "Ready" || !String(sourceTextEl.value || "").trim();
|
|
9911
10015
|
saveAsBtn.disabled = uiBusy;
|
|
9912
10016
|
saveOverBtn.disabled = uiBusy || !canSaveOver;
|
|
9913
10017
|
if (refreshFromDiskBtn) refreshFromDiskBtn.disabled = uiBusy || !canRefreshFromDisk;
|
|
@@ -10030,6 +10134,7 @@
|
|
|
10030
10134
|
}
|
|
10031
10135
|
|
|
10032
10136
|
function shouldRestorePersistedWorkspaceState(state) {
|
|
10137
|
+
if (skipInitialWorkspaceRestore) return false;
|
|
10033
10138
|
if (!state || typeof state.text !== "string") return false;
|
|
10034
10139
|
const storedSourceState = normalizeWorkspaceSourceState(state.sourceState);
|
|
10035
10140
|
const initialIdentity = getWorkspaceStateIdentity(initialSourceState);
|
|
@@ -10951,6 +11056,7 @@
|
|
|
10951
11056
|
else params.delete("docPath");
|
|
10952
11057
|
if (nextDraftId) params.set("draftId", nextDraftId);
|
|
10953
11058
|
else params.delete("draftId");
|
|
11059
|
+
params.delete("skipWorkspaceRestore");
|
|
10954
11060
|
window.history.replaceState(null, "", currentUrl.toString());
|
|
10955
11061
|
} catch {
|
|
10956
11062
|
// Ignore URL-state update failures.
|
|
@@ -11166,10 +11272,10 @@
|
|
|
11166
11272
|
appendPreviewLinkMenuButton(menu, "Open PDF preview", "open-pdf");
|
|
11167
11273
|
appendPreviewLinkMenuButton(menu, "Open in new Studio tab", "open-preview-new");
|
|
11168
11274
|
} else if (kind === "text") {
|
|
11169
|
-
appendPreviewLinkMenuButton(menu, "Open
|
|
11275
|
+
appendPreviewLinkMenuButton(menu, "Open file tab", "open-new");
|
|
11170
11276
|
appendPreviewLinkMenuButton(menu, "Open here", "open-here");
|
|
11171
11277
|
} else if (kind === "office") {
|
|
11172
|
-
appendPreviewLinkMenuButton(menu, "Convert
|
|
11278
|
+
appendPreviewLinkMenuButton(menu, "Convert tab", "open-new");
|
|
11173
11279
|
appendPreviewLinkMenuButton(menu, "Convert here", "open-here");
|
|
11174
11280
|
} else if (kind === "image") {
|
|
11175
11281
|
appendPreviewLinkMenuButton(menu, "Open image preview", "open-image");
|
|
@@ -11306,10 +11412,11 @@
|
|
|
11306
11412
|
return;
|
|
11307
11413
|
}
|
|
11308
11414
|
const popup = pendingWindow || window.open("", "_blank");
|
|
11415
|
+
const openingLabel = getPreviewLocalLinkKind(href) === "office" ? "Opening converted document…" : "Opening file tab…";
|
|
11309
11416
|
try {
|
|
11310
11417
|
if (popup && popup.document && popup.document.body) {
|
|
11311
|
-
popup.document.title =
|
|
11312
|
-
popup.document.body.innerHTML = "<p style=\"font: 13px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 16px;\">
|
|
11418
|
+
popup.document.title = openingLabel;
|
|
11419
|
+
popup.document.body.innerHTML = "<p style=\"font: 13px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 16px;\">" + escapeHtml(openingLabel) + "</p>";
|
|
11313
11420
|
}
|
|
11314
11421
|
} catch {}
|
|
11315
11422
|
try {
|
|
@@ -11322,12 +11429,12 @@
|
|
|
11322
11429
|
try {
|
|
11323
11430
|
popup.opener = null;
|
|
11324
11431
|
popup.location.href = targetUrl;
|
|
11325
|
-
setStatus(payload && payload.converted ? "Opening converted document in a new editor." : "Opening
|
|
11432
|
+
setStatus(payload && payload.converted ? "Opening converted document in a new editor." : "Opening file-backed document in a new editor.", "success");
|
|
11326
11433
|
return;
|
|
11327
11434
|
} catch {}
|
|
11328
11435
|
}
|
|
11329
11436
|
window.open(targetUrl, "_blank", "noopener");
|
|
11330
|
-
setStatus(payload && payload.converted ? "Opening converted document in a new editor." : "Opening
|
|
11437
|
+
setStatus(payload && payload.converted ? "Opening converted document in a new editor." : "Opening file-backed document in a new editor.", "success");
|
|
11331
11438
|
} catch (error) {
|
|
11332
11439
|
if (popup && !popup.closed) {
|
|
11333
11440
|
try { popup.close(); } catch {}
|
|
@@ -18502,11 +18609,11 @@
|
|
|
18502
18609
|
const opened = navigatePendingCompanionWindow(responseRequestId, targetUrl);
|
|
18503
18610
|
const readyMessage = typeof message.message === "string" && message.message.trim()
|
|
18504
18611
|
? message.message.trim()
|
|
18505
|
-
: "Opened
|
|
18612
|
+
: "Opened editor tab with a detached copy of the current editor text.";
|
|
18506
18613
|
setStatus(
|
|
18507
18614
|
opened
|
|
18508
18615
|
? readyMessage
|
|
18509
|
-
: (targetUrl ? "
|
|
18616
|
+
: (targetUrl ? "Editor tab ready: " + targetUrl : "Editor tab is ready, but Studio did not receive a URL."),
|
|
18510
18617
|
opened ? "success" : "warning",
|
|
18511
18618
|
);
|
|
18512
18619
|
return;
|
|
@@ -18826,8 +18933,8 @@
|
|
|
18826
18933
|
try {
|
|
18827
18934
|
companionWindow = window.open("", "_blank");
|
|
18828
18935
|
if (companionWindow && companionWindow.document && companionWindow.document.body) {
|
|
18829
|
-
companionWindow.document.title = "Opening
|
|
18830
|
-
companionWindow.document.body.innerHTML = "<p style=\"font: 13px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 16px;\">Opening
|
|
18936
|
+
companionWindow.document.title = "Opening editor tab…";
|
|
18937
|
+
companionWindow.document.body.innerHTML = "<p style=\"font: 13px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 16px;\">Opening editor tab…</p>";
|
|
18831
18938
|
}
|
|
18832
18939
|
} catch {
|
|
18833
18940
|
companionWindow = null;
|
|
@@ -18883,6 +18990,28 @@
|
|
|
18883
18990
|
}
|
|
18884
18991
|
}
|
|
18885
18992
|
|
|
18993
|
+
function requestOpenEditorOnlyDocument(content, options) {
|
|
18994
|
+
const requestId = beginUiAction("open_editor_only");
|
|
18995
|
+
if (!requestId) return false;
|
|
18996
|
+
openPendingCompanionWindow(requestId);
|
|
18997
|
+
const config = options && typeof options === "object" ? options : {};
|
|
18998
|
+
const sent = sendMessage({
|
|
18999
|
+
type: "open_editor_only_request",
|
|
19000
|
+
requestId,
|
|
19001
|
+
content: String(content || ""),
|
|
19002
|
+
label: config.label || "current editor",
|
|
19003
|
+
path: config.path || undefined,
|
|
19004
|
+
resourceDir: config.resourceDir || undefined,
|
|
19005
|
+
});
|
|
19006
|
+
if (!sent) {
|
|
19007
|
+
closePendingCompanionWindow(requestId);
|
|
19008
|
+
pendingRequestId = null;
|
|
19009
|
+
pendingKind = null;
|
|
19010
|
+
setBusy(false);
|
|
19011
|
+
}
|
|
19012
|
+
return sent;
|
|
19013
|
+
}
|
|
19014
|
+
|
|
18886
19015
|
function describeSourceForAnnotation() {
|
|
18887
19016
|
if (sourceState.source === "file" && sourceState.label) {
|
|
18888
19017
|
return "file " + sourceState.label;
|
|
@@ -19498,27 +19627,10 @@
|
|
|
19498
19627
|
|
|
19499
19628
|
if (openCompanionBtn) {
|
|
19500
19629
|
openCompanionBtn.addEventListener("click", () => {
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
const requestId = beginUiAction("open_editor_only");
|
|
19504
|
-
if (!requestId) return;
|
|
19505
|
-
openPendingCompanionWindow(requestId);
|
|
19506
|
-
|
|
19507
|
-
const sent = sendMessage({
|
|
19508
|
-
type: "open_editor_only_request",
|
|
19509
|
-
requestId,
|
|
19510
|
-
content,
|
|
19511
|
-
label: sourceState && sourceState.label ? sourceState.label : "current editor",
|
|
19512
|
-
path: sourceState && sourceState.path ? sourceState.path : undefined,
|
|
19630
|
+
requestOpenEditorOnlyDocument("", {
|
|
19631
|
+
label: "blank",
|
|
19513
19632
|
resourceDir: getCurrentResourceDirValue() || undefined,
|
|
19514
19633
|
});
|
|
19515
|
-
|
|
19516
|
-
if (!sent) {
|
|
19517
|
-
closePendingCompanionWindow(requestId);
|
|
19518
|
-
pendingRequestId = null;
|
|
19519
|
-
pendingKind = null;
|
|
19520
|
-
setBusy(false);
|
|
19521
|
-
}
|
|
19522
19634
|
});
|
|
19523
19635
|
}
|
|
19524
19636
|
|
|
@@ -20038,7 +20150,7 @@
|
|
|
20038
20150
|
}
|
|
20039
20151
|
if (sourceBadgeEl) {
|
|
20040
20152
|
sourceBadgeEl.addEventListener("click", () => {
|
|
20041
|
-
resetEditorOrigin();
|
|
20153
|
+
if (!studioUiRefreshEnabled) resetEditorOrigin();
|
|
20042
20154
|
});
|
|
20043
20155
|
}
|
|
20044
20156
|
if (resourceDirBtn) {
|
package/client/studio.css
CHANGED
|
@@ -5176,6 +5176,15 @@
|
|
|
5176
5176
|
font-weight: 450;
|
|
5177
5177
|
}
|
|
5178
5178
|
|
|
5179
|
+
body.studio-ui-refresh .studio-refresh-menu-item > .source-origin-summary {
|
|
5180
|
+
width: 100%;
|
|
5181
|
+
border-color: var(--border-subtle);
|
|
5182
|
+
background: var(--panel-2);
|
|
5183
|
+
color: var(--studio-info-text, var(--muted));
|
|
5184
|
+
white-space: normal;
|
|
5185
|
+
line-height: 1.35;
|
|
5186
|
+
}
|
|
5187
|
+
|
|
5179
5188
|
body.studio-ui-refresh .studio-refresh-menu #critiqueBtn {
|
|
5180
5189
|
justify-content: flex-start;
|
|
5181
5190
|
text-align: left;
|
package/index.ts
CHANGED
|
@@ -6991,7 +6991,7 @@ async function respondLocalPreviewLinkJson(req: IncomingMessage, res: ServerResp
|
|
|
6991
6991
|
}
|
|
6992
6992
|
const document = buildStudioLocalResourcePreviewDocument(resource);
|
|
6993
6993
|
const docId = storeTransientStudioDocument(document);
|
|
6994
|
-
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId);
|
|
6994
|
+
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId, { skipWorkspaceRestore: true });
|
|
6995
6995
|
const parsedUrl = new URL(url);
|
|
6996
6996
|
respondJson(res, 200, {
|
|
6997
6997
|
...basePayload,
|
|
@@ -7056,7 +7056,7 @@ async function respondLocalPreviewLinkJson(req: IncomingMessage, res: ServerResp
|
|
|
7056
7056
|
}
|
|
7057
7057
|
|
|
7058
7058
|
const docId = storeTransientStudioDocument(document);
|
|
7059
|
-
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId);
|
|
7059
|
+
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId, { skipWorkspaceRestore: true });
|
|
7060
7060
|
const parsedUrl = new URL(url);
|
|
7061
7061
|
respondJson(res, 200, {
|
|
7062
7062
|
...basePayload,
|
|
@@ -9525,6 +9525,7 @@ function buildStudioUrl(
|
|
|
9525
9525
|
mode: StudioUiMode = "full",
|
|
9526
9526
|
doc?: InitialStudioDocument | null,
|
|
9527
9527
|
docId?: string,
|
|
9528
|
+
options?: { skipWorkspaceRestore?: boolean },
|
|
9528
9529
|
): string {
|
|
9529
9530
|
const params = new URLSearchParams({ token });
|
|
9530
9531
|
if (mode !== "full") params.set("mode", mode);
|
|
@@ -9534,6 +9535,7 @@ function buildStudioUrl(
|
|
|
9534
9535
|
if (doc?.path) params.set("docPath", doc.path);
|
|
9535
9536
|
if (doc?.draftId) params.set("draftId", doc.draftId);
|
|
9536
9537
|
if (doc?.resourceDir) params.set("resourceDir", doc.resourceDir);
|
|
9538
|
+
if (options?.skipWorkspaceRestore) params.set("skipWorkspaceRestore", "1");
|
|
9537
9539
|
return `http://127.0.0.1:${port}/?${params.toString()}`;
|
|
9538
9540
|
}
|
|
9539
9541
|
|
|
@@ -9994,8 +9996,8 @@ ${cssVarsBlock}
|
|
|
9994
9996
|
<option value="cursor" selected>Context: editor only</option>
|
|
9995
9997
|
<option value="session">Context: editor + latest response</option>
|
|
9996
9998
|
</select>
|
|
9997
|
-
<button id="openCompanionBtn" type="button" title="Open a
|
|
9998
|
-
<button id="sendEditorBtn" type="button">Send to
|
|
9999
|
+
<button id="openCompanionBtn" type="button" title="Open a blank editor-only Studio tab.">New editor tab</button>
|
|
10000
|
+
<button id="sendEditorBtn" type="button">Send current text to Pi editor</button>
|
|
9999
10001
|
</div>
|
|
10000
10002
|
<div class="source-actions-row">
|
|
10001
10003
|
<button id="insertHeaderBtn" type="button" title="Insert annotated-reply protocol header (source metadata, [an: ...] syntax hint, precedence note, and end marker).">Annotation header</button>
|
|
@@ -10116,7 +10118,7 @@ ${cssVarsBlock}
|
|
|
10116
10118
|
<div class="scratchpad-header">
|
|
10117
10119
|
<div>
|
|
10118
10120
|
<h2 id="reviewNotesTitle">Comments</h2>
|
|
10119
|
-
<p class="scratchpad-description">Local comments for editor text and editor previews. They stay out of the text;
|
|
10121
|
+
<p class="scratchpad-description">Local comments for editor text and editor previews. They stay out of the text; can be converted into inline <span class="review-notes-inline-token">[an: ...]</span> annotations.</p>
|
|
10120
10122
|
</div>
|
|
10121
10123
|
<button id="reviewNotesCloseBtn" type="button" class="scratchpad-close-btn" aria-label="Hide comments" title="Hide comments">✕</button>
|
|
10122
10124
|
</div>
|
|
@@ -10145,7 +10147,7 @@ ${cssVarsBlock}
|
|
|
10145
10147
|
<section id="rightPane">
|
|
10146
10148
|
<div id="rightSectionHeader" class="section-header">
|
|
10147
10149
|
<div class="section-header-main">
|
|
10148
|
-
<select id="rightViewSelect" aria-label="Response view mode" title="Right pane view mode.
|
|
10150
|
+
<select id="rightViewSelect" aria-label="Response view mode" title="Right pane view mode. F7 cycles when the right pane is active; Cmd/Ctrl+Alt+P switches directly to Preview.">
|
|
10149
10151
|
<option value="markdown">Response (Raw)</option>
|
|
10150
10152
|
<option value="preview" selected>Response (Preview)</option>
|
|
10151
10153
|
<option value="editor-preview">Editor (Preview)</option>
|
|
@@ -10237,6 +10239,7 @@ ${cssVarsBlock}
|
|
|
10237
10239
|
<dl>
|
|
10238
10240
|
<div><dt>F6</dt><dd>Switch between editor and right pane</dd></div>
|
|
10239
10241
|
<div><dt>F7 / Shift+F7</dt><dd>Cycle the active pane's view</dd></div>
|
|
10242
|
+
<div><dt>Cmd/Ctrl+Alt+P</dt><dd>Switch the right pane directly to Preview</dd></div>
|
|
10240
10243
|
<div><dt>F8</dt><dd>Focus editor text</dd></div>
|
|
10241
10244
|
<div><dt>Shift+F8</dt><dd>Focus right-pane content</dd></div>
|
|
10242
10245
|
<div><dt>F9</dt><dd>Toggle Zen mode</dd></div>
|
|
@@ -11696,7 +11699,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
11696
11699
|
resourceDir,
|
|
11697
11700
|
};
|
|
11698
11701
|
const docId = storeTransientStudioDocument(document);
|
|
11699
|
-
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId);
|
|
11702
|
+
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId, { skipWorkspaceRestore: true });
|
|
11700
11703
|
const parsedUrl = new URL(url);
|
|
11701
11704
|
sendToClient(client, {
|
|
11702
11705
|
type: "editor_only_ready",
|
|
@@ -11704,8 +11707,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
11704
11707
|
url,
|
|
11705
11708
|
relativeUrl: `${parsedUrl.pathname}${parsedUrl.search}`,
|
|
11706
11709
|
message: hasContent
|
|
11707
|
-
? "
|
|
11708
|
-
: "Blank
|
|
11710
|
+
? "Editor tab is ready with a detached copy of the current editor text."
|
|
11711
|
+
: "Blank editor tab is ready.",
|
|
11709
11712
|
});
|
|
11710
11713
|
return;
|
|
11711
11714
|
}
|
|
@@ -13070,7 +13073,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
13070
13073
|
resourceDir: dirname(exportedPath),
|
|
13071
13074
|
};
|
|
13072
13075
|
const docId = storeTransientStudioDocument(document);
|
|
13073
|
-
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId);
|
|
13076
|
+
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId, { skipWorkspaceRestore: true });
|
|
13074
13077
|
const parsedUrl = new URL(url);
|
|
13075
13078
|
respondJson(res, 200, {
|
|
13076
13079
|
ok: true,
|
|
@@ -13217,7 +13220,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
13217
13220
|
draftId: exportedPath ? undefined : createStudioDraftId(),
|
|
13218
13221
|
};
|
|
13219
13222
|
const docId = storeTransientStudioDocument(document);
|
|
13220
|
-
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId);
|
|
13223
|
+
const url = buildStudioUrl(serverState.port, serverState.token, "editor-only", document, docId, { skipWorkspaceRestore: true });
|
|
13221
13224
|
const parsedUrl = new URL(url);
|
|
13222
13225
|
respondJson(res, 200, {
|
|
13223
13226
|
ok: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.22",
|
|
4
4
|
"description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, active quiz, prompt/response history, live previews, and tmux-backed REPL/literate REPL workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|