@parallelworks/ui 0.1.0 → 0.3.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.
Files changed (146) hide show
  1. package/dist/assets/editor.worker-CuBRIwi-.js +14776 -0
  2. package/dist/assets/json.worker-8ytS02vz.js +22161 -0
  3. package/dist/assets/yaml.worker-DJC-VUcv.js +48367 -0
  4. package/dist/chunks/CodePreviewEditor-CSPRILMp.js +61 -0
  5. package/dist/chunks/Indicator-hBSIxOPK.js +39 -0
  6. package/dist/chunks/Loader-DQiZ75fa.js +25 -0
  7. package/dist/chunks/Markdown-C5Re2ezs.js +101 -0
  8. package/dist/chunks/MarkdownImpl-Bq4QhrtA.js +33 -0
  9. package/dist/chunks/Modal-sOUEwebV.js +121 -0
  10. package/dist/chunks/Monaco-CC4ak1JG.js +717 -0
  11. package/dist/chunks/Provider-DvVvScTQ.js +391 -0
  12. package/dist/chunks/StatusBadge-DXp4zkSd.js +46 -0
  13. package/dist/chunks/SwitchToggle-2uRw8ZeJ.js +725 -0
  14. package/dist/chunks/SwitchToggleSmall-FcsaYSTs.js +14 -0
  15. package/dist/chunks/Table-C5z6qWjM.js +91 -0
  16. package/dist/chunks/Tooltip-WgbiC_nd.js +71 -0
  17. package/dist/chunks/editor-D55tumuZ.js +372 -0
  18. package/dist/chunks/lib-DTgL8klR.js +191 -0
  19. package/dist/chunks/list-7PyDF1tz.js +465 -0
  20. package/dist/components/CollapsiblePanel.d.ts +27 -0
  21. package/dist/components/CopyToClipboard.d.ts +17 -0
  22. package/dist/components/Dropdown.d.ts +24 -0
  23. package/dist/components/Indicator.d.ts +4 -0
  24. package/dist/components/Listbox.d.ts +18 -0
  25. package/dist/components/Markdown.d.ts +7 -0
  26. package/dist/components/MarkdownImpl.d.ts +2 -0
  27. package/dist/components/MultiSelect.d.ts +19 -0
  28. package/dist/components/Overview.d.ts +12 -0
  29. package/dist/components/Provider.d.ts +438 -0
  30. package/dist/components/SectionHeader.d.ts +6 -0
  31. package/dist/components/SwitchToggle.d.ts +12 -0
  32. package/dist/components/SwitchToggleSmall.d.ts +6 -0
  33. package/dist/components/dropdownUtils.d.ts +18 -0
  34. package/dist/editor/Editor.d.ts +2 -0
  35. package/dist/editor/EditorField.d.ts +11 -0
  36. package/dist/editor/FormCodePanel.d.ts +8 -0
  37. package/dist/editor/Monaco.d.ts +31 -0
  38. package/dist/editor/index.d.ts +7 -0
  39. package/dist/editor/index.js +2 -0
  40. package/dist/editor/themes.d.ts +4 -0
  41. package/dist/editor/useCssIsDark.d.ts +6 -0
  42. package/dist/editor/workers.d.ts +6 -0
  43. package/dist/file-explorer/FileExplorer.d.ts +23 -0
  44. package/dist/file-explorer/FileExplorerContext.d.ts +17 -0
  45. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +6 -0
  46. package/dist/file-explorer/FilePreview/Inline.d.ts +13 -0
  47. package/dist/file-explorer/FilePreview/StreamedBadge.d.ts +1 -0
  48. package/dist/file-explorer/FilePreview/Viewer.d.ts +15 -0
  49. package/dist/file-explorer/FilePreview/index.d.ts +15 -0
  50. package/dist/file-explorer/FilePreview/parse.d.ts +24 -0
  51. package/dist/file-explorer/FilePreview/previewCache.d.ts +26 -0
  52. package/dist/file-explorer/FilePreview/previewType.d.ts +21 -0
  53. package/dist/file-explorer/FilePreview/readCappedBytes.d.ts +37 -0
  54. package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +16 -0
  55. package/dist/file-explorer/FilePreview/useObjectText.d.ts +23 -0
  56. package/dist/file-explorer/FilePreview/usePresignedUrl.d.ts +12 -0
  57. package/dist/file-explorer/FileRow.d.ts +35 -0
  58. package/dist/file-explorer/FileRowsWindow.d.ts +43 -0
  59. package/dist/file-explorer/FileTable.d.ts +26 -0
  60. package/dist/file-explorer/FileTableHeaders.d.ts +13 -0
  61. package/dist/file-explorer/LoadMoreRow.d.ts +15 -0
  62. package/dist/file-explorer/TreeView.d.ts +28 -0
  63. package/dist/file-explorer/helpers.d.ts +47 -0
  64. package/dist/file-explorer/index.d.ts +10 -0
  65. package/dist/file-explorer/index.js +5646 -0
  66. package/dist/file-explorer/lib/batch.d.ts +3 -0
  67. package/dist/file-explorer/lib/errors.d.ts +3 -0
  68. package/dist/file-explorer/lib/fileExplorer.d.ts +32 -0
  69. package/dist/file-explorer/lib/paging.d.ts +93 -0
  70. package/dist/file-explorer/lib/types.d.ts +92 -0
  71. package/dist/file-explorer/lib/utils.d.ts +37 -0
  72. package/dist/file-explorer/toasts.d.ts +9 -0
  73. package/dist/file-explorer/treeRows.d.ts +35 -0
  74. package/dist/file-explorer/useNearEnd.d.ts +7 -0
  75. package/dist/form/FieldWrapper.d.ts +10 -0
  76. package/dist/form/Fields/BooleanField.d.ts +12 -0
  77. package/dist/form/Fields/BucketReadOnlyField.d.ts +7 -0
  78. package/dist/form/Fields/CheckboxGroupField.d.ts +18 -0
  79. package/dist/form/Fields/ColorField.d.ts +8 -0
  80. package/dist/form/Fields/DropdownField.d.ts +24 -0
  81. package/dist/form/Fields/DurationField.d.ts +13 -0
  82. package/dist/form/Fields/LabelField.d.ts +9 -0
  83. package/dist/form/Fields/MultiDropdownField.d.ts +18 -0
  84. package/dist/form/Fields/NumberField.d.ts +13 -0
  85. package/dist/form/Fields/ObjectField.d.ts +8 -0
  86. package/dist/form/Fields/RadioField.d.ts +9 -0
  87. package/dist/form/Fields/RangeField.d.ts +11 -0
  88. package/dist/form/Fields/ResourceOptionIcon.d.ts +11 -0
  89. package/dist/form/Fields/StepField.d.ts +3 -0
  90. package/dist/form/Fields/StringField.d.ts +17 -0
  91. package/dist/form/Fields/TextAreaField.d.ts +8 -0
  92. package/dist/form/Fields/durationValidity.d.ts +2 -0
  93. package/dist/form/Form.d.ts +105 -0
  94. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +14 -0
  95. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +53 -0
  96. package/dist/form/SpecialTypes/FormikCustomInput.d.ts +17 -0
  97. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.d.ts +13 -0
  98. package/dist/form/SpecialTypes/FormikNumericInput.d.ts +9 -0
  99. package/dist/form/SpecialTypes/index.d.ts +3 -0
  100. package/dist/form/Wizard/WizardContainer.d.ts +5 -0
  101. package/dist/form/Wizard/WizardNavigation.d.ts +2 -0
  102. package/dist/form/Wizard/WizardStepContent.d.ts +8 -0
  103. package/dist/form/Wizard/WizardStepIndicator.d.ts +2 -0
  104. package/dist/form/Wizard/types.d.ts +171 -0
  105. package/dist/form/Wizard/useWizardState.d.ts +28 -0
  106. package/dist/form/Wizard/utils.d.ts +2 -0
  107. package/dist/form/fieldRegistry.d.ts +7 -0
  108. package/dist/form/index.d.ts +16 -0
  109. package/dist/form/index.js +2319 -0
  110. package/dist/form/lib.d.ts +15 -0
  111. package/dist/form/types/fieldComponentTypes.d.ts +32 -0
  112. package/dist/form/types/fieldTypes.d.ts +80 -0
  113. package/dist/form/types/workflow-schema.d.ts +1107 -0
  114. package/dist/form/useParsedOpts.d.ts +5 -0
  115. package/dist/form/usePrevious.d.ts +1 -0
  116. package/dist/form/utils/checkShowIfObject.d.ts +1 -0
  117. package/dist/form/utils/getParentValue.d.ts +1 -0
  118. package/dist/form/utils/getPlaceholder.d.ts +2 -0
  119. package/dist/form/utils/getValueUsingPath.d.ts +1 -0
  120. package/dist/form/utils/pwResolver.d.ts +18 -0
  121. package/dist/form/utils/secondaryField.d.ts +8 -0
  122. package/dist/form/utils/slurmDuration.d.ts +15 -0
  123. package/dist/icons.d.ts +1 -1
  124. package/dist/icons.js +2 -2
  125. package/dist/index.d.ts +17 -0
  126. package/dist/index.js +42 -350
  127. package/dist/list/ListTable.d.ts +63 -0
  128. package/dist/list/RowContextMenu.d.ts +113 -0
  129. package/dist/list/index.d.ts +2 -0
  130. package/dist/list/index.js +2 -0
  131. package/dist/logviewer/LogViewer.d.ts +18 -0
  132. package/dist/logviewer/index.d.ts +1 -0
  133. package/dist/logviewer/index.js +534 -0
  134. package/dist/logviewer/logviewer.css +70 -0
  135. package/dist/styles/theme.css +30 -0
  136. package/dist/styles.css +1 -1
  137. package/dist/workflow/AnnotationBanner.d.ts +5 -0
  138. package/dist/workflow/Collapse.d.ts +6 -0
  139. package/dist/workflow/DependencyGraph.d.ts +15 -0
  140. package/dist/workflow/JobSummary.d.ts +38 -0
  141. package/dist/workflow/MatrixGroup.d.ts +41 -0
  142. package/dist/workflow/Reveal.d.ts +7 -0
  143. package/dist/workflow/TreeView.d.ts +42 -0
  144. package/dist/workflow/index.d.ts +1 -0
  145. package/dist/workflow/index.js +2331 -0
  146. package/package.json +71 -6
@@ -0,0 +1,717 @@
1
+ import { l as useWorkflowJsonResolver } from "./Provider-DvVvScTQ.js";
2
+ import { i as getThemeName, n as setupMonacoWorkers, r as defineEditorThemes, t as useCssIsDark } from "./editor-D55tumuZ.js";
3
+ import cx from "classnames";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { useEffect, useRef } from "react";
6
+ import { dumpYaml } from "@parallelworks/workflow-parser";
7
+ import yaml from "js-yaml";
8
+ import * as monaco from "monaco-editor";
9
+ import { configureMonacoYaml } from "monaco-yaml";
10
+ //#region src/editor/Monaco.tsx
11
+ setupMonacoWorkers();
12
+ var activeWorkflowJsonResolver;
13
+ var monacoWithWorkerBridge = {
14
+ ...monaco,
15
+ editor: {
16
+ ...monaco.editor,
17
+ createWebWorker: (opts) => "worker" in opts ? monaco.editor.createWebWorker(opts) : monaco.createWebWorker(opts)
18
+ }
19
+ };
20
+ var monacoYamlConfigured = false;
21
+ function ensureMonacoYamlConfigured() {
22
+ if (monacoYamlConfigured) return;
23
+ monacoYamlConfigured = true;
24
+ configureMonacoYaml(monacoWithWorkerBridge, {
25
+ enableSchemaRequest: true,
26
+ completion: true,
27
+ validate: true,
28
+ hover: true,
29
+ format: { enable: true },
30
+ schemas: [{
31
+ fileMatch: ["file:///workflow.yaml"],
32
+ uri: `${window.location.origin}/workflow.schema.json`
33
+ }]
34
+ });
35
+ }
36
+ var workflowCompletionContexts = /* @__PURE__ */ new Map();
37
+ var registeredCompletionProviders = [];
38
+ var workflowCompletionProvidersRegistered = false;
39
+ var noop = () => {};
40
+ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop, width = "100%", height = "100%", path = "file:///default", marketplaceItems = [], workflows = [], ...options }) {
41
+ const isDark = useCssIsDark();
42
+ const resolveWorkflowJson = useWorkflowJsonResolver();
43
+ const monacoRef = useRef(null);
44
+ const isSyncingValueRef = useRef(false);
45
+ useEffect(() => {
46
+ activeWorkflowJsonResolver = resolveWorkflowJson;
47
+ }, [resolveWorkflowJson]);
48
+ useEffect(() => {
49
+ if (!monacoRef.current) return;
50
+ defineEditorThemes(monaco.editor);
51
+ monaco.editor.setTheme(getThemeName(isDark, disabled));
52
+ monacoRef.current.updateOptions({ readOnly: disabled });
53
+ }, [disabled, isDark]);
54
+ useEffect(() => {
55
+ if (!monacoRef.current) return;
56
+ const model = monacoRef.current.getModel();
57
+ if (model && options.value !== void 0 && model.getValue() !== options.value) {
58
+ isSyncingValueRef.current = true;
59
+ model.setValue(options.value);
60
+ isSyncingValueRef.current = false;
61
+ }
62
+ }, [options.value]);
63
+ useEffect(() => {
64
+ return () => {
65
+ monacoRef.current?.dispose();
66
+ monacoRef.current = null;
67
+ };
68
+ }, []);
69
+ useEffect(() => {
70
+ const uri = monaco.Uri.parse(path).toString();
71
+ if (marketplaceItems.length > 0 || workflows.length > 0) workflowCompletionContexts.set(uri, {
72
+ marketplaceItems,
73
+ workflows
74
+ });
75
+ else workflowCompletionContexts.delete(uri);
76
+ return () => {
77
+ workflowCompletionContexts.delete(uri);
78
+ };
79
+ }, [
80
+ marketplaceItems,
81
+ workflows,
82
+ path
83
+ ]);
84
+ /** Strips CR (\r) — Windows line endings break bash scripts. */
85
+ const onChangeWrapper = (value) => {
86
+ value = value.replace(/\r/g, "");
87
+ onChange(value);
88
+ };
89
+ return /* @__PURE__ */ jsx("div", {
90
+ style: {
91
+ width,
92
+ height
93
+ },
94
+ className: cx("relative border rounded", className),
95
+ ref: (ref) => {
96
+ if (ref && !monacoRef.current) {
97
+ monaco.editor.getModel(monaco.Uri.parse(path))?.dispose();
98
+ defineEditorThemes(monaco.editor);
99
+ monaco.editor.setTheme(getThemeName(isDark, disabled));
100
+ const model = monaco.editor.createModel(options.value ?? "", options.language, monaco.Uri.parse(path));
101
+ const editor = monaco.editor.create(ref, {
102
+ automaticLayout: true,
103
+ model,
104
+ ...options,
105
+ readOnly: disabled || options.readOnly,
106
+ fixedOverflowWidgets: true,
107
+ suggest: {
108
+ preview: true,
109
+ showWords: false,
110
+ showInlineDetails: true
111
+ }
112
+ });
113
+ model.onDidChangeContent(() => {
114
+ if (isSyncingValueRef.current) return;
115
+ onChangeWrapper(editor.getValue());
116
+ });
117
+ model.onDidChangeDecorations(() => {
118
+ onValidate(monaco.editor.getModelMarkers({ resource: model.uri }));
119
+ });
120
+ ensureMonacoYamlConfigured();
121
+ monacoRef.current = editor;
122
+ if (workflowCompletionProvidersRegistered) return;
123
+ workflowCompletionProvidersRegistered = true;
124
+ registeredCompletionProviders.push(monaco.languages.registerCompletionItemProvider("yaml", {
125
+ triggerCharacters: [
126
+ "/",
127
+ " ",
128
+ "@"
129
+ ],
130
+ provideCompletionItems: async (model, position) => {
131
+ const context = workflowCompletionContexts.get(model.uri.toString());
132
+ if (!context) return { suggestions: [] };
133
+ const { marketplaceItems, workflows } = context;
134
+ const lineContent = model.getLineContent(position.lineNumber);
135
+ const textUntilPos = lineContent.substring(0, position.column - 1);
136
+ const defaultItem = {
137
+ kind: monaco.languages.CompletionItemKind.Value,
138
+ range: {
139
+ startLineNumber: position.lineNumber,
140
+ startColumn: position.column,
141
+ endLineNumber: position.lineNumber,
142
+ endColumn: position.column
143
+ }
144
+ };
145
+ let suggestions = [];
146
+ if (!/^\s*(?:-\s+)?uses\s*:\s*([^\n\r]*)$/.test(textUntilPos)) return { suggestions };
147
+ const marketIndex = textUntilPos.lastIndexOf("marketplace/");
148
+ const workflowIndex = textUntilPos.lastIndexOf("workflow/");
149
+ const pwIndex = textUntilPos.lastIndexOf("parallelworks/");
150
+ const githubIndex = textUntilPos.lastIndexOf("github/");
151
+ const usesText = textUntilPos.match(/^\s*(?:-\s+)?uses\s*:\s*([^\n\r]*)$/)?.[1]?.trim() ?? "";
152
+ if (marketIndex === -1 && workflowIndex === -1 && pwIndex === -1 && githubIndex === -1) {
153
+ if (usesText.trim().length > 0) return { suggestions };
154
+ return { suggestions: [
155
+ {
156
+ label: "marketplace",
157
+ sortText: "2",
158
+ insertText: "marketplace",
159
+ detail: "use a workflow from the marketplace",
160
+ ...defaultItem
161
+ },
162
+ {
163
+ label: "workflow",
164
+ sortText: "3",
165
+ insertText: "workflow",
166
+ detail: "use one of your workflows",
167
+ ...defaultItem
168
+ },
169
+ {
170
+ label: "github",
171
+ sortText: "4",
172
+ insertText: "github",
173
+ detail: "use a workflow from a public GitHub repository",
174
+ ...defaultItem
175
+ }
176
+ ] };
177
+ }
178
+ const noExtra = [
179
+ "marketplace/",
180
+ "workflow/",
181
+ "parallelworks/",
182
+ "github/"
183
+ ].includes(usesText);
184
+ if (workflowIndex !== -1 && noExtra) {
185
+ if (lineContent.substring(workflowIndex, position.column - 1).split("/").length === 2) suggestions = workflows.map((item) => {
186
+ const name = item.name ?? "";
187
+ return {
188
+ label: item.displayName && item.displayName !== name ? `${item.displayName} (${name})` : name,
189
+ insertText: name,
190
+ detail: item.description ? item.description : `${item.type ?? ""} workflow`,
191
+ ...defaultItem
192
+ };
193
+ });
194
+ } else if (marketIndex !== -1) {
195
+ const sections = lineContent.substring(marketIndex, position.column - 1).split("/");
196
+ if (sections.length === 2 && noExtra) suggestions = marketplaceItems.filter((item) => item.type === "workflow").map((item) => {
197
+ const slug = item.slug ?? "";
198
+ return {
199
+ label: item.name && item.name !== slug ? `${item.name} (${slug})` : slug,
200
+ insertText: slug,
201
+ detail: item.description ? item.description : `${item.subtype ?? ""} workflow`,
202
+ ...defaultItem
203
+ };
204
+ });
205
+ else if (sections.length === 3 && sections[2] === "") {
206
+ const mpItem = marketplaceItems.find((item) => item.slug === sections[1]);
207
+ if (mpItem && mpItem.versions) suggestions = [...mpItem.versions.map((v) => {
208
+ return {
209
+ label: v,
210
+ insertText: v,
211
+ ...defaultItem
212
+ };
213
+ }), {
214
+ label: "latest",
215
+ insertText: "latest",
216
+ ...defaultItem
217
+ }];
218
+ }
219
+ } else if (githubIndex !== -1) {
220
+ const afterGithub = usesText.replace("github/", "");
221
+ if (afterGithub === "") suggestions = [{
222
+ label: "Type a GitHub username or organization",
223
+ insertText: "",
224
+ detail: "e.g. github/myorg/",
225
+ ...defaultItem
226
+ }];
227
+ else if (!afterGithub.includes("/")) {} else {
228
+ const ghOwner = afterGithub.split("/")[0];
229
+ const rest = afterGithub.split("/").slice(1).join("/");
230
+ if (rest === "") try {
231
+ const resp = await fetch(`https://api.github.com/users/${ghOwner}/repos?per_page=100&sort=updated`);
232
+ if (resp.ok) suggestions = (await resp.json()).map((r) => ({
233
+ label: r.description ? `${r.name} — ${r.description}` : r.name,
234
+ insertText: r.name,
235
+ detail: r.description || "GitHub repository",
236
+ ...defaultItem
237
+ }));
238
+ } catch {}
239
+ else {
240
+ let ghRepo = "";
241
+ let isRefTrigger = false;
242
+ let replaceSlash = false;
243
+ const slashRefMatch = rest.match(/^([^@/]+)\/$/);
244
+ if (slashRefMatch) {
245
+ ghRepo = slashRefMatch[1];
246
+ isRefTrigger = true;
247
+ replaceSlash = true;
248
+ } else if (rest.includes("@")) {
249
+ const atRepo = rest.split("@")[0];
250
+ const refPart = rest.split("@").slice(1).join("@");
251
+ if (atRepo && refPart === "") {
252
+ ghRepo = atRepo;
253
+ isRefTrigger = true;
254
+ }
255
+ }
256
+ if (isRefTrigger && ghRepo) {
257
+ const slashToAtEdit = replaceSlash ? [{
258
+ range: {
259
+ startLineNumber: position.lineNumber,
260
+ startColumn: position.column - 1,
261
+ endLineNumber: position.lineNumber,
262
+ endColumn: position.column
263
+ },
264
+ text: "@"
265
+ }] : [];
266
+ try {
267
+ const [branchResp, tagResp, commitResp] = await Promise.all([
268
+ fetch(`https://api.github.com/repos/${ghOwner}/${ghRepo}/branches?per_page=100`),
269
+ fetch(`https://api.github.com/repos/${ghOwner}/${ghRepo}/tags?per_page=100`),
270
+ fetch(`https://api.github.com/repos/${ghOwner}/${ghRepo}/commits?per_page=10`)
271
+ ]);
272
+ if (branchResp.ok) {
273
+ const branches = await branchResp.json();
274
+ suggestions.push(...branches.map((b) => ({
275
+ label: b.name,
276
+ insertText: b.name,
277
+ detail: "branch",
278
+ additionalTextEdits: slashToAtEdit,
279
+ ...defaultItem
280
+ })));
281
+ }
282
+ if (tagResp.ok) {
283
+ const tags = await tagResp.json();
284
+ suggestions.push(...tags.map((t) => ({
285
+ label: t.name,
286
+ insertText: t.name,
287
+ detail: "tag",
288
+ additionalTextEdits: slashToAtEdit,
289
+ ...defaultItem
290
+ })));
291
+ }
292
+ if (commitResp.ok) {
293
+ const commits = await commitResp.json();
294
+ suggestions.push(...commits.map((c) => ({
295
+ label: `${c.sha.slice(0, 7)} — ${c.commit.message.split("\n")[0]}`,
296
+ insertText: c.sha,
297
+ detail: "commit",
298
+ additionalTextEdits: slashToAtEdit,
299
+ ...defaultItem
300
+ })));
301
+ }
302
+ } catch {}
303
+ }
304
+ }
305
+ }
306
+ }
307
+ return { suggestions };
308
+ }
309
+ }));
310
+ registeredCompletionProviders.push(monaco.languages.registerCompletionItemProvider("yaml", {
311
+ triggerCharacters: [" ", "\n"],
312
+ provideCompletionItems: async (model, position) => {
313
+ if (!workflowCompletionContexts.has(model.uri.toString())) return { suggestions: [] };
314
+ const lineNumber = position.lineNumber;
315
+ const lineContent = model.getLineContent(lineNumber);
316
+ const textUntilPos = lineContent.substring(0, position.column - 1);
317
+ if (/^\s*\$yaml\s*:\s*$/.test(textUntilPos)) {
318
+ for (let ln = lineNumber - 1; ln >= Math.max(lineNumber - 30, 1); ln--) {
319
+ const m = model.getLineContent(ln).trim().match(/^(?:-\s+)?uses\s*:\s*github\/([^/]+)\/([^@]+)@(.+)$/);
320
+ if (m) {
321
+ const [, owner, repo, ref] = m;
322
+ try {
323
+ const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/trees/${ref}?recursive=1`);
324
+ if (resp.ok) {
325
+ const data = await resp.json();
326
+ const defaultItem = {
327
+ kind: monaco.languages.CompletionItemKind.File,
328
+ range: {
329
+ startLineNumber: lineNumber,
330
+ startColumn: position.column,
331
+ endLineNumber: lineNumber,
332
+ endColumn: position.column
333
+ }
334
+ };
335
+ return { suggestions: (data.tree || []).filter((item) => item.type === "blob" && (item.path.endsWith(".yaml") || item.path.endsWith(".yml"))).map((f) => ({
336
+ label: f.path,
337
+ insertText: f.path,
338
+ detail: "yaml file",
339
+ ...defaultItem
340
+ })) };
341
+ }
342
+ } catch {}
343
+ break;
344
+ }
345
+ }
346
+ return { suggestions: [] };
347
+ }
348
+ if (/^\s*\$thumbnail\s*:\s*$/.test(textUntilPos)) {
349
+ for (let ln = lineNumber - 1; ln >= Math.max(lineNumber - 30, 1); ln--) {
350
+ const m = model.getLineContent(ln).trim().match(/^(?:-\s+)?uses\s*:\s*github\/([^/]+)\/([^@]+)@(.+)$/);
351
+ if (m) {
352
+ const [, owner, repo, ref] = m;
353
+ try {
354
+ const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/trees/${ref}?recursive=1`);
355
+ if (resp.ok) {
356
+ const data = await resp.json();
357
+ const defaultItem = {
358
+ kind: monaco.languages.CompletionItemKind.File,
359
+ range: {
360
+ startLineNumber: lineNumber,
361
+ startColumn: position.column,
362
+ endLineNumber: lineNumber,
363
+ endColumn: position.column
364
+ }
365
+ };
366
+ const imageExts = [
367
+ ".png",
368
+ ".jpg",
369
+ ".jpeg",
370
+ ".gif",
371
+ ".svg",
372
+ ".webp"
373
+ ];
374
+ return { suggestions: (data.tree || []).filter((item) => item.type === "blob" && imageExts.some((ext) => item.path.toLowerCase().endsWith(ext))).map((f) => ({
375
+ label: f.path,
376
+ insertText: f.path,
377
+ detail: "image file",
378
+ ...defaultItem
379
+ })) };
380
+ }
381
+ } catch {}
382
+ break;
383
+ }
384
+ }
385
+ return { suggestions: [] };
386
+ }
387
+ const isOnWithLine = /^\s*with\s*:\s*([^\n\r]*)$/.test(textUntilPos);
388
+ const isOnYamlLine = /^\s*\$(yaml|thumbnail)\s*:\s*\S/.test(textUntilPos);
389
+ let withLineNum = -1;
390
+ if (isOnWithLine) withLineNum = lineNumber;
391
+ else if (isOnYamlLine) for (let ln = lineNumber - 1; ln >= Math.max(lineNumber - 20, 1); ln--) {
392
+ const content = model.getLineContent(ln);
393
+ if (content.trim() === "") continue;
394
+ if (/^\s*with\s*:\s*([^\n\r]*)$/.test(content)) {
395
+ withLineNum = ln;
396
+ break;
397
+ }
398
+ }
399
+ else if (textUntilPos.trim() === "") for (let ln = lineNumber - 1; ln >= Math.max(lineNumber - 20, 1); ln--) {
400
+ const content = model.getLineContent(ln);
401
+ if (content.trim() === "") continue;
402
+ if (/^\s*with\s*:\s*([^\n\r]*)$/.test(content)) {
403
+ withLineNum = ln;
404
+ break;
405
+ }
406
+ }
407
+ if (withLineNum === -1) return { suggestions: [] };
408
+ if (!isOnWithLine && !isOnYamlLine && withLineNum !== -1) for (let ln = withLineNum + 1; ln < lineNumber; ln++) {
409
+ const content = model.getLineContent(ln).trim();
410
+ if (content === "") continue;
411
+ if (/^\$(yaml|thumbnail)\s*:/.test(content)) continue;
412
+ return { suggestions: [] };
413
+ }
414
+ const needsNewline = isOnWithLine || isOnYamlLine;
415
+ const needsIndent = isOnWithLine;
416
+ let usesLine = "";
417
+ let usesLineNum = -1;
418
+ for (let ln = lineNumber - 1; ln >= Math.max(lineNumber - 20, 1); ln--) {
419
+ const content = model.getLineContent(ln).trim();
420
+ if (/^(?:-\s+)?uses\s*:\s*(.+)$/.test(content)) {
421
+ usesLine = content;
422
+ usesLineNum = ln;
423
+ break;
424
+ }
425
+ }
426
+ const needsWithLine = usesLineNum !== -1 && withLineNum < usesLineNum;
427
+ let withIndent = "";
428
+ if (needsWithLine && usesLineNum !== -1) {
429
+ const indentMatch = model.getLineContent(usesLineNum).match(/^(\s*(?:-\s+)?)uses/);
430
+ if (indentMatch) withIndent = " ".repeat(indentMatch[1].length);
431
+ }
432
+ const defaultItem = {
433
+ kind: monaco.languages.CompletionItemKind.Value,
434
+ range: needsWithLine ? {
435
+ startLineNumber: lineNumber,
436
+ startColumn: 1,
437
+ endLineNumber: lineNumber,
438
+ endColumn: lineContent.length + 1
439
+ } : {
440
+ startLineNumber: lineNumber,
441
+ startColumn: position.column,
442
+ endLineNumber: lineNumber,
443
+ endColumn: position.column
444
+ }
445
+ };
446
+ if (usesLine === "") return { suggestions: [] };
447
+ const m = usesLine.match(/^(?:-\s+)?uses\s*:\s*(.+)$/);
448
+ if (!m) return { suggestions: [] };
449
+ const usesValue = m[1].trim();
450
+ if (!usesValue) return { suggestions: [] };
451
+ if (usesValue.startsWith("parallelworks/")) {
452
+ if (!needsWithLine && !isOnWithLine) return { suggestions: [] };
453
+ const actionDef = {
454
+ "parallelworks/checkout": {
455
+ label: "checkout",
456
+ required: {
457
+ repo: "https://github.com/parallelworks/interactive_session.git",
458
+ branch: "main"
459
+ }
460
+ },
461
+ "parallelworks/scheduler-agent": {
462
+ label: "scheduler-agent",
463
+ required: { "scheduler-type": "slurm" }
464
+ },
465
+ "parallelworks/wait-for-agent": {
466
+ label: "wait-for-agent",
467
+ required: { agentId: "${{ needs.JOB.steps.STEP.outputs.agentId }}" }
468
+ },
469
+ "parallelworks/cancel-jobs": {
470
+ label: "cancel-jobs",
471
+ required: {}
472
+ },
473
+ "parallelworks/update-session": {
474
+ label: "update-session",
475
+ required: { name: "session-name" }
476
+ }
477
+ }[usesValue];
478
+ if (!actionDef || Object.keys(actionDef.required).length === 0) return { suggestions: [] };
479
+ const withObj = { ...actionDef.required };
480
+ const withSuggestionYaml = prepareWithSuggestion(needsNewline, needsIndent, needsWithLine, withIndent, withObj);
481
+ return { suggestions: [{
482
+ label: actionDef.label + " inputs",
483
+ detail: "inputs for the " + actionDef.label + " action",
484
+ sortText: "1",
485
+ insertText: withSuggestionYaml,
486
+ ...defaultItem
487
+ }] };
488
+ }
489
+ if (!(usesValue.startsWith("marketplace/") || usesValue.startsWith("workflow/") || usesValue.startsWith("github/"))) return { suggestions: [] };
490
+ let inputs = {};
491
+ let usesWorkflowRef;
492
+ const isGithub = usesValue.startsWith("github/");
493
+ const hasYamlOverride = isGithub && (needsWithLine ? hasYamlNearUses(model, usesLineNum) : scanForYamlInWithBlock(model, withLineNum) !== void 0);
494
+ if (isGithub) {
495
+ const [ownerRepo, ref] = usesValue.replace("github/", "").split("@");
496
+ if (!ownerRepo || !ref) return { suggestions: [] };
497
+ const [owner, repo] = ownerRepo.split("/");
498
+ if (!owner || !repo) return { suggestions: [] };
499
+ usesWorkflowRef = [ownerRepo];
500
+ const yamlPath = (!needsWithLine ? scanForYamlInWithBlock(model, withLineNum) : void 0) || "workflow.yaml";
501
+ try {
502
+ const response = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${yamlPath}`);
503
+ if (response.ok) {
504
+ const text = await response.text();
505
+ inputs = yaml.load(text)?.on?.execute?.inputs || {};
506
+ }
507
+ } catch {}
508
+ } else {
509
+ usesWorkflowRef = usesValue.split("/").slice(1);
510
+ const resolveJson = activeWorkflowJsonResolver;
511
+ if (!resolveJson) return { suggestions: [] };
512
+ try {
513
+ const data = await resolveJson(usesValue.startsWith("marketplace/") ? {
514
+ kind: "marketplace",
515
+ slug: usesWorkflowRef[0],
516
+ version: usesWorkflowRef.length > 1 ? usesWorkflowRef[1] : "latest"
517
+ } : {
518
+ kind: "workflow",
519
+ name: usesWorkflowRef[0]
520
+ });
521
+ if (!data) return { suggestions: [] };
522
+ inputs = data?.on?.execute?.inputs || {};
523
+ } catch {
524
+ return { suggestions: [] };
525
+ }
526
+ }
527
+ if (Object.keys(inputs).length === 0) {
528
+ const noInputSuggestions = [{
529
+ label: "No inputs found",
530
+ detail: usesWorkflowRef[0] + " has no inputs.",
531
+ insertText: "",
532
+ ...defaultItem
533
+ }];
534
+ if (isGithub) {
535
+ if (!hasYamlOverride) noInputSuggestions.unshift({
536
+ label: "$yaml",
537
+ detail: "Specify the yaml path in the GitHub repo (default: workflow.yaml)",
538
+ sortText: "0",
539
+ insertText: needsWithLine ? `${withIndent}with:\n${withIndent} $yaml:` : needsIndent ? "\n $yaml:" : "$yaml:",
540
+ ...defaultItem
541
+ });
542
+ noInputSuggestions.unshift({
543
+ label: "$thumbnail",
544
+ detail: "Specify a thumbnail image path or URL for this subworkflow",
545
+ sortText: "0a",
546
+ insertText: needsWithLine ? `${withIndent}with:\n${withIndent} $thumbnail:` : needsIndent ? "\n $thumbnail:" : "$thumbnail:",
547
+ ...defaultItem
548
+ });
549
+ }
550
+ return { suggestions: noInputSuggestions };
551
+ }
552
+ let groupname = usesValue.replaceAll("/", "_").replaceAll(".", "-").replaceAll("@", "_");
553
+ const fullText = model.getValue();
554
+ let groupidx = 0;
555
+ try {
556
+ const basename = groupname;
557
+ while (true) if (yaml.load(fullText)?.on?.execute?.inputs?.[groupname]) {
558
+ groupidx++;
559
+ groupname = `${basename}_${groupidx}`;
560
+ } else break;
561
+ } catch {}
562
+ const withSuggestionYaml = prepareWithSuggestion(needsNewline, needsIndent, needsWithLine, withIndent, constructWithSuggestion(inputs, groupname));
563
+ const lines = fullText.split(/\r?\n/);
564
+ let inputsLineIndex = -1;
565
+ for (let i = 0; i < lines.length; i++) if (/^[ \t]*inputs\s*:\s*(#.*)?$/.test(lines[i])) {
566
+ const inputsIndentMatch = lines[i].match(/^([ \t]*)/);
567
+ const inputsIndent = inputsIndentMatch ? inputsIndentMatch[1].length : 0;
568
+ let executeIndex = -1;
569
+ for (let j = i - 1; j >= 0; j--) {
570
+ const m = lines[j].match(/^([ \t]*)([^\s:]+)\s*:\s*(#.*)?$/);
571
+ if (!m) continue;
572
+ const indent = m[1].length;
573
+ if (m[2] === "execute" && indent < inputsIndent) {
574
+ executeIndex = j;
575
+ break;
576
+ }
577
+ }
578
+ if (executeIndex === -1) continue;
579
+ const execIndentMatch = lines[executeIndex].match(/^([ \t]*)/);
580
+ const execIndent = execIndentMatch ? execIndentMatch[1].length : 0;
581
+ let onFound = false;
582
+ for (let k = executeIndex - 1; k >= 0; k--) {
583
+ const mOn = lines[k].match(/^([ \t]*)(?:'on'|on)\s*:\s*(#.*)?$/);
584
+ if (!mOn) continue;
585
+ if (mOn[1].length < execIndent) {
586
+ onFound = true;
587
+ break;
588
+ }
589
+ }
590
+ if (onFound) {
591
+ inputsLineIndex = i;
592
+ break;
593
+ }
594
+ }
595
+ const additionalTextEdits = [];
596
+ const yamlInputs = ` ${groupname}:\nlabel: ${usesWorkflowRef[0]} inputs${groupidx === 0 ? "" : ` ${groupidx}`}\ntype: group\nitems:\n `.replaceAll("\n", "\n ") + rewriteInputExpressionsForGroup(dumpYaml(inputs), groupname).trim().replaceAll("\n", "\n ") + "\n";
597
+ if (inputsLineIndex !== -1) {
598
+ const insertLineNumber = inputsLineIndex + 2;
599
+ additionalTextEdits.push({
600
+ range: {
601
+ startLineNumber: insertLineNumber,
602
+ startColumn: 1,
603
+ endLineNumber: insertLineNumber,
604
+ endColumn: 1
605
+ },
606
+ text: yamlInputs
607
+ });
608
+ } else {
609
+ if (position.lineNumber === lines.length) {
610
+ const editor = monaco.editor.getEditors().find((e) => e.getModel()?.uri.toString() === model.uri.toString());
611
+ editor?.executeEdits("ensure-trailing-newline", [{
612
+ range: new monaco.Range(lines.length, lineContent.length + 1, lines.length, lineContent.length + 1),
613
+ text: "\n",
614
+ forceMoveMarkers: true
615
+ }], () => {
616
+ const sel = editor?.getSelection();
617
+ return sel ? [sel] : null;
618
+ });
619
+ }
620
+ additionalTextEdits.push({
621
+ range: {
622
+ startLineNumber: lines.length + 1,
623
+ startColumn: 1,
624
+ endLineNumber: lines.length + 1,
625
+ endColumn: 1
626
+ },
627
+ text: "\non:\n execute:\n inputs:\n" + yamlInputs
628
+ });
629
+ }
630
+ const suggestions = [{
631
+ label: usesWorkflowRef[0] + " inputs",
632
+ detail: "all input expressions and dynamic form additions",
633
+ sortText: "1",
634
+ insertText: withSuggestionYaml,
635
+ additionalTextEdits,
636
+ ...defaultItem
637
+ }, {
638
+ label: usesWorkflowRef[0] + " inputs only",
639
+ detail: "all input expressions only, no dynamic form additions",
640
+ sortText: "2",
641
+ insertText: withSuggestionYaml,
642
+ ...defaultItem
643
+ }];
644
+ if (isGithub) {
645
+ if (!hasYamlOverride) suggestions.unshift({
646
+ label: "$yaml (custom yaml path)",
647
+ detail: "Override the default workflow.yaml path in the GitHub repo",
648
+ sortText: "0",
649
+ insertText: needsWithLine ? `${withIndent}with:\n${withIndent} $yaml:` : needsIndent ? "\n $yaml:" : "$yaml:",
650
+ ...defaultItem
651
+ });
652
+ suggestions.unshift({
653
+ label: "$thumbnail (custom thumbnail)",
654
+ detail: "Specify a thumbnail image path or URL for this subworkflow",
655
+ sortText: "0a",
656
+ insertText: needsWithLine ? `${withIndent}with:\n${withIndent} $thumbnail:` : needsIndent ? "\n $thumbnail:" : "$thumbnail:",
657
+ ...defaultItem
658
+ });
659
+ }
660
+ return { suggestions };
661
+ }
662
+ }));
663
+ }
664
+ }
665
+ });
666
+ }
667
+ /** When a workflow is imported as a group, rewrites bare `inputs.X` → `inputs.<groupname>.X` so expressions still resolve. */
668
+ function rewriteInputExpressionsForGroup(yamlStr, groupname) {
669
+ return yamlStr.replace(/\$\{\{(.*?)\}\}/g, (_match, expr) => {
670
+ return "${{ " + expr.replace(/\binputs\./g, `inputs.${groupname}.`).trim() + " }}";
671
+ });
672
+ }
673
+ function constructWithSuggestion(inputs, path) {
674
+ const withSuggestion = {};
675
+ Object.keys(inputs).map((k) => {
676
+ const input = inputs[k];
677
+ if (input.type === "group" && input.items) withSuggestion[k] = constructWithSuggestion(input.items, path + "." + k);
678
+ else withSuggestion[k] = "${{ inputs." + path + "." + k + " }}";
679
+ });
680
+ return withSuggestion;
681
+ }
682
+ function prepareWithSuggestion(needsNewline, needsIndent, needsWithLine, withIndent, withSuggestion) {
683
+ let withSuggestionYaml = dumpYaml(withSuggestion);
684
+ if (needsWithLine) {
685
+ const childIndent = withIndent + " ";
686
+ withSuggestionYaml = (withIndent + "with:\n" + withSuggestionYaml).replaceAll("\n", "\n" + childIndent);
687
+ } else if (needsNewline && needsIndent) withSuggestionYaml = ("\n" + withSuggestionYaml).replaceAll("\n", "\n ");
688
+ else if (needsNewline) withSuggestionYaml = "\n" + withSuggestionYaml;
689
+ return withSuggestionYaml;
690
+ }
691
+ /** Scan lines below a `uses:` line for a `$yaml:` key, stopping at another step. */
692
+ function hasYamlNearUses(model, usesLineNumber) {
693
+ const totalLines = model.getLineCount();
694
+ for (let ln = usesLineNumber + 1; ln <= Math.min(usesLineNumber + 20, totalLines); ln++) {
695
+ const content = model.getLineContent(ln).trim();
696
+ if (content === "") continue;
697
+ if (/^-\s/.test(content)) break;
698
+ if (/^\$yaml\s*:/.test(content)) return true;
699
+ }
700
+ return false;
701
+ }
702
+ /** Scan lines below a `with:` line for a `$yaml:` key within the same indent block. */
703
+ function scanForYamlInWithBlock(model, withLineNumber) {
704
+ const withIndentMatch = model.getLineContent(withLineNumber).match(/^(\s*)/);
705
+ const withIndent = withIndentMatch ? withIndentMatch[1].length : 0;
706
+ const totalLines = model.getLineCount();
707
+ for (let ln = withLineNumber + 1; ln <= totalLines; ln++) {
708
+ const line = model.getLineContent(ln);
709
+ if (line.trim() === "") continue;
710
+ const indentMatch = line.match(/^(\s*)/);
711
+ if ((indentMatch ? indentMatch[1].length : 0) <= withIndent) break;
712
+ const yamlMatch = line.match(/^\s*\$yaml\s*:\s*(.*)$/);
713
+ if (yamlMatch) return yamlMatch[1].trim();
714
+ }
715
+ }
716
+ //#endregion
717
+ export { MonacoEditor as default };