@parallelworks/ui 0.37.0 → 0.39.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 (64) hide show
  1. package/README.md +1 -1
  2. package/dist/ai/components/ChatInput.js +8 -23
  3. package/dist/ai/components/ChatSidebar.js +7 -4
  4. package/dist/ai/components/chatDraft.d.ts +7 -0
  5. package/dist/ai/components/chatDraft.js +66 -0
  6. package/dist/ai/index.d.ts +1 -1
  7. package/dist/ai/index.js +2 -2
  8. package/dist/ai/ui/sidebar.d.ts +6 -0
  9. package/dist/ai/ui/sidebar.js +15 -1
  10. package/dist/editing.d.ts +2 -0
  11. package/dist/editor/Monaco.js +1 -1
  12. package/dist/editor/index.js +1 -1
  13. package/dist/editor/lintContext.js +15 -3
  14. package/dist/form/FieldWrapper.js +1 -0
  15. package/dist/form/Form.js +64 -23
  16. package/dist/form/Wizard/WizardContainer.js +66 -6
  17. package/dist/form/Wizard/WizardStepContent.js +23 -10
  18. package/dist/form/Wizard/wizardPages.d.ts +12 -0
  19. package/dist/form/Wizard/wizardPages.js +5 -0
  20. package/dist/form/formEditing.d.ts +25 -0
  21. package/dist/form/formEditing.js +20 -0
  22. package/dist/graph/DependencyGraph.d.ts +8 -1
  23. package/dist/graph/DependencyGraph.js +433 -204
  24. package/dist/graph/GraphEditor.d.ts +27 -0
  25. package/dist/graph/GraphEditor.js +1543 -0
  26. package/dist/graph/GraphEditorDialogs.d.ts +87 -0
  27. package/dist/graph/GraphEditorDialogs.js +1808 -0
  28. package/dist/graph/InputDialog.js +1568 -54
  29. package/dist/graph/InputsEditor.d.ts +12 -0
  30. package/dist/graph/InputsEditor.js +1594 -0
  31. package/dist/graph/JobSummary.d.ts +3 -0
  32. package/dist/graph/JobSummary.js +34 -21
  33. package/dist/graph/MatrixGroup.d.ts +2 -1
  34. package/dist/graph/MatrixGroup.js +10 -3
  35. package/dist/graph/ShortcutsButton.js +56 -0
  36. package/dist/graph/SuggestionInput.js +29 -0
  37. package/dist/graph/WorkflowSettingsDialog.d.ts +21 -0
  38. package/dist/graph/WorkflowSettingsDialog.js +670 -0
  39. package/dist/graph/editorApi.js +8 -0
  40. package/dist/graph/editorChrome.js +75 -0
  41. package/dist/graph/editorFields.js +535 -0
  42. package/dist/graph/editorPrimitives.js +95 -4
  43. package/dist/graph/editorRows.d.ts +58 -0
  44. package/dist/graph/editorRows.js +460 -0
  45. package/dist/graph/editorStrings.d.ts +1 -0
  46. package/dist/graph/editorStrings.js +10 -1
  47. package/dist/graph/expressionRefs.js +39 -0
  48. package/dist/graph/fieldProblems.js +111 -0
  49. package/dist/graph/graphEditorState.d.ts +191 -0
  50. package/dist/graph/graphEditorState.js +74 -0
  51. package/dist/graph/index.d.ts +4 -0
  52. package/dist/graph/index.js +6 -2
  53. package/dist/graph/inputLayout.js +337 -0
  54. package/dist/graph/inputPages.d.ts +33 -0
  55. package/dist/graph/inputPages.js +186 -0
  56. package/dist/graph/inputRefs.js +388 -0
  57. package/dist/graph/inputRows.d.ts +43 -0
  58. package/dist/graph/inputRows.js +65 -0
  59. package/dist/graph/refusalText.js +13 -0
  60. package/dist/graph/settingsViews.js +62 -0
  61. package/dist/graph/stepWith.d.ts +38 -0
  62. package/dist/graph/stepWith.js +536 -0
  63. package/dist/styles.css +1 -1
  64. package/package.json +1 -1
@@ -0,0 +1,670 @@
1
+ import { useWorkflowEditing } from "../components/Provider.js";
2
+ import { IoMdClose } from "../icons/io/IoMdClose.js";
3
+ import { IconButton } from "../components/IconButton.js";
4
+ import { Input } from "../components/Input.js";
5
+ import { useGraphEditorStrings, useInputsEditorStrings } from "./editorStrings.js";
6
+ import { asRecord } from "./records.js";
7
+ import { SETTINGS_YAML_PATH } from "../editor/settingsYaml.js";
8
+ import { AddRowButton, ChoiceButtons, DialogShell, ENV_KEY, ExpressionToggle, FieldError, FieldLabel, KeyValueEditor, Section, StringListEditor, ToggleField, diffPatch, durationError, expressionError, flagError, flagOf, isEmptyPatch, labelled, orUndefined, rowsError, rowsFrom, rowsTo, text, withoutUndefined } from "./editorFields.js";
9
+ import { FlagField, refSuggestions } from "./inputRefs.js";
10
+ import { expressionRefs } from "./expressionRefs.js";
11
+ import { useNewInputs } from "./InputDialog.js";
12
+ import { useSettingsViews } from "./GraphEditorDialogs.js";
13
+ import { firstPageEdits, pagesBackEdits } from "./inputPages.js";
14
+ import { useState } from "react";
15
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
16
+ //#region src/graph/WorkflowSettingsDialog.tsx
17
+ var LINK_NAME = /^[a-z0-9_-]*[a-z0-9]$/;
18
+ var ORG_VARIABLE = /^[A-Z][A-Z0-9_]*$/;
19
+ var USER_VARIABLE = /^[a-zA-Z0-9_-]+$/;
20
+ var SESSION_FLAGS = [
21
+ "redirect",
22
+ "useTLS",
23
+ "useCustomDomain",
24
+ "openAI",
25
+ "detach"
26
+ ];
27
+ var LINK_FLAGS = ["redirect", "detach"];
28
+ var WIZARD_FLAGS = [
29
+ "showSteps",
30
+ "allowJump",
31
+ "hideStepNumbers"
32
+ ];
33
+ /** What this dialog writes; the schema coverage test holds these to the workflow schema. */
34
+ var WORKFLOW_FIELDS = [
35
+ "env",
36
+ "timeout",
37
+ "permissions",
38
+ "sessions",
39
+ "links",
40
+ "needs"
41
+ ];
42
+ var SESSION_FIELDS = [
43
+ "type",
44
+ "prompt-for-name",
45
+ "prompt-for-name.default",
46
+ ...SESSION_FLAGS
47
+ ];
48
+ var LINK_FIELDS = [
49
+ "endpoint",
50
+ "url",
51
+ ...LINK_FLAGS
52
+ ];
53
+ var NEEDS_FIELDS = [
54
+ "organizationVariables",
55
+ "userVariables",
56
+ "userVariables.<*>.hint"
57
+ ];
58
+ var INPUT_FORM_FIELDS = [
59
+ "labelPosition",
60
+ "wizard",
61
+ "wizard.mode",
62
+ "wizard.navigation",
63
+ ...WIZARD_FLAGS.map((flag) => `wizard.navigation.${flag}`),
64
+ "wizard.submitLabel",
65
+ "wizard.flatten"
66
+ ];
67
+ var cards = 0;
68
+ var cardKey = () => `card-${++cards}`;
69
+ function sessionsFrom(value) {
70
+ return Object.entries(asRecord(value)).map(([name, raw]) => {
71
+ const session = asRecord(raw);
72
+ const prompt = session["prompt-for-name"];
73
+ return {
74
+ key: cardKey(),
75
+ name,
76
+ original: raw,
77
+ type: session["type"] === "link" ? "link" : "tunnel",
78
+ prompt: Object.hasOwn(session, "prompt-for-name"),
79
+ promptDefault: text(asRecord(prompt)["default"]),
80
+ flags: Object.fromEntries(SESSION_FLAGS.map((flag) => [flag, flagOf(session[flag])]))
81
+ };
82
+ });
83
+ }
84
+ function sessionValue(draft) {
85
+ const session = {};
86
+ const original = asRecord(draft.original);
87
+ if (draft.type === "link" || original["type"] === "tunnel") session["type"] = draft.type;
88
+ if (draft.prompt) session["prompt-for-name"] = draft.promptDefault.trim() ? { default: draft.promptDefault.trim() } : null;
89
+ for (const flag of SESSION_FLAGS) if (draft.flags[flag] !== void 0) session[flag] = draft.flags[flag];
90
+ return Object.keys(session).length === 0 && draft.original === null ? null : session;
91
+ }
92
+ function linksFrom(value) {
93
+ return Object.entries(asRecord(value)).map(([name, raw]) => {
94
+ const link = asRecord(raw);
95
+ const target = link["url"] !== void 0 ? "url" : "endpoint";
96
+ return {
97
+ key: cardKey(),
98
+ name,
99
+ original: raw,
100
+ target,
101
+ value: text(link[target]),
102
+ flags: Object.fromEntries(LINK_FLAGS.map((flag) => [flag, flagOf(link[flag])]))
103
+ };
104
+ });
105
+ }
106
+ function linkValue(draft) {
107
+ const link = { [draft.target]: draft.value.trim() };
108
+ for (const flag of LINK_FLAGS) if (draft.flags[flag] !== void 0) link[flag] = draft.flags[flag];
109
+ return link;
110
+ }
111
+ function userVariablesFrom(value) {
112
+ if (typeof value === "string") return {
113
+ expression: value,
114
+ asList: false,
115
+ rows: []
116
+ };
117
+ if (Array.isArray(value)) return {
118
+ expression: void 0,
119
+ asList: true,
120
+ rows: value.map((name) => ({
121
+ key: String(name),
122
+ value: ""
123
+ }))
124
+ };
125
+ return {
126
+ expression: void 0,
127
+ asList: false,
128
+ rows: Object.entries(asRecord(value)).map(([name, raw]) => ({
129
+ key: name,
130
+ value: text(asRecord(raw)["hint"])
131
+ }))
132
+ };
133
+ }
134
+ function userVariablesValue(draft) {
135
+ if (draft.expression !== void 0) return orUndefined(draft.expression);
136
+ if (draft.rows.length === 0) return;
137
+ if (draft.asList && draft.rows.every((row) => !row.value.trim())) return draft.rows.map((row) => row.key.trim());
138
+ return Object.fromEntries(draft.rows.map((row) => [row.key.trim(), row.value.trim() ? { hint: row.value.trim() } : {}]));
139
+ }
140
+ function namesError(names, pattern, message, t) {
141
+ const trimmed = names.map((name) => name.trim());
142
+ for (const [i, name] of trimmed.entries()) {
143
+ if (!pattern.test(name)) return message;
144
+ if (trimmed.indexOf(name) !== i) return t.duplicateKey;
145
+ }
146
+ }
147
+ function Card({ onRemove, removeLabel, children }) {
148
+ return /* @__PURE__ */ jsxs("div", {
149
+ className: "flex items-start gap-2",
150
+ children: [/* @__PURE__ */ jsx("div", {
151
+ className: "flex flex-1 flex-col gap-3 rounded-md border theme-border p-3",
152
+ children
153
+ }), /* @__PURE__ */ jsx(IconButton, {
154
+ icon: /* @__PURE__ */ jsx(IoMdClose, { className: "h-4 w-4" }),
155
+ label: removeLabel,
156
+ variant: "ghost",
157
+ size: "sm",
158
+ onClick: onRemove
159
+ })]
160
+ });
161
+ }
162
+ function reflattenEdits(inputs, update) {
163
+ const names = Object.keys(inputs).filter((name) => !name.startsWith("$"));
164
+ const pages = names.filter((name) => asRecord(inputs[name])["type"] === "step").map((page) => ({
165
+ page,
166
+ fields: Object.keys(asRecord(asRecord(inputs[page])["options"])).filter((name) => !name.startsWith("$"))
167
+ })).filter(({ fields }) => fields.length > 0);
168
+ if (pages.length === 0) return [update];
169
+ return [
170
+ {
171
+ type: "moveInputs",
172
+ paths: pages.flatMap(({ page, fields }) => fields.map((name) => [page, name])),
173
+ parent: [],
174
+ index: names.length
175
+ },
176
+ update,
177
+ ...pages.map(({ page, fields }) => ({
178
+ type: "moveInputs",
179
+ paths: fields.map((name) => [name]),
180
+ parent: [page],
181
+ index: 0
182
+ }))
183
+ ];
184
+ }
185
+ /** The workflow's own settings in a form, or as YAML; switching carries the edits across. */
186
+ function WorkflowSettingsDialog(props) {
187
+ const t = useGraphEditorStrings();
188
+ const editing = useWorkflowEditing();
189
+ const views = useSettingsViews({
190
+ ...props,
191
+ name: "",
192
+ read: editing.settingsYaml,
193
+ write: (_, yaml) => ({
194
+ type: "setSettingsYaml",
195
+ yaml
196
+ }),
197
+ scope: () => [],
198
+ exclude: ["jobs", "on"],
199
+ optional: true
200
+ });
201
+ if (views.start === void 0) return /* @__PURE__ */ jsx(SettingsForm, { ...props });
202
+ if (views.yaml !== null) return views.yamlDialog(t.workflowSettings, SETTINGS_YAML_PATH, views.yaml);
203
+ return views.formDialog((host) => /* @__PURE__ */ jsx(SettingsForm, {
204
+ ...props,
205
+ ...host,
206
+ workflow: views.current ?? props.workflow
207
+ }, views.version));
208
+ }
209
+ function SettingsForm({ workflow, onEdit, onClose, renderShell, onDraft, pending = false }) {
210
+ const t = useGraphEditorStrings();
211
+ const inputStrings = useInputsEditorStrings();
212
+ const [original] = useState(() => workflow);
213
+ const editing = useWorkflowEditing();
214
+ const newInputs = useNewInputs(editing.workflowInputsSchema(original));
215
+ const source = {
216
+ ...newInputs.source,
217
+ extras: expressionRefs(editing, original)
218
+ };
219
+ const needs = asRecord(original["needs"]);
220
+ const [meta] = useState(() => asRecord(asRecord(editing.workflowInputsSchema(workflow))["$meta"]));
221
+ const wizard = asRecord(meta["wizard"]);
222
+ const [touched, setTouched] = useState(() => /* @__PURE__ */ new Set());
223
+ const touch = (section) => setTouched((current) => current.has(section) ? current : new Set(current).add(section));
224
+ const [env, setEnvRows] = useState(() => rowsFrom(original["env"], false));
225
+ const [timeout, setTimeoutText] = useState(text(original["timeout"]));
226
+ const [permissions, setPermissionsList] = useState(() => Array.isArray(original["permissions"]) ? original["permissions"].map(String) : []);
227
+ const [sessions, setSessionsList] = useState(() => sessionsFrom(original["sessions"]));
228
+ const [links, setLinksList] = useState(() => linksFrom(original["links"]));
229
+ const [orgVariables, setOrgVariablesList] = useState(() => Array.isArray(needs["organizationVariables"]) ? needs["organizationVariables"].map(String) : []);
230
+ const [userVariables, setUserVariablesDraft] = useState(() => userVariablesFrom(needs["userVariables"]));
231
+ const [labelPosition, setLabelPositionValue] = useState(meta["labelPosition"] === "top" ? "top" : "left");
232
+ const [wizardOn, setWizardOn] = useState(wizard["mode"] === "wizard");
233
+ const [wizardFlags, setWizardFlags] = useState(() => Object.fromEntries(WIZARD_FLAGS.map((flag) => [flag, flagOf(asRecord(wizard["navigation"])[flag])])));
234
+ const [submitLabel, setSubmitLabel] = useState(text(wizard["submitLabel"]));
235
+ const [flatten, setFlatten] = useState(wizard["flatten"] !== false);
236
+ const tracked = (section, set) => (value) => {
237
+ touch(section);
238
+ set(value);
239
+ };
240
+ const setEnv = tracked("env", setEnvRows);
241
+ const changeTimeout = tracked("timeout", setTimeoutText);
242
+ const setPermissions = tracked("permissions", setPermissionsList);
243
+ const setSessions = tracked("sessions", setSessionsList);
244
+ const setLinks = tracked("links", setLinksList);
245
+ const setOrgVariables = tracked("needs", setOrgVariablesList);
246
+ const setUserVariables = tracked("needs", setUserVariablesDraft);
247
+ const setLabelPosition = tracked("meta", setLabelPositionValue);
248
+ const updateSession = (i, patch) => setSessions(sessions.map((s, j) => j === i ? {
249
+ ...s,
250
+ ...patch
251
+ } : s));
252
+ const updateLink = (i, patch) => setLinks(links.map((l, j) => j === i ? {
253
+ ...l,
254
+ ...patch
255
+ } : l));
256
+ const redirects = [...sessions.map((session) => session.flags.redirect), ...links.map((link) => link.flags.redirect)].filter((value) => value === true).length;
257
+ const errors = {
258
+ env: rowsError(env, ENV_KEY, t.invalidEnvKey, t),
259
+ timeout: durationError(timeout, t),
260
+ permissions: permissions.some((value) => !value.trim()) ? t.required : void 0,
261
+ sessions: namesError(sessions.map((session) => session.name), /\S/, t.required, t) ?? sessions.flatMap((session) => SESSION_FLAGS.map((flag) => session.flags[flag])).map((value) => flagError(value, t)).find(Boolean),
262
+ links: namesError(links.map((link) => link.name), LINK_NAME, t.invalidLinkName, t) ?? (links.some((link) => !link.value.trim()) ? t.required : void 0) ?? links.flatMap((link) => LINK_FLAGS.map((f) => link.flags[f])).map((value) => flagError(value, t)).find(Boolean),
263
+ redirect: redirects > 1 ? t.oneRedirect : void 0,
264
+ orgVariables: namesError(orgVariables, ORG_VARIABLE, t.invalidOrganizationVariable, t),
265
+ userVariables: userVariables.expression !== void 0 ? expressionError(userVariables.expression, t) : rowsError(userVariables.rows, USER_VARIABLE, t.invalidKey, t),
266
+ wizard: wizardOn ? WIZARD_FLAGS.map((flag) => flagError(wizardFlags[flag], t)).find(Boolean) : void 0
267
+ };
268
+ const nextNeeds = () => {
269
+ if (!touched.has("needs")) return original["needs"];
270
+ const next = { ...needs };
271
+ next["organizationVariables"] = orgVariables.length > 0 ? orgVariables.map((name) => name.trim()) : void 0;
272
+ next["userVariables"] = userVariablesValue(userVariables);
273
+ const written = withoutUndefined(next);
274
+ return Object.keys(written).length > 0 ? written : void 0;
275
+ };
276
+ const pick = (section, value) => touched.has(section) ? value() : original[section];
277
+ const patch = diffPatch(original, {
278
+ env: pick("env", () => rowsTo(env, (value) => value)),
279
+ timeout: pick("timeout", () => orUndefined(timeout)),
280
+ permissions: pick("permissions", () => permissions.length > 0 ? permissions.map((value) => value.trim()) : void 0),
281
+ sessions: pick("sessions", () => sessions.length > 0 ? Object.fromEntries(sessions.map((session) => [session.name.trim(), sessionValue(session)])) : void 0),
282
+ links: pick("links", () => links.length > 0 ? Object.fromEntries(links.map((link) => [link.name.trim(), linkValue(link)])) : void 0),
283
+ needs: nextNeeds()
284
+ });
285
+ const nextWizard = () => {
286
+ if (!wizardOn) return;
287
+ const navigation = {};
288
+ for (const flag of WIZARD_FLAGS) if (wizardFlags[flag] !== void 0) navigation[flag] = wizardFlags[flag];
289
+ const next = {
290
+ ...wizard,
291
+ mode: "wizard"
292
+ };
293
+ next["navigation"] = Object.keys(navigation).length > 0 ? navigation : void 0;
294
+ next["submitLabel"] = orUndefined(submitLabel);
295
+ next["flatten"] = flatten ? wizard["flatten"] === true ? true : void 0 : false;
296
+ return withoutUndefined(next);
297
+ };
298
+ const metaPatch = touched.has("meta") ? diffPatch(meta, {
299
+ labelPosition: labelPosition === "top" ? "top" : meta["labelPosition"] === "left" ? "left" : void 0,
300
+ wizard: nextWizard()
301
+ }) : {
302
+ set: {},
303
+ unset: []
304
+ };
305
+ const dirty = !isEmptyPatch(patch) || !isEmptyPatch(metaPatch);
306
+ const invalid = Object.values(errors).some(Boolean);
307
+ const setWizard = (set) => tracked("meta", set);
308
+ const update = {
309
+ type: "updateWorkflow",
310
+ ...patch,
311
+ inputsMeta: metaPatch
312
+ };
313
+ const inputs = newInputs.all;
314
+ const wasWizard = wizard["mode"] === "wizard";
315
+ const settings = isEmptyPatch(patch) ? [] : [{
316
+ type: "updateWorkflow",
317
+ ...patch
318
+ }];
319
+ const wizardEdit = {
320
+ type: "updateWorkflow",
321
+ inputsMeta: metaPatch
322
+ };
323
+ const edits = wizardOn && !wasWizard ? [
324
+ ...settings,
325
+ wizardEdit,
326
+ ...firstPageEdits(editing, inputs, inputStrings.stepTitle(1))
327
+ ] : !wizardOn && wasWizard ? [
328
+ ...settings,
329
+ ...pagesBackEdits(inputs, []),
330
+ wizardEdit
331
+ ] : wizardOn && flatten !== (wizard["flatten"] !== false) ? [...settings, ...reflattenEdits(inputs, wizardEdit)] : [update];
332
+ const saveEdit = newInputs.save(dirty ? editing.batchOf(edits) : null);
333
+ onDraft?.(saveEdit, invalid);
334
+ const children = /* @__PURE__ */ jsxs(Fragment$1, { children: [
335
+ newInputs.dialog,
336
+ /* @__PURE__ */ jsxs(Section, {
337
+ title: t.sectionGeneral,
338
+ open: true,
339
+ children: [
340
+ /* @__PURE__ */ jsx(FieldLabel, {
341
+ label: t.fields.env,
342
+ yamlKey: "env",
343
+ description: t.help.workflowEnv
344
+ }),
345
+ /* @__PURE__ */ jsx(KeyValueEditor, {
346
+ rows: env,
347
+ onChange: setEnv,
348
+ error: errors.env,
349
+ valueSuggestions: refSuggestions(source)
350
+ }),
351
+ /* @__PURE__ */ jsx(Input, {
352
+ mono: true,
353
+ ...labelled(t.fields.timeout, "timeout"),
354
+ description: t.help.workflowTimeout,
355
+ value: timeout,
356
+ error: errors.timeout,
357
+ onChange: (e) => changeTimeout(e.target.value)
358
+ })
359
+ ]
360
+ }),
361
+ /* @__PURE__ */ jsx(Section, {
362
+ title: t.sectionPermissions,
363
+ description: t.help.permissions,
364
+ open: permissions.length > 0,
365
+ alert: !!errors.permissions,
366
+ children: /* @__PURE__ */ jsx(StringListEditor, {
367
+ label: t.sectionPermissions,
368
+ values: permissions,
369
+ onChange: setPermissions,
370
+ addLabel: t.addPermission,
371
+ placeholder: "*",
372
+ error: errors.permissions
373
+ })
374
+ }),
375
+ /* @__PURE__ */ jsxs(Section, {
376
+ title: t.sectionSessions,
377
+ description: t.help.sessions,
378
+ open: sessions.length > 0,
379
+ alert: !!(errors.sessions || errors.redirect),
380
+ children: [
381
+ sessions.map((session, i) => /* @__PURE__ */ jsxs(Card, {
382
+ removeLabel: t.removeRow,
383
+ onRemove: () => setSessions(sessions.filter((_, j) => j !== i)),
384
+ children: [
385
+ /* @__PURE__ */ jsx(Input, {
386
+ mono: true,
387
+ label: t.fields.name,
388
+ description: t.help.sessionName,
389
+ value: session.name,
390
+ onChange: (e) => updateSession(i, { name: e.target.value })
391
+ }),
392
+ /* @__PURE__ */ jsx(FieldLabel, {
393
+ label: t.fields.sessionType,
394
+ yamlKey: "type",
395
+ description: t.help.sessionType
396
+ }),
397
+ /* @__PURE__ */ jsx(ChoiceButtons, {
398
+ options: [{
399
+ value: "tunnel",
400
+ label: t.sessionTypeTunnel
401
+ }, {
402
+ value: "link",
403
+ label: t.sessionTypeLink
404
+ }],
405
+ value: session.type,
406
+ onChange: (type) => updateSession(i, { type })
407
+ }),
408
+ /* @__PURE__ */ jsx(FieldLabel, {
409
+ label: t.fields.promptForName,
410
+ yamlKey: "prompt-for-name",
411
+ description: t.help.promptForName
412
+ }),
413
+ /* @__PURE__ */ jsx(ChoiceButtons, {
414
+ options: [{
415
+ value: "off",
416
+ label: t.promptOff
417
+ }, {
418
+ value: "ask",
419
+ label: t.promptAsk
420
+ }],
421
+ value: session.prompt ? "ask" : "off",
422
+ onChange: (mode) => updateSession(i, { prompt: mode === "ask" })
423
+ }),
424
+ session.prompt && /* @__PURE__ */ jsx(Input, {
425
+ ...labelled(t.fields.promptDefault, "default"),
426
+ description: t.help.promptDefault,
427
+ value: session.promptDefault,
428
+ onChange: (e) => updateSession(i, { promptDefault: e.target.value })
429
+ }),
430
+ SESSION_FLAGS.map((flag) => /* @__PURE__ */ jsx(FlagField, {
431
+ label: t.fields[flag],
432
+ yamlKey: flag,
433
+ description: flag === "redirect" ? t.help.sessionRedirect : flag === "detach" ? t.help.sessionDetach : t.help[flag],
434
+ value: session.flags[flag],
435
+ original: asRecord(session.original)[flag],
436
+ onChange: (value) => updateSession(i, { flags: {
437
+ ...session.flags,
438
+ [flag]: value
439
+ } }),
440
+ source
441
+ }, flag))
442
+ ]
443
+ }, session.key)),
444
+ /* @__PURE__ */ jsx(FieldError, { message: errors.sessions ?? errors.redirect }),
445
+ /* @__PURE__ */ jsx(AddRowButton, {
446
+ label: t.addSession,
447
+ onClick: () => setSessions([...sessions, {
448
+ key: cardKey(),
449
+ name: "",
450
+ original: null,
451
+ type: "tunnel",
452
+ prompt: false,
453
+ promptDefault: "",
454
+ flags: {
455
+ redirect: void 0,
456
+ useTLS: void 0,
457
+ useCustomDomain: void 0,
458
+ openAI: void 0,
459
+ detach: void 0
460
+ }
461
+ }])
462
+ })
463
+ ]
464
+ }),
465
+ /* @__PURE__ */ jsxs(Section, {
466
+ title: t.sectionLinks,
467
+ description: t.help.links,
468
+ open: links.length > 0,
469
+ alert: !!(errors.links || errors.redirect),
470
+ children: [
471
+ links.map((link, i) => /* @__PURE__ */ jsxs(Card, {
472
+ removeLabel: t.removeRow,
473
+ onRemove: () => setLinks(links.filter((_, j) => j !== i)),
474
+ children: [
475
+ /* @__PURE__ */ jsx(Input, {
476
+ mono: true,
477
+ label: t.fields.name,
478
+ description: t.help.linkName,
479
+ value: link.name,
480
+ onChange: (e) => updateLink(i, { name: e.target.value })
481
+ }),
482
+ /* @__PURE__ */ jsx(FieldLabel, {
483
+ label: t.fields.linkTarget,
484
+ description: t.help.linkTarget
485
+ }),
486
+ /* @__PURE__ */ jsx(ChoiceButtons, {
487
+ options: [{
488
+ value: "endpoint",
489
+ label: t.linkToEndpoint
490
+ }, {
491
+ value: "url",
492
+ label: t.linkToUrl
493
+ }],
494
+ value: link.target,
495
+ onChange: (target) => updateLink(i, { target })
496
+ }),
497
+ /* @__PURE__ */ jsx(Input, {
498
+ mono: true,
499
+ "aria-label": link.target === "url" ? t.linkToUrl : t.linkToEndpoint,
500
+ description: link.target === "url" ? t.help.linkUrl : t.help.linkEndpoint,
501
+ value: link.value,
502
+ placeholder: link.target === "url" ? "https://" : "app",
503
+ onChange: (e) => updateLink(i, { value: e.target.value })
504
+ }),
505
+ LINK_FLAGS.map((flag) => /* @__PURE__ */ jsx(FlagField, {
506
+ label: t.fields[flag],
507
+ yamlKey: flag,
508
+ description: flag === "redirect" ? t.help.linkRedirect : t.help.linkDetach,
509
+ value: link.flags[flag],
510
+ original: asRecord(link.original)[flag],
511
+ onChange: (value) => updateLink(i, { flags: {
512
+ ...link.flags,
513
+ [flag]: value
514
+ } }),
515
+ source
516
+ }, flag))
517
+ ]
518
+ }, link.key)),
519
+ /* @__PURE__ */ jsx(FieldError, { message: errors.links ?? errors.redirect }),
520
+ /* @__PURE__ */ jsx(AddRowButton, {
521
+ label: t.addLink,
522
+ onClick: () => setLinks([...links, {
523
+ key: cardKey(),
524
+ name: "",
525
+ original: void 0,
526
+ target: "endpoint",
527
+ value: "",
528
+ flags: {
529
+ redirect: void 0,
530
+ detach: void 0
531
+ }
532
+ }])
533
+ })
534
+ ]
535
+ }),
536
+ /* @__PURE__ */ jsxs(Section, {
537
+ title: t.sectionRequiredVariables,
538
+ open: orgVariables.length > 0 || userVariables.rows.length > 0 || userVariables.expression !== void 0,
539
+ alert: !!(errors.orgVariables || errors.userVariables),
540
+ children: [
541
+ /* @__PURE__ */ jsx(FieldLabel, {
542
+ label: t.fields.organizationVariables,
543
+ yamlKey: "needs.organizationVariables",
544
+ description: t.help.organizationVariables
545
+ }),
546
+ /* @__PURE__ */ jsx(StringListEditor, {
547
+ label: t.fields.organizationVariables,
548
+ values: orgVariables,
549
+ onChange: setOrgVariables,
550
+ addLabel: t.addOrganizationVariable,
551
+ placeholder: "MY_VARIABLE",
552
+ error: errors.orgVariables
553
+ }),
554
+ /* @__PURE__ */ jsx(FieldLabel, {
555
+ label: t.fields.userVariables,
556
+ yamlKey: "needs.userVariables",
557
+ description: t.help.userVariables,
558
+ actions: /* @__PURE__ */ jsx(ExpressionToggle, {
559
+ active: userVariables.expression !== void 0,
560
+ onChange: (on) => setUserVariables({
561
+ ...userVariables,
562
+ expression: on ? "" : void 0
563
+ })
564
+ })
565
+ }),
566
+ userVariables.expression !== void 0 ? /* @__PURE__ */ jsx(Input, {
567
+ mono: true,
568
+ "aria-label": t.fields.userVariables,
569
+ value: userVariables.expression,
570
+ error: errors.userVariables,
571
+ onChange: (e) => setUserVariables({
572
+ ...userVariables,
573
+ expression: e.target.value
574
+ })
575
+ }) : /* @__PURE__ */ jsx(KeyValueEditor, {
576
+ rows: userVariables.rows,
577
+ onChange: (rows) => setUserVariables({
578
+ ...userVariables,
579
+ rows
580
+ }),
581
+ error: errors.userVariables,
582
+ keyPlaceholder: t.key,
583
+ valuePlaceholder: t.help.userVariableHint,
584
+ addLabel: t.addUserVariable
585
+ })
586
+ ]
587
+ }),
588
+ /* @__PURE__ */ jsxs(Section, {
589
+ title: t.sectionInputForm,
590
+ open: wizardOn,
591
+ alert: !!errors.wizard,
592
+ children: [
593
+ /* @__PURE__ */ jsx(FieldLabel, {
594
+ label: t.fields.labelPosition,
595
+ yamlKey: "labelPosition",
596
+ description: t.help.labelPosition
597
+ }),
598
+ /* @__PURE__ */ jsx(ChoiceButtons, {
599
+ options: [{
600
+ value: "left",
601
+ label: t.labelsBeside
602
+ }, {
603
+ value: "top",
604
+ label: t.labelsAbove
605
+ }],
606
+ value: labelPosition,
607
+ onChange: setLabelPosition
608
+ }),
609
+ /* @__PURE__ */ jsx(ToggleField, {
610
+ label: t.fields.wizard,
611
+ yamlKey: "wizard",
612
+ description: t.help.wizard,
613
+ checked: wizardOn,
614
+ onChange: setWizard((on) => {
615
+ setWizardOn(on);
616
+ if (on && wizard["mode"] !== "wizard") {
617
+ setWizardFlags((current) => ({
618
+ ...current,
619
+ allowJump: current.allowJump ?? true
620
+ }));
621
+ setSubmitLabel((current) => current || inputStrings.submitLabel);
622
+ }
623
+ })
624
+ }),
625
+ wizardOn && /* @__PURE__ */ jsxs(Fragment$1, { children: [
626
+ WIZARD_FLAGS.map((flag) => /* @__PURE__ */ jsx(FlagField, {
627
+ label: t.fields[flag],
628
+ yamlKey: flag,
629
+ description: t.help[flag],
630
+ value: wizardFlags[flag],
631
+ original: asRecord(wizard["navigation"])[flag],
632
+ fallback: flag === "showSteps",
633
+ onChange: setWizard((value) => setWizardFlags((current) => ({
634
+ ...current,
635
+ [flag]: value
636
+ })))
637
+ }, flag)),
638
+ /* @__PURE__ */ jsx(Input, {
639
+ ...labelled(t.fields.submitLabel, "submitLabel"),
640
+ description: t.help.submitLabel,
641
+ value: submitLabel,
642
+ onChange: (e) => setWizard(setSubmitLabel)(e.target.value)
643
+ }),
644
+ /* @__PURE__ */ jsx(ToggleField, {
645
+ label: t.fields.flatten,
646
+ yamlKey: "flatten",
647
+ description: t.help.wizardFlatten,
648
+ checked: flatten,
649
+ onChange: setWizard(setFlatten)
650
+ })
651
+ ] })
652
+ ]
653
+ })
654
+ ] });
655
+ const shellProps = {
656
+ title: t.workflowSettings,
657
+ onClose,
658
+ dirty: dirty || newInputs.pending.length > 0 || pending,
659
+ locked: newInputs.dialog !== null,
660
+ saveDisabled: invalid,
661
+ onSubmit: () => {
662
+ if (saveEdit) onEdit(saveEdit);
663
+ onClose();
664
+ },
665
+ children
666
+ };
667
+ return renderShell ? renderShell(shellProps) : /* @__PURE__ */ jsx(DialogShell, { ...shellProps });
668
+ }
669
+ //#endregion
670
+ export { INPUT_FORM_FIELDS, LINK_FIELDS, NEEDS_FIELDS, SESSION_FIELDS, WORKFLOW_FIELDS, WorkflowSettingsDialog };
@@ -0,0 +1,8 @@
1
+ //#region src/graph/editorApi.ts
2
+ var TEXT_INPUT = "input, textarea, select, [contenteditable=\"true\"]";
3
+ var typing = (target) => target instanceof Element && !!target.closest(TEXT_INPUT);
4
+ function overlaps(rect, box) {
5
+ return rect.left <= box.right && rect.right >= box.left && rect.top <= box.bottom && rect.bottom >= box.top;
6
+ }
7
+ //#endregion
8
+ export { overlaps, typing };