pi-studio 0.5.22 → 0.5.23

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 +5 -0
  2. package/index.ts +77 -2
  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.23] — 2026-03-20
8
+
9
+ ### Fixed
10
+ - LaTeX PDF export now preprocesses common `algorithm` / `algorithmic` / `algpseudocode` blocks into pandoc-friendly quoted step layouts, improving exported algorithm readability while keeping the existing Studio PDF pipeline.
11
+
7
12
  ## [0.5.22] — 2026-03-20
8
13
 
9
14
  ### Fixed
package/index.ts CHANGED
@@ -1560,6 +1560,78 @@ function preprocessStudioLatexAlgorithmsForPreview(markdown: string): StudioLate
1560
1560
  };
1561
1561
  }
1562
1562
 
1563
+ function renderStudioLatexAlgorithmPdfLines(
1564
+ lines: StudioLatexAlgorithmPreviewLine[],
1565
+ startIndex: number,
1566
+ indent: number,
1567
+ ): { latex: string; nextIndex: number } {
1568
+ const parts: string[] = [];
1569
+ let index = startIndex;
1570
+
1571
+ while (index < lines.length) {
1572
+ const line = lines[index]!;
1573
+ if (line.indent < indent) break;
1574
+ if (line.indent > indent) {
1575
+ const nested = renderStudioLatexAlgorithmPdfLines(lines, index, line.indent);
1576
+ if (nested.latex.trim()) {
1577
+ parts.push(`\\begin{quote}\n${nested.latex}\n\\end{quote}`);
1578
+ }
1579
+ index = nested.nextIndex;
1580
+ continue;
1581
+ }
1582
+
1583
+ const prefix = line.lineNumber == null ? "" : `${line.lineNumber}. `;
1584
+ parts.push(`${prefix}${line.content}`.trim());
1585
+ index++;
1586
+
1587
+ while (index < lines.length && lines[index]!.indent > indent) {
1588
+ const nested = renderStudioLatexAlgorithmPdfLines(lines, index, lines[index]!.indent);
1589
+ if (nested.latex.trim()) {
1590
+ parts.push(`\\begin{quote}\n${nested.latex}\n\\end{quote}`);
1591
+ }
1592
+ index = nested.nextIndex;
1593
+ }
1594
+ }
1595
+
1596
+ return {
1597
+ latex: parts.filter(Boolean).join("\n\n"),
1598
+ nextIndex: index,
1599
+ };
1600
+ }
1601
+
1602
+ function buildStudioLatexAlgorithmPdfBlock(
1603
+ block: StudioLatexAlgorithmPreviewBlock,
1604
+ labels: Map<string, { number: string; kind: string }>,
1605
+ ): string {
1606
+ const body = renderStudioLatexAlgorithmPdfLines(block.lines, 0, 0).latex.trim();
1607
+ const captionLabel = formatStudioLatexMainAlgorithmCaptionLabel(block.label, labels);
1608
+ const heading = captionLabel
1609
+ ? (block.caption ? `\\textbf{${captionLabel}} ${block.caption}` : `\\textbf{${captionLabel}}`)
1610
+ : (block.caption ? `\\textbf{${block.caption}}` : "");
1611
+ const parts = [heading, body].filter(Boolean);
1612
+ return `\n\n\\begin{quote}\n${parts.join("\n\n")}\n\\end{quote}\n\n`;
1613
+ }
1614
+
1615
+ function preprocessStudioLatexAlgorithmsForPdf(markdown: string, sourcePath: string | undefined, baseDir: string | undefined): string {
1616
+ const previewTransform = preprocessStudioLatexAlgorithmsForPreview(markdown);
1617
+ if (previewTransform.algorithmBlocks.length === 0) return markdown;
1618
+ const labels = readStudioLatexAuxLabels(sourcePath, baseDir);
1619
+ let transformed = previewTransform.markdown;
1620
+
1621
+ for (const block of previewTransform.algorithmBlocks) {
1622
+ const startMarker = `PISTUDIOALGORITHMSTART${block.markerId}`;
1623
+ const endMarker = `PISTUDIOALGORITHMEND${block.markerId}`;
1624
+ const startIndex = transformed.indexOf(startMarker);
1625
+ if (startIndex < 0) continue;
1626
+ const endIndex = transformed.indexOf(endMarker, startIndex + startMarker.length);
1627
+ if (endIndex < 0) continue;
1628
+ const endSliceIndex = endIndex + endMarker.length;
1629
+ transformed = transformed.slice(0, startIndex) + buildStudioLatexAlgorithmPdfBlock(block, labels) + transformed.slice(endSliceIndex);
1630
+ }
1631
+
1632
+ return transformed;
1633
+ }
1634
+
1563
1635
  function appendStudioHtmlClassAttribute(attrs: string, className: string): string {
1564
1636
  if (/\bclass="([^"]*)"/.test(attrs)) {
1565
1637
  return attrs.replace(/\bclass="([^"]*)"/, (_match, existing) => {
@@ -2646,12 +2718,15 @@ async function renderStudioPdfWithPandoc(
2646
2718
  ): Promise<{ pdf: Buffer; warning?: string }> {
2647
2719
  const pandocCommand = process.env.PANDOC_PATH?.trim() || "pandoc";
2648
2720
  const pdfEngine = process.env.PANDOC_PDF_ENGINE?.trim() || "xelatex";
2721
+ const latexPdfSource = isLatex
2722
+ ? preprocessStudioLatexAlgorithmsForPdf(markdown, sourcePath, resourcePath)
2723
+ : markdown;
2649
2724
  const sourceWithResolvedRefs = isLatex
2650
- ? injectStudioLatexEquationTags(preprocessStudioLatexReferences(markdown, sourcePath, resourcePath), sourcePath, resourcePath)
2725
+ ? injectStudioLatexEquationTags(preprocessStudioLatexReferences(latexPdfSource, sourcePath, resourcePath), sourcePath, resourcePath)
2651
2726
  : markdown;
2652
2727
  const effectiveEditorLanguage = inferStudioPdfLanguage(sourceWithResolvedRefs, editorPdfLanguage);
2653
2728
  const pandocWorkingDir = resolveStudioPandocWorkingDir(resourcePath);
2654
- const bibliographyArgs = buildStudioPandocBibliographyArgs(sourceWithResolvedRefs, isLatex, resourcePath);
2729
+ const bibliographyArgs = buildStudioPandocBibliographyArgs(markdown, isLatex, resourcePath);
2655
2730
 
2656
2731
  const runPandocPdfExport = async (
2657
2732
  inputFormat: string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Browser GUI for structured critique workflows in pi",
5
5
  "type": "module",
6
6
  "license": "MIT",