@parallelworks/ui 0.36.0 → 0.38.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 (106) hide show
  1. package/README.md +16 -0
  2. package/dist/ai/agent/permissions.d.ts +2 -2
  3. package/dist/ai/agent/permissions.js +3 -1
  4. package/dist/ai/components/ChatSidebar.js +7 -4
  5. package/dist/ai/components/agent/PermissionPicker.js +1 -0
  6. package/dist/ai/index.d.ts +1 -1
  7. package/dist/ai/index.js +2 -2
  8. package/dist/ai/strings.d.ts +2 -0
  9. package/dist/ai/strings.js +2 -0
  10. package/dist/ai/ui/sidebar.d.ts +6 -0
  11. package/dist/ai/ui/sidebar.js +15 -1
  12. package/dist/components/Dropdown.d.ts +2 -0
  13. package/dist/components/Dropdown.js +7 -2
  14. package/dist/components/Input.d.ts +11 -6
  15. package/dist/components/Input.js +93 -74
  16. package/dist/components/Provider.d.ts +31 -0
  17. package/dist/components/Provider.js +50 -2
  18. package/dist/editing.d.ts +404 -0
  19. package/dist/editor/EditorField.js +1 -1
  20. package/dist/editor/Monaco.d.ts +15 -1
  21. package/dist/editor/Monaco.js +117 -13
  22. package/dist/editor/editorModel.d.ts +5 -0
  23. package/dist/editor/editorModel.js +23 -0
  24. package/dist/editor/index.d.ts +4 -0
  25. package/dist/editor/index.js +5 -1
  26. package/dist/editor/lintContext.d.ts +21 -0
  27. package/dist/editor/lintContext.js +136 -0
  28. package/dist/editor/lintOwner.d.ts +2 -0
  29. package/dist/editor/lintOwner.js +5 -0
  30. package/dist/editor/nestedText.d.ts +13 -0
  31. package/dist/editor/nestedText.js +15 -0
  32. package/dist/editor/reveal.d.ts +4 -0
  33. package/dist/editor/reveal.js +15 -0
  34. package/dist/editor/settingsYaml.d.ts +12 -0
  35. package/dist/editor/settingsYaml.js +41 -0
  36. package/dist/form/FieldWrapper.js +1 -0
  37. package/dist/form/Fields/DropdownField.js +1 -1
  38. package/dist/form/Fields/DurationField.js +1 -1
  39. package/dist/form/Form.js +65 -24
  40. package/dist/form/Wizard/WizardContainer.js +66 -6
  41. package/dist/form/Wizard/WizardStepContent.js +23 -10
  42. package/dist/form/Wizard/wizardPages.d.ts +12 -0
  43. package/dist/form/Wizard/wizardPages.js +5 -0
  44. package/dist/form/fieldRegistry.js +1 -1
  45. package/dist/form/formEditing.d.ts +25 -0
  46. package/dist/form/formEditing.js +20 -0
  47. package/dist/form/index.js +5 -5
  48. package/dist/graph/InputDialog.d.ts +80 -0
  49. package/dist/graph/InputDialog.js +2110 -0
  50. package/dist/graph/InputsEditor.d.ts +12 -0
  51. package/dist/graph/InputsEditor.js +1594 -0
  52. package/dist/graph/ProblemsButton.d.ts +9 -0
  53. package/dist/graph/ProblemsButton.js +61 -0
  54. package/dist/graph/ShortcutsButton.d.ts +15 -0
  55. package/dist/graph/ShortcutsButton.js +56 -0
  56. package/dist/graph/SuggestionInput.d.ts +18 -0
  57. package/dist/graph/SuggestionInput.js +29 -0
  58. package/dist/graph/ToolbarPopover.d.ts +8 -0
  59. package/dist/graph/ToolbarPopover.js +71 -0
  60. package/dist/graph/editorApi.d.ts +51 -0
  61. package/dist/graph/editorApi.js +8 -0
  62. package/dist/graph/editorChrome.d.ts +27 -0
  63. package/dist/graph/editorChrome.js +75 -0
  64. package/dist/graph/editorFields.d.ts +180 -0
  65. package/dist/graph/editorFields.js +394 -0
  66. package/dist/graph/editorPrimitives.d.ts +60 -0
  67. package/dist/graph/editorPrimitives.js +150 -0
  68. package/dist/graph/editorStrings.d.ts +695 -0
  69. package/dist/graph/editorStrings.js +707 -0
  70. package/dist/graph/expressionRefs.d.ts +12 -0
  71. package/dist/graph/fieldProblems.d.ts +32 -0
  72. package/dist/graph/fieldProblems.js +77 -0
  73. package/dist/graph/index.d.ts +7 -0
  74. package/dist/graph/index.js +6 -1
  75. package/dist/graph/inputLayout.d.ts +25 -0
  76. package/dist/graph/inputLayout.js +337 -0
  77. package/dist/graph/inputPages.d.ts +33 -0
  78. package/dist/graph/inputPages.js +186 -0
  79. package/dist/graph/inputRefs.d.ts +103 -0
  80. package/dist/graph/inputRefs.js +196 -0
  81. package/dist/graph/inputRows.d.ts +43 -0
  82. package/dist/graph/inputRows.js +65 -0
  83. package/dist/graph/records.d.ts +3 -0
  84. package/dist/graph/records.js +7 -0
  85. package/dist/graph/refusalText.d.ts +6 -0
  86. package/dist/graph/settingsViews.d.ts +26 -0
  87. package/dist/graph/settingsViews.js +62 -0
  88. package/dist/graph/usesInputs.d.ts +17 -0
  89. package/dist/graph/usesInputs.js +40 -0
  90. package/dist/icons/bi/BiRedo.js +16 -0
  91. package/dist/icons/bi/BiUndo.js +16 -0
  92. package/dist/icons/lu/LuBraces.js +27 -0
  93. package/dist/icons/lu/LuCode.js +27 -0
  94. package/dist/icons/lu/LuKeyboard.js +71 -0
  95. package/dist/icons/lu/LuNetwork.js +63 -0
  96. package/dist/icons/lu/LuTextCursorInput.js +45 -0
  97. package/dist/icons.d.ts +2 -2
  98. package/dist/icons.js +8 -1
  99. package/dist/index.d.ts +2 -1
  100. package/dist/index.js +2 -2
  101. package/dist/list/ActionToolbar.d.ts +2 -0
  102. package/dist/list/ActionToolbar.js +18 -5
  103. package/dist/list/RowContextMenu.d.ts +10 -1
  104. package/dist/list/RowContextMenu.js +185 -30
  105. package/dist/styles.css +1 -1
  106. package/package.json +1 -1
@@ -1,7 +1,11 @@
1
+ import { INPUT_YAML_PATH, JOB_YAML_PATH, SETTINGS_YAML_PATH, STEP_YAML_PATH, settingsSchemas } from "./settingsYaml.js";
1
2
  import Editor from "./Editor.js";
2
3
  import EditorField from "./EditorField.js";
3
4
  import FormCodePanel from "./FormCodePanel.js";
5
+ import { HOST_MARKER_OWNER, LINT_OWNER } from "./lintOwner.js";
6
+ import { nestedWorkflowText } from "./nestedText.js";
7
+ import { revealLines } from "./reveal.js";
4
8
  import { defineEditorThemes, getThemeName } from "./themes.js";
5
9
  import { setupMonacoWorkers } from "./workers.js";
6
10
  import { configureEditorYaml } from "./yaml.js";
7
- export { Editor, EditorField, FormCodePanel, configureEditorYaml, defineEditorThemes, getThemeName, setupMonacoWorkers };
11
+ export { Editor, EditorField, FormCodePanel, HOST_MARKER_OWNER, INPUT_YAML_PATH, JOB_YAML_PATH, LINT_OWNER, SETTINGS_YAML_PATH, STEP_YAML_PATH, configureEditorYaml, defineEditorThemes, getThemeName, nestedWorkflowText, revealLines, settingsSchemas, setupMonacoWorkers };
@@ -0,0 +1,21 @@
1
+ import type { WorkflowEditing, WorkflowLintContext } from '../editing.js';
2
+ import { type UsesSources } from '../graph/usesInputs.js';
3
+ export { HOST_MARKER_OWNER, LINT_OWNER } from './lintOwner.js';
4
+ export interface LintSources extends UsesSources {
5
+ secrets?: (() => Promise<string[]>) | undefined;
6
+ /** `uses` values the host runs itself, which have no workflow to read inputs from. */
7
+ actions?: Record<string, unknown> | undefined;
8
+ }
9
+ /**
10
+ * What the linter can't read from `source`: the inputs of each workflow a step uses, and which
11
+ * of the user's variables are secret, as far as they have arrived. `onMore` runs when more does.
12
+ */
13
+ export declare function lintContext(editing: WorkflowEditing, source: string, sources: LintSources, onMore: () => void): WorkflowLintContext;
14
+ export declare function useLintSources(): LintSources;
15
+ /** lintContext for a component, which renders again when more of it arrives. */
16
+ export declare function useLintContext(source: string | undefined): WorkflowLintContext;
17
+ /**
18
+ * The engine's editing once its checks have loaded, rendering again when they have; undefined
19
+ * before then, without an engine that edits, or with `load` false.
20
+ */
21
+ export declare function useLintEditing(load?: boolean): WorkflowEditing | undefined;
@@ -0,0 +1,136 @@
1
+ import { useLoadedWorkflowEngine, useRepoSuggestions, useSecretVariablesResolver, useWorkflowActions, useWorkflowJsonResolver } from "../components/Provider.js";
2
+ import { asRecord } from "../graph/records.js";
3
+ import { HOST_MARKER_OWNER, LINT_OWNER } from "./lintOwner.js";
4
+ import { DEFAULT_GITLAB_HOST, DEFAULT_REPO_YAML, loadUsesInputs } from "../graph/usesInputs.js";
5
+ import { useEffect, useMemo, useState } from "react";
6
+ //#region src/editor/lintContext.ts
7
+ var KEEP_MS = 6e4;
8
+ var KEEP_FAILED_MS = 1e4;
9
+ var stale = (kept) => !kept || Date.now() - kept.at > (kept.value === null ? KEEP_FAILED_MS : KEEP_MS);
10
+ function failed(kept) {
11
+ return kept?.value ? {
12
+ value: kept.value,
13
+ at: Date.now() - KEEP_MS + KEEP_FAILED_MS
14
+ } : {
15
+ value: null,
16
+ at: Date.now()
17
+ };
18
+ }
19
+ var targets = /* @__PURE__ */ new Map();
20
+ var secretNames;
21
+ var waiting = /* @__PURE__ */ new Map();
22
+ var SECRETS = "\0secrets";
23
+ function fetchOnce(key, onMore, load) {
24
+ const waiters = waiting.get(key);
25
+ if (waiters) {
26
+ waiters.add(onMore);
27
+ return;
28
+ }
29
+ const told = /* @__PURE__ */ new Set([onMore]);
30
+ waiting.set(key, told);
31
+ load().finally(() => {
32
+ waiting.delete(key);
33
+ for (const tell of told) tell();
34
+ });
35
+ }
36
+ var text = (value, fallback) => typeof value === "string" && value ? value : fallback;
37
+ /**
38
+ * What the linter can't read from `source`: the inputs of each workflow a step uses, and which
39
+ * of the user's variables are secret, as far as they have arrived. `onMore` runs when more does.
40
+ */
41
+ function lintContext(editing, source, sources, onMore) {
42
+ let doc;
43
+ try {
44
+ doc = editing.loadYaml(source);
45
+ } catch {
46
+ doc = void 0;
47
+ }
48
+ const wanted = /* @__PURE__ */ new Map();
49
+ for (const job of Object.values(asRecord(asRecord(doc)["jobs"]))) {
50
+ const steps = asRecord(job)["steps"];
51
+ for (const step of Array.isArray(steps) ? steps : []) {
52
+ const uses = asRecord(step)["uses"];
53
+ if (typeof uses !== "string" || uses.includes("${{") || Object.hasOwn(sources.actions ?? {}, uses.trim())) continue;
54
+ const withBlock = asRecord(asRecord(step)["with"]);
55
+ const key = [
56
+ uses.trim(),
57
+ text(withBlock["$yaml"], DEFAULT_REPO_YAML),
58
+ text(withBlock["$host"], DEFAULT_GITLAB_HOST)
59
+ ].join("\n");
60
+ wanted.set(uses.trim(), (wanted.get(uses.trim()) ?? /* @__PURE__ */ new Set()).add(key));
61
+ }
62
+ }
63
+ const usesInputs = {};
64
+ for (const [uses, keys] of wanted) {
65
+ const key = keys.size === 1 ? [...keys][0] : void 0;
66
+ if (key === void 0) continue;
67
+ const kept = targets.get(key);
68
+ if (kept?.value) usesInputs[uses] = kept.value;
69
+ if (stale(kept)) {
70
+ const [, yamlPath = DEFAULT_REPO_YAML, host = DEFAULT_GITLAB_HOST] = key.split("\n");
71
+ fetchOnce(key, onMore, () => loadUsesInputs(editing, uses, {
72
+ yamlPath,
73
+ host
74
+ }, sources).then((inputs) => {
75
+ targets.set(key, {
76
+ value: inputs,
77
+ at: Date.now()
78
+ });
79
+ }, () => {
80
+ targets.set(key, failed(targets.get(key)));
81
+ }));
82
+ }
83
+ }
84
+ const secrets = sources.secrets;
85
+ if (secrets && stale(secretNames)) fetchOnce(SECRETS, onMore, () => secrets().then((names) => {
86
+ secretNames = {
87
+ value: names,
88
+ at: Date.now()
89
+ };
90
+ }, () => {
91
+ secretNames = failed(secretNames);
92
+ }));
93
+ return {
94
+ usesInputs,
95
+ ...secretNames?.value ? { secretVars: secretNames.value } : {}
96
+ };
97
+ }
98
+ function useLintSources() {
99
+ const resolve = useWorkflowJsonResolver();
100
+ const repos = useRepoSuggestions();
101
+ const secrets = useSecretVariablesResolver();
102
+ const actions = useWorkflowActions();
103
+ return useMemo(() => ({
104
+ resolve,
105
+ repos,
106
+ secrets,
107
+ actions
108
+ }), [
109
+ resolve,
110
+ repos,
111
+ secrets,
112
+ actions
113
+ ]);
114
+ }
115
+ /**
116
+ * The engine's editing once its checks have loaded, rendering again when they have; undefined
117
+ * before then, without an engine that edits, or with `load` false.
118
+ */
119
+ function useLintEditing(load = true) {
120
+ const engine = useLoadedWorkflowEngine();
121
+ const [ready, setReady] = useState(() => !!engine?.isReady());
122
+ useEffect(() => {
123
+ if (!load || !engine?.editing) return;
124
+ if (engine.isReady()) {
125
+ setReady(true);
126
+ return;
127
+ }
128
+ engine.init();
129
+ return engine.onReady((loaded) => {
130
+ if (loaded) setReady(true);
131
+ });
132
+ }, [load, engine]);
133
+ return load && ready ? engine?.editing : void 0;
134
+ }
135
+ //#endregion
136
+ export { HOST_MARKER_OWNER, LINT_OWNER, lintContext, useLintEditing, useLintSources };
@@ -0,0 +1,2 @@
1
+ export declare const LINT_OWNER = "workflowlint";
2
+ export declare const HOST_MARKER_OWNER = "workflowmarkers";
@@ -0,0 +1,5 @@
1
+ //#region src/editor/lintOwner.ts
2
+ var LINT_OWNER = "workflowlint";
3
+ var HOST_MARKER_OWNER = "workflowmarkers";
4
+ //#endregion
5
+ export { HOST_MARKER_OWNER, LINT_OWNER };
@@ -0,0 +1,13 @@
1
+ /** An editor holding one job or step of a workflow, whose inputs live outside its text. */
2
+ export interface NestedWorkflowText {
3
+ /** The workflow's inputs, so a new group is named clear of them. */
4
+ inputs: () => Record<string, unknown>;
5
+ /** Adds the group of inputs a `with:` completion's expressions read. */
6
+ addInputs: (name: string, definition: Record<string, unknown>) => void;
7
+ }
8
+ /** Lets completions reach the workflow around the editor of model `uri`; returns the undo. */
9
+ export declare function publishNestedText(uri: string, text: {
10
+ current: NestedWorkflowText | undefined;
11
+ }): () => void;
12
+ /** The workflow around the editor of model `uri`, or undefined when its text is a whole workflow. */
13
+ export declare function nestedWorkflowText(uri: string): NestedWorkflowText | undefined;
@@ -0,0 +1,15 @@
1
+ //#region src/editor/nestedText.ts
2
+ var published = /* @__PURE__ */ new Map();
3
+ /** Lets completions reach the workflow around the editor of model `uri`; returns the undo. */
4
+ function publishNestedText(uri, text) {
5
+ published.set(uri, text);
6
+ return () => {
7
+ if (published.get(uri) === text) published.delete(uri);
8
+ };
9
+ }
10
+ /** The workflow around the editor of model `uri`, or undefined when its text is a whole workflow. */
11
+ function nestedWorkflowText(uri) {
12
+ return published.get(uri)?.current;
13
+ }
14
+ //#endregion
15
+ export { nestedWorkflowText, publishNestedText };
@@ -0,0 +1,4 @@
1
+ /** Lets revealLines reach the editor showing `path`; returns the function that undoes it. */
2
+ export declare function registerRevealer(path: string, reveal: (start: number, end: number) => void): () => void;
3
+ /** Scrolls the editor showing `path` to 1-based lines `start`–`end` and flashes them. */
4
+ export declare function revealLines(path: string, start: number, end: number): void;
@@ -0,0 +1,15 @@
1
+ //#region src/editor/reveal.ts
2
+ var revealers = /* @__PURE__ */ new Map();
3
+ /** Lets revealLines reach the editor showing `path`; returns the function that undoes it. */
4
+ function registerRevealer(path, reveal) {
5
+ revealers.set(path, reveal);
6
+ return () => {
7
+ if (revealers.get(path) === reveal) revealers.delete(path);
8
+ };
9
+ }
10
+ /** Scrolls the editor showing `path` to 1-based lines `start`–`end` and flashes them. */
11
+ function revealLines(path, start, end) {
12
+ revealers.get(path)?.(start, end);
13
+ }
14
+ //#endregion
15
+ export { registerRevealer, revealLines };
@@ -0,0 +1,12 @@
1
+ /** Editors holding one job's, step's or input's settings, each checked against that part of the workflow schema. */
2
+ export declare const JOB_YAML_PATH = "file:///workflow-job.yaml";
3
+ export declare const STEP_YAML_PATH = "file:///workflow-step.yaml";
4
+ export declare const INPUT_YAML_PATH = "file:///workflow-input.yaml";
5
+ export declare const SETTINGS_YAML_PATH = "file:///workflow-settings.yaml";
6
+ export type JsonSchema = Record<string, unknown>;
7
+ export declare function settingsSchemas(workflow: JsonSchema): {
8
+ job: JsonSchema;
9
+ step: JsonSchema;
10
+ input: JsonSchema;
11
+ settings: JsonSchema;
12
+ };
@@ -0,0 +1,41 @@
1
+ //#region src/editor/settingsYaml.ts
2
+ /** Editors holding one job's, step's or input's settings, each checked against that part of the workflow schema. */
3
+ var JOB_YAML_PATH = "file:///workflow-job.yaml";
4
+ var STEP_YAML_PATH = "file:///workflow-step.yaml";
5
+ var INPUT_YAML_PATH = "file:///workflow-input.yaml";
6
+ var SETTINGS_YAML_PATH = "file:///workflow-settings.yaml";
7
+ var record = (value) => typeof value === "object" && value !== null ? value : {};
8
+ function settingsSchemas(workflow) {
9
+ const patterns = record(record(record(workflow["properties"])["jobs"])["patternProperties"]);
10
+ const job = record(Object.values(patterns)[0]);
11
+ const step = record(record(record(job["properties"])["steps"])["items"]);
12
+ const inputs = record(record(record(workflow["$defs"])["inputs"])["patternProperties"]);
13
+ const input = record(Object.values(inputs)[0]);
14
+ const shared = {
15
+ $schema: workflow["$schema"],
16
+ $defs: workflow["$defs"]
17
+ };
18
+ const { jobs: _jobs, on: _on, ...settings } = record(workflow["properties"]);
19
+ return {
20
+ job: {
21
+ ...job,
22
+ ...shared
23
+ },
24
+ step: {
25
+ ...step,
26
+ ...shared
27
+ },
28
+ input: {
29
+ ...input,
30
+ ...shared
31
+ },
32
+ settings: {
33
+ type: "object",
34
+ properties: settings,
35
+ additionalProperties: false,
36
+ ...shared
37
+ }
38
+ };
39
+ }
40
+ //#endregion
41
+ export { INPUT_YAML_PATH, JOB_YAML_PATH, SETTINGS_YAML_PATH, STEP_YAML_PATH, settingsSchemas };
@@ -27,6 +27,7 @@ function FieldWrapper({ label, tooltipComponent, description, labelPosition = "l
27
27
  children: [/* @__PURE__ */ jsxs("label", {
28
28
  htmlFor: id,
29
29
  id: labelId,
30
+ "data-field-label": true,
30
31
  className: cx("relative", labelPosition === "left" ? "form-label" : "form-label-top", spaceCompact && "form-label-compact"),
31
32
  children: [
32
33
  label,
@@ -1,7 +1,7 @@
1
1
  import { FieldWrapper } from "../FieldWrapper.js";
2
2
  import FormikCustomDropdown_default from "../SpecialTypes/FormikCustomDropdown.js";
3
- import { getPlaceholder } from "../utils/getPlaceholder.js";
4
3
  import { dependentOptions } from "../utils/dependentOptions.js";
4
+ import { getPlaceholder } from "../utils/getPlaceholder.js";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/form/Fields/DropdownField.tsx
7
7
  var missingSetter = () => {
@@ -1,7 +1,7 @@
1
1
  import { useStrings } from "../../components/Provider.js";
2
2
  import { FieldWrapper } from "../FieldWrapper.js";
3
- import { setDurationValidity } from "./durationValidity.js";
4
3
  import { formatDuration, parseDuration } from "../utils/duration.js";
4
+ import { setDurationValidity } from "./durationValidity.js";
5
5
  import cx from "classnames";
6
6
  import { memo, useEffect, useRef, useState } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
package/dist/form/Form.js CHANGED
@@ -9,11 +9,12 @@ import SectionHeader from "../components/SectionHeader.js";
9
9
  import { Table } from "../components/Table.js";
10
10
  import { parseWizardConfig } from "./Wizard/utils.js";
11
11
  import { inputWidth, resolvedFlag } from "./lib.js";
12
- import { useFieldControlProps, useFieldRequired } from "./fieldContext.js";
13
12
  import { resolveFormLayout } from "./layout.js";
14
13
  import { FormLayout, LayoutCSSContext } from "./FormLayout.js";
14
+ import { useFieldControlProps, useFieldRequired } from "./fieldContext.js";
15
15
  import { getValueUsingPath } from "./utils/getValueUsingPath.js";
16
16
  import { FieldRegistryContext, Registry } from "./fieldRegistry.js";
17
+ import { EditingScope, useFormEditing } from "./formEditing.js";
17
18
  import { useParsedOpts } from "./useParsedOpts.js";
18
19
  import { usePrevious } from "./usePrevious.js";
19
20
  import { getParentValue } from "./utils/getParentValue.js";
@@ -209,6 +210,7 @@ function GroupHeader({ children, open, setOpen, title }) {
209
210
  children: [/* @__PURE__ */ jsxs("button", {
210
211
  type: "button",
211
212
  "aria-expanded": open,
213
+ "data-field-label": true,
212
214
  className: "w-full flex items-center text-left transform ease-in transition cursor-pointer",
213
215
  onClick: () => setOpen((open) => !open),
214
216
  children: [/* @__PURE__ */ jsx(FaAngleRight, { className: cx(open && "transform rotate-90 ", "px-1 text-[1.25rem] transition ease-in ") }), /* @__PURE__ */ jsx("span", { children: title })]
@@ -393,6 +395,8 @@ function useFieldParse(optionsField, values, arrayIndex) {
393
395
  }
394
396
  var FormField = React.memo(function FormField({ optionsField, fieldName, values, setFormDirty, setFieldValue, setFieldTouched, parentInfo, labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false }) {
395
397
  const parsedField = useFieldParse(optionsField, values, parentInfo?.arrayIndex);
398
+ const editing = useFormEditing();
399
+ const path = editing ? [...editing.parent, fieldName] : null;
396
400
  const field = asSchemaField(parentInfo ? parsedField : optionsField);
397
401
  if (field === null) return null;
398
402
  const fieldNamePrefix = parentInfo?.fieldNamePrefix || "";
@@ -405,7 +409,11 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
405
409
  if (!fieldObj.name || fieldName === "v3") return null;
406
410
  if (field.hidden === true || !field.type) {
407
411
  if (resolvedFlag(field.ignore) && !workflowForm) deleteFieldByPath(values, fieldObj.name);
408
- return null;
412
+ return editing && path && fieldName !== "$meta" ? /* @__PURE__ */ jsx(editing.Row, {
413
+ path,
414
+ instance: fieldObj.name,
415
+ hidden: true
416
+ }) : null;
409
417
  }
410
418
  let onChange;
411
419
  if (field.one_must_be_true && parentInfo?.onChange) {
@@ -421,7 +429,11 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
421
429
  if (shouldShowField && field.show_if_not !== void 0) shouldShowField = !getParentValue(index, field.show_if_not, values, field);
422
430
  if (!shouldShowField) {
423
431
  if (!workflowForm) deleteFieldByPath(values, fieldObj.name);
424
- return null;
432
+ return editing && path ? /* @__PURE__ */ jsx(editing.Row, {
433
+ path,
434
+ instance: fieldObj.name,
435
+ hidden: true
436
+ }) : null;
425
437
  }
426
438
  }
427
439
  if (field.depends_on && parentInfo?.arrayIndex !== void 0) fieldObj.depends_on = field.depends_on.replace("[index]", `[${parentInfo.arrayIndex}]`);
@@ -436,7 +448,7 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
436
448
  const hideHeader = field.noCollapse;
437
449
  const HeaderElement = hideHeader ? React.Fragment : UncontrolledCollapsiblePanel;
438
450
  const meta = resolveMetaOverrides(field.options, labelPosition, spaceCompact);
439
- return /* @__PURE__ */ jsx("div", {
451
+ const group = /* @__PURE__ */ jsx("div", {
440
452
  className: "flex flex-col w-full",
441
453
  children: /* @__PURE__ */ jsx(HeaderElement, {
442
454
  ...!hideHeader && {
@@ -449,21 +461,30 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
449
461
  },
450
462
  children: /* @__PURE__ */ jsx("div", {
451
463
  className: "w-full",
452
- children: /* @__PURE__ */ jsx(FieldsFromOptions, {
453
- options: field.options ?? {},
454
- setFormDirty,
455
- setFieldValue,
456
- setFieldTouched,
457
- values,
458
- parentInfo,
459
- workflowForm,
460
- labelPosition: meta.labelPosition,
461
- spaceCompact: meta.spaceCompact,
462
- missingFields
464
+ children: /* @__PURE__ */ jsx(EditingScope, {
465
+ editing,
466
+ path,
467
+ children: /* @__PURE__ */ jsx(FieldsFromOptions, {
468
+ options: field.options ?? {},
469
+ setFormDirty,
470
+ setFieldValue,
471
+ setFieldTouched,
472
+ values,
473
+ parentInfo,
474
+ workflowForm,
475
+ labelPosition: meta.labelPosition,
476
+ spaceCompact: meta.spaceCompact,
477
+ missingFields
478
+ })
463
479
  })
464
480
  })
465
481
  })
466
482
  });
483
+ return editing && path ? /* @__PURE__ */ jsx(editing.Row, {
484
+ path,
485
+ instance: fieldObj.name,
486
+ children: group
487
+ }) : group;
467
488
  }
468
489
  if (typeof field.type === "object" && field.depends_on) {
469
490
  const parentValue = getValueUsingPath(values, field.depends_on);
@@ -489,7 +510,7 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
489
510
  if (Array.isArray(field.secondaryField)) fieldObj.secondaryField = field.secondaryField.map((secondaryField) => fieldNamePrefix + secondaryField);
490
511
  else fieldObj.secondaryField = fieldNamePrefix + field.secondaryField;
491
512
  }
492
- return /* @__PURE__ */ jsx("div", {
513
+ const input = /* @__PURE__ */ jsx("div", {
493
514
  className: cx("flex w-full", labelPosition === "left" ? "mb-[15px]" : "mb-[5px]"),
494
515
  children: /* @__PURE__ */ jsx(InputField, {
495
516
  field: fieldObj,
@@ -508,6 +529,17 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
508
529
  workflowForm
509
530
  })
510
531
  });
532
+ if (!editing || !path) return input;
533
+ const holds = fieldObj.type === "object" || fieldObj.type === "list";
534
+ return /* @__PURE__ */ jsx(editing.Row, {
535
+ path,
536
+ instance: fieldObj.name,
537
+ children: /* @__PURE__ */ jsx(EditingScope, {
538
+ editing,
539
+ path: holds ? path : null,
540
+ children: input
541
+ })
542
+ });
511
543
  }, (prev, next) => {
512
544
  if (!deepEqual(prev.optionsField, next.optionsField)) return false;
513
545
  if (prev.values !== next.values) return false;
@@ -519,6 +551,7 @@ var FormField = React.memo(function FormField({ optionsField, fieldName, values,
519
551
  return true;
520
552
  });
521
553
  function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue, setFieldTouched, parentInfo, labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false }) {
554
+ const editing = useFormEditing();
522
555
  const chosen = useChosenLabelPosition(options);
523
556
  const wizard = parseWizardConfig(options);
524
557
  if (wizard) return /* @__PURE__ */ jsx(ChosenLabelPosition.Provider, {
@@ -556,22 +589,27 @@ function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue,
556
589
  spaceCompact,
557
590
  workflowForm
558
591
  }, fieldName);
592
+ const add = editing ? /* @__PURE__ */ jsx(editing.Add, { parent: editing.parent }) : null;
559
593
  const layout = resolveFormLayout(asRecord(options["$meta"])["layout"], names.filter((name) => !name.startsWith("$") && asSchemaField(options[name])));
560
594
  if (layout.layout) return /* @__PURE__ */ jsx(ChosenLabelPosition.Provider, {
561
595
  value: chosen,
562
596
  children: /* @__PURE__ */ jsxs("div", {
563
597
  className: "flex min-w-0 flex-col gap-4",
564
- children: [/* @__PURE__ */ jsx(FormLayout, {
565
- layout: layout.layout,
566
- renderField: (name) => fieldOf(name, true)
567
- }), layout.remaining.map((name) => fieldOf(name, true))]
598
+ children: [
599
+ /* @__PURE__ */ jsx(FormLayout, {
600
+ layout: layout.layout,
601
+ renderField: (name) => fieldOf(name, true)
602
+ }),
603
+ layout.remaining.map((name) => fieldOf(name, true)),
604
+ add
605
+ ]
568
606
  })
569
607
  });
570
608
  return /* @__PURE__ */ jsx(ChosenLabelPosition.Provider, {
571
609
  value: chosen,
572
- children: flows ? /* @__PURE__ */ jsx("div", {
610
+ children: flows ? /* @__PURE__ */ jsxs("div", {
573
611
  className: "@container/inputs -mx-2 flex flex-wrap items-start",
574
- children: columns.map((column) => {
612
+ children: [columns.map((column) => {
575
613
  const width = inputWidth(asRecord(options[column.head])["width"]);
576
614
  return /* @__PURE__ */ jsx("div", {
577
615
  "data-input-cell": true,
@@ -587,8 +625,11 @@ function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue,
587
625
  }, name);
588
626
  })
589
627
  }, column.head);
590
- })
591
- }) : names.map((name) => fieldOf(name))
628
+ }), add && /* @__PURE__ */ jsx("div", {
629
+ className: "order-2 w-full px-2",
630
+ children: add
631
+ })]
632
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [names.map((name) => fieldOf(name, void 0)), add] })
592
633
  });
593
634
  }
594
635
  var noop = () => {};
@@ -2,16 +2,21 @@ import { useStrings, useWorkflowEngine } from "../../components/Provider.js";
2
2
  import { copyBounds, stepText } from "./utils.js";
3
3
  import { initializeValues } from "../lib.js";
4
4
  import { getValueUsingPath } from "../utils/getValueUsingPath.js";
5
+ import { useFormEditing } from "../formEditing.js";
5
6
  import { useWizardState } from "./useWizardState.js";
6
7
  import { WizardNavigation } from "./WizardNavigation.js";
7
8
  import { WizardStepContent } from "./WizardStepContent.js";
8
9
  import { WizardStepIndicator } from "./WizardStepIndicator.js";
9
- import { useCallback, useEffect, useMemo, useRef } from "react";
10
+ import { WizardPagesContext } from "./wizardPages.js";
11
+ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
10
12
  import { jsx, jsxs } from "react/jsx-runtime";
11
13
  import { useFormikContext } from "formik";
12
14
  //#region src/form/Wizard/WizardContainer.tsx
13
15
  function WizardContainer({ wizardConfig, values, onChange, onSubmit, className = "", labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false, nested = false, fieldNamePrefix = "", setFieldValue, setFieldTouched }) {
14
16
  const { validateForm, setTouched, touched, initialValues } = useFormikContext();
17
+ const editing = useFormEditing();
18
+ const pages = useContext(WizardPagesContext);
19
+ const [ownPage, setOwnPage] = useState(0);
15
20
  const { form: strings } = useStrings();
16
21
  const engine = useWorkflowEngine();
17
22
  const { config, steps, stepOrder } = wizardConfig;
@@ -197,19 +202,39 @@ function WizardContainer({ wizardConfig, values, onChange, onSubmit, className =
197
202
  touched,
198
203
  onSubmit
199
204
  ]);
205
+ const last = Math.max(shown.order.length - 1, 0);
206
+ const wizardKey = editing ? JSON.stringify(editing.parent) : "";
207
+ const editIndex = Math.min(Math.max(pages ? pages.page(wizardKey) : ownPage, 0), last);
208
+ const turn = (to) => {
209
+ const next = Math.max(to, 0);
210
+ if (pages) pages.setPage(wizardKey, next);
211
+ else setOwnPage(next);
212
+ };
213
+ const drawn = shown.order.length;
214
+ const setCount = pages?.setCount;
215
+ useEffect(() => {
216
+ setCount?.(wizardKey, drawn);
217
+ }, [
218
+ setCount,
219
+ wizardKey,
220
+ drawn
221
+ ]);
200
222
  const shownBefore = useRef(shown.order);
201
223
  useEffect(() => {
202
224
  shownBefore.current = shown.order;
203
225
  });
204
- const pageKey = shown.byKey[currentStep] ? currentStep : nearestPage(currentStep, shown.order, shownBefore.current);
226
+ const runKey = shown.byKey[currentStep] ? currentStep : nearestPage(currentStep, shown.order, shownBefore.current);
205
227
  useEffect(() => {
206
- if (pageKey && pageKey !== currentStep) showStep(pageKey);
228
+ if (!editing && runKey && runKey !== currentStep) showStep(runKey);
207
229
  }, [
208
- pageKey,
230
+ editing,
231
+ runKey,
209
232
  currentStep,
210
233
  showStep
211
234
  ]);
235
+ const pageKey = editing ? shown.order[editIndex] ?? "" : runKey;
212
236
  const page = shown.byKey[pageKey];
237
+ const show = (key, index) => editing ? turn(index) : showStep(key);
213
238
  const copiesOf = (step) => {
214
239
  const rows = getValueUsingPath(values, `${fieldNamePrefix}${step}`);
215
240
  return Array.isArray(rows) ? rows : [];
@@ -220,13 +245,15 @@ function WizardContainer({ wizardConfig, values, onChange, onSubmit, className =
220
245
  const kept = Array.from({ length: shownPage.count ?? rows.length }, (_, i) => rows[i] ?? initializeValues(options, {}) ?? {});
221
246
  const aside = setAside.current[`${fieldNamePrefix}${shownPage.step}`] ?? [];
222
247
  setFieldValue(`${fieldNamePrefix}${shownPage.step}`, [...kept, takeAside(aside, kept.length) ?? initializeValues(options, {}) ?? {}]);
223
- showStep(`${shownPage.step}[${kept.length}]`);
248
+ const at = shown.order.indexOf(pageKey) + (shownPage.copy === void 0 ? 0 : 1);
249
+ show(`${shownPage.step}[${kept.length}]`, at);
224
250
  };
225
251
  const removeCopy = (shownPage) => {
226
252
  const copy = shownPage.copy ?? 0;
227
253
  const rows = copiesOf(shownPage.step).filter((_, i) => i !== copy);
228
254
  setFieldValue(`${fieldNamePrefix}${shownPage.step}`, rows);
229
- showStep(rows.length > 0 ? `${shownPage.step}[${Math.max(copy - 1, 0)}]` : shownPage.step);
255
+ const at = shown.order.indexOf(pageKey) - (copy > 0 ? 1 : 0);
256
+ show(rows.length > 0 ? `${shownPage.step}[${Math.max(copy - 1, 0)}]` : shownPage.step, at);
230
257
  };
231
258
  const rawTitle = page ? steps[page.step]?.title : void 0;
232
259
  const titledPerCopy = Array.isArray(rawTitle) || typeof rawTitle === "string" && rawTitle.includes("${{");
@@ -264,6 +291,39 @@ function WizardContainer({ wizardConfig, values, onChange, onSubmit, className =
264
291
  setFieldTouched
265
292
  }), copies]
266
293
  }) : null;
294
+ if (editing) return /* @__PURE__ */ jsxs("div", {
295
+ className,
296
+ children: [
297
+ navigation.showSteps !== false && /* @__PURE__ */ jsx(WizardStepIndicator, {
298
+ stepOrder: shown.order,
299
+ currentStep: pageKey,
300
+ steps: configs,
301
+ visitedSteps: new Set(shown.order),
302
+ invalidSteps: /* @__PURE__ */ new Set(),
303
+ onStepClick: (key) => {
304
+ turn(shown.order.indexOf(key));
305
+ return true;
306
+ },
307
+ allowJump: true,
308
+ hideStepNumbers: navigation.hideStepNumbers
309
+ }),
310
+ content,
311
+ /* @__PURE__ */ jsx(WizardNavigation, {
312
+ isLastStep: editIndex === last,
313
+ canGoBack: editIndex > 0,
314
+ isCurrentStepValid: true,
315
+ nextLabel: page?.config.nextLabel,
316
+ prevLabel: page?.config.prevLabel,
317
+ submitLabel: config.submitLabel || "Execute",
318
+ onNext: async () => {
319
+ turn(editIndex + 1);
320
+ return true;
321
+ },
322
+ onPrevious: () => turn(editIndex - 1),
323
+ onSubmit: nested ? void 0 : async () => {}
324
+ })
325
+ ]
326
+ });
267
327
  return /* @__PURE__ */ jsxs("div", {
268
328
  className,
269
329
  children: [