@backstage/plugin-scaffolder 1.11.0-next.2 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
- import React, { useState, useCallback, createContext, useEffect, useContext, useRef, useMemo, Component, Children } from 'react';
1
+ import React, { useState, useCallback, useMemo, Component, useEffect } from 'react';
2
2
  import { useNavigate, Navigate, useOutlet, Routes, Route } from 'react-router-dom';
3
- import { ItemCardHeader, MarkdownContent, Button, Link, ContentHeader, Progress, WarningPanel, Content, ItemCardGrid, Page, Header, CreateButton, SupportButton, StructuredMetadataTable, InfoCard, ErrorPanel, LogViewer } from '@backstage/core-components';
3
+ import { ItemCardHeader, MarkdownContent, Button, Link, ContentHeader, Progress, WarningPanel, Content, ItemCardGrid, Page, Header, CreateButton, SupportButton, StructuredMetadataTable, InfoCard } from '@backstage/core-components';
4
4
  import { useApp, useRouteRef, useApi, useRouteRefParams, useAnalytics, errorApiRef, featureFlagsApiRef, useApiHolder, AnalyticsContext, alertApiRef } from '@backstage/core-plugin-api';
5
5
  import { getEntityRelations, getEntitySourceLocation, FavoriteEntity, EntityRefLinks, useEntityList, EntityListProvider, CatalogFilterLayout, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker, catalogApiRef, humanizeEntityRef } from '@backstage/plugin-catalog-react';
6
6
  import { RELATION_OWNED_BY, parseEntityRef, stringifyEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
7
- import { makeStyles, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Tooltip, IconButton, Paper, Button as Button$1, Stepper, Step, StepLabel, StepContent, LinearProgress, FormControl, InputLabel, Select, MenuItem as MenuItem$1, CardHeader, Divider as Divider$1 } from '@material-ui/core';
7
+ import { makeStyles, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Tooltip, IconButton, Paper, Button as Button$1, Stepper, Step, StepLabel, StepContent, LinearProgress, FormControl, InputLabel, Select, MenuItem as MenuItem$1, CardHeader } from '@material-ui/core';
8
8
  import { scmIntegrationsApiRef, ScmIntegrationIcon } from '@backstage/integration-react';
9
9
  import LanguageIcon from '@material-ui/icons/Language';
10
10
  import WarningIcon from '@material-ui/icons/Warning';
11
- import { s as selectedTemplateRouteRef, v as viewTechDocRouteRef, e as editRouteRef, a as actionsRouteRef, b as scaffolderListTaskRouteRef, r as registerComponentRouteRef, T as TemplateTypePicker, c as scaffolderTaskRouteRef, d as rootRouteRef, f as TaskStatusStepper, g as TaskPageLinks, l as legacySelectedTemplateRouteRef, A as ActionsPage, h as TaskPage } from './index-02b0e824.esm.js';
11
+ import { s as selectedTemplateRouteRef, v as viewTechDocRouteRef, e as editRouteRef, a as actionsRouteRef, b as scaffolderListTaskRouteRef, r as registerComponentRouteRef, T as TemplateTypePicker, c as scaffolderTaskRouteRef, d as rootRouteRef, u as useDryRun, f as useDirectoryEditor, D as DirectoryEditorProvider, g as DryRunProvider, h as TemplateEditorBrowser, i as TemplateEditorTextArea, j as DryRunResults, k as TemplateEditorIntro, W as WebFileSystemAccess, l as legacySelectedTemplateRouteRef, A as ActionsPage, m as TaskPage } from './index-5b3a75fa.esm.js';
12
12
  import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common';
13
13
  import { usePermission } from '@backstage/plugin-permission-react';
14
14
  import IconButton$1 from '@material-ui/core/IconButton';
@@ -34,36 +34,7 @@ import CloseIcon from '@material-ui/icons/Close';
34
34
  import CodeMirror from '@uiw/react-codemirror';
35
35
  import yaml from 'yaml';
36
36
  import useDebounce from 'react-use/lib/useDebounce';
37
- import { useAsync as useAsync$1, useRerender, usePrevious, useKeyboardEvent } from '@react-hookz/web';
38
- import Card$1 from '@material-ui/core/Card';
39
- import CardActionArea from '@material-ui/core/CardActionArea';
40
- import CardContent$1 from '@material-ui/core/CardContent';
41
- import Tooltip$1 from '@material-ui/core/Tooltip';
42
- import Typography$1 from '@material-ui/core/Typography';
43
- import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
44
- import Accordion from '@material-ui/core/Accordion';
45
- import AccordionDetails from '@material-ui/core/AccordionDetails';
46
- import AccordionSummary from '@material-ui/core/AccordionSummary';
47
- import Divider from '@material-ui/core/Divider';
48
- import ExpandMoreIcon$1 from '@material-ui/icons/ExpandLess';
49
- import List$1 from '@material-ui/core/List';
50
- import ListItem from '@material-ui/core/ListItem';
51
- import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
52
- import Cancel from '@material-ui/icons/Cancel';
53
- import Check from '@material-ui/icons/Check';
54
- import DeleteIcon from '@material-ui/icons/Delete';
55
- import Box$1 from '@material-ui/core/Box';
56
- import Tab from '@material-ui/core/Tab';
57
- import Tabs from '@material-ui/core/Tabs';
58
- import TreeView from '@material-ui/lab/TreeView';
59
- import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
60
- import ChevronRightIcon from '@material-ui/icons/ChevronRight';
61
- import TreeItem from '@material-ui/lab/TreeItem';
62
- import classNames from 'classnames';
63
- import RefreshIcon from '@material-ui/icons/Refresh';
64
- import SaveIcon from '@material-ui/icons/Save';
65
- import { showPanel } from '@codemirror/view';
66
- import { D as DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS, L as ListTasksPage } from './ListTasksPage-6e377096.esm.js';
37
+ import { D as DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS, L as ListTasksPage } from './ListTasksPage-a0fe74a9.esm.js';
67
38
  import '@backstage/errors';
68
39
  import 'zen-observable';
69
40
  import '@material-ui/core/FormControl';
@@ -78,28 +49,58 @@ import '@material-ui/lab';
78
49
  import 'lodash/capitalize';
79
50
  import '@material-ui/icons/CheckBox';
80
51
  import '@material-ui/icons/CheckBoxOutlineBlank';
52
+ import '@material-ui/icons/ExpandMore';
81
53
  import '@material-ui/core/Button';
82
54
  import '@material-ui/core/useMediaQuery';
83
55
  import '@material-ui/icons/AddCircleOutline';
84
56
  import 'use-immer';
85
57
  import '@material-ui/icons/RemoveCircleOutline';
86
58
  import '@material-ui/icons/PanoramaFishEye';
59
+ import 'classnames';
87
60
  import '@material-ui/icons/CheckCircleOutline';
88
61
  import '@material-ui/icons/ErrorOutline';
89
62
  import 'react-use/lib/useInterval';
90
63
  import 'luxon';
91
64
  import 'humanize-duration';
65
+ import '@react-hookz/web';
92
66
  import '@material-ui/icons/Repeat';
93
67
  import '@material-ui/icons/Toc';
94
68
  import '@material-ui/icons/Settings';
95
69
  import '@material-ui/icons/FontDownload';
70
+ import '@material-ui/core/Typography';
71
+ import '@rjsf/validator-ajv8';
72
+ import '@material-ui/core/Accordion';
73
+ import '@material-ui/core/AccordionDetails';
74
+ import '@material-ui/core/AccordionSummary';
75
+ import '@material-ui/core/Divider';
76
+ import '@material-ui/icons/ExpandLess';
77
+ import '@material-ui/core/List';
78
+ import '@material-ui/core/ListItem';
79
+ import '@material-ui/core/ListItemSecondaryAction';
80
+ import '@material-ui/icons/Cancel';
81
+ import '@material-ui/icons/Check';
82
+ import '@material-ui/icons/Delete';
83
+ import '@material-ui/core/Box';
84
+ import '@material-ui/core/Tab';
85
+ import '@material-ui/core/Tabs';
96
86
  import '@material-ui/core/Grid';
97
87
  import '@material-ui/core/Step';
98
88
  import '@material-ui/core/StepLabel';
99
89
  import '@material-ui/core/Stepper';
100
90
  import '@material-ui/icons/FiberManualRecord';
91
+ import '@material-ui/lab/TreeView';
92
+ import '@material-ui/icons/ChevronRight';
93
+ import '@material-ui/lab/TreeItem';
94
+ import '@material-ui/icons/Refresh';
95
+ import '@material-ui/icons/Save';
96
+ import '@codemirror/view';
97
+ import '@material-ui/core/Card';
98
+ import '@material-ui/core/CardActionArea';
99
+ import '@material-ui/core/CardContent';
100
+ import '@material-ui/core/Tooltip';
101
+ import '@material-ui/icons/InfoOutlined';
101
102
 
102
- const useStyles$d = makeStyles((theme) => ({
103
+ const useStyles$5 = makeStyles((theme) => ({
103
104
  cardHeader: {
104
105
  position: "relative"
105
106
  },
@@ -189,7 +190,7 @@ const TemplateCard = ({ template, deprecated }) => {
189
190
  );
190
191
  const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type }) ? templateProps.type : "other";
191
192
  const theme = backstageTheme.getPageTheme({ themeId });
192
- const classes = useStyles$d({ backgroundImage: theme.backgroundImage });
193
+ const classes = useStyles$5({ backgroundImage: theme.backgroundImage });
193
194
  const { name, namespace } = parseEntityRef(stringifyEntityRef(template));
194
195
  const href = templateRoute({ templateName: name, namespace });
195
196
  const viewTechDoc = useRouteRef(viewTechDocRouteRef);
@@ -301,13 +302,13 @@ const TemplateList = ({
301
302
  )))));
302
303
  };
303
304
 
304
- const useStyles$c = makeStyles$1({
305
+ const useStyles$4 = makeStyles$1({
305
306
  button: {
306
307
  color: "white"
307
308
  }
308
309
  });
309
310
  function ScaffolderPageContextMenu(props) {
310
- const classes = useStyles$c();
311
+ const classes = useStyles$4();
311
312
  const [anchorEl, setAnchorEl] = useState();
312
313
  const editLink = useRouteRef(editRouteRef);
313
314
  const actionsLink = useRouteRef(actionsRouteRef);
@@ -716,6 +717,9 @@ const MultistepJsonForm = (props) => {
716
717
  function isObject(obj) {
717
718
  return typeof obj === "object" && obj !== null && !Array.isArray(obj);
718
719
  }
720
+ function isArray(obj) {
721
+ return typeof obj === "object" && obj !== null && Array.isArray(obj);
722
+ }
719
723
  const createValidator = (rootSchema, validators, context) => {
720
724
  function validate(schema, formData, errors) {
721
725
  const schemaProps = schema.properties;
@@ -726,8 +730,8 @@ const createValidator = (rootSchema, validators, context) => {
726
730
  if (schemaProps) {
727
731
  for (const [key, propData] of Object.entries(formData)) {
728
732
  const propValidation = errors[key];
733
+ const propSchemaProps = schemaProps[key];
729
734
  if (isObject(propData)) {
730
- const propSchemaProps = schemaProps[key];
731
735
  if (isObject(propSchemaProps)) {
732
736
  validate(
733
737
  propSchemaProps,
@@ -735,9 +739,22 @@ const createValidator = (rootSchema, validators, context) => {
735
739
  propValidation
736
740
  );
737
741
  }
742
+ } else if (isArray(propData)) {
743
+ if (isObject(propSchemaProps)) {
744
+ const { items } = propSchemaProps;
745
+ if (isObject(items)) {
746
+ const fieldName = items["ui:field"];
747
+ if (fieldName && typeof validators[fieldName] === "function") {
748
+ validators[fieldName](
749
+ propData,
750
+ propValidation,
751
+ context
752
+ );
753
+ }
754
+ }
755
+ }
738
756
  } else {
739
- const propSchema = schemaProps[key];
740
- const fieldName = isObject(propSchema) && propSchema["ui:field"];
757
+ const fieldName = isObject(propSchemaProps) && propSchemaProps["ui:field"];
741
758
  if (fieldName && typeof validators[fieldName] === "function") {
742
759
  validators[fieldName](
743
760
  propData,
@@ -875,338 +892,7 @@ const TemplatePage = ({
875
892
  ))));
876
893
  };
877
894
 
878
- const showDirectoryPicker = window.showDirectoryPicker;
879
- class WebFileAccess {
880
- constructor(path, handle) {
881
- this.path = path;
882
- this.handle = handle;
883
- }
884
- file() {
885
- return this.handle.getFile();
886
- }
887
- async save(data) {
888
- const writable = await this.handle.createWritable();
889
- await writable.write(data);
890
- await writable.close();
891
- }
892
- }
893
- class WebDirectoryAccess {
894
- constructor(handle) {
895
- this.handle = handle;
896
- }
897
- async listFiles() {
898
- const content = [];
899
- for await (const entry of this.listDirectoryContents(this.handle)) {
900
- content.push(entry);
901
- }
902
- return content;
903
- }
904
- async *listDirectoryContents(dirHandle, basePath = []) {
905
- for await (const handle of dirHandle.values()) {
906
- if (handle.kind === "file") {
907
- yield new WebFileAccess([...basePath, handle.name].join("/"), handle);
908
- } else if (handle.kind === "directory") {
909
- if (handle.name === ".git") {
910
- continue;
911
- }
912
- yield* this.listDirectoryContents(handle, [...basePath, handle.name]);
913
- }
914
- }
915
- }
916
- }
917
- class WebFileSystemAccess {
918
- static isSupported() {
919
- return Boolean(showDirectoryPicker);
920
- }
921
- static async requestDirectoryAccess() {
922
- if (!showDirectoryPicker) {
923
- throw new Error("File system access is not supported");
924
- }
925
- const handle = await showDirectoryPicker();
926
- return new WebDirectoryAccess(handle);
927
- }
928
- constructor() {
929
- }
930
- }
931
-
932
- var __accessCheck = (obj, member, msg) => {
933
- if (!member.has(obj))
934
- throw TypeError("Cannot " + msg);
935
- };
936
- var __privateGet = (obj, member, getter) => {
937
- __accessCheck(obj, member, "read from private field");
938
- return getter ? getter.call(obj) : member.get(obj);
939
- };
940
- var __privateAdd = (obj, member, value) => {
941
- if (member.has(obj))
942
- throw TypeError("Cannot add the same private member more than once");
943
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
944
- };
945
- var __privateSet = (obj, member, value, setter) => {
946
- __accessCheck(obj, member, "write to private field");
947
- setter ? setter.call(obj, value) : member.set(obj, value);
948
- return value;
949
- };
950
- var _access, _signalUpdate, _content, _savedContent, _access2, _listeners, _files, _selectedFile, _signalUpdate2;
951
- const MAX_SIZE = 1024 * 1024;
952
- const MAX_SIZE_MESSAGE = "This file is too large to be displayed";
953
- class DirectoryEditorFileManager {
954
- constructor(access, signalUpdate) {
955
- __privateAdd(this, _access, void 0);
956
- __privateAdd(this, _signalUpdate, void 0);
957
- __privateAdd(this, _content, void 0);
958
- __privateAdd(this, _savedContent, void 0);
959
- __privateSet(this, _access, access);
960
- __privateSet(this, _signalUpdate, signalUpdate);
961
- }
962
- get path() {
963
- return __privateGet(this, _access).path;
964
- }
965
- get content() {
966
- var _a;
967
- return (_a = __privateGet(this, _content)) != null ? _a : MAX_SIZE_MESSAGE;
968
- }
969
- updateContent(content) {
970
- if (__privateGet(this, _content) === void 0) {
971
- return;
972
- }
973
- __privateSet(this, _content, content);
974
- __privateGet(this, _signalUpdate).call(this);
975
- }
976
- get dirty() {
977
- return __privateGet(this, _content) !== __privateGet(this, _savedContent);
978
- }
979
- async save() {
980
- if (__privateGet(this, _content) !== void 0) {
981
- await __privateGet(this, _access).save(__privateGet(this, _content));
982
- __privateSet(this, _savedContent, __privateGet(this, _content));
983
- __privateGet(this, _signalUpdate).call(this);
984
- }
985
- }
986
- async reload() {
987
- const file = await __privateGet(this, _access).file();
988
- if (file.size > MAX_SIZE) {
989
- if (__privateGet(this, _content) !== void 0) {
990
- __privateSet(this, _content, void 0);
991
- __privateSet(this, _savedContent, void 0);
992
- __privateGet(this, _signalUpdate).call(this);
993
- }
994
- return;
995
- }
996
- const content = await file.text();
997
- if (__privateGet(this, _content) !== content) {
998
- __privateSet(this, _content, content);
999
- __privateSet(this, _savedContent, content);
1000
- __privateGet(this, _signalUpdate).call(this);
1001
- }
1002
- }
1003
- }
1004
- _access = new WeakMap();
1005
- _signalUpdate = new WeakMap();
1006
- _content = new WeakMap();
1007
- _savedContent = new WeakMap();
1008
- class DirectoryEditorManager {
1009
- constructor(access) {
1010
- __privateAdd(this, _access2, void 0);
1011
- __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
1012
- __privateAdd(this, _files, []);
1013
- __privateAdd(this, _selectedFile, void 0);
1014
- this.setSelectedFile = (path) => {
1015
- const prev = __privateGet(this, _selectedFile);
1016
- const next = __privateGet(this, _files).find((file) => file.path === path);
1017
- if (prev !== next) {
1018
- __privateSet(this, _selectedFile, next);
1019
- __privateGet(this, _signalUpdate2).call(this);
1020
- }
1021
- };
1022
- __privateAdd(this, _signalUpdate2, () => {
1023
- __privateGet(this, _listeners).forEach((listener) => listener());
1024
- });
1025
- __privateSet(this, _access2, access);
1026
- }
1027
- get files() {
1028
- return __privateGet(this, _files);
1029
- }
1030
- get selectedFile() {
1031
- return __privateGet(this, _selectedFile);
1032
- }
1033
- get dirty() {
1034
- return __privateGet(this, _files).some((file) => file.dirty);
1035
- }
1036
- async save() {
1037
- await Promise.all(__privateGet(this, _files).map((file) => file.save()));
1038
- }
1039
- async reload() {
1040
- var _a;
1041
- const selectedPath = (_a = __privateGet(this, _selectedFile)) == null ? void 0 : _a.path;
1042
- const files = await __privateGet(this, _access2).listFiles();
1043
- const fileManagers = await Promise.all(
1044
- files.map(async (file) => {
1045
- const manager = new DirectoryEditorFileManager(
1046
- file,
1047
- __privateGet(this, _signalUpdate2)
1048
- );
1049
- await manager.reload();
1050
- return manager;
1051
- })
1052
- );
1053
- __privateGet(this, _files).length = 0;
1054
- __privateGet(this, _files).push(...fileManagers);
1055
- this.setSelectedFile(selectedPath);
1056
- __privateGet(this, _signalUpdate2).call(this);
1057
- }
1058
- subscribe(listener) {
1059
- __privateGet(this, _listeners).add(listener);
1060
- return () => {
1061
- __privateGet(this, _listeners).delete(listener);
1062
- };
1063
- }
1064
- }
1065
- _access2 = new WeakMap();
1066
- _listeners = new WeakMap();
1067
- _files = new WeakMap();
1068
- _selectedFile = new WeakMap();
1069
- _signalUpdate2 = new WeakMap();
1070
- const DirectoryEditorContext = createContext(
1071
- void 0
1072
- );
1073
- function useDirectoryEditor() {
1074
- const value = useContext(DirectoryEditorContext);
1075
- const rerender = useRerender();
1076
- useEffect(() => value == null ? void 0 : value.subscribe(rerender), [value, rerender]);
1077
- if (!value) {
1078
- throw new Error("must be used within a DirectoryEditorProvider");
1079
- }
1080
- return value;
1081
- }
1082
- function DirectoryEditorProvider(props) {
1083
- const { directory } = props;
1084
- const [{ result, error }, { execute }] = useAsync$1(
1085
- async (dir) => {
1086
- const manager = new DirectoryEditorManager(dir);
1087
- await manager.reload();
1088
- const firstYaml = manager.files.find((file) => file.path.match(/\.ya?ml$/));
1089
- if (firstYaml) {
1090
- manager.setSelectedFile(firstYaml.path);
1091
- }
1092
- return manager;
1093
- }
1094
- );
1095
- useEffect(() => {
1096
- execute(directory);
1097
- }, [execute, directory]);
1098
- if (error) {
1099
- return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
1100
- } else if (!result) {
1101
- return /* @__PURE__ */ React.createElement(Progress, null);
1102
- }
1103
- return /* @__PURE__ */ React.createElement(DirectoryEditorContext.Provider, { value: result }, props.children);
1104
- }
1105
-
1106
- const MAX_CONTENT_SIZE = 64 * 1024;
1107
- const CHUNK_SIZE = 32 * 1024;
1108
- const DryRunContext = createContext(void 0);
1109
- function base64EncodeContent(content) {
1110
- if (content.length > MAX_CONTENT_SIZE) {
1111
- return window.btoa("<file too large>");
1112
- }
1113
- try {
1114
- return window.btoa(content);
1115
- } catch {
1116
- const decoder = new TextEncoder();
1117
- const buffer = decoder.encode(content);
1118
- const chunks = new Array();
1119
- for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
1120
- chunks.push(
1121
- String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE))
1122
- );
1123
- }
1124
- return window.btoa(chunks.join(""));
1125
- }
1126
- }
1127
- function DryRunProvider(props) {
1128
- const scaffolderApi = useApi(scaffolderApiRef);
1129
- const [state, setState] = useState({
1130
- results: [],
1131
- selectedResult: void 0
1132
- });
1133
- const idRef = useRef(1);
1134
- const selectResult = useCallback((id) => {
1135
- setState((prevState) => {
1136
- const result = prevState.results.find((r) => r.id === id);
1137
- if (result === prevState.selectedResult) {
1138
- return prevState;
1139
- }
1140
- return {
1141
- results: prevState.results,
1142
- selectedResult: result
1143
- };
1144
- });
1145
- }, []);
1146
- const deleteResult = useCallback((id) => {
1147
- setState((prevState) => {
1148
- var _a;
1149
- const index = prevState.results.findIndex((r) => r.id === id);
1150
- if (index === -1) {
1151
- return prevState;
1152
- }
1153
- const newResults = prevState.results.slice();
1154
- const [deleted] = newResults.splice(index, 1);
1155
- return {
1156
- results: newResults,
1157
- selectedResult: ((_a = prevState.selectedResult) == null ? void 0 : _a.id) === deleted.id ? newResults[0] : prevState.selectedResult
1158
- };
1159
- });
1160
- }, []);
1161
- const execute = useCallback(
1162
- async (options) => {
1163
- if (!scaffolderApi.dryRun) {
1164
- throw new Error("Scaffolder API does not support dry-run");
1165
- }
1166
- const parsed = yaml.parse(options.templateContent);
1167
- const response = await scaffolderApi.dryRun({
1168
- template: parsed,
1169
- values: options.values,
1170
- secrets: {},
1171
- directoryContents: options.files.map((file) => ({
1172
- path: file.path,
1173
- base64Content: base64EncodeContent(file.content)
1174
- }))
1175
- });
1176
- const result = {
1177
- ...response,
1178
- id: idRef.current++
1179
- };
1180
- setState((prevState) => {
1181
- var _a;
1182
- return {
1183
- results: [...prevState.results, result],
1184
- selectedResult: (_a = prevState.selectedResult) != null ? _a : result
1185
- };
1186
- });
1187
- },
1188
- [scaffolderApi]
1189
- );
1190
- const dryRun = useMemo(
1191
- () => ({
1192
- ...state,
1193
- selectResult,
1194
- deleteResult,
1195
- execute
1196
- }),
1197
- [state, selectResult, deleteResult, execute]
1198
- );
1199
- return /* @__PURE__ */ React.createElement(DryRunContext.Provider, { value: dryRun }, props.children);
1200
- }
1201
- function useDryRun() {
1202
- const value = useContext(DryRunContext);
1203
- if (!value) {
1204
- throw new Error("must be used within a DryRunProvider");
1205
- }
1206
- return value;
1207
- }
1208
-
1209
- const useStyles$b = makeStyles$1({
895
+ const useStyles$3 = makeStyles$1({
1210
896
  containerWrapper: {
1211
897
  position: "relative",
1212
898
  width: "100%",
@@ -1255,7 +941,7 @@ function TemplateEditorForm(props) {
1255
941
  fieldExtensions = [],
1256
942
  layouts = []
1257
943
  } = props;
1258
- const classes = useStyles$b();
944
+ const classes = useStyles$3();
1259
945
  const apiHolder = useApiHolder();
1260
946
  const [steps, setSteps] = useState();
1261
947
  const fields = useMemo(() => {
@@ -1370,7 +1056,7 @@ function TemplateEditorFormDirectoryEditorDryRun(props) {
1370
1056
  TemplateEditorForm.DirectoryEditorDryRun = TemplateEditorFormDirectoryEditorDryRun;
1371
1057
 
1372
1058
  const Form = withTheme(Theme);
1373
- const useStyles$a = makeStyles((theme) => ({
1059
+ const useStyles$2 = makeStyles((theme) => ({
1374
1060
  root: {
1375
1061
  gridArea: "pageContent",
1376
1062
  display: "grid",
@@ -1400,7 +1086,7 @@ const CustomFieldExplorer = ({
1400
1086
  onClose
1401
1087
  }) => {
1402
1088
  var _a, _b;
1403
- const classes = useStyles$a();
1089
+ const classes = useStyles$2();
1404
1090
  const fieldOptions = customFieldExtensions.filter((field) => !!field.schema);
1405
1091
  const [selectedField, setSelectedField] = useState(fieldOptions[0]);
1406
1092
  const [fieldFormState, setFieldFormState] = useState({});
@@ -1500,589 +1186,6 @@ const CustomFieldExplorer = ({
1500
1186
  )));
1501
1187
  };
1502
1188
 
1503
- const useStyles$9 = makeStyles$1((theme) => ({
1504
- introText: {
1505
- textAlign: "center",
1506
- marginTop: theme.spacing(2)
1507
- },
1508
- card: {
1509
- position: "relative",
1510
- maxWidth: 340,
1511
- marginTop: theme.spacing(4),
1512
- margin: theme.spacing(0, 2)
1513
- },
1514
- infoIcon: {
1515
- position: "absolute",
1516
- top: theme.spacing(1),
1517
- right: theme.spacing(1)
1518
- }
1519
- }));
1520
- function TemplateEditorIntro(props) {
1521
- const classes = useStyles$9();
1522
- const supportsLoad = WebFileSystemAccess.isSupported();
1523
- const cardLoadLocal = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(
1524
- CardActionArea,
1525
- {
1526
- disabled: !supportsLoad,
1527
- onClick: () => {
1528
- var _a;
1529
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "local");
1530
- }
1531
- },
1532
- /* @__PURE__ */ React.createElement(CardContent$1, null, /* @__PURE__ */ React.createElement(
1533
- Typography$1,
1534
- {
1535
- variant: "h5",
1536
- gutterBottom: true,
1537
- color: supportsLoad ? void 0 : "textSecondary",
1538
- style: { display: "flex", flexFlow: "row nowrap" }
1539
- },
1540
- "Load Template Directory"
1541
- ), /* @__PURE__ */ React.createElement(
1542
- Typography$1,
1543
- {
1544
- variant: "body1",
1545
- color: supportsLoad ? void 0 : "textSecondary"
1546
- },
1547
- "Load a local template directory, allowing you to both edit and try executing your own template."
1548
- ))
1549
- ), !supportsLoad && /* @__PURE__ */ React.createElement("div", { className: classes.infoIcon }, /* @__PURE__ */ React.createElement(
1550
- Tooltip$1,
1551
- {
1552
- placement: "top",
1553
- title: "Only supported in some Chromium-based browsers"
1554
- },
1555
- /* @__PURE__ */ React.createElement(InfoOutlinedIcon, null)
1556
- )));
1557
- const cardFormEditor = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
1558
- var _a;
1559
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "form");
1560
- } }, /* @__PURE__ */ React.createElement(CardContent$1, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h5", gutterBottom: true }, "Edit Template Form"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "Preview and edit a template form, either using a sample template or by loading a template from the catalog."))));
1561
- const cardFieldExplorer = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
1562
- var _a;
1563
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "field-explorer");
1564
- } }, /* @__PURE__ */ React.createElement(CardContent$1, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h5", gutterBottom: true }, "Custom Field Explorer"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "View and play around with available installed custom field extensions."))));
1565
- return /* @__PURE__ */ React.createElement("div", { style: props.style }, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h6", className: classes.introText }, "Get started by choosing one of the options below"), /* @__PURE__ */ React.createElement(
1566
- "div",
1567
- {
1568
- style: {
1569
- display: "flex",
1570
- flexFlow: "row wrap",
1571
- alignItems: "flex-start",
1572
- justifyContent: "center",
1573
- alignContent: "flex-start"
1574
- }
1575
- },
1576
- supportsLoad && cardLoadLocal,
1577
- cardFormEditor,
1578
- !supportsLoad && cardLoadLocal,
1579
- cardFieldExplorer
1580
- ));
1581
- }
1582
-
1583
- const useStyles$8 = makeStyles$1((theme) => ({
1584
- root: {
1585
- overflowY: "auto",
1586
- background: theme.palette.background.default
1587
- },
1588
- iconSuccess: {
1589
- minWidth: 0,
1590
- marginRight: theme.spacing(1),
1591
- color: theme.palette.status.ok
1592
- },
1593
- iconFailure: {
1594
- minWidth: 0,
1595
- marginRight: theme.spacing(1),
1596
- color: theme.palette.status.error
1597
- }
1598
- }));
1599
- function DryRunResultsList() {
1600
- const classes = useStyles$8();
1601
- const dryRun = useDryRun();
1602
- return /* @__PURE__ */ React.createElement(List$1, { className: classes.root, dense: true }, dryRun.results.map((result) => {
1603
- var _a;
1604
- const failed = result.log.some((l) => l.body.status === "failed");
1605
- return /* @__PURE__ */ React.createElement(
1606
- ListItem,
1607
- {
1608
- button: true,
1609
- key: result.id,
1610
- selected: ((_a = dryRun.selectedResult) == null ? void 0 : _a.id) === result.id,
1611
- onClick: () => dryRun.selectResult(result.id)
1612
- },
1613
- /* @__PURE__ */ React.createElement(
1614
- ListItemIcon,
1615
- {
1616
- className: failed ? classes.iconFailure : classes.iconSuccess
1617
- },
1618
- failed ? /* @__PURE__ */ React.createElement(Cancel, null) : /* @__PURE__ */ React.createElement(Check, null)
1619
- ),
1620
- /* @__PURE__ */ React.createElement(ListItemText, { primary: `Result ${result.id}` }),
1621
- /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
1622
- IconButton$1,
1623
- {
1624
- edge: "end",
1625
- "aria-label": "delete",
1626
- onClick: () => dryRun.deleteResult(result.id)
1627
- },
1628
- /* @__PURE__ */ React.createElement(DeleteIcon, null)
1629
- ))
1630
- );
1631
- }));
1632
- }
1633
-
1634
- const useStyles$7 = makeStyles$1({
1635
- root: {
1636
- whiteSpace: "nowrap",
1637
- overflowY: "auto"
1638
- }
1639
- });
1640
- function parseFileEntires(paths) {
1641
- const root = {
1642
- type: "directory",
1643
- name: "",
1644
- path: "",
1645
- children: []
1646
- };
1647
- for (const path of paths.slice().sort()) {
1648
- const parts = path.split("/");
1649
- let current = root;
1650
- for (let i = 0; i < parts.length; i++) {
1651
- const part = parts[i];
1652
- if (part === "") {
1653
- throw new Error(`Invalid path part: ''`);
1654
- }
1655
- const entryPath = parts.slice(0, i + 1).join("/");
1656
- const existing = current.children.find((child) => child.name === part);
1657
- if ((existing == null ? void 0 : existing.type) === "file") {
1658
- throw new Error(`Duplicate filename at '${entryPath}'`);
1659
- } else if (existing) {
1660
- current = existing;
1661
- } else {
1662
- if (i < parts.length - 1) {
1663
- const newEntry = {
1664
- type: "directory",
1665
- name: part,
1666
- path: entryPath,
1667
- children: []
1668
- };
1669
- const firstFileIndex = current.children.findIndex(
1670
- (child) => child.type === "file"
1671
- );
1672
- current.children.splice(firstFileIndex, 0, newEntry);
1673
- current = newEntry;
1674
- } else {
1675
- current.children.push({
1676
- type: "file",
1677
- name: part,
1678
- path: entryPath
1679
- });
1680
- }
1681
- }
1682
- }
1683
- }
1684
- return root.children;
1685
- }
1686
- function FileTreeItem({ entry }) {
1687
- if (entry.type === "file") {
1688
- return /* @__PURE__ */ React.createElement(TreeItem, { nodeId: entry.path, label: entry.name });
1689
- }
1690
- return /* @__PURE__ */ React.createElement(TreeItem, { nodeId: entry.path, label: entry.name }, entry.children.map((child) => /* @__PURE__ */ React.createElement(FileTreeItem, { key: child.path, entry: child })));
1691
- }
1692
- function FileBrowser(props) {
1693
- const classes = useStyles$7();
1694
- const fileTree = useMemo(
1695
- () => parseFileEntires(props.filePaths),
1696
- [props.filePaths]
1697
- );
1698
- return /* @__PURE__ */ React.createElement(
1699
- TreeView,
1700
- {
1701
- selected: props.selected,
1702
- className: classes.root,
1703
- defaultCollapseIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
1704
- defaultExpandIcon: /* @__PURE__ */ React.createElement(ChevronRightIcon, null),
1705
- onNodeSelect: (_e, nodeId) => {
1706
- if (props.onSelect && props.filePaths.includes(nodeId)) {
1707
- props.onSelect(nodeId);
1708
- }
1709
- }
1710
- },
1711
- fileTree.map((entry) => /* @__PURE__ */ React.createElement(FileTreeItem, { key: entry.path, entry }))
1712
- );
1713
- }
1714
-
1715
- const useStyles$6 = makeStyles$1((theme) => ({
1716
- root: {
1717
- display: "grid",
1718
- gridTemplateColumns: "280px auto 3fr",
1719
- gridTemplateRows: "1fr"
1720
- },
1721
- child: {
1722
- overflowY: "auto",
1723
- height: "100%",
1724
- minHeight: 0
1725
- },
1726
- firstChild: {
1727
- background: theme.palette.background.paper
1728
- }
1729
- }));
1730
- function DryRunResultsSplitView(props) {
1731
- const classes = useStyles$6();
1732
- const childArray = Children.toArray(props.children);
1733
- if (childArray.length !== 2) {
1734
- throw new Error("must have exactly 2 children");
1735
- }
1736
- return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement("div", { className: classNames(classes.child, classes.firstChild) }, childArray[0]), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement("div", { className: classes.child }, childArray[1]));
1737
- }
1738
-
1739
- const useStyles$5 = makeStyles$1({
1740
- root: {
1741
- display: "flex",
1742
- flexFlow: "column nowrap"
1743
- },
1744
- contentWrapper: {
1745
- flex: 1,
1746
- position: "relative"
1747
- },
1748
- content: {
1749
- position: "absolute",
1750
- top: 0,
1751
- left: 0,
1752
- right: 0,
1753
- bottom: 0,
1754
- display: "flex",
1755
- "& > *": {
1756
- flex: 1
1757
- }
1758
- },
1759
- codeMirror: {
1760
- height: "100%",
1761
- overflowY: "auto"
1762
- }
1763
- });
1764
- function FilesContent() {
1765
- const classes = useStyles$5();
1766
- const { selectedResult } = useDryRun();
1767
- const [selectedPath, setSelectedPath] = useState("");
1768
- const selectedFile = selectedResult == null ? void 0 : selectedResult.directoryContents.find(
1769
- (f) => f.path === selectedPath
1770
- );
1771
- useEffect(() => {
1772
- if (selectedResult) {
1773
- const [firstFile] = selectedResult.directoryContents;
1774
- if (firstFile) {
1775
- setSelectedPath(firstFile.path);
1776
- } else {
1777
- setSelectedPath("");
1778
- }
1779
- }
1780
- return void 0;
1781
- }, [selectedResult]);
1782
- if (!selectedResult) {
1783
- return null;
1784
- }
1785
- return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(
1786
- FileBrowser,
1787
- {
1788
- selected: selectedPath,
1789
- onSelect: setSelectedPath,
1790
- filePaths: selectedResult.directoryContents.map((file) => file.path)
1791
- }
1792
- ), /* @__PURE__ */ React.createElement(
1793
- CodeMirror,
1794
- {
1795
- className: classes.codeMirror,
1796
- theme: "dark",
1797
- height: "100%",
1798
- extensions: [StreamLanguage.define(yaml$1)],
1799
- readOnly: true,
1800
- value: (selectedFile == null ? void 0 : selectedFile.base64Content) ? atob(selectedFile.base64Content) : ""
1801
- }
1802
- ));
1803
- }
1804
- function LogContent() {
1805
- var _a, _b;
1806
- const { selectedResult } = useDryRun();
1807
- const [currentStepId, setUserSelectedStepId] = useState();
1808
- const steps = useMemo(() => {
1809
- var _a2;
1810
- if (!selectedResult) {
1811
- return [];
1812
- }
1813
- return (_a2 = selectedResult.steps.map((step) => {
1814
- var _a3, _b2;
1815
- const stepLog = selectedResult.log.filter(
1816
- (l) => l.body.stepId === step.id
1817
- );
1818
- return {
1819
- id: step.id,
1820
- name: step.name,
1821
- logString: stepLog.map((l) => l.body.message).join("\n"),
1822
- status: (_b2 = (_a3 = stepLog[stepLog.length - 1]) == null ? void 0 : _a3.body.status) != null ? _b2 : "completed"
1823
- };
1824
- })) != null ? _a2 : [];
1825
- }, [selectedResult]);
1826
- if (!selectedResult) {
1827
- return null;
1828
- }
1829
- const selectedStep = (_a = steps.find((s) => s.id === currentStepId)) != null ? _a : steps[0];
1830
- return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(
1831
- TaskStatusStepper,
1832
- {
1833
- steps,
1834
- currentStepId: selectedStep.id,
1835
- onUserStepChange: setUserSelectedStepId
1836
- }
1837
- ), /* @__PURE__ */ React.createElement(LogViewer, { text: (_b = selectedStep == null ? void 0 : selectedStep.logString) != null ? _b : "" }));
1838
- }
1839
- function OutputContent() {
1840
- var _a, _b;
1841
- const classes = useStyles$5();
1842
- const { selectedResult } = useDryRun();
1843
- if (!selectedResult) {
1844
- return null;
1845
- }
1846
- return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(Box$1, { pt: 2 }, ((_b = (_a = selectedResult.output) == null ? void 0 : _a.links) == null ? void 0 : _b.length) && /* @__PURE__ */ React.createElement(TaskPageLinks, { output: selectedResult.output })), /* @__PURE__ */ React.createElement(
1847
- CodeMirror,
1848
- {
1849
- className: classes.codeMirror,
1850
- theme: "dark",
1851
- height: "100%",
1852
- extensions: [StreamLanguage.define(yaml$1)],
1853
- readOnly: true,
1854
- value: JSON.stringify(selectedResult.output, null, 2)
1855
- }
1856
- ));
1857
- }
1858
- function DryRunResultsView() {
1859
- const classes = useStyles$5();
1860
- const [selectedTab, setSelectedTab] = useState(
1861
- "files"
1862
- );
1863
- return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement(Tabs, { value: selectedTab, onChange: (_, v) => setSelectedTab(v) }, /* @__PURE__ */ React.createElement(Tab, { value: "files", label: "Files" }), /* @__PURE__ */ React.createElement(Tab, { value: "log", label: "Log" }), /* @__PURE__ */ React.createElement(Tab, { value: "output", label: "Output" })), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement("div", { className: classes.contentWrapper }, /* @__PURE__ */ React.createElement("div", { className: classes.content }, selectedTab === "files" && /* @__PURE__ */ React.createElement(FilesContent, null), selectedTab === "log" && /* @__PURE__ */ React.createElement(LogContent, null), selectedTab === "output" && /* @__PURE__ */ React.createElement(OutputContent, null))));
1864
- }
1865
-
1866
- const useStyles$4 = makeStyles$1((theme) => ({
1867
- header: {
1868
- height: 48,
1869
- minHeight: 0,
1870
- "&.Mui-expanded": {
1871
- height: 48,
1872
- minHeight: 0
1873
- }
1874
- },
1875
- content: {
1876
- display: "grid",
1877
- background: theme.palette.background.default,
1878
- gridTemplateColumns: "180px auto 1fr",
1879
- gridTemplateRows: "1fr",
1880
- padding: 0,
1881
- height: 400
1882
- }
1883
- }));
1884
- function DryRunResults() {
1885
- const classes = useStyles$4();
1886
- const dryRun = useDryRun();
1887
- const [expanded, setExpanded] = useState(false);
1888
- const [hidden, setHidden] = useState(true);
1889
- const resultsLength = dryRun.results.length;
1890
- const prevResultsLength = usePrevious(resultsLength);
1891
- useEffect(() => {
1892
- if (prevResultsLength === 0 && resultsLength === 1) {
1893
- setHidden(false);
1894
- setExpanded(true);
1895
- } else if (prevResultsLength === 1 && resultsLength === 0) {
1896
- setExpanded(false);
1897
- }
1898
- }, [prevResultsLength, resultsLength]);
1899
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1900
- Accordion,
1901
- {
1902
- variant: "outlined",
1903
- expanded,
1904
- hidden: resultsLength === 0 && hidden,
1905
- onChange: (_, exp) => setExpanded(exp),
1906
- onTransitionEnd: () => resultsLength === 0 && setHidden(true)
1907
- },
1908
- /* @__PURE__ */ React.createElement(
1909
- AccordionSummary,
1910
- {
1911
- className: classes.header,
1912
- expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon$1, null)
1913
- },
1914
- /* @__PURE__ */ React.createElement(Typography$1, null, "Dry-run results")
1915
- ),
1916
- /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }),
1917
- /* @__PURE__ */ React.createElement(AccordionDetails, { className: classes.content }, /* @__PURE__ */ React.createElement(DryRunResultsList, null), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement(DryRunResultsView, null))
1918
- ));
1919
- }
1920
-
1921
- const useStyles$3 = makeStyles((theme) => ({
1922
- button: {
1923
- padding: theme.spacing(1)
1924
- },
1925
- buttons: {
1926
- display: "flex",
1927
- flexFlow: "row nowrap",
1928
- alignItems: "center",
1929
- justifyContent: "flex-start"
1930
- },
1931
- buttonsGap: {
1932
- flex: "1 1 auto"
1933
- },
1934
- buttonsDivider: {
1935
- marginBottom: theme.spacing(1)
1936
- }
1937
- }));
1938
- function TemplateEditorBrowser(props) {
1939
- var _a, _b;
1940
- const classes = useStyles$3();
1941
- const directoryEditor = useDirectoryEditor();
1942
- const changedFiles = directoryEditor.files.filter((file) => file.dirty);
1943
- const handleClose = () => {
1944
- if (!props.onClose) {
1945
- return;
1946
- }
1947
- if (changedFiles.length > 0) {
1948
- const accepted = window.confirm(
1949
- "Are you sure? Unsaved changes will be lost"
1950
- );
1951
- if (!accepted) {
1952
- return;
1953
- }
1954
- }
1955
- props.onClose();
1956
- };
1957
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: classes.buttons }, /* @__PURE__ */ React.createElement(Tooltip, { title: "Save all files" }, /* @__PURE__ */ React.createElement(
1958
- IconButton,
1959
- {
1960
- className: classes.button,
1961
- disabled: directoryEditor.files.every((file) => !file.dirty),
1962
- onClick: () => directoryEditor.save()
1963
- },
1964
- /* @__PURE__ */ React.createElement(SaveIcon, null)
1965
- )), /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload directory" }, /* @__PURE__ */ React.createElement(
1966
- IconButton,
1967
- {
1968
- className: classes.button,
1969
- onClick: () => directoryEditor.reload()
1970
- },
1971
- /* @__PURE__ */ React.createElement(RefreshIcon, null)
1972
- )), /* @__PURE__ */ React.createElement("div", { className: classes.buttonsGap }), /* @__PURE__ */ React.createElement(Tooltip, { title: "Close directory" }, /* @__PURE__ */ React.createElement(IconButton, { className: classes.button, onClick: handleClose }, /* @__PURE__ */ React.createElement(CloseIcon, null)))), /* @__PURE__ */ React.createElement(Divider$1, { className: classes.buttonsDivider }), /* @__PURE__ */ React.createElement(
1973
- FileBrowser,
1974
- {
1975
- selected: (_b = (_a = directoryEditor.selectedFile) == null ? void 0 : _a.path) != null ? _b : "",
1976
- onSelect: directoryEditor.setSelectedFile,
1977
- filePaths: directoryEditor.files.map((file) => file.path)
1978
- }
1979
- ));
1980
- }
1981
-
1982
- const useStyles$2 = makeStyles((theme) => ({
1983
- container: {
1984
- position: "relative",
1985
- width: "100%",
1986
- height: "100%"
1987
- },
1988
- codeMirror: {
1989
- position: "absolute",
1990
- top: 0,
1991
- bottom: 0,
1992
- left: 0,
1993
- right: 0
1994
- },
1995
- errorPanel: {
1996
- color: theme.palette.error.main,
1997
- lineHeight: 2,
1998
- margin: theme.spacing(0, 1)
1999
- },
2000
- floatingButtons: {
2001
- position: "absolute",
2002
- top: theme.spacing(1),
2003
- right: theme.spacing(3)
2004
- },
2005
- floatingButton: {
2006
- padding: theme.spacing(1)
2007
- }
2008
- }));
2009
- function TemplateEditorTextArea(props) {
2010
- const { errorText } = props;
2011
- const classes = useStyles$2();
2012
- const panelExtension = useMemo(() => {
2013
- if (!errorText) {
2014
- return showPanel.of(null);
2015
- }
2016
- const dom = document.createElement("div");
2017
- dom.classList.add(classes.errorPanel);
2018
- dom.textContent = errorText;
2019
- return showPanel.of(() => ({ dom, bottom: true }));
2020
- }, [classes, errorText]);
2021
- useKeyboardEvent(
2022
- (e) => e.key === "s" && (e.ctrlKey || e.metaKey),
2023
- (e) => {
2024
- e.preventDefault();
2025
- if (props.onSave) {
2026
- props.onSave();
2027
- }
2028
- }
2029
- );
2030
- return /* @__PURE__ */ React.createElement("div", { className: classes.container }, /* @__PURE__ */ React.createElement(
2031
- CodeMirror,
2032
- {
2033
- className: classes.codeMirror,
2034
- theme: "dark",
2035
- height: "100%",
2036
- extensions: [StreamLanguage.define(yaml$1), panelExtension],
2037
- value: props.content,
2038
- onChange: props.onUpdate
2039
- }
2040
- ), (props.onSave || props.onReload) && /* @__PURE__ */ React.createElement("div", { className: classes.floatingButtons }, /* @__PURE__ */ React.createElement(Paper, null, props.onSave && /* @__PURE__ */ React.createElement(Tooltip, { title: "Save file" }, /* @__PURE__ */ React.createElement(
2041
- IconButton,
2042
- {
2043
- className: classes.floatingButton,
2044
- onClick: () => {
2045
- var _a;
2046
- return (_a = props.onSave) == null ? void 0 : _a.call(props);
2047
- }
2048
- },
2049
- /* @__PURE__ */ React.createElement(SaveIcon, null)
2050
- )), props.onReload && /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload file" }, /* @__PURE__ */ React.createElement(
2051
- IconButton,
2052
- {
2053
- className: classes.floatingButton,
2054
- onClick: () => {
2055
- var _a;
2056
- return (_a = props.onReload) == null ? void 0 : _a.call(props);
2057
- }
2058
- },
2059
- /* @__PURE__ */ React.createElement(RefreshIcon, null)
2060
- )))));
2061
- }
2062
- function TemplateEditorDirectoryEditorTextArea(props) {
2063
- var _a, _b;
2064
- const directoryEditor = useDirectoryEditor();
2065
- const actions = ((_a = directoryEditor.selectedFile) == null ? void 0 : _a.dirty) ? {
2066
- onSave: () => directoryEditor.save(),
2067
- onReload: () => directoryEditor.reload()
2068
- } : {
2069
- onReload: () => directoryEditor.reload()
2070
- };
2071
- return /* @__PURE__ */ React.createElement(
2072
- TemplateEditorTextArea,
2073
- {
2074
- errorText: props.errorText,
2075
- content: (_b = directoryEditor.selectedFile) == null ? void 0 : _b.content,
2076
- onUpdate: (content) => {
2077
- var _a2;
2078
- return (_a2 = directoryEditor.selectedFile) == null ? void 0 : _a2.updateContent(content);
2079
- },
2080
- ...actions
2081
- }
2082
- );
2083
- }
2084
- TemplateEditorTextArea.DirectoryEditor = TemplateEditorDirectoryEditorTextArea;
2085
-
2086
1189
  const useStyles$1 = makeStyles({
2087
1190
  // Reset and fix sizing to make sure scrolling behaves correctly
2088
1191
  root: {
@@ -2411,4 +1514,4 @@ const Router = (props) => {
2411
1514
  };
2412
1515
 
2413
1516
  export { Router };
2414
- //# sourceMappingURL=Router-e878e04f.esm.js.map
1517
+ //# sourceMappingURL=Router-fae7a62f.esm.js.map