pi-studio 0.5.35 → 0.5.37

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.
@@ -0,0 +1,370 @@
1
+ export function normalizeStudioAnnotationText(text) {
2
+ return String(text || "")
3
+ .replace(/\r\n/g, "\n")
4
+ .replace(/\s*\n\s*/g, " ")
5
+ .replace(/\s{2,}/g, " ")
6
+ .trim();
7
+ }
8
+
9
+ export function advancePastStudioInlineBacktickSpan(source, startIndex) {
10
+ const text = String(source || "");
11
+ let fenceLength = 1;
12
+ while (text[startIndex + fenceLength] === "`") fenceLength += 1;
13
+
14
+ let index = startIndex + fenceLength;
15
+ while (index < text.length) {
16
+ const ch = text[index];
17
+ if (ch === "\\") {
18
+ index = Math.min(text.length, index + 2);
19
+ continue;
20
+ }
21
+ if (ch === "`") {
22
+ let runLength = 1;
23
+ while (text[index + runLength] === "`") runLength += 1;
24
+ if (runLength === fenceLength) {
25
+ return index + runLength;
26
+ }
27
+ index += runLength;
28
+ continue;
29
+ }
30
+ if (ch === "\n") {
31
+ return index + 1;
32
+ }
33
+ index += 1;
34
+ }
35
+
36
+ return text.length;
37
+ }
38
+
39
+ export function readStudioInlineAnnotationMarkerAt(source, startIndex) {
40
+ const text = String(source || "");
41
+ if (startIndex < 0 || startIndex + 4 > text.length) return null;
42
+ if (text[startIndex] !== "[" || text.slice(startIndex, startIndex + 4).toLowerCase() !== "[an:") {
43
+ return null;
44
+ }
45
+
46
+ let index = startIndex + 4;
47
+ while (index < text.length && /\s/.test(text[index])) index += 1;
48
+ const bodyStart = index;
49
+ let squareDepth = 0;
50
+
51
+ while (index < text.length) {
52
+ const ch = text[index];
53
+ if (ch === "\\") {
54
+ index = Math.min(text.length, index + 2);
55
+ continue;
56
+ }
57
+ if (ch === "`") {
58
+ index = advancePastStudioInlineBacktickSpan(text, index);
59
+ continue;
60
+ }
61
+ if (ch === "[") {
62
+ squareDepth += 1;
63
+ index += 1;
64
+ continue;
65
+ }
66
+ if (ch === "]") {
67
+ if (squareDepth === 0) {
68
+ const end = index + 1;
69
+ return {
70
+ start: startIndex,
71
+ end,
72
+ raw: text.slice(startIndex, end),
73
+ body: text.slice(bodyStart, index),
74
+ };
75
+ }
76
+ squareDepth -= 1;
77
+ index += 1;
78
+ continue;
79
+ }
80
+ index += 1;
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ export function collectStudioInlineAnnotationMarkers(text) {
87
+ const source = String(text || "");
88
+ const markers = [];
89
+ let index = 0;
90
+
91
+ while (index < source.length) {
92
+ const ch = source[index];
93
+ if (ch === "\\") {
94
+ index = Math.min(source.length, index + 2);
95
+ continue;
96
+ }
97
+ if (ch === "`") {
98
+ index = advancePastStudioInlineBacktickSpan(source, index);
99
+ continue;
100
+ }
101
+ if (ch === "[" && source.slice(index, index + 4).toLowerCase() === "[an:") {
102
+ const marker = readStudioInlineAnnotationMarkerAt(source, index);
103
+ if (marker) {
104
+ markers.push(marker);
105
+ index = marker.end;
106
+ continue;
107
+ }
108
+ }
109
+ index += 1;
110
+ }
111
+
112
+ return markers;
113
+ }
114
+
115
+ export function replaceStudioInlineAnnotationMarkers(text, annotationReplacer, textReplacer) {
116
+ const source = String(text || "");
117
+ const markers = collectStudioInlineAnnotationMarkers(source);
118
+ const replaceAnnotation = typeof annotationReplacer === "function"
119
+ ? annotationReplacer
120
+ : function(marker) { return marker.raw; };
121
+ const replaceText = typeof textReplacer === "function"
122
+ ? textReplacer
123
+ : function(segment) { return segment; };
124
+
125
+ if (markers.length === 0) {
126
+ return replaceText(source);
127
+ }
128
+
129
+ let out = "";
130
+ let lastIndex = 0;
131
+ markers.forEach(function(marker) {
132
+ if (marker.start > lastIndex) {
133
+ out += String(replaceText(source.slice(lastIndex, marker.start)) ?? "");
134
+ }
135
+ out += String(replaceAnnotation(marker) ?? "");
136
+ lastIndex = marker.end;
137
+ });
138
+ if (lastIndex < source.length) {
139
+ out += String(replaceText(source.slice(lastIndex)) ?? "");
140
+ }
141
+ return out;
142
+ }
143
+
144
+ export function transformStudioMarkdownOutsideFences(text, plainTransformer) {
145
+ const source = String(text || "").replace(/\r\n/g, "\n");
146
+ if (!source) return source;
147
+
148
+ const transformPlain = typeof plainTransformer === "function"
149
+ ? plainTransformer
150
+ : function(segment) { return segment; };
151
+ const lines = source.split("\n");
152
+ const out = [];
153
+ let plainBuffer = [];
154
+ let inFence = false;
155
+ let fenceChar = null;
156
+ let fenceLength = 0;
157
+
158
+ function flushPlain() {
159
+ if (plainBuffer.length === 0) return;
160
+ const transformed = transformPlain(plainBuffer.join("\n"));
161
+ out.push(typeof transformed === "string" ? transformed : String(transformed ?? ""));
162
+ plainBuffer = [];
163
+ }
164
+
165
+ lines.forEach(function(line) {
166
+ const trimmed = line.trimStart();
167
+ const fenceMatch = trimmed.match(/^(`{3,}|~{3,})/);
168
+ if (fenceMatch) {
169
+ const marker = fenceMatch[1] || "";
170
+ const markerChar = marker.charAt(0);
171
+ const markerLength = marker.length;
172
+
173
+ if (!inFence) {
174
+ flushPlain();
175
+ inFence = true;
176
+ fenceChar = markerChar;
177
+ fenceLength = markerLength;
178
+ out.push(line);
179
+ return;
180
+ }
181
+
182
+ if (fenceChar === markerChar && markerLength >= fenceLength) {
183
+ inFence = false;
184
+ fenceChar = null;
185
+ fenceLength = 0;
186
+ }
187
+
188
+ out.push(line);
189
+ return;
190
+ }
191
+
192
+ if (inFence) {
193
+ out.push(line);
194
+ } else {
195
+ plainBuffer.push(line);
196
+ }
197
+ });
198
+
199
+ flushPlain();
200
+ return out.join("\n");
201
+ }
202
+
203
+ export function hasStudioMarkdownAnnotationMarkers(text) {
204
+ let found = false;
205
+ transformStudioMarkdownOutsideFences(text, function(segment) {
206
+ if (!found && collectStudioInlineAnnotationMarkers(segment).length > 0) {
207
+ found = true;
208
+ }
209
+ return segment;
210
+ });
211
+ return found;
212
+ }
213
+
214
+ export function isStudioAnnotationWordChar(ch) {
215
+ return typeof ch === "string" && /[A-Za-z0-9]/.test(ch);
216
+ }
217
+
218
+ export function readStudioInlineMarkdownLinkAt(source, startIndex) {
219
+ const text = String(source || "");
220
+ if (text[startIndex] !== "[") return null;
221
+
222
+ let index = startIndex + 1;
223
+ let squareDepth = 0;
224
+ while (index < text.length) {
225
+ const ch = text[index];
226
+ if (ch === "\\") {
227
+ index = Math.min(text.length, index + 2);
228
+ continue;
229
+ }
230
+ if (ch === "`") {
231
+ index = advancePastStudioInlineBacktickSpan(text, index);
232
+ continue;
233
+ }
234
+ if (ch === "[") {
235
+ squareDepth += 1;
236
+ index += 1;
237
+ continue;
238
+ }
239
+ if (ch === "]") {
240
+ if (squareDepth === 0) break;
241
+ squareDepth -= 1;
242
+ index += 1;
243
+ continue;
244
+ }
245
+ if (ch === "\n") return null;
246
+ index += 1;
247
+ }
248
+
249
+ if (index >= text.length || text[index] !== "]" || text[index + 1] !== "(") return null;
250
+
251
+ index += 2;
252
+ let parenDepth = 0;
253
+ while (index < text.length) {
254
+ const ch = text[index];
255
+ if (ch === "\\") {
256
+ index = Math.min(text.length, index + 2);
257
+ continue;
258
+ }
259
+ if (ch === "`") {
260
+ index = advancePastStudioInlineBacktickSpan(text, index);
261
+ continue;
262
+ }
263
+ if (ch === "(") {
264
+ parenDepth += 1;
265
+ index += 1;
266
+ continue;
267
+ }
268
+ if (ch === ")") {
269
+ if (parenDepth === 0) {
270
+ return {
271
+ type: "literal",
272
+ raw: text.slice(startIndex, index + 1),
273
+ end: index + 1,
274
+ };
275
+ }
276
+ parenDepth -= 1;
277
+ index += 1;
278
+ continue;
279
+ }
280
+ if (ch === "\n") return null;
281
+ index += 1;
282
+ }
283
+
284
+ return null;
285
+ }
286
+
287
+ export function readStudioAnnotationDelimitedTokenAt(source, startIndex, open, close, allowNewlines) {
288
+ const text = String(source || "");
289
+ if (text.slice(startIndex, startIndex + open.length) !== open) return null;
290
+
291
+ let index = startIndex + open.length;
292
+ while (index < text.length) {
293
+ const ch = text[index];
294
+ if (!allowNewlines && ch === "\n") return null;
295
+ if (ch === "\\") {
296
+ index = Math.min(text.length, index + 2);
297
+ continue;
298
+ }
299
+ if (text.slice(index, index + close.length) === close) {
300
+ return {
301
+ type: "math",
302
+ raw: text.slice(startIndex, index + close.length),
303
+ end: index + close.length,
304
+ };
305
+ }
306
+ index += 1;
307
+ }
308
+
309
+ return null;
310
+ }
311
+
312
+ export function readStudioInlineMathTokenAt(source, startIndex) {
313
+ const text = String(source || "");
314
+ if (text[startIndex] === "\\" && text[startIndex + 1] === "(") {
315
+ return readStudioAnnotationDelimitedTokenAt(text, startIndex, "\\(", "\\)", true);
316
+ }
317
+ if (text[startIndex] === "\\" && text[startIndex + 1] === "[") {
318
+ return readStudioAnnotationDelimitedTokenAt(text, startIndex, "\\[", "\\]", true);
319
+ }
320
+ if (text[startIndex] === "$" && text[startIndex + 1] === "$") {
321
+ return readStudioAnnotationDelimitedTokenAt(text, startIndex, "$$", "$$", true);
322
+ }
323
+ if (text[startIndex] === "$" && text[startIndex + 1] !== "$" && text[startIndex + 1] && !/\s/.test(text[startIndex + 1])) {
324
+ const token = readStudioAnnotationDelimitedTokenAt(text, startIndex, "$", "$", false);
325
+ if (token && token.raw.length > 2) return token;
326
+ }
327
+ return null;
328
+ }
329
+
330
+ export function readStudioBareUrlTokenAt(source, startIndex) {
331
+ const text = String(source || "").slice(startIndex);
332
+ const match = text.match(/^https?:\/\/[^\s<]+/i);
333
+ if (!match) return null;
334
+ return {
335
+ type: "literal",
336
+ raw: match[0],
337
+ end: startIndex + match[0].length,
338
+ };
339
+ }
340
+
341
+ export function readStudioAnnotationProtectedTokenAt(source, startIndex) {
342
+ const text = String(source || "");
343
+ if (startIndex < 0 || startIndex >= text.length) return null;
344
+
345
+ if (text[startIndex] === "`") {
346
+ const end = advancePastStudioInlineBacktickSpan(text, startIndex);
347
+ return {
348
+ type: "code",
349
+ raw: text.slice(startIndex, end),
350
+ end,
351
+ };
352
+ }
353
+
354
+ const linkToken = text[startIndex] === "["
355
+ ? readStudioInlineMarkdownLinkAt(text, startIndex)
356
+ : null;
357
+ if (linkToken) return linkToken;
358
+
359
+ const mathToken = (text[startIndex] === "$" || text[startIndex] === "\\")
360
+ ? readStudioInlineMathTokenAt(text, startIndex)
361
+ : null;
362
+ if (mathToken) return mathToken;
363
+
364
+ const urlToken = text[startIndex].toLowerCase() === "h"
365
+ ? readStudioBareUrlTokenAt(text, startIndex)
366
+ : null;
367
+ if (urlToken) return urlToken;
368
+
369
+ return null;
370
+ }
@@ -0,0 +1,34 @@
1
+ export function escapeStudioPdfLatexTextFragment(text) {
2
+ let out = "";
3
+ const source = String(text || "");
4
+
5
+ for (const ch of source) {
6
+ if (ch === "\\") {
7
+ out += "\\textbackslash{}";
8
+ } else if (ch === "{") {
9
+ out += "\\{";
10
+ } else if (ch === "}") {
11
+ out += "\\}";
12
+ } else if (ch === "%") {
13
+ out += "\\%";
14
+ } else if (ch === "#") {
15
+ out += "\\#";
16
+ } else if (ch === "$") {
17
+ out += "\\$";
18
+ } else if (ch === "&") {
19
+ out += "\\&";
20
+ } else if (ch === "_") {
21
+ out += "\\_";
22
+ } else if (ch === "~") {
23
+ out += "\\textasciitilde{}";
24
+ } else if (ch === "`") {
25
+ out += "\\textasciigrave{}";
26
+ } else if (ch === "^") {
27
+ out += "\\textasciicircum{}";
28
+ } else {
29
+ out += ch;
30
+ }
31
+ }
32
+
33
+ return out;
34
+ }