@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.
- package/README.md +1 -1
- package/dist/ai/components/ChatInput.js +8 -23
- package/dist/ai/components/ChatSidebar.js +7 -4
- package/dist/ai/components/chatDraft.d.ts +7 -0
- package/dist/ai/components/chatDraft.js +66 -0
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +2 -2
- package/dist/ai/ui/sidebar.d.ts +6 -0
- package/dist/ai/ui/sidebar.js +15 -1
- package/dist/editing.d.ts +2 -0
- package/dist/editor/Monaco.js +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/editor/lintContext.js +15 -3
- package/dist/form/FieldWrapper.js +1 -0
- package/dist/form/Form.js +64 -23
- 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/formEditing.d.ts +25 -0
- package/dist/form/formEditing.js +20 -0
- package/dist/graph/DependencyGraph.d.ts +8 -1
- package/dist/graph/DependencyGraph.js +433 -204
- package/dist/graph/GraphEditor.d.ts +27 -0
- package/dist/graph/GraphEditor.js +1543 -0
- package/dist/graph/GraphEditorDialogs.d.ts +87 -0
- package/dist/graph/GraphEditorDialogs.js +1808 -0
- package/dist/graph/InputDialog.js +1568 -54
- package/dist/graph/InputsEditor.d.ts +12 -0
- package/dist/graph/InputsEditor.js +1594 -0
- package/dist/graph/JobSummary.d.ts +3 -0
- package/dist/graph/JobSummary.js +34 -21
- package/dist/graph/MatrixGroup.d.ts +2 -1
- package/dist/graph/MatrixGroup.js +10 -3
- package/dist/graph/ShortcutsButton.js +56 -0
- package/dist/graph/SuggestionInput.js +29 -0
- package/dist/graph/WorkflowSettingsDialog.d.ts +21 -0
- package/dist/graph/WorkflowSettingsDialog.js +670 -0
- package/dist/graph/editorApi.js +8 -0
- package/dist/graph/editorChrome.js +75 -0
- package/dist/graph/editorFields.js +535 -0
- package/dist/graph/editorPrimitives.js +95 -4
- package/dist/graph/editorRows.d.ts +58 -0
- package/dist/graph/editorRows.js +460 -0
- package/dist/graph/editorStrings.d.ts +1 -0
- package/dist/graph/editorStrings.js +10 -1
- package/dist/graph/expressionRefs.js +39 -0
- package/dist/graph/fieldProblems.js +111 -0
- package/dist/graph/graphEditorState.d.ts +191 -0
- package/dist/graph/graphEditorState.js +74 -0
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.js +6 -2
- 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.js +388 -0
- package/dist/graph/inputRows.d.ts +43 -0
- package/dist/graph/inputRows.js +65 -0
- package/dist/graph/refusalText.js +13 -0
- package/dist/graph/settingsViews.js +62 -0
- package/dist/graph/stepWith.d.ts +38 -0
- package/dist/graph/stepWith.js +536 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { asRecord } from "./records.js";
|
|
2
|
+
import { inputRefs, refExpression } from "./inputRefs.js";
|
|
3
|
+
//#region src/graph/expressionRefs.ts
|
|
4
|
+
var ref = (group, label) => ({
|
|
5
|
+
group,
|
|
6
|
+
label,
|
|
7
|
+
expression: `\${{ ${label} }}`
|
|
8
|
+
});
|
|
9
|
+
function names(value) {
|
|
10
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
|
|
11
|
+
return Object.keys(asRecord(value));
|
|
12
|
+
}
|
|
13
|
+
/** Everything an expression in `job` can read; without a job, only what the whole workflow has. */
|
|
14
|
+
function expressionRefs(editing, workflow, job) {
|
|
15
|
+
const root = asRecord(workflow);
|
|
16
|
+
const jobs = asRecord(root["jobs"]);
|
|
17
|
+
const own = job === void 0 ? {} : asRecord(jobs[job]);
|
|
18
|
+
const out = inputRefs(editing, editing.workflowInputsSchema(root)).map((input) => ({
|
|
19
|
+
group: "inputs",
|
|
20
|
+
label: `inputs.${input.path.join(".")}`,
|
|
21
|
+
expression: refExpression(input.path)
|
|
22
|
+
}));
|
|
23
|
+
const needs = Array.isArray(own["needs"]) ? own["needs"] : [];
|
|
24
|
+
for (const need of needs) {
|
|
25
|
+
if (typeof need !== "string") continue;
|
|
26
|
+
const target = editing.needTarget(need);
|
|
27
|
+
for (const output of Object.keys(asRecord(asRecord(jobs[target])["outputs"]))) out.push(ref("outputs", `needs.${target}.outputs.${output}`));
|
|
28
|
+
}
|
|
29
|
+
for (const variable of editing.matrixNames(own) ?? []) out.push(ref("matrix", `matrix.${variable}`));
|
|
30
|
+
const declared = asRecord(root["needs"]);
|
|
31
|
+
for (const name of names(declared["organizationVariables"])) out.push(ref("variables", `org.${name}`));
|
|
32
|
+
for (const name of names(declared["userVariables"])) out.push(ref("variables", `var.${name}`));
|
|
33
|
+
for (const name of Object.keys(asRecord(root["sessions"]))) out.push(ref("sessions", `sessions.${name}`));
|
|
34
|
+
const env = /* @__PURE__ */ new Set([...Object.keys(asRecord(root["env"])), ...Object.keys(asRecord(own["env"]))]);
|
|
35
|
+
for (const key of env) out.push(ref("env", `env.${key}`));
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { expressionRefs };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useWorkflowEditing } from "../components/Provider.js";
|
|
2
|
+
import { TbAlertTriangle } from "../icons/tb/TbAlertTriangle.js";
|
|
3
|
+
import { keyedByContent } from "../components/keys.js";
|
|
4
|
+
import { useGraphEditorStrings } from "./editorStrings.js";
|
|
5
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/graph/fieldProblems.tsx
|
|
8
|
+
var FieldProblemsContext = createContext(null);
|
|
9
|
+
var keyOf = (at) => at.join(".");
|
|
10
|
+
var within = (problem, key) => keyOf(problem.at) === key || keyOf(problem.at).startsWith(`${key}.`);
|
|
11
|
+
/** Hands a form's fields the problems at the keys they set. */
|
|
12
|
+
function FieldProblems({ problems, children }) {
|
|
13
|
+
const [fields, setFields] = useState(/* @__PURE__ */ new Map());
|
|
14
|
+
const register = useCallback((key, id) => {
|
|
15
|
+
setFields((current) => current.get(key) === id ? current : new Map(current).set(key, id));
|
|
16
|
+
return () => setFields((current) => {
|
|
17
|
+
if (current.get(key) !== id) return current;
|
|
18
|
+
const next = new Map(current);
|
|
19
|
+
next.delete(key);
|
|
20
|
+
return next;
|
|
21
|
+
});
|
|
22
|
+
}, []);
|
|
23
|
+
const value = useMemo(() => ({
|
|
24
|
+
problems,
|
|
25
|
+
fields,
|
|
26
|
+
register
|
|
27
|
+
}), [
|
|
28
|
+
problems,
|
|
29
|
+
fields,
|
|
30
|
+
register
|
|
31
|
+
]);
|
|
32
|
+
return /* @__PURE__ */ jsx(FieldProblemsContext.Provider, {
|
|
33
|
+
value,
|
|
34
|
+
children
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
var LintScopeContext = createContext("");
|
|
38
|
+
/** Where the fields inside sit, such as `ssh` for a remote host's fields. */
|
|
39
|
+
function LintScope({ at, children }) {
|
|
40
|
+
const outer = useContext(LintScopeContext);
|
|
41
|
+
return /* @__PURE__ */ jsx(LintScopeContext.Provider, {
|
|
42
|
+
value: outer ? `${outer}.${at}` : at,
|
|
43
|
+
children
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** The problems at the field with id `id`, which sets `key`; the dialog's list then leads to it. */
|
|
47
|
+
function useFieldProblems(key, id) {
|
|
48
|
+
const context = useContext(FieldProblemsContext);
|
|
49
|
+
const scope = useContext(LintScopeContext);
|
|
50
|
+
const full = key === void 0 ? void 0 : scope ? `${scope}.${key}` : key;
|
|
51
|
+
const register = context?.register;
|
|
52
|
+
useEffect(() => full && register ? register(full, id) : void 0, [
|
|
53
|
+
full,
|
|
54
|
+
id,
|
|
55
|
+
register
|
|
56
|
+
]);
|
|
57
|
+
return full && context ? context.problems.filter((problem) => within(problem, full)) : [];
|
|
58
|
+
}
|
|
59
|
+
/** A field's problems as one message, and the buttons that make their likely fixes. */
|
|
60
|
+
function useFieldLint(key, id, value, onChange) {
|
|
61
|
+
const { replaceReference } = useWorkflowEditing();
|
|
62
|
+
const problems = useFieldProblems(key, id);
|
|
63
|
+
const fixes = problems.flatMap(({ fix }) => fix && !fix.key && replaceReference(value, fix.find, fix.replace) !== value ? [fix] : []);
|
|
64
|
+
return {
|
|
65
|
+
message: problems.length > 0 ? problems.map((problem) => problem.message).join(" ") : void 0,
|
|
66
|
+
fixes: /* @__PURE__ */ jsx(FixButtons, {
|
|
67
|
+
fixes,
|
|
68
|
+
onFix: (fix) => onChange(replaceReference(value, fix.find, fix.replace))
|
|
69
|
+
})
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function FixButtons({ fixes, onFix }) {
|
|
73
|
+
const t = useGraphEditorStrings();
|
|
74
|
+
if (fixes.length === 0) return null;
|
|
75
|
+
return /* @__PURE__ */ jsx("div", {
|
|
76
|
+
className: "flex flex-wrap gap-2",
|
|
77
|
+
children: fixes.map((fix) => /* @__PURE__ */ jsx("button", {
|
|
78
|
+
type: "button",
|
|
79
|
+
className: "cursor-pointer text-left text-xs font-medium text-(--theme-link) hover:underline",
|
|
80
|
+
onClick: () => onFix(fix),
|
|
81
|
+
children: t.applyFix(fix.replace)
|
|
82
|
+
}, `${fix.find}>${fix.replace}`))
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function reveal(id) {
|
|
86
|
+
const element = document.getElementById(id);
|
|
87
|
+
for (let section = element?.closest("details"); section; section = section.parentElement?.closest("details")) section.open = true;
|
|
88
|
+
element?.focus();
|
|
89
|
+
}
|
|
90
|
+
function ProblemList({ problems }) {
|
|
91
|
+
const fields = useContext(FieldProblemsContext)?.fields;
|
|
92
|
+
if (problems.length === 0) return null;
|
|
93
|
+
const fieldOf = (problem) => [...fields ?? []].filter(([key]) => within(problem, key)).sort(([a], [b]) => b.length - a.length)[0]?.[1];
|
|
94
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
95
|
+
className: "flex flex-col gap-1 text-sm text-(--theme-error)",
|
|
96
|
+
children: keyedByContent(problems, (problem) => `${problem.at.join(".")}:${problem.message}`).map(({ key, item: problem }) => {
|
|
97
|
+
const field = fieldOf(problem);
|
|
98
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
99
|
+
className: "flex gap-1.5",
|
|
100
|
+
children: [/* @__PURE__ */ jsx(TbAlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), field ? /* @__PURE__ */ jsx("button", {
|
|
101
|
+
type: "button",
|
|
102
|
+
className: "cursor-pointer text-left hover:underline",
|
|
103
|
+
onClick: () => reveal(field),
|
|
104
|
+
children: problem.message
|
|
105
|
+
}) : problem.message]
|
|
106
|
+
}, key);
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { FieldProblems, FixButtons, LintScope, ProblemList, useFieldLint, useFieldProblems };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { type PointerEvent as ReactPointerEvent } from 'react';
|
|
2
|
+
import type { GraphEdit, GraphSlot, NeedRef, NudgeDirection, StepRef } from '../editing.js';
|
|
3
|
+
import type { EditorProblem, RevealTarget } from './editorApi.js';
|
|
4
|
+
import type { Box, Point, Store } from './editorPrimitives.js';
|
|
5
|
+
import type { GraphEditorStrings } from './editorStrings.js';
|
|
6
|
+
/** Pointer-downs on this class drag editor items instead of panning the graph. */
|
|
7
|
+
export declare const EDITOR_HANDLE_CLASS = "dag-editor-handle";
|
|
8
|
+
/** A node's left circle takes dependencies; its right circle gives them. */
|
|
9
|
+
export type PortSide = 'in' | 'out';
|
|
10
|
+
export type ConnectPayload = {
|
|
11
|
+
kind: 'connect';
|
|
12
|
+
from: string[];
|
|
13
|
+
side: PortSide;
|
|
14
|
+
label: string;
|
|
15
|
+
/** The circle dragged from: `node:<head job>`, or `selection`. */
|
|
16
|
+
port: string;
|
|
17
|
+
/** An end of a connector being moved: its needs go on the drop, and the line starts at its other end. */
|
|
18
|
+
replaces?: NeedRef[];
|
|
19
|
+
origin?: Point;
|
|
20
|
+
};
|
|
21
|
+
export type DragPayload =
|
|
22
|
+
/** `anchor` lands where the drag drops; the other jobs keep their offsets from it. */
|
|
23
|
+
{
|
|
24
|
+
kind: 'jobs';
|
|
25
|
+
jobs: string[];
|
|
26
|
+
label: string;
|
|
27
|
+
anchor?: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'new';
|
|
30
|
+
matrix: boolean;
|
|
31
|
+
label: string;
|
|
32
|
+
} | ConnectPayload | {
|
|
33
|
+
kind: 'steps';
|
|
34
|
+
steps: StepRef[];
|
|
35
|
+
label: string;
|
|
36
|
+
};
|
|
37
|
+
export type DropTarget =
|
|
38
|
+
/** `blocked` when the move would cut a need an expression of a moved job reads. */
|
|
39
|
+
{
|
|
40
|
+
kind: 'slot';
|
|
41
|
+
slot: GraphSlot;
|
|
42
|
+
indicator: Box;
|
|
43
|
+
blocked?: boolean;
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'trash';
|
|
46
|
+
}
|
|
47
|
+
/** `edits` is null when the drop can't connect. */
|
|
48
|
+
| {
|
|
49
|
+
kind: 'connect';
|
|
50
|
+
edits: GraphEdit[] | null;
|
|
51
|
+
box: Box;
|
|
52
|
+
}
|
|
53
|
+
/** Dragged jobs join `into`'s node, taking its needs; `valid` is false when that would loop. */
|
|
54
|
+
| {
|
|
55
|
+
kind: 'merge';
|
|
56
|
+
into: string;
|
|
57
|
+
jobs: string[];
|
|
58
|
+
valid: boolean;
|
|
59
|
+
box: Box;
|
|
60
|
+
}
|
|
61
|
+
/** Steps land at `index` of `job`; `around` outlines a job whose steps aren't shown, `problem` refuses the drop. */
|
|
62
|
+
| {
|
|
63
|
+
kind: 'steps';
|
|
64
|
+
job: string;
|
|
65
|
+
index: number;
|
|
66
|
+
indicator: Box;
|
|
67
|
+
around?: boolean;
|
|
68
|
+
problem?: string;
|
|
69
|
+
};
|
|
70
|
+
export interface DragView {
|
|
71
|
+
payload: DragPayload;
|
|
72
|
+
client: Point;
|
|
73
|
+
content: Point | null;
|
|
74
|
+
origin: Point | null;
|
|
75
|
+
target: DropTarget | null;
|
|
76
|
+
}
|
|
77
|
+
/** A job or step being added: `yml` is the workflow as if `edit` were applied, which the dialog's first save does. */
|
|
78
|
+
export interface Addition {
|
|
79
|
+
edit: GraphEdit;
|
|
80
|
+
yml: string;
|
|
81
|
+
}
|
|
82
|
+
export type Dialog = {
|
|
83
|
+
kind: 'job';
|
|
84
|
+
job: string;
|
|
85
|
+
addition?: Addition;
|
|
86
|
+
} | {
|
|
87
|
+
kind: 'step';
|
|
88
|
+
job: string;
|
|
89
|
+
index: number;
|
|
90
|
+
addition?: Addition;
|
|
91
|
+
};
|
|
92
|
+
export declare const labelsOf: (jobs: string[]) => string;
|
|
93
|
+
/** A drawn connector, named by the head jobs of the boxes it joins. */
|
|
94
|
+
export interface EdgeSelection {
|
|
95
|
+
from: string;
|
|
96
|
+
to: string;
|
|
97
|
+
}
|
|
98
|
+
export interface UiState {
|
|
99
|
+
drag: DragView | null;
|
|
100
|
+
/** Selected connectors. Jobs, steps and connectors are never selected together. */
|
|
101
|
+
edges: EdgeSelection[];
|
|
102
|
+
dialog: Dialog | null;
|
|
103
|
+
selection: string[];
|
|
104
|
+
steps: StepRef[];
|
|
105
|
+
problems: EditorProblem[];
|
|
106
|
+
/** The box being dragged out on empty space, in content coordinates. */
|
|
107
|
+
marquee: Box | null;
|
|
108
|
+
}
|
|
109
|
+
export declare const NO_SELECTION: string[];
|
|
110
|
+
export declare const NO_STEPS: StepRef[];
|
|
111
|
+
export declare const NO_EDGES: EdgeSelection[];
|
|
112
|
+
export declare const NO_PROBLEMS: EditorProblem[];
|
|
113
|
+
export declare const problemsIn: (problems: EditorProblem[], job: string, step?: number) => EditorProblem[];
|
|
114
|
+
export declare const sameStep: (a: StepRef, b: StepRef) => boolean;
|
|
115
|
+
export type UiStore = Store<UiState>;
|
|
116
|
+
/** The drawn grid of the level being edited; `rows` holds each box's row, `columns` each column's number. */
|
|
117
|
+
export interface GraphGrid {
|
|
118
|
+
wrapper: HTMLDivElement;
|
|
119
|
+
cols: string[][][];
|
|
120
|
+
rows: number[][];
|
|
121
|
+
columns: number[];
|
|
122
|
+
}
|
|
123
|
+
/** What the editor asks of the graph's view. */
|
|
124
|
+
export interface GraphView {
|
|
125
|
+
/** Keeps `job` where it is on screen through the next layout change, rather than refitting. */
|
|
126
|
+
hold: (job: string) => void;
|
|
127
|
+
}
|
|
128
|
+
export interface GraphEditorApi {
|
|
129
|
+
store: UiStore;
|
|
130
|
+
/** The editor's strings, so what the graph draws for it needn't load them itself. */
|
|
131
|
+
t: GraphEditorStrings;
|
|
132
|
+
isMatrixJob: (job: string) => boolean;
|
|
133
|
+
startDrag: (e: ReactPointerEvent, payload: DragPayload) => void;
|
|
134
|
+
openJobMenu: (x: number, y: number, job: string) => void;
|
|
135
|
+
openStepMenu: (x: number, y: number, job: string, index: number) => void;
|
|
136
|
+
openEdgeMenu: (x: number, y: number, edge: EdgeSelection) => void;
|
|
137
|
+
/**
|
|
138
|
+
* Selects a connector alone, or with `add` adds it to the selected connectors or takes it out;
|
|
139
|
+
* selected jobs or steps give way to it.
|
|
140
|
+
*/
|
|
141
|
+
selectEdge: (edge: EdgeSelection, add: boolean) => void;
|
|
142
|
+
/** Whether an expression reads one of a connector's needs, so it can't be removed or moved. */
|
|
143
|
+
edgeInUse: (edge: EdgeSelection) => boolean;
|
|
144
|
+
/** Starts moving one end of a connector; `fixed` is its other end, in the graph's own pixels. */
|
|
145
|
+
startEdgeEnd: (e: ReactPointerEvent, edge: EdgeSelection, end: 'from' | 'to', fixed: Point) => void;
|
|
146
|
+
/** Removes the selected jobs, steps and connectors; false when nothing is selected. */
|
|
147
|
+
deleteSelection: () => boolean;
|
|
148
|
+
/** Opens the dialog of the one selected job or step, reporting whether there was one. */
|
|
149
|
+
editSelection: () => boolean;
|
|
150
|
+
addJob: (matrix: boolean) => void;
|
|
151
|
+
addStep: (job: string) => void;
|
|
152
|
+
editJob: (job: string) => void;
|
|
153
|
+
editStep: (job: string, index: number) => void;
|
|
154
|
+
registerGrid: (grid: GraphGrid | null) => void;
|
|
155
|
+
grid: () => GraphGrid | null;
|
|
156
|
+
registerView: (view: GraphView | null) => void;
|
|
157
|
+
/** The selected jobs that still exist. */
|
|
158
|
+
selected: () => string[];
|
|
159
|
+
/** Adds a job to the selected jobs or takes it out; selected steps or connectors give way to it. */
|
|
160
|
+
toggleSelected: (job: string) => void;
|
|
161
|
+
/** The selected steps that still exist. */
|
|
162
|
+
selectedSteps: () => StepRef[];
|
|
163
|
+
/** Adds a step to the selected steps or takes it out; selected jobs or connectors give way to it. */
|
|
164
|
+
toggleStep: (step: StepRef) => void;
|
|
165
|
+
/** Puts the selected jobs or steps on the clipboard, as YAML; null when nothing is selected. */
|
|
166
|
+
copySelection: () => string | null;
|
|
167
|
+
/** Adds the jobs or steps in `text` near `pointer`; false when `text` holds neither. */
|
|
168
|
+
paste: (text: string, pointer: Point | null) => boolean;
|
|
169
|
+
/** Moves the selected jobs one slot; false when no jobs are selected. */
|
|
170
|
+
nudge: (direction: NudgeDirection) => boolean;
|
|
171
|
+
reveal: (target: RevealTarget) => void;
|
|
172
|
+
/** Selects a job, or one of its steps, alone. */
|
|
173
|
+
focus: (target: {
|
|
174
|
+
job: string;
|
|
175
|
+
step?: number;
|
|
176
|
+
}) => void;
|
|
177
|
+
}
|
|
178
|
+
export declare const GraphEditorProvider: import("react").Provider<GraphEditorApi | null>;
|
|
179
|
+
export declare function useGraphEditor(): GraphEditorApi | null;
|
|
180
|
+
export declare function useUi<T>(api: GraphEditorApi | null, select: (state: UiState) => T, fallback: T): T;
|
|
181
|
+
export declare function useSelectedEdges(api: GraphEditorApi | null): EdgeSelection[];
|
|
182
|
+
export declare const sameEdge: (a: EdgeSelection, b: EdgeSelection) => boolean;
|
|
183
|
+
export declare function toContent(wrapper: HTMLElement, client: Point): Point;
|
|
184
|
+
export declare function contentBox(wrapper: HTMLElement, el: Element): Box;
|
|
185
|
+
/**
|
|
186
|
+
* The outline around the selected jobs, in content coordinates: as tall as their rows and
|
|
187
|
+
* as wide as their nodes, so its circles sit clear of the nodes' own.
|
|
188
|
+
*/
|
|
189
|
+
export declare function selectionBounds(wrapper: HTMLElement, jobs: string[]): (Box & {
|
|
190
|
+
connectorY: number;
|
|
191
|
+
}) | null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jobLabel } from "./util.js";
|
|
2
|
+
import { createContext, useContext, useSyncExternalStore } from "react";
|
|
3
|
+
//#region src/graph/graphEditorState.ts
|
|
4
|
+
/** Pointer-downs on this class drag editor items instead of panning the graph. */
|
|
5
|
+
var EDITOR_HANDLE_CLASS = "dag-editor-handle";
|
|
6
|
+
var labelsOf = (jobs) => jobs.map((job) => jobLabel(job)).join(", ");
|
|
7
|
+
var NO_SELECTION = [];
|
|
8
|
+
var NO_STEPS = [];
|
|
9
|
+
var NO_EDGES = [];
|
|
10
|
+
var NO_PROBLEMS = [];
|
|
11
|
+
var problemsIn = (problems, job, step) => problems.filter((problem) => problem.job === job && problem.step === step);
|
|
12
|
+
var sameStep = (a, b) => a.job === b.job && a.index === b.index;
|
|
13
|
+
var EditorContext = createContext(null);
|
|
14
|
+
var GraphEditorProvider = EditorContext.Provider;
|
|
15
|
+
function useGraphEditor() {
|
|
16
|
+
return useContext(EditorContext);
|
|
17
|
+
}
|
|
18
|
+
var noopSubscribe = () => () => {};
|
|
19
|
+
function useUi(api, select, fallback) {
|
|
20
|
+
const snapshot = () => api ? select(api.store.get()) : fallback;
|
|
21
|
+
return useSyncExternalStore(api ? api.store.subscribe : noopSubscribe, snapshot, snapshot);
|
|
22
|
+
}
|
|
23
|
+
function useSelectedEdges(api) {
|
|
24
|
+
return useUi(api, (state) => state.edges, NO_EDGES);
|
|
25
|
+
}
|
|
26
|
+
var sameEdge = (a, b) => a.from === b.from && a.to === b.to;
|
|
27
|
+
function toContent(wrapper, client) {
|
|
28
|
+
const rect = wrapper.getBoundingClientRect();
|
|
29
|
+
const scale = wrapper.offsetWidth > 0 ? rect.width / wrapper.offsetWidth : 1;
|
|
30
|
+
return {
|
|
31
|
+
x: (client.x - rect.left) / scale,
|
|
32
|
+
y: (client.y - rect.top) / scale
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function contentBox(wrapper, el) {
|
|
36
|
+
const rect = wrapper.getBoundingClientRect();
|
|
37
|
+
const scale = wrapper.offsetWidth > 0 ? rect.width / wrapper.offsetWidth : 1;
|
|
38
|
+
const r = el.getBoundingClientRect();
|
|
39
|
+
return {
|
|
40
|
+
left: (r.left - rect.left) / scale,
|
|
41
|
+
top: (r.top - rect.top) / scale,
|
|
42
|
+
right: (r.right - rect.left) / scale,
|
|
43
|
+
bottom: (r.bottom - rect.top) / scale
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
var SELECTION_PAD = 12;
|
|
47
|
+
var SELECTION_SIDE_PAD = 36;
|
|
48
|
+
/**
|
|
49
|
+
* The outline around the selected jobs, in content coordinates: as tall as their rows and
|
|
50
|
+
* as wide as their nodes, so its circles sit clear of the nodes' own.
|
|
51
|
+
*/
|
|
52
|
+
function selectionBounds(wrapper, jobs) {
|
|
53
|
+
const found = jobs.flatMap((job) => {
|
|
54
|
+
const row = wrapper.querySelector(`[data-dag-job="${CSS.escape(job)}"]`);
|
|
55
|
+
const node = row?.closest("[id^=\"node_\"]");
|
|
56
|
+
const first = node?.querySelector("[data-dag-job]");
|
|
57
|
+
return row && node && first ? [{
|
|
58
|
+
row: contentBox(wrapper, row),
|
|
59
|
+
node: contentBox(wrapper, node),
|
|
60
|
+
first: contentBox(wrapper, first)
|
|
61
|
+
}] : [];
|
|
62
|
+
});
|
|
63
|
+
if (found.length === 0) return null;
|
|
64
|
+
const connectors = found.map((f) => f.node.top + 37 + f.row.top - f.first.top);
|
|
65
|
+
return {
|
|
66
|
+
left: Math.min(...found.map((f) => f.node.left)) - SELECTION_SIDE_PAD,
|
|
67
|
+
top: Math.min(...found.map((f) => f.row.top)) - SELECTION_PAD,
|
|
68
|
+
right: Math.max(...found.map((f) => f.node.right)) + SELECTION_SIDE_PAD,
|
|
69
|
+
bottom: Math.max(...found.map((f) => f.row.bottom)) + SELECTION_PAD,
|
|
70
|
+
connectorY: (Math.min(...connectors) + Math.max(...connectors)) / 2
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { EDITOR_HANDLE_CLASS, GraphEditorProvider, NO_EDGES, NO_PROBLEMS, NO_SELECTION, NO_STEPS, contentBox, labelsOf, problemsIn, sameEdge, sameStep, selectionBounds, toContent, useGraphEditor, useSelectedEdges, useUi };
|
package/dist/graph/index.d.ts
CHANGED
|
@@ -2,7 +2,11 @@ export { DependencyGraphPreview, default as DependencyGraph, type ViewMode, } fr
|
|
|
2
2
|
export type { DependencyGraphEditor, EditorProblem, RevealTarget, } from './editorApi.js';
|
|
3
3
|
export { trackDrag } from './editorPrimitives.js';
|
|
4
4
|
export { GRAPH_EDITOR_STRINGS, INPUTS_EDITOR_STRINGS } from './editorStrings.js';
|
|
5
|
+
export { JOB_FIELDS, RETRY_FIELDS, RUNS_ON_FIELDS, SSH_KEYS, STEP_FIELDS, STRATEGY_FIELDS, } from './GraphEditorDialogs.js';
|
|
5
6
|
export { inputTypes, offeredInputKeys } from './InputDialog.js';
|
|
7
|
+
export { InputsFormEditor } from './InputsEditor.js';
|
|
6
8
|
export { type ListedProblem, ProblemsButton } from './ProblemsButton.js';
|
|
7
9
|
export type { SettingsView } from './settingsViews.js';
|
|
10
|
+
export { type WorkflowActions, withFields } from './stepWith.js';
|
|
8
11
|
export type { RunLink } from './types.js';
|
|
12
|
+
export { INPUT_FORM_FIELDS, LINK_FIELDS, NEEDS_FIELDS, SESSION_FIELDS, WORKFLOW_FIELDS, WorkflowSettingsDialog, } from './WorkflowSettingsDialog.js';
|
package/dist/graph/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import DependencyGraph, { DependencyGraphPreview } from "./DependencyGraph.js";
|
|
2
1
|
import { trackDrag } from "./editorPrimitives.js";
|
|
2
|
+
import DependencyGraph, { DependencyGraphPreview } from "./DependencyGraph.js";
|
|
3
3
|
import { GRAPH_EDITOR_STRINGS, INPUTS_EDITOR_STRINGS } from "./editorStrings.js";
|
|
4
4
|
import { inputTypes, offeredInputKeys } from "./InputDialog.js";
|
|
5
|
+
import { withFields } from "./stepWith.js";
|
|
6
|
+
import { JOB_FIELDS, RETRY_FIELDS, RUNS_ON_FIELDS, SSH_KEYS, STEP_FIELDS, STRATEGY_FIELDS } from "./GraphEditorDialogs.js";
|
|
5
7
|
import { ProblemsButton } from "./ProblemsButton.js";
|
|
6
|
-
|
|
8
|
+
import { InputsFormEditor } from "./InputsEditor.js";
|
|
9
|
+
import { INPUT_FORM_FIELDS, LINK_FIELDS, NEEDS_FIELDS, SESSION_FIELDS, WORKFLOW_FIELDS, WorkflowSettingsDialog } from "./WorkflowSettingsDialog.js";
|
|
10
|
+
export { DependencyGraph, DependencyGraphPreview, GRAPH_EDITOR_STRINGS, INPUTS_EDITOR_STRINGS, INPUT_FORM_FIELDS, InputsFormEditor, JOB_FIELDS, LINK_FIELDS, NEEDS_FIELDS, ProblemsButton, RETRY_FIELDS, RUNS_ON_FIELDS, SESSION_FIELDS, SSH_KEYS, STEP_FIELDS, STRATEGY_FIELDS, WORKFLOW_FIELDS, WorkflowSettingsDialog, inputTypes, offeredInputKeys, trackDrag, withFields };
|