@tduniec/plugin-template-designer-foundation 0.1.1
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/CHANGELOG.md +13 -0
- package/README.md +13 -0
- package/dist/api/useScaffolderActions.esm.js +59 -0
- package/dist/api/useScaffolderActions.esm.js.map +1 -0
- package/dist/components/FieldEditorDialog.esm.js +55 -0
- package/dist/components/FieldEditorDialog.esm.js.map +1 -0
- package/dist/components/Nodes/ActionNode.esm.js +613 -0
- package/dist/components/Nodes/ActionNode.esm.js.map +1 -0
- package/dist/components/Nodes/OutputNode.esm.js +373 -0
- package/dist/components/Nodes/OutputNode.esm.js.map +1 -0
- package/dist/components/Nodes/ParameterInputNode.esm.js +320 -0
- package/dist/components/Nodes/ParameterInputNode.esm.js.map +1 -0
- package/dist/components/Nodes/ParameterTitlesNode.esm.js +251 -0
- package/dist/components/Nodes/ParameterTitlesNode.esm.js.map +1 -0
- package/dist/components/Nodes/ParametersNode.esm.js +147 -0
- package/dist/components/Nodes/ParametersNode.esm.js.map +1 -0
- package/dist/components/Nodes/action/schema.esm.js +68 -0
- package/dist/components/Nodes/action/schema.esm.js.map +1 -0
- package/dist/components/Nodes/action/useActionInputs.esm.js +71 -0
- package/dist/components/Nodes/action/useActionInputs.esm.js.map +1 -0
- package/dist/components/Nodes/common/AutoWidthPopper.esm.js +11 -0
- package/dist/components/Nodes/common/AutoWidthPopper.esm.js.map +1 -0
- package/dist/components/Nodes/common/nodeInteraction.esm.js +20 -0
- package/dist/components/Nodes/common/nodeInteraction.esm.js.map +1 -0
- package/dist/components/Nodes/output/useOutputController.esm.js +125 -0
- package/dist/components/Nodes/output/useOutputController.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/TemplateLanding.esm.js +157 -0
- package/dist/components/TemplateDesigner/TemplateLanding.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/TemplateWorkspace.esm.js +416 -0
- package/dist/components/TemplateDesigner/TemplateWorkspace.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/codemirrorTheme.esm.js +30 -0
- package/dist/components/TemplateDesigner/codemirrorTheme.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/useFieldEditor.esm.js +95 -0
- package/dist/components/TemplateDesigner/useFieldEditor.esm.js.map +1 -0
- package/dist/components/TemplateDesignerIcon.esm.js +33 -0
- package/dist/components/TemplateDesignerIcon.esm.js.map +1 -0
- package/dist/components/designerFlowConfig.esm.js +13 -0
- package/dist/components/designerFlowConfig.esm.js.map +1 -0
- package/dist/designerFlow/DesignerFlow.esm.js +828 -0
- package/dist/designerFlow/DesignerFlow.esm.js.map +1 -0
- package/dist/designerFlow/handlers.esm.js +317 -0
- package/dist/designerFlow/handlers.esm.js.map +1 -0
- package/dist/designerFlow/model.esm.js +166 -0
- package/dist/designerFlow/model.esm.js.map +1 -0
- package/dist/designerFlow/nodeLayout.esm.js +108 -0
- package/dist/designerFlow/nodeLayout.esm.js.map +1 -0
- package/dist/designerFlow/parameterTransforms.esm.js +124 -0
- package/dist/designerFlow/parameterTransforms.esm.js.map +1 -0
- package/dist/designerFlow/utils/stableComparators.esm.js +69 -0
- package/dist/designerFlow/utils/stableComparators.esm.js.map +1 -0
- package/dist/foundation/actionNodeCustomization.esm.js +20 -0
- package/dist/foundation/actionNodeCustomization.esm.js.map +1 -0
- package/dist/foundation/actionNodeRegistry.esm.js +30 -0
- package/dist/foundation/actionNodeRegistry.esm.js.map +1 -0
- package/dist/foundation/featureFlags.esm.js +6 -0
- package/dist/foundation/featureFlags.esm.js.map +1 -0
- package/dist/foundation/templateSources.esm.js +16 -0
- package/dist/foundation/templateSources.esm.js.map +1 -0
- package/dist/index.d.ts +382 -0
- package/dist/index.esm.js +25 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/state/templateUtils.esm.js +46 -0
- package/dist/state/templateUtils.esm.js.map +1 -0
- package/dist/state/useParameterSections.esm.js +162 -0
- package/dist/state/useParameterSections.esm.js.map +1 -0
- package/dist/state/useTemplateState.esm.js +627 -0
- package/dist/state/useTemplateState.esm.js.map +1 -0
- package/dist/types/flowNodes.esm.js +8 -0
- package/dist/types/flowNodes.esm.js.map +1 -0
- package/dist/utils/createSequentialEdges.esm.js +15 -0
- package/dist/utils/createSequentialEdges.esm.js.map +1 -0
- package/dist/utils/mocks/mocks.esm.js +120 -0
- package/dist/utils/mocks/mocks.esm.js.map +1 -0
- package/dist/utils/sampleTemplate.esm.js +40 -0
- package/dist/utils/sampleTemplate.esm.js.map +1 -0
- package/dist/utils/yamlJsonConversion.esm.js +47 -0
- package/dist/utils/yamlJsonConversion.esm.js.map +1 -0
- package/package.json +103 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## [0.1.1](https://github.com/tduniec/template-designer-plugin/compare/template-designer-foundation-v0.1.0...template-designer-foundation-v0.1.1) (2025-12-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **template-designer-foundation:** initial release ([1ddc9d7](https://github.com/tduniec/template-designer-plugin/commit/1ddc9d72f343ab944e706919788e244b8efcdb39))
|
|
7
|
+
|
|
8
|
+
# [0.1.0](https://github.com/tduniec/template-designer-plugin/compare/template-designer-foundation-v0.0.1...template-designer-foundation-v0.1.0) (2025-12-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* bulk refactor to commons ([#55](https://github.com/tduniec/template-designer-plugin/issues/55)) optimized rendering - redesigned rendering logic ([99aa586](https://github.com/tduniec/template-designer-plugin/commit/99aa5862ffb3bac254f1bb0e69b0e94a5ed96c16))
|
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# template-designer-foundation
|
|
2
|
+
|
|
3
|
+
Welcome to the template-designer-foundation plugin!
|
|
4
|
+
|
|
5
|
+
_This plugin was created through the Backstage CLI_
|
|
6
|
+
|
|
7
|
+
## Getting started
|
|
8
|
+
|
|
9
|
+
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/template-designer-foundation](http://localhost:3000/template-designer-foundation).
|
|
10
|
+
|
|
11
|
+
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
|
12
|
+
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
|
13
|
+
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useApiHolder } from '@backstage/core-plugin-api';
|
|
3
|
+
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
|
|
4
|
+
import { MOCK_SCAFFOLDER_ACTIONS } from '../utils/mocks/mocks.esm.js';
|
|
5
|
+
|
|
6
|
+
const buildCache = (list) => {
|
|
7
|
+
const { inputsById, outputsById, inputRequiredById } = list.reduce(
|
|
8
|
+
(acc, action) => {
|
|
9
|
+
acc.inputsById[action.id] = action.schema?.input?.properties ?? {};
|
|
10
|
+
acc.outputsById[action.id] = action.schema?.output?.properties ?? {};
|
|
11
|
+
const required = Array.isArray(action.schema?.input?.required) ? action.schema?.input?.required.filter(
|
|
12
|
+
(key) => typeof key === "string" && key.trim().length > 0
|
|
13
|
+
) : [];
|
|
14
|
+
acc.inputRequiredById[action.id] = required;
|
|
15
|
+
return acc;
|
|
16
|
+
},
|
|
17
|
+
{ inputsById: {}, outputsById: {}, inputRequiredById: {} }
|
|
18
|
+
);
|
|
19
|
+
return {
|
|
20
|
+
ids: list.map((action) => action.id),
|
|
21
|
+
inputsById,
|
|
22
|
+
outputsById,
|
|
23
|
+
inputRequiredById
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const fallbackCache = buildCache(
|
|
27
|
+
MOCK_SCAFFOLDER_ACTIONS
|
|
28
|
+
);
|
|
29
|
+
const useScaffolderActions = () => {
|
|
30
|
+
const apiHolder = useApiHolder();
|
|
31
|
+
const scaffolderApi = apiHolder.get(scaffolderApiRef);
|
|
32
|
+
const [cache, setCache] = useState(fallbackCache);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
if (!scaffolderApi) {
|
|
36
|
+
setCache(fallbackCache);
|
|
37
|
+
return () => {
|
|
38
|
+
cancelled = true;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
scaffolderApi.listActions().then((remoteActions) => {
|
|
42
|
+
if (cancelled) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
setCache(buildCache(remoteActions));
|
|
46
|
+
}).catch(() => {
|
|
47
|
+
if (!cancelled) {
|
|
48
|
+
setCache(fallbackCache);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return () => {
|
|
52
|
+
cancelled = true;
|
|
53
|
+
};
|
|
54
|
+
}, [scaffolderApi]);
|
|
55
|
+
return cache;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { useScaffolderActions };
|
|
59
|
+
//# sourceMappingURL=useScaffolderActions.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScaffolderActions.esm.js","sources":["../../src/api/useScaffolderActions.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useApiHolder } from \"@backstage/core-plugin-api\";\nimport { scaffolderApiRef } from \"@backstage/plugin-scaffolder-react\";\nimport type { ListActionsResponse } from \"@backstage/plugin-scaffolder-common\";\nimport { MOCK_SCAFFOLDER_ACTIONS } from \"../utils/mocks/mocks\";\n\ntype ScaffolderAction = {\n id: string;\n schema?: {\n input?: {\n properties?: Record<string, unknown>;\n required?: unknown;\n };\n output?: {\n properties?: Record<string, unknown>;\n };\n };\n};\n\nexport type ScaffolderActionsCache = {\n ids: string[];\n inputsById: Record<string, Record<string, unknown>>;\n outputsById: Record<string, Record<string, unknown>>;\n inputRequiredById: Record<string, string[]>;\n};\n\nconst buildCache = (list: ScaffolderAction[]): ScaffolderActionsCache => {\n const { inputsById, outputsById, inputRequiredById } = list.reduce<{\n inputsById: Record<string, Record<string, unknown>>;\n outputsById: Record<string, Record<string, unknown>>;\n inputRequiredById: Record<string, string[]>;\n }>(\n (acc, action) => {\n acc.inputsById[action.id] = action.schema?.input?.properties ?? {};\n acc.outputsById[action.id] = action.schema?.output?.properties ?? {};\n const required = Array.isArray(action.schema?.input?.required)\n ? action.schema?.input?.required.filter(\n (key): key is string =>\n typeof key === \"string\" && key.trim().length > 0\n )\n : [];\n acc.inputRequiredById[action.id] = required;\n return acc;\n },\n { inputsById: {}, outputsById: {}, inputRequiredById: {} }\n );\n\n return {\n ids: list.map((action) => action.id),\n inputsById,\n outputsById,\n inputRequiredById,\n };\n};\n\nconst fallbackCache = buildCache(\n MOCK_SCAFFOLDER_ACTIONS as ListActionsResponse\n);\n\nexport const useScaffolderActions = () => {\n const apiHolder = useApiHolder();\n const scaffolderApi = apiHolder.get(scaffolderApiRef);\n const [cache, setCache] = useState<ScaffolderActionsCache>(fallbackCache);\n\n useEffect(() => {\n let cancelled = false;\n\n if (!scaffolderApi) {\n setCache(fallbackCache);\n return () => {\n cancelled = true;\n };\n }\n\n scaffolderApi\n .listActions()\n .then((remoteActions) => {\n if (cancelled) {\n return;\n }\n setCache(buildCache(remoteActions));\n })\n .catch(() => {\n if (!cancelled) {\n setCache(fallbackCache);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [scaffolderApi]);\n\n return cache;\n};\n"],"names":[],"mappings":";;;;;AA0BA,MAAM,UAAA,GAAa,CAAC,IAAA,KAAqD;AACvE,EAAA,MAAM,EAAE,UAAA,EAAY,WAAA,EAAa,iBAAA,KAAsB,IAAA,CAAK,MAAA;AAAA,IAK1D,CAAC,KAAK,MAAA,KAAW;AACf,MAAA,GAAA,CAAI,UAAA,CAAW,OAAO,EAAE,CAAA,GAAI,OAAO,MAAA,EAAQ,KAAA,EAAO,cAAc,EAAC;AACjE,MAAA,GAAA,CAAI,WAAA,CAAY,OAAO,EAAE,CAAA,GAAI,OAAO,MAAA,EAAQ,MAAA,EAAQ,cAAc,EAAC;AACnE,MAAA,MAAM,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ,KAAA,EAAO,QAAQ,CAAA,GACzD,MAAA,CAAO,MAAA,EAAQ,KAAA,EAAO,QAAA,CAAS,MAAA;AAAA,QAC7B,CAAC,QACC,OAAO,GAAA,KAAQ,YAAY,GAAA,CAAI,IAAA,GAAO,MAAA,GAAS;AAAA,UAEnD,EAAC;AACL,MAAA,GAAA,CAAI,iBAAA,CAAkB,MAAA,CAAO,EAAE,CAAA,GAAI,QAAA;AACnC,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA,EAAE,YAAY,EAAC,EAAG,aAAa,EAAC,EAAG,iBAAA,EAAmB,EAAC;AAAE,GAC3D;AAEA,EAAA,OAAO;AAAA,IACL,KAAK,IAAA,CAAK,GAAA,CAAI,CAAC,MAAA,KAAW,OAAO,EAAE,CAAA;AAAA,IACnC,UAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACF,CAAA;AAEA,MAAM,aAAA,GAAgB,UAAA;AAAA,EACpB;AACF,CAAA;AAEO,MAAM,uBAAuB,MAAM;AACxC,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,MAAM,aAAA,GAAgB,SAAA,CAAU,GAAA,CAAI,gBAAgB,CAAA;AACpD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAiC,aAAa,CAAA;AAExE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,IAAI,CAAC,aAAA,EAAe;AAClB,MAAA,QAAA,CAAS,aAAa,CAAA;AACtB,MAAA,OAAO,MAAM;AACX,QAAA,SAAA,GAAY,IAAA;AAAA,MACd,CAAA;AAAA,IACF;AAEA,IAAA,aAAA,CACG,WAAA,EAAY,CACZ,IAAA,CAAK,CAAC,aAAA,KAAkB;AACvB,MAAA,IAAI,SAAA,EAAW;AACb,QAAA;AAAA,MACF;AACA,MAAA,QAAA,CAAS,UAAA,CAAW,aAAa,CAAC,CAAA;AAAA,IACpC,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,QAAA,CAAS,aAAa,CAAA;AAAA,MACxB;AAAA,IACF,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,OAAO,KAAA;AACT;;;;"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Dialog, DialogTitle, DialogContent, TextField, DialogActions, Button } from '@material-ui/core';
|
|
3
|
+
import { useRef, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
const FieldEditorDialog = ({
|
|
6
|
+
open,
|
|
7
|
+
label,
|
|
8
|
+
value,
|
|
9
|
+
onClose,
|
|
10
|
+
onApply
|
|
11
|
+
}) => {
|
|
12
|
+
const draftRef = useRef(value);
|
|
13
|
+
const inputRef = useRef(null);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (open) {
|
|
16
|
+
draftRef.current = value;
|
|
17
|
+
if (inputRef.current) {
|
|
18
|
+
inputRef.current.value = value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}, [open, value]);
|
|
22
|
+
return /* @__PURE__ */ jsxs(Dialog, { open, onClose, fullWidth: true, maxWidth: "md", children: [
|
|
23
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: label ? `Edit ${label}` : "Edit field" }),
|
|
24
|
+
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(
|
|
25
|
+
TextField,
|
|
26
|
+
{
|
|
27
|
+
multiline: true,
|
|
28
|
+
minRows: 8,
|
|
29
|
+
variant: "outlined",
|
|
30
|
+
defaultValue: value,
|
|
31
|
+
inputRef,
|
|
32
|
+
onChange: (event) => {
|
|
33
|
+
draftRef.current = event.target.value;
|
|
34
|
+
},
|
|
35
|
+
fullWidth: true,
|
|
36
|
+
autoFocus: true
|
|
37
|
+
}
|
|
38
|
+
) }),
|
|
39
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
40
|
+
/* @__PURE__ */ jsx(Button, { onClick: onClose, children: "Cancel" }),
|
|
41
|
+
/* @__PURE__ */ jsx(
|
|
42
|
+
Button,
|
|
43
|
+
{
|
|
44
|
+
color: "primary",
|
|
45
|
+
variant: "contained",
|
|
46
|
+
onClick: () => onApply(draftRef.current ?? value),
|
|
47
|
+
children: "Apply"
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
] })
|
|
51
|
+
] });
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { FieldEditorDialog };
|
|
55
|
+
//# sourceMappingURL=FieldEditorDialog.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldEditorDialog.esm.js","sources":["../../src/components/FieldEditorDialog.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n TextField,\n} from \"@material-ui/core\";\nimport { useEffect, useRef } from \"react\";\n\nexport type FieldEditorDialogProps = {\n open: boolean;\n label?: string;\n value: string;\n onClose: () => void;\n onApply: (value: string) => void;\n};\n\nexport const FieldEditorDialog = ({\n open,\n label,\n value,\n onClose,\n onApply,\n}: FieldEditorDialogProps) => {\n const draftRef = useRef(value);\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);\n\n useEffect(() => {\n if (open) {\n draftRef.current = value;\n if (inputRef.current) {\n inputRef.current.value = value;\n }\n }\n }, [open, value]);\n\n return (\n <Dialog open={open} onClose={onClose} fullWidth maxWidth=\"md\">\n <DialogTitle>{label ? `Edit ${label}` : \"Edit field\"}</DialogTitle>\n <DialogContent>\n <TextField\n multiline\n minRows={8}\n variant=\"outlined\"\n defaultValue={value}\n inputRef={inputRef}\n onChange={(event) => {\n draftRef.current = event.target.value;\n }}\n fullWidth\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>Cancel</Button>\n <Button\n color=\"primary\"\n variant=\"contained\"\n onClick={() => onApply(draftRef.current ?? value)}\n >\n Apply\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":";;;;AAkBO,MAAM,oBAAoB,CAAC;AAAA,EAChC,IAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAA8B;AAC5B,EAAA,MAAM,QAAA,GAAW,OAAO,KAAK,CAAA;AAC7B,EAAA,MAAM,QAAA,GAAW,OAAsD,IAAI,CAAA;AAE3E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AACnB,MAAA,IAAI,SAAS,OAAA,EAAS;AACpB,QAAA,QAAA,CAAS,QAAQ,KAAA,GAAQ,KAAA;AAAA,MAC3B;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,KAAK,CAAC,CAAA;AAEhB,EAAA,4BACG,MAAA,EAAA,EAAO,IAAA,EAAY,SAAkB,SAAA,EAAS,IAAA,EAAC,UAAS,IAAA,EACvD,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,WAAA,EAAA,EAAa,QAAA,EAAA,KAAA,GAAQ,CAAA,KAAA,EAAQ,KAAK,KAAK,YAAA,EAAa,CAAA;AAAA,wBACpD,aAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,MAAC,SAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAS,IAAA;AAAA,QACT,OAAA,EAAS,CAAA;AAAA,QACT,OAAA,EAAQ,UAAA;AAAA,QACR,YAAA,EAAc,KAAA;AAAA,QACd,QAAA;AAAA,QACA,QAAA,EAAU,CAAC,KAAA,KAAU;AACnB,UAAA,QAAA,CAAS,OAAA,GAAU,MAAM,MAAA,CAAO,KAAA;AAAA,QAClC,CAAA;AAAA,QACA,SAAA,EAAS,IAAA;AAAA,QAET,SAAA,EAAS;AAAA;AAAA,KACX,EACF,CAAA;AAAA,yBACC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,OAAA,EAAS,OAAA,EAAS,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,sBAChC,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAM,SAAA;AAAA,UACN,OAAA,EAAQ,WAAA;AAAA,UACR,OAAA,EAAS,MAAM,OAAA,CAAQ,QAAA,CAAS,WAAW,KAAK,CAAA;AAAA,UACjD,QAAA,EAAA;AAAA;AAAA;AAED,KAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}
|