pi-studio 0.5.34 → 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.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/index.ts +89 -3
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@ 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
+
7
12
  ## [0.5.34] — 2026-03-27
8
13
 
9
14
  ### Changed
@@ -11,6 +16,7 @@ All notable changes to `pi-studio` are documented here.
11
16
 
12
17
  ### Fixed
13
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.
14
20
 
15
21
  ## [0.5.33] — 2026-03-27
16
22
 
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
- if (markerText) {
4080
- rewritten += `\\studioannotation{${markerText}}`;
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.34",
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",