From d8716924d65ef2f3a92f1c9cacbc9e7d749bd981 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Sat, 26 Feb 2022 16:47:45 -0500 Subject: [PATCH] feat(TemplatePreviewPage): implement a view for previewing template form UIs Signed-off-by: Phil Kuang --- .changeset/ten-otters-act.md | 5 + packages/app/src/App.tsx | 2 + .../scaffolder/defaultPreviewTemplate.ts | 54 ++++ plugins/scaffolder/api-report.md | 1 + plugins/scaffolder/package.json | 7 +- .../MultistepJsonForm/MultistepJsonForm.tsx | 8 +- plugins/scaffolder/src/components/Router.tsx | 15 +- .../src/components/TemplatePage/index.ts | 2 +- .../TemplatePreviewPage.tsx | 269 ++++++++++++++++++ .../components/TemplatePreviewPage/index.ts | 16 ++ yarn.lock | 235 +++++++++++++-- 11 files changed, 584 insertions(+), 30 deletions(-) create mode 100644 .changeset/ten-otters-act.md create mode 100644 packages/app/src/components/scaffolder/defaultPreviewTemplate.ts create mode 100644 plugins/scaffolder/src/components/TemplatePreviewPage/TemplatePreviewPage.tsx create mode 100644 plugins/scaffolder/src/components/TemplatePreviewPage/index.ts diff --git a/.changeset/ten-otters-act.md b/.changeset/ten-otters-act.md new file mode 100644 index 0000000000..c9c755820c --- /dev/null +++ b/.changeset/ten-otters-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Implement a template preview page (`/create/preview`) to test creating form UIs diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index befd3274f3..ce765bd312 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -79,6 +79,7 @@ import { entityPage } from './components/catalog/EntityPage'; import { homePage } from './components/home/HomePage'; import { Root } from './components/Root'; import { LowerCaseValuePickerFieldExtension } from './components/scaffolder/customScaffolderExtensions'; +import { defaultPreviewTemplate } from './components/scaffolder/defaultPreviewTemplate'; import { searchPage } from './components/search/SearchPage'; import { providers } from './identityProviders'; import * as plugins from './plugins'; @@ -179,6 +180,7 @@ const routes = ( path="/create" element={ boolean; }>; + defaultPreviewTemplate?: string; }; // @public diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index dcdeeac1c9..8293d086ef 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -34,7 +34,6 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@types/json-schema": "^7.0.9", "@backstage/catalog-client": "^0.9.0", "@backstage/catalog-model": "^0.13.0", "@backstage/config": "^0.1.15", @@ -49,11 +48,16 @@ "@backstage/plugin-scaffolder-common": "^0.3.0", "@backstage/theme": "^0.2.15", "@backstage/types": "^0.1.3", + "@codemirror/legacy-modes": "^0.19.0", + "@codemirror/panel": "^0.19.1", + "@codemirror/stream-parser": "^0.19.6", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "@rjsf/core": "^3.2.1", "@rjsf/material-ui": "^3.2.1", + "@types/json-schema": "^7.0.9", + "@uiw/react-codemirror": "^4.5.1", "classnames": "^2.2.6", "git-url-parse": "^11.6.0", "humanize-duration": "^3.25.1", @@ -66,6 +70,7 @@ "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4", "use-immer": "^0.6.0", + "yaml": "^1.9.2", "zen-observable": "^0.8.15" }, "peerDependencies": { diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index 4d37eafee4..a868c50d7a 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -51,7 +51,7 @@ type Props = { formData: Record; onChange: (e: IChangeEvent) => void; onReset: () => void; - onFinish: () => Promise; + onFinish?: () => Promise; widgets?: FormProps['widgets']; fields?: FormProps['fields']; }; @@ -163,6 +163,10 @@ export const MultistepJsonForm = (props: Props) => { }; const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0)); const handleCreate = async () => { + if (!onFinish) { + return; + } + setDisableButtons(true); try { await onFinish(); @@ -232,7 +236,7 @@ export const MultistepJsonForm = (props: Props) => { variant="contained" color="primary" onClick={handleCreate} - disabled={disableButtons} + disabled={!onFinish || disableButtons} > Create diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 261556d638..ef4f373ef2 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -23,6 +23,7 @@ import { TemplatePage } from './TemplatePage'; import { TaskPage } from './TaskPage'; import { ActionsPage } from './ActionsPage'; import { SecretsContextProvider } from './secrets/SecretsContext'; +import { TemplatePreviewPage } from './TemplatePreviewPage'; import { FieldExtensionOptions, @@ -48,6 +49,7 @@ export type RouterProps = { titleComponent?: React.ReactNode; filter: (entity: Entity) => boolean; }>; + defaultPreviewTemplate?: string; }; /** @@ -56,7 +58,7 @@ export type RouterProps = { * @public */ export const Router = (props: RouterProps) => { - const { groups, components = {} } = props; + const { groups, components = {}, defaultPreviewTemplate } = props; const { TemplateCardComponent, TaskPageComponent } = components; @@ -104,6 +106,17 @@ export const Router = (props: RouterProps) => { /> } /> } /> + + + + } + /> ); }; diff --git a/plugins/scaffolder/src/components/TemplatePage/index.ts b/plugins/scaffolder/src/components/TemplatePage/index.ts index f796bd6500..b01da032e5 100644 --- a/plugins/scaffolder/src/components/TemplatePage/index.ts +++ b/plugins/scaffolder/src/components/TemplatePage/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { TemplatePage } from './TemplatePage'; +export { createValidator, TemplatePage } from './TemplatePage'; diff --git a/plugins/scaffolder/src/components/TemplatePreviewPage/TemplatePreviewPage.tsx b/plugins/scaffolder/src/components/TemplatePreviewPage/TemplatePreviewPage.tsx new file mode 100644 index 0000000000..cb4f8e9896 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplatePreviewPage/TemplatePreviewPage.tsx @@ -0,0 +1,269 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useCallback, useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import useDebounce from 'react-use/lib/useDebounce'; +import { Entity } from '@backstage/catalog-model'; +import { Content, Header, InfoCard, Page } from '@backstage/core-components'; +import { alertApiRef, useApi, useApiHolder } from '@backstage/core-plugin-api'; +import { + catalogApiRef, + humanizeEntityRef, +} from '@backstage/plugin-catalog-react'; +import { JsonObject } from '@backstage/types'; +import { yaml as yamlSupport } from '@codemirror/legacy-modes/mode/yaml'; +import { showPanel } from '@codemirror/panel'; +import { StreamLanguage } from '@codemirror/stream-parser'; +import { + FormControl, + Grid, + InputLabel, + LinearProgress, + makeStyles, + MenuItem, + Select, +} from '@material-ui/core'; +import { IChangeEvent } from '@rjsf/core'; +import CodeMirror from '@uiw/react-codemirror'; +import yaml from 'yaml'; +import { FieldExtensionOptions } from '../../extensions'; +import { TemplateParameterSchema } from '../../types'; +import { MultistepJsonForm } from '../MultistepJsonForm'; +import { createValidator } from '../TemplatePage'; + +const EXAMPLE_TEMPLATE_PARAMS_YAML = `# Edit the template parameters below to see how they will render in the scaffolder form UI +parameters: + - title: Fill in some steps + required: + - name + properties: + name: + title: Name + type: string + description: Unique name of the component + owner: + title: Owner + type: string + description: Owner of the component + ui:field: OwnerPicker + ui:options: + allowedKinds: + - Group + - title: Choose a location + required: + - repoUrl + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com +`; + +type TemplateOption = { + label: string; + value: Entity; +}; + +const useStyles = makeStyles({ + templateSelect: { + marginBottom: '10px', + }, + grid: { + height: '100%', + }, + codeMirror: { + height: '95%', + }, +}); + +export const TemplatePreviewPage = ({ + defaultPreviewTemplate = EXAMPLE_TEMPLATE_PARAMS_YAML, + customFieldExtensions = [], +}: { + defaultPreviewTemplate?: string; + customFieldExtensions?: FieldExtensionOptions[]; +}) => { + const classes = useStyles(); + const alertApi = useApi(alertApiRef); + const catalogApi = useApi(catalogApiRef); + const apiHolder = useApiHolder(); + const [selectedTemplate, setSelectedTemplate] = useState(''); + const [schema, setSchema] = useState({ + title: '', + steps: [], + }); + const [templateOptions, setTemplateOptions] = useState([]); + const [templateYaml, setTemplateYaml] = useState(defaultPreviewTemplate); + const [formState, setFormState] = useState({}); + + const { loading } = useAsync( + () => + catalogApi + .getEntities({ + filter: { kind: 'template' }, + fields: [ + 'kind', + 'metadata.namespace', + 'metadata.name', + 'metadata.title', + 'spec.parameters', + ], + }) + .then(({ items }) => + setTemplateOptions( + items.map(template => ({ + label: + template.metadata.title ?? + humanizeEntityRef(template, { defaultKind: 'template' }), + value: template, + })), + ), + ) + .catch(e => + alertApi.post({ + message: `Error loading exisiting templates: ${e.message}`, + severity: 'error', + }), + ), + [catalogApi], + ); + + const errorPanel = document.createElement('div'); + errorPanel.style.color = 'red'; + + useDebounce( + () => { + try { + const parsedTemplate = yaml.parse(templateYaml); + + setSchema({ + title: 'Preview', + steps: parsedTemplate.parameters.map((param: JsonObject) => ({ + title: param.title, + schema: param, + })), + }); + setFormState({}); + } catch (e) { + errorPanel.textContent = e.message; + } + }, + 250, + [setFormState, setSchema, templateYaml], + ); + + const handleSelectChange = useCallback( + selected => { + setSelectedTemplate(selected); + setTemplateYaml(yaml.stringify(selected.spec)); + }, + [setTemplateYaml], + ); + + const handleFormReset = () => setFormState({}); + const handleFormChange = useCallback( + (e: IChangeEvent) => setFormState(e.formData), + [setFormState], + ); + + const handleCodeChange = useCallback( + (code: string) => { + setTemplateYaml(code); + }, + [setTemplateYaml], + ); + + const customFieldComponents = Object.fromEntries( + customFieldExtensions.map(({ name, component }) => [name, component]), + ); + + const customFieldValidators = Object.fromEntries( + customFieldExtensions.map(({ name, validation }) => [name, validation]), + ); + + return ( + +
+ + {loading && } + + + + + Load Existing Template + + + + ({ dom: errorPanel, top: true })), + ]} + onChange={handleCodeChange} + /> + + + {schema && ( + + { + return { + ...step, + validate: createValidator( + step.schema, + customFieldValidators, + { apiHolder }, + ), + }; + })} + /> + + )} + + + + + ); +}; diff --git a/plugins/scaffolder/src/components/TemplatePreviewPage/index.ts b/plugins/scaffolder/src/components/TemplatePreviewPage/index.ts new file mode 100644 index 0000000000..cdf7bd1e82 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplatePreviewPage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { TemplatePreviewPage } from './TemplatePreviewPage'; diff --git a/yarn.lock b/yarn.lock index 2e597e6ede..ca437689f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1295,10 +1295,10 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.17.2" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" - integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.17.7" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" + integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== dependencies: regenerator-runtime "^0.13.4" @@ -1568,6 +1568,91 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@codemirror/autocomplete@^0.19.0": + version "0.19.14" + resolved "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.19.14.tgz#5f61b0fced56e7960791063d03b21d11a63f0ec5" + integrity sha512-4PqJG7GGTePc+FQF387RFebDV4ERvKj23gQBmzNtu64ZSHlYEGulwP5EIIfulBiaWEmei9TYVaMFmTdNfofpRQ== + dependencies: + "@codemirror/language" "^0.19.0" + "@codemirror/state" "^0.19.4" + "@codemirror/text" "^0.19.2" + "@codemirror/tooltip" "^0.19.12" + "@codemirror/view" "^0.19.0" + "@lezer/common" "^0.15.0" + +"@codemirror/basic-setup@^0.19.1": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.19.1.tgz#17b27d02f15c628eb62a85d01e3e1b1958933eb4" + integrity sha512-gLjD7YgZU/we6BzS/ecCmD3viw83dsgv5ZUaSydYbYx9X4w4w9RqYnckcJ+0GDyHfNr5Jtfv2Z5ZtFQnBj0UDA== + dependencies: + "@codemirror/autocomplete" "^0.19.0" + "@codemirror/closebrackets" "^0.19.0" + "@codemirror/commands" "^0.19.0" + "@codemirror/comment" "^0.19.0" + "@codemirror/fold" "^0.19.0" + "@codemirror/gutter" "^0.19.0" + "@codemirror/highlight" "^0.19.0" + "@codemirror/history" "^0.19.0" + "@codemirror/language" "^0.19.0" + "@codemirror/lint" "^0.19.0" + "@codemirror/matchbrackets" "^0.19.0" + "@codemirror/rectangular-selection" "^0.19.0" + "@codemirror/search" "^0.19.0" + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.31" + +"@codemirror/closebrackets@^0.19.0": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/closebrackets/-/closebrackets-0.19.1.tgz#c93219d6800c27a880e569135b468cf4a05d903f" + integrity sha512-ZiLXT6u+VuBK5QnfBbt/Vmfd9Pg6449wn1DIOWFZHUOldg5eFn3VGGjYY2XWuHQz5WuK+7dXamV2KE885O1gyA== + dependencies: + "@codemirror/language" "^0.19.0" + "@codemirror/rangeset" "^0.19.0" + "@codemirror/state" "^0.19.2" + "@codemirror/text" "^0.19.0" + "@codemirror/view" "^0.19.44" + +"@codemirror/commands@^0.19.0": + version "0.19.8" + resolved "https://registry.npmjs.org/@codemirror/commands/-/commands-0.19.8.tgz#1f99c1a8bf200d17c4d6997379099459f3678107" + integrity sha512-65LIMSGUGGpY3oH6mzV46YWRrgao6NmfJ+AuC7jNz3K5NPnH6GCV1H5I6SwOFyVbkiygGyd0EFwrWqywTBD1aw== + dependencies: + "@codemirror/language" "^0.19.0" + "@codemirror/matchbrackets" "^0.19.0" + "@codemirror/state" "^0.19.2" + "@codemirror/text" "^0.19.6" + "@codemirror/view" "^0.19.22" + "@lezer/common" "^0.15.0" + +"@codemirror/comment@^0.19.0": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/comment/-/comment-0.19.1.tgz#7def8345eeb9095ef1ef33676fbde1ab4fe33fad" + integrity sha512-uGKteBuVWAC6fW+Yt8u27DOnXMT/xV4Ekk2Z5mRsiADCZDqYvryrJd6PLL5+8t64BVyocwQwNfz1UswYS2CtFQ== + dependencies: + "@codemirror/state" "^0.19.9" + "@codemirror/text" "^0.19.0" + "@codemirror/view" "^0.19.0" + +"@codemirror/fold@^0.19.0": + version "0.19.3" + resolved "https://registry.npmjs.org/@codemirror/fold/-/fold-0.19.3.tgz#de55d44a7313f2a8920aefb6ebf9eff34715d8d4" + integrity sha512-8hT+Eq2G68mL0yPRvSD2ewhnLQAX6sbUJmtGVKFcj8oAXtfpYCX8LIcfXsuI19Qs7gZkOSpqZvn+KKj8IhZoAw== + dependencies: + "@codemirror/gutter" "^0.19.0" + "@codemirror/language" "^0.19.0" + "@codemirror/rangeset" "^0.19.0" + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.22" + +"@codemirror/gutter@^0.19.0", "@codemirror/gutter@^0.19.4": + version "0.19.9" + resolved "https://registry.npmjs.org/@codemirror/gutter/-/gutter-0.19.9.tgz#bbb69f4d49570d9c1b3f3df5d134980c516cd42b" + integrity sha512-PFrtmilahin1g6uL27aG5tM/rqR9DZzZYZsIrCXA5Uc2OFTFqx4owuhoU9hqfYxHp5ovfvBwQ+txFzqS4vog6Q== + dependencies: + "@codemirror/rangeset" "^0.19.0" + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.23" + "@codemirror/highlight@^0.19.0": version "0.19.6" resolved "https://registry.npmjs.org/@codemirror/highlight/-/highlight-0.19.6.tgz#7f2e066f83f5649e8e0748a3abe0aaeaf64b8ac2" @@ -1580,6 +1665,14 @@ "@lezer/common" "^0.15.0" style-mod "^4.0.0" +"@codemirror/history@^0.19.0": + version "0.19.2" + resolved "https://registry.npmjs.org/@codemirror/history/-/history-0.19.2.tgz#25e3fda755f77ac1223a6ae6e9d7899f5919265e" + integrity sha512-unhP4t3N2smzmHoo/Yio6ueWi+il8gm9VKrvi6wlcdGH5fOfVDNkmjHQ495SiR+EdOG35+3iNebSPYww0vN7ow== + dependencies: + "@codemirror/state" "^0.19.2" + "@codemirror/view" "^0.19.0" + "@codemirror/language@^0.19.0": version "0.19.7" resolved "https://registry.npmjs.org/@codemirror/language/-/language-0.19.7.tgz#9eef8e827692d93a701b18db9d46a42be34ecca6" @@ -1591,24 +1684,83 @@ "@lezer/common" "^0.15.5" "@lezer/lr" "^0.15.0" -"@codemirror/rangeset@^0.19.0": - version "0.19.2" - resolved "https://registry.npmjs.org/@codemirror/rangeset/-/rangeset-0.19.2.tgz#d7a999e4273c00fecef4aba8535a426073cdcddf" - integrity sha512-5d+X8LtmeZtfFtKrSx57bIHRUpKv2HD0b74clp4fGA7qJLLfYehF6FGkJJxJb8lKsqAga1gdjjWr0jiypmIxoQ== +"@codemirror/legacy-modes@^0.19.0": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-0.19.1.tgz#7dc3b5df1842060648f75764ab6919fcfce3ea1a" + integrity sha512-vYPLsD/ON+3SXhlGj9Qb3fpFNNU3Ya/AtDiv/g3OyqVzhh5vs5rAnOvk8xopGWRwppdhlNPD9VyXjiOmZUQtmQ== + dependencies: + "@codemirror/stream-parser" "^0.19.0" + +"@codemirror/lint@^0.19.0": + version "0.19.6" + resolved "https://registry.npmjs.org/@codemirror/lint/-/lint-0.19.6.tgz#0379688da3e16739db4a6304c73db857ca85d7ec" + integrity sha512-Pbw1Y5kHVs2J+itQ0uez3dI4qY9ApYVap7eNfV81x1/3/BXgBkKfadaw0gqJ4h4FDG7OnJwb0VbPsjJQllHjaA== + dependencies: + "@codemirror/gutter" "^0.19.4" + "@codemirror/panel" "^0.19.0" + "@codemirror/rangeset" "^0.19.1" + "@codemirror/state" "^0.19.4" + "@codemirror/tooltip" "^0.19.16" + "@codemirror/view" "^0.19.22" + crelt "^1.0.5" + +"@codemirror/matchbrackets@^0.19.0": + version "0.19.4" + resolved "https://registry.npmjs.org/@codemirror/matchbrackets/-/matchbrackets-0.19.4.tgz#50b5188eb2d53f32598dca906bf5fd66626a9ebc" + integrity sha512-VFkaOKPNudAA5sGP1zikRHCEKU0hjYmkKpr04pybUpQvfTvNJXlReCyP0rvH/1iEwAGPL990ZTT+QrLdu4MeEA== + dependencies: + "@codemirror/language" "^0.19.0" + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.0" + "@lezer/common" "^0.15.0" + +"@codemirror/panel@^0.19.0", "@codemirror/panel@^0.19.1": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/panel/-/panel-0.19.1.tgz#bf77d27b962cf16357139e50864d0eb69d634441" + integrity sha512-sYeOCMA3KRYxZYJYn5PNlt9yNsjy3zTNTrbYSfVgjgL9QomIVgOJWPO5hZ2sTN8lufO6lw0vTBsIPL9MSidmBg== + dependencies: + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.0" + +"@codemirror/rangeset@^0.19.0", "@codemirror/rangeset@^0.19.1", "@codemirror/rangeset@^0.19.5": + version "0.19.9" + resolved "https://registry.npmjs.org/@codemirror/rangeset/-/rangeset-0.19.9.tgz#e80895de93c39dc7899f5be31d368c9d88aa4efc" + integrity sha512-V8YUuOvK+ew87Xem+71nKcqu1SXd5QROMRLMS/ljT5/3MCxtgrRie1Cvild0G/Z2f1fpWxzX78V0U4jjXBorBQ== dependencies: "@codemirror/state" "^0.19.0" -"@codemirror/state@^0.19.0", "@codemirror/state@^0.19.3": - version "0.19.6" - resolved "https://registry.npmjs.org/@codemirror/state/-/state-0.19.6.tgz#d631f041d39ce41b7891b099fca26cb1fdb9763e" - integrity sha512-sqIQZE9VqwQj7D4c2oz9mfLhlT1ElAzGB5lO1lE33BPyrdNy1cJyCIOecT4cn4VeJOFrnjOeu+IftZ3zqdFETw== +"@codemirror/rectangular-selection@^0.19.0": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/rectangular-selection/-/rectangular-selection-0.19.1.tgz#5a88ece4fb68ce5682539497db8a64fc015aae63" + integrity sha512-9ElnqOg3mpZIWe0prPRd1SZ48Q9QB3bR8Aocq8UtjboJSUG8ABhRrbuTZMW/rMqpBPSjVpCe9xkCCkEQMYQVmw== + dependencies: + "@codemirror/state" "^0.19.0" + "@codemirror/text" "^0.19.4" + "@codemirror/view" "^0.19.0" + +"@codemirror/search@^0.19.0": + version "0.19.9" + resolved "https://registry.npmjs.org/@codemirror/search/-/search-0.19.9.tgz#b9548dfeb2028e6d5ecd1965f8f0ee50e2925603" + integrity sha512-h3MuwbUbiyOp6Np3IB5r4LH0w4inZvbtLO1Ipmz8RhElcGRiYr11Q6Bim8ocLfe08RmZT6B5EkTj1E8eNlugQQ== + dependencies: + "@codemirror/panel" "^0.19.0" + "@codemirror/rangeset" "^0.19.0" + "@codemirror/state" "^0.19.3" + "@codemirror/text" "^0.19.0" + "@codemirror/view" "^0.19.34" + crelt "^1.0.5" + +"@codemirror/state@^0.19.0", "@codemirror/state@^0.19.2", "@codemirror/state@^0.19.3", "@codemirror/state@^0.19.4", "@codemirror/state@^0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@codemirror/state/-/state-0.19.9.tgz#b797f9fbc204d6dc7975485e231693c09001b0dd" + integrity sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw== dependencies: "@codemirror/text" "^0.19.0" -"@codemirror/stream-parser@^0.19.2": - version "0.19.2" - resolved "https://registry.npmjs.org/@codemirror/stream-parser/-/stream-parser-0.19.2.tgz#793428e55aa7b9daa64cb733973e5d5e3d9a2306" - integrity sha512-hBKRQlyu8GUOrY33xZ6/1kAfNZ8ZUm6cX9a7mPx8zAAqnpz/fpksC/qJRrkg1mPMBwxm+JG4fqAwDGJ3gLVniQ== +"@codemirror/stream-parser@^0.19.0", "@codemirror/stream-parser@^0.19.2", "@codemirror/stream-parser@^0.19.6": + version "0.19.7" + resolved "https://registry.npmjs.org/@codemirror/stream-parser/-/stream-parser-0.19.7.tgz#99a9696307b90677f6e3abe29ea6d77c1c0a8db5" + integrity sha512-4ExcbKksmU4PIT8s11/dPESgMNLQqlMlbQzRQ1CyMcmygcuQk+58zQ84nv/X17OCUd2ephZ1DKEaHHACifzCiA== dependencies: "@codemirror/highlight" "^0.19.0" "@codemirror/language" "^0.19.0" @@ -1617,17 +1769,34 @@ "@lezer/common" "^0.15.0" "@lezer/lr" "^0.15.0" -"@codemirror/text@^0.19.0": - version "0.19.5" - resolved "https://registry.npmjs.org/@codemirror/text/-/text-0.19.5.tgz#75033af2476214e79eae22b81ada618815441c18" - integrity sha512-Syu5Xc7tZzeUAM/y4fETkT0zgGr48rDG+w4U38bPwSIUr+L9S/7w2wDE1WGNzjaZPz12F6gb1gxWiSTg9ocLow== +"@codemirror/text@^0.19.0", "@codemirror/text@^0.19.2", "@codemirror/text@^0.19.4", "@codemirror/text@^0.19.6": + version "0.19.6" + resolved "https://registry.npmjs.org/@codemirror/text/-/text-0.19.6.tgz#9adcbd8137f69b75518eacd30ddb16fd67bbac45" + integrity sha512-T9jnREMIygx+TPC1bOuepz18maGq/92q2a+n4qTqObKwvNMg+8cMTslb8yxeEDEq7S3kpgGWxgO1UWbQRij0dA== -"@codemirror/view@^0.19.0": - version "0.19.27" - resolved "https://registry.npmjs.org/@codemirror/view/-/view-0.19.27.tgz#76e5dc19ecb4ce53e9fef1d29245040d7ff64183" - integrity sha512-Uz/LecEf7CyvMWaQBlKtbJCYn0hRnEZ2yYvuZVy9YMhmvGmES6ec7FaKw7lDFFOMLwLbBThc9kfw4DCHreHN1w== +"@codemirror/theme-one-dark@^0.19.1": + version "0.19.1" + resolved "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-0.19.1.tgz#648b9cbe37186a2b7bd2a83fb483dc7aa18ce218" + integrity sha512-8gc4c2k2o/EhyHoWkghCxp5vyDT96JaFGtRy35PHwIom0LZdx7aU4AbDUnITvwiFB+0+i54VO+WQjBqgTyJvqg== dependencies: - "@codemirror/rangeset" "^0.19.0" + "@codemirror/highlight" "^0.19.0" + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.0" + +"@codemirror/tooltip@^0.19.12", "@codemirror/tooltip@^0.19.16": + version "0.19.16" + resolved "https://registry.npmjs.org/@codemirror/tooltip/-/tooltip-0.19.16.tgz#6ba2c43f9d8e3d943d9d7bbae22bf800f7726a22" + integrity sha512-zxKDHryUV5/RS45AQL+wOeN+i7/l81wK56OMnUPoTSzCWNITfxHn7BToDsjtrRKbzHqUxKYmBnn/4hPjpZ4WJQ== + dependencies: + "@codemirror/state" "^0.19.0" + "@codemirror/view" "^0.19.0" + +"@codemirror/view@^0.19.0", "@codemirror/view@^0.19.22", "@codemirror/view@^0.19.23", "@codemirror/view@^0.19.31", "@codemirror/view@^0.19.34", "@codemirror/view@^0.19.44", "@codemirror/view@^0.19.45": + version "0.19.47" + resolved "https://registry.npmjs.org/@codemirror/view/-/view-0.19.47.tgz#2163c3016d7680bf50dd695c0e3abdaf80f45363" + integrity sha512-SfbagKvJQl5dtt+9wYpo9sa3ZkMgUxTq+/hXDf0KVwIx+zu3cJIqfEm9xSx6yXkq7it7RsPGHaPasApNffF/8g== + dependencies: + "@codemirror/rangeset" "^0.19.5" "@codemirror/state" "^0.19.3" "@codemirror/text" "^0.19.0" style-mod "^4.0.0" @@ -6757,6 +6926,17 @@ "@typescript-eslint/types" "5.9.0" eslint-visitor-keys "^3.0.0" +"@uiw/react-codemirror@^4.5.1": + version "4.5.1" + resolved "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.5.1.tgz#f2af4e761f13b729a3ec7c7c858b34432cdf46a7" + integrity sha512-90SNE89uJOQgcZwV0fuZjCgDeH7pjagCqwOcYHvDG5Qedp2nKmb4/2mgDCtLBxTGKxb73ktUOkbdu3L05ck1Yg== + dependencies: + "@babel/runtime" "^7.17.2" + "@codemirror/basic-setup" "^0.19.1" + "@codemirror/state" "^0.19.9" + "@codemirror/theme-one-dark" "^0.19.1" + "@codemirror/view" "^0.19.45" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -9658,6 +9838,11 @@ create-require@^1.1.0: resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +crelt@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94" + integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA== + cron@^1.8.2: version "1.8.2" resolved "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz#4ac5e3c55ba8c163d84f3407bde94632da8370ce"