@petrarca/sonnet-ui 0.5.0 → 0.7.0
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/README.md +64 -65
- package/dist/index.d.ts +165 -2
- package/dist/index.js +1198 -510
- package/dist/index.js.map +1 -1
- package/dist/json-editor/index.d.ts +4 -2
- package/dist/json-editor/index.js +32 -17
- package/dist/json-editor/index.js.map +1 -1
- package/package.json +3 -2
|
@@ -48,8 +48,10 @@ declare function JsonEditor({ value, editablePaths, onChange, height, className,
|
|
|
48
48
|
/**
|
|
49
49
|
* Default CodeMirror theme for JsonEditor.
|
|
50
50
|
*
|
|
51
|
-
* Reads
|
|
52
|
-
*
|
|
51
|
+
* Reads design tokens from @petrarca/sonnet-ui/styles.css: --border, --muted
|
|
52
|
+
* and --state-selected. Each holds a bare HSL triple, so every reference has
|
|
53
|
+
* to be wrapped in hsl() -- `var(--border)` on its own is not a colour and the
|
|
54
|
+
* declaration is dropped.
|
|
53
55
|
*
|
|
54
56
|
* Consumers not using sonnet-ui tokens can pass their own Extension
|
|
55
57
|
* via the JsonEditor `theme` prop to override this entirely.
|
|
@@ -6,19 +6,48 @@ import { EditorState, StateField } from "@codemirror/state";
|
|
|
6
6
|
import { EditorView as EditorView2, Decoration } from "@codemirror/view";
|
|
7
7
|
import { resolvePointerRanges } from "@petrarca/sonnet-core";
|
|
8
8
|
|
|
9
|
+
// src/JsonEditor/readonlyRanges.ts
|
|
10
|
+
function mergeRanges(ranges) {
|
|
11
|
+
const sorted = [...ranges].sort((a, b) => a.from - b.from);
|
|
12
|
+
const merged = [];
|
|
13
|
+
for (const range of sorted) {
|
|
14
|
+
const last = merged[merged.length - 1];
|
|
15
|
+
if (last && range.from <= last.to) {
|
|
16
|
+
last.to = Math.max(last.to, range.to);
|
|
17
|
+
} else {
|
|
18
|
+
merged.push({ from: range.from, to: range.to });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return merged;
|
|
22
|
+
}
|
|
23
|
+
function computeReadonlyRanges(editableRanges, docLength) {
|
|
24
|
+
const ranges = [];
|
|
25
|
+
let cursor = 0;
|
|
26
|
+
for (const range of mergeRanges(editableRanges)) {
|
|
27
|
+
if (cursor < range.from) {
|
|
28
|
+
ranges.push({ from: cursor, to: range.from });
|
|
29
|
+
}
|
|
30
|
+
cursor = range.to;
|
|
31
|
+
}
|
|
32
|
+
if (cursor < docLength) {
|
|
33
|
+
ranges.push({ from: cursor, to: docLength });
|
|
34
|
+
}
|
|
35
|
+
return ranges;
|
|
36
|
+
}
|
|
37
|
+
|
|
9
38
|
// src/JsonEditor/defaultTheme.ts
|
|
10
39
|
import { EditorView } from "@codemirror/view";
|
|
11
40
|
var defaultTheme = EditorView.theme({
|
|
12
41
|
"&": {
|
|
13
42
|
fontSize: "13px",
|
|
14
|
-
border: "1px solid var(--border)",
|
|
43
|
+
border: "1px solid hsl(var(--border))",
|
|
15
44
|
borderRadius: "6px"
|
|
16
45
|
},
|
|
17
46
|
".cm-content": {
|
|
18
|
-
fontFamily: "
|
|
47
|
+
fontFamily: "'JetBrains Mono', ui-monospace, SFMono-Regular, monospace"
|
|
19
48
|
},
|
|
20
49
|
".cm-readonly-region": {
|
|
21
|
-
backgroundColor: "
|
|
50
|
+
backgroundColor: "hsl(var(--state-selected) / 0.5)",
|
|
22
51
|
opacity: "0.7"
|
|
23
52
|
},
|
|
24
53
|
".cm-gutters": {
|
|
@@ -36,20 +65,6 @@ var defaultTheme = EditorView.theme({
|
|
|
36
65
|
// src/JsonEditor/JsonEditor.tsx
|
|
37
66
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
38
67
|
var readonlyMark = Decoration.mark({ class: "cm-readonly-region" });
|
|
39
|
-
function computeReadonlyRanges(editableRanges, docLength) {
|
|
40
|
-
const ranges = [];
|
|
41
|
-
let cursor = 0;
|
|
42
|
-
for (const range of editableRanges) {
|
|
43
|
-
if (cursor < range.from) {
|
|
44
|
-
ranges.push({ from: cursor, to: range.from });
|
|
45
|
-
}
|
|
46
|
-
cursor = range.to;
|
|
47
|
-
}
|
|
48
|
-
if (cursor < docLength) {
|
|
49
|
-
ranges.push({ from: cursor, to: docLength });
|
|
50
|
-
}
|
|
51
|
-
return ranges;
|
|
52
|
-
}
|
|
53
68
|
function buildEditableExtensions(editablePaths) {
|
|
54
69
|
const changeFilter = EditorState.changeFilter.of((tr) => {
|
|
55
70
|
const currentDoc = tr.startState.doc.toString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/JsonEditor/JsonEditor.tsx","../../src/JsonEditor/defaultTheme.ts"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport CodeMirror from \"@uiw/react-codemirror\";\nimport { json } from \"@codemirror/lang-json\";\nimport { EditorState, StateField, type Extension } from \"@codemirror/state\";\nimport { EditorView, Decoration, type DecorationSet } from \"@codemirror/view\";\nimport { resolvePointerRanges, type OffsetRange } from \"@petrarca/sonnet-core\";\nimport { defaultTheme } from \"./defaultTheme\";\n\nexport interface JsonEditorProps {\n /** The JSON string to display and edit. */\n value: string;\n /**\n * JSON Pointer (RFC 6901) paths whose values are editable.\n * Everything outside these paths is readonly.\n * Example: [\"/metadata\", \"/definitions/Patient/description\"]\n */\n editablePaths: string[];\n /** Called when the document changes (only editable regions can change). */\n onChange?: (value: string) => void;\n /** Editor height. Defaults to \"400px\". */\n height?: string;\n /** Additional CSS class on the wrapper. */\n className?: string;\n /**\n * Make the editor fill its container's height. Sets height=\"100%\" and\n * propagates the fill through CodeMirror's internal wrappers so the editor\n * scrolls internally. Use when the container has an explicit height (e.g.\n * `absolute inset-0` or `flex-1`). Prefer this over external `<style>` rules\n * targeting CodeMirror's internal class names.\n */\n fillContainer?: boolean;\n /**\n * CodeMirror theme extension. Defaults to a built-in theme that reads\n * CSS variables (--border, --font-mono, --muted) from the host app.\n * Pass your own Extension to override styling entirely.\n */\n theme?: Extension;\n}\n\n// Decoration mark for readonly regions (subtle blue background)\nconst readonlyMark = Decoration.mark({ class: \"cm-readonly-region\" });\n\n/**\n * Compute readonly ranges from editable ranges (inversion against doc length).\n * Used by both the changeFilter and the decoration builder.\n */\nfunction computeReadonlyRanges(\n editableRanges: OffsetRange[],\n docLength: number,\n): { from: number; to: number }[] {\n const ranges: { from: number; to: number }[] = [];\n let cursor = 0;\n for (const range of editableRanges) {\n if (cursor < range.from) {\n ranges.push({ from: cursor, to: range.from });\n }\n cursor = range.to;\n }\n if (cursor < docLength) {\n ranges.push({ from: cursor, to: docLength });\n }\n return ranges;\n}\n\n/**\n * Build CodeMirror extensions that enforce editable regions:\n * - changeFilter: suppresses changes outside editable ranges\n * - StateField: decorates readonly regions with a visual style\n */\nfunction buildEditableExtensions(editablePaths: string[]) {\n // changeFilter: recompute editable ranges from the live document on every\n // transaction so offsets remain accurate as the user types. Using a snapshot\n // from mount time would cause ranges to drift and incorrectly block edits.\n const changeFilter = EditorState.changeFilter.of((tr) => {\n const currentDoc = tr.startState.doc.toString();\n const editableRanges = resolvePointerRanges(currentDoc, editablePaths);\n const readonlyRanges = computeReadonlyRanges(\n editableRanges,\n tr.startState.doc.length,\n );\n // Empty readonly list means the whole document is editable -- return true\n // to allow all changes. An empty array would block everything.\n if (readonlyRanges.length === 0) return true;\n return readonlyRanges.flatMap((r) => [r.from, r.to]);\n });\n\n // Decorations: mark readonly regions with a subtle background.\n // Recomputed from the live document on every change so highlights stay\n // accurate as content shifts.\n const readonlyDecorations = StateField.define<DecorationSet>({\n create(state) {\n const editableRanges = resolvePointerRanges(\n state.doc.toString(),\n editablePaths,\n );\n const ranges = computeReadonlyRanges(editableRanges, state.doc.length);\n return Decoration.set(\n ranges.map((r) => readonlyMark.range(r.from, r.to)),\n );\n },\n update(deco, tr) {\n if (!tr.docChanged) return deco;\n const editableRanges = resolvePointerRanges(\n tr.state.doc.toString(),\n editablePaths,\n );\n const ranges = computeReadonlyRanges(editableRanges, tr.state.doc.length);\n return Decoration.set(\n ranges.map((r) => readonlyMark.range(r.from, r.to)),\n );\n },\n provide: (field) => EditorView.decorations.from(field),\n });\n\n return [changeFilter, readonlyDecorations];\n}\n\n/**\n * JsonEditor - A JSON text editor with selectively editable regions.\n *\n * Pass JSON Pointer paths to `editablePaths` to mark which parts of\n * the document the user can modify. Everything else is visible but\n * locked. Changes outside editable regions are silently rejected.\n *\n * The default theme reads CSS variables from @petrarca/sonnet-ui/styles.css\n * (--border, --font-mono, --muted). Pass a custom `theme` extension to\n * override styling for consumers that do not use sonnet-ui tokens.\n */\nexport default function JsonEditor({\n value,\n editablePaths,\n onChange,\n height = \"400px\",\n className,\n theme = defaultTheme,\n fillContainer = false,\n}: JsonEditorProps) {\n // Ranges are structural: recompute only when the set of editable paths\n // or the theme changes, not on every keystroke. When paths change, use the\n // current value so ranges are computed against what is actually in the editor.\n const joinedPaths = editablePaths.join(\",\");\n\n const extensions = useMemo(\n () => [json(), theme, ...buildEditableExtensions(editablePaths)],\n [joinedPaths, theme], // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n const handleChange = useCallback(\n (val: string) => {\n onChange?.(val);\n },\n [onChange],\n );\n\n const effectiveHeight = fillContainer ? \"100%\" : height;\n // When fillContainer=true the wrapper div needs an explicit height so the\n // \"100%\" value on CodeMirror resolves against the container. However\n // @uiw/react-codemirror does not propagate a percentage height to its\n // internal .cm-theme-light wrapper, so we also set that explicitly via a\n // scoped <style> that only targets children of the .json-editor--fill class.\n const wrapperStyle = fillContainer\n ? ({ height: \"100%\" } as React.CSSProperties)\n : undefined;\n const wrapperClass = fillContainer\n ? `json-editor--fill${className ? ` ${className}` : \"\"}`\n : className;\n\n return (\n <div className={wrapperClass} style={wrapperStyle}>\n {fillContainer && (\n <style>\n {\".json-editor--fill > .cm-theme-light { height: 100%; }\"}\n </style>\n )}\n <CodeMirror\n value={value}\n height={effectiveHeight}\n extensions={extensions}\n onChange={handleChange}\n basicSetup={{\n lineNumbers: true,\n foldGutter: true,\n bracketMatching: true,\n closeBrackets: true,\n indentOnInput: true,\n }}\n />\n </div>\n );\n}\n","import { EditorView } from \"@codemirror/view\";\n\n/**\n * Default CodeMirror theme for JsonEditor.\n *\n * Reads CSS variables provided by @petrarca/sonnet-ui/styles.css:\n * --border, --font-mono, --muted\n *\n * Consumers not using sonnet-ui tokens can pass their own Extension\n * via the JsonEditor `theme` prop to override this entirely.\n */\nexport const defaultTheme = EditorView.theme({\n \"&\": {\n fontSize: \"13px\",\n border: \"1px solid var(--border)\",\n borderRadius: \"6px\",\n },\n \".cm-content\": {\n fontFamily: \"var(--font-mono, 'JetBrains Mono', monospace)\",\n },\n \".cm-readonly-region\": {\n backgroundColor: \"rgb(219 234 254 / 0.5)\",\n opacity: \"0.7\",\n },\n \".cm-gutters\": {\n backgroundColor: \"hsl(var(--muted) / 0.3)\",\n border: \"none\",\n },\n \".cm-activeLine\": {\n backgroundColor: \"transparent\",\n },\n \".cm-activeLineGutter\": {\n backgroundColor: \"transparent\",\n },\n});\n"],"mappings":"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","names":["EditorView","EditorView"]}
|
|
1
|
+
{"version":3,"sources":["../../src/JsonEditor/JsonEditor.tsx","../../src/JsonEditor/readonlyRanges.ts","../../src/JsonEditor/defaultTheme.ts"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport CodeMirror from \"@uiw/react-codemirror\";\nimport { json } from \"@codemirror/lang-json\";\nimport { EditorState, StateField, type Extension } from \"@codemirror/state\";\nimport { EditorView, Decoration, type DecorationSet } from \"@codemirror/view\";\nimport { resolvePointerRanges } from \"@petrarca/sonnet-core\";\nimport { computeReadonlyRanges } from \"./readonlyRanges\";\nimport { defaultTheme } from \"./defaultTheme\";\n\nexport interface JsonEditorProps {\n /** The JSON string to display and edit. */\n value: string;\n /**\n * JSON Pointer (RFC 6901) paths whose values are editable.\n * Everything outside these paths is readonly.\n * Example: [\"/metadata\", \"/definitions/Patient/description\"]\n */\n editablePaths: string[];\n /** Called when the document changes (only editable regions can change). */\n onChange?: (value: string) => void;\n /** Editor height. Defaults to \"400px\". */\n height?: string;\n /** Additional CSS class on the wrapper. */\n className?: string;\n /**\n * Make the editor fill its container's height. Sets height=\"100%\" and\n * propagates the fill through CodeMirror's internal wrappers so the editor\n * scrolls internally. Use when the container has an explicit height (e.g.\n * `absolute inset-0` or `flex-1`). Prefer this over external `<style>` rules\n * targeting CodeMirror's internal class names.\n */\n fillContainer?: boolean;\n /**\n * CodeMirror theme extension. Defaults to a built-in theme that reads\n * CSS variables (--border, --font-mono, --muted) from the host app.\n * Pass your own Extension to override styling entirely.\n */\n theme?: Extension;\n}\n\n// Decoration mark for readonly regions (subtle blue background)\nconst readonlyMark = Decoration.mark({ class: \"cm-readonly-region\" });\n\n/**\n * Build CodeMirror extensions that enforce editable regions:\n * - changeFilter: suppresses changes outside editable ranges\n * - StateField: decorates readonly regions with a visual style\n */\nfunction buildEditableExtensions(editablePaths: string[]) {\n // changeFilter: recompute editable ranges from the live document on every\n // transaction so offsets remain accurate as the user types. Using a snapshot\n // from mount time would cause ranges to drift and incorrectly block edits.\n const changeFilter = EditorState.changeFilter.of((tr) => {\n const currentDoc = tr.startState.doc.toString();\n const editableRanges = resolvePointerRanges(currentDoc, editablePaths);\n const readonlyRanges = computeReadonlyRanges(\n editableRanges,\n tr.startState.doc.length,\n );\n // Empty readonly list means the whole document is editable -- return true\n // to allow all changes. An empty array would block everything.\n if (readonlyRanges.length === 0) return true;\n return readonlyRanges.flatMap((r) => [r.from, r.to]);\n });\n\n // Decorations: mark readonly regions with a subtle background.\n // Recomputed from the live document on every change so highlights stay\n // accurate as content shifts.\n const readonlyDecorations = StateField.define<DecorationSet>({\n create(state) {\n const editableRanges = resolvePointerRanges(\n state.doc.toString(),\n editablePaths,\n );\n const ranges = computeReadonlyRanges(editableRanges, state.doc.length);\n return Decoration.set(\n ranges.map((r) => readonlyMark.range(r.from, r.to)),\n );\n },\n update(deco, tr) {\n if (!tr.docChanged) return deco;\n const editableRanges = resolvePointerRanges(\n tr.state.doc.toString(),\n editablePaths,\n );\n const ranges = computeReadonlyRanges(editableRanges, tr.state.doc.length);\n return Decoration.set(\n ranges.map((r) => readonlyMark.range(r.from, r.to)),\n );\n },\n provide: (field) => EditorView.decorations.from(field),\n });\n\n return [changeFilter, readonlyDecorations];\n}\n\n/**\n * JsonEditor - A JSON text editor with selectively editable regions.\n *\n * Pass JSON Pointer paths to `editablePaths` to mark which parts of\n * the document the user can modify. Everything else is visible but\n * locked. Changes outside editable regions are silently rejected.\n *\n * The default theme reads CSS variables from @petrarca/sonnet-ui/styles.css\n * (--border, --font-mono, --muted). Pass a custom `theme` extension to\n * override styling for consumers that do not use sonnet-ui tokens.\n */\nexport default function JsonEditor({\n value,\n editablePaths,\n onChange,\n height = \"400px\",\n className,\n theme = defaultTheme,\n fillContainer = false,\n}: JsonEditorProps) {\n // Ranges are structural: recompute only when the set of editable paths\n // or the theme changes, not on every keystroke. When paths change, use the\n // current value so ranges are computed against what is actually in the editor.\n const joinedPaths = editablePaths.join(\",\");\n\n const extensions = useMemo(\n () => [json(), theme, ...buildEditableExtensions(editablePaths)],\n [joinedPaths, theme], // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n const handleChange = useCallback(\n (val: string) => {\n onChange?.(val);\n },\n [onChange],\n );\n\n const effectiveHeight = fillContainer ? \"100%\" : height;\n // When fillContainer=true the wrapper div needs an explicit height so the\n // \"100%\" value on CodeMirror resolves against the container. However\n // @uiw/react-codemirror does not propagate a percentage height to its\n // internal .cm-theme-light wrapper, so we also set that explicitly via a\n // scoped <style> that only targets children of the .json-editor--fill class.\n const wrapperStyle = fillContainer\n ? ({ height: \"100%\" } as React.CSSProperties)\n : undefined;\n const wrapperClass = fillContainer\n ? `json-editor--fill${className ? ` ${className}` : \"\"}`\n : className;\n\n return (\n <div className={wrapperClass} style={wrapperStyle}>\n {fillContainer && (\n <style>\n {\".json-editor--fill > .cm-theme-light { height: 100%; }\"}\n </style>\n )}\n <CodeMirror\n value={value}\n height={effectiveHeight}\n extensions={extensions}\n onChange={handleChange}\n basicSetup={{\n lineNumbers: true,\n foldGutter: true,\n bracketMatching: true,\n closeBrackets: true,\n indentOnInput: true,\n }}\n />\n </div>\n );\n}\n","/**\n * Editable-region maths for JsonEditor.\n *\n * Pure functions, kept out of the component file so it exports only components\n * (Fast Refresh) and so the inversion is directly testable. Not re-exported\n * from the package root -- these are internals, like the table's column\n * resolver.\n */\n\nimport type { OffsetRange } from \"@petrarca/sonnet-core\";\n\n/**\n * Normalize ranges into a sorted, non-overlapping set.\n *\n * Inversion walks a single cursor forward, so it is only correct for input that\n * is already sorted and disjoint. Neither holds in general: a caller may pass a\n * pointer together with one nested inside it -- `/metadata` and\n * `/metadata/version` -- which is a reasonable way to say \"this object is\n * editable, and so is this field within it\". Merging first makes the inversion\n * correct for any input rather than for input that happens to satisfy an\n * unstated precondition.\n */\nfunction mergeRanges(ranges: OffsetRange[]): { from: number; to: number }[] {\n const sorted = [...ranges].sort((a, b) => a.from - b.from);\n const merged: { from: number; to: number }[] = [];\n for (const range of sorted) {\n const last = merged[merged.length - 1];\n // `<=` also folds ranges that merely touch, so the inversion emits no\n // zero-width readonly slivers between them.\n if (last && range.from <= last.to) {\n last.to = Math.max(last.to, range.to);\n } else {\n merged.push({ from: range.from, to: range.to });\n }\n }\n return merged;\n}\n\n/**\n * Compute readonly ranges from editable ranges (inversion against doc length).\n * Used by both the changeFilter and the decoration builder.\n */\nexport function computeReadonlyRanges(\n editableRanges: OffsetRange[],\n docLength: number,\n): { from: number; to: number }[] {\n const ranges: { from: number; to: number }[] = [];\n let cursor = 0;\n for (const range of mergeRanges(editableRanges)) {\n if (cursor < range.from) {\n ranges.push({ from: cursor, to: range.from });\n }\n cursor = range.to;\n }\n if (cursor < docLength) {\n ranges.push({ from: cursor, to: docLength });\n }\n return ranges;\n}\n","import { EditorView } from \"@codemirror/view\";\n\n/**\n * Default CodeMirror theme for JsonEditor.\n *\n * Reads design tokens from @petrarca/sonnet-ui/styles.css: --border, --muted\n * and --state-selected. Each holds a bare HSL triple, so every reference has\n * to be wrapped in hsl() -- `var(--border)` on its own is not a colour and the\n * declaration is dropped.\n *\n * Consumers not using sonnet-ui tokens can pass their own Extension\n * via the JsonEditor `theme` prop to override this entirely.\n */\nexport const defaultTheme = EditorView.theme({\n \"&\": {\n fontSize: \"13px\",\n border: \"1px solid hsl(var(--border))\",\n borderRadius: \"6px\",\n },\n \".cm-content\": {\n fontFamily: \"'JetBrains Mono', ui-monospace, SFMono-Regular, monospace\",\n },\n \".cm-readonly-region\": {\n backgroundColor: \"hsl(var(--state-selected) / 0.5)\",\n opacity: \"0.7\",\n },\n \".cm-gutters\": {\n backgroundColor: \"hsl(var(--muted) / 0.3)\",\n border: \"none\",\n },\n \".cm-activeLine\": {\n backgroundColor: \"transparent\",\n },\n \".cm-activeLineGutter\": {\n backgroundColor: \"transparent\",\n },\n});\n"],"mappings":"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","names":["EditorView","EditorView"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@petrarca/sonnet-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "UI primitives, data components, and table schema system for the Petrarca Sonnet component library",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"@radix-ui/react-label": "^2.1.8",
|
|
36
36
|
"@radix-ui/react-popover": "^1.1.15",
|
|
37
37
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
38
|
+
"@radix-ui/react-select": "^2.3.3",
|
|
38
39
|
"@radix-ui/react-separator": "^1.1.8",
|
|
39
40
|
"@radix-ui/react-slot": "^1.2.4",
|
|
40
41
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
"radix-ui": "^1.4.3",
|
|
47
48
|
"react-json-tree": "0.20.0",
|
|
48
49
|
"zustand": "5.0.8",
|
|
49
|
-
"@petrarca/sonnet-core": "0.
|
|
50
|
+
"@petrarca/sonnet-core": "0.7.0"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"@codemirror/lang-json": ">=6",
|