@mirror-physics/fractal-ui 0.2.0-next.51 → 0.2.0-next.53
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/dist/index.cjs +90 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +91 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,7 +168,7 @@ Button.displayName = "Button";
|
|
|
168
168
|
// src/components/CodeBlock/CodeBlock.tsx
|
|
169
169
|
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
170
170
|
import CodeMirror from "@uiw/react-codemirror";
|
|
171
|
-
import { HighlightStyle, foldGutter, syntaxHighlighting } from "@codemirror/language";
|
|
171
|
+
import { HighlightStyle, foldGutter, foldService, syntaxHighlighting } from "@codemirror/language";
|
|
172
172
|
import { json } from "@codemirror/lang-json";
|
|
173
173
|
import { EditorView, lineNumbers as lineNumberGutter } from "@codemirror/view";
|
|
174
174
|
import { tags } from "@lezer/highlight";
|
|
@@ -197,22 +197,23 @@ var codeTheme = EditorView.theme({
|
|
|
197
197
|
".cm-lineNumbers .cm-gutterElement": { padding: "0 10px 0 4px" },
|
|
198
198
|
".cm-foldGutter": { width: "14px" },
|
|
199
199
|
".cm-foldGutter .cm-gutterElement": {
|
|
200
|
-
|
|
200
|
+
position: "relative",
|
|
201
|
+
display: "block",
|
|
201
202
|
width: "14px",
|
|
202
|
-
|
|
203
|
-
justifyContent: "flex-end",
|
|
204
|
-
padding: "0 1px 0 0"
|
|
203
|
+
padding: "0"
|
|
205
204
|
},
|
|
206
205
|
".cm-foldGutter .cm-gutterElement > span": {
|
|
206
|
+
position: "absolute",
|
|
207
|
+
top: "2px",
|
|
208
|
+
left: "0",
|
|
207
209
|
display: "inline-flex",
|
|
208
|
-
width: "
|
|
209
|
-
height: "
|
|
210
|
+
width: "14px",
|
|
211
|
+
height: "14px",
|
|
210
212
|
alignItems: "center",
|
|
211
213
|
justifyContent: "center",
|
|
212
214
|
padding: "0",
|
|
213
215
|
color: "var(--fg-tertiary)",
|
|
214
|
-
lineHeight: "
|
|
215
|
-
transform: "translateY(-1px)",
|
|
216
|
+
lineHeight: "14px",
|
|
216
217
|
transition: "color 120ms ease"
|
|
217
218
|
},
|
|
218
219
|
".cm-foldGutter .cm-gutterElement > span:hover": { color: "var(--fg-secondary)" },
|
|
@@ -249,16 +250,87 @@ var fractalHighlightStyle = HighlightStyle.define([
|
|
|
249
250
|
]);
|
|
250
251
|
var jsonSupport = json();
|
|
251
252
|
var JSON_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["json", "jsonl", "ndjson", "geojson"]);
|
|
253
|
+
var JSON_LINES_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["jsonl", "ndjson"]);
|
|
254
|
+
var JSON_LINES_PRETTY_PRINT_THRESHOLD = 160;
|
|
255
|
+
function formatJsonLinesForDisplay(value) {
|
|
256
|
+
return value.split(/\r?\n/).map((line) => {
|
|
257
|
+
const firstContentIndex = line.search(/\S/);
|
|
258
|
+
if (firstContentIndex < 0 || line.length - firstContentIndex <= JSON_LINES_PRETTY_PRINT_THRESHOLD) return line;
|
|
259
|
+
const indentation = line.slice(0, firstContentIndex);
|
|
260
|
+
try {
|
|
261
|
+
const parsed = JSON.parse(line.slice(firstContentIndex));
|
|
262
|
+
if (parsed === null || typeof parsed !== "object") return line;
|
|
263
|
+
return JSON.stringify(parsed, null, 2).split("\n").map((formattedLine) => `${indentation}${formattedLine}`).join("\n");
|
|
264
|
+
} catch {
|
|
265
|
+
return line;
|
|
266
|
+
}
|
|
267
|
+
}).join("\n");
|
|
268
|
+
}
|
|
269
|
+
function findJsonContainerEnd(document2, start) {
|
|
270
|
+
const openingCharacter = document2[start];
|
|
271
|
+
if (openingCharacter !== "{" && openingCharacter !== "[") return null;
|
|
272
|
+
const closingCharacters = [];
|
|
273
|
+
let isInsideString = false;
|
|
274
|
+
let isEscaped = false;
|
|
275
|
+
for (let index = start; index < document2.length; index += 1) {
|
|
276
|
+
const character = document2[index];
|
|
277
|
+
if (isInsideString) {
|
|
278
|
+
if (isEscaped) {
|
|
279
|
+
isEscaped = false;
|
|
280
|
+
} else if (character === "\\") {
|
|
281
|
+
isEscaped = true;
|
|
282
|
+
} else if (character === '"') {
|
|
283
|
+
isInsideString = false;
|
|
284
|
+
}
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (character === '"') {
|
|
288
|
+
isInsideString = true;
|
|
289
|
+
} else if (character === "{") {
|
|
290
|
+
closingCharacters.push("}");
|
|
291
|
+
} else if (character === "[") {
|
|
292
|
+
closingCharacters.push("]");
|
|
293
|
+
} else if (character === "}" || character === "]") {
|
|
294
|
+
if (closingCharacters.pop() !== character) return null;
|
|
295
|
+
if (closingCharacters.length === 0) return index;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
var jsonLinesFoldSupport = foldService.of((state, lineStart, lineEnd) => {
|
|
301
|
+
const line = state.doc.sliceString(lineStart, lineEnd);
|
|
302
|
+
const firstContentIndex = line.search(/\S/);
|
|
303
|
+
if (firstContentIndex < 0) return null;
|
|
304
|
+
const containerStart = lineStart + firstContentIndex;
|
|
305
|
+
const openingCharacter = line[firstContentIndex];
|
|
306
|
+
if (openingCharacter !== "{" && openingCharacter !== "[") return null;
|
|
307
|
+
const document2 = state.doc.toString();
|
|
308
|
+
const containerEnd = findJsonContainerEnd(document2, containerStart);
|
|
309
|
+
if (containerEnd === null || containerEnd - containerStart < 2) return null;
|
|
310
|
+
try {
|
|
311
|
+
const parsed = JSON.parse(document2.slice(containerStart, containerEnd + 1));
|
|
312
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
313
|
+
} catch {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
from: containerStart + 1,
|
|
318
|
+
to: containerEnd
|
|
319
|
+
};
|
|
320
|
+
});
|
|
252
321
|
function resolveLanguage(language, fileName) {
|
|
253
322
|
const normalized = language?.trim().toLowerCase();
|
|
254
323
|
const extension = fileName?.split(/[?#]/, 1)[0].split(".").pop()?.toLowerCase();
|
|
255
324
|
if (normalized && JSON_LANGUAGE_NAMES.has(normalized) || extension && JSON_LANGUAGE_NAMES.has(extension)) {
|
|
256
|
-
|
|
325
|
+
const isJsonLines = Boolean(
|
|
326
|
+
normalized && JSON_LINES_LANGUAGE_NAMES.has(normalized) || extension && JSON_LINES_LANGUAGE_NAMES.has(extension)
|
|
327
|
+
);
|
|
328
|
+
return { label: isJsonLines ? "JSONL" : "JSON", support: jsonSupport, isJsonLines };
|
|
257
329
|
}
|
|
258
330
|
if (normalized && !["text", "txt", "plaintext", "plain"].includes(normalized)) {
|
|
259
|
-
return { label: language?.trim(), support: null };
|
|
331
|
+
return { label: language?.trim(), support: null, isJsonLines: false };
|
|
260
332
|
}
|
|
261
|
-
return { support: null };
|
|
333
|
+
return { support: null, isJsonLines: false };
|
|
262
334
|
}
|
|
263
335
|
function CodeBlock({
|
|
264
336
|
value,
|
|
@@ -280,6 +352,10 @@ function CodeBlock({
|
|
|
280
352
|
...props
|
|
281
353
|
}) {
|
|
282
354
|
const resolvedLanguage = useMemo(() => resolveLanguage(language, fileName), [fileName, language]);
|
|
355
|
+
const displayValue = useMemo(
|
|
356
|
+
() => !editable && resolvedLanguage.isJsonLines ? formatJsonLinesForDisplay(value) : value,
|
|
357
|
+
[editable, resolvedLanguage.isJsonLines, value]
|
|
358
|
+
);
|
|
283
359
|
const [copyStatus, setCopyStatus] = useState2("idle");
|
|
284
360
|
const copyResetTimer = useRef(null);
|
|
285
361
|
useEffect(() => () => {
|
|
@@ -289,9 +365,10 @@ function CodeBlock({
|
|
|
289
365
|
codeTheme,
|
|
290
366
|
syntaxHighlighting(fractalHighlightStyle),
|
|
291
367
|
...resolvedLanguage.support ? [resolvedLanguage.support] : [],
|
|
368
|
+
...resolvedLanguage.isJsonLines ? [jsonLinesFoldSupport] : [],
|
|
292
369
|
...lineNumbers ? [foldGutter(), lineNumberGutter()] : [],
|
|
293
370
|
...wrap ? [EditorView.lineWrapping] : []
|
|
294
|
-
], [lineNumbers, resolvedLanguage.support, wrap]);
|
|
371
|
+
], [lineNumbers, resolvedLanguage.isJsonLines, resolvedLanguage.support, wrap]);
|
|
295
372
|
const languageLabel = resolvedLanguage.label;
|
|
296
373
|
const toolbarLabel = label ?? fileName ?? languageLabel;
|
|
297
374
|
const surfaceLabel = ariaLabel ?? (fileName ? `${fileName} ${editable ? "editor" : "viewer"}` : `Code ${editable ? "editor" : "viewer"}`);
|
|
@@ -341,7 +418,7 @@ function CodeBlock({
|
|
|
341
418
|
CodeMirror,
|
|
342
419
|
{
|
|
343
420
|
"aria-label": surfaceLabel,
|
|
344
|
-
value,
|
|
421
|
+
value: displayValue,
|
|
345
422
|
editable,
|
|
346
423
|
readOnly: !editable,
|
|
347
424
|
onChange,
|