pi-studio 0.5.33 → 0.5.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/client/studio-client.js +27 -0
- package/client/studio.css +16 -0
- package/index.ts +89 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.35] — 2026-03-27
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Diff/file exports via `/studio-pdf <path>` now also make inline math scripts in diff-line annotation badges verbatim-safe inside Pandoc's generated `Highlighting` environment, so subscripts/superscripts like `$x_n$` and `$\epsilon_n=\frac{1}{n}$` render correctly instead of showing literal underscores in the exported PDF.
|
|
11
|
+
|
|
12
|
+
## [0.5.34] — 2026-03-27
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Preview-side fenced `text`/`plaintext` blocks now soft-wrap long lines instead of forcing horizontal scrolling, while code/diff blocks keep their existing scrollable behavior.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Preview annotation pills once again render inline math within long `[an: ...]` notes instead of leaving `$...$` / `\(...\)` fragments as literal text.
|
|
19
|
+
- Diff/file exports via `/studio-pdf <path>` now also preserve math inside diff-line annotation badges such as `[an: add note $\epsilon_n=\frac{1}{n}$]`, instead of leaving escaped TeX literals in the exported PDF.
|
|
20
|
+
|
|
7
21
|
## [0.5.33] — 2026-03-27
|
|
8
22
|
|
|
9
23
|
### Changed
|
package/client/studio-client.js
CHANGED
|
@@ -1697,6 +1697,32 @@
|
|
|
1697
1697
|
}
|
|
1698
1698
|
}
|
|
1699
1699
|
|
|
1700
|
+
async function renderAnnotationMathInElement(targetEl) {
|
|
1701
|
+
if (!targetEl || typeof targetEl.querySelectorAll !== "function") return;
|
|
1702
|
+
|
|
1703
|
+
const markers = Array.from(targetEl.querySelectorAll(".annotation-preview-marker")).filter((node) => {
|
|
1704
|
+
const text = typeof node.textContent === "string" ? node.textContent : "";
|
|
1705
|
+
return /\\\(|\\\[|\$\$?|\\[A-Za-z]+/.test(text);
|
|
1706
|
+
});
|
|
1707
|
+
if (markers.length === 0) return;
|
|
1708
|
+
|
|
1709
|
+
let mathJax;
|
|
1710
|
+
try {
|
|
1711
|
+
mathJax = await ensureMathJax();
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
console.error("Annotation MathJax load failed:", error);
|
|
1714
|
+
appendMathFallbackNotice(targetEl, MATHJAX_UNAVAILABLE_MESSAGE);
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
try {
|
|
1719
|
+
await mathJax.typesetPromise(markers);
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
console.error("Annotation math render failed:", error);
|
|
1722
|
+
appendMathFallbackNotice(targetEl, MATHJAX_RENDER_FAIL_MESSAGE);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1700
1726
|
function applyPreviewAnnotationPlaceholdersToElement(targetEl, placeholders) {
|
|
1701
1727
|
if (!targetEl || !Array.isArray(placeholders) || placeholders.length === 0) return;
|
|
1702
1728
|
if (typeof document.createTreeWalker !== "function") return;
|
|
@@ -2258,6 +2284,7 @@
|
|
|
2258
2284
|
finishPreviewRender(targetEl);
|
|
2259
2285
|
targetEl.innerHTML = sanitizeRenderedHtml(renderedHtml, markdown);
|
|
2260
2286
|
applyPreviewAnnotationPlaceholdersToElement(targetEl, previewPrepared.placeholders);
|
|
2287
|
+
await renderAnnotationMathInElement(targetEl);
|
|
2261
2288
|
decoratePdfEmbeds(targetEl);
|
|
2262
2289
|
await renderPdfPreviewsInElement(targetEl);
|
|
2263
2290
|
const annotationMode = (pane === "source" || pane === "response")
|
package/client/studio.css
CHANGED
|
@@ -742,6 +742,22 @@
|
|
|
742
742
|
color: var(--md-codeblock);
|
|
743
743
|
}
|
|
744
744
|
|
|
745
|
+
.rendered-markdown pre.text,
|
|
746
|
+
.rendered-markdown pre.plaintext,
|
|
747
|
+
.rendered-markdown pre.sourceCode.txt {
|
|
748
|
+
white-space: pre-wrap;
|
|
749
|
+
overflow-x: hidden;
|
|
750
|
+
overflow-wrap: anywhere;
|
|
751
|
+
word-break: break-word;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.rendered-markdown pre.text code,
|
|
755
|
+
.rendered-markdown pre.plaintext code,
|
|
756
|
+
.rendered-markdown pre.sourceCode.txt code,
|
|
757
|
+
.rendered-markdown pre.sourceCode.txt code > span {
|
|
758
|
+
white-space: inherit;
|
|
759
|
+
}
|
|
760
|
+
|
|
745
761
|
.rendered-markdown :not(pre) > code {
|
|
746
762
|
background: rgba(127, 127, 127, 0.13);
|
|
747
763
|
border: 1px solid var(--md-codeblock-border);
|
package/index.ts
CHANGED
|
@@ -4056,6 +4056,91 @@ function isStudioGeneratedDiffHighlightingBlock(lines: string[]): boolean {
|
|
|
4056
4056
|
return hasAdditionOrDeletion && hasDiffStructure;
|
|
4057
4057
|
}
|
|
4058
4058
|
|
|
4059
|
+
function decodeStudioGeneratedCodeLatexText(text: string): string {
|
|
4060
|
+
return String(text ?? "")
|
|
4061
|
+
.replace(/\\textbackslash\{\}/g, "\\")
|
|
4062
|
+
.replace(/\\textasciitilde\{\}/g, "~")
|
|
4063
|
+
.replace(/\\textasciicircum\{\}/g, "^")
|
|
4064
|
+
.replace(/\\([{}$&#_%])/g, "$1");
|
|
4065
|
+
}
|
|
4066
|
+
|
|
4067
|
+
function readStudioVerbatimMathOperand(expr: string, startIndex: number): { operand: string; nextIndex: number } | null {
|
|
4068
|
+
if (startIndex >= expr.length) return null;
|
|
4069
|
+
const first = expr[startIndex]!;
|
|
4070
|
+
|
|
4071
|
+
if (first === "{") {
|
|
4072
|
+
let depth = 1;
|
|
4073
|
+
let index = startIndex + 1;
|
|
4074
|
+
while (index < expr.length) {
|
|
4075
|
+
const char = expr[index]!;
|
|
4076
|
+
if (char === "{") {
|
|
4077
|
+
depth += 1;
|
|
4078
|
+
} else if (char === "}") {
|
|
4079
|
+
depth -= 1;
|
|
4080
|
+
if (depth === 0) {
|
|
4081
|
+
return {
|
|
4082
|
+
operand: expr.slice(startIndex + 1, index),
|
|
4083
|
+
nextIndex: index + 1,
|
|
4084
|
+
};
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
index += 1;
|
|
4088
|
+
}
|
|
4089
|
+
return {
|
|
4090
|
+
operand: expr.slice(startIndex + 1),
|
|
4091
|
+
nextIndex: expr.length,
|
|
4092
|
+
};
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
if (first === "\\") {
|
|
4096
|
+
let index = startIndex + 1;
|
|
4097
|
+
while (index < expr.length && /[A-Za-z]/.test(expr[index]!)) {
|
|
4098
|
+
index += 1;
|
|
4099
|
+
}
|
|
4100
|
+
if (index === startIndex + 1 && index < expr.length) {
|
|
4101
|
+
index += 1;
|
|
4102
|
+
}
|
|
4103
|
+
return {
|
|
4104
|
+
operand: expr.slice(startIndex, index),
|
|
4105
|
+
nextIndex: index,
|
|
4106
|
+
};
|
|
4107
|
+
}
|
|
4108
|
+
|
|
4109
|
+
return {
|
|
4110
|
+
operand: first,
|
|
4111
|
+
nextIndex: startIndex + 1,
|
|
4112
|
+
};
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
function makeStudioHighlightingMathScriptsVerbatimSafe(text: string): string {
|
|
4116
|
+
const rewriteExpr = (expr: string): string => {
|
|
4117
|
+
let out = "";
|
|
4118
|
+
for (let index = 0; index < expr.length; index += 1) {
|
|
4119
|
+
const char = expr[index]!;
|
|
4120
|
+
if (char !== "_" && char !== "^") {
|
|
4121
|
+
out += char;
|
|
4122
|
+
continue;
|
|
4123
|
+
}
|
|
4124
|
+
|
|
4125
|
+
const operand = readStudioVerbatimMathOperand(expr, index + 1);
|
|
4126
|
+
if (!operand || !operand.operand) {
|
|
4127
|
+
out += char;
|
|
4128
|
+
continue;
|
|
4129
|
+
}
|
|
4130
|
+
|
|
4131
|
+
out += char === "_" ? `\\sb{${operand.operand}}` : `\\sp{${operand.operand}}`;
|
|
4132
|
+
index = operand.nextIndex - 1;
|
|
4133
|
+
}
|
|
4134
|
+
return out;
|
|
4135
|
+
};
|
|
4136
|
+
|
|
4137
|
+
return String(text ?? "")
|
|
4138
|
+
.replace(/\\\(([\s\S]*?)\\\)/g, (_match, expr: string) => `\\(${rewriteExpr(expr)}\\)`)
|
|
4139
|
+
.replace(/\\\[([\s\S]*?)\\\]/g, (_match, expr: string) => `\\[${rewriteExpr(expr)}\\]`)
|
|
4140
|
+
.replace(/\$\$([\s\S]*?)\$\$/g, (_match, expr: string) => `$$${rewriteExpr(expr)}$$`)
|
|
4141
|
+
.replace(/\$([^$\n]+?)\$/g, (_match, expr: string) => `$${rewriteExpr(expr)}$`);
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4059
4144
|
function replaceStudioAnnotationMarkersInDiffTokenLine(line: string, macroName: string): string {
|
|
4060
4145
|
const tokenMatch = line.match(new RegExp(`^\\\\${macroName}\\{([\\s\\S]*)\\}$`));
|
|
4061
4146
|
if (!tokenMatch) return line;
|
|
@@ -4075,9 +4160,10 @@ function replaceStudioAnnotationMarkersInDiffTokenLine(line: string, macroName:
|
|
|
4075
4160
|
rewritten += wrapText(body.slice(lastIndex, start));
|
|
4076
4161
|
}
|
|
4077
4162
|
|
|
4078
|
-
const markerText = (match[1] ?? "").replace(/\s{2,}/g, " ").trim();
|
|
4079
|
-
|
|
4080
|
-
|
|
4163
|
+
const markerText = decodeStudioGeneratedCodeLatexText((match[1] ?? "").replace(/\s{2,}/g, " ").trim());
|
|
4164
|
+
const cleaned = makeStudioHighlightingMathScriptsVerbatimSafe(escapeStudioPdfLatexText(markerText));
|
|
4165
|
+
if (cleaned) {
|
|
4166
|
+
rewritten += `\\studioannotation{${cleaned}}`;
|
|
4081
4167
|
}
|
|
4082
4168
|
|
|
4083
4169
|
lastIndex = start + token.length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.35",
|
|
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",
|