pi-studio 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/README.md +3 -3
- package/client/studio-client.js +67 -23
- package/client/studio.css +3 -3
- package/index.ts +60 -8
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,25 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.3] — 2026-05-13
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Updated package, README, and UI wording to describe the feature as interactive HTML preview.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Open new editor** now opens a blank companion editor when the current editor is empty.
|
|
14
|
+
- PDF export now preserves Pandoc YAML front matter while applying Studio Markdown transforms and lets Markdown documents with `header-includes` control their own LaTeX preamble, fixing exports that use YAML-defined commands such as `\firstpageletterhead`.
|
|
15
|
+
- Reduced scroll snap-back after using browser Find by preserving Studio pane scroll positions during pane activation.
|
|
16
|
+
|
|
17
|
+
## [0.8.2] — 2026-05-13
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Improved dark-theme contrast for Studio dropdown arrows and the interactive HTML preview zoom percentage by using the stronger Studio info text colour token.
|
|
21
|
+
|
|
7
22
|
## [0.8.1] — 2026-05-13
|
|
8
23
|
|
|
9
24
|
### Added
|
|
10
|
-
- Added first-cut HTML
|
|
25
|
+
- Added first-cut interactive HTML preview for straight, unfenced HTML in Studio preview panes, rendered in a sandboxed iframe with inline scripts enabled, network requests blocked by CSP, fit/capped sizing, and toolbar zoom controls; HTML export preserves authored HTML previews instead of converting them through Markdown.
|
|
11
26
|
|
|
12
27
|
## [0.8.0] — 2026-05-12
|
|
13
28
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-studio
|
|
2
2
|
|
|
3
|
-
Extension for [pi](https://pi.dev) that opens a local two-pane browser workspace for working with prompts, responses, live working details, Markdown and LaTeX documents, code files, and other common text-based files side by side. Annotate responses and files, add local comments, write, edit, and run prompts, browse prompt and response history, request critiques, and use live preview for code, Markdown, and
|
|
3
|
+
Extension for [pi](https://pi.dev) that opens a local two-pane browser workspace for working with prompts, responses, live working details, Markdown and LaTeX documents, interactive HTML previews, code files, and other common text-based files side by side. Annotate responses and files, add local comments, write, edit, and run prompts, browse prompt and response history, request critiques, and use live preview for code, Markdown, LaTeX, and interactive HTML.
|
|
4
4
|
|
|
5
5
|
## Quick demo
|
|
6
6
|
|
|
@@ -35,10 +35,10 @@ Extension for [pi](https://pi.dev) that opens a local two-pane browser workspace
|
|
|
35
35
|
- strips markers before send (optional)
|
|
36
36
|
- saves `.annotated.md`
|
|
37
37
|
- Renders Markdown/LaTeX/code previews (math + Mermaid), theme-synced with pi
|
|
38
|
-
- Renders straight, unfenced HTML
|
|
38
|
+
- Renders straight, unfenced interactive HTML in preview via a sandboxed browser iframe with zoom controls, while fenced `html` blocks remain source code
|
|
39
39
|
- Embeds local PDFs in Studio Markdown previews via explicit `studio-pdf` fenced blocks
|
|
40
40
|
- Ships optional `pi-studio-dark` and `pi-studio-light` themes tuned for Studio's browser workspace
|
|
41
|
-
- Exports right-pane preview as PDF (pandoc + LaTeX) or standalone HTML, preserving authored HTML
|
|
41
|
+
- Exports right-pane preview as PDF (pandoc + LaTeX) or standalone HTML, preserving authored HTML previews as HTML
|
|
42
42
|
- Exports local files headlessly via `/studio-pdf <path>` to `<name>.studio.pdf` or `/studio-html <path>` to `<name>.studio.html`; without a path, those commands export the last model response to a timestamped file
|
|
43
43
|
- Shows model/session/context usage in the footer, plus a compact-context action
|
|
44
44
|
|
package/client/studio-client.js
CHANGED
|
@@ -1954,6 +1954,51 @@
|
|
|
1954
1954
|
updatePaneFocusButtons();
|
|
1955
1955
|
}
|
|
1956
1956
|
|
|
1957
|
+
function snapshotStudioScrollablePositions() {
|
|
1958
|
+
return [sourceTextEl, sourcePreviewEl, critiqueViewEl]
|
|
1959
|
+
.filter((el) => el && typeof el.scrollTop === "number" && typeof el.scrollLeft === "number")
|
|
1960
|
+
.map((el) => ({ el, top: el.scrollTop, left: el.scrollLeft }));
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
function restoreStudioScrollablePositions(snapshot) {
|
|
1964
|
+
if (!Array.isArray(snapshot)) return;
|
|
1965
|
+
snapshot.forEach((entry) => {
|
|
1966
|
+
const el = entry && entry.el;
|
|
1967
|
+
if (!el || !el.isConnected) return;
|
|
1968
|
+
if (typeof entry.top === "number") el.scrollTop = entry.top;
|
|
1969
|
+
if (typeof entry.left === "number") el.scrollLeft = entry.left;
|
|
1970
|
+
});
|
|
1971
|
+
syncEditorHighlightScroll();
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
function scheduleStudioScrollablePositionRestore(snapshot) {
|
|
1975
|
+
if (!Array.isArray(snapshot) || snapshot.length === 0) return;
|
|
1976
|
+
const schedule = typeof window.requestAnimationFrame === "function"
|
|
1977
|
+
? window.requestAnimationFrame.bind(window)
|
|
1978
|
+
: (cb) => window.setTimeout(cb, 16);
|
|
1979
|
+
window.setTimeout(() => restoreStudioScrollablePositions(snapshot), 0);
|
|
1980
|
+
schedule(() => {
|
|
1981
|
+
restoreStudioScrollablePositions(snapshot);
|
|
1982
|
+
schedule(() => restoreStudioScrollablePositions(snapshot));
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
function shouldPreserveScrollForPaneActivationEvent(event) {
|
|
1987
|
+
const target = event && event.target;
|
|
1988
|
+
if (!(target instanceof Element)) return true;
|
|
1989
|
+
if (target.closest("button, select, input, a, [role='button'], .studio-copy-block-btn, .preview-comment-add, .preview-comment-jump")) {
|
|
1990
|
+
return false;
|
|
1991
|
+
}
|
|
1992
|
+
return true;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
function activatePaneFromInteraction(nextPane, event) {
|
|
1996
|
+
const shouldPreserveScroll = shouldPreserveScrollForPaneActivationEvent(event);
|
|
1997
|
+
const snapshot = shouldPreserveScroll ? snapshotStudioScrollablePositions() : [];
|
|
1998
|
+
setActivePane(nextPane);
|
|
1999
|
+
if (shouldPreserveScroll) scheduleStudioScrollablePositionRestore(snapshot);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
1957
2002
|
function setActivePane(nextPane) {
|
|
1958
2003
|
activePane = nextPane === "right" ? "right" : "left";
|
|
1959
2004
|
|
|
@@ -2711,7 +2756,7 @@
|
|
|
2711
2756
|
record.shell.classList.toggle("is-height-capped", capped);
|
|
2712
2757
|
}
|
|
2713
2758
|
if (record.detail) {
|
|
2714
|
-
record.detail.textContent = "HTML
|
|
2759
|
+
record.detail.textContent = "HTML preview";
|
|
2715
2760
|
}
|
|
2716
2761
|
}
|
|
2717
2762
|
|
|
@@ -2719,7 +2764,7 @@
|
|
|
2719
2764
|
|
|
2720
2765
|
function renderHtmlArtifactPreview(targetEl, html, pane, options) {
|
|
2721
2766
|
if (!targetEl) return;
|
|
2722
|
-
const title = options && options.title ? String(options.title) : "HTML
|
|
2767
|
+
const title = options && options.title ? String(options.title) : "HTML preview";
|
|
2723
2768
|
const previewId = "html_artifact_" + Date.now().toString(36) + "_" + Math.random().toString(36).slice(2, 10);
|
|
2724
2769
|
pruneDisconnectedHtmlArtifactFrames();
|
|
2725
2770
|
clearPreviewJumpHighlight(targetEl);
|
|
@@ -2736,7 +2781,7 @@
|
|
|
2736
2781
|
label.textContent = title;
|
|
2737
2782
|
const detail = document.createElement("span");
|
|
2738
2783
|
detail.className = "studio-html-artifact-detail";
|
|
2739
|
-
detail.textContent = "HTML
|
|
2784
|
+
detail.textContent = "HTML preview";
|
|
2740
2785
|
|
|
2741
2786
|
const tools = document.createElement("span");
|
|
2742
2787
|
tools.className = "studio-html-artifact-tools";
|
|
@@ -2783,10 +2828,10 @@
|
|
|
2783
2828
|
});
|
|
2784
2829
|
return button;
|
|
2785
2830
|
};
|
|
2786
|
-
const zoomOutBtn = makeZoomButton("−", "Zoom out HTML
|
|
2787
|
-
const zoomResetBtn = makeZoomButton("100%", "Reset HTML
|
|
2831
|
+
const zoomOutBtn = makeZoomButton("−", "Zoom out HTML preview", () => setArtifactZoom(artifactZoom - HTML_ARTIFACT_ZOOM_STEP));
|
|
2832
|
+
const zoomResetBtn = makeZoomButton("100%", "Reset HTML preview zoom", () => setArtifactZoom(1));
|
|
2788
2833
|
zoomResetBtn.classList.add("studio-html-artifact-zoom-reset");
|
|
2789
|
-
const zoomInBtn = makeZoomButton("+", "Zoom in HTML
|
|
2834
|
+
const zoomInBtn = makeZoomButton("+", "Zoom in HTML preview", () => setArtifactZoom(artifactZoom + HTML_ARTIFACT_ZOOM_STEP));
|
|
2790
2835
|
zoomControls.appendChild(zoomOutBtn);
|
|
2791
2836
|
zoomControls.appendChild(zoomResetBtn);
|
|
2792
2837
|
zoomControls.appendChild(zoomInBtn);
|
|
@@ -3882,7 +3927,7 @@
|
|
|
3882
3927
|
|
|
3883
3928
|
const htmlArtifactSource = getRightPaneHtmlArtifactSource();
|
|
3884
3929
|
if (htmlArtifactSource) {
|
|
3885
|
-
setStatus("PDF export does not support HTML
|
|
3930
|
+
setStatus("PDF export does not support interactive HTML previews yet. Export as HTML or use the browser print dialog inside the preview.", "warning");
|
|
3886
3931
|
return;
|
|
3887
3932
|
}
|
|
3888
3933
|
|
|
@@ -4400,7 +4445,7 @@
|
|
|
4400
4445
|
if (editorView !== "preview") return;
|
|
4401
4446
|
const text = prepareEditorTextForPreview(sourceTextEl.value || "");
|
|
4402
4447
|
if (isHtmlArtifactPreviewText(text, editorLanguage)) {
|
|
4403
|
-
renderHtmlArtifactPreview(sourcePreviewEl, text, "source", { title: "Editor HTML
|
|
4448
|
+
renderHtmlArtifactPreview(sourcePreviewEl, text, "source", { title: "Editor HTML preview" });
|
|
4404
4449
|
return;
|
|
4405
4450
|
}
|
|
4406
4451
|
if (supportsCodePreviewCommentsForCurrentEditor()) {
|
|
@@ -4672,7 +4717,7 @@
|
|
|
4672
4717
|
return;
|
|
4673
4718
|
}
|
|
4674
4719
|
if (isHtmlArtifactPreviewText(editorText, editorLanguage)) {
|
|
4675
|
-
renderHtmlArtifactPreview(critiqueViewEl, editorText, "response", { title: "Editor HTML
|
|
4720
|
+
renderHtmlArtifactPreview(critiqueViewEl, editorText, "response", { title: "Editor HTML preview" });
|
|
4676
4721
|
return;
|
|
4677
4722
|
}
|
|
4678
4723
|
if (supportsCodePreviewCommentsForCurrentEditor()) {
|
|
@@ -4696,7 +4741,7 @@
|
|
|
4696
4741
|
|
|
4697
4742
|
if (rightView === "preview") {
|
|
4698
4743
|
if (isHtmlArtifactPreviewText(markdown, "")) {
|
|
4699
|
-
renderHtmlArtifactPreview(critiqueViewEl, markdown, "response", { title: "Response HTML
|
|
4744
|
+
renderHtmlArtifactPreview(critiqueViewEl, markdown, "response", { title: "Response HTML preview" });
|
|
4700
4745
|
return;
|
|
4701
4746
|
}
|
|
4702
4747
|
const nonce = ++responsePreviewRenderNonce;
|
|
@@ -4777,7 +4822,7 @@
|
|
|
4777
4822
|
} else if (!canExportPreview) {
|
|
4778
4823
|
exportPdfBtn.title = "Nothing to export yet.";
|
|
4779
4824
|
} else if (isHtmlArtifactPreview) {
|
|
4780
|
-
exportPdfBtn.title = "This is an HTML
|
|
4825
|
+
exportPdfBtn.title = "This is an interactive HTML preview. Export as HTML; PDF export is not available yet.";
|
|
4781
4826
|
} else {
|
|
4782
4827
|
exportPdfBtn.title = "Choose PDF or HTML and export the current right-pane preview.";
|
|
4783
4828
|
}
|
|
@@ -4785,18 +4830,18 @@
|
|
|
4785
4830
|
if (exportPreviewPdfBtn) {
|
|
4786
4831
|
exportPreviewPdfBtn.disabled = uiBusy || previewExportInProgress || !canExportPreview || isHtmlArtifactPreview;
|
|
4787
4832
|
exportPreviewPdfBtn.title = isHtmlArtifactPreview
|
|
4788
|
-
? "HTML
|
|
4833
|
+
? "Interactive HTML preview PDF export is not available yet."
|
|
4789
4834
|
: "Export the current right-pane preview as PDF.";
|
|
4790
4835
|
}
|
|
4791
4836
|
if (exportPreviewHtmlBtn) {
|
|
4792
4837
|
exportPreviewHtmlBtn.disabled = uiBusy || previewExportInProgress || !canExportPreview;
|
|
4793
4838
|
exportPreviewHtmlBtn.title = isHtmlArtifactPreview
|
|
4794
|
-
? "Export the authored HTML
|
|
4839
|
+
? "Export the authored HTML preview."
|
|
4795
4840
|
: "Export the current right-pane preview as standalone HTML.";
|
|
4796
4841
|
}
|
|
4797
4842
|
if (exportPreviewControlsEl) {
|
|
4798
4843
|
exportPreviewControlsEl.title = canExportPreview
|
|
4799
|
-
? (isHtmlArtifactPreview ? "Export this HTML
|
|
4844
|
+
? (isHtmlArtifactPreview ? "Export this HTML preview." : "Choose a format and export the current right-pane preview.")
|
|
4800
4845
|
: "Switch right pane to a non-empty preview before exporting.";
|
|
4801
4846
|
}
|
|
4802
4847
|
if (!canExportPreview || previewExportInProgress) {
|
|
@@ -11114,9 +11159,12 @@
|
|
|
11114
11159
|
setWsState("Ready");
|
|
11115
11160
|
const targetUrl = resolveCompanionEditorTargetUrl(message);
|
|
11116
11161
|
const opened = navigatePendingCompanionWindow(responseRequestId, targetUrl);
|
|
11162
|
+
const readyMessage = typeof message.message === "string" && message.message.trim()
|
|
11163
|
+
? message.message.trim()
|
|
11164
|
+
: "Opened companion editor with a detached copy of the current editor text.";
|
|
11117
11165
|
setStatus(
|
|
11118
11166
|
opened
|
|
11119
|
-
?
|
|
11167
|
+
? readyMessage
|
|
11120
11168
|
: (targetUrl ? "Companion editor ready: " + targetUrl : "Companion editor is ready, but Studio did not receive a URL."),
|
|
11121
11169
|
opened ? "success" : "warning",
|
|
11122
11170
|
);
|
|
@@ -11578,13 +11626,13 @@
|
|
|
11578
11626
|
}
|
|
11579
11627
|
|
|
11580
11628
|
if (leftPaneEl) {
|
|
11581
|
-
leftPaneEl.addEventListener("mousedown", () =>
|
|
11582
|
-
leftPaneEl.addEventListener("focusin", () =>
|
|
11629
|
+
leftPaneEl.addEventListener("mousedown", (event) => activatePaneFromInteraction("left", event));
|
|
11630
|
+
leftPaneEl.addEventListener("focusin", (event) => activatePaneFromInteraction("left", event));
|
|
11583
11631
|
}
|
|
11584
11632
|
|
|
11585
11633
|
if (rightPaneEl) {
|
|
11586
|
-
rightPaneEl.addEventListener("mousedown", () =>
|
|
11587
|
-
rightPaneEl.addEventListener("focusin", () =>
|
|
11634
|
+
rightPaneEl.addEventListener("mousedown", (event) => activatePaneFromInteraction("right", event));
|
|
11635
|
+
rightPaneEl.addEventListener("focusin", (event) => activatePaneFromInteraction("right", event));
|
|
11588
11636
|
}
|
|
11589
11637
|
|
|
11590
11638
|
if (leftFocusBtn) {
|
|
@@ -12074,10 +12122,6 @@
|
|
|
12074
12122
|
if (openCompanionBtn) {
|
|
12075
12123
|
openCompanionBtn.addEventListener("click", () => {
|
|
12076
12124
|
const content = sourceTextEl.value;
|
|
12077
|
-
if (!content.trim()) {
|
|
12078
|
-
setStatus("Editor is empty. Nothing to copy into a companion view.", "warning");
|
|
12079
|
-
return;
|
|
12080
|
-
}
|
|
12081
12125
|
|
|
12082
12126
|
const requestId = beginUiAction("open_editor_only");
|
|
12083
12127
|
if (!requestId) return;
|
package/client/studio.css
CHANGED
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
.export-preview-trigger::after {
|
|
79
79
|
content: "⌄";
|
|
80
|
-
color: var(--
|
|
80
|
+
color: var(--studio-info-text, var(--text));
|
|
81
81
|
font-size: 14px;
|
|
82
82
|
line-height: 1;
|
|
83
83
|
transform: translateY(-1px);
|
|
@@ -1598,7 +1598,7 @@
|
|
|
1598
1598
|
|
|
1599
1599
|
.rendered-markdown .studio-html-artifact-zoom-reset {
|
|
1600
1600
|
min-width: 42px;
|
|
1601
|
-
color: var(--
|
|
1601
|
+
color: var(--studio-info-text, var(--text));
|
|
1602
1602
|
}
|
|
1603
1603
|
|
|
1604
1604
|
.rendered-markdown .studio-html-artifact-frame {
|
|
@@ -3121,7 +3121,7 @@
|
|
|
3121
3121
|
|
|
3122
3122
|
body.studio-ui-refresh .studio-refresh-chip::after {
|
|
3123
3123
|
content: "⌄";
|
|
3124
|
-
color: var(--
|
|
3124
|
+
color: var(--studio-info-text, var(--text));
|
|
3125
3125
|
font-size: 14px;
|
|
3126
3126
|
line-height: 1;
|
|
3127
3127
|
transform: translateY(-1px);
|
package/index.ts
CHANGED
|
@@ -3457,6 +3457,49 @@ function normalizeStudioMarkdownFencedBlocks(markdown: string): string {
|
|
|
3457
3457
|
return out.join("\n");
|
|
3458
3458
|
}
|
|
3459
3459
|
|
|
3460
|
+
interface StudioYamlFrontMatterSplit {
|
|
3461
|
+
frontMatter: string;
|
|
3462
|
+
body: string;
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3465
|
+
function splitStudioYamlFrontMatter(markdown: string): StudioYamlFrontMatterSplit | null {
|
|
3466
|
+
const source = String(markdown ?? "");
|
|
3467
|
+
const match = source.match(/^(\uFEFF?---[ \t]*(?:\r?\n)[\s\S]*?(?:\r?\n)---[ \t]*(?:\r?\n|$))([\s\S]*)$/);
|
|
3468
|
+
if (!match) return null;
|
|
3469
|
+
return {
|
|
3470
|
+
frontMatter: match[1] ?? "",
|
|
3471
|
+
body: match[2] ?? "",
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
|
|
3475
|
+
function mapStudioMarkdownBodyPreservingYamlFrontMatter(markdown: string, transformBody: (body: string) => string): string {
|
|
3476
|
+
const source = String(markdown ?? "");
|
|
3477
|
+
const split = splitStudioYamlFrontMatter(source);
|
|
3478
|
+
if (!split) return transformBody(source);
|
|
3479
|
+
return `${split.frontMatter}${transformBody(split.body)}`;
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
function stripStudioMarkdownHtmlCommentsPreservingYamlFrontMatter(markdown: string): string {
|
|
3483
|
+
return mapStudioMarkdownBodyPreservingYamlFrontMatter(markdown, (body) => stripStudioMarkdownHtmlComments(body));
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
function hasStudioYamlHeaderIncludes(markdown: string): boolean {
|
|
3487
|
+
const split = splitStudioYamlFrontMatter(markdown);
|
|
3488
|
+
if (!split) return false;
|
|
3489
|
+
return /^\s*header-includes\s*:/im.test(split.frontMatter);
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
function prepareStudioMarkdownForPandoc(markdown: string, options?: { preserveLiteralLatexCommands?: boolean }): string {
|
|
3493
|
+
const shouldPreserveLiteralLatexCommands = options?.preserveLiteralLatexCommands !== false;
|
|
3494
|
+
return mapStudioMarkdownBodyPreservingYamlFrontMatter(markdown, (body) => {
|
|
3495
|
+
const normalizedMath = normalizeMathDelimiters(body);
|
|
3496
|
+
const latexReady = shouldPreserveLiteralLatexCommands
|
|
3497
|
+
? preserveLiteralLatexCommandsInMarkdown(normalizedMath)
|
|
3498
|
+
: normalizedMath;
|
|
3499
|
+
return normalizeObsidianImages(latexReady);
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3460
3503
|
function hasStudioMarkdownDiffFence(markdown: string): boolean {
|
|
3461
3504
|
const lines = String(markdown ?? "").replace(/\r\n/g, "\n").split("\n");
|
|
3462
3505
|
|
|
@@ -4300,8 +4343,10 @@ function prepareStudioPdfMarkdown(markdown: string, isLatex?: boolean, editorLan
|
|
|
4300
4343
|
const annotationReadySource = !effectiveEditorLanguage || effectiveEditorLanguage === "markdown" || effectiveEditorLanguage === "latex"
|
|
4301
4344
|
? replaceStudioAnnotationMarkersForPdf(source)
|
|
4302
4345
|
: source;
|
|
4303
|
-
const commentStrippedSource =
|
|
4304
|
-
return
|
|
4346
|
+
const commentStrippedSource = stripStudioMarkdownHtmlCommentsPreservingYamlFrontMatter(annotationReadySource);
|
|
4347
|
+
return prepareStudioMarkdownForPandoc(commentStrippedSource, {
|
|
4348
|
+
preserveLiteralLatexCommands: !hasStudioYamlHeaderIncludes(annotationReadySource),
|
|
4349
|
+
});
|
|
4305
4350
|
}
|
|
4306
4351
|
|
|
4307
4352
|
function stripMathMlAnnotationTags(html: string): string {
|
|
@@ -4559,7 +4604,7 @@ function decorateStudioPandocSyntaxHtml(html: string): string {
|
|
|
4559
4604
|
|
|
4560
4605
|
async function renderStudioMarkdownWithPandoc(markdown: string, isLatex?: boolean, resourcePath?: string, sourcePath?: string): Promise<string> {
|
|
4561
4606
|
const pandocCommand = process.env.PANDOC_PATH?.trim() || "pandoc";
|
|
4562
|
-
const markdownWithoutHtmlComments = isLatex ? markdown :
|
|
4607
|
+
const markdownWithoutHtmlComments = isLatex ? markdown : stripStudioMarkdownHtmlCommentsPreservingYamlFrontMatter(markdown);
|
|
4563
4608
|
const markdownWithPreviewPageBreaks = isLatex ? markdownWithoutHtmlComments : replaceStudioPreviewPageBreakCommands(markdownWithoutHtmlComments);
|
|
4564
4609
|
const latexSubfigurePreviewTransform = isLatex
|
|
4565
4610
|
? preprocessStudioLatexSubfiguresForPreview(markdownWithPreviewPageBreaks)
|
|
@@ -4580,7 +4625,7 @@ async function renderStudioMarkdownWithPandoc(markdown: string, isLatex?: boolea
|
|
|
4580
4625
|
}
|
|
4581
4626
|
const normalizedMarkdown = isLatex
|
|
4582
4627
|
? sourceWithResolvedRefs
|
|
4583
|
-
: normalizeStudioMarkdownFencedBlocks(
|
|
4628
|
+
: normalizeStudioMarkdownFencedBlocks(prepareStudioMarkdownForPandoc(sourceWithResolvedRefs));
|
|
4584
4629
|
const pandocWorkingDir = resolveStudioPandocWorkingDir(resourcePath);
|
|
4585
4630
|
|
|
4586
4631
|
let renderedHtml = await new Promise<string>((resolve, reject) => {
|
|
@@ -5501,6 +5546,8 @@ async function renderStudioPdfWithPandoc(
|
|
|
5501
5546
|
await mkdir(tempDir, { recursive: true });
|
|
5502
5547
|
await writeFile(preamblePath, buildStudioPdfPreamble(pdfOptions), "utf-8");
|
|
5503
5548
|
|
|
5549
|
+
const hasYamlHeaderIncludesForPdf = inputFormat !== "latex" && hasStudioYamlHeaderIncludes(markdownForPdf);
|
|
5550
|
+
const headerIncludeArgs = hasYamlHeaderIncludesForPdf ? [] : ["--include-in-header", preamblePath];
|
|
5504
5551
|
const args = [
|
|
5505
5552
|
"-f", inputFormat,
|
|
5506
5553
|
"-o", outputPath,
|
|
@@ -5508,7 +5555,7 @@ async function renderStudioPdfWithPandoc(
|
|
|
5508
5555
|
...buildStudioPdfPandocVariableArgs(pdfOptions, inputFormat !== "latex"),
|
|
5509
5556
|
"-V", "urlcolor=blue",
|
|
5510
5557
|
"-V", "linkcolor=blue",
|
|
5511
|
-
|
|
5558
|
+
...headerIncludeArgs,
|
|
5512
5559
|
...bibliographyArgs,
|
|
5513
5560
|
];
|
|
5514
5561
|
if (resourcePath) args.push(`--resource-path=${resourcePath}`);
|
|
@@ -5652,6 +5699,8 @@ async function renderStudioPdfWithPandoc(
|
|
|
5652
5699
|
return { pdf: rendered.pdf, warning: mermaidPrepared.warning ?? rendered.warning };
|
|
5653
5700
|
}
|
|
5654
5701
|
|
|
5702
|
+
const hasYamlHeaderIncludesForPdf = !isLatex && hasStudioYamlHeaderIncludes(markdownForPdf);
|
|
5703
|
+
const headerIncludeArgs = hasYamlHeaderIncludesForPdf ? [] : ["--include-in-header", preamblePath];
|
|
5655
5704
|
const args = [
|
|
5656
5705
|
"-f", inputFormat,
|
|
5657
5706
|
"-o", outputPath,
|
|
@@ -5659,7 +5708,7 @@ async function renderStudioPdfWithPandoc(
|
|
|
5659
5708
|
...buildStudioPdfPandocVariableArgs(pdfOptions, !isLatex),
|
|
5660
5709
|
"-V", "urlcolor=blue",
|
|
5661
5710
|
"-V", "linkcolor=blue",
|
|
5662
|
-
|
|
5711
|
+
...headerIncludeArgs,
|
|
5663
5712
|
...bibliographyArgs,
|
|
5664
5713
|
];
|
|
5665
5714
|
if (resourcePath) args.push(`--resource-path=${resourcePath}`);
|
|
@@ -8599,9 +8648,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
8599
8648
|
}
|
|
8600
8649
|
|
|
8601
8650
|
const resourceDir = resolveStudioCompanionResourceDir(msg.path, msg.resourceDir, studioCwd);
|
|
8651
|
+
const hasContent = msg.content.trim().length > 0;
|
|
8602
8652
|
const document: InitialStudioDocument = {
|
|
8603
8653
|
text: msg.content,
|
|
8604
|
-
label: buildStudioCompanionLabel(msg.label),
|
|
8654
|
+
label: hasContent ? buildStudioCompanionLabel(msg.label) : "blank companion editor",
|
|
8605
8655
|
source: "blank",
|
|
8606
8656
|
draftId: createStudioDraftId(),
|
|
8607
8657
|
resourceDir,
|
|
@@ -8614,7 +8664,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
8614
8664
|
requestId: msg.requestId,
|
|
8615
8665
|
url,
|
|
8616
8666
|
relativeUrl: `${parsedUrl.pathname}${parsedUrl.search}`,
|
|
8617
|
-
message:
|
|
8667
|
+
message: hasContent
|
|
8668
|
+
? "Companion editor is ready with a detached copy of the current editor text."
|
|
8669
|
+
: "Blank companion editor is ready.",
|
|
8618
8670
|
});
|
|
8619
8671
|
return;
|
|
8620
8672
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code preview",
|
|
3
|
+
"version": "0.8.3",
|
|
4
|
+
"description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code/interactive HTML preview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|