pi-studio 0.5.34 → 0.5.36
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 +16 -0
- package/client/studio-annotation-helpers.js +543 -0
- package/client/studio-client.js +61 -132
- package/client/studio.css +8 -0
- package/index.ts +256 -90
- package/package.json +5 -1
- package/shared/studio-annotation-scanner.js +370 -0
- package/shared/studio-pdf-escape.js +34 -0
package/client/studio-client.js
CHANGED
|
@@ -241,8 +241,11 @@
|
|
|
241
241
|
let responseHighlightEnabled = false;
|
|
242
242
|
let editorHighlightRenderRaf = null;
|
|
243
243
|
let annotationsEnabled = true;
|
|
244
|
-
const ANNOTATION_MARKER_REGEX = /\[an:\s*([^\]]+?)\]/gi;
|
|
245
244
|
const PREVIEW_ANNOTATION_PLACEHOLDER_PREFIX = "PISTUDIOANNOT";
|
|
245
|
+
const annotationHelpers = globalThis.PiStudioAnnotationHelpers;
|
|
246
|
+
if (!annotationHelpers || typeof annotationHelpers.collectInlineAnnotationMarkers !== "function") {
|
|
247
|
+
throw new Error("Studio annotation helpers failed to load.");
|
|
248
|
+
}
|
|
246
249
|
const EMPTY_OVERLAY_LINE = "\u200b";
|
|
247
250
|
const MERMAID_CDN_URL = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
248
251
|
const MATHJAX_CDN_URL = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js";
|
|
@@ -1163,15 +1166,11 @@
|
|
|
1163
1166
|
}
|
|
1164
1167
|
|
|
1165
1168
|
function hasAnnotationMarkers(text) {
|
|
1166
|
-
|
|
1167
|
-
ANNOTATION_MARKER_REGEX.lastIndex = 0;
|
|
1168
|
-
const hasMarker = ANNOTATION_MARKER_REGEX.test(source);
|
|
1169
|
-
ANNOTATION_MARKER_REGEX.lastIndex = 0;
|
|
1170
|
-
return hasMarker;
|
|
1169
|
+
return annotationHelpers.hasAnnotationMarkers(text);
|
|
1171
1170
|
}
|
|
1172
1171
|
|
|
1173
1172
|
function stripAnnotationMarkers(text) {
|
|
1174
|
-
return
|
|
1173
|
+
return annotationHelpers.stripAnnotationMarkers(text);
|
|
1175
1174
|
}
|
|
1176
1175
|
|
|
1177
1176
|
function prepareEditorTextForSend(text) {
|
|
@@ -1184,78 +1183,8 @@
|
|
|
1184
1183
|
return annotationsEnabled ? raw : stripAnnotationMarkers(raw);
|
|
1185
1184
|
}
|
|
1186
1185
|
|
|
1187
|
-
function normalizePreviewAnnotationLabel(text) {
|
|
1188
|
-
return String(text || "")
|
|
1189
|
-
.replace(/\r\n/g, "\n")
|
|
1190
|
-
.replace(/\s*\n\s*/g, " ")
|
|
1191
|
-
.replace(/\s{2,}/g, " ")
|
|
1192
|
-
.trim();
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
1186
|
function prepareMarkdownForPandocPreview(markdown) {
|
|
1196
|
-
|
|
1197
|
-
const placeholders = [];
|
|
1198
|
-
if (!source) {
|
|
1199
|
-
return { markdown: source, placeholders: placeholders };
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
const lines = source.split("\n");
|
|
1203
|
-
const out = [];
|
|
1204
|
-
let plainBuffer = [];
|
|
1205
|
-
let inFence = false;
|
|
1206
|
-
let fenceChar = null;
|
|
1207
|
-
let fenceLength = 0;
|
|
1208
|
-
|
|
1209
|
-
function flushPlain() {
|
|
1210
|
-
if (plainBuffer.length === 0) return;
|
|
1211
|
-
const segment = plainBuffer.join("\n").replace(/\[an:\s*([^\]]+?)\]/gi, function(_match, markerText) {
|
|
1212
|
-
const label = normalizePreviewAnnotationLabel(markerText);
|
|
1213
|
-
if (!label) return "";
|
|
1214
|
-
const token = PREVIEW_ANNOTATION_PLACEHOLDER_PREFIX + placeholders.length + "TOKEN";
|
|
1215
|
-
placeholders.push({ token: token, text: label, title: "[an: " + label + "]" });
|
|
1216
|
-
return token;
|
|
1217
|
-
});
|
|
1218
|
-
out.push(segment);
|
|
1219
|
-
plainBuffer = [];
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
for (const line of lines) {
|
|
1223
|
-
const trimmed = line.trimStart();
|
|
1224
|
-
const fenceMatch = trimmed.match(/^(`{3,}|~{3,})/);
|
|
1225
|
-
|
|
1226
|
-
if (fenceMatch) {
|
|
1227
|
-
const marker = fenceMatch[1] || "";
|
|
1228
|
-
const markerChar = marker.charAt(0);
|
|
1229
|
-
const markerLength = marker.length;
|
|
1230
|
-
|
|
1231
|
-
if (!inFence) {
|
|
1232
|
-
flushPlain();
|
|
1233
|
-
inFence = true;
|
|
1234
|
-
fenceChar = markerChar;
|
|
1235
|
-
fenceLength = markerLength;
|
|
1236
|
-
out.push(line);
|
|
1237
|
-
continue;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
if (fenceChar === markerChar && markerLength >= fenceLength) {
|
|
1241
|
-
inFence = false;
|
|
1242
|
-
fenceChar = null;
|
|
1243
|
-
fenceLength = 0;
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
out.push(line);
|
|
1247
|
-
continue;
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
if (inFence) {
|
|
1251
|
-
out.push(line);
|
|
1252
|
-
} else {
|
|
1253
|
-
plainBuffer.push(line);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
flushPlain();
|
|
1258
|
-
return { markdown: out.join("\n"), placeholders: placeholders };
|
|
1187
|
+
return annotationHelpers.prepareMarkdownForPandocPreview(markdown, PREVIEW_ANNOTATION_PLACEHOLDER_PREFIX);
|
|
1259
1188
|
}
|
|
1260
1189
|
|
|
1261
1190
|
function wrapAsFencedCodeBlock(text, language) {
|
|
@@ -1774,8 +1703,9 @@
|
|
|
1774
1703
|
if (entry) {
|
|
1775
1704
|
const markerEl = document.createElement("span");
|
|
1776
1705
|
markerEl.className = "annotation-preview-marker";
|
|
1777
|
-
|
|
1778
|
-
markerEl.title = typeof entry.title === "string" ? entry.title :
|
|
1706
|
+
const markerText = typeof entry.text === "string" ? entry.text : token;
|
|
1707
|
+
markerEl.title = typeof entry.title === "string" ? entry.title : markerText;
|
|
1708
|
+
setAnnotationPreviewMarkerContent(markerEl, markerText);
|
|
1779
1709
|
fragment.appendChild(markerEl);
|
|
1780
1710
|
} else {
|
|
1781
1711
|
fragment.appendChild(document.createTextNode(token));
|
|
@@ -1819,33 +1749,28 @@
|
|
|
1819
1749
|
for (const textNode of textNodes) {
|
|
1820
1750
|
const text = typeof textNode.nodeValue === "string" ? textNode.nodeValue : "";
|
|
1821
1751
|
if (!text) continue;
|
|
1822
|
-
|
|
1823
|
-
if (
|
|
1824
|
-
ANNOTATION_MARKER_REGEX.lastIndex = 0;
|
|
1752
|
+
const markers = annotationHelpers.collectInlineAnnotationMarkers(text);
|
|
1753
|
+
if (markers.length === 0) continue;
|
|
1825
1754
|
|
|
1826
1755
|
const fragment = document.createDocumentFragment();
|
|
1827
1756
|
let lastIndex = 0;
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
if (start > lastIndex) {
|
|
1833
|
-
fragment.appendChild(document.createTextNode(text.slice(lastIndex, start)));
|
|
1757
|
+
markers.forEach(function(marker) {
|
|
1758
|
+
const token = marker.raw || "";
|
|
1759
|
+
if (marker.start > lastIndex) {
|
|
1760
|
+
fragment.appendChild(document.createTextNode(text.slice(lastIndex, marker.start)));
|
|
1834
1761
|
}
|
|
1835
1762
|
|
|
1836
1763
|
if (mode === "highlight") {
|
|
1837
1764
|
const markerEl = document.createElement("span");
|
|
1838
1765
|
markerEl.className = "annotation-preview-marker";
|
|
1839
|
-
|
|
1766
|
+
const markerText = annotationHelpers.normalizePreviewAnnotationLabel(marker.body) || token;
|
|
1840
1767
|
markerEl.title = token;
|
|
1768
|
+
setAnnotationPreviewMarkerContent(markerEl, markerText);
|
|
1841
1769
|
fragment.appendChild(markerEl);
|
|
1842
1770
|
}
|
|
1843
1771
|
|
|
1844
|
-
lastIndex =
|
|
1845
|
-
|
|
1846
|
-
ANNOTATION_MARKER_REGEX.lastIndex += 1;
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1772
|
+
lastIndex = marker.end;
|
|
1773
|
+
});
|
|
1849
1774
|
|
|
1850
1775
|
if (lastIndex < text.length) {
|
|
1851
1776
|
fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
@@ -2732,50 +2657,44 @@
|
|
|
2732
2657
|
return "<span class='" + className + "'>" + escapeHtml(String(text || "")) + "</span>";
|
|
2733
2658
|
}
|
|
2734
2659
|
|
|
2735
|
-
function
|
|
2660
|
+
function buildAnnotationPreviewMarkerHtml(text, title) {
|
|
2736
2661
|
const titleAttr = title ? " title='" + escapeHtml(String(title)) + "'" : "";
|
|
2737
|
-
|
|
2662
|
+
const rendered = typeof annotationHelpers.renderPreviewAnnotationHtml === "function"
|
|
2663
|
+
? annotationHelpers.renderPreviewAnnotationHtml(text)
|
|
2664
|
+
: escapeHtml(String(text || ""));
|
|
2665
|
+
return "<span class='annotation-preview-marker'" + titleAttr + ">" + rendered + "</span>";
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
function setAnnotationPreviewMarkerContent(markerEl, text) {
|
|
2669
|
+
if (!markerEl) return;
|
|
2670
|
+
const rendered = typeof annotationHelpers.renderPreviewAnnotationHtml === "function"
|
|
2671
|
+
? annotationHelpers.renderPreviewAnnotationHtml(text)
|
|
2672
|
+
: escapeHtml(String(text || ""));
|
|
2673
|
+
markerEl.innerHTML = rendered;
|
|
2738
2674
|
}
|
|
2739
2675
|
|
|
2740
2676
|
function highlightInlineAnnotations(text, mode) {
|
|
2741
2677
|
const source = String(text || "");
|
|
2742
2678
|
const renderMode = mode === "preview" ? "preview" : "overlay";
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
if (renderMode === "preview") {
|
|
2758
|
-
out += wrapHighlightWithTitle("annotation-preview-marker", markerText || token, token);
|
|
2759
|
-
} else {
|
|
2760
|
-
out += wrapHighlight(annotationsEnabled ? "hl-annotation" : "hl-annotation-muted", token);
|
|
2761
|
-
}
|
|
2762
|
-
lastIndex = start + token.length;
|
|
2763
|
-
if (token.length === 0) {
|
|
2764
|
-
ANNOTATION_MARKER_REGEX.lastIndex += 1;
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
|
-
ANNOTATION_MARKER_REGEX.lastIndex = 0;
|
|
2769
|
-
if (lastIndex < source.length) {
|
|
2770
|
-
out += escapeHtml(source.slice(lastIndex));
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
|
-
return out;
|
|
2679
|
+
return annotationHelpers.replaceInlineAnnotationMarkers(
|
|
2680
|
+
source,
|
|
2681
|
+
function(marker) {
|
|
2682
|
+
const token = marker.raw || "";
|
|
2683
|
+
const markerText = annotationHelpers.normalizePreviewAnnotationLabel(marker.body) || token;
|
|
2684
|
+
if (renderMode === "preview") {
|
|
2685
|
+
return buildAnnotationPreviewMarkerHtml(markerText, token);
|
|
2686
|
+
}
|
|
2687
|
+
return wrapHighlight(annotationsEnabled ? "hl-annotation" : "hl-annotation-muted", token);
|
|
2688
|
+
},
|
|
2689
|
+
function(segment) {
|
|
2690
|
+
return escapeHtml(segment);
|
|
2691
|
+
},
|
|
2692
|
+
);
|
|
2774
2693
|
}
|
|
2775
2694
|
|
|
2776
|
-
function
|
|
2695
|
+
function highlightInlineMarkdownWithoutAnnotations(text) {
|
|
2777
2696
|
const source = String(text || "");
|
|
2778
|
-
const pattern = /(\x60[^\x60]*\x60)|(\[[^\]]+\]\([^)]+\))
|
|
2697
|
+
const pattern = /(\x60[^\x60]*\x60)|(\[[^\]]+\]\([^)]+\))/g;
|
|
2779
2698
|
let lastIndex = 0;
|
|
2780
2699
|
let out = "";
|
|
2781
2700
|
|
|
@@ -2798,8 +2717,6 @@
|
|
|
2798
2717
|
} else {
|
|
2799
2718
|
out += escapeHtml(token);
|
|
2800
2719
|
}
|
|
2801
|
-
} else if (match[3]) {
|
|
2802
|
-
out += highlightInlineAnnotations(token);
|
|
2803
2720
|
} else {
|
|
2804
2721
|
out += escapeHtml(token);
|
|
2805
2722
|
}
|
|
@@ -2814,6 +2731,18 @@
|
|
|
2814
2731
|
return out;
|
|
2815
2732
|
}
|
|
2816
2733
|
|
|
2734
|
+
function highlightInlineMarkdown(text) {
|
|
2735
|
+
return annotationHelpers.replaceInlineAnnotationMarkers(
|
|
2736
|
+
String(text || ""),
|
|
2737
|
+
function(marker) {
|
|
2738
|
+
return highlightInlineAnnotations(marker.raw || "");
|
|
2739
|
+
},
|
|
2740
|
+
function(segment) {
|
|
2741
|
+
return highlightInlineMarkdownWithoutAnnotations(segment);
|
|
2742
|
+
},
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2817
2746
|
function normalizeFenceLanguage(info) {
|
|
2818
2747
|
const raw = String(info || "").trim();
|
|
2819
2748
|
if (!raw) return "";
|
package/client/studio.css
CHANGED
|
@@ -523,6 +523,14 @@
|
|
|
523
523
|
vertical-align: baseline;
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
.annotation-preview-marker code {
|
|
527
|
+
font-family: var(--mono-font, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
528
|
+
font-size: 0.95em;
|
|
529
|
+
background: rgba(0, 0, 0, 0.08);
|
|
530
|
+
border-radius: 3px;
|
|
531
|
+
padding: 0 0.2em;
|
|
532
|
+
}
|
|
533
|
+
|
|
526
534
|
#sourcePreview {
|
|
527
535
|
flex: 1 1 auto;
|
|
528
536
|
min-height: 0;
|