pi-studio 0.5.12 → 0.5.13
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 +7 -0
- package/index.ts +89 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.13] — 2026-03-15
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Studio `Editor (Preview)` PDF export now fences non-markdown editor content such as diff/code before Pandoc export, preventing LaTeX failures on raw diff/code text.
|
|
11
|
+
- Non-markdown editor preview modes such as `diff` now support inline `[an: ...]` markers and render them as compact note pills.
|
|
12
|
+
- The editor highlight overlay keeps exact annotation source text/width, preserving cursor and text alignment while preview-only panes use the compact annotation-pill rendering.
|
|
13
|
+
|
|
7
14
|
## [0.5.12] — 2026-03-15
|
|
8
15
|
|
|
9
16
|
### Added
|
package/index.ts
CHANGED
|
@@ -855,7 +855,9 @@ function readStudioFile(pathArg: string, cwd: string):
|
|
|
855
855
|
| { ok: true; text: string; label: string; resolvedPath: string }
|
|
856
856
|
| { ok: false; message: string } {
|
|
857
857
|
const resolved = resolveStudioPath(pathArg, cwd);
|
|
858
|
-
if (
|
|
858
|
+
if (resolved.ok === false) {
|
|
859
|
+
return { ok: false, message: resolved.message };
|
|
860
|
+
}
|
|
859
861
|
|
|
860
862
|
try {
|
|
861
863
|
const stats = statSync(resolved.resolved);
|
|
@@ -899,7 +901,9 @@ function writeStudioFile(pathArg: string, cwd: string, content: string):
|
|
|
899
901
|
| { ok: true; label: string; resolvedPath: string }
|
|
900
902
|
| { ok: false; message: string } {
|
|
901
903
|
const resolved = resolveStudioPath(pathArg, cwd);
|
|
902
|
-
if (
|
|
904
|
+
if (resolved.ok === false) {
|
|
905
|
+
return { ok: false, message: resolved.message };
|
|
906
|
+
}
|
|
903
907
|
|
|
904
908
|
try {
|
|
905
909
|
writeFileSync(resolved.resolved, content, "utf-8");
|
|
@@ -4662,6 +4666,24 @@ ${cssVarsBlock}
|
|
|
4662
4666
|
return annotationsEnabled ? raw : stripAnnotationMarkers(raw);
|
|
4663
4667
|
}
|
|
4664
4668
|
|
|
4669
|
+
function wrapAsFencedCodeBlock(text, language) {
|
|
4670
|
+
const source = String(text || "").trimEnd();
|
|
4671
|
+
const lang = String(language || "").trim();
|
|
4672
|
+
const backtickFence = "\x60\x60\x60";
|
|
4673
|
+
const newline = "\\n";
|
|
4674
|
+
const marker = source.includes(backtickFence) ? "~~~" : backtickFence;
|
|
4675
|
+
return marker + (lang ? lang : "") + newline + source + newline + marker;
|
|
4676
|
+
}
|
|
4677
|
+
|
|
4678
|
+
function prepareEditorTextForPdfExport(text) {
|
|
4679
|
+
const prepared = prepareEditorTextForPreview(text);
|
|
4680
|
+
const lang = normalizeFenceLanguage(editorLanguage || "");
|
|
4681
|
+
if (lang && lang !== "markdown" && lang !== "latex") {
|
|
4682
|
+
return wrapAsFencedCodeBlock(prepared, lang);
|
|
4683
|
+
}
|
|
4684
|
+
return prepared;
|
|
4685
|
+
}
|
|
4686
|
+
|
|
4665
4687
|
function updateSyncBadge(normalizedEditorText) {
|
|
4666
4688
|
if (!syncBadgeEl) return;
|
|
4667
4689
|
|
|
@@ -5020,7 +5042,7 @@ ${cssVarsBlock}
|
|
|
5020
5042
|
return;
|
|
5021
5043
|
}
|
|
5022
5044
|
|
|
5023
|
-
const markdown = rightView === "editor-preview" ?
|
|
5045
|
+
const markdown = rightView === "editor-preview" ? prepareEditorTextForPdfExport(sourceTextEl.value) : latestResponseMarkdown;
|
|
5024
5046
|
if (!markdown || !markdown.trim()) {
|
|
5025
5047
|
setStatus("Nothing to export yet.", "warning");
|
|
5026
5048
|
return;
|
|
@@ -5028,7 +5050,8 @@ ${cssVarsBlock}
|
|
|
5028
5050
|
|
|
5029
5051
|
const sourcePath = sourceState.path || "";
|
|
5030
5052
|
const resourceDir = (!sourceState.path && resourceDirInput) ? resourceDirInput.value.trim() : "";
|
|
5031
|
-
const
|
|
5053
|
+
const editorPdfLanguage = rightView === "editor-preview" ? normalizeFenceLanguage(editorLanguage || "") : "";
|
|
5054
|
+
const isLatex = editorPdfLanguage === "latex" || /\\\\documentclass\\b|\\\\begin\\{document\\}/.test(markdown);
|
|
5032
5055
|
let filenameHint = rightView === "editor-preview" ? "studio-editor-preview.pdf" : "studio-response-preview.pdf";
|
|
5033
5056
|
if (sourceState.path) {
|
|
5034
5057
|
const baseName = sourceState.path.split(/[\\\\/]/).pop() || "studio";
|
|
@@ -5150,7 +5173,7 @@ ${cssVarsBlock}
|
|
|
5150
5173
|
const text = prepareEditorTextForPreview(sourceTextEl.value || "");
|
|
5151
5174
|
if (editorLanguage && editorLanguage !== "markdown" && editorLanguage !== "latex") {
|
|
5152
5175
|
finishPreviewRender(sourcePreviewEl);
|
|
5153
|
-
sourcePreviewEl.innerHTML = "<div class='response-markdown-highlight' style='white-space:pre;font-family:var(--font-mono);font-size:13px;line-height:1.5;padding:16px;overflow:auto;'>" + highlightCode(text, editorLanguage) + "</div>";
|
|
5176
|
+
sourcePreviewEl.innerHTML = "<div class='response-markdown-highlight' style='white-space:pre;font-family:var(--font-mono);font-size:13px;line-height:1.5;padding:16px;overflow:auto;'>" + highlightCode(text, editorLanguage, "preview") + "</div>";
|
|
5154
5177
|
return;
|
|
5155
5178
|
}
|
|
5156
5179
|
const nonce = ++sourcePreviewRenderNonce;
|
|
@@ -5215,7 +5238,7 @@ ${cssVarsBlock}
|
|
|
5215
5238
|
}
|
|
5216
5239
|
if (editorLanguage && editorLanguage !== "markdown" && editorLanguage !== "latex") {
|
|
5217
5240
|
finishPreviewRender(critiqueViewEl);
|
|
5218
|
-
critiqueViewEl.innerHTML = "<div class='response-markdown-highlight' style='white-space:pre;font-family:var(--font-mono);font-size:13px;line-height:1.5;padding:16px;overflow:auto;'>" + highlightCode(editorText, editorLanguage) + "</div>";
|
|
5241
|
+
critiqueViewEl.innerHTML = "<div class='response-markdown-highlight' style='white-space:pre;font-family:var(--font-mono);font-size:13px;line-height:1.5;padding:16px;overflow:auto;'>" + highlightCode(editorText, editorLanguage, "preview") + "</div>";
|
|
5219
5242
|
return;
|
|
5220
5243
|
}
|
|
5221
5244
|
const nonce = ++responsePreviewRenderNonce;
|
|
@@ -5537,6 +5560,47 @@ ${cssVarsBlock}
|
|
|
5537
5560
|
return "<span class='" + className + "'>" + escapeHtml(String(text || "")) + "</span>";
|
|
5538
5561
|
}
|
|
5539
5562
|
|
|
5563
|
+
function wrapHighlightWithTitle(className, text, title) {
|
|
5564
|
+
const titleAttr = title ? " title='" + escapeHtml(String(title)) + "'" : "";
|
|
5565
|
+
return "<span class='" + className + "'" + titleAttr + ">" + escapeHtml(String(text || "")) + "</span>";
|
|
5566
|
+
}
|
|
5567
|
+
|
|
5568
|
+
function highlightInlineAnnotations(text, mode) {
|
|
5569
|
+
const source = String(text || "");
|
|
5570
|
+
const renderMode = mode === "preview" ? "preview" : "overlay";
|
|
5571
|
+
ANNOTATION_MARKER_REGEX.lastIndex = 0;
|
|
5572
|
+
let lastIndex = 0;
|
|
5573
|
+
let out = "";
|
|
5574
|
+
|
|
5575
|
+
let match;
|
|
5576
|
+
while ((match = ANNOTATION_MARKER_REGEX.exec(source)) !== null) {
|
|
5577
|
+
const token = match[0] || "";
|
|
5578
|
+
const start = typeof match.index === "number" ? match.index : 0;
|
|
5579
|
+
const markerText = typeof match[1] === "string" ? match[1].trim() : token;
|
|
5580
|
+
|
|
5581
|
+
if (start > lastIndex) {
|
|
5582
|
+
out += escapeHtml(source.slice(lastIndex, start));
|
|
5583
|
+
}
|
|
5584
|
+
|
|
5585
|
+
if (renderMode === "preview") {
|
|
5586
|
+
out += wrapHighlightWithTitle("annotation-preview-marker", markerText || token, token);
|
|
5587
|
+
} else {
|
|
5588
|
+
out += wrapHighlight(annotationsEnabled ? "hl-annotation" : "hl-annotation-muted", token);
|
|
5589
|
+
}
|
|
5590
|
+
lastIndex = start + token.length;
|
|
5591
|
+
if (token.length === 0) {
|
|
5592
|
+
ANNOTATION_MARKER_REGEX.lastIndex += 1;
|
|
5593
|
+
}
|
|
5594
|
+
}
|
|
5595
|
+
|
|
5596
|
+
ANNOTATION_MARKER_REGEX.lastIndex = 0;
|
|
5597
|
+
if (lastIndex < source.length) {
|
|
5598
|
+
out += escapeHtml(source.slice(lastIndex));
|
|
5599
|
+
}
|
|
5600
|
+
|
|
5601
|
+
return out;
|
|
5602
|
+
}
|
|
5603
|
+
|
|
5540
5604
|
function highlightInlineMarkdown(text) {
|
|
5541
5605
|
const source = String(text || "");
|
|
5542
5606
|
const pattern = /(\\x60[^\\x60]*\\x60)|(\\[[^\\]]+\\]\\([^)]+\\))|(\\[an:\\s*[^\\]]+\\])/gi;
|
|
@@ -5563,7 +5627,7 @@ ${cssVarsBlock}
|
|
|
5563
5627
|
out += escapeHtml(token);
|
|
5564
5628
|
}
|
|
5565
5629
|
} else if (match[3]) {
|
|
5566
|
-
out +=
|
|
5630
|
+
out += highlightInlineAnnotations(token);
|
|
5567
5631
|
} else {
|
|
5568
5632
|
out += escapeHtml(token);
|
|
5569
5633
|
}
|
|
@@ -5635,9 +5699,10 @@ ${cssVarsBlock}
|
|
|
5635
5699
|
return out;
|
|
5636
5700
|
}
|
|
5637
5701
|
|
|
5638
|
-
function highlightCodeLine(line, language) {
|
|
5702
|
+
function highlightCodeLine(line, language, annotationRenderMode) {
|
|
5639
5703
|
const source = String(line || "");
|
|
5640
5704
|
const lang = normalizeFenceLanguage(language);
|
|
5705
|
+
const renderMode = annotationRenderMode === "preview" ? "preview" : "overlay";
|
|
5641
5706
|
|
|
5642
5707
|
if (!lang) {
|
|
5643
5708
|
return wrapHighlight("hl-code", source);
|
|
@@ -5781,14 +5846,14 @@ ${cssVarsBlock}
|
|
|
5781
5846
|
}
|
|
5782
5847
|
|
|
5783
5848
|
if (lang === "diff") {
|
|
5784
|
-
var
|
|
5785
|
-
if (/^@@/.test(source)) return "<span class=\\"hl-code-fn\\">" +
|
|
5786
|
-
if (/^\\+\\+\\+|^---/.test(source)) return "<span class=\\"hl-code-kw\\">" +
|
|
5787
|
-
if (/^\\+/.test(source)) return "<span class=\\"hl-diff-add\\">" +
|
|
5788
|
-
if (/^-/.test(source)) return "<span class=\\"hl-diff-del\\">" +
|
|
5789
|
-
if (/^diff /.test(source)) return "<span class=\\"hl-code-kw\\">" +
|
|
5790
|
-
if (/^index /.test(source)) return "<span class=\\"hl-code-com\\">" +
|
|
5791
|
-
return
|
|
5849
|
+
var highlightedDiff = highlightInlineAnnotations(source, renderMode);
|
|
5850
|
+
if (/^@@/.test(source)) return "<span class=\\"hl-code-fn\\">" + highlightedDiff + "</span>";
|
|
5851
|
+
if (/^\\+\\+\\+|^---/.test(source)) return "<span class=\\"hl-code-kw\\">" + highlightedDiff + "</span>";
|
|
5852
|
+
if (/^\\+/.test(source)) return "<span class=\\"hl-diff-add\\">" + highlightedDiff + "</span>";
|
|
5853
|
+
if (/^-/.test(source)) return "<span class=\\"hl-diff-del\\">" + highlightedDiff + "</span>";
|
|
5854
|
+
if (/^diff /.test(source)) return "<span class=\\"hl-code-kw\\">" + highlightedDiff + "</span>";
|
|
5855
|
+
if (/^index /.test(source)) return "<span class=\\"hl-code-com\\">" + highlightedDiff + "</span>";
|
|
5856
|
+
return highlightedDiff;
|
|
5792
5857
|
}
|
|
5793
5858
|
|
|
5794
5859
|
return wrapHighlight("hl-code", source);
|
|
@@ -5864,15 +5929,16 @@ ${cssVarsBlock}
|
|
|
5864
5929
|
return out.join("<br>");
|
|
5865
5930
|
}
|
|
5866
5931
|
|
|
5867
|
-
function highlightCode(text, language) {
|
|
5932
|
+
function highlightCode(text, language, annotationRenderMode) {
|
|
5868
5933
|
const lines = String(text || "").replace(/\\r\\n/g, "\\n").split("\\n");
|
|
5869
5934
|
const lang = normalizeFenceLanguage(language);
|
|
5935
|
+
const renderMode = annotationRenderMode === "preview" ? "preview" : "overlay";
|
|
5870
5936
|
const out = [];
|
|
5871
5937
|
for (const line of lines) {
|
|
5872
5938
|
if (line.length === 0) {
|
|
5873
5939
|
out.push(EMPTY_OVERLAY_LINE);
|
|
5874
5940
|
} else if (lang) {
|
|
5875
|
-
out.push(highlightCodeLine(line, lang));
|
|
5941
|
+
out.push(highlightCodeLine(line, lang, renderMode));
|
|
5876
5942
|
} else {
|
|
5877
5943
|
out.push(escapeHtml(line));
|
|
5878
5944
|
}
|
|
@@ -8005,7 +8071,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
8005
8071
|
|
|
8006
8072
|
const baseDir = resolveStudioGitDiffBaseDir(msg.sourcePath, msg.resourceDir, studioCwd);
|
|
8007
8073
|
const diffResult = readStudioGitDiff(baseDir);
|
|
8008
|
-
if (
|
|
8074
|
+
if (diffResult.ok === false) {
|
|
8009
8075
|
sendToClient(client, {
|
|
8010
8076
|
type: "info",
|
|
8011
8077
|
requestId: msg.requestId,
|
|
@@ -8037,7 +8103,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
8037
8103
|
}
|
|
8038
8104
|
|
|
8039
8105
|
const result = cancelActiveRequest(msg.requestId);
|
|
8040
|
-
if (
|
|
8106
|
+
if (result.ok === false) {
|
|
8041
8107
|
sendToClient(client, { type: "error", requestId: msg.requestId, message: result.message });
|
|
8042
8108
|
}
|
|
8043
8109
|
return;
|
|
@@ -8235,7 +8301,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
8235
8301
|
}
|
|
8236
8302
|
|
|
8237
8303
|
const result = writeStudioFile(msg.path, studioCwd, msg.content);
|
|
8238
|
-
if (
|
|
8304
|
+
if (result.ok === false) {
|
|
8239
8305
|
sendToClient(client, { type: "error", requestId: msg.requestId, message: result.message });
|
|
8240
8306
|
return;
|
|
8241
8307
|
}
|
|
@@ -9099,7 +9165,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
9099
9165
|
}
|
|
9100
9166
|
|
|
9101
9167
|
const file = readStudioFile(pathArg, ctx.cwd);
|
|
9102
|
-
if (
|
|
9168
|
+
if (file.ok === false) {
|
|
9103
9169
|
ctx.ui.notify(file.message, "error");
|
|
9104
9170
|
return;
|
|
9105
9171
|
}
|
|
@@ -9165,7 +9231,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
9165
9231
|
}
|
|
9166
9232
|
|
|
9167
9233
|
const file = readStudioFile(pathArg, ctx.cwd);
|
|
9168
|
-
if (
|
|
9234
|
+
if (file.ok === false) {
|
|
9169
9235
|
ctx.ui.notify(file.message, "error");
|
|
9170
9236
|
return;
|
|
9171
9237
|
}
|