@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.
- package/README.md +16 -0
- package/dist/ai/agent/permissions.d.ts +2 -2
- package/dist/ai/agent/permissions.js +3 -1
- package/dist/ai/components/ChatSidebar.js +7 -4
- package/dist/ai/components/agent/PermissionPicker.js +1 -0
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +2 -2
- package/dist/ai/strings.d.ts +2 -0
- package/dist/ai/strings.js +2 -0
- package/dist/ai/ui/sidebar.d.ts +6 -0
- package/dist/ai/ui/sidebar.js +15 -1
- package/dist/components/Dropdown.d.ts +2 -0
- package/dist/components/Dropdown.js +7 -2
- package/dist/components/Input.d.ts +11 -6
- package/dist/components/Input.js +93 -74
- package/dist/components/Provider.d.ts +31 -0
- package/dist/components/Provider.js +50 -2
- package/dist/editing.d.ts +404 -0
- package/dist/editor/EditorField.js +1 -1
- package/dist/editor/Monaco.d.ts +15 -1
- package/dist/editor/Monaco.js +117 -13
- package/dist/editor/editorModel.d.ts +5 -0
- package/dist/editor/editorModel.js +23 -0
- package/dist/editor/index.d.ts +4 -0
- package/dist/editor/index.js +5 -1
- package/dist/editor/lintContext.d.ts +21 -0
- package/dist/editor/lintContext.js +136 -0
- package/dist/editor/lintOwner.d.ts +2 -0
- package/dist/editor/lintOwner.js +5 -0
- package/dist/editor/nestedText.d.ts +13 -0
- package/dist/editor/nestedText.js +15 -0
- package/dist/editor/reveal.d.ts +4 -0
- package/dist/editor/reveal.js +15 -0
- package/dist/editor/settingsYaml.d.ts +12 -0
- package/dist/editor/settingsYaml.js +41 -0
- package/dist/form/FieldWrapper.js +1 -0
- package/dist/form/Fields/DropdownField.js +1 -1
- package/dist/form/Fields/DurationField.js +1 -1
- package/dist/form/Form.js +65 -24
- package/dist/form/Wizard/WizardContainer.js +66 -6
- package/dist/form/Wizard/WizardStepContent.js +23 -10
- package/dist/form/Wizard/wizardPages.d.ts +12 -0
- package/dist/form/Wizard/wizardPages.js +5 -0
- package/dist/form/fieldRegistry.js +1 -1
- package/dist/form/formEditing.d.ts +25 -0
- package/dist/form/formEditing.js +20 -0
- package/dist/form/index.js +5 -5
- package/dist/graph/InputDialog.d.ts +80 -0
- package/dist/graph/InputDialog.js +2110 -0
- package/dist/graph/InputsEditor.d.ts +12 -0
- package/dist/graph/InputsEditor.js +1594 -0
- package/dist/graph/ProblemsButton.d.ts +9 -0
- package/dist/graph/ProblemsButton.js +61 -0
- package/dist/graph/ShortcutsButton.d.ts +15 -0
- package/dist/graph/ShortcutsButton.js +56 -0
- package/dist/graph/SuggestionInput.d.ts +18 -0
- package/dist/graph/SuggestionInput.js +29 -0
- package/dist/graph/ToolbarPopover.d.ts +8 -0
- package/dist/graph/ToolbarPopover.js +71 -0
- package/dist/graph/editorApi.d.ts +51 -0
- package/dist/graph/editorApi.js +8 -0
- package/dist/graph/editorChrome.d.ts +27 -0
- package/dist/graph/editorChrome.js +75 -0
- package/dist/graph/editorFields.d.ts +180 -0
- package/dist/graph/editorFields.js +394 -0
- package/dist/graph/editorPrimitives.d.ts +60 -0
- package/dist/graph/editorPrimitives.js +150 -0
- package/dist/graph/editorStrings.d.ts +695 -0
- package/dist/graph/editorStrings.js +707 -0
- package/dist/graph/expressionRefs.d.ts +12 -0
- package/dist/graph/fieldProblems.d.ts +32 -0
- package/dist/graph/fieldProblems.js +77 -0
- package/dist/graph/index.d.ts +7 -0
- package/dist/graph/index.js +6 -1
- package/dist/graph/inputLayout.d.ts +25 -0
- package/dist/graph/inputLayout.js +337 -0
- package/dist/graph/inputPages.d.ts +33 -0
- package/dist/graph/inputPages.js +186 -0
- package/dist/graph/inputRefs.d.ts +103 -0
- package/dist/graph/inputRefs.js +196 -0
- package/dist/graph/inputRows.d.ts +43 -0
- package/dist/graph/inputRows.js +65 -0
- package/dist/graph/records.d.ts +3 -0
- package/dist/graph/records.js +7 -0
- package/dist/graph/refusalText.d.ts +6 -0
- package/dist/graph/settingsViews.d.ts +26 -0
- package/dist/graph/settingsViews.js +62 -0
- package/dist/graph/usesInputs.d.ts +17 -0
- package/dist/graph/usesInputs.js +40 -0
- package/dist/icons/bi/BiRedo.js +16 -0
- package/dist/icons/bi/BiUndo.js +16 -0
- package/dist/icons/lu/LuBraces.js +27 -0
- package/dist/icons/lu/LuCode.js +27 -0
- package/dist/icons/lu/LuKeyboard.js +71 -0
- package/dist/icons/lu/LuNetwork.js +63 -0
- package/dist/icons/lu/LuTextCursorInput.js +45 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +8 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/list/ActionToolbar.d.ts +2 -0
- package/dist/list/ActionToolbar.js +18 -5
- package/dist/list/RowContextMenu.d.ts +10 -1
- package/dist/list/RowContextMenu.js +185 -30
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EditorProblem } from './editorApi.js';
|
|
2
|
+
/** A problem as a pane lists it; `pick` shows where it is, when an open pane can. */
|
|
3
|
+
export interface ListedProblem extends EditorProblem {
|
|
4
|
+
pick?: (() => void) | undefined;
|
|
5
|
+
}
|
|
6
|
+
/** A toolbar's count of the problems it lists, which opens the list. */
|
|
7
|
+
export declare function ProblemsButton({ problems }: {
|
|
8
|
+
problems: ListedProblem[];
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TbAlertTriangle } from "../icons/tb/TbAlertTriangle.js";
|
|
2
|
+
import { keyedByContent } from "../components/keys.js";
|
|
3
|
+
import { TOOLTIP_ID } from "../components/Tooltip.js";
|
|
4
|
+
import { useGraphEditorStrings } from "./editorStrings.js";
|
|
5
|
+
import { ToolbarPopover } from "./ToolbarPopover.js";
|
|
6
|
+
import { useRef, useState } from "react";
|
|
7
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/graph/ProblemsButton.tsx
|
|
9
|
+
/** A toolbar's count of the problems it lists, which opens the list. */
|
|
10
|
+
function ProblemsButton({ problems }) {
|
|
11
|
+
const t = useGraphEditorStrings();
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
const button = useRef(null);
|
|
14
|
+
if (problems.length === 0) return null;
|
|
15
|
+
const label = t.problemCount(problems.length);
|
|
16
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
|
|
17
|
+
ref: button,
|
|
18
|
+
type: "button",
|
|
19
|
+
"aria-label": label,
|
|
20
|
+
"aria-expanded": open,
|
|
21
|
+
...open ? {} : { "data-tooltip-id": TOOLTIP_ID },
|
|
22
|
+
"data-tooltip-content": label,
|
|
23
|
+
className: "flex h-7 cursor-pointer items-center gap-1 rounded px-2 text-xs font-medium text-(--theme-error) theme-hover",
|
|
24
|
+
onClick: () => setOpen((current) => !current),
|
|
25
|
+
children: [/* @__PURE__ */ jsx(TbAlertTriangle, { className: "h-3.5 w-3.5" }), problems.length]
|
|
26
|
+
}), open && /* @__PURE__ */ jsxs(ToolbarPopover, {
|
|
27
|
+
anchor: button,
|
|
28
|
+
label,
|
|
29
|
+
onClose: () => setOpen(false),
|
|
30
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
31
|
+
className: "mb-2 text-[0.8125rem] font-semibold",
|
|
32
|
+
children: label
|
|
33
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
34
|
+
className: "flex flex-col gap-0.5",
|
|
35
|
+
children: keyedByContent(problems, (problem) => `${problem.line}:${problem.message}`).map(({ key, item: problem }) => {
|
|
36
|
+
const { pick } = problem;
|
|
37
|
+
const text = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
38
|
+
className: "mr-1.5 theme-muted-text",
|
|
39
|
+
children: t.problemLine(problem.line)
|
|
40
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
41
|
+
className: "text-(--theme-error)",
|
|
42
|
+
children: problem.message
|
|
43
|
+
})] });
|
|
44
|
+
return /* @__PURE__ */ jsx("li", { children: pick ? /* @__PURE__ */ jsx("button", {
|
|
45
|
+
type: "button",
|
|
46
|
+
className: "w-full cursor-pointer rounded px-1.5 py-1 text-left theme-hover",
|
|
47
|
+
onClick: () => {
|
|
48
|
+
setOpen(false);
|
|
49
|
+
pick();
|
|
50
|
+
},
|
|
51
|
+
children: text
|
|
52
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
53
|
+
className: "px-1.5 py-1",
|
|
54
|
+
children: text
|
|
55
|
+
}) }, key);
|
|
56
|
+
})
|
|
57
|
+
})]
|
|
58
|
+
})] });
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { ProblemsButton };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** What a gesture or key does; each combo is a set of keys, and a second combo does the same. */
|
|
2
|
+
export interface Shortcut {
|
|
3
|
+
combos: string[][];
|
|
4
|
+
does: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ShortcutGroup {
|
|
7
|
+
title: string;
|
|
8
|
+
shortcuts: Shortcut[];
|
|
9
|
+
}
|
|
10
|
+
export declare const MOD_KEY: string;
|
|
11
|
+
export declare const ALT_KEY: string;
|
|
12
|
+
/** A toolbar button that opens the list of gestures and keys a pane takes. */
|
|
13
|
+
export declare function ShortcutsButton({ groups }: {
|
|
14
|
+
groups: ShortcutGroup[];
|
|
15
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { LuKeyboard } from "../icons/lu/LuKeyboard.js";
|
|
2
|
+
import { IconButton } from "../components/IconButton.js";
|
|
3
|
+
import { useGraphEditorStrings } from "./editorStrings.js";
|
|
4
|
+
import { ToolbarPopover } from "./ToolbarPopover.js";
|
|
5
|
+
import { Fragment, useRef, useState } from "react";
|
|
6
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/graph/ShortcutsButton.tsx
|
|
8
|
+
var MOD_KEY = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform) ? "⌘" : "Ctrl";
|
|
9
|
+
var ALT_KEY = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform) ? "⌥" : "Alt";
|
|
10
|
+
/** A toolbar button that opens the list of gestures and keys a pane takes. */
|
|
11
|
+
function ShortcutsButton({ groups }) {
|
|
12
|
+
const t = useGraphEditorStrings();
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const button = useRef(null);
|
|
15
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(IconButton, {
|
|
16
|
+
ref: button,
|
|
17
|
+
icon: /* @__PURE__ */ jsx(LuKeyboard, { className: "h-4 w-4" }),
|
|
18
|
+
label: t.shortcuts,
|
|
19
|
+
size: "sm",
|
|
20
|
+
variant: "ghost",
|
|
21
|
+
"aria-expanded": open,
|
|
22
|
+
showTooltip: !open,
|
|
23
|
+
onClick: () => setOpen((current) => !current)
|
|
24
|
+
}), open && /* @__PURE__ */ jsxs(ToolbarPopover, {
|
|
25
|
+
anchor: button,
|
|
26
|
+
label: t.shortcuts,
|
|
27
|
+
onClose: () => setOpen(false),
|
|
28
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "text-[0.8125rem] font-semibold",
|
|
30
|
+
children: t.shortcuts
|
|
31
|
+
}), groups.map((group) => /* @__PURE__ */ jsxs("section", {
|
|
32
|
+
className: "mt-3",
|
|
33
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
34
|
+
className: "mb-1.5 text-xs font-semibold theme-muted-text",
|
|
35
|
+
children: group.title
|
|
36
|
+
}), /* @__PURE__ */ jsx("dl", {
|
|
37
|
+
className: "grid grid-cols-[8.5rem_1fr] items-baseline gap-x-3 gap-y-1.5",
|
|
38
|
+
children: group.shortcuts.map((shortcut) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("dt", {
|
|
39
|
+
className: "flex flex-wrap items-center gap-1",
|
|
40
|
+
children: shortcut.combos.map((keys, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("span", {
|
|
41
|
+
className: "theme-muted-text",
|
|
42
|
+
children: "/"
|
|
43
|
+
}), keys.map((key) => /* @__PURE__ */ jsx("kbd", {
|
|
44
|
+
className: "rounded border theme-border px-1 py-px text-[11px]",
|
|
45
|
+
children: key
|
|
46
|
+
}, key))] }, keys.join("+")))
|
|
47
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
48
|
+
className: "theme-muted-text",
|
|
49
|
+
children: shortcut.does
|
|
50
|
+
})] }, shortcut.does))
|
|
51
|
+
})]
|
|
52
|
+
}, group.title))]
|
|
53
|
+
})] });
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { ALT_KEY, MOD_KEY, ShortcutsButton };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type IProps as DropdownProps } from '../components/Dropdown.js';
|
|
2
|
+
import type { IOptions } from '../components/dropdownUtils.js';
|
|
3
|
+
/** A value a typed field offers, with what it means. */
|
|
4
|
+
export interface Suggestion {
|
|
5
|
+
value: string;
|
|
6
|
+
label?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function suggestionOptions(suggestions: Suggestion[]): (IOptions | string)[];
|
|
9
|
+
export declare const FIELD_TEXT_BOX = "py-3 pl-4 rounded-lg text-sm !bg-[var(--theme-muted-panel-bg)]";
|
|
10
|
+
/**
|
|
11
|
+
* The dropdown for a field that takes any typed value as well as the ones it offers. Any of
|
|
12
|
+
* the dropdown's own props can still be passed, such as `allowCustomValue={false}`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function SuggestionInput({ value, onChange, suggestions, allowCustomValue, ...props }: Omit<DropdownProps, 'options' | 'value' | 'onChange'> & {
|
|
15
|
+
value: string;
|
|
16
|
+
onChange: (value: string) => void;
|
|
17
|
+
suggestions: Suggestion[];
|
|
18
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Dropdown from "../components/Dropdown.js";
|
|
2
|
+
import cx from "classnames";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/graph/SuggestionInput.tsx
|
|
5
|
+
function suggestionOptions(suggestions) {
|
|
6
|
+
return suggestions.map(({ value, label }) => label ? {
|
|
7
|
+
label,
|
|
8
|
+
value,
|
|
9
|
+
description: value
|
|
10
|
+
} : value);
|
|
11
|
+
}
|
|
12
|
+
var FIELD_TEXT_BOX = "py-3 pl-4 rounded-lg text-sm !bg-[var(--theme-muted-panel-bg)]";
|
|
13
|
+
/**
|
|
14
|
+
* The dropdown for a field that takes any typed value as well as the ones it offers. Any of
|
|
15
|
+
* the dropdown's own props can still be passed, such as `allowCustomValue={false}`.
|
|
16
|
+
*/
|
|
17
|
+
function SuggestionInput({ value, onChange, suggestions, allowCustomValue = true, ...props }) {
|
|
18
|
+
return /* @__PURE__ */ jsx(Dropdown, {
|
|
19
|
+
showCaret: suggestions.length > 0,
|
|
20
|
+
...props,
|
|
21
|
+
textBoxClassName: cx(FIELD_TEXT_BOX, props.textBoxClassName),
|
|
22
|
+
allowCustomValue,
|
|
23
|
+
value,
|
|
24
|
+
options: suggestionOptions(suggestions),
|
|
25
|
+
onChange: (next) => onChange(next === void 0 ? "" : String(next))
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { FIELD_TEXT_BOX, SuggestionInput, suggestionOptions };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReactNode, type RefObject } from 'react';
|
|
2
|
+
/** A toolbar button's panel, drawn over the page so the pane it sits in can't clip it. */
|
|
3
|
+
export declare function ToolbarPopover({ anchor, label, onClose, children, }: {
|
|
4
|
+
anchor: RefObject<HTMLElement | null>;
|
|
5
|
+
label: string;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): import("react").ReactPortal;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
//#region src/graph/ToolbarPopover.tsx
|
|
5
|
+
var GAP = 8;
|
|
6
|
+
var PANEL_WIDTH = 384;
|
|
7
|
+
function placeBy(anchor, width) {
|
|
8
|
+
const rect = anchor.getBoundingClientRect();
|
|
9
|
+
const above = rect.top - 16;
|
|
10
|
+
const below = window.innerHeight - rect.bottom - 16;
|
|
11
|
+
const right = Math.max(GAP, Math.min(window.innerWidth - rect.right, window.innerWidth - GAP - width));
|
|
12
|
+
return above >= below ? {
|
|
13
|
+
right,
|
|
14
|
+
bottom: window.innerHeight - rect.top + GAP,
|
|
15
|
+
maxHeight: above
|
|
16
|
+
} : {
|
|
17
|
+
right,
|
|
18
|
+
top: rect.bottom + GAP,
|
|
19
|
+
maxHeight: below
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** A toolbar button's panel, drawn over the page so the pane it sits in can't clip it. */
|
|
23
|
+
function ToolbarPopover({ anchor, label, onClose, children }) {
|
|
24
|
+
const panel = useRef(null);
|
|
25
|
+
const [place, setPlace] = useState(null);
|
|
26
|
+
const close = useRef(onClose);
|
|
27
|
+
close.current = onClose;
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
const update = () => {
|
|
30
|
+
if (anchor.current) setPlace(placeBy(anchor.current, panel.current?.offsetWidth || PANEL_WIDTH));
|
|
31
|
+
};
|
|
32
|
+
update();
|
|
33
|
+
window.addEventListener("resize", update);
|
|
34
|
+
window.addEventListener("scroll", update, true);
|
|
35
|
+
return () => {
|
|
36
|
+
window.removeEventListener("resize", update);
|
|
37
|
+
window.removeEventListener("scroll", update, true);
|
|
38
|
+
};
|
|
39
|
+
}, [anchor]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
panel.current?.focus({ preventScroll: true });
|
|
42
|
+
const away = (e) => {
|
|
43
|
+
const target = e.target;
|
|
44
|
+
if (!panel.current?.contains(target) && !anchor.current?.contains(target)) close.current();
|
|
45
|
+
};
|
|
46
|
+
const onEscape = (e) => {
|
|
47
|
+
if (e.key === "Escape") {
|
|
48
|
+
e.stopPropagation();
|
|
49
|
+
close.current();
|
|
50
|
+
anchor.current?.focus({ preventScroll: true });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
window.addEventListener("pointerdown", away, true);
|
|
54
|
+
window.addEventListener("keydown", onEscape, true);
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener("pointerdown", away, true);
|
|
57
|
+
window.removeEventListener("keydown", onEscape, true);
|
|
58
|
+
};
|
|
59
|
+
}, [anchor]);
|
|
60
|
+
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
61
|
+
ref: panel,
|
|
62
|
+
role: "dialog",
|
|
63
|
+
"aria-label": label,
|
|
64
|
+
tabIndex: -1,
|
|
65
|
+
className: "fixed z-50 w-96 max-w-[calc(100vw-16px)] overflow-y-auto rounded-md border theme-border bg-(--theme-panel-bg) p-3 text-xs text-(--theme-app) shadow-lg outline-none",
|
|
66
|
+
style: place ?? { visibility: "hidden" },
|
|
67
|
+
children
|
|
68
|
+
}), document.body);
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { ToolbarPopover };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { GraphEdit, GraphEditResult } from '../editing.js';
|
|
2
|
+
import type { Box } from './editorPrimitives.js';
|
|
3
|
+
import type { ListedProblem } from './ProblemsButton.js';
|
|
4
|
+
import type { SettingsView } from './settingsViews.js';
|
|
5
|
+
export type { Box };
|
|
6
|
+
export interface EditorProblem {
|
|
7
|
+
message: string;
|
|
8
|
+
line: number;
|
|
9
|
+
job?: string;
|
|
10
|
+
step?: number;
|
|
11
|
+
input?: string[];
|
|
12
|
+
/** In a workflow-level setting the settings dialog edits. */
|
|
13
|
+
setting?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** What to show in the YAML beside the graph: a job, one of its steps, or a line. */
|
|
16
|
+
export type RevealTarget = {
|
|
17
|
+
job: string;
|
|
18
|
+
step?: number;
|
|
19
|
+
} | {
|
|
20
|
+
line: number;
|
|
21
|
+
};
|
|
22
|
+
/** Build-page callbacks that turn the dependency graph into an editor. */
|
|
23
|
+
export interface DependencyGraphEditor {
|
|
24
|
+
onEdit: (edit: GraphEdit) => GraphEditResult | undefined;
|
|
25
|
+
canUndo: boolean;
|
|
26
|
+
canRedo: boolean;
|
|
27
|
+
onUndo: () => void;
|
|
28
|
+
onRedo: () => void;
|
|
29
|
+
/** `uses` values offered in the step editor, such as the user's own workflows. */
|
|
30
|
+
usesSuggestions?: string[];
|
|
31
|
+
/** Opens the workflow-level settings: env, permissions, sessions and more. */
|
|
32
|
+
onOpenSettings?: () => void;
|
|
33
|
+
/** The YAML the edits apply to, for editing a job or step as text. */
|
|
34
|
+
readSource?: () => string;
|
|
35
|
+
/** The view the job, step and input dialogs open in; YAML when not given. */
|
|
36
|
+
settingsView?: SettingsView;
|
|
37
|
+
onSettingsViewChange?: (view: SettingsView) => void;
|
|
38
|
+
/** False adds a new job, step or input with its defaults, without opening its dialog. */
|
|
39
|
+
openOnAdd?: boolean;
|
|
40
|
+
onOpenOnAddChange?: (open: boolean) => void;
|
|
41
|
+
/** Problems in the workflow, marked on the jobs, steps and inputs they're in. */
|
|
42
|
+
problems?: EditorProblem[];
|
|
43
|
+
/** The problems this pane's toolbar counts and lists. */
|
|
44
|
+
listedProblems?: ListedProblem[];
|
|
45
|
+
/** Hands the page this pane's way to bring a problem into view, false where it can't; returns its undo. */
|
|
46
|
+
registerShow?: (show: (problem: EditorProblem) => boolean) => () => void;
|
|
47
|
+
/** Shows a job's, step's or line's YAML, when the YAML editor is beside the graph. */
|
|
48
|
+
onReveal?: (target: RevealTarget) => void;
|
|
49
|
+
}
|
|
50
|
+
export declare const typing: (target: EventTarget | null) => boolean;
|
|
51
|
+
export declare function overlaps(rect: DOMRect, box: Box): boolean;
|
|
@@ -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 };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactNode, PointerEvent as ReactPointerEvent } from 'react';
|
|
2
|
+
import type { DependencyGraphEditor } from './editorApi.js';
|
|
3
|
+
import type { Point } from './editorPrimitives.js';
|
|
4
|
+
import { type ShortcutGroup } from './ShortcutsButton.js';
|
|
5
|
+
/** What a drag carries, beside the pointer at `at` in its layer's coordinates. */
|
|
6
|
+
export declare function DragLabel({ at, text, refused, }: {
|
|
7
|
+
at: Point;
|
|
8
|
+
text: string;
|
|
9
|
+
refused?: boolean;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function BarDivider(): import("react").JSX.Element;
|
|
12
|
+
/** A toolbar chip that adds something: click to add it, or, given `onPointerDown`, drag it to where it goes. */
|
|
13
|
+
export declare function AddChip({ icon, label, hint, className, onPointerDown, onClick, }: {
|
|
14
|
+
icon: ReactNode;
|
|
15
|
+
label: string;
|
|
16
|
+
hint: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
onPointerDown?: (e: ReactPointerEvent<HTMLButtonElement>) => void;
|
|
19
|
+
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
/** The bar both editors draw: problems, undo and redo, the editor's own controls, settings, shortcuts. */
|
|
22
|
+
export declare function EditorBar({ editor, groups, className, children, }: {
|
|
23
|
+
editor: DependencyGraphEditor;
|
|
24
|
+
groups: ShortcutGroup[];
|
|
25
|
+
className?: string;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BiRedo } from "../icons/bi/BiRedo.js";
|
|
2
|
+
import { BiUndo } from "../icons/bi/BiUndo.js";
|
|
3
|
+
import { IoSettingsOutline } from "../icons/io5/IoSettingsOutline.js";
|
|
4
|
+
import { TOOLTIP_ID } from "../components/Tooltip.js";
|
|
5
|
+
import { IconButton } from "../components/IconButton.js";
|
|
6
|
+
import { useGraphEditorStrings } from "./editorStrings.js";
|
|
7
|
+
import { ProblemsButton } from "./ProblemsButton.js";
|
|
8
|
+
import { ShortcutsButton } from "./ShortcutsButton.js";
|
|
9
|
+
import cx from "classnames";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/graph/editorChrome.tsx
|
|
12
|
+
/** What a drag carries, beside the pointer at `at` in its layer's coordinates. */
|
|
13
|
+
function DragLabel({ at, text, refused = false }) {
|
|
14
|
+
return /* @__PURE__ */ jsx("div", {
|
|
15
|
+
className: cx("pointer-events-none absolute z-30 rounded-md border bg-(--theme-panel-bg) px-2 py-1 text-xs shadow", refused ? "border-(--theme-error) text-(--theme-error)" : "theme-border"),
|
|
16
|
+
style: {
|
|
17
|
+
left: at.x + 12,
|
|
18
|
+
top: at.y + 12
|
|
19
|
+
},
|
|
20
|
+
children: text
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function BarDivider() {
|
|
24
|
+
return /* @__PURE__ */ jsx("div", { className: "mx-0.5 h-5 w-px bg-(--theme-border)" });
|
|
25
|
+
}
|
|
26
|
+
/** A toolbar chip that adds something: click to add it, or, given `onPointerDown`, drag it to where it goes. */
|
|
27
|
+
function AddChip({ icon, label, hint, className, onPointerDown, onClick }) {
|
|
28
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
29
|
+
type: "button",
|
|
30
|
+
className: cx("flex h-7 items-center gap-1 whitespace-nowrap rounded px-2 text-xs theme-hover", onPointerDown ? "cursor-grab" : "cursor-pointer", className),
|
|
31
|
+
"data-tooltip-id": TOOLTIP_ID,
|
|
32
|
+
"data-tooltip-content": hint,
|
|
33
|
+
onPointerDown,
|
|
34
|
+
onClick,
|
|
35
|
+
children: [icon, label]
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** The bar both editors draw: problems, undo and redo, the editor's own controls, settings, shortcuts. */
|
|
39
|
+
function EditorBar({ editor, groups, className, children }) {
|
|
40
|
+
const t = useGraphEditorStrings();
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: cx("flex max-w-full flex-wrap items-center justify-end gap-0.5 rounded-md border theme-border bg-(--theme-panel-bg) p-0.5", className),
|
|
43
|
+
children: [
|
|
44
|
+
!!editor.listedProblems?.length && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ProblemsButton, { problems: editor.listedProblems }), /* @__PURE__ */ jsx(BarDivider, {})] }),
|
|
45
|
+
/* @__PURE__ */ jsx(IconButton, {
|
|
46
|
+
icon: /* @__PURE__ */ jsx(BiUndo, { className: "h-4 w-4" }),
|
|
47
|
+
label: t.undo,
|
|
48
|
+
size: "sm",
|
|
49
|
+
variant: "ghost",
|
|
50
|
+
disabled: !editor.canUndo,
|
|
51
|
+
onClick: editor.onUndo
|
|
52
|
+
}),
|
|
53
|
+
/* @__PURE__ */ jsx(IconButton, {
|
|
54
|
+
icon: /* @__PURE__ */ jsx(BiRedo, { className: "h-4 w-4" }),
|
|
55
|
+
label: t.redo,
|
|
56
|
+
size: "sm",
|
|
57
|
+
variant: "ghost",
|
|
58
|
+
disabled: !editor.canRedo,
|
|
59
|
+
onClick: editor.onRedo
|
|
60
|
+
}),
|
|
61
|
+
/* @__PURE__ */ jsx(BarDivider, {}),
|
|
62
|
+
children,
|
|
63
|
+
editor.onOpenSettings && /* @__PURE__ */ jsx(IconButton, {
|
|
64
|
+
icon: /* @__PURE__ */ jsx(IoSettingsOutline, { className: "h-4 w-4" }),
|
|
65
|
+
label: t.workflowSettings,
|
|
66
|
+
size: "sm",
|
|
67
|
+
variant: "ghost",
|
|
68
|
+
onClick: editor.onOpenSettings
|
|
69
|
+
}),
|
|
70
|
+
/* @__PURE__ */ jsx(ShortcutsButton, { groups })
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { AddChip, BarDivider, DragLabel, EditorBar };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
import { Textarea } from '../components/Input.js';
|
|
3
|
+
import type { FieldPatch } from '../editing.js';
|
|
4
|
+
import { type GraphEditorStrings } from './editorStrings.js';
|
|
5
|
+
import { type Json } from './records.js';
|
|
6
|
+
export { asRecord, type Json } from './records.js';
|
|
7
|
+
export type Strings = GraphEditorStrings;
|
|
8
|
+
export declare const EXPRESSION: RegExp;
|
|
9
|
+
export declare const ENV_KEY: RegExp;
|
|
10
|
+
export declare const DURATION: RegExp;
|
|
11
|
+
export declare function withoutUndefined(value: Json): Json;
|
|
12
|
+
export declare function text(value: unknown): string;
|
|
13
|
+
export declare function isScalar(value: unknown): boolean;
|
|
14
|
+
export declare function parseScalar(value: string): unknown;
|
|
15
|
+
export declare function sameValue(a: unknown, b: unknown): boolean;
|
|
16
|
+
/** Keys whose value changed: undefined removes a key, anything else writes it. */
|
|
17
|
+
export declare function diffPatch(original: Json, next: Json): Required<FieldPatch>;
|
|
18
|
+
export declare function isEmptyPatch(patch: Required<FieldPatch>): boolean;
|
|
19
|
+
export declare function conditionText(value: unknown): string;
|
|
20
|
+
export declare function parseCondition(value: string): unknown;
|
|
21
|
+
export declare function durationError(value: string, t: Strings, pattern?: RegExp): string | undefined;
|
|
22
|
+
export declare function expressionError(value: string, t: Strings): string | undefined;
|
|
23
|
+
export declare function orUndefined(value: string): string | undefined;
|
|
24
|
+
export declare function countError(value: string, min: number, message: string): string | undefined;
|
|
25
|
+
export declare function parseCount(value: string): unknown;
|
|
26
|
+
export declare function parseJson(value: string): {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
value?: unknown;
|
|
29
|
+
};
|
|
30
|
+
/** A key/value row; nested values are edited as JSON. */
|
|
31
|
+
export interface Row {
|
|
32
|
+
key: string;
|
|
33
|
+
value: string;
|
|
34
|
+
nested?: boolean;
|
|
35
|
+
/** What the row was read from; while its text is unchanged a save writes this back as it was. */
|
|
36
|
+
original?: unknown;
|
|
37
|
+
}
|
|
38
|
+
export declare function unchangedValue(text: string, original: unknown, nested?: boolean): boolean;
|
|
39
|
+
/** Rows for a map; with `nested` false a non-scalar is shown as its JSON text and saved as a string. */
|
|
40
|
+
export declare function rowsFrom(value: unknown, nested?: boolean): Row[];
|
|
41
|
+
export declare function rowsTo(rows: Row[], parse: (value: string) => unknown): Json | undefined;
|
|
42
|
+
export declare function rowsError(rows: Row[], keyPattern: RegExp, keyMessage: string, t: Strings, valueError?: (value: string) => string | undefined): string | undefined;
|
|
43
|
+
/** A switch that may instead hold a `${{ }}` expression. */
|
|
44
|
+
export type Flag = boolean | string | undefined;
|
|
45
|
+
export declare function flagOf(value: unknown): Flag;
|
|
46
|
+
export declare function flagError(value: Flag, t: Strings): string | undefined;
|
|
47
|
+
export declare function flagValue(checked: boolean, fallback: boolean, original: unknown): Flag;
|
|
48
|
+
/** Whether each section of a dialog is open, kept while the dialog is up so switching views keeps it. */
|
|
49
|
+
export declare const SectionMemory: import("react").Context<Map<string, boolean> | null>;
|
|
50
|
+
export declare function useSectionMemory(): Map<string, boolean>;
|
|
51
|
+
export declare function Section({ title, description, open, alert, children, }: {
|
|
52
|
+
title: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
/** Whether it starts open; read once, when its dialog first shows it. */
|
|
55
|
+
open?: boolean;
|
|
56
|
+
/** A problem inside, which opens it so a folded section can't hide why Save is off. */
|
|
57
|
+
alert?: boolean;
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}): import("react").JSX.Element;
|
|
60
|
+
export declare function FieldError({ message }: {
|
|
61
|
+
message: string | undefined;
|
|
62
|
+
}): import("react").JSX.Element | null;
|
|
63
|
+
/** The YAML key a field sets, beside its name; left out where the name already says it. */
|
|
64
|
+
export declare function KeyHint({ label, yamlKey }: {
|
|
65
|
+
label: string;
|
|
66
|
+
yamlKey: string;
|
|
67
|
+
}): import("react").JSX.Element | null;
|
|
68
|
+
/** Label props for an Input or Textarea that sets `yamlKey`. */
|
|
69
|
+
export declare function labelled(label: string, yamlKey: string): {
|
|
70
|
+
label: string;
|
|
71
|
+
labelHint: import("react").JSX.Element;
|
|
72
|
+
};
|
|
73
|
+
/** A field's name and help text, for controls without their own label; `actions` sit at its right. */
|
|
74
|
+
export declare function FieldLabel({ label, yamlKey, description, htmlFor, descriptionId, actions, }: {
|
|
75
|
+
label: string;
|
|
76
|
+
yamlKey?: string | undefined;
|
|
77
|
+
description: string;
|
|
78
|
+
htmlFor?: string;
|
|
79
|
+
descriptionId?: string;
|
|
80
|
+
actions?: ReactNode;
|
|
81
|
+
}): import("react").JSX.Element;
|
|
82
|
+
/** A field's label and description above the editor that sets it. */
|
|
83
|
+
export declare function LabelledField({ children, ...label }: ComponentProps<typeof FieldLabel> & {
|
|
84
|
+
children: ReactNode;
|
|
85
|
+
}): import("react").JSX.Element;
|
|
86
|
+
/**
|
|
87
|
+
* Switches a field between its own control and a typed expression, where the schema takes one. Its
|
|
88
|
+
* name says what pressing it does, so it isn't also announced as pressed.
|
|
89
|
+
*/
|
|
90
|
+
export declare function ExpressionToggle({ active, onChange, }: {
|
|
91
|
+
active: boolean;
|
|
92
|
+
onChange: (active: boolean) => void;
|
|
93
|
+
}): import("react").JSX.Element;
|
|
94
|
+
export declare function ToggleField({ label, yamlKey, description, checked, onChange, disabled, }: {
|
|
95
|
+
label: string;
|
|
96
|
+
yamlKey?: string;
|
|
97
|
+
description: string;
|
|
98
|
+
checked: boolean;
|
|
99
|
+
onChange: (checked: boolean) => void;
|
|
100
|
+
disabled?: boolean;
|
|
101
|
+
}): import("react").JSX.Element;
|
|
102
|
+
export declare function ChoiceButtons<T extends string>({ options, value, onChange, }: {
|
|
103
|
+
options: {
|
|
104
|
+
value: T;
|
|
105
|
+
label: string;
|
|
106
|
+
}[];
|
|
107
|
+
value: T;
|
|
108
|
+
onChange: (value: T) => void;
|
|
109
|
+
}): import("react").JSX.Element;
|
|
110
|
+
export declare function AddRowButton({ label, onClick }: {
|
|
111
|
+
label: string;
|
|
112
|
+
onClick: () => void;
|
|
113
|
+
}): import("react").JSX.Element;
|
|
114
|
+
export declare function KeyValueEditor({ rows, onChange, error, valuePlaceholder, keyPlaceholder, addLabel, valueSuggestions, lint, }: {
|
|
115
|
+
rows: Row[];
|
|
116
|
+
onChange: (rows: Row[]) => void;
|
|
117
|
+
error: string | undefined;
|
|
118
|
+
valuePlaceholder?: string;
|
|
119
|
+
keyPlaceholder?: string;
|
|
120
|
+
addLabel?: string;
|
|
121
|
+
/** Offered on each value, such as references to the workflow's inputs. */
|
|
122
|
+
valueSuggestions?: string[];
|
|
123
|
+
/** Whether each row shows the problems at its key, inside the caller's LintScope. */
|
|
124
|
+
lint?: boolean;
|
|
125
|
+
}): import("react").JSX.Element;
|
|
126
|
+
export declare function updateAt<T>(items: T[], index: number, item: T): T[];
|
|
127
|
+
export declare function removeAt<T>(items: T[], index: number): T[];
|
|
128
|
+
/** A list of single values, such as permissions or variable names. */
|
|
129
|
+
export declare function StringListEditor({ label, values, onChange, addLabel, placeholder, error, suggestions, }: {
|
|
130
|
+
/** The list's name, which names each of its rows. */
|
|
131
|
+
label: string;
|
|
132
|
+
values: string[];
|
|
133
|
+
onChange: (values: string[]) => void;
|
|
134
|
+
addLabel: string;
|
|
135
|
+
placeholder?: string;
|
|
136
|
+
error?: string | undefined;
|
|
137
|
+
/** Offered on each row; any other value can still be typed. */
|
|
138
|
+
suggestions?: string[];
|
|
139
|
+
}): import("react").JSX.Element;
|
|
140
|
+
export declare function SuggestedInput({ label, yamlKey, description, value, onChange, suggestions, error, placeholder, }: {
|
|
141
|
+
label: string;
|
|
142
|
+
yamlKey?: string;
|
|
143
|
+
description?: string;
|
|
144
|
+
value: string;
|
|
145
|
+
onChange: (value: string) => void;
|
|
146
|
+
suggestions: string[];
|
|
147
|
+
error?: string | undefined;
|
|
148
|
+
placeholder?: string;
|
|
149
|
+
}): import("react").JSX.Element;
|
|
150
|
+
/** A text area that starts one line tall and grows with its text. */
|
|
151
|
+
export declare function useGrowingArea(value: string): import("react").RefObject<HTMLTextAreaElement | null>;
|
|
152
|
+
export declare const GROWING_AREA = "!min-h-0 !resize-none overflow-hidden";
|
|
153
|
+
export declare function GrowingTextarea(props: Omit<ComponentProps<typeof Textarea>, 'rows'> & {
|
|
154
|
+
value: string;
|
|
155
|
+
}): import("react").JSX.Element;
|
|
156
|
+
/** Whether adding a job, step or input opens its dialog first; any of those dialogs sets it. */
|
|
157
|
+
export interface OpenOnAdd {
|
|
158
|
+
open: boolean;
|
|
159
|
+
onChange: (open: boolean) => void;
|
|
160
|
+
}
|
|
161
|
+
/** The dialogs' "open when adding" switch, when the host keeps the setting. */
|
|
162
|
+
export declare function openOnAddOf(editor: {
|
|
163
|
+
openOnAdd?: boolean;
|
|
164
|
+
onOpenOnAddChange?: (open: boolean) => void;
|
|
165
|
+
} | undefined): OpenOnAdd | undefined;
|
|
166
|
+
export interface DialogShellProps {
|
|
167
|
+
title: string;
|
|
168
|
+
onClose: () => void;
|
|
169
|
+
onSubmit: () => void;
|
|
170
|
+
saveDisabled: boolean;
|
|
171
|
+
dirty: boolean;
|
|
172
|
+
/** A dialog opened on top of this one owns Escape and outside clicks. */
|
|
173
|
+
locked?: boolean;
|
|
174
|
+
footerStart?: ReactNode;
|
|
175
|
+
/** Controls beside the title, such as a switch between views. */
|
|
176
|
+
headerEnd?: ReactNode;
|
|
177
|
+
openOnAdd?: OpenOnAdd | undefined;
|
|
178
|
+
children: ReactNode;
|
|
179
|
+
}
|
|
180
|
+
export declare function DialogShell({ title, onClose, onSubmit, saveDisabled, dirty, locked, footerStart, headerEnd, openOnAdd, children, }: DialogShellProps): import("react").JSX.Element;
|