@mirror-physics/fractal-ui 0.2.0-next.52 → 0.2.0-next.54
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 +115 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +121 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -170,7 +170,12 @@ import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
|
170
170
|
import CodeMirror from "@uiw/react-codemirror";
|
|
171
171
|
import { HighlightStyle, foldGutter, foldService, syntaxHighlighting } from "@codemirror/language";
|
|
172
172
|
import { json } from "@codemirror/lang-json";
|
|
173
|
-
import {
|
|
173
|
+
import {
|
|
174
|
+
Decoration,
|
|
175
|
+
EditorView,
|
|
176
|
+
ViewPlugin,
|
|
177
|
+
lineNumbers as lineNumberGutter
|
|
178
|
+
} from "@codemirror/view";
|
|
174
179
|
import { tags } from "@lezer/highlight";
|
|
175
180
|
import { Check, Copy } from "@mirror-physics/fractal-icons";
|
|
176
181
|
import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -187,7 +192,10 @@ var codeTheme = EditorView.theme({
|
|
|
187
192
|
overscrollBehavior: "none"
|
|
188
193
|
},
|
|
189
194
|
".cm-content": { padding: "10px 0" },
|
|
190
|
-
".cm-line": {
|
|
195
|
+
".cm-line": {
|
|
196
|
+
padding: "0 12px 0 calc(12px + var(--fractal-code-wrap-indent, 0ch))",
|
|
197
|
+
textIndent: "calc(0px - var(--fractal-code-wrap-indent, 0ch))"
|
|
198
|
+
},
|
|
191
199
|
".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--fg-primary)" },
|
|
192
200
|
".cm-gutters": {
|
|
193
201
|
backgroundColor: "var(--bg-canvas)",
|
|
@@ -197,22 +205,23 @@ var codeTheme = EditorView.theme({
|
|
|
197
205
|
".cm-lineNumbers .cm-gutterElement": { padding: "0 10px 0 4px" },
|
|
198
206
|
".cm-foldGutter": { width: "14px" },
|
|
199
207
|
".cm-foldGutter .cm-gutterElement": {
|
|
200
|
-
|
|
208
|
+
position: "relative",
|
|
209
|
+
display: "block",
|
|
201
210
|
width: "14px",
|
|
202
|
-
|
|
203
|
-
justifyContent: "flex-end",
|
|
204
|
-
padding: "0 1px 0 0"
|
|
211
|
+
padding: "0"
|
|
205
212
|
},
|
|
206
213
|
".cm-foldGutter .cm-gutterElement > span": {
|
|
214
|
+
position: "absolute",
|
|
215
|
+
top: "2px",
|
|
216
|
+
left: "0",
|
|
207
217
|
display: "inline-flex",
|
|
208
|
-
width: "
|
|
209
|
-
height: "
|
|
218
|
+
width: "14px",
|
|
219
|
+
height: "14px",
|
|
210
220
|
alignItems: "center",
|
|
211
221
|
justifyContent: "center",
|
|
212
222
|
padding: "0",
|
|
213
223
|
color: "var(--fg-tertiary)",
|
|
214
|
-
lineHeight: "
|
|
215
|
-
transform: "translateY(-1px)",
|
|
224
|
+
lineHeight: "14px",
|
|
216
225
|
transition: "color 120ms ease"
|
|
217
226
|
},
|
|
218
227
|
".cm-foldGutter .cm-gutterElement > span:hover": { color: "var(--fg-secondary)" },
|
|
@@ -250,24 +259,113 @@ var fractalHighlightStyle = HighlightStyle.define([
|
|
|
250
259
|
var jsonSupport = json();
|
|
251
260
|
var JSON_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["json", "jsonl", "ndjson", "geojson"]);
|
|
252
261
|
var JSON_LINES_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["jsonl", "ndjson"]);
|
|
262
|
+
var JSON_LINES_PRETTY_PRINT_THRESHOLD = 160;
|
|
263
|
+
var JSON_PROPERTY_PREFIX = /^(\s*"(?:\\.|[^"\\])*"\s*:\s*)/;
|
|
264
|
+
function countVisualColumns(value) {
|
|
265
|
+
let columns = 0;
|
|
266
|
+
for (const character of value) columns += character === " " ? 2 : 1;
|
|
267
|
+
return columns;
|
|
268
|
+
}
|
|
269
|
+
function getWrappedLineIndent(line) {
|
|
270
|
+
const propertyPrefix = line.match(JSON_PROPERTY_PREFIX)?.[1];
|
|
271
|
+
if (propertyPrefix) return Math.min(countVisualColumns(propertyPrefix), 24);
|
|
272
|
+
const leadingWhitespace = line.match(/^\s*/)?.[0] ?? "";
|
|
273
|
+
return Math.min(countVisualColumns(leadingWhitespace) + 2, 12);
|
|
274
|
+
}
|
|
275
|
+
function buildWrappedLineIndentation(view) {
|
|
276
|
+
const decorations = [];
|
|
277
|
+
const decoratedLines = /* @__PURE__ */ new Set();
|
|
278
|
+
for (const visibleRange of view.visibleRanges) {
|
|
279
|
+
let position = visibleRange.from;
|
|
280
|
+
while (position <= visibleRange.to) {
|
|
281
|
+
const line = view.state.doc.lineAt(position);
|
|
282
|
+
if (!decoratedLines.has(line.from)) {
|
|
283
|
+
const indent = getWrappedLineIndent(line.text);
|
|
284
|
+
decorations.push(Decoration.line({
|
|
285
|
+
attributes: { style: `--fractal-code-wrap-indent: ${indent}ch` }
|
|
286
|
+
}).range(line.from));
|
|
287
|
+
decoratedLines.add(line.from);
|
|
288
|
+
}
|
|
289
|
+
if (line.to >= visibleRange.to) break;
|
|
290
|
+
position = line.to + 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return Decoration.set(decorations, true);
|
|
294
|
+
}
|
|
295
|
+
var wrappedLineIndentation = ViewPlugin.fromClass(class {
|
|
296
|
+
constructor(view) {
|
|
297
|
+
this.decorations = buildWrappedLineIndentation(view);
|
|
298
|
+
}
|
|
299
|
+
update(update) {
|
|
300
|
+
this.decorations = buildWrappedLineIndentation(update.view);
|
|
301
|
+
}
|
|
302
|
+
}, {
|
|
303
|
+
decorations: (plugin) => plugin.decorations
|
|
304
|
+
});
|
|
305
|
+
function formatJsonLinesForDisplay(value) {
|
|
306
|
+
return value.split(/\r?\n/).map((line) => {
|
|
307
|
+
const firstContentIndex = line.search(/\S/);
|
|
308
|
+
if (firstContentIndex < 0 || line.length - firstContentIndex <= JSON_LINES_PRETTY_PRINT_THRESHOLD) return line;
|
|
309
|
+
const indentation = line.slice(0, firstContentIndex);
|
|
310
|
+
try {
|
|
311
|
+
const parsed = JSON.parse(line.slice(firstContentIndex));
|
|
312
|
+
if (parsed === null || typeof parsed !== "object") return line;
|
|
313
|
+
return JSON.stringify(parsed, null, 2).split("\n").map((formattedLine) => `${indentation}${formattedLine}`).join("\n");
|
|
314
|
+
} catch {
|
|
315
|
+
return line;
|
|
316
|
+
}
|
|
317
|
+
}).join("\n");
|
|
318
|
+
}
|
|
319
|
+
function findJsonContainerEnd(document2, start) {
|
|
320
|
+
const openingCharacter = document2[start];
|
|
321
|
+
if (openingCharacter !== "{" && openingCharacter !== "[") return null;
|
|
322
|
+
const closingCharacters = [];
|
|
323
|
+
let isInsideString = false;
|
|
324
|
+
let isEscaped = false;
|
|
325
|
+
for (let index = start; index < document2.length; index += 1) {
|
|
326
|
+
const character = document2[index];
|
|
327
|
+
if (isInsideString) {
|
|
328
|
+
if (isEscaped) {
|
|
329
|
+
isEscaped = false;
|
|
330
|
+
} else if (character === "\\") {
|
|
331
|
+
isEscaped = true;
|
|
332
|
+
} else if (character === '"') {
|
|
333
|
+
isInsideString = false;
|
|
334
|
+
}
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (character === '"') {
|
|
338
|
+
isInsideString = true;
|
|
339
|
+
} else if (character === "{") {
|
|
340
|
+
closingCharacters.push("}");
|
|
341
|
+
} else if (character === "[") {
|
|
342
|
+
closingCharacters.push("]");
|
|
343
|
+
} else if (character === "}" || character === "]") {
|
|
344
|
+
if (closingCharacters.pop() !== character) return null;
|
|
345
|
+
if (closingCharacters.length === 0) return index;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
253
350
|
var jsonLinesFoldSupport = foldService.of((state, lineStart, lineEnd) => {
|
|
254
351
|
const line = state.doc.sliceString(lineStart, lineEnd);
|
|
255
352
|
const firstContentIndex = line.search(/\S/);
|
|
256
353
|
if (firstContentIndex < 0) return null;
|
|
257
|
-
const
|
|
354
|
+
const containerStart = lineStart + firstContentIndex;
|
|
258
355
|
const openingCharacter = line[firstContentIndex];
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
356
|
+
if (openingCharacter !== "{" && openingCharacter !== "[") return null;
|
|
357
|
+
const document2 = state.doc.toString();
|
|
358
|
+
const containerEnd = findJsonContainerEnd(document2, containerStart);
|
|
359
|
+
if (containerEnd === null || containerEnd - containerStart < 2) return null;
|
|
262
360
|
try {
|
|
263
|
-
const parsed = JSON.parse(
|
|
361
|
+
const parsed = JSON.parse(document2.slice(containerStart, containerEnd + 1));
|
|
264
362
|
if (parsed === null || typeof parsed !== "object") return null;
|
|
265
363
|
} catch {
|
|
266
364
|
return null;
|
|
267
365
|
}
|
|
268
366
|
return {
|
|
269
|
-
from:
|
|
270
|
-
to:
|
|
367
|
+
from: containerStart + 1,
|
|
368
|
+
to: containerEnd
|
|
271
369
|
};
|
|
272
370
|
});
|
|
273
371
|
function resolveLanguage(language, fileName) {
|
|
@@ -304,6 +402,10 @@ function CodeBlock({
|
|
|
304
402
|
...props
|
|
305
403
|
}) {
|
|
306
404
|
const resolvedLanguage = useMemo(() => resolveLanguage(language, fileName), [fileName, language]);
|
|
405
|
+
const displayValue = useMemo(
|
|
406
|
+
() => !editable && resolvedLanguage.isJsonLines ? formatJsonLinesForDisplay(value) : value,
|
|
407
|
+
[editable, resolvedLanguage.isJsonLines, value]
|
|
408
|
+
);
|
|
307
409
|
const [copyStatus, setCopyStatus] = useState2("idle");
|
|
308
410
|
const copyResetTimer = useRef(null);
|
|
309
411
|
useEffect(() => () => {
|
|
@@ -315,7 +417,7 @@ function CodeBlock({
|
|
|
315
417
|
...resolvedLanguage.support ? [resolvedLanguage.support] : [],
|
|
316
418
|
...resolvedLanguage.isJsonLines ? [jsonLinesFoldSupport] : [],
|
|
317
419
|
...lineNumbers ? [foldGutter(), lineNumberGutter()] : [],
|
|
318
|
-
...wrap ? [EditorView.lineWrapping] : []
|
|
420
|
+
...wrap ? [EditorView.lineWrapping, wrappedLineIndentation] : []
|
|
319
421
|
], [lineNumbers, resolvedLanguage.isJsonLines, resolvedLanguage.support, wrap]);
|
|
320
422
|
const languageLabel = resolvedLanguage.label;
|
|
321
423
|
const toolbarLabel = label ?? fileName ?? languageLabel;
|
|
@@ -366,7 +468,7 @@ function CodeBlock({
|
|
|
366
468
|
CodeMirror,
|
|
367
469
|
{
|
|
368
470
|
"aria-label": surfaceLabel,
|
|
369
|
-
value,
|
|
471
|
+
value: displayValue,
|
|
370
472
|
editable,
|
|
371
473
|
readOnly: !editable,
|
|
372
474
|
onChange,
|