@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 CHANGED
@@ -306,22 +306,23 @@ var codeTheme = import_view.EditorView.theme({
306
306
  ".cm-lineNumbers .cm-gutterElement": { padding: "0 10px 0 4px" },
307
307
  ".cm-foldGutter": { width: "14px" },
308
308
  ".cm-foldGutter .cm-gutterElement": {
309
- display: "flex",
309
+ position: "relative",
310
+ display: "block",
310
311
  width: "14px",
311
- alignItems: "center",
312
- justifyContent: "flex-end",
313
- padding: "0 1px 0 0"
312
+ padding: "0"
314
313
  },
315
314
  ".cm-foldGutter .cm-gutterElement > span": {
315
+ position: "absolute",
316
+ top: "2px",
317
+ left: "0",
316
318
  display: "inline-flex",
317
- width: "12px",
318
- height: "100%",
319
+ width: "14px",
320
+ height: "14px",
319
321
  alignItems: "center",
320
322
  justifyContent: "center",
321
323
  padding: "0",
322
324
  color: "var(--fg-tertiary)",
323
- lineHeight: "1",
324
- transform: "translateY(-1px)",
325
+ lineHeight: "14px",
325
326
  transition: "color 120ms ease"
326
327
  },
327
328
  ".cm-foldGutter .cm-gutterElement > span:hover": { color: "var(--fg-secondary)" },
@@ -358,16 +359,87 @@ var fractalHighlightStyle = import_language.HighlightStyle.define([
358
359
  ]);
359
360
  var jsonSupport = (0, import_lang_json.json)();
360
361
  var JSON_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["json", "jsonl", "ndjson", "geojson"]);
362
+ var JSON_LINES_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["jsonl", "ndjson"]);
363
+ var JSON_LINES_PRETTY_PRINT_THRESHOLD = 160;
364
+ function formatJsonLinesForDisplay(value) {
365
+ return value.split(/\r?\n/).map((line) => {
366
+ const firstContentIndex = line.search(/\S/);
367
+ if (firstContentIndex < 0 || line.length - firstContentIndex <= JSON_LINES_PRETTY_PRINT_THRESHOLD) return line;
368
+ const indentation = line.slice(0, firstContentIndex);
369
+ try {
370
+ const parsed = JSON.parse(line.slice(firstContentIndex));
371
+ if (parsed === null || typeof parsed !== "object") return line;
372
+ return JSON.stringify(parsed, null, 2).split("\n").map((formattedLine) => `${indentation}${formattedLine}`).join("\n");
373
+ } catch {
374
+ return line;
375
+ }
376
+ }).join("\n");
377
+ }
378
+ function findJsonContainerEnd(document2, start) {
379
+ const openingCharacter = document2[start];
380
+ if (openingCharacter !== "{" && openingCharacter !== "[") return null;
381
+ const closingCharacters = [];
382
+ let isInsideString = false;
383
+ let isEscaped = false;
384
+ for (let index = start; index < document2.length; index += 1) {
385
+ const character = document2[index];
386
+ if (isInsideString) {
387
+ if (isEscaped) {
388
+ isEscaped = false;
389
+ } else if (character === "\\") {
390
+ isEscaped = true;
391
+ } else if (character === '"') {
392
+ isInsideString = false;
393
+ }
394
+ continue;
395
+ }
396
+ if (character === '"') {
397
+ isInsideString = true;
398
+ } else if (character === "{") {
399
+ closingCharacters.push("}");
400
+ } else if (character === "[") {
401
+ closingCharacters.push("]");
402
+ } else if (character === "}" || character === "]") {
403
+ if (closingCharacters.pop() !== character) return null;
404
+ if (closingCharacters.length === 0) return index;
405
+ }
406
+ }
407
+ return null;
408
+ }
409
+ var jsonLinesFoldSupport = import_language.foldService.of((state, lineStart, lineEnd) => {
410
+ const line = state.doc.sliceString(lineStart, lineEnd);
411
+ const firstContentIndex = line.search(/\S/);
412
+ if (firstContentIndex < 0) return null;
413
+ const containerStart = lineStart + firstContentIndex;
414
+ const openingCharacter = line[firstContentIndex];
415
+ if (openingCharacter !== "{" && openingCharacter !== "[") return null;
416
+ const document2 = state.doc.toString();
417
+ const containerEnd = findJsonContainerEnd(document2, containerStart);
418
+ if (containerEnd === null || containerEnd - containerStart < 2) return null;
419
+ try {
420
+ const parsed = JSON.parse(document2.slice(containerStart, containerEnd + 1));
421
+ if (parsed === null || typeof parsed !== "object") return null;
422
+ } catch {
423
+ return null;
424
+ }
425
+ return {
426
+ from: containerStart + 1,
427
+ to: containerEnd
428
+ };
429
+ });
361
430
  function resolveLanguage(language, fileName) {
362
431
  const normalized = language?.trim().toLowerCase();
363
432
  const extension = fileName?.split(/[?#]/, 1)[0].split(".").pop()?.toLowerCase();
364
433
  if (normalized && JSON_LANGUAGE_NAMES.has(normalized) || extension && JSON_LANGUAGE_NAMES.has(extension)) {
365
- return { label: normalized === "jsonl" || extension === "jsonl" ? "JSONL" : "JSON", support: jsonSupport };
434
+ const isJsonLines = Boolean(
435
+ normalized && JSON_LINES_LANGUAGE_NAMES.has(normalized) || extension && JSON_LINES_LANGUAGE_NAMES.has(extension)
436
+ );
437
+ return { label: isJsonLines ? "JSONL" : "JSON", support: jsonSupport, isJsonLines };
366
438
  }
367
439
  if (normalized && !["text", "txt", "plaintext", "plain"].includes(normalized)) {
368
- return { label: language?.trim(), support: null };
440
+ return { label: language?.trim(), support: null, isJsonLines: false };
369
441
  }
370
- return { support: null };
442
+ return { support: null, isJsonLines: false };
371
443
  }
372
444
  function CodeBlock({
373
445
  value,
@@ -389,6 +461,10 @@ function CodeBlock({
389
461
  ...props
390
462
  }) {
391
463
  const resolvedLanguage = (0, import_react.useMemo)(() => resolveLanguage(language, fileName), [fileName, language]);
464
+ const displayValue = (0, import_react.useMemo)(
465
+ () => !editable && resolvedLanguage.isJsonLines ? formatJsonLinesForDisplay(value) : value,
466
+ [editable, resolvedLanguage.isJsonLines, value]
467
+ );
392
468
  const [copyStatus, setCopyStatus] = (0, import_react.useState)("idle");
393
469
  const copyResetTimer = (0, import_react.useRef)(null);
394
470
  (0, import_react.useEffect)(() => () => {
@@ -398,9 +474,10 @@ function CodeBlock({
398
474
  codeTheme,
399
475
  (0, import_language.syntaxHighlighting)(fractalHighlightStyle),
400
476
  ...resolvedLanguage.support ? [resolvedLanguage.support] : [],
477
+ ...resolvedLanguage.isJsonLines ? [jsonLinesFoldSupport] : [],
401
478
  ...lineNumbers ? [(0, import_language.foldGutter)(), (0, import_view.lineNumbers)()] : [],
402
479
  ...wrap ? [import_view.EditorView.lineWrapping] : []
403
- ], [lineNumbers, resolvedLanguage.support, wrap]);
480
+ ], [lineNumbers, resolvedLanguage.isJsonLines, resolvedLanguage.support, wrap]);
404
481
  const languageLabel = resolvedLanguage.label;
405
482
  const toolbarLabel = label ?? fileName ?? languageLabel;
406
483
  const surfaceLabel = ariaLabel ?? (fileName ? `${fileName} ${editable ? "editor" : "viewer"}` : `Code ${editable ? "editor" : "viewer"}`);
@@ -450,7 +527,7 @@ function CodeBlock({
450
527
  import_react_codemirror.default,
451
528
  {
452
529
  "aria-label": surfaceLabel,
453
- value,
530
+ value: displayValue,
454
531
  editable,
455
532
  readOnly: !editable,
456
533
  onChange,