@aglyn/shared-ui-json-editor 1.0.0-beta.249 → 1.0.0-beta.250
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/shared-ui-json-editor",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.250",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"./package.json": "./package.json"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
28
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
27
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.250",
|
|
28
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.250",
|
|
29
29
|
"@codemirror/lang-json": "^6.0.2",
|
|
30
30
|
"@monaco-editor/react": "^4.7.0",
|
|
31
31
|
"@uiw/codemirror-theme-github": "^4.25.12",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
2
3
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
4
|
/**
|
|
4
5
|
* @license
|
|
@@ -17,6 +18,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
17
18
|
* limitations under the License.
|
|
18
19
|
*/ import ReactMonacoEditor, { loader } from "@monaco-editor/react";
|
|
19
20
|
import { useColorScheme, useTheme } from "@mui/material/styles";
|
|
21
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
20
22
|
/**
|
|
21
23
|
* Where Monaco's AMD bundle is served from — our origin, never a CDN.
|
|
22
24
|
*
|
|
@@ -77,13 +79,25 @@ export const MonacoEditor = (props)=>{
|
|
|
77
79
|
// one — `systemMode` when following the OS, else the explicit `mode` —
|
|
78
80
|
// with `palette.mode` kept as the fallback for the non-CSS-vars trees.
|
|
79
81
|
// `props` spreads last, so a caller can still pin a Monaco theme.
|
|
80
|
-
const
|
|
82
|
+
const theme = useTheme();
|
|
81
83
|
const { mode, systemMode } = useColorScheme();
|
|
82
|
-
const resolved = (_ref = systemMode != null ? systemMode : mode) != null ? _ref : palette.mode;
|
|
84
|
+
const resolved = (_ref = systemMode != null ? systemMode : mode) != null ? _ref : theme.palette.mode;
|
|
85
|
+
// Below `md` the minimap costs a strip of an already narrow editor and is
|
|
86
|
+
// no use for navigation at that width (AGL-3770). A caller's own
|
|
87
|
+
// `options.minimap` still wins.
|
|
88
|
+
const compact = useMediaQuery(theme.breakpoints.down('md'));
|
|
89
|
+
const { options } = props, rest = _object_without_properties_loose(props, [
|
|
90
|
+
"options"
|
|
91
|
+
]);
|
|
83
92
|
return /*#__PURE__*/ _jsx(ReactMonacoEditor, _extends({
|
|
84
93
|
defaultLanguage: "json",
|
|
85
|
-
theme: resolved === 'dark' ? 'vs-dark' : 'light'
|
|
86
|
-
|
|
94
|
+
theme: resolved === 'dark' ? 'vs-dark' : 'light',
|
|
95
|
+
options: _extends({
|
|
96
|
+
minimap: {
|
|
97
|
+
enabled: !compact
|
|
98
|
+
}
|
|
99
|
+
}, options)
|
|
100
|
+
}, rest));
|
|
87
101
|
};
|
|
88
102
|
MonacoEditor.displayName = 'MonacoEditor';
|
|
89
103
|
export default MonacoEditor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/json-editor/src/lib/components/monaco-editor.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2023 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport ReactMonacoEditor, { EditorProps, loader } from '@monaco-editor/react'\nimport { useColorScheme, useTheme } from '@mui/material/styles'\n\n/**\n * Where Monaco's AMD bundle is served from — our origin, never a CDN.\n *\n * `apps/console/next.config.js` copies `monaco-editor/min/vs` into\n * `apps/console/public/_static/monaco/vs` at build time and FAILS THE BUILD if\n * it cannot, so this path is either correct or the build never shipped.\n *\n * UNDER `_static`, AND NOT AT THE APEX (AGL-3208).\n *\n * This was `/monaco/vs`, and the console's router answered 404 to every byte\n * of it. `apps/console/middleware.ts` reads an unrecognized first path segment\n * as a workspace slug and refuses an unknown one with a bare 404 (AGL-3017),\n * so `monaco` was looked up as an org and was not one; on a workspace\n * subdomain the same path would instead have been rewritten to\n * `/{slug}/monaco/vs/...`. Monaco never loaded, and the Edit JSON dialog sat\n * on its loading state with nothing on screen to say why.\n *\n * `_static` is the namespace the console already reserves for assets: it is\n * excluded from the middleware matcher, and `isConsoleRouteSegment` admits\n * anything beginning `_`. Vendoring under it is what keeps this fixed —\n * a fourth top-level exception would only be as durable as the next gate's\n * memory of it, and three gates have now claimed unmatched first segments.\n */\nexport const MONACO_VS_PATH = '/_static/monaco/vs'\n\n/**\n * Point the loader at our own copy before anything can mount (AGL-1779).\n *\n * `@monaco-editor/loader` defaults to\n * `https://cdn.jsdelivr.net/npm/monaco-editor@<version>/min/vs` and `init()`\n * injects `<script src=\"${paths.vs}/loader.js\">` into `document.body` with no\n * `integrity` and no `crossOrigin` — SRI is not reachable through that path at\n * all. That put an unpinned third-party script inside the `app.aglyn.com`\n * origin, with the session cookie, the DOM and every live Firestore listener\n * in scope, for anyone who could open Edit -> Raw JSON: any org member with\n * edit rights, site collaborators included. The console CSP allowed it — the\n * `script-src` carries a bare `https:` on purpose (`strict-dynamic` took\n * violations from 1 to 70), so a jsDelivr compromise was a console-origin XSS.\n *\n * Deliberately NOT wrapped in a try/catch and deliberately without a CDN\n * fallback: falling back would restore exactly the exposure this closes, and\n * would be invisible in every normal run because the local path wins.\n *\n * Module scope, not an effect: this module is `next/dynamic`-imported by\n * `json-editor.tsx`, so it is evaluated before the first `<ReactMonacoEditor>`\n * renders and therefore before `loader.init()` reads `state.config`.\n */\nloader.config({ paths: { vs: MONACO_VS_PATH } })\n\nexport interface MonacoEditorProps extends EditorProps {}\n\nexport const MonacoEditor = (props: MonacoEditorProps) => {\n // Monaco defaults to its light theme regardless of the app's, so the raw\n // JSON editor rendered a white page inside a dark dialog.\n //\n // `palette.mode` is NOT the answer on its own: the console runs a CSS-vars\n // theme whose scheme is driven by a class on <html>, so `palette.mode`\n // stays 'light' whatever the user picked. `useColorScheme` is the live\n // one — `systemMode` when following the OS, else the explicit `mode` —\n // with `palette.mode` kept as the fallback for the non-CSS-vars trees.\n // `props` spreads last, so a caller can still pin a Monaco theme.\n const
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/json-editor/src/lib/components/monaco-editor.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2023 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport ReactMonacoEditor, { EditorProps, loader } from '@monaco-editor/react'\nimport { useColorScheme, useTheme } from '@mui/material/styles'\nimport useMediaQuery from '@mui/material/useMediaQuery'\n\n/**\n * Where Monaco's AMD bundle is served from — our origin, never a CDN.\n *\n * `apps/console/next.config.js` copies `monaco-editor/min/vs` into\n * `apps/console/public/_static/monaco/vs` at build time and FAILS THE BUILD if\n * it cannot, so this path is either correct or the build never shipped.\n *\n * UNDER `_static`, AND NOT AT THE APEX (AGL-3208).\n *\n * This was `/monaco/vs`, and the console's router answered 404 to every byte\n * of it. `apps/console/middleware.ts` reads an unrecognized first path segment\n * as a workspace slug and refuses an unknown one with a bare 404 (AGL-3017),\n * so `monaco` was looked up as an org and was not one; on a workspace\n * subdomain the same path would instead have been rewritten to\n * `/{slug}/monaco/vs/...`. Monaco never loaded, and the Edit JSON dialog sat\n * on its loading state with nothing on screen to say why.\n *\n * `_static` is the namespace the console already reserves for assets: it is\n * excluded from the middleware matcher, and `isConsoleRouteSegment` admits\n * anything beginning `_`. Vendoring under it is what keeps this fixed —\n * a fourth top-level exception would only be as durable as the next gate's\n * memory of it, and three gates have now claimed unmatched first segments.\n */\nexport const MONACO_VS_PATH = '/_static/monaco/vs'\n\n/**\n * Point the loader at our own copy before anything can mount (AGL-1779).\n *\n * `@monaco-editor/loader` defaults to\n * `https://cdn.jsdelivr.net/npm/monaco-editor@<version>/min/vs` and `init()`\n * injects `<script src=\"${paths.vs}/loader.js\">` into `document.body` with no\n * `integrity` and no `crossOrigin` — SRI is not reachable through that path at\n * all. That put an unpinned third-party script inside the `app.aglyn.com`\n * origin, with the session cookie, the DOM and every live Firestore listener\n * in scope, for anyone who could open Edit -> Raw JSON: any org member with\n * edit rights, site collaborators included. The console CSP allowed it — the\n * `script-src` carries a bare `https:` on purpose (`strict-dynamic` took\n * violations from 1 to 70), so a jsDelivr compromise was a console-origin XSS.\n *\n * Deliberately NOT wrapped in a try/catch and deliberately without a CDN\n * fallback: falling back would restore exactly the exposure this closes, and\n * would be invisible in every normal run because the local path wins.\n *\n * Module scope, not an effect: this module is `next/dynamic`-imported by\n * `json-editor.tsx`, so it is evaluated before the first `<ReactMonacoEditor>`\n * renders and therefore before `loader.init()` reads `state.config`.\n */\nloader.config({ paths: { vs: MONACO_VS_PATH } })\n\nexport interface MonacoEditorProps extends EditorProps {}\n\nexport const MonacoEditor = (props: MonacoEditorProps) => {\n // Monaco defaults to its light theme regardless of the app's, so the raw\n // JSON editor rendered a white page inside a dark dialog.\n //\n // `palette.mode` is NOT the answer on its own: the console runs a CSS-vars\n // theme whose scheme is driven by a class on <html>, so `palette.mode`\n // stays 'light' whatever the user picked. `useColorScheme` is the live\n // one — `systemMode` when following the OS, else the explicit `mode` —\n // with `palette.mode` kept as the fallback for the non-CSS-vars trees.\n // `props` spreads last, so a caller can still pin a Monaco theme.\n const theme = useTheme()\n const { mode, systemMode } = useColorScheme()\n const resolved = systemMode ?? mode ?? theme.palette.mode\n // Below `md` the minimap costs a strip of an already narrow editor and is\n // no use for navigation at that width (AGL-3770). A caller's own\n // `options.minimap` still wins.\n const compact = useMediaQuery(theme.breakpoints.down('md'))\n const { options, ...rest } = props\n return (\n <ReactMonacoEditor\n defaultLanguage=\"json\"\n theme={resolved === 'dark' ? 'vs-dark' : 'light'}\n options={{ minimap: { enabled: !compact }, ...options }}\n {...rest}\n />\n )\n}\nMonacoEditor.displayName = 'MonacoEditor'\n\nexport default MonacoEditor\n"],"names":["ReactMonacoEditor","loader","useColorScheme","useTheme","useMediaQuery","MONACO_VS_PATH","config","paths","vs","MonacoEditor","props","systemMode","theme","mode","resolved","palette","compact","breakpoints","down","options","rest","defaultLanguage","minimap","enabled","displayName"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,OAAOA,qBAAkCC,MAAM,QAAQ,uBAAsB;AAC7E,SAASC,cAAc,EAAEC,QAAQ,QAAQ,uBAAsB;AAC/D,OAAOC,mBAAmB,8BAA6B;AAEvD;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,MAAMC,iBAAiB,qBAAoB;AAElD;;;;;;;;;;;;;;;;;;;;;CAqBC,GACDJ,OAAOK,MAAM,CAAC;IAAEC,OAAO;QAAEC,IAAIH;IAAe;AAAE;AAI9C,OAAO,MAAMI,eAAe,CAACC;QAYVC;IAXjB,yEAAyE;IACzE,0DAA0D;IAC1D,EAAE;IACF,2EAA2E;IAC3E,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;IACvE,kEAAkE;IAClE,MAAMC,QAAQT;IACd,MAAM,EAAEU,IAAI,EAAEF,UAAU,EAAE,GAAGT;IAC7B,MAAMY,YAAWH,OAAAA,qBAAAA,aAAcE,gBAAdF,OAAsBC,MAAMG,OAAO,CAACF,IAAI;IACzD,0EAA0E;IAC1E,iEAAiE;IACjE,gCAAgC;IAChC,MAAMG,UAAUZ,cAAcQ,MAAMK,WAAW,CAACC,IAAI,CAAC;IACrD,MAAM,EAAEC,OAAO,EAAW,GAAGT,OAATU,wCAASV;;;IAC7B,qBACE,KAACV;QACCqB,iBAAgB;QAChBT,OAAOE,aAAa,SAAS,YAAY;QACzCK,SAAS;YAAEG,SAAS;gBAAEC,SAAS,CAACP;YAAQ;WAAMG;OAC1CC;AAGV,EAAC;AACDX,aAAae,WAAW,GAAG;AAE3B,eAAef,aAAY"}
|
package/src/lib/json-editor.js
CHANGED
|
@@ -196,18 +196,54 @@ const JsonEditorRaw = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
196
196
|
},
|
|
197
197
|
children: description
|
|
198
198
|
}) : null,
|
|
199
|
+
/*#__PURE__*/ _jsx(When, {
|
|
200
|
+
condition: open && warnOpen,
|
|
201
|
+
children: /*#__PURE__*/ _jsxs(Alert, {
|
|
202
|
+
severity: "warning",
|
|
203
|
+
"data-testid": "json-editor-warning",
|
|
204
|
+
sx: {
|
|
205
|
+
mx: {
|
|
206
|
+
xs: 2,
|
|
207
|
+
sm: 3
|
|
208
|
+
},
|
|
209
|
+
mb: 1,
|
|
210
|
+
flexShrink: 0
|
|
211
|
+
},
|
|
212
|
+
action: /*#__PURE__*/ _jsx(IconButton, {
|
|
213
|
+
onClick: closeWarn,
|
|
214
|
+
color: "inherit",
|
|
215
|
+
"aria-label": "Dismiss this warning",
|
|
216
|
+
title: "Dismiss this warning",
|
|
217
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
218
|
+
path: ICON_VARIANT_CLOSE.path
|
|
219
|
+
})
|
|
220
|
+
}),
|
|
221
|
+
children: [
|
|
222
|
+
/*#__PURE__*/ _jsx(AlertTitle, {
|
|
223
|
+
children: "Warning: Advanced Feature Ahead!"
|
|
224
|
+
}),
|
|
225
|
+
"Using the raw json editor is highly discouraged and should only be used by individuals who understand the consequences. Changes may potentially result in undesired outcomes which are ",
|
|
226
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
227
|
+
children: "destructive and irreversible"
|
|
228
|
+
}),
|
|
229
|
+
"."
|
|
230
|
+
]
|
|
231
|
+
})
|
|
232
|
+
}),
|
|
199
233
|
saveError ? /*#__PURE__*/ _jsx(Alert, {
|
|
200
234
|
severity: "error",
|
|
201
235
|
sx: {
|
|
202
236
|
mx: 3,
|
|
203
|
-
mb: 1
|
|
237
|
+
mb: 1,
|
|
238
|
+
flexShrink: 0
|
|
204
239
|
},
|
|
205
240
|
children: saveError
|
|
206
241
|
}) : parseError ? /*#__PURE__*/ _jsxs(Alert, {
|
|
207
242
|
severity: "warning",
|
|
208
243
|
sx: {
|
|
209
244
|
mx: 3,
|
|
210
|
-
mb: 1
|
|
245
|
+
mb: 1,
|
|
246
|
+
flexShrink: 0
|
|
211
247
|
},
|
|
212
248
|
children: [
|
|
213
249
|
'Not valid JSON yet — ',
|
|
@@ -218,64 +254,20 @@ const JsonEditorRaw = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
218
254
|
}) : null,
|
|
219
255
|
/*#__PURE__*/ _jsx(When, {
|
|
220
256
|
condition: open,
|
|
221
|
-
children: /*#__PURE__*/
|
|
257
|
+
children: /*#__PURE__*/ _jsx(Box, {
|
|
258
|
+
"data-testid": "json-editor-pane",
|
|
222
259
|
sx: {
|
|
223
260
|
flex: 1,
|
|
224
261
|
minHeight: 0,
|
|
225
|
-
position: 'relative'
|
|
262
|
+
position: 'relative',
|
|
263
|
+
isolation: 'isolate'
|
|
226
264
|
},
|
|
227
|
-
children:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}),
|
|
234
|
-
/*#__PURE__*/ _jsx(When, {
|
|
235
|
-
condition: warnOpen,
|
|
236
|
-
children: /*#__PURE__*/ _jsx(Box, {
|
|
237
|
-
sx: {
|
|
238
|
-
position: 'absolute',
|
|
239
|
-
inset: 0,
|
|
240
|
-
zIndex: 1,
|
|
241
|
-
bgcolor: 'action.disabled',
|
|
242
|
-
display: 'flex',
|
|
243
|
-
alignItems: 'center',
|
|
244
|
-
justifyContent: 'center',
|
|
245
|
-
backdropFilter: 'blur(2px)',
|
|
246
|
-
p: {
|
|
247
|
-
xs: 2,
|
|
248
|
-
sm: 3
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
children: /*#__PURE__*/ _jsxs(Alert, {
|
|
252
|
-
severity: "warning",
|
|
253
|
-
sx: {
|
|
254
|
-
maxWidth: 620
|
|
255
|
-
},
|
|
256
|
-
action: /*#__PURE__*/ _jsx(IconButton, {
|
|
257
|
-
onClick: closeWarn,
|
|
258
|
-
color: "inherit",
|
|
259
|
-
"aria-label": "Dismiss this warning",
|
|
260
|
-
title: "Dismiss this warning",
|
|
261
|
-
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
262
|
-
path: ICON_VARIANT_CLOSE.path
|
|
263
|
-
})
|
|
264
|
-
}),
|
|
265
|
-
children: [
|
|
266
|
-
/*#__PURE__*/ _jsx(AlertTitle, {
|
|
267
|
-
children: "Warning: Advanced Feature Ahead!"
|
|
268
|
-
}),
|
|
269
|
-
"Using the raw json editor is highly discouraged and should only be used by individuals who understand the consequences. Changes may potentially result in undesired outcomes which are ",
|
|
270
|
-
/*#__PURE__*/ _jsx("strong", {
|
|
271
|
-
children: "destructive and irreversible"
|
|
272
|
-
}),
|
|
273
|
-
"."
|
|
274
|
-
]
|
|
275
|
-
})
|
|
276
|
-
})
|
|
277
|
-
})
|
|
278
|
-
]
|
|
265
|
+
children: /*#__PURE__*/ _jsx(Editor, {
|
|
266
|
+
height: "100%",
|
|
267
|
+
defaultValue: documentText,
|
|
268
|
+
value: data,
|
|
269
|
+
onChange: handleChange
|
|
270
|
+
})
|
|
279
271
|
})
|
|
280
272
|
})
|
|
281
273
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/shared/ui/json-editor/src/lib/json-editor.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n type DialogProps,\n DialogTitle,\n IconButton,\n} from '@mui/material'\nimport { observer } from 'mobx-react-lite'\nimport dynamic from 'next/dynamic'\nimport {\n forwardRef,\n SyntheticEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { When } from 'react-if'\nimport type { CodeMirrorProps } from './components/code-mirror-editor'\nimport type { MonacoEditorProps } from './components/monaco-editor'\n\nconst Editor = dynamic<MonacoEditorProps>(\n () => import('./components/monaco-editor'),\n {\n ssr: false,\n /*\n A dynamic import with no `loading` renders `null` (AGL-2541).\n\n That is indistinguishable from an editor that will never arrive: the\n same empty box while the chunk is in flight, and the same empty box\n forever if it fails. The report this fixes was exactly that — Raw JSON\n opens on a blank pane with no console error — leaving nothing on screen\n to say whether to wait, reload, or give up.\n */\n loading: () => (\n <Box\n sx={{\n flex: 1,\n minHeight: 0,\n display: 'grid',\n placeItems: 'center',\n color: 'text.secondary',\n }}\n >\n {'Loading the editor…'}\n </Box>\n ),\n },\n)\n\ntype OnClose = {\n bivarianceHack(\n event: any,\n reason: 'backdropClick' | 'escapeKeyDown' | 'saveClick' | 'cancelClick',\n ): void\n}['bivarianceHack']\ntype OnSave = {\n bivarianceHack(event: SyntheticEvent<any, any>, value: iJSON): void\n}['bivarianceHack']\n\nexport interface JsonEditorProps\n extends Omit<DialogProps, 'defaultValue' | 'title'> {\n defaultValue?: CodeMirrorProps['defaultValue']\n onSave?: OnSave\n onClose?: OnClose\n /** Dialog title; defaults to the classic \"Raw JSON\". */\n title?: React.ReactNode\n /** Optional hint rendered under the title (e.g. subtree scope note). */\n description?: React.ReactNode\n /**\n * Pre-save check (AGL-457): return an error message to keep the dialog\n * open and surface it instead of saving.\n */\n validate?: (value: iJSON) => string | null | undefined\n}\n\nconst JsonEditorRaw = forwardRef<any, JsonEditorProps>(\n (props, ref) => {\n const {\n onClose,\n onSave,\n defaultValue,\n open,\n title = 'Raw JSON',\n description,\n validate,\n ...rest\n } = props\n /** The stored document, as the text a buffer is seeded from. */\n const documentText = useMemo(\n () => JSON.stringify(defaultValue || {}, null, 2),\n [defaultValue],\n )\n const [data, setData] = useState(documentText)\n const [warnOpen, setWarnOpen] = useState(true)\n const [saveError, setSaveError] = useState<string | null>(null)\n const closeWarn = useCallback(() => setWarnOpen(false), [])\n\n /**\n * The document text this buffer was seeded from, or `null` while closed.\n *\n * `data !== seededFrom.current` is therefore \"the author has typed\n * something that is not yet saved\", which is the one question both the\n * re-seed below and the backdrop guard need answered.\n */\n const seededFrom = useRef<string | null>(null)\n\n /**\n * Seed the buffer, and NEVER re-seed one that has been typed into\n * (AGL-2486 item 17).\n *\n * `defaultValue` is `Aglyn.canvas.nestedNodes` at every call site — a\n * freshly built object, from a MobX store this dialog re-renders with.\n * The previous version compared the incoming document against the BUFFER\n * and re-seeded whenever they differed, which is true by definition the\n * moment the author types: any canvas change, autosave or co-edit echo\n * then replaced their work in progress with the stored document.\n *\n * Re-seeding a PRISTINE buffer is still right — nothing is lost, and the\n * dialog should not show a document the canvas has moved past.\n */\n useEffect(() => {\n if (!open) {\n // A closed dialog holds nothing: the next open seeds from whatever\n // the document says then, rather than from a buffer walked away from.\n seededFrom.current = null\n return\n }\n setData(prev => {\n if (seededFrom.current !== null && prev !== seededFrom.current) {\n return prev\n }\n seededFrom.current = documentText\n return documentText\n })\n }, [documentText, open])\n\n /**\n * Why the buffer does not parse, or `null` when it does.\n *\n * Surfaced as a live, NON-destructive notice: a half-written document is\n * allowed to be invalid, so the only thing to do about it is say so.\n */\n const parseError = useMemo<string | null>(() => {\n if (!open) return null\n try {\n JSON.parse(data)\n return null\n } catch (e: any) {\n return e?.message ?? 'Invalid JSON'\n }\n }, [data, open])\n\n const handleChange = useCallback((value: any) => {\n setData(value)\n setSaveError(null)\n }, [])\n const handleClose = useCallback<OnClose>(\n (event, reason) => {\n // A stray click outside a 95vw dialog must not destroy an unsaved\n // buffer. Cancel and Escape are deliberate acts and still close.\n if (reason === 'backdropClick' && data !== seededFrom.current) return\n setSaveError(null)\n onClose && onClose(event, reason)\n },\n [onClose, data],\n )\n const handleSave = useCallback(\n (event: any) => {\n // Parse the raw text directly so malformed JSON blocks the save\n // with an error instead of silently saving `{}` (AGL-457).\n let value: iJSON\n try {\n value = JSON.parse(data)\n } catch (error: any) {\n setSaveError(error?.message ?? 'Invalid JSON')\n return\n }\n const problem = validate?.(value)\n if (problem) {\n setSaveError(problem)\n return\n }\n onSave && onSave(event, value)\n handleClose(event, 'saveClick')\n },\n [onSave, handleClose, data, validate],\n )\n\n return (\n <Dialog\n ref={ref}\n open={open}\n // A raw-JSON editor is unusable in a small box — component\n // overrides run to hundreds of lines. Fill most of the viewport in\n // BOTH axes; `rest` spreads after, so a caller can still override.\n maxWidth={false}\n fullWidth\n slotProps={{\n paper: {\n sx: {\n width: '95vw',\n maxWidth: 'none',\n height: '92vh',\n maxHeight: 'none',\n },\n },\n }}\n onClose={handleClose}\n // keepMounted\n {...rest}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogContent\n sx={{\n position: 'relative',\n p: 0,\n display: 'flex',\n flexDirection: 'column',\n minHeight: 0,\n }}\n >\n {description ? (\n <Box sx={{ px: 3, pb: 1, color: 'text.secondary' }}>\n {description}\n </Box>\n ) : null}\n {saveError ? (\n <Alert severity=\"error\" sx={{ mx: 3, mb: 1 }}>\n {saveError}\n </Alert>\n ) : parseError ? (\n <Alert severity=\"warning\" sx={{ mx: 3, mb: 1 }}>\n {'Not valid JSON yet — '}\n {parseError}\n {'. Your text is kept exactly as typed; Save JSON works again '}\n {'once it parses.'}\n </Alert>\n ) : null}\n <When condition={open}>\n {/*\n The editor mounts UNDER the warning, not instead of it\n (AGL-2541).\n\n The gate used to be an `If`/`Else`, so while the warning stood\n the `Editor` was not rendered at all — its chunk was never\n requested and Monaco's loader never ran. An author who dismissed\n the warning was then waiting on a download that had not started,\n against a pane that renders nothing while it waits. That is the\n \"the editor never mounts, and no request for loader.js is ever\n issued\" report: both halves are true, and neither is Monaco's.\n\n Rendering both, warning stacked above, puts the chunk in flight\n while the warning is being read. The overlay was already written\n to be an overlay — full-bleed, blurred, over a translucent wash\n — it was only ever wired as a replacement.\n\n `value` is the author's buffer VERBATIM, and nothing else\n (AGL-2486 item 17). It used to be\n `JSON.stringify(JSON.parse(data) ?? {})`, whose parse fell back\n to `{}`. `@monaco-editor/react` treats `value` as authoritative\n — on every change it runs `executeEdits(fullModelRange, value)`\n — so the first keystroke that left the buffer transiently\n unparseable replaced the whole document on screen with `{}`. A\n comma does that, which is exactly how it was reported. Worse,\n that overwrite is made with `onChange` suppressed, so `data`\n kept text the editor no longer showed and there was nothing to\n recover from. Feeding the buffer back unchanged means the prop\n always equals `editor.getValue()`, so the effect's `value !==`\n guard never fires from typing and no keystroke can rewrite the\n document. It also stops the buffer being re-indented under the\n cursor whenever the author's valid text was not already 2-space\n pretty-printed.\n */}\n <Box sx={{ flex: 1, minHeight: 0, position: 'relative' }}>\n <Editor\n height=\"100%\"\n defaultValue={documentText}\n value={data}\n onChange={handleChange}\n />\n <When condition={warnOpen}>\n <Box\n sx={{\n position: 'absolute',\n inset: 0,\n zIndex: 1,\n bgcolor: 'action.disabled',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backdropFilter: 'blur(2px)',\n p: { xs: 2, sm: 3 }\n }}>\n <Alert\n severity=\"warning\"\n sx={{\n maxWidth: 620,\n }}\n action={\n <IconButton\n onClick={closeWarn}\n color=\"inherit\"\n aria-label=\"Dismiss this warning\"\n title=\"Dismiss this warning\"\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n </IconButton>\n }\n >\n <AlertTitle>Warning: Advanced Feature Ahead!</AlertTitle>\n Using the raw json editor is highly discouraged and should\n only be used by individuals who understand the consequences.\n Changes may potentially result in undesired outcomes which\n are <strong>destructive and irreversible</strong>.\n </Alert>\n </Box>\n </When>\n </Box>\n </When>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"contained\"\n onClick={(e) => handleClose(e, 'cancelClick')}\n >\n {'Cancel'}\n </Button>\n <Button variant=\"contained\" onClick={handleSave}>\n {'Save JSON'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n },\n)\nJsonEditorRaw.displayName = 'JsonEditor'\n\nexport const JsonEditor = observer(JsonEditorRaw)\n\nexport default JsonEditor\n"],"names":["ICON_VARIANT_CLOSE","MdiIcon","Alert","AlertTitle","Box","Button","Dialog","DialogActions","DialogContent","DialogTitle","IconButton","observer","dynamic","forwardRef","useCallback","useEffect","useMemo","useRef","useState","When","Editor","ssr","loading","sx","flex","minHeight","display","placeItems","color","JsonEditorRaw","props","ref","onClose","onSave","defaultValue","open","title","description","validate","rest","documentText","JSON","stringify","data","setData","warnOpen","setWarnOpen","saveError","setSaveError","closeWarn","seededFrom","current","prev","parseError","parse","e","message","handleChange","value","handleClose","event","reason","handleSave","error","problem","maxWidth","fullWidth","slotProps","paper","width","height","maxHeight","position","p","flexDirection","px","pb","severity","mx","mb","condition","onChange","inset","zIndex","bgcolor","alignItems","justifyContent","backdropFilter","xs","sm","action","onClick","aria-label","path","strong","variant","displayName","JsonEditor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/shared/ui/json-editor/src/lib/json-editor.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n type DialogProps,\n DialogTitle,\n IconButton,\n} from '@mui/material'\nimport { observer } from 'mobx-react-lite'\nimport dynamic from 'next/dynamic'\nimport {\n forwardRef,\n SyntheticEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { When } from 'react-if'\nimport type { CodeMirrorProps } from './components/code-mirror-editor'\nimport type { MonacoEditorProps } from './components/monaco-editor'\n\nconst Editor = dynamic<MonacoEditorProps>(\n () => import('./components/monaco-editor'),\n {\n ssr: false,\n /*\n A dynamic import with no `loading` renders `null` (AGL-2541).\n\n That is indistinguishable from an editor that will never arrive: the\n same empty box while the chunk is in flight, and the same empty box\n forever if it fails. The report this fixes was exactly that — Raw JSON\n opens on a blank pane with no console error — leaving nothing on screen\n to say whether to wait, reload, or give up.\n */\n loading: () => (\n <Box\n sx={{\n flex: 1,\n minHeight: 0,\n display: 'grid',\n placeItems: 'center',\n color: 'text.secondary',\n }}\n >\n {'Loading the editor…'}\n </Box>\n ),\n },\n)\n\ntype OnClose = {\n bivarianceHack(\n event: any,\n reason: 'backdropClick' | 'escapeKeyDown' | 'saveClick' | 'cancelClick',\n ): void\n}['bivarianceHack']\ntype OnSave = {\n bivarianceHack(event: SyntheticEvent<any, any>, value: iJSON): void\n}['bivarianceHack']\n\nexport interface JsonEditorProps\n extends Omit<DialogProps, 'defaultValue' | 'title'> {\n defaultValue?: CodeMirrorProps['defaultValue']\n onSave?: OnSave\n onClose?: OnClose\n /** Dialog title; defaults to the classic \"Raw JSON\". */\n title?: React.ReactNode\n /** Optional hint rendered under the title (e.g. subtree scope note). */\n description?: React.ReactNode\n /**\n * Pre-save check (AGL-457): return an error message to keep the dialog\n * open and surface it instead of saving.\n */\n validate?: (value: iJSON) => string | null | undefined\n}\n\nconst JsonEditorRaw = forwardRef<any, JsonEditorProps>(\n (props, ref) => {\n const {\n onClose,\n onSave,\n defaultValue,\n open,\n title = 'Raw JSON',\n description,\n validate,\n ...rest\n } = props\n /** The stored document, as the text a buffer is seeded from. */\n const documentText = useMemo(\n () => JSON.stringify(defaultValue || {}, null, 2),\n [defaultValue],\n )\n const [data, setData] = useState(documentText)\n const [warnOpen, setWarnOpen] = useState(true)\n const [saveError, setSaveError] = useState<string | null>(null)\n const closeWarn = useCallback(() => setWarnOpen(false), [])\n\n /**\n * The document text this buffer was seeded from, or `null` while closed.\n *\n * `data !== seededFrom.current` is therefore \"the author has typed\n * something that is not yet saved\", which is the one question both the\n * re-seed below and the backdrop guard need answered.\n */\n const seededFrom = useRef<string | null>(null)\n\n /**\n * Seed the buffer, and NEVER re-seed one that has been typed into\n * (AGL-2486 item 17).\n *\n * `defaultValue` is `Aglyn.canvas.nestedNodes` at every call site — a\n * freshly built object, from a MobX store this dialog re-renders with.\n * The previous version compared the incoming document against the BUFFER\n * and re-seeded whenever they differed, which is true by definition the\n * moment the author types: any canvas change, autosave or co-edit echo\n * then replaced their work in progress with the stored document.\n *\n * Re-seeding a PRISTINE buffer is still right — nothing is lost, and the\n * dialog should not show a document the canvas has moved past.\n */\n useEffect(() => {\n if (!open) {\n // A closed dialog holds nothing: the next open seeds from whatever\n // the document says then, rather than from a buffer walked away from.\n seededFrom.current = null\n return\n }\n setData(prev => {\n if (seededFrom.current !== null && prev !== seededFrom.current) {\n return prev\n }\n seededFrom.current = documentText\n return documentText\n })\n }, [documentText, open])\n\n /**\n * Why the buffer does not parse, or `null` when it does.\n *\n * Surfaced as a live, NON-destructive notice: a half-written document is\n * allowed to be invalid, so the only thing to do about it is say so.\n */\n const parseError = useMemo<string | null>(() => {\n if (!open) return null\n try {\n JSON.parse(data)\n return null\n } catch (e: any) {\n return e?.message ?? 'Invalid JSON'\n }\n }, [data, open])\n\n const handleChange = useCallback((value: any) => {\n setData(value)\n setSaveError(null)\n }, [])\n const handleClose = useCallback<OnClose>(\n (event, reason) => {\n // A stray click outside a 95vw dialog must not destroy an unsaved\n // buffer. Cancel and Escape are deliberate acts and still close.\n if (reason === 'backdropClick' && data !== seededFrom.current) return\n setSaveError(null)\n onClose && onClose(event, reason)\n },\n [onClose, data],\n )\n const handleSave = useCallback(\n (event: any) => {\n // Parse the raw text directly so malformed JSON blocks the save\n // with an error instead of silently saving `{}` (AGL-457).\n let value: iJSON\n try {\n value = JSON.parse(data)\n } catch (error: any) {\n setSaveError(error?.message ?? 'Invalid JSON')\n return\n }\n const problem = validate?.(value)\n if (problem) {\n setSaveError(problem)\n return\n }\n onSave && onSave(event, value)\n handleClose(event, 'saveClick')\n },\n [onSave, handleClose, data, validate],\n )\n\n return (\n <Dialog\n ref={ref}\n open={open}\n // A raw-JSON editor is unusable in a small box — component\n // overrides run to hundreds of lines. Fill most of the viewport in\n // BOTH axes; `rest` spreads after, so a caller can still override.\n maxWidth={false}\n fullWidth\n slotProps={{\n paper: {\n sx: {\n width: '95vw',\n maxWidth: 'none',\n height: '92vh',\n maxHeight: 'none',\n },\n },\n }}\n onClose={handleClose}\n // keepMounted\n {...rest}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogContent\n sx={{\n position: 'relative',\n p: 0,\n display: 'flex',\n flexDirection: 'column',\n minHeight: 0,\n }}\n >\n {description ? (\n <Box sx={{ px: 3, pb: 1, color: 'text.secondary' }}>\n {description}\n </Box>\n ) : null}\n <When condition={open && warnOpen}>\n {/*\n The warning is a banner IN the dialog's column, above the\n editor, and the editor is pushed down by it (AGL-3770).\n\n It must never be layered over Monaco. Monaco positions its\n minimap, scrollbars and overview ruler with z-indexes of its\n own, and on a narrow viewport they land exactly where the\n alert's close button sits: the button was drawn UNDER the\n minimap, so the warning could not be dismissed and covered the\n editor for good. In normal flow there is nothing to stack\n against, and the editor pane below is `isolation: isolate` so\n none of Monaco's layers can escape it either way.\n */}\n <Alert\n severity=\"warning\"\n data-testid=\"json-editor-warning\"\n sx={{ mx: { xs: 2, sm: 3 }, mb: 1, flexShrink: 0 }}\n action={\n <IconButton\n onClick={closeWarn}\n color=\"inherit\"\n aria-label=\"Dismiss this warning\"\n title=\"Dismiss this warning\"\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n </IconButton>\n }\n >\n <AlertTitle>Warning: Advanced Feature Ahead!</AlertTitle>\n Using the raw json editor is highly discouraged and should\n only be used by individuals who understand the consequences.\n Changes may potentially result in undesired outcomes which\n are <strong>destructive and irreversible</strong>.\n </Alert>\n </When>\n {saveError ? (\n <Alert severity=\"error\" sx={{ mx: 3, mb: 1, flexShrink: 0 }}>\n {saveError}\n </Alert>\n ) : parseError ? (\n <Alert severity=\"warning\" sx={{ mx: 3, mb: 1, flexShrink: 0 }}>\n {'Not valid JSON yet — '}\n {parseError}\n {'. Your text is kept exactly as typed; Save JSON works again '}\n {'once it parses.'}\n </Alert>\n ) : null}\n <When condition={open}>\n {/*\n The editor mounts while the warning is showing, not instead of\n it (AGL-2541).\n\n Gated on the warning, the `Editor` was not rendered at all while\n the warning stood — its chunk was never requested and Monaco's\n loader never ran. An author who dismissed the warning was then\n waiting on a download that had not started, against a pane that\n renders nothing while it waits: \"the editor never mounts, and no\n request for loader.js is ever issued\". Rendering both puts the\n chunk in flight while the warning is being read.\n\n `value` is the author's buffer VERBATIM, and nothing else\n (AGL-2486 item 17). It used to be\n `JSON.stringify(JSON.parse(data) ?? {})`, whose parse fell back\n to `{}`. `@monaco-editor/react` treats `value` as authoritative\n — on every change it runs `executeEdits(fullModelRange, value)`\n — so the first keystroke that left the buffer transiently\n unparseable replaced the whole document on screen with `{}`. A\n comma does that, which is exactly how it was reported. Worse,\n that overwrite is made with `onChange` suppressed, so `data`\n kept text the editor no longer showed and there was nothing to\n recover from. Feeding the buffer back unchanged means the prop\n always equals `editor.getValue()`, so the effect's `value !==`\n guard never fires from typing and no keystroke can rewrite the\n document. It also stops the buffer being re-indented under the\n cursor whenever the author's valid text was not already 2-space\n pretty-printed.\n */}\n <Box\n data-testid=\"json-editor-pane\"\n sx={{\n flex: 1,\n minHeight: 0,\n position: 'relative',\n isolation: 'isolate',\n }}\n >\n <Editor\n height=\"100%\"\n defaultValue={documentText}\n value={data}\n onChange={handleChange}\n />\n </Box>\n </When>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"contained\"\n onClick={(e) => handleClose(e, 'cancelClick')}\n >\n {'Cancel'}\n </Button>\n <Button variant=\"contained\" onClick={handleSave}>\n {'Save JSON'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n },\n)\nJsonEditorRaw.displayName = 'JsonEditor'\n\nexport const JsonEditor = observer(JsonEditorRaw)\n\nexport default JsonEditor\n"],"names":["ICON_VARIANT_CLOSE","MdiIcon","Alert","AlertTitle","Box","Button","Dialog","DialogActions","DialogContent","DialogTitle","IconButton","observer","dynamic","forwardRef","useCallback","useEffect","useMemo","useRef","useState","When","Editor","ssr","loading","sx","flex","minHeight","display","placeItems","color","JsonEditorRaw","props","ref","onClose","onSave","defaultValue","open","title","description","validate","rest","documentText","JSON","stringify","data","setData","warnOpen","setWarnOpen","saveError","setSaveError","closeWarn","seededFrom","current","prev","parseError","parse","e","message","handleChange","value","handleClose","event","reason","handleSave","error","problem","maxWidth","fullWidth","slotProps","paper","width","height","maxHeight","position","p","flexDirection","px","pb","condition","severity","data-testid","mx","xs","sm","mb","flexShrink","action","onClick","aria-label","path","strong","isolation","onChange","variant","displayName","JsonEditor"],"mappings":"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"}
|