pi-studio 0.5.20 → 0.5.22
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 +15 -0
- package/client/studio-client.js +58 -8
- package/client/studio.css +133 -0
- package/index.ts +993 -45
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.22] — 2026-03-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Citeproc-rendered LaTeX bibliographies now request a visible `References` section heading in Studio preview/PDF output.
|
|
11
|
+
- LaTeX preview now regroups `subfigure`-based figures so adjacent subfigures keep their shared overall figure/caption structure instead of rendering as unrelated standalone figures, including visible `(a)` / `(b)` subfigure markers and `Figure n` main-caption labels when `.aux` labels are available.
|
|
12
|
+
- LaTeX preview now converts common `algorithm` / `algorithmic` / `algpseudocode` blocks into readable algorithm cards with preserved captions, indentation, and optional line numbers instead of showing the raw environment text.
|
|
13
|
+
- The editor language dropdown is now alphabetised for quicker scanning.
|
|
14
|
+
|
|
15
|
+
## [0.5.21] — 2026-03-19
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- PDF export now uses a two-step prepare/download flow and opens the generated PDF in the system’s default viewer first, so browser surfaces like cmux do not need to navigate away from the current Studio page.
|
|
19
|
+
- LaTeX preview and PDF export now use the document `.aux` file when available to substitute basic `\eqref{...}`, `\ref{...}`, and `\autoref{...}` values more reliably, and preview decorates block equations with their resolved equation numbers.
|
|
20
|
+
- Upload + working-directory LaTeX workflows now derive the effective source path more reliably, helping Studio find the correct `.aux` file for reference resolution.
|
|
21
|
+
|
|
7
22
|
## [0.5.20] — 2026-03-19
|
|
8
23
|
|
|
9
24
|
### Fixed
|
package/client/studio-client.js
CHANGED
|
@@ -1351,6 +1351,8 @@
|
|
|
1351
1351
|
|
|
1352
1352
|
let response;
|
|
1353
1353
|
try {
|
|
1354
|
+
const effectivePath = getEffectiveSavePath();
|
|
1355
|
+
const sourcePath = effectivePath || sourceState.path || "";
|
|
1354
1356
|
response = await fetch("/render-preview?token=" + encodeURIComponent(token), {
|
|
1355
1357
|
method: "POST",
|
|
1356
1358
|
headers: {
|
|
@@ -1358,8 +1360,8 @@
|
|
|
1358
1360
|
},
|
|
1359
1361
|
body: JSON.stringify({
|
|
1360
1362
|
markdown: String(markdown || ""),
|
|
1361
|
-
sourcePath:
|
|
1362
|
-
resourceDir: (!
|
|
1363
|
+
sourcePath: sourcePath,
|
|
1364
|
+
resourceDir: (!sourcePath && resourceDirInput) ? resourceDirInput.value.trim() : "",
|
|
1363
1365
|
}),
|
|
1364
1366
|
signal: controller ? controller.signal : undefined,
|
|
1365
1367
|
});
|
|
@@ -1444,16 +1446,17 @@
|
|
|
1444
1446
|
return;
|
|
1445
1447
|
}
|
|
1446
1448
|
|
|
1447
|
-
const
|
|
1448
|
-
const
|
|
1449
|
+
const effectivePath = getEffectiveSavePath();
|
|
1450
|
+
const sourcePath = effectivePath || sourceState.path || "";
|
|
1451
|
+
const resourceDir = (!sourcePath && resourceDirInput) ? resourceDirInput.value.trim() : "";
|
|
1449
1452
|
const isEditorPreview = rightView === "editor-preview";
|
|
1450
1453
|
const editorPdfLanguage = isEditorPreview ? normalizeFenceLanguage(editorLanguage || "") : "";
|
|
1451
1454
|
const isLatex = isEditorPreview
|
|
1452
1455
|
? editorPdfLanguage === "latex"
|
|
1453
1456
|
: /\\documentclass\b|\\begin\{document\}/.test(markdown);
|
|
1454
1457
|
let filenameHint = isEditorPreview ? "studio-editor-preview.pdf" : "studio-response-preview.pdf";
|
|
1455
|
-
if (
|
|
1456
|
-
const baseName =
|
|
1458
|
+
if (sourcePath) {
|
|
1459
|
+
const baseName = sourcePath.split(/[\\/]/).pop() || "studio";
|
|
1457
1460
|
const stem = baseName.replace(/\.[^.]+$/, "") || "studio";
|
|
1458
1461
|
filenameHint = stem + "-preview.pdf";
|
|
1459
1462
|
}
|
|
@@ -1478,8 +1481,8 @@
|
|
|
1478
1481
|
}),
|
|
1479
1482
|
});
|
|
1480
1483
|
|
|
1484
|
+
const contentType = String(response.headers.get("content-type") || "").toLowerCase();
|
|
1481
1485
|
if (!response.ok) {
|
|
1482
|
-
const contentType = String(response.headers.get("content-type") || "").toLowerCase();
|
|
1483
1486
|
let message = "PDF export failed with HTTP " + response.status + ".";
|
|
1484
1487
|
if (contentType.includes("application/json")) {
|
|
1485
1488
|
const payload = await response.json().catch(() => null);
|
|
@@ -1495,6 +1498,53 @@
|
|
|
1495
1498
|
throw new Error(message);
|
|
1496
1499
|
}
|
|
1497
1500
|
|
|
1501
|
+
if (contentType.includes("application/json")) {
|
|
1502
|
+
const payload = await response.json().catch(() => null);
|
|
1503
|
+
if (!payload || typeof payload.downloadUrl !== "string") {
|
|
1504
|
+
throw new Error("PDF export prepared successfully, but Studio did not receive a download URL.");
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
const exportWarning = typeof payload.warning === "string" ? payload.warning.trim() : "";
|
|
1508
|
+
const openError = typeof payload.openError === "string" ? payload.openError.trim() : "";
|
|
1509
|
+
const openedExternal = payload.openedExternal === true;
|
|
1510
|
+
let downloadName = typeof payload.filename === "string" && payload.filename.trim()
|
|
1511
|
+
? payload.filename.trim()
|
|
1512
|
+
: (filenameHint || "studio-preview.pdf");
|
|
1513
|
+
if (!/\.pdf$/i.test(downloadName)) {
|
|
1514
|
+
downloadName += ".pdf";
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
if (openedExternal) {
|
|
1518
|
+
if (exportWarning) {
|
|
1519
|
+
setStatus("Opened PDF in default viewer with warning: " + exportWarning, "warning");
|
|
1520
|
+
} else {
|
|
1521
|
+
setStatus("Opened PDF in default viewer: " + downloadName, "success");
|
|
1522
|
+
}
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const link = document.createElement("a");
|
|
1527
|
+
link.href = payload.downloadUrl;
|
|
1528
|
+
link.download = downloadName;
|
|
1529
|
+
link.rel = "noopener";
|
|
1530
|
+
document.body.appendChild(link);
|
|
1531
|
+
link.click();
|
|
1532
|
+
link.remove();
|
|
1533
|
+
|
|
1534
|
+
if (openError) {
|
|
1535
|
+
if (exportWarning) {
|
|
1536
|
+
setStatus("Opened browser fallback because external viewer failed (" + openError + "). Warning: " + exportWarning, "warning");
|
|
1537
|
+
} else {
|
|
1538
|
+
setStatus("Opened browser fallback because external viewer failed (" + openError + ").", "warning");
|
|
1539
|
+
}
|
|
1540
|
+
} else if (exportWarning) {
|
|
1541
|
+
setStatus("Exported PDF with warning: " + exportWarning, "warning");
|
|
1542
|
+
} else {
|
|
1543
|
+
setStatus("Exported PDF: " + downloadName, "success");
|
|
1544
|
+
}
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1498
1548
|
const exportWarning = String(response.headers.get("x-pi-studio-export-warning") || "").trim();
|
|
1499
1549
|
const blob = await response.blob();
|
|
1500
1550
|
const headerFilename = parseContentDispositionFilename(response.headers.get("content-disposition"));
|
|
@@ -4030,7 +4080,7 @@
|
|
|
4030
4080
|
const text = typeof reader.result === "string" ? reader.result : "";
|
|
4031
4081
|
setEditorText(text, { preserveScroll: false, preserveSelection: false });
|
|
4032
4082
|
setSourceState({
|
|
4033
|
-
source: "
|
|
4083
|
+
source: "upload",
|
|
4034
4084
|
label: "upload: " + file.name,
|
|
4035
4085
|
path: null,
|
|
4036
4086
|
});
|
package/client/studio.css
CHANGED
|
@@ -569,6 +569,14 @@
|
|
|
569
569
|
text-align: center;
|
|
570
570
|
margin-bottom: 2em;
|
|
571
571
|
}
|
|
572
|
+
|
|
573
|
+
.rendered-markdown #bibliography {
|
|
574
|
+
margin-bottom: 0.75em;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.rendered-markdown #refs {
|
|
578
|
+
margin-top: 0.5em;
|
|
579
|
+
}
|
|
572
580
|
.rendered-markdown #title-block-header .title {
|
|
573
581
|
margin-bottom: 0.25em;
|
|
574
582
|
}
|
|
@@ -758,10 +766,135 @@
|
|
|
758
766
|
max-width: 100%;
|
|
759
767
|
}
|
|
760
768
|
|
|
769
|
+
.rendered-markdown .studio-subfigure-group {
|
|
770
|
+
margin: 1.25em auto;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.rendered-markdown .studio-subfigure-grid {
|
|
774
|
+
display: flex;
|
|
775
|
+
flex-wrap: wrap;
|
|
776
|
+
gap: 1rem;
|
|
777
|
+
justify-content: center;
|
|
778
|
+
align-items: flex-start;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.rendered-markdown .studio-subfigure-entry {
|
|
782
|
+
flex: 1 1 220px;
|
|
783
|
+
margin: 0;
|
|
784
|
+
max-width: 100%;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.rendered-markdown .studio-subfigure-entry img {
|
|
788
|
+
width: 100%;
|
|
789
|
+
height: auto;
|
|
790
|
+
display: block;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.rendered-markdown .studio-subfigure-entry > figcaption,
|
|
794
|
+
.rendered-markdown .studio-subfigure-group > figcaption {
|
|
795
|
+
text-align: center;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.rendered-markdown .studio-subfigure-group > figcaption {
|
|
799
|
+
margin-top: 0.8em;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
.rendered-markdown .studio-subfigure-caption-label,
|
|
803
|
+
.rendered-markdown .studio-figure-caption-label {
|
|
804
|
+
font-weight: 600;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.rendered-markdown .studio-subfigure-caption-label {
|
|
808
|
+
margin-right: 0.35em;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.rendered-markdown .studio-figure-caption-label {
|
|
812
|
+
margin-right: 0.45em;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.rendered-markdown .studio-algorithm-block {
|
|
816
|
+
margin: 1.25em 0;
|
|
817
|
+
border: 1px solid var(--md-codeblock-border);
|
|
818
|
+
border-radius: 10px;
|
|
819
|
+
background: var(--panel-2);
|
|
820
|
+
overflow: hidden;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.rendered-markdown .studio-algorithm-block > figcaption {
|
|
824
|
+
padding: 0.8em 1em 0.65em;
|
|
825
|
+
border-bottom: 1px solid var(--border-muted);
|
|
826
|
+
background: rgba(127, 127, 127, 0.06);
|
|
827
|
+
text-align: left;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
.rendered-markdown .studio-algorithm-caption-label {
|
|
831
|
+
font-weight: 600;
|
|
832
|
+
margin-right: 0.45em;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.rendered-markdown .studio-algorithm-body {
|
|
836
|
+
padding: 0.7em 0.9em 0.85em;
|
|
837
|
+
font-family: var(--font-mono);
|
|
838
|
+
font-size: 0.95em;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.rendered-markdown .studio-algorithm-line {
|
|
842
|
+
display: grid;
|
|
843
|
+
grid-template-columns: 2.6em minmax(0, 1fr);
|
|
844
|
+
gap: 0.8em;
|
|
845
|
+
align-items: baseline;
|
|
846
|
+
padding: 0.08em 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.rendered-markdown .studio-algorithm-line-number {
|
|
850
|
+
color: var(--muted);
|
|
851
|
+
text-align: right;
|
|
852
|
+
font-variant-numeric: tabular-nums;
|
|
853
|
+
user-select: none;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.rendered-markdown .studio-algorithm-line-content {
|
|
857
|
+
min-width: 0;
|
|
858
|
+
padding-left: calc(var(--studio-algorithm-indent, 0) * 1.35em);
|
|
859
|
+
white-space: pre-wrap;
|
|
860
|
+
overflow-wrap: anywhere;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.rendered-markdown .studio-algorithm-line-content math {
|
|
864
|
+
font-size: 1em;
|
|
865
|
+
}
|
|
866
|
+
|
|
761
867
|
.rendered-markdown math {
|
|
762
868
|
font-family: "STIX Two Math", "Cambria Math", "Latin Modern Math", "STIXGeneral", serif;
|
|
763
869
|
}
|
|
764
870
|
|
|
871
|
+
.rendered-markdown .studio-display-equation {
|
|
872
|
+
position: relative;
|
|
873
|
+
margin: 1em 0;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.rendered-markdown .studio-display-equation-body {
|
|
877
|
+
padding-right: 4.5em;
|
|
878
|
+
overflow-x: auto;
|
|
879
|
+
overflow-y: hidden;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.rendered-markdown .studio-display-equation-body math[display="block"] {
|
|
883
|
+
margin: 0;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.rendered-markdown .studio-display-equation-number {
|
|
887
|
+
position: absolute;
|
|
888
|
+
top: 50%;
|
|
889
|
+
right: 0;
|
|
890
|
+
transform: translateY(-50%);
|
|
891
|
+
color: var(--muted);
|
|
892
|
+
font-size: 0.95em;
|
|
893
|
+
line-height: 1;
|
|
894
|
+
white-space: nowrap;
|
|
895
|
+
font-variant-numeric: tabular-nums;
|
|
896
|
+
}
|
|
897
|
+
|
|
765
898
|
.rendered-markdown math[display="block"] {
|
|
766
899
|
display: block;
|
|
767
900
|
margin: 1em 0;
|