pi-studio 0.9.30 → 0.9.31

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 +24 -11
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to `pi-studio` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.31] — 2026-06-09
8
+
9
+ ### Fixed
10
+ - Render long PDF annotations as full-width display-style boxes at their marker position so exported feedback avoids clipping while preserving the surrounding annotation order.
11
+ - Avoided annotation placeholder collisions in HTML exports with ten or more annotations.
12
+
7
13
  ## [0.9.30] — 2026-06-09
8
14
 
9
15
  ### Fixed
package/index.ts CHANGED
@@ -1112,6 +1112,7 @@ function buildStudioPdfPreamble(options?: StudioPdfRenderOptions, extraPreamble
1112
1112
  \\definecolor{StudioCalloutCautionText}{HTML}{A40E26}
1113
1113
  \\definecolor{StudioCalloutCautionLabelBg}{HTML}{FDEBEC}
1114
1114
  \\newcommand{\\studioannotation}[1]{\\begingroup\\setlength{\\fboxsep}{1.5pt}\\fcolorbox{StudioAnnotationBorder}{StudioAnnotationBg}{\\begin{varwidth}{\\dimexpr\\linewidth-2\\fboxsep-2\\fboxrule\\relax}\\raggedright\\textcolor{StudioAnnotationText}{\\sffamily\\footnotesize\\strut #1}\\end{varwidth}}\\endgroup}
1115
+ \\newcommand{\\studioblockannotation}[1]{\\par\\smallskip\\noindent\\begingroup\\setlength{\\fboxsep}{1.5pt}\\fcolorbox{StudioAnnotationBorder}{StudioAnnotationBg}{\\begin{minipage}{\\dimexpr\\linewidth-2\\fboxsep-2\\fboxrule\\relax}\\raggedright\\textcolor{StudioAnnotationText}{\\sffamily\\footnotesize\\strut #1}\\end{minipage}}\\endgroup\\par\\smallskip\\noindent\\ignorespaces}
1115
1116
  \\newcommand{\\StudioDiffAddTok}[1]{\\textcolor{StudioDiffAddText}{#1}}
1116
1117
  \\newcommand{\\StudioDiffDelTok}[1]{\\textcolor{StudioDiffDelText}{#1}}
1117
1118
  \\newcommand{\\StudioDiffMetaTok}[1]{\\textcolor{StudioDiffMetaText}{#1}}
@@ -5069,22 +5070,25 @@ function renderStudioAnnotationPdfLatex(text: string): string {
5069
5070
  return renderStudioAnnotationPdfLatexContent(normalized).trim();
5070
5071
  }
5071
5072
 
5073
+ function renderStudioAnnotationPdfBox(markerText: string, block = false): string {
5074
+ const cleaned = renderStudioAnnotationPdfLatex(markerText);
5075
+ if (!cleaned) return "";
5076
+ return block ? `\\studioblockannotation{${cleaned}}` : `\\studioannotation{${cleaned}}`;
5077
+ }
5078
+
5072
5079
  function replaceStudioAnnotationMarkersForPdfInSegment(text: string): string {
5080
+ const renderMarker = (markerText: string): string => {
5081
+ const label = normalizeStudioAnnotationText(markerText);
5082
+ if (!label) return "";
5083
+ return renderStudioAnnotationPdfBox(label, shouldRenderStudioAnnotationAsPdfBlock(label));
5084
+ };
5073
5085
  const replaced = replaceStudioInlineAnnotationMarkers(
5074
5086
  String(text ?? ""),
5075
- (marker: { body: string }) => {
5076
- const cleaned = renderStudioAnnotationPdfLatex(marker.body);
5077
- if (!cleaned) return "";
5078
- return `\\studioannotation{${cleaned}}`;
5079
- },
5087
+ (marker: { body: string }) => renderMarker(marker.body),
5080
5088
  );
5081
5089
 
5082
5090
  return String(replaced ?? "")
5083
- .replace(/\{\[\}\s*an:\s*([\s\S]*?)\s*\{\]\}/gi, (_match, markerText: string) => {
5084
- const cleaned = renderStudioAnnotationPdfLatex(markerText);
5085
- if (!cleaned) return "";
5086
- return `\\studioannotation{${cleaned}}`;
5087
- });
5091
+ .replace(/\{\[\}\s*an:\s*([\s\S]*?)\s*\{\]\}/gi, (_match, markerText: string) => renderMarker(markerText));
5088
5092
  }
5089
5093
 
5090
5094
  function replaceStudioAnnotationMarkersForPdf(markdown: string): string {
@@ -6096,6 +6100,15 @@ function parseStudioHtmlPdfBlockOptions(body: string): StudioHtmlPdfBlockOptions
6096
6100
  return options;
6097
6101
  }
6098
6102
 
6103
+ // PDF/LaTeX cannot fully mimic the browser's inline-block wrapping for long
6104
+ // annotation chips, so long annotations switch to a display box at the marker.
6105
+ const STUDIO_PDF_ANNOTATION_DISPLAY_THRESHOLD_CHARS = 115;
6106
+
6107
+ function shouldRenderStudioAnnotationAsPdfBlock(text: string): boolean {
6108
+ const normalized = normalizeStudioAnnotationText(text);
6109
+ return normalized.length > STUDIO_PDF_ANNOTATION_DISPLAY_THRESHOLD_CHARS;
6110
+ }
6111
+
6099
6112
  function prepareStudioPdfBlocksForHtml(markdown: string): { markdown: string; blocks: StudioHtmlPdfBlock[] } {
6100
6113
  const blocks: StudioHtmlPdfBlock[] = [];
6101
6114
  const prefix = `PISTUDIOHTMLPDF${Date.now().toString(36)}${randomUUID().replace(/-/g, "")}TOKEN`;
@@ -6126,7 +6139,7 @@ function prepareStudioAnnotationMarkersForHtml(markdown: string): { markdown: st
6126
6139
 
6127
6140
  function applyStudioAnnotationPlaceholdersToHtml(html: string, placeholders: StudioHtmlAnnotationPlaceholder[]): string {
6128
6141
  let transformed = String(html ?? "");
6129
- for (const placeholder of placeholders) {
6142
+ for (const placeholder of [...placeholders].sort((a, b) => b.token.length - a.token.length)) {
6130
6143
  const tokenPattern = new RegExp(escapeStudioRegExpLiteral(placeholder.token), "g");
6131
6144
  const markerHtml = `<span class="annotation-preview-marker" title="${escapeStudioHtmlText(placeholder.title)}">${renderStudioAnnotationInlineHtml(placeholder.text)}</span>`;
6132
6145
  transformed = transformed.replace(tokenPattern, markerHtml);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.9.30",
3
+ "version": "0.9.31",
4
4
  "description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, active quiz, prompt/response history, live previews, and tmux-backed REPL/literate REPL workflows",
5
5
  "type": "module",
6
6
  "license": "MIT",