pi-studio 0.5.41 → 0.5.43
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 +18 -0
- package/client/studio-client.js +133 -5
- package/client/studio.css +11 -14
- package/index.ts +80 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.43] — 2026-04-01
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Preview-side fenced code blocks now soft-wrap long lines by default instead of only wrapping `text`/`plaintext` fences, so Markdown code examples, shell snippets, and diffs are easier to read without horizontal scrolling.
|
|
11
|
+
|
|
12
|
+
## [0.5.42] — 2026-03-31
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- Studio now includes a **Refresh from disk** action for file-backed documents, including files originally opened from disk and documents later saved to disk from Studio.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- `/studio-pdf` section, subsection, and deeper heading styling is now more robust for larger-font exports: headings avoid awkward hyphenation, paragraph-level headings (`####`) render cleanly, callout title badges scale better with larger body font sizes, and exported code blocks now use a subtle shaded background.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Re-selecting the same file in **Load file content** now reloads it reliably instead of sometimes doing nothing.
|
|
22
|
+
- `/studio-pdf` now supports LaTeX `[H]` float placement in exported documents.
|
|
23
|
+
- Response preview now resets to the top more reliably for genuinely new replies, while keeping editor preview behavior unchanged.
|
|
24
|
+
|
|
7
25
|
## [0.5.41] — 2026-03-30
|
|
8
26
|
|
|
9
27
|
### Changed
|
package/client/studio-client.js
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
const rightPaneEl = document.getElementById("rightPane");
|
|
47
47
|
const sourceBadgeEl = document.getElementById("sourceBadge");
|
|
48
48
|
const syncBadgeEl = document.getElementById("syncBadge");
|
|
49
|
-
|
|
49
|
+
let critiqueViewEl = document.getElementById("critiqueView");
|
|
50
50
|
const referenceBadgeEl = document.getElementById("referenceBadge");
|
|
51
51
|
const editorViewSelect = document.getElementById("editorViewSelect");
|
|
52
52
|
const rightViewSelect = document.getElementById("rightViewSelect");
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
const loadHistoryPromptBtn = document.getElementById("loadHistoryPromptBtn");
|
|
75
75
|
const saveAsBtn = document.getElementById("saveAsBtn");
|
|
76
76
|
const saveOverBtn = document.getElementById("saveOverBtn");
|
|
77
|
+
const refreshFromDiskBtn = document.getElementById("refreshFromDiskBtn");
|
|
77
78
|
const sendEditorBtn = document.getElementById("sendEditorBtn");
|
|
78
79
|
const getEditorBtn = document.getElementById("getEditorBtn");
|
|
79
80
|
const loadGitDiffBtn = document.getElementById("loadGitDiffBtn");
|
|
@@ -196,6 +197,7 @@
|
|
|
196
197
|
label: initialSourceState.label,
|
|
197
198
|
path: initialSourceState.path,
|
|
198
199
|
};
|
|
200
|
+
let fileBackedBaselineText = null;
|
|
199
201
|
let activePane = "left";
|
|
200
202
|
let paneFocusTarget = "off";
|
|
201
203
|
const EDITOR_HIGHLIGHT_MAX_CHARS = 100_000;
|
|
@@ -427,6 +429,7 @@
|
|
|
427
429
|
if (kind === "send_to_editor") return "sending to pi editor";
|
|
428
430
|
if (kind === "get_from_editor") return "loading from pi editor";
|
|
429
431
|
if (kind === "load_git_diff") return "loading git diff";
|
|
432
|
+
if (kind === "refresh_from_disk") return "refreshing from disk";
|
|
430
433
|
if (kind === "save_as" || kind === "save_over") return "saving editor text";
|
|
431
434
|
return "submitting request";
|
|
432
435
|
}
|
|
@@ -779,11 +782,29 @@
|
|
|
779
782
|
}
|
|
780
783
|
});
|
|
781
784
|
|
|
785
|
+
function markFileBackedBaseline(text) {
|
|
786
|
+
fileBackedBaselineText = String(text || "");
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function clearFileBackedBaseline() {
|
|
790
|
+
fileBackedBaselineText = null;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function hasRefreshableFilePath() {
|
|
794
|
+
return Boolean(sourceState && sourceState.path);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function editorDiffersFromFileBackedBaseline() {
|
|
798
|
+
if (!hasRefreshableFilePath()) return false;
|
|
799
|
+
if (fileBackedBaselineText === null) return true;
|
|
800
|
+
return sourceTextEl.value !== fileBackedBaselineText;
|
|
801
|
+
}
|
|
802
|
+
|
|
782
803
|
function updateSourceBadge() {
|
|
783
804
|
const label = sourceState && sourceState.label ? sourceState.label : "blank";
|
|
784
805
|
sourceBadgeEl.textContent = "Editor origin: " + label;
|
|
785
806
|
// Show "Set working dir" button when not file-backed
|
|
786
|
-
var isFileBacked =
|
|
807
|
+
var isFileBacked = hasRefreshableFilePath();
|
|
787
808
|
if (isFileBacked) {
|
|
788
809
|
if (resourceDirInput) resourceDirInput.value = "";
|
|
789
810
|
if (resourceDirLabel) resourceDirLabel.textContent = "";
|
|
@@ -1926,12 +1947,52 @@
|
|
|
1926
1947
|
});
|
|
1927
1948
|
}
|
|
1928
1949
|
|
|
1950
|
+
function replaceResponsePaneWithClone() {
|
|
1951
|
+
const currentEl = critiqueViewEl;
|
|
1952
|
+
if (!currentEl || !currentEl.parentNode || typeof currentEl.cloneNode !== "function") {
|
|
1953
|
+
return currentEl;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
const replacement = currentEl.cloneNode(true);
|
|
1957
|
+
if (!replacement || replacement.nodeType !== 1) {
|
|
1958
|
+
return currentEl;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
currentEl.parentNode.replaceChild(replacement, currentEl);
|
|
1962
|
+
critiqueViewEl = replacement;
|
|
1963
|
+
return critiqueViewEl;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1929
1966
|
function applyPendingResponseScrollReset() {
|
|
1930
1967
|
if (!pendingResponseScrollReset || !critiqueViewEl) return false;
|
|
1931
1968
|
if (rightView === "editor-preview") return false;
|
|
1932
|
-
|
|
1933
|
-
critiqueViewEl.scrollLeft = 0;
|
|
1969
|
+
|
|
1934
1970
|
pendingResponseScrollReset = false;
|
|
1971
|
+
let targetEl = replaceResponsePaneWithClone();
|
|
1972
|
+
const schedule = typeof window.requestAnimationFrame === "function"
|
|
1973
|
+
? window.requestAnimationFrame.bind(window)
|
|
1974
|
+
: (cb) => window.setTimeout(cb, 16);
|
|
1975
|
+
const resetScroll = () => {
|
|
1976
|
+
if (!targetEl || !targetEl.isConnected) return;
|
|
1977
|
+
if (rightView === "editor-preview") return;
|
|
1978
|
+
targetEl.scrollTop = 0;
|
|
1979
|
+
targetEl.scrollLeft = 0;
|
|
1980
|
+
};
|
|
1981
|
+
|
|
1982
|
+
if (targetEl && targetEl.classList) {
|
|
1983
|
+
targetEl.classList.add("response-scroll-resetting");
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
resetScroll();
|
|
1987
|
+
schedule(() => {
|
|
1988
|
+
resetScroll();
|
|
1989
|
+
schedule(() => {
|
|
1990
|
+
resetScroll();
|
|
1991
|
+
if (targetEl && targetEl.classList) {
|
|
1992
|
+
targetEl.classList.remove("response-scroll-resetting");
|
|
1993
|
+
}
|
|
1994
|
+
});
|
|
1995
|
+
});
|
|
1935
1996
|
return true;
|
|
1936
1997
|
}
|
|
1937
1998
|
|
|
@@ -2518,7 +2579,7 @@
|
|
|
2518
2579
|
|
|
2519
2580
|
function getEffectiveSavePath() {
|
|
2520
2581
|
// File-backed: use the original path
|
|
2521
|
-
if (sourceState.
|
|
2582
|
+
if (sourceState.path) return sourceState.path;
|
|
2522
2583
|
// Upload with working dir + filename: derive path
|
|
2523
2584
|
if (sourceState.source === "upload" && sourceState.label && resourceDirInput && resourceDirInput.value.trim()) {
|
|
2524
2585
|
var name = sourceState.label.replace(/^upload:\s*/i, "");
|
|
@@ -2557,12 +2618,25 @@
|
|
|
2557
2618
|
saveOverBtn.title = "Save editor is available after opening a file, setting a working dir, or using Save editor as…. Shortcut: Cmd/Ctrl+S falls back to Save editor as… when needed.";
|
|
2558
2619
|
}
|
|
2559
2620
|
|
|
2621
|
+
function updateRefreshFromDiskTooltip() {
|
|
2622
|
+
if (!refreshFromDiskBtn) return;
|
|
2623
|
+
|
|
2624
|
+
if (hasRefreshableFilePath()) {
|
|
2625
|
+
refreshFromDiskBtn.title = "Reload the current file-backed document from disk: " + sourceState.path;
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
refreshFromDiskBtn.title = "Refresh from disk is only available for documents that currently have a file path.";
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2560
2632
|
function syncActionButtons() {
|
|
2561
2633
|
const canSaveOver = Boolean(getEffectiveSavePath());
|
|
2634
|
+
const canRefreshFromDisk = hasRefreshableFilePath();
|
|
2562
2635
|
|
|
2563
2636
|
fileInput.disabled = uiBusy;
|
|
2564
2637
|
saveAsBtn.disabled = uiBusy;
|
|
2565
2638
|
saveOverBtn.disabled = uiBusy || !canSaveOver;
|
|
2639
|
+
if (refreshFromDiskBtn) refreshFromDiskBtn.disabled = uiBusy || !canRefreshFromDisk;
|
|
2566
2640
|
sendEditorBtn.disabled = uiBusy || isEditorOnlyMode;
|
|
2567
2641
|
if (getEditorBtn) getEditorBtn.disabled = uiBusy;
|
|
2568
2642
|
if (loadGitDiffBtn) loadGitDiffBtn.disabled = uiBusy;
|
|
@@ -2581,6 +2655,7 @@
|
|
|
2581
2655
|
insertHeaderBtn.disabled = uiBusy || isEditorOnlyMode;
|
|
2582
2656
|
lensSelect.disabled = uiBusy || isEditorOnlyMode;
|
|
2583
2657
|
updateSaveFileTooltip();
|
|
2658
|
+
updateRefreshFromDiskTooltip();
|
|
2584
2659
|
updateHistoryControls();
|
|
2585
2660
|
updateResultActionButtons();
|
|
2586
2661
|
}
|
|
@@ -2598,6 +2673,9 @@
|
|
|
2598
2673
|
label: next && next.label ? next.label : "blank",
|
|
2599
2674
|
path: next && next.path ? next.path : null,
|
|
2600
2675
|
};
|
|
2676
|
+
if (!sourceState.path) {
|
|
2677
|
+
clearFileBackedBaseline();
|
|
2678
|
+
}
|
|
2601
2679
|
updateSourceBadge();
|
|
2602
2680
|
syncActionButtons();
|
|
2603
2681
|
}
|
|
@@ -3778,6 +3856,9 @@
|
|
|
3778
3856
|
label: message.initialDocument.label || "blank",
|
|
3779
3857
|
path: message.initialDocument.path || null,
|
|
3780
3858
|
});
|
|
3859
|
+
if (message.initialDocument.path) {
|
|
3860
|
+
markFileBackedBaseline(message.initialDocument.text);
|
|
3861
|
+
}
|
|
3781
3862
|
refreshResponseUi();
|
|
3782
3863
|
if (typeof message.initialDocument.label === "string" && message.initialDocument.label.length > 0) {
|
|
3783
3864
|
setStatus("Loaded " + message.initialDocument.label + ".", "success");
|
|
@@ -3978,6 +4059,8 @@
|
|
|
3978
4059
|
if (typeof message.requestId === "string" && pendingRequestId === message.requestId) {
|
|
3979
4060
|
pendingRequestId = null;
|
|
3980
4061
|
pendingKind = null;
|
|
4062
|
+
clearArmedTitleAttention(message.requestId);
|
|
4063
|
+
stickyStudioKind = null;
|
|
3981
4064
|
}
|
|
3982
4065
|
if (message.path) {
|
|
3983
4066
|
setSourceState({
|
|
@@ -3985,6 +4068,7 @@
|
|
|
3985
4068
|
label: message.label || message.path,
|
|
3986
4069
|
path: message.path,
|
|
3987
4070
|
});
|
|
4071
|
+
markFileBackedBaseline(sourceTextEl.value);
|
|
3988
4072
|
}
|
|
3989
4073
|
setBusy(false);
|
|
3990
4074
|
setWsState("Ready");
|
|
@@ -4032,6 +4116,15 @@
|
|
|
4032
4116
|
return;
|
|
4033
4117
|
}
|
|
4034
4118
|
|
|
4119
|
+
if (typeof message.requestId === "string" && pendingRequestId === message.requestId) {
|
|
4120
|
+
pendingRequestId = null;
|
|
4121
|
+
pendingKind = null;
|
|
4122
|
+
clearArmedTitleAttention(message.requestId);
|
|
4123
|
+
stickyStudioKind = null;
|
|
4124
|
+
setBusy(false);
|
|
4125
|
+
setWsState("Ready");
|
|
4126
|
+
}
|
|
4127
|
+
|
|
4035
4128
|
const nextSource =
|
|
4036
4129
|
nextDoc.source === "file" || nextDoc.source === "last-response"
|
|
4037
4130
|
? nextDoc.source
|
|
@@ -4045,6 +4138,9 @@
|
|
|
4045
4138
|
|
|
4046
4139
|
setEditorText(nextDoc.text, { preserveScroll: false, preserveSelection: false });
|
|
4047
4140
|
setSourceState({ source: nextSource, label: nextLabel, path: nextPath });
|
|
4141
|
+
if (nextPath) {
|
|
4142
|
+
markFileBackedBaseline(nextDoc.text);
|
|
4143
|
+
}
|
|
4048
4144
|
refreshResponseUi();
|
|
4049
4145
|
setStatus(
|
|
4050
4146
|
typeof message.message === "string" && message.message.trim()
|
|
@@ -4828,6 +4924,34 @@
|
|
|
4828
4924
|
}
|
|
4829
4925
|
});
|
|
4830
4926
|
|
|
4927
|
+
if (refreshFromDiskBtn) {
|
|
4928
|
+
refreshFromDiskBtn.addEventListener("click", () => {
|
|
4929
|
+
if (!hasRefreshableFilePath()) {
|
|
4930
|
+
setStatus("Refresh from disk is only available for file-backed documents.", "warning");
|
|
4931
|
+
return;
|
|
4932
|
+
}
|
|
4933
|
+
|
|
4934
|
+
if (editorDiffersFromFileBackedBaseline()) {
|
|
4935
|
+
const confirmed = window.confirm("Replace current editor contents with the latest version from disk?");
|
|
4936
|
+
if (!confirmed) return;
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
const requestId = beginUiAction("refresh_from_disk");
|
|
4940
|
+
if (!requestId) return;
|
|
4941
|
+
|
|
4942
|
+
const sent = sendMessage({
|
|
4943
|
+
type: "refresh_from_disk_request",
|
|
4944
|
+
requestId,
|
|
4945
|
+
});
|
|
4946
|
+
|
|
4947
|
+
if (!sent) {
|
|
4948
|
+
pendingRequestId = null;
|
|
4949
|
+
pendingKind = null;
|
|
4950
|
+
setBusy(false);
|
|
4951
|
+
}
|
|
4952
|
+
});
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4831
4955
|
sendEditorBtn.addEventListener("click", () => {
|
|
4832
4956
|
const content = sourceTextEl.value;
|
|
4833
4957
|
if (!content.trim()) {
|
|
@@ -5136,6 +5260,10 @@
|
|
|
5136
5260
|
const file = fileInput.files && fileInput.files[0];
|
|
5137
5261
|
if (!file) return;
|
|
5138
5262
|
|
|
5263
|
+
// Clear the input immediately so selecting the same file again will
|
|
5264
|
+
// still fire a future change event.
|
|
5265
|
+
fileInput.value = "";
|
|
5266
|
+
|
|
5139
5267
|
const reader = new FileReader();
|
|
5140
5268
|
reader.onload = () => {
|
|
5141
5269
|
const text = typeof reader.result === "string" ? reader.result : "";
|
package/client/studio.css
CHANGED
|
@@ -795,7 +795,11 @@
|
|
|
795
795
|
border: 1px solid var(--md-codeblock-border);
|
|
796
796
|
border-radius: 8px;
|
|
797
797
|
padding: 12px 14px;
|
|
798
|
-
overflow:
|
|
798
|
+
overflow-x: hidden;
|
|
799
|
+
overflow-y: auto;
|
|
800
|
+
white-space: pre-wrap;
|
|
801
|
+
overflow-wrap: anywhere;
|
|
802
|
+
word-break: break-word;
|
|
799
803
|
margin-top: 0;
|
|
800
804
|
margin-bottom: 1em;
|
|
801
805
|
}
|
|
@@ -808,21 +812,10 @@
|
|
|
808
812
|
|
|
809
813
|
.rendered-markdown pre code {
|
|
810
814
|
color: var(--md-codeblock);
|
|
815
|
+
white-space: inherit;
|
|
811
816
|
}
|
|
812
817
|
|
|
813
|
-
.rendered-markdown pre
|
|
814
|
-
.rendered-markdown pre.plaintext,
|
|
815
|
-
.rendered-markdown pre.sourceCode.txt {
|
|
816
|
-
white-space: pre-wrap;
|
|
817
|
-
overflow-x: hidden;
|
|
818
|
-
overflow-wrap: anywhere;
|
|
819
|
-
word-break: break-word;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
.rendered-markdown pre.text code,
|
|
823
|
-
.rendered-markdown pre.plaintext code,
|
|
824
|
-
.rendered-markdown pre.sourceCode.txt code,
|
|
825
|
-
.rendered-markdown pre.sourceCode.txt code > span {
|
|
818
|
+
.rendered-markdown pre code > span {
|
|
826
819
|
white-space: inherit;
|
|
827
820
|
}
|
|
828
821
|
|
|
@@ -1220,6 +1213,10 @@
|
|
|
1220
1213
|
transform: translateZ(0);
|
|
1221
1214
|
}
|
|
1222
1215
|
|
|
1216
|
+
.panel-scroll.response-scroll-resetting {
|
|
1217
|
+
overflow-anchor: none;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1223
1220
|
.preview-error {
|
|
1224
1221
|
color: var(--warn);
|
|
1225
1222
|
margin-bottom: 0.75em;
|
package/index.ts
CHANGED
|
@@ -159,6 +159,11 @@ interface SaveOverRequestMessage {
|
|
|
159
159
|
content: string;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
interface RefreshFromDiskRequestMessage {
|
|
163
|
+
type: "refresh_from_disk_request";
|
|
164
|
+
requestId: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
162
167
|
interface SendToEditorRequestMessage {
|
|
163
168
|
type: "send_to_editor_request";
|
|
164
169
|
requestId: string;
|
|
@@ -192,6 +197,7 @@ type IncomingStudioMessage =
|
|
|
192
197
|
| CompactRequestMessage
|
|
193
198
|
| SaveAsRequestMessage
|
|
194
199
|
| SaveOverRequestMessage
|
|
200
|
+
| RefreshFromDiskRequestMessage
|
|
195
201
|
| SendToEditorRequestMessage
|
|
196
202
|
| GetFromEditorRequestMessage
|
|
197
203
|
| LoadGitDiffRequestMessage
|
|
@@ -234,25 +240,38 @@ function buildStudioPdfTitleSpacingLength(value: string | undefined, fallback: s
|
|
|
234
240
|
return trimmed || fallback;
|
|
235
241
|
}
|
|
236
242
|
|
|
243
|
+
function buildStudioPdfCalloutTitleSizeCommand(options?: StudioPdfRenderOptions): string {
|
|
244
|
+
const sizePt = getStudioRequestedPdfFontsizePt(options);
|
|
245
|
+
if (sizePt && sizePt >= 14) return "\\normalsize";
|
|
246
|
+
if (sizePt && sizePt >= 13) return "\\small";
|
|
247
|
+
return "\\footnotesize";
|
|
248
|
+
}
|
|
249
|
+
|
|
237
250
|
function buildStudioPdfPreamble(options?: StudioPdfRenderOptions): string {
|
|
238
251
|
const sectionHeadingSize = buildStudioPdfHeadingSizeCommand(options?.sectionSize, "\\Large");
|
|
239
252
|
const subsectionHeadingSize = buildStudioPdfHeadingSizeCommand(options?.subsectionSize, "\\large");
|
|
240
253
|
const subsubsectionHeadingSize = buildStudioPdfHeadingSizeCommand(options?.subsubsectionSize, "\\normalsize");
|
|
254
|
+
const calloutTitleSize = buildStudioPdfCalloutTitleSizeCommand(options);
|
|
241
255
|
const sectionSpaceBefore = buildStudioPdfTitleSpacingLength(options?.sectionSpaceBefore, "1.5ex plus 0.5ex minus 0.2ex");
|
|
242
256
|
const sectionSpaceAfter = buildStudioPdfTitleSpacingLength(options?.sectionSpaceAfter, "1ex plus 0.2ex");
|
|
243
257
|
const subsectionSpaceBefore = buildStudioPdfTitleSpacingLength(options?.subsectionSpaceBefore, "1.2ex plus 0.4ex minus 0.2ex");
|
|
244
258
|
const subsectionSpaceAfter = buildStudioPdfTitleSpacingLength(options?.subsectionSpaceAfter, "0.6ex plus 0.1ex");
|
|
245
259
|
return `\\usepackage{titlesec}
|
|
246
|
-
\\titleformat{\\section}{${sectionHeadingSize}\\bfseries\\sffamily}{}{0pt}{}[\\vspace{3pt}\\titlerule\\vspace{12pt}]
|
|
247
|
-
\\titleformat{\\subsection}{${subsectionHeadingSize}\\bfseries\\sffamily}{}{0pt}{}
|
|
248
|
-
\\titleformat{\\subsubsection}{${subsubsectionHeadingSize}\\bfseries\\sffamily}{}{0pt}{}
|
|
260
|
+
\\titleformat{\\section}{${sectionHeadingSize}\\bfseries\\sffamily\\raggedright\\hyphenpenalty=10000\\exhyphenpenalty=10000\\relax}{}{0pt}{}[\\vspace{3pt}\\titlerule\\vspace{12pt}]
|
|
261
|
+
\\titleformat{\\subsection}{${subsectionHeadingSize}\\bfseries\\sffamily\\raggedright\\hyphenpenalty=10000\\exhyphenpenalty=10000\\relax}{}{0pt}{}
|
|
262
|
+
\\titleformat{\\subsubsection}{${subsubsectionHeadingSize}\\bfseries\\sffamily\\raggedright\\hyphenpenalty=10000\\exhyphenpenalty=10000\\relax}{}{0pt}{}
|
|
263
|
+
\\titleformat{\\paragraph}[runin]{\\normalsize\\bfseries\\sffamily\\raggedright\\hyphenpenalty=10000\\exhyphenpenalty=10000\\relax}{}{0pt}{}
|
|
264
|
+
\\titleformat{\\subparagraph}[runin]{\\small\\bfseries\\sffamily\\raggedright\\hyphenpenalty=10000\\exhyphenpenalty=10000\\relax}{}{0pt}{}
|
|
249
265
|
\\titlespacing*{\\section}{0pt}{${sectionSpaceBefore}}{${sectionSpaceAfter}}
|
|
250
266
|
\\titlespacing*{\\subsection}{0pt}{${subsectionSpaceBefore}}{${subsectionSpaceAfter}}
|
|
267
|
+
\\titlespacing*{\\paragraph}{0pt}{0.9ex plus 0.3ex minus 0.1ex}{0.8em}
|
|
268
|
+
\\titlespacing*{\\subparagraph}{0pt}{0.7ex plus 0.2ex minus 0.1ex}{0.7em}
|
|
251
269
|
\\usepackage{xcolor}
|
|
252
270
|
\\usepackage{varwidth}
|
|
253
271
|
\\definecolor{StudioAnnotationBg}{HTML}{EAF3FF}
|
|
254
272
|
\\definecolor{StudioAnnotationBorder}{HTML}{8CB8FF}
|
|
255
273
|
\\definecolor{StudioAnnotationText}{HTML}{1F5FBF}
|
|
274
|
+
\\definecolor{StudioCodeBlockBg}{HTML}{F6F8FA}
|
|
256
275
|
\\definecolor{StudioDiffAddText}{HTML}{1A7F37}
|
|
257
276
|
\\definecolor{StudioDiffDelText}{HTML}{CF222E}
|
|
258
277
|
\\definecolor{StudioDiffMetaText}{HTML}{57606A}
|
|
@@ -278,7 +297,8 @@ function buildStudioPdfPreamble(options?: StudioPdfRenderOptions): string {
|
|
|
278
297
|
\\newcommand{\\StudioDiffMetaTok}[1]{\\textcolor{StudioDiffMetaText}{#1}}
|
|
279
298
|
\\newcommand{\\StudioDiffHunkTok}[1]{\\textcolor{StudioDiffHunkText}{#1}}
|
|
280
299
|
\\newcommand{\\StudioDiffHeaderTok}[1]{\\textcolor{StudioDiffHunkText}{\\textbf{#1}}}
|
|
281
|
-
\\newenvironment{studiocallout}[4]{\\par\\vspace{0.6em}\\noindent\\begingroup\\def\\StudioCalloutBorder{#2}\\def\\StudioCalloutText{#3}\\def\\StudioCalloutLabelBg{#4}\\color{\\StudioCalloutBorder}\\hrule height 0.8pt\\relax\\vspace{0.32em}\\noindent\\colorbox{\\StudioCalloutLabelBg}{\\strut\\hspace{0.55em}{\\sffamily\\bfseries\\
|
|
300
|
+
\\newenvironment{studiocallout}[4]{\\par\\vspace{0.6em}\\noindent\\begingroup\\def\\StudioCalloutBorder{#2}\\def\\StudioCalloutText{#3}\\def\\StudioCalloutLabelBg{#4}\\color{\\StudioCalloutBorder}\\hrule height 0.8pt\\relax\\vspace{0.32em}\\noindent\\colorbox{\\StudioCalloutLabelBg}{\\strut\\hspace{0.55em}{${calloutTitleSize}\\sffamily\\bfseries\\textcolor{\\StudioCalloutText}{#1}}\\hspace{0.55em}}\\par\\vspace{0.24em}\\normalcolor\\leftskip=0.9em\\rightskip=0pt\\parindent=0pt\\parskip=0.18em}{\\par\\vspace{0.12em}\\noindent\\color{\\StudioCalloutBorder}\\hrule height 0.55pt\\par\\endgroup\\vspace{0.5em}}
|
|
301
|
+
\\usepackage{float}
|
|
282
302
|
\\usepackage{caption}
|
|
283
303
|
\\captionsetup[figure]{justification=raggedright,singlelinecheck=false}
|
|
284
304
|
\\usepackage{enumitem}
|
|
@@ -288,9 +308,9 @@ function buildStudioPdfPreamble(options?: StudioPdfRenderOptions): string {
|
|
|
288
308
|
\\usepackage{fvextra}
|
|
289
309
|
\\makeatletter
|
|
290
310
|
\\@ifundefined{Highlighting}{%
|
|
291
|
-
\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},breaklines,breakanywhere}%
|
|
311
|
+
\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},breaklines,breakanywhere,bgcolor=StudioCodeBlockBg,framesep=2mm}%
|
|
292
312
|
}{%
|
|
293
|
-
\\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},breaklines,breakanywhere}%
|
|
313
|
+
\\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\},breaklines,breakanywhere,bgcolor=StudioCodeBlockBg,framesep=2mm}%
|
|
294
314
|
}
|
|
295
315
|
\\makeatother
|
|
296
316
|
`;
|
|
@@ -4096,11 +4116,12 @@ async function renderStudioLiteralTextPdf(text: string, title = "Studio export",
|
|
|
4096
4116
|
\\usepackage[${literalPdfConfig.geometryOptions}]{geometry}
|
|
4097
4117
|
${literalPdfConfig.fontCommands}\\usepackage{fvextra}
|
|
4098
4118
|
\\usepackage{xcolor}
|
|
4119
|
+
\\definecolor{StudioCodeBlockBg}{HTML}{F6F8FA}
|
|
4099
4120
|
\\usepackage{upquote}
|
|
4100
4121
|
\\begin{document}
|
|
4101
4122
|
\\renewcommand{\\baselinestretch}{${literalPdfConfig.lineStretch}}\\selectfont
|
|
4102
4123
|
${literalPdfConfig.fontSizeCommand}\\section*{${title.replace(/[{}\\]/g, "").trim() || "Studio export"}}
|
|
4103
|
-
\\VerbatimInput[breaklines,breakanywhere,fontsize=\\small,frame=single,rulecolor=\\color{black!15},framesep=2mm]{input.txt}
|
|
4124
|
+
\\VerbatimInput[breaklines,breakanywhere,fontsize=\\small,bgcolor=StudioCodeBlockBg,frame=single,rulecolor=\\color{black!15},framesep=2mm]{input.txt}
|
|
4104
4125
|
\\end{document}
|
|
4105
4126
|
`;
|
|
4106
4127
|
|
|
@@ -5400,6 +5421,13 @@ function parseIncomingMessage(data: RawData): IncomingStudioMessage | null {
|
|
|
5400
5421
|
};
|
|
5401
5422
|
}
|
|
5402
5423
|
|
|
5424
|
+
if (msg.type === "refresh_from_disk_request" && typeof msg.requestId === "string") {
|
|
5425
|
+
return {
|
|
5426
|
+
type: "refresh_from_disk_request",
|
|
5427
|
+
requestId: msg.requestId,
|
|
5428
|
+
};
|
|
5429
|
+
}
|
|
5430
|
+
|
|
5403
5431
|
if (msg.type === "send_to_editor_request" && typeof msg.requestId === "string" && typeof msg.content === "string") {
|
|
5404
5432
|
return {
|
|
5405
5433
|
type: "send_to_editor_request",
|
|
@@ -5797,6 +5825,7 @@ ${cssVarsBlock}
|
|
|
5797
5825
|
<div class="controls">
|
|
5798
5826
|
<button id="saveAsBtn" type="button" title="Save editor content to a new file path. Cmd/Ctrl+S falls back here when no direct save path is available.">Save editor as…</button>
|
|
5799
5827
|
<button id="saveOverBtn" type="button" title="Overwrite current file with editor content. Shortcut: Cmd/Ctrl+S.">Save editor</button>
|
|
5828
|
+
<button id="refreshFromDiskBtn" type="button" title="Reload the current file-backed document from disk.">Refresh from disk</button>
|
|
5800
5829
|
<label class="file-label" title="Load a local file into editor text.">Load file content<input id="fileInput" type="file" accept=".md,.markdown,.mdx,.qmd,.js,.mjs,.cjs,.jsx,.ts,.mts,.cts,.tsx,.py,.pyw,.sh,.bash,.zsh,.json,.jsonc,.json5,.rs,.c,.h,.cpp,.cxx,.cc,.hpp,.hxx,.jl,.f90,.f95,.f03,.f,.for,.r,.R,.m,.tex,.latex,.diff,.patch,.java,.go,.rb,.swift,.html,.htm,.css,.xml,.yaml,.yml,.toml,.lua,.txt,.rst,.adoc" /></label>
|
|
5801
5830
|
<button id="loadGitDiffBtn" type="button" title="Load the current git diff from the Studio context into the editor.">Load git diff</button>
|
|
5802
5831
|
<button id="getEditorBtn" type="button" title="Load the current terminal editor draft into Studio.">Load from pi editor</button>
|
|
@@ -7182,6 +7211,50 @@ export default function (pi: ExtensionAPI) {
|
|
|
7182
7211
|
return;
|
|
7183
7212
|
}
|
|
7184
7213
|
|
|
7214
|
+
if (msg.type === "refresh_from_disk_request") {
|
|
7215
|
+
if (!isValidRequestId(msg.requestId)) {
|
|
7216
|
+
sendToClient(client, { type: "error", requestId: msg.requestId, message: "Invalid request ID." });
|
|
7217
|
+
return;
|
|
7218
|
+
}
|
|
7219
|
+
if (isStudioBusy()) {
|
|
7220
|
+
sendToClient(client, { type: "busy", requestId: msg.requestId, message: "Studio is busy." });
|
|
7221
|
+
return;
|
|
7222
|
+
}
|
|
7223
|
+
if (!initialStudioDocument || !initialStudioDocument.path) {
|
|
7224
|
+
sendToClient(client, {
|
|
7225
|
+
type: "error",
|
|
7226
|
+
requestId: msg.requestId,
|
|
7227
|
+
message: "Refresh from disk is only available for file-backed documents.",
|
|
7228
|
+
});
|
|
7229
|
+
return;
|
|
7230
|
+
}
|
|
7231
|
+
|
|
7232
|
+
const refreshed = readStudioFile(initialStudioDocument.path, studioCwd);
|
|
7233
|
+
if (refreshed.ok === false) {
|
|
7234
|
+
sendToClient(client, {
|
|
7235
|
+
type: "error",
|
|
7236
|
+
requestId: msg.requestId,
|
|
7237
|
+
message: refreshed.message,
|
|
7238
|
+
});
|
|
7239
|
+
return;
|
|
7240
|
+
}
|
|
7241
|
+
|
|
7242
|
+
initialStudioDocument = {
|
|
7243
|
+
text: refreshed.text,
|
|
7244
|
+
label: refreshed.label,
|
|
7245
|
+
source: "file",
|
|
7246
|
+
path: refreshed.resolvedPath,
|
|
7247
|
+
};
|
|
7248
|
+
|
|
7249
|
+
broadcast({
|
|
7250
|
+
type: "studio_document",
|
|
7251
|
+
requestId: msg.requestId,
|
|
7252
|
+
document: initialStudioDocument,
|
|
7253
|
+
message: `Reloaded ${refreshed.label} from disk.`,
|
|
7254
|
+
});
|
|
7255
|
+
return;
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7185
7258
|
if (msg.type === "send_to_editor_request") {
|
|
7186
7259
|
if (!isValidRequestId(msg.requestId)) {
|
|
7187
7260
|
sendToClient(client, { type: "error", requestId: msg.requestId, message: "Invalid request ID." });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.43",
|
|
4
4
|
"description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code preview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|