@parallelworks/ui 0.38.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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/ai/components/ChatInput.js +8 -23
  3. package/dist/ai/components/chatDraft.d.ts +7 -0
  4. package/dist/ai/components/chatDraft.js +66 -0
  5. package/dist/editing.d.ts +2 -0
  6. package/dist/editor/Monaco.js +1 -1
  7. package/dist/editor/index.js +1 -1
  8. package/dist/editor/lintContext.js +15 -3
  9. package/dist/graph/DependencyGraph.d.ts +8 -1
  10. package/dist/graph/DependencyGraph.js +433 -204
  11. package/dist/graph/GraphEditor.d.ts +27 -0
  12. package/dist/graph/GraphEditor.js +1543 -0
  13. package/dist/graph/GraphEditorDialogs.d.ts +87 -0
  14. package/dist/graph/GraphEditorDialogs.js +1808 -0
  15. package/dist/graph/InputDialog.js +4 -4
  16. package/dist/graph/InputsEditor.js +2 -2
  17. package/dist/graph/JobSummary.d.ts +3 -0
  18. package/dist/graph/JobSummary.js +34 -21
  19. package/dist/graph/MatrixGroup.d.ts +2 -1
  20. package/dist/graph/MatrixGroup.js +10 -3
  21. package/dist/graph/WorkflowSettingsDialog.d.ts +21 -0
  22. package/dist/graph/WorkflowSettingsDialog.js +670 -0
  23. package/dist/graph/editorFields.js +144 -3
  24. package/dist/graph/editorRows.d.ts +58 -0
  25. package/dist/graph/editorRows.js +460 -0
  26. package/dist/graph/editorStrings.d.ts +1 -0
  27. package/dist/graph/editorStrings.js +1 -0
  28. package/dist/graph/expressionRefs.js +39 -0
  29. package/dist/graph/fieldProblems.js +36 -2
  30. package/dist/graph/graphEditorState.d.ts +191 -0
  31. package/dist/graph/graphEditorState.js +74 -0
  32. package/dist/graph/index.d.ts +3 -0
  33. package/dist/graph/index.js +5 -2
  34. package/dist/graph/inputLayout.js +1 -1
  35. package/dist/graph/inputRefs.js +196 -4
  36. package/dist/graph/refusalText.js +13 -0
  37. package/dist/graph/stepWith.d.ts +38 -0
  38. package/dist/graph/stepWith.js +536 -0
  39. package/dist/styles.css +1 -1
  40. package/package.json +1 -1
@@ -9,18 +9,20 @@ import { Button } from "../components/Button.js";
9
9
  import { ghostButtonClasses, primaryButtonClasses } from "../components/ghostButton.js";
10
10
  import { FilterPill } from "../components/FilterPill.js";
11
11
  import { IconButton } from "../components/IconButton.js";
12
- import { Textarea } from "../components/Input.js";
12
+ import { Input, Textarea } from "../components/Input.js";
13
13
  import { Toggle } from "../components/Toggle.js";
14
14
  import { useGraphEditorStrings } from "./editorStrings.js";
15
- import { useFieldLint } from "./fieldProblems.js";
16
15
  import { asRecord } from "./records.js";
16
+ import { useFieldLint } from "./fieldProblems.js";
17
17
  import { SuggestionInput } from "./SuggestionInput.js";
18
18
  import cx from "classnames";
19
19
  import { createContext, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
20
20
  import { jsx, jsxs } from "react/jsx-runtime";
21
21
  //#region src/graph/editorFields.tsx
22
22
  var EXPRESSION = /^\$\{\{.*\}\}$/;
23
+ var ENV_KEY = /^[a-zA-Z0-9_]+$/;
23
24
  var EMPTY_EXPRESSION = /^\$\{\{\s*\}\}$/;
25
+ var DURATION = /^(\d+)(d|h|m|s)?$/;
24
26
  function withoutUndefined(value) {
25
27
  return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
26
28
  }
@@ -59,10 +61,36 @@ function diffPatch(original, next) {
59
61
  function isEmptyPatch(patch) {
60
62
  return Object.keys(patch.set).length === 0 && patch.unset.length === 0;
61
63
  }
64
+ function conditionText(value) {
65
+ return typeof value === "boolean" ? String(value) : text(value);
66
+ }
67
+ function parseCondition(value) {
68
+ const trimmed = value.trim();
69
+ if (!trimmed) return;
70
+ if (trimmed === "true" || trimmed === "false") return trimmed === "true";
71
+ return trimmed;
72
+ }
73
+ function durationError(value, t, pattern = DURATION) {
74
+ const trimmed = value.trim();
75
+ return !trimmed || pattern.test(trimmed) || EXPRESSION.test(trimmed) ? void 0 : t.invalidDuration;
76
+ }
62
77
  function expressionError(value, t) {
63
78
  const trimmed = value.trim();
64
79
  return !trimmed || EXPRESSION.test(trimmed) && !EMPTY_EXPRESSION.test(trimmed) ? void 0 : t.invalidExpressionValue;
65
80
  }
81
+ function orUndefined(value) {
82
+ return value.trim() ? value.trim() : void 0;
83
+ }
84
+ function countError(value, min, message) {
85
+ const trimmed = value.trim();
86
+ if (!trimmed || EXPRESSION.test(trimmed)) return;
87
+ return /^\d+$/.test(trimmed) && Number(trimmed) >= min ? void 0 : message;
88
+ }
89
+ function parseCount(value) {
90
+ const trimmed = value.trim();
91
+ if (!trimmed) return;
92
+ return /^\d+$/.test(trimmed) ? Number(trimmed) : trimmed;
93
+ }
66
94
  function parseJson(value) {
67
95
  try {
68
96
  return {
@@ -76,6 +104,39 @@ function parseJson(value) {
76
104
  function unchangedValue(text, original, nested = false) {
77
105
  return original !== void 0 && text === (nested ? JSON.stringify(original) : String(original));
78
106
  }
107
+ /** Rows for a map; with `nested` false a non-scalar is shown as its JSON text and saved as a string. */
108
+ function rowsFrom(value, nested = true) {
109
+ return Object.entries(asRecord(value)).map(([key, item]) => isScalar(item) ? {
110
+ key,
111
+ value: String(item),
112
+ original: item
113
+ } : nested ? {
114
+ key,
115
+ value: JSON.stringify(item),
116
+ nested: true,
117
+ original: item
118
+ } : {
119
+ key,
120
+ value: JSON.stringify(item) ?? ""
121
+ });
122
+ }
123
+ function rowsTo(rows, parse) {
124
+ if (rows.length === 0) return;
125
+ return Object.fromEntries(rows.map((row) => [row.key.trim(), unchangedValue(row.value, row.original, row.nested) ? row.original : row.nested ? parseJson(row.value).value : parse(row.value)]));
126
+ }
127
+ function rowsError(rows, keyPattern, keyMessage, t, valueError) {
128
+ const keys = rows.map((row) => row.key.trim());
129
+ for (const [i, row] of rows.entries()) {
130
+ const key = keys[i] ?? "";
131
+ if (!keyPattern.test(key)) return keyMessage;
132
+ if (keys.indexOf(key) !== i) return t.duplicateKey;
133
+ const error = row.nested ? parseJson(row.value).ok ? void 0 : t.invalidJson : valueError?.(row.value);
134
+ if (error) return error;
135
+ }
136
+ }
137
+ function flagOf(value) {
138
+ return typeof value === "boolean" || typeof value === "string" ? value : void 0;
139
+ }
79
140
  function flagError(value, t) {
80
141
  return typeof value === "string" ? expressionError(value, t) : void 0;
81
142
  }
@@ -234,6 +295,86 @@ function AddRowButton({ label, onClick }) {
234
295
  children: label
235
296
  }) });
236
297
  }
298
+ function KeyValueEditor({ rows, onChange, error, valuePlaceholder, keyPlaceholder, addLabel, valueSuggestions = [], lint = false }) {
299
+ const t = useGraphEditorStrings();
300
+ return /* @__PURE__ */ jsxs("div", {
301
+ className: "flex flex-col gap-2",
302
+ children: [
303
+ withPositionKeys(rows).map(({ key, item: row }, i) => /* @__PURE__ */ jsx(KeyValueRow, {
304
+ row,
305
+ onChange: (next) => onChange(updateAt(rows, i, next)),
306
+ onRemove: () => onChange(removeAt(rows, i)),
307
+ keyPlaceholder: keyPlaceholder ?? t.key,
308
+ valuePlaceholder: valuePlaceholder ?? t.value,
309
+ valueSuggestions,
310
+ lint
311
+ }, key)),
312
+ /* @__PURE__ */ jsx(FieldError, { message: error }),
313
+ /* @__PURE__ */ jsx(AddRowButton, {
314
+ label: addLabel ?? t.addRow,
315
+ onClick: () => onChange([...rows, {
316
+ key: "",
317
+ value: ""
318
+ }])
319
+ })
320
+ ]
321
+ });
322
+ }
323
+ function KeyValueRow({ row, onChange, onRemove, keyPlaceholder, valuePlaceholder, valueSuggestions, lint: linted }) {
324
+ const t = useGraphEditorStrings();
325
+ const id = useId();
326
+ const lint = useFieldLint(linted ? row.key.trim() || void 0 : void 0, id, row.value, (value) => onChange({
327
+ ...row,
328
+ value
329
+ }));
330
+ return /* @__PURE__ */ jsxs("div", {
331
+ className: "flex flex-col gap-1.5",
332
+ children: [
333
+ /* @__PURE__ */ jsxs("div", {
334
+ className: "flex items-center gap-2",
335
+ children: [
336
+ /* @__PURE__ */ jsx("div", {
337
+ className: "w-2/5",
338
+ children: /* @__PURE__ */ jsx(Input, {
339
+ mono: true,
340
+ "aria-label": t.key,
341
+ placeholder: keyPlaceholder,
342
+ value: row.key,
343
+ onChange: (e) => onChange({
344
+ ...row,
345
+ key: e.target.value
346
+ })
347
+ })
348
+ }),
349
+ /* @__PURE__ */ jsx("div", {
350
+ className: "flex-1",
351
+ title: row.nested ? t.jsonValue : void 0,
352
+ children: /* @__PURE__ */ jsx(SuggestionInput, {
353
+ id,
354
+ ariaLabel: t.value,
355
+ placeholder: valuePlaceholder,
356
+ value: row.value,
357
+ suggestions: valueSuggestions.map((value) => ({ value })),
358
+ onChange: (value) => onChange({
359
+ ...row,
360
+ value
361
+ })
362
+ })
363
+ }),
364
+ /* @__PURE__ */ jsx(IconButton, {
365
+ icon: /* @__PURE__ */ jsx(IoMdClose, { className: "h-4 w-4" }),
366
+ label: t.removeRow,
367
+ variant: "ghost",
368
+ size: "sm",
369
+ onClick: onRemove
370
+ })
371
+ ]
372
+ }),
373
+ /* @__PURE__ */ jsx(FieldError, { message: lint.message }),
374
+ lint.fixes
375
+ ]
376
+ });
377
+ }
237
378
  function updateAt(items, index, item) {
238
379
  return items.map((current, i) => i === index ? item : current);
239
380
  }
@@ -391,4 +532,4 @@ function DialogShell({ title, onClose, onSubmit, saveDisabled, dirty, locked = f
391
532
  });
392
533
  }
393
534
  //#endregion
394
- export { AddRowButton, ChoiceButtons, DialogShell, EXPRESSION, ExpressionToggle, FieldError, FieldLabel, GROWING_AREA, GrowingTextarea, KeyHint, LabelledField, Section, SectionMemory, StringListEditor, SuggestedInput, ToggleField, asRecord, diffPatch, expressionError, flagError, flagValue, isEmptyPatch, isScalar, labelled, openOnAddOf, parseJson, parseScalar, removeAt, sameValue, text, unchangedValue, updateAt, useGrowingArea, useSectionMemory, withoutUndefined };
535
+ export { AddRowButton, ChoiceButtons, DURATION, DialogShell, ENV_KEY, EXPRESSION, ExpressionToggle, FieldError, FieldLabel, GROWING_AREA, GrowingTextarea, KeyHint, KeyValueEditor, LabelledField, Section, SectionMemory, StringListEditor, SuggestedInput, ToggleField, asRecord, conditionText, countError, diffPatch, durationError, expressionError, flagError, flagOf, flagValue, isEmptyPatch, isScalar, labelled, openOnAddOf, orUndefined, parseCondition, parseCount, parseJson, parseScalar, removeAt, rowsError, rowsFrom, rowsTo, sameValue, text, unchangedValue, updateAt, useGrowingArea, useSectionMemory, withoutUndefined };
@@ -0,0 +1,58 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type Point } from './editorPrimitives.js';
3
+ import { type EdgeSelection } from './graphEditorState.js';
4
+ /** Drop indicators, the connection line and the selection, drawn in the graph's own coordinates. */
5
+ export declare function GraphEditorCanvas(): import("react").JSX.Element | null;
6
+ /** A multi-job box's handle for moving all its jobs at once. */
7
+ export declare function BoxGrip({ jobs }: {
8
+ jobs: string[];
9
+ }): import("react").JSX.Element | null;
10
+ /** A job row made draggable, with its menu and matrix badge; shift-click selects it. */
11
+ export declare function EditableJobRow({ job, label, badge, children, }: {
12
+ job: string;
13
+ label: string;
14
+ badge?: boolean;
15
+ children: ReactNode;
16
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
17
+ /**
18
+ * A node's connector circles, where the graph draws its connector dots. Drag from the
19
+ * right one to add jobs that depend on the node, from the left one to add what it needs.
20
+ */
21
+ export declare function NodePorts({ jobs, left, right, drawLeft, drawRight, zIndex, }: {
22
+ jobs: string[];
23
+ left: Point;
24
+ right: Point;
25
+ drawLeft: boolean;
26
+ drawRight: boolean;
27
+ zIndex: number;
28
+ }): import("react").JSX.Element | null;
29
+ /** A step row made draggable within its job, with its menu. */
30
+ export declare function EditableStepRow({ job, index, label, children, }: {
31
+ job: string;
32
+ index: number;
33
+ label: string;
34
+ children: ReactNode;
35
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
36
+ export declare function AddStepButton({ job }: {
37
+ job: string;
38
+ }): import("react").JSX.Element | null;
39
+ /** Connector hit area: a click selects the dependency, shift adds it, a right-click opens its menu. */
40
+ export declare function EdgeHitPath({ d, edge }: {
41
+ d: string;
42
+ edge: EdgeSelection;
43
+ }): import("react").JSX.Element | null;
44
+ /** A selected connector's two ends; dragging one onto another job or node moves that end there. */
45
+ export declare function EdgeEnds({ edge, start, end, zIndex, }: {
46
+ edge: EdgeSelection;
47
+ start: Point;
48
+ end: Point;
49
+ zIndex: number;
50
+ }): import("react").JSX.Element | null;
51
+ /** Shown in place of the graph when the workflow has no jobs yet. */
52
+ /** The canvas of a graph with no jobs, which takes the graph's keys, so undo and paste still work. */
53
+ export declare function EmptyGraphEditor({ containerRef, overlays, height, removeBorder, }: {
54
+ containerRef: (el: HTMLDivElement | null) => void;
55
+ overlays: ReactNode;
56
+ height: string;
57
+ removeBorder?: boolean | undefined;
58
+ }): import("react").JSX.Element;