@backstage/plugin-scaffolder 1.12.0 → 1.13.0-next.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.d.ts +12 -59
  4. package/dist/alpha.esm.js +27 -33
  5. package/dist/alpha.esm.js.map +1 -1
  6. package/dist/esm/{alpha/ListTasksPage-2e8f4176.esm.js → ListTasksPage-ca799894.esm.js} +3 -2
  7. package/dist/esm/ListTasksPage-ca799894.esm.js.map +1 -0
  8. package/dist/esm/{index/Router-6fd61bff.esm.js → Router-1b71c4f0.esm.js} +39 -28
  9. package/dist/esm/Router-1b71c4f0.esm.js.map +1 -0
  10. package/dist/esm/TaskPage-589238dc.esm.js +1684 -0
  11. package/dist/esm/TaskPage-589238dc.esm.js.map +1 -0
  12. package/dist/esm/TemplateEditorIntro-644bad26.esm.js +1128 -0
  13. package/dist/esm/TemplateEditorIntro-644bad26.esm.js.map +1 -0
  14. package/dist/esm/TemplateFormPreviewer-32c3dcf3.esm.js +630 -0
  15. package/dist/esm/TemplateFormPreviewer-32c3dcf3.esm.js.map +1 -0
  16. package/dist/esm/TemplateTypePicker-4f07b216.esm.js +58 -0
  17. package/dist/esm/TemplateTypePicker-4f07b216.esm.js.map +1 -0
  18. package/dist/esm/{alpha/index-2131f4a0.esm.js → index-bce9c23d.esm.js} +95 -156
  19. package/dist/esm/index-bce9c23d.esm.js.map +1 -0
  20. package/dist/index.d.ts +28 -167
  21. package/dist/index.esm.js +27 -65
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/types/plugin.d-bc532f9e.d.ts +232 -0
  24. package/package.json +24 -22
  25. package/dist/esm/alpha/ListTasksPage-2e8f4176.esm.js.map +0 -1
  26. package/dist/esm/alpha/Router-2826a2b8.esm.js +0 -1566
  27. package/dist/esm/alpha/Router-2826a2b8.esm.js.map +0 -1
  28. package/dist/esm/alpha/alpha-714dad1b.esm.js +0 -3422
  29. package/dist/esm/alpha/alpha-714dad1b.esm.js.map +0 -1
  30. package/dist/esm/alpha/index-2131f4a0.esm.js.map +0 -1
  31. package/dist/esm/index/ListTasksPage-fa403ee3.esm.js +0 -192
  32. package/dist/esm/index/ListTasksPage-fa403ee3.esm.js.map +0 -1
  33. package/dist/esm/index/Router-6fd61bff.esm.js.map +0 -1
  34. package/dist/esm/index/index-0b6cdf44.esm.js +0 -3475
  35. package/dist/esm/index/index-0b6cdf44.esm.js.map +0 -1
  36. package/dist/esm/index/index-f404fb0b.esm.js +0 -449
  37. package/dist/esm/index/index-f404fb0b.esm.js.map +0 -1
@@ -0,0 +1,1128 @@
1
+ import React, { Fragment, createContext, useState, useRef, useCallback, useMemo, useContext, useEffect, Children } from 'react';
2
+ import useAsync from 'react-use/lib/useAsync';
3
+ import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
4
+ import { makeStyles, Box, Typography, Accordion, AccordionSummary, AccordionDetails, Grid, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Chip, Card, List, MenuItem, ListItemIcon, ListItemText, Tooltip, IconButton as IconButton$1, Divider as Divider$1 } from '@material-ui/core';
5
+ import classNames from 'classnames';
6
+ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
7
+ import { useApi } from '@backstage/core-plugin-api';
8
+ import { Progress, ErrorPage, MarkdownContent, Page, Header, Content, CodeSnippet, ErrorPanel, LogViewer } from '@backstage/core-components';
9
+ import SettingsIcon from '@material-ui/icons/Settings';
10
+ import AllIcon from '@material-ui/icons/FontDownload';
11
+ import Accordion$1 from '@material-ui/core/Accordion';
12
+ import AccordionDetails$1 from '@material-ui/core/AccordionDetails';
13
+ import AccordionSummary$1 from '@material-ui/core/AccordionSummary';
14
+ import Divider from '@material-ui/core/Divider';
15
+ import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
16
+ import Typography$1 from '@material-ui/core/Typography';
17
+ import ExpandMoreIcon$1 from '@material-ui/icons/ExpandLess';
18
+ import { useAsync as useAsync$1, useRerender, usePrevious, useKeyboardEvent } from '@react-hookz/web';
19
+ import yaml from 'yaml';
20
+ import IconButton from '@material-ui/core/IconButton';
21
+ import List$1 from '@material-ui/core/List';
22
+ import ListItem from '@material-ui/core/ListItem';
23
+ import ListItemIcon$1 from '@material-ui/core/ListItemIcon';
24
+ import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
25
+ import ListItemText$1 from '@material-ui/core/ListItemText';
26
+ import Cancel from '@material-ui/icons/Cancel';
27
+ import Check from '@material-ui/icons/Check';
28
+ import DeleteIcon from '@material-ui/icons/Delete';
29
+ import { StreamLanguage } from '@codemirror/language';
30
+ import { yaml as yaml$1 } from '@codemirror/legacy-modes/mode/yaml';
31
+ import Box$1 from '@material-ui/core/Box';
32
+ import Tab from '@material-ui/core/Tab';
33
+ import Tabs from '@material-ui/core/Tabs';
34
+ import CodeMirror from '@uiw/react-codemirror';
35
+ import { l as TaskStatusStepper, n as TaskPageLinks } from './TaskPage-589238dc.esm.js';
36
+ import CloseIcon from '@material-ui/icons/Close';
37
+ import RefreshIcon from '@material-ui/icons/Refresh';
38
+ import SaveIcon from '@material-ui/icons/Save';
39
+ import TreeView from '@material-ui/lab/TreeView';
40
+ import ChevronRightIcon from '@material-ui/icons/ChevronRight';
41
+ import TreeItem from '@material-ui/lab/TreeItem';
42
+ import { showPanel } from '@codemirror/view';
43
+ import Card$1 from '@material-ui/core/Card';
44
+ import CardActionArea from '@material-ui/core/CardActionArea';
45
+ import CardContent from '@material-ui/core/CardContent';
46
+ import Tooltip$1 from '@material-ui/core/Tooltip';
47
+ import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
48
+
49
+ const useStyles$9 = makeStyles((theme) => ({
50
+ code: {
51
+ fontFamily: "Menlo, monospace",
52
+ padding: theme.spacing(1),
53
+ backgroundColor: theme.palette.type === "dark" ? theme.palette.grey[700] : theme.palette.grey[300],
54
+ display: "inline-block",
55
+ borderRadius: 5,
56
+ border: `1px solid ${theme.palette.grey[500]}`,
57
+ position: "relative"
58
+ },
59
+ codeRequired: {
60
+ "&::after": {
61
+ position: "absolute",
62
+ content: '"*"',
63
+ top: 0,
64
+ right: theme.spacing(0.5),
65
+ fontWeight: "bolder",
66
+ color: theme.palette.error.light
67
+ }
68
+ }
69
+ }));
70
+ const ExamplesTable = (props) => {
71
+ return /* @__PURE__ */ React.createElement(Grid, { container: true }, props.examples.map((example, index) => {
72
+ return /* @__PURE__ */ React.createElement(Fragment, { key: `example-${index}` }, /* @__PURE__ */ React.createElement(Grid, { item: true, lg: 3 }, /* @__PURE__ */ React.createElement(Box, { padding: 4 }, /* @__PURE__ */ React.createElement(Typography, null, example.description))), /* @__PURE__ */ React.createElement(Grid, { item: true, lg: 9 }, /* @__PURE__ */ React.createElement(Box, { padding: 1 }, /* @__PURE__ */ React.createElement(
73
+ CodeSnippet,
74
+ {
75
+ text: example.example,
76
+ showLineNumbers: true,
77
+ showCopyCodeButton: true,
78
+ language: "yaml"
79
+ }
80
+ ))));
81
+ }));
82
+ };
83
+ const ActionsPage = () => {
84
+ const api = useApi(scaffolderApiRef);
85
+ const classes = useStyles$9();
86
+ const { loading, value, error } = useAsync(async () => {
87
+ return api.listActions();
88
+ });
89
+ if (loading) {
90
+ return /* @__PURE__ */ React.createElement(Progress, null);
91
+ }
92
+ if (error) {
93
+ return /* @__PURE__ */ React.createElement(
94
+ ErrorPage,
95
+ {
96
+ statusMessage: "Failed to load installed actions",
97
+ status: "500"
98
+ }
99
+ );
100
+ }
101
+ const formatRows = (input) => {
102
+ const properties = input.properties;
103
+ if (!properties) {
104
+ return void 0;
105
+ }
106
+ return Object.entries(properties).map((entry) => {
107
+ var _a;
108
+ const [key] = entry;
109
+ const props = entry[1];
110
+ const codeClassname = classNames(classes.code, {
111
+ [classes.codeRequired]: (_a = input.required) == null ? void 0 : _a.includes(key)
112
+ });
113
+ return /* @__PURE__ */ React.createElement(TableRow, { key }, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement("div", { className: codeClassname }, key)), /* @__PURE__ */ React.createElement(TableCell, null, props.title), /* @__PURE__ */ React.createElement(TableCell, null, props.description), /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(React.Fragment, null, [props.type].flat().map((type) => /* @__PURE__ */ React.createElement(Chip, { label: type, key: type })))));
114
+ });
115
+ };
116
+ const renderTable = (input) => {
117
+ if (!input.properties) {
118
+ return void 0;
119
+ }
120
+ return /* @__PURE__ */ React.createElement(TableContainer, { component: Paper }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Name"), /* @__PURE__ */ React.createElement(TableCell, null, "Title"), /* @__PURE__ */ React.createElement(TableCell, null, "Description"), /* @__PURE__ */ React.createElement(TableCell, null, "Type"))), /* @__PURE__ */ React.createElement(TableBody, null, formatRows(input))));
121
+ };
122
+ const renderTables = (name, input) => {
123
+ if (!input) {
124
+ return void 0;
125
+ }
126
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, name), input.map((i, index) => /* @__PURE__ */ React.createElement("div", { key: index }, renderTable(i))));
127
+ };
128
+ const items = value == null ? void 0 : value.map((action) => {
129
+ var _a, _b, _c, _d;
130
+ if (action.id.startsWith("legacy:")) {
131
+ return void 0;
132
+ }
133
+ const oneOf = renderTables("oneOf", (_b = (_a = action.schema) == null ? void 0 : _a.input) == null ? void 0 : _b.oneOf);
134
+ return /* @__PURE__ */ React.createElement(Box, { pb: 4, key: action.id }, /* @__PURE__ */ React.createElement(Typography, { variant: "h4", className: classes.code }, action.id), action.description && /* @__PURE__ */ React.createElement(MarkdownContent, { content: action.description }), ((_c = action.schema) == null ? void 0 : _c.input) && /* @__PURE__ */ React.createElement(Box, { pb: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Input"), renderTable(action.schema.input), oneOf), ((_d = action.schema) == null ? void 0 : _d.output) && /* @__PURE__ */ React.createElement(Box, { pb: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Output"), renderTable(action.schema.output)), action.examples && /* @__PURE__ */ React.createElement(Accordion, null, /* @__PURE__ */ React.createElement(AccordionSummary, { expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null) }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Examples")), /* @__PURE__ */ React.createElement(AccordionDetails, null, /* @__PURE__ */ React.createElement(Box, { pb: 2 }, /* @__PURE__ */ React.createElement(ExamplesTable, { examples: action.examples })))));
135
+ });
136
+ return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, /* @__PURE__ */ React.createElement(
137
+ Header,
138
+ {
139
+ pageTitleOverride: "Create a New Component",
140
+ title: "Installed actions",
141
+ subtitle: "This is the collection of all installed actions"
142
+ }
143
+ ), /* @__PURE__ */ React.createElement(Content, null, items));
144
+ };
145
+
146
+ const useStyles$8 = makeStyles(
147
+ (theme) => ({
148
+ root: {
149
+ backgroundColor: "rgba(0, 0, 0, .11)",
150
+ boxShadow: "none",
151
+ margin: theme.spacing(1, 0, 1, 0)
152
+ },
153
+ title: {
154
+ margin: theme.spacing(1, 0, 0, 1),
155
+ textTransform: "uppercase",
156
+ fontSize: 12,
157
+ fontWeight: "bold"
158
+ },
159
+ listIcon: {
160
+ minWidth: 30,
161
+ color: theme.palette.text.primary
162
+ },
163
+ menuItem: {
164
+ minHeight: theme.spacing(6)
165
+ },
166
+ groupWrapper: {
167
+ margin: theme.spacing(1, 1, 2, 1)
168
+ }
169
+ }),
170
+ {
171
+ name: "ScaffolderReactOwnerListPicker"
172
+ }
173
+ );
174
+ function getFilterGroups() {
175
+ return [
176
+ {
177
+ name: "Task Owner",
178
+ items: [
179
+ {
180
+ id: "owned",
181
+ label: "Owned",
182
+ icon: SettingsIcon
183
+ },
184
+ {
185
+ id: "all",
186
+ label: "All",
187
+ icon: AllIcon
188
+ }
189
+ ]
190
+ }
191
+ ];
192
+ }
193
+ const OwnerListPicker = (props) => {
194
+ const { filter, onSelectOwner } = props;
195
+ const classes = useStyles$8();
196
+ const filterGroups = getFilterGroups();
197
+ return /* @__PURE__ */ React.createElement(Card, { className: classes.root }, filterGroups.map((group) => /* @__PURE__ */ React.createElement(Fragment, { key: group.name }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", className: classes.title }, group.name), /* @__PURE__ */ React.createElement(Card, { className: classes.groupWrapper }, /* @__PURE__ */ React.createElement(List, { disablePadding: true, dense: true }, group.items.map((item) => /* @__PURE__ */ React.createElement(
198
+ MenuItem,
199
+ {
200
+ key: item.id,
201
+ button: true,
202
+ divider: true,
203
+ onClick: () => onSelectOwner(item.id),
204
+ selected: item.id === filter,
205
+ className: classes.menuItem,
206
+ "data-testid": `owner-picker-${item.id}`
207
+ },
208
+ item.icon && /* @__PURE__ */ React.createElement(ListItemIcon, { className: classes.listIcon }, /* @__PURE__ */ React.createElement(item.icon, { fontSize: "small" })),
209
+ /* @__PURE__ */ React.createElement(ListItemText, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, item.label))
210
+ )))))));
211
+ };
212
+
213
+ const showDirectoryPicker = window.showDirectoryPicker;
214
+ class WebFileAccess {
215
+ constructor(path, handle) {
216
+ this.path = path;
217
+ this.handle = handle;
218
+ }
219
+ file() {
220
+ return this.handle.getFile();
221
+ }
222
+ async save(data) {
223
+ const writable = await this.handle.createWritable();
224
+ await writable.write(data);
225
+ await writable.close();
226
+ }
227
+ }
228
+ class WebDirectoryAccess {
229
+ constructor(handle) {
230
+ this.handle = handle;
231
+ }
232
+ async listFiles() {
233
+ const content = [];
234
+ for await (const entry of this.listDirectoryContents(this.handle)) {
235
+ content.push(entry);
236
+ }
237
+ return content;
238
+ }
239
+ async *listDirectoryContents(dirHandle, basePath = []) {
240
+ for await (const handle of dirHandle.values()) {
241
+ if (handle.kind === "file") {
242
+ yield new WebFileAccess([...basePath, handle.name].join("/"), handle);
243
+ } else if (handle.kind === "directory") {
244
+ if (handle.name === ".git") {
245
+ continue;
246
+ }
247
+ yield* this.listDirectoryContents(handle, [...basePath, handle.name]);
248
+ }
249
+ }
250
+ }
251
+ }
252
+ class WebFileSystemAccess {
253
+ static isSupported() {
254
+ return Boolean(showDirectoryPicker);
255
+ }
256
+ static async requestDirectoryAccess() {
257
+ if (!showDirectoryPicker) {
258
+ throw new Error("File system access is not supported");
259
+ }
260
+ const handle = await showDirectoryPicker();
261
+ return new WebDirectoryAccess(handle);
262
+ }
263
+ constructor() {
264
+ }
265
+ }
266
+
267
+ const MAX_CONTENT_SIZE = 64 * 1024;
268
+ const CHUNK_SIZE = 32 * 1024;
269
+ const DryRunContext = createContext(void 0);
270
+ function base64EncodeContent(content) {
271
+ if (content.length > MAX_CONTENT_SIZE) {
272
+ return window.btoa("<file too large>");
273
+ }
274
+ try {
275
+ return window.btoa(content);
276
+ } catch {
277
+ const decoder = new TextEncoder();
278
+ const buffer = decoder.encode(content);
279
+ const chunks = new Array();
280
+ for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
281
+ chunks.push(
282
+ String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE))
283
+ );
284
+ }
285
+ return window.btoa(chunks.join(""));
286
+ }
287
+ }
288
+ function DryRunProvider(props) {
289
+ const scaffolderApi = useApi(scaffolderApiRef);
290
+ const [state, setState] = useState({
291
+ results: [],
292
+ selectedResult: void 0
293
+ });
294
+ const idRef = useRef(1);
295
+ const selectResult = useCallback((id) => {
296
+ setState((prevState) => {
297
+ const result = prevState.results.find((r) => r.id === id);
298
+ if (result === prevState.selectedResult) {
299
+ return prevState;
300
+ }
301
+ return {
302
+ results: prevState.results,
303
+ selectedResult: result
304
+ };
305
+ });
306
+ }, []);
307
+ const deleteResult = useCallback((id) => {
308
+ setState((prevState) => {
309
+ var _a;
310
+ const index = prevState.results.findIndex((r) => r.id === id);
311
+ if (index === -1) {
312
+ return prevState;
313
+ }
314
+ const newResults = prevState.results.slice();
315
+ const [deleted] = newResults.splice(index, 1);
316
+ return {
317
+ results: newResults,
318
+ selectedResult: ((_a = prevState.selectedResult) == null ? void 0 : _a.id) === deleted.id ? newResults[0] : prevState.selectedResult
319
+ };
320
+ });
321
+ }, []);
322
+ const execute = useCallback(
323
+ async (options) => {
324
+ if (!scaffolderApi.dryRun) {
325
+ throw new Error("Scaffolder API does not support dry-run");
326
+ }
327
+ const parsed = yaml.parse(options.templateContent);
328
+ const response = await scaffolderApi.dryRun({
329
+ template: parsed,
330
+ values: options.values,
331
+ secrets: {},
332
+ directoryContents: options.files.map((file) => ({
333
+ path: file.path,
334
+ base64Content: base64EncodeContent(file.content)
335
+ }))
336
+ });
337
+ const result = {
338
+ ...response,
339
+ id: idRef.current++
340
+ };
341
+ setState((prevState) => {
342
+ var _a;
343
+ return {
344
+ results: [...prevState.results, result],
345
+ selectedResult: (_a = prevState.selectedResult) != null ? _a : result
346
+ };
347
+ });
348
+ },
349
+ [scaffolderApi]
350
+ );
351
+ const dryRun = useMemo(
352
+ () => ({
353
+ ...state,
354
+ selectResult,
355
+ deleteResult,
356
+ execute
357
+ }),
358
+ [state, selectResult, deleteResult, execute]
359
+ );
360
+ return /* @__PURE__ */ React.createElement(DryRunContext.Provider, { value: dryRun }, props.children);
361
+ }
362
+ function useDryRun() {
363
+ const value = useContext(DryRunContext);
364
+ if (!value) {
365
+ throw new Error("must be used within a DryRunProvider");
366
+ }
367
+ return value;
368
+ }
369
+
370
+ var __accessCheck = (obj, member, msg) => {
371
+ if (!member.has(obj))
372
+ throw TypeError("Cannot " + msg);
373
+ };
374
+ var __privateGet = (obj, member, getter) => {
375
+ __accessCheck(obj, member, "read from private field");
376
+ return getter ? getter.call(obj) : member.get(obj);
377
+ };
378
+ var __privateAdd = (obj, member, value) => {
379
+ if (member.has(obj))
380
+ throw TypeError("Cannot add the same private member more than once");
381
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
382
+ };
383
+ var __privateSet = (obj, member, value, setter) => {
384
+ __accessCheck(obj, member, "write to private field");
385
+ setter ? setter.call(obj, value) : member.set(obj, value);
386
+ return value;
387
+ };
388
+ var _access, _signalUpdate, _content, _savedContent, _access2, _listeners, _files, _selectedFile, _signalUpdate2;
389
+ const MAX_SIZE = 1024 * 1024;
390
+ const MAX_SIZE_MESSAGE = "This file is too large to be displayed";
391
+ class DirectoryEditorFileManager {
392
+ constructor(access, signalUpdate) {
393
+ __privateAdd(this, _access, void 0);
394
+ __privateAdd(this, _signalUpdate, void 0);
395
+ __privateAdd(this, _content, void 0);
396
+ __privateAdd(this, _savedContent, void 0);
397
+ __privateSet(this, _access, access);
398
+ __privateSet(this, _signalUpdate, signalUpdate);
399
+ }
400
+ get path() {
401
+ return __privateGet(this, _access).path;
402
+ }
403
+ get content() {
404
+ var _a;
405
+ return (_a = __privateGet(this, _content)) != null ? _a : MAX_SIZE_MESSAGE;
406
+ }
407
+ updateContent(content) {
408
+ if (__privateGet(this, _content) === void 0) {
409
+ return;
410
+ }
411
+ __privateSet(this, _content, content);
412
+ __privateGet(this, _signalUpdate).call(this);
413
+ }
414
+ get dirty() {
415
+ return __privateGet(this, _content) !== __privateGet(this, _savedContent);
416
+ }
417
+ async save() {
418
+ if (__privateGet(this, _content) !== void 0) {
419
+ await __privateGet(this, _access).save(__privateGet(this, _content));
420
+ __privateSet(this, _savedContent, __privateGet(this, _content));
421
+ __privateGet(this, _signalUpdate).call(this);
422
+ }
423
+ }
424
+ async reload() {
425
+ const file = await __privateGet(this, _access).file();
426
+ if (file.size > MAX_SIZE) {
427
+ if (__privateGet(this, _content) !== void 0) {
428
+ __privateSet(this, _content, void 0);
429
+ __privateSet(this, _savedContent, void 0);
430
+ __privateGet(this, _signalUpdate).call(this);
431
+ }
432
+ return;
433
+ }
434
+ const content = await file.text();
435
+ if (__privateGet(this, _content) !== content) {
436
+ __privateSet(this, _content, content);
437
+ __privateSet(this, _savedContent, content);
438
+ __privateGet(this, _signalUpdate).call(this);
439
+ }
440
+ }
441
+ }
442
+ _access = new WeakMap();
443
+ _signalUpdate = new WeakMap();
444
+ _content = new WeakMap();
445
+ _savedContent = new WeakMap();
446
+ class DirectoryEditorManager {
447
+ constructor(access) {
448
+ __privateAdd(this, _access2, void 0);
449
+ __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
450
+ __privateAdd(this, _files, []);
451
+ __privateAdd(this, _selectedFile, void 0);
452
+ this.setSelectedFile = (path) => {
453
+ const prev = __privateGet(this, _selectedFile);
454
+ const next = __privateGet(this, _files).find((file) => file.path === path);
455
+ if (prev !== next) {
456
+ __privateSet(this, _selectedFile, next);
457
+ __privateGet(this, _signalUpdate2).call(this);
458
+ }
459
+ };
460
+ __privateAdd(this, _signalUpdate2, () => {
461
+ __privateGet(this, _listeners).forEach((listener) => listener());
462
+ });
463
+ __privateSet(this, _access2, access);
464
+ }
465
+ get files() {
466
+ return __privateGet(this, _files);
467
+ }
468
+ get selectedFile() {
469
+ return __privateGet(this, _selectedFile);
470
+ }
471
+ get dirty() {
472
+ return __privateGet(this, _files).some((file) => file.dirty);
473
+ }
474
+ async save() {
475
+ await Promise.all(__privateGet(this, _files).map((file) => file.save()));
476
+ }
477
+ async reload() {
478
+ var _a;
479
+ const selectedPath = (_a = __privateGet(this, _selectedFile)) == null ? void 0 : _a.path;
480
+ const files = await __privateGet(this, _access2).listFiles();
481
+ const fileManagers = await Promise.all(
482
+ files.map(async (file) => {
483
+ const manager = new DirectoryEditorFileManager(
484
+ file,
485
+ __privateGet(this, _signalUpdate2)
486
+ );
487
+ await manager.reload();
488
+ return manager;
489
+ })
490
+ );
491
+ __privateGet(this, _files).length = 0;
492
+ __privateGet(this, _files).push(...fileManagers);
493
+ this.setSelectedFile(selectedPath);
494
+ __privateGet(this, _signalUpdate2).call(this);
495
+ }
496
+ subscribe(listener) {
497
+ __privateGet(this, _listeners).add(listener);
498
+ return () => {
499
+ __privateGet(this, _listeners).delete(listener);
500
+ };
501
+ }
502
+ }
503
+ _access2 = new WeakMap();
504
+ _listeners = new WeakMap();
505
+ _files = new WeakMap();
506
+ _selectedFile = new WeakMap();
507
+ _signalUpdate2 = new WeakMap();
508
+ const DirectoryEditorContext = createContext(
509
+ void 0
510
+ );
511
+ function useDirectoryEditor() {
512
+ const value = useContext(DirectoryEditorContext);
513
+ const rerender = useRerender();
514
+ useEffect(() => value == null ? void 0 : value.subscribe(rerender), [value, rerender]);
515
+ if (!value) {
516
+ throw new Error("must be used within a DirectoryEditorProvider");
517
+ }
518
+ return value;
519
+ }
520
+ function DirectoryEditorProvider(props) {
521
+ const { directory } = props;
522
+ const [{ result, error }, { execute }] = useAsync$1(
523
+ async (dir) => {
524
+ const manager = new DirectoryEditorManager(dir);
525
+ await manager.reload();
526
+ const firstYaml = manager.files.find((file) => file.path.match(/\.ya?ml$/));
527
+ if (firstYaml) {
528
+ manager.setSelectedFile(firstYaml.path);
529
+ }
530
+ return manager;
531
+ }
532
+ );
533
+ useEffect(() => {
534
+ execute(directory);
535
+ }, [execute, directory]);
536
+ if (error) {
537
+ return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
538
+ } else if (!result) {
539
+ return /* @__PURE__ */ React.createElement(Progress, null);
540
+ }
541
+ return /* @__PURE__ */ React.createElement(DirectoryEditorContext.Provider, { value: result }, props.children);
542
+ }
543
+
544
+ const useStyles$7 = makeStyles$1((theme) => ({
545
+ root: {
546
+ overflowY: "auto",
547
+ background: theme.palette.background.default
548
+ },
549
+ iconSuccess: {
550
+ minWidth: 0,
551
+ marginRight: theme.spacing(1),
552
+ color: theme.palette.status.ok
553
+ },
554
+ iconFailure: {
555
+ minWidth: 0,
556
+ marginRight: theme.spacing(1),
557
+ color: theme.palette.status.error
558
+ }
559
+ }));
560
+ function DryRunResultsList() {
561
+ const classes = useStyles$7();
562
+ const dryRun = useDryRun();
563
+ return /* @__PURE__ */ React.createElement(List$1, { className: classes.root, dense: true }, dryRun.results.map((result) => {
564
+ var _a;
565
+ const failed = result.log.some((l) => l.body.status === "failed");
566
+ return /* @__PURE__ */ React.createElement(
567
+ ListItem,
568
+ {
569
+ button: true,
570
+ key: result.id,
571
+ selected: ((_a = dryRun.selectedResult) == null ? void 0 : _a.id) === result.id,
572
+ onClick: () => dryRun.selectResult(result.id)
573
+ },
574
+ /* @__PURE__ */ React.createElement(
575
+ ListItemIcon$1,
576
+ {
577
+ className: failed ? classes.iconFailure : classes.iconSuccess
578
+ },
579
+ failed ? /* @__PURE__ */ React.createElement(Cancel, null) : /* @__PURE__ */ React.createElement(Check, null)
580
+ ),
581
+ /* @__PURE__ */ React.createElement(ListItemText$1, { primary: `Result ${result.id}` }),
582
+ /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
583
+ IconButton,
584
+ {
585
+ edge: "end",
586
+ "aria-label": "delete",
587
+ onClick: () => dryRun.deleteResult(result.id)
588
+ },
589
+ /* @__PURE__ */ React.createElement(DeleteIcon, null)
590
+ ))
591
+ );
592
+ }));
593
+ }
594
+
595
+ const useStyles$6 = makeStyles$1({
596
+ root: {
597
+ whiteSpace: "nowrap",
598
+ overflowY: "auto"
599
+ }
600
+ });
601
+ function parseFileEntires(paths) {
602
+ const root = {
603
+ type: "directory",
604
+ name: "",
605
+ path: "",
606
+ children: []
607
+ };
608
+ for (const path of paths.slice().sort()) {
609
+ const parts = path.split("/");
610
+ let current = root;
611
+ for (let i = 0; i < parts.length; i++) {
612
+ const part = parts[i];
613
+ if (part === "") {
614
+ throw new Error(`Invalid path part: ''`);
615
+ }
616
+ const entryPath = parts.slice(0, i + 1).join("/");
617
+ const existing = current.children.find((child) => child.name === part);
618
+ if ((existing == null ? void 0 : existing.type) === "file") {
619
+ throw new Error(`Duplicate filename at '${entryPath}'`);
620
+ } else if (existing) {
621
+ current = existing;
622
+ } else {
623
+ if (i < parts.length - 1) {
624
+ const newEntry = {
625
+ type: "directory",
626
+ name: part,
627
+ path: entryPath,
628
+ children: []
629
+ };
630
+ const firstFileIndex = current.children.findIndex(
631
+ (child) => child.type === "file"
632
+ );
633
+ current.children.splice(firstFileIndex, 0, newEntry);
634
+ current = newEntry;
635
+ } else {
636
+ current.children.push({
637
+ type: "file",
638
+ name: part,
639
+ path: entryPath
640
+ });
641
+ }
642
+ }
643
+ }
644
+ }
645
+ return root.children;
646
+ }
647
+ function FileTreeItem({ entry }) {
648
+ if (entry.type === "file") {
649
+ return /* @__PURE__ */ React.createElement(TreeItem, { nodeId: entry.path, label: entry.name });
650
+ }
651
+ return /* @__PURE__ */ React.createElement(TreeItem, { nodeId: entry.path, label: entry.name }, entry.children.map((child) => /* @__PURE__ */ React.createElement(FileTreeItem, { key: child.path, entry: child })));
652
+ }
653
+ function FileBrowser(props) {
654
+ const classes = useStyles$6();
655
+ const fileTree = useMemo(
656
+ () => parseFileEntires(props.filePaths),
657
+ [props.filePaths]
658
+ );
659
+ return /* @__PURE__ */ React.createElement(
660
+ TreeView,
661
+ {
662
+ selected: props.selected,
663
+ className: classes.root,
664
+ defaultCollapseIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
665
+ defaultExpandIcon: /* @__PURE__ */ React.createElement(ChevronRightIcon, null),
666
+ onNodeSelect: (_e, nodeId) => {
667
+ if (props.onSelect && props.filePaths.includes(nodeId)) {
668
+ props.onSelect(nodeId);
669
+ }
670
+ }
671
+ },
672
+ fileTree.map((entry) => /* @__PURE__ */ React.createElement(FileTreeItem, { key: entry.path, entry }))
673
+ );
674
+ }
675
+
676
+ const useStyles$5 = makeStyles$1((theme) => ({
677
+ root: {
678
+ display: "grid",
679
+ gridTemplateColumns: "280px auto 3fr",
680
+ gridTemplateRows: "1fr"
681
+ },
682
+ child: {
683
+ overflowY: "auto",
684
+ height: "100%",
685
+ minHeight: 0
686
+ },
687
+ firstChild: {
688
+ background: theme.palette.background.paper
689
+ }
690
+ }));
691
+ function DryRunResultsSplitView(props) {
692
+ const classes = useStyles$5();
693
+ const childArray = Children.toArray(props.children);
694
+ if (childArray.length !== 2) {
695
+ throw new Error("must have exactly 2 children");
696
+ }
697
+ 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]));
698
+ }
699
+
700
+ const useStyles$4 = makeStyles$1({
701
+ root: {
702
+ display: "flex",
703
+ flexFlow: "column nowrap"
704
+ },
705
+ contentWrapper: {
706
+ flex: 1,
707
+ position: "relative"
708
+ },
709
+ content: {
710
+ position: "absolute",
711
+ top: 0,
712
+ left: 0,
713
+ right: 0,
714
+ bottom: 0,
715
+ display: "flex",
716
+ "& > *": {
717
+ flex: 1
718
+ }
719
+ },
720
+ codeMirror: {
721
+ height: "100%",
722
+ overflowY: "auto"
723
+ }
724
+ });
725
+ function FilesContent() {
726
+ const classes = useStyles$4();
727
+ const { selectedResult } = useDryRun();
728
+ const [selectedPath, setSelectedPath] = useState("");
729
+ const selectedFile = selectedResult == null ? void 0 : selectedResult.directoryContents.find(
730
+ (f) => f.path === selectedPath
731
+ );
732
+ useEffect(() => {
733
+ if (selectedResult) {
734
+ const [firstFile] = selectedResult.directoryContents;
735
+ if (firstFile) {
736
+ setSelectedPath(firstFile.path);
737
+ } else {
738
+ setSelectedPath("");
739
+ }
740
+ }
741
+ return void 0;
742
+ }, [selectedResult]);
743
+ if (!selectedResult) {
744
+ return null;
745
+ }
746
+ return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(
747
+ FileBrowser,
748
+ {
749
+ selected: selectedPath,
750
+ onSelect: setSelectedPath,
751
+ filePaths: selectedResult.directoryContents.map((file) => file.path)
752
+ }
753
+ ), /* @__PURE__ */ React.createElement(
754
+ CodeMirror,
755
+ {
756
+ className: classes.codeMirror,
757
+ theme: "dark",
758
+ height: "100%",
759
+ extensions: [StreamLanguage.define(yaml$1)],
760
+ readOnly: true,
761
+ value: (selectedFile == null ? void 0 : selectedFile.base64Content) ? atob(selectedFile.base64Content) : ""
762
+ }
763
+ ));
764
+ }
765
+ function LogContent() {
766
+ var _a, _b;
767
+ const { selectedResult } = useDryRun();
768
+ const [currentStepId, setUserSelectedStepId] = useState();
769
+ const steps = useMemo(() => {
770
+ var _a2;
771
+ if (!selectedResult) {
772
+ return [];
773
+ }
774
+ return (_a2 = selectedResult.steps.map((step) => {
775
+ var _a3, _b2;
776
+ const stepLog = selectedResult.log.filter(
777
+ (l) => l.body.stepId === step.id
778
+ );
779
+ return {
780
+ id: step.id,
781
+ name: step.name,
782
+ logString: stepLog.map((l) => l.body.message).join("\n"),
783
+ status: (_b2 = (_a3 = stepLog[stepLog.length - 1]) == null ? void 0 : _a3.body.status) != null ? _b2 : "completed"
784
+ };
785
+ })) != null ? _a2 : [];
786
+ }, [selectedResult]);
787
+ if (!selectedResult) {
788
+ return null;
789
+ }
790
+ const selectedStep = (_a = steps.find((s) => s.id === currentStepId)) != null ? _a : steps[0];
791
+ return /* @__PURE__ */ React.createElement(DryRunResultsSplitView, null, /* @__PURE__ */ React.createElement(
792
+ TaskStatusStepper,
793
+ {
794
+ steps,
795
+ currentStepId: selectedStep.id,
796
+ onUserStepChange: setUserSelectedStepId
797
+ }
798
+ ), /* @__PURE__ */ React.createElement(LogViewer, { text: (_b = selectedStep == null ? void 0 : selectedStep.logString) != null ? _b : "" }));
799
+ }
800
+ function OutputContent() {
801
+ var _a, _b;
802
+ const classes = useStyles$4();
803
+ const { selectedResult } = useDryRun();
804
+ if (!selectedResult) {
805
+ return null;
806
+ }
807
+ 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(
808
+ CodeMirror,
809
+ {
810
+ className: classes.codeMirror,
811
+ theme: "dark",
812
+ height: "100%",
813
+ extensions: [StreamLanguage.define(yaml$1)],
814
+ readOnly: true,
815
+ value: JSON.stringify(selectedResult.output, null, 2)
816
+ }
817
+ ));
818
+ }
819
+ function DryRunResultsView() {
820
+ const classes = useStyles$4();
821
+ const [selectedTab, setSelectedTab] = useState(
822
+ "files"
823
+ );
824
+ 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))));
825
+ }
826
+
827
+ const useStyles$3 = makeStyles$1((theme) => ({
828
+ header: {
829
+ height: 48,
830
+ minHeight: 0,
831
+ "&.Mui-expanded": {
832
+ height: 48,
833
+ minHeight: 0
834
+ }
835
+ },
836
+ content: {
837
+ display: "grid",
838
+ background: theme.palette.background.default,
839
+ gridTemplateColumns: "180px auto 1fr",
840
+ gridTemplateRows: "1fr",
841
+ padding: 0,
842
+ height: 400
843
+ }
844
+ }));
845
+ function DryRunResults() {
846
+ const classes = useStyles$3();
847
+ const dryRun = useDryRun();
848
+ const [expanded, setExpanded] = useState(false);
849
+ const [hidden, setHidden] = useState(true);
850
+ const resultsLength = dryRun.results.length;
851
+ const prevResultsLength = usePrevious(resultsLength);
852
+ useEffect(() => {
853
+ if (prevResultsLength === 0 && resultsLength === 1) {
854
+ setHidden(false);
855
+ setExpanded(true);
856
+ } else if (prevResultsLength === 1 && resultsLength === 0) {
857
+ setExpanded(false);
858
+ }
859
+ }, [prevResultsLength, resultsLength]);
860
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
861
+ Accordion$1,
862
+ {
863
+ variant: "outlined",
864
+ expanded,
865
+ hidden: resultsLength === 0 && hidden,
866
+ onChange: (_, exp) => setExpanded(exp),
867
+ onTransitionEnd: () => resultsLength === 0 && setHidden(true)
868
+ },
869
+ /* @__PURE__ */ React.createElement(
870
+ AccordionSummary$1,
871
+ {
872
+ className: classes.header,
873
+ expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon$1, null)
874
+ },
875
+ /* @__PURE__ */ React.createElement(Typography$1, null, "Dry-run results")
876
+ ),
877
+ /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }),
878
+ /* @__PURE__ */ React.createElement(AccordionDetails$1, { className: classes.content }, /* @__PURE__ */ React.createElement(DryRunResultsList, null), /* @__PURE__ */ React.createElement(Divider, { orientation: "horizontal" }), /* @__PURE__ */ React.createElement(DryRunResultsView, null))
879
+ ));
880
+ }
881
+
882
+ const useStyles$2 = makeStyles((theme) => ({
883
+ button: {
884
+ padding: theme.spacing(1)
885
+ },
886
+ buttons: {
887
+ display: "flex",
888
+ flexFlow: "row nowrap",
889
+ alignItems: "center",
890
+ justifyContent: "flex-start"
891
+ },
892
+ buttonsGap: {
893
+ flex: "1 1 auto"
894
+ },
895
+ buttonsDivider: {
896
+ marginBottom: theme.spacing(1)
897
+ }
898
+ }));
899
+ function TemplateEditorBrowser(props) {
900
+ var _a, _b;
901
+ const classes = useStyles$2();
902
+ const directoryEditor = useDirectoryEditor();
903
+ const changedFiles = directoryEditor.files.filter((file) => file.dirty);
904
+ const handleClose = () => {
905
+ if (!props.onClose) {
906
+ return;
907
+ }
908
+ if (changedFiles.length > 0) {
909
+ const accepted = window.confirm(
910
+ "Are you sure? Unsaved changes will be lost"
911
+ );
912
+ if (!accepted) {
913
+ return;
914
+ }
915
+ }
916
+ props.onClose();
917
+ };
918
+ 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(
919
+ IconButton$1,
920
+ {
921
+ className: classes.button,
922
+ disabled: directoryEditor.files.every((file) => !file.dirty),
923
+ onClick: () => directoryEditor.save()
924
+ },
925
+ /* @__PURE__ */ React.createElement(SaveIcon, null)
926
+ )), /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload directory" }, /* @__PURE__ */ React.createElement(
927
+ IconButton$1,
928
+ {
929
+ className: classes.button,
930
+ onClick: () => directoryEditor.reload()
931
+ },
932
+ /* @__PURE__ */ React.createElement(RefreshIcon, null)
933
+ )), /* @__PURE__ */ React.createElement("div", { className: classes.buttonsGap }), /* @__PURE__ */ React.createElement(Tooltip, { title: "Close directory" }, /* @__PURE__ */ React.createElement(IconButton$1, { className: classes.button, onClick: handleClose }, /* @__PURE__ */ React.createElement(CloseIcon, null)))), /* @__PURE__ */ React.createElement(Divider$1, { className: classes.buttonsDivider }), /* @__PURE__ */ React.createElement(
934
+ FileBrowser,
935
+ {
936
+ selected: (_b = (_a = directoryEditor.selectedFile) == null ? void 0 : _a.path) != null ? _b : "",
937
+ onSelect: directoryEditor.setSelectedFile,
938
+ filePaths: directoryEditor.files.map((file) => file.path)
939
+ }
940
+ ));
941
+ }
942
+
943
+ const useStyles$1 = makeStyles((theme) => ({
944
+ container: {
945
+ position: "relative",
946
+ width: "100%",
947
+ height: "100%"
948
+ },
949
+ codeMirror: {
950
+ position: "absolute",
951
+ top: 0,
952
+ bottom: 0,
953
+ left: 0,
954
+ right: 0
955
+ },
956
+ errorPanel: {
957
+ color: theme.palette.error.main,
958
+ lineHeight: 2,
959
+ margin: theme.spacing(0, 1)
960
+ },
961
+ floatingButtons: {
962
+ position: "absolute",
963
+ top: theme.spacing(1),
964
+ right: theme.spacing(3)
965
+ },
966
+ floatingButton: {
967
+ padding: theme.spacing(1)
968
+ }
969
+ }));
970
+ function TemplateEditorTextArea(props) {
971
+ const { errorText } = props;
972
+ const classes = useStyles$1();
973
+ const panelExtension = useMemo(() => {
974
+ if (!errorText) {
975
+ return showPanel.of(null);
976
+ }
977
+ const dom = document.createElement("div");
978
+ dom.classList.add(classes.errorPanel);
979
+ dom.textContent = errorText;
980
+ return showPanel.of(() => ({ dom, bottom: true }));
981
+ }, [classes, errorText]);
982
+ useKeyboardEvent(
983
+ (e) => e.key === "s" && (e.ctrlKey || e.metaKey),
984
+ (e) => {
985
+ e.preventDefault();
986
+ if (props.onSave) {
987
+ props.onSave();
988
+ }
989
+ }
990
+ );
991
+ return /* @__PURE__ */ React.createElement("div", { className: classes.container }, /* @__PURE__ */ React.createElement(
992
+ CodeMirror,
993
+ {
994
+ className: classes.codeMirror,
995
+ theme: "dark",
996
+ height: "100%",
997
+ extensions: [StreamLanguage.define(yaml$1), panelExtension],
998
+ value: props.content,
999
+ onChange: props.onUpdate
1000
+ }
1001
+ ), (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(
1002
+ IconButton$1,
1003
+ {
1004
+ className: classes.floatingButton,
1005
+ onClick: () => {
1006
+ var _a;
1007
+ return (_a = props.onSave) == null ? void 0 : _a.call(props);
1008
+ }
1009
+ },
1010
+ /* @__PURE__ */ React.createElement(SaveIcon, null)
1011
+ )), props.onReload && /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload file" }, /* @__PURE__ */ React.createElement(
1012
+ IconButton$1,
1013
+ {
1014
+ className: classes.floatingButton,
1015
+ onClick: () => {
1016
+ var _a;
1017
+ return (_a = props.onReload) == null ? void 0 : _a.call(props);
1018
+ }
1019
+ },
1020
+ /* @__PURE__ */ React.createElement(RefreshIcon, null)
1021
+ )))));
1022
+ }
1023
+ function TemplateEditorDirectoryEditorTextArea(props) {
1024
+ var _a, _b;
1025
+ const directoryEditor = useDirectoryEditor();
1026
+ const actions = ((_a = directoryEditor.selectedFile) == null ? void 0 : _a.dirty) ? {
1027
+ onSave: () => directoryEditor.save(),
1028
+ onReload: () => directoryEditor.reload()
1029
+ } : {
1030
+ onReload: () => directoryEditor.reload()
1031
+ };
1032
+ return /* @__PURE__ */ React.createElement(
1033
+ TemplateEditorTextArea,
1034
+ {
1035
+ errorText: props.errorText,
1036
+ content: (_b = directoryEditor.selectedFile) == null ? void 0 : _b.content,
1037
+ onUpdate: (content) => {
1038
+ var _a2;
1039
+ return (_a2 = directoryEditor.selectedFile) == null ? void 0 : _a2.updateContent(content);
1040
+ },
1041
+ ...actions
1042
+ }
1043
+ );
1044
+ }
1045
+ TemplateEditorTextArea.DirectoryEditor = TemplateEditorDirectoryEditorTextArea;
1046
+
1047
+ const useStyles = makeStyles$1((theme) => ({
1048
+ introText: {
1049
+ textAlign: "center",
1050
+ marginTop: theme.spacing(2)
1051
+ },
1052
+ card: {
1053
+ position: "relative",
1054
+ maxWidth: 340,
1055
+ marginTop: theme.spacing(4),
1056
+ margin: theme.spacing(0, 2)
1057
+ },
1058
+ infoIcon: {
1059
+ position: "absolute",
1060
+ top: theme.spacing(1),
1061
+ right: theme.spacing(1)
1062
+ }
1063
+ }));
1064
+ function TemplateEditorIntro(props) {
1065
+ const classes = useStyles();
1066
+ const supportsLoad = WebFileSystemAccess.isSupported();
1067
+ const cardLoadLocal = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(
1068
+ CardActionArea,
1069
+ {
1070
+ disabled: !supportsLoad,
1071
+ onClick: () => {
1072
+ var _a;
1073
+ return (_a = props.onSelect) == null ? void 0 : _a.call(props, "local");
1074
+ }
1075
+ },
1076
+ /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(
1077
+ Typography$1,
1078
+ {
1079
+ variant: "h5",
1080
+ gutterBottom: true,
1081
+ color: supportsLoad ? void 0 : "textSecondary",
1082
+ style: { display: "flex", flexFlow: "row nowrap" }
1083
+ },
1084
+ "Load Template Directory"
1085
+ ), /* @__PURE__ */ React.createElement(
1086
+ Typography$1,
1087
+ {
1088
+ variant: "body1",
1089
+ color: supportsLoad ? void 0 : "textSecondary"
1090
+ },
1091
+ "Load a local template directory, allowing you to both edit and try executing your own template."
1092
+ ))
1093
+ ), !supportsLoad && /* @__PURE__ */ React.createElement("div", { className: classes.infoIcon }, /* @__PURE__ */ React.createElement(
1094
+ Tooltip$1,
1095
+ {
1096
+ placement: "top",
1097
+ title: "Only supported in some Chromium-based browsers"
1098
+ },
1099
+ /* @__PURE__ */ React.createElement(InfoOutlinedIcon, null)
1100
+ )));
1101
+ const cardFormEditor = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
1102
+ var _a;
1103
+ return (_a = props.onSelect) == null ? void 0 : _a.call(props, "form");
1104
+ } }, /* @__PURE__ */ React.createElement(CardContent, 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."))));
1105
+ const cardFieldExplorer = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
1106
+ var _a;
1107
+ return (_a = props.onSelect) == null ? void 0 : _a.call(props, "field-explorer");
1108
+ } }, /* @__PURE__ */ React.createElement(CardContent, 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."))));
1109
+ 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(
1110
+ "div",
1111
+ {
1112
+ style: {
1113
+ display: "flex",
1114
+ flexFlow: "row wrap",
1115
+ alignItems: "flex-start",
1116
+ justifyContent: "center",
1117
+ alignContent: "flex-start"
1118
+ }
1119
+ },
1120
+ supportsLoad && cardLoadLocal,
1121
+ cardFormEditor,
1122
+ !supportsLoad && cardLoadLocal,
1123
+ cardFieldExplorer
1124
+ ));
1125
+ }
1126
+
1127
+ export { ActionsPage as A, DirectoryEditorProvider as D, OwnerListPicker as O, TemplateEditorBrowser as T, WebFileSystemAccess as W, useDirectoryEditor as a, DryRunProvider as b, TemplateEditorTextArea as c, DryRunResults as d, TemplateEditorIntro as e, useDryRun as u };
1128
+ //# sourceMappingURL=TemplateEditorIntro-644bad26.esm.js.map