feat(TemplatePreviewPage): implement a view for previewing template form UIs

Signed-off-by: Phil Kuang <pkuang@factset.com>
This commit is contained in:
Phil Kuang
2022-02-26 16:47:45 -05:00
parent 2d63d04743
commit d8716924d6
11 changed files with 584 additions and 30 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Implement a template preview page (`/create/preview`) to test creating form UIs
+2
View File
@@ -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={
<ScaffolderPage
defaultPreviewTemplate={defaultPreviewTemplate}
groups={[
{
title: 'Recommended',
@@ -0,0 +1,54 @@
/*
* 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 const defaultPreviewTemplate = `# 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
- title: Custom Fields
required:
- lowerCaseValue
properties:
lowerCaseValue:
title: Lower Cased Value
type: string
ui:field: LowerCaseValuePicker
`;
+1
View File
@@ -200,6 +200,7 @@ export type RouterProps = {
titleComponent?: React_2.ReactNode;
filter: (entity: Entity) => boolean;
}>;
defaultPreviewTemplate?: string;
};
// @public
+6 -1
View File
@@ -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": {
@@ -51,7 +51,7 @@ type Props = {
formData: Record<string, any>;
onChange: (e: IChangeEvent) => void;
onReset: () => void;
onFinish: () => Promise<void>;
onFinish?: () => Promise<void>;
widgets?: FormProps<any>['widgets'];
fields?: FormProps<any>['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
</Button>
+14 -1
View File
@@ -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) => {
/>
<Route path="/tasks/:taskId" element={<TaskPageElement />} />
<Route path="/actions" element={<ActionsPage />} />
<Route
path="/preview"
element={
<SecretsContextProvider>
<TemplatePreviewPage
defaultPreviewTemplate={defaultPreviewTemplate}
customFieldExtensions={fieldExtensions}
/>
</SecretsContextProvider>
}
/>
</Routes>
);
};
@@ -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';
@@ -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<any, any>[];
}) => {
const classes = useStyles();
const alertApi = useApi(alertApiRef);
const catalogApi = useApi(catalogApiRef);
const apiHolder = useApiHolder();
const [selectedTemplate, setSelectedTemplate] = useState('');
const [schema, setSchema] = useState<TemplateParameterSchema>({
title: '',
steps: [],
});
const [templateOptions, setTemplateOptions] = useState<TemplateOption[]>([]);
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 (
<Page themeId="home">
<Header
title="Template Preview"
subtitle="Preview your template parameter UI"
/>
<Content>
{loading && <LinearProgress />}
<Grid container className={classes.grid}>
<Grid item xs={6}>
<FormControl
className={classes.templateSelect}
variant="outlined"
fullWidth
>
<InputLabel id="select-template-label">
Load Existing Template
</InputLabel>
<Select
value={selectedTemplate}
label="Load Existing Template"
labelId="select-template-label"
onChange={e => handleSelectChange(e.target.value)}
>
{templateOptions.map((option, idx) => (
<MenuItem key={idx} value={option.value as any}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<CodeMirror
className={classes.codeMirror}
value={templateYaml}
theme="dark"
height="100%"
extensions={[
StreamLanguage.define(yamlSupport),
showPanel.of(() => ({ dom: errorPanel, top: true })),
]}
onChange={handleCodeChange}
/>
</Grid>
<Grid item xs={6}>
{schema && (
<InfoCard key={JSON.stringify(schema)}>
<MultistepJsonForm
formData={formState}
fields={customFieldComponents}
onChange={handleFormChange}
onReset={handleFormReset}
steps={schema.steps.map(step => {
return {
...step,
validate: createValidator(
step.schema,
customFieldValidators,
{ apiHolder },
),
};
})}
/>
</InfoCard>
)}
</Grid>
</Grid>
</Content>
</Page>
);
};
@@ -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';
+210 -25
View File
@@ -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"