@parallelworks/ui 0.39.0 → 0.40.1
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 +6 -0
- package/dist/ai/agent/permissions.d.ts +5 -4
- package/dist/ai/agent/permissions.js +15 -6
- package/dist/ai/ai.css +4 -2
- package/dist/ai/components/agent/PermissionPicker.d.ts +3 -1
- package/dist/ai/components/agent/PermissionPicker.js +2 -2
- package/dist/ai/ui/Markdown.js +4 -1
- package/dist/graph/Connectors.d.ts +11 -0
- package/dist/graph/Connectors.js +158 -0
- package/dist/graph/DependencyGraph.d.ts +2 -16
- package/dist/graph/DependencyGraph.js +375 -725
- package/dist/graph/GraphNode.d.ts +35 -0
- package/dist/graph/GraphNode.js +70 -0
- package/dist/graph/MatrixGroup.d.ts +4 -8
- package/dist/graph/MatrixGroup.js +27 -40
- package/dist/graph/connectorGeometry.d.ts +44 -0
- package/dist/graph/connectorGeometry.js +103 -0
- package/dist/graph/graphLayout.d.ts +24 -0
- package/dist/graph/graphLayout.js +205 -0
- package/dist/graph/lineage.d.ts +51 -0
- package/dist/graph/lineage.js +94 -0
- package/dist/graph/store.d.ts +10 -0
- package/dist/graph/store.js +25 -0
- package/dist/graph/viewport.d.ts +40 -0
- package/dist/graph/viewport.js +56 -0
- package/dist/styles.css +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -99,6 +99,12 @@ const engine = { ...createWorkflowEngine(), editing: workflowPackage }
|
|
|
99
99
|
|
|
100
100
|
Without `editing`, the graph lays its jobs out by their needs alone.
|
|
101
101
|
|
|
102
|
+
The graph draws thousands of jobs, and subworkflows opened inline several levels deep. Layout is
|
|
103
|
+
near linear in jobs and needs; hovering a job re-renders only the nodes and connectors whose
|
|
104
|
+
highlight changes, and panning, zooming and a running run's clock re-render none. The view fits
|
|
105
|
+
the whole graph, zooms out to 1% to show the shape of the largest, and in to 200% to read a node;
|
|
106
|
+
the wheel zooms by the same factor at every scale, about the pointer.
|
|
107
|
+
|
|
102
108
|
The editor checks YAML against the schemas a host registers with `configureEditorYaml` from
|
|
103
109
|
`@parallelworks/ui/editor`. The job, step, input and settings dialogs edit their YAML at their own
|
|
104
110
|
paths (`JOB_YAML_PATH`, `STEP_YAML_PATH`, `INPUT_YAML_PATH`, `SETTINGS_YAML_PATH`); `settingsSchemas`
|
|
@@ -4,8 +4,9 @@ export type PermissionMode = (typeof REQUESTABLE_PERMISSION_MODES)[number];
|
|
|
4
4
|
export type NamedPermissionMode = (typeof NAMED_PERMISSION_MODES)[number];
|
|
5
5
|
export declare const DEFAULT_PERMISSION_MODE: PermissionMode;
|
|
6
6
|
export declare function isNamedPermissionMode(mode: string | undefined): mode is NamedPermissionMode;
|
|
7
|
-
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
8
|
-
* so a menu offers only what that
|
|
9
|
-
|
|
10
|
-
export declare function
|
|
7
|
+
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
8
|
+
* and refuses a mode it does not know, so a menu offers only what that
|
|
9
|
+
* machine will honour. */
|
|
10
|
+
export declare function permissionModesUpTo(ceiling: string | undefined, accepted?: readonly string[] | undefined): PermissionMode[];
|
|
11
|
+
export declare function clampPermissionMode(mode: PermissionMode, ceiling: string | undefined, accepted?: readonly string[] | undefined): PermissionMode;
|
|
11
12
|
export {};
|
|
@@ -17,14 +17,23 @@ var PERMISSION_RANK = {
|
|
|
17
17
|
function isNamedPermissionMode(mode) {
|
|
18
18
|
return NAMED_PERMISSION_MODES.some((m) => m === mode);
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/** What a machine that does not list its modes accepts: it predates auto. */
|
|
21
|
+
var LEGACY_PERMISSION_MODES = [
|
|
22
|
+
"read-only",
|
|
23
|
+
"accept-edits",
|
|
24
|
+
"bypass-permissions",
|
|
25
|
+
"plan"
|
|
26
|
+
];
|
|
27
|
+
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
28
|
+
* and refuses a mode it does not know, so a menu offers only what that
|
|
29
|
+
* machine will honour. */
|
|
30
|
+
function permissionModesUpTo(ceiling, accepted = void 0) {
|
|
23
31
|
const max = isNamedPermissionMode(ceiling) ? PERMISSION_RANK[ceiling] : PERMISSION_RANK[DEFAULT_PERMISSION_MODE];
|
|
24
|
-
|
|
32
|
+
const known = accepted?.length ? accepted : LEGACY_PERMISSION_MODES;
|
|
33
|
+
return REQUESTABLE_PERMISSION_MODES.filter((m) => PERMISSION_RANK[m] <= max && known.includes(m));
|
|
25
34
|
}
|
|
26
|
-
function clampPermissionMode(mode, ceiling) {
|
|
27
|
-
const offered = permissionModesUpTo(ceiling);
|
|
35
|
+
function clampPermissionMode(mode, ceiling, accepted = void 0) {
|
|
36
|
+
const offered = permissionModesUpTo(ceiling, accepted);
|
|
28
37
|
return offered.includes(mode) ? mode : offered[offered.length - 1] ?? "read-only";
|
|
29
38
|
}
|
|
30
39
|
//#endregion
|
package/dist/ai/ai.css
CHANGED
|
@@ -471,8 +471,10 @@
|
|
|
471
471
|
color: color-mix(in oklab, var(--theme-accent, #06354f) 70%, var(--theme-panel-bg, #fff));
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
/* The reply being written ends in the accent's caret.
|
|
475
|
-
|
|
474
|
+
/* The reply being written ends in the accent's caret. Matched by a class, not by the inline style
|
|
475
|
+
that sets the caret: a rule reaching past an element its style attribute selects makes every
|
|
476
|
+
inline style change anywhere on the page restyle that element's whole subtree. */
|
|
477
|
+
.chat-thread .markdown-streaming > :last-child::after {
|
|
476
478
|
color: var(--chat-accent);
|
|
477
479
|
}
|
|
478
480
|
|
|
@@ -2,10 +2,12 @@ import { type PermissionMode } from '../../agent/permissions.js';
|
|
|
2
2
|
/** What the agent may do, as a toolbar chip: the choices stop at the machine's
|
|
3
3
|
* ceiling, and a mode set elsewhere (plan, or one past the ceiling) still
|
|
4
4
|
* shows by name. */
|
|
5
|
-
export default function PermissionPicker({ mode, onPick, ceiling, disabled, }: {
|
|
5
|
+
export default function PermissionPicker({ mode, onPick, ceiling, accepted, disabled, }: {
|
|
6
6
|
mode: string | undefined;
|
|
7
7
|
onPick: (mode: PermissionMode) => void;
|
|
8
8
|
/** The machine's remoteMaxPermissionMode; unknown offers the daemon default. */
|
|
9
9
|
ceiling: string | undefined;
|
|
10
|
+
/** The machine's permissionModes; unknown offers what a machine that predates the list accepts. */
|
|
11
|
+
accepted?: readonly string[] | undefined;
|
|
10
12
|
disabled?: boolean;
|
|
11
13
|
}): import("react").JSX.Element;
|
|
@@ -13,11 +13,11 @@ var LABEL_KEY = {
|
|
|
13
13
|
/** What the agent may do, as a toolbar chip: the choices stop at the machine's
|
|
14
14
|
* ceiling, and a mode set elsewhere (plan, or one past the ceiling) still
|
|
15
15
|
* shows by name. */
|
|
16
|
-
function PermissionPicker({ mode, onPick, ceiling, disabled = false }) {
|
|
16
|
+
function PermissionPicker({ mode, onPick, ceiling, accepted, disabled = false }) {
|
|
17
17
|
const t = useChatConfig().strings.permissions;
|
|
18
18
|
const current = mode ?? "accept-edits";
|
|
19
19
|
const named = isNamedPermissionMode(current) ? current : void 0;
|
|
20
|
-
const options = permissionModesUpTo(ceiling).map((m) => ({
|
|
20
|
+
const options = permissionModesUpTo(ceiling, accepted).map((m) => ({
|
|
21
21
|
value: m,
|
|
22
22
|
label: t[LABEL_KEY[m]],
|
|
23
23
|
description: t[`${LABEL_KEY[m]}Hint`]
|
package/dist/ai/ui/Markdown.js
CHANGED
|
@@ -57,7 +57,10 @@ function Markdown({ children, isStreaming }) {
|
|
|
57
57
|
children: /* @__PURE__ */ jsx(Streamdown, {
|
|
58
58
|
mode: isStreaming ? "streaming" : "static",
|
|
59
59
|
...isStreaming === void 0 ? {} : { isAnimating: isStreaming },
|
|
60
|
-
...isStreaming ? {
|
|
60
|
+
...isStreaming ? {
|
|
61
|
+
caret: "block",
|
|
62
|
+
className: "markdown-streaming"
|
|
63
|
+
} : {},
|
|
61
64
|
plugins,
|
|
62
65
|
rehypePlugins,
|
|
63
66
|
shikiTheme,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DrawnEdge } from './connectorGeometry.js';
|
|
2
|
+
/**
|
|
3
|
+
* A graph level's connectors in three layers: lines behind the nodes, the lineage shown above the
|
|
4
|
+
* nodes outside it, and the dots above all. However many connectors there are, the lines are one
|
|
5
|
+
* path and the dots two, each dimming as one; only the lineage shown draws connectors of its own,
|
|
6
|
+
* and a hover re-renders only those.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ConnectorLayers: import("react").MemoExoticComponent<({ edges, zBase, }: {
|
|
9
|
+
edges: DrawnEdge[];
|
|
10
|
+
zBase: number;
|
|
11
|
+
}) => import("react").JSX.Element>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { highlightOf, useLineage, useLineageStore } from "./lineage.js";
|
|
2
|
+
import { dimClasses } from "./GraphNode.js";
|
|
3
|
+
import { memo, useMemo } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/graph/Connectors.tsx
|
|
6
|
+
var CONNECTOR_COLOR = "var(--theme-border)";
|
|
7
|
+
var HIGHLIGHT_COLOR = "var(--theme-element)";
|
|
8
|
+
var PANEL_COLOR = "var(--theme-panel-bg)";
|
|
9
|
+
var STROKE = 6;
|
|
10
|
+
var KEYFRAMES = [
|
|
11
|
+
"@keyframes dag-draw-in{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}",
|
|
12
|
+
"@keyframes dag-draw-out{from{stroke-dashoffset:0}to{stroke-dashoffset:1}}",
|
|
13
|
+
`@keyframes dag-light{from{fill:${CONNECTOR_COLOR}}to{fill:${HIGHLIGHT_COLOR}}}`,
|
|
14
|
+
`@keyframes dag-unlight{from{fill:${HIGHLIGHT_COLOR}}to{fill:${CONNECTOR_COLOR}}}`
|
|
15
|
+
].join("");
|
|
16
|
+
/** Plays the named keyframes once `delay` seconds pass, holding the first until then and the last after. */
|
|
17
|
+
var play = (name, seconds, delay) => ({ animation: `${name} ${seconds}s ease ${delay}s both` });
|
|
18
|
+
var LitEdge = memo(function LitEdge({ d, reversed, highlight, animT }) {
|
|
19
|
+
const { lit, delay, back } = highlightOf(highlight);
|
|
20
|
+
return /* @__PURE__ */ jsx("path", {
|
|
21
|
+
d: back ? reversed : d,
|
|
22
|
+
pathLength: 1,
|
|
23
|
+
fill: "none",
|
|
24
|
+
stroke: HIGHLIGHT_COLOR,
|
|
25
|
+
strokeWidth: STROKE,
|
|
26
|
+
strokeDasharray: "1 1",
|
|
27
|
+
strokeDashoffset: lit ? 0 : 1,
|
|
28
|
+
style: play(lit ? "dag-draw-in" : "dag-draw-out", animT, delay)
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
var LitDot = memo(function LitDot({ x, y, highlight, animT }) {
|
|
32
|
+
const { lit, delay } = highlightOf(highlight);
|
|
33
|
+
return /* @__PURE__ */ jsx("circle", {
|
|
34
|
+
cx: x,
|
|
35
|
+
cy: y,
|
|
36
|
+
r: 8,
|
|
37
|
+
fill: lit ? HIGHLIGHT_COLOR : CONNECTOR_COLOR,
|
|
38
|
+
style: play(lit ? "dag-light" : "dag-unlight", animT, delay)
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
function LitEdges({ edges }) {
|
|
42
|
+
const lineage = useLineageStore();
|
|
43
|
+
useLineage((l) => l.version, 0);
|
|
44
|
+
if (!lineage?.showing) return null;
|
|
45
|
+
const { animT } = lineage;
|
|
46
|
+
const lines = [];
|
|
47
|
+
const lit = [];
|
|
48
|
+
for (const { from, to, path } of edges) {
|
|
49
|
+
const highlight = lineage.between(from, to);
|
|
50
|
+
if (highlight) {
|
|
51
|
+
if (lineage.hovering) lines.push(path.d);
|
|
52
|
+
lit.push(/* @__PURE__ */ jsx(LitEdge, {
|
|
53
|
+
d: path.d,
|
|
54
|
+
reversed: path.reversed,
|
|
55
|
+
highlight,
|
|
56
|
+
animT
|
|
57
|
+
}, `${from}→${to}`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [lines.length > 0 && /* @__PURE__ */ jsx("path", {
|
|
61
|
+
d: lines.join(" "),
|
|
62
|
+
stroke: CONNECTOR_COLOR,
|
|
63
|
+
strokeWidth: STROKE,
|
|
64
|
+
fill: "none"
|
|
65
|
+
}), lit] });
|
|
66
|
+
}
|
|
67
|
+
function LitDots({ ports }) {
|
|
68
|
+
const lineage = useLineageStore();
|
|
69
|
+
useLineage((l) => l.version, 0);
|
|
70
|
+
if (!lineage?.showing) return null;
|
|
71
|
+
const at = [];
|
|
72
|
+
const lit = [];
|
|
73
|
+
for (const [key, port] of ports) {
|
|
74
|
+
const highlight = lineage.between(port.job, port.job);
|
|
75
|
+
if (highlight) {
|
|
76
|
+
at.push(port);
|
|
77
|
+
lit.push(/* @__PURE__ */ jsx(LitDot, {
|
|
78
|
+
x: port.x,
|
|
79
|
+
y: port.y,
|
|
80
|
+
highlight,
|
|
81
|
+
animT: lineage.animT
|
|
82
|
+
}, key));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [at.length > 0 && /* @__PURE__ */ jsx("path", {
|
|
86
|
+
d: squaresAt(at),
|
|
87
|
+
fill: PANEL_COLOR
|
|
88
|
+
}), lit] });
|
|
89
|
+
}
|
|
90
|
+
/** A square of panel color around each dot, which leaves a gap at the node's edge. */
|
|
91
|
+
var squaresAt = (at) => at.map(({ x, y }) => `M ${x - 16} ${y - 16} h 32 v 32 h -32 Z`).join(" ");
|
|
92
|
+
/**
|
|
93
|
+
* A graph level's connectors in three layers: lines behind the nodes, the lineage shown above the
|
|
94
|
+
* nodes outside it, and the dots above all. However many connectors there are, the lines are one
|
|
95
|
+
* path and the dots two, each dimming as one; only the lineage shown draws connectors of its own,
|
|
96
|
+
* and a hover re-renders only those.
|
|
97
|
+
*/
|
|
98
|
+
var ConnectorLayers = memo(function ConnectorLayers({ edges, zBase }) {
|
|
99
|
+
const fade = dimClasses(useLineageStore()?.depth ?? 0).edge;
|
|
100
|
+
const { lines, squares, dots, ports } = useMemo(() => {
|
|
101
|
+
const ports = /* @__PURE__ */ new Map();
|
|
102
|
+
for (const { from, to, start, end } of edges) {
|
|
103
|
+
ports.set(`${from}\0out`, {
|
|
104
|
+
job: from,
|
|
105
|
+
...start
|
|
106
|
+
});
|
|
107
|
+
ports.set(`${to}\0in`, {
|
|
108
|
+
job: to,
|
|
109
|
+
...end
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const at = [...ports.values()];
|
|
113
|
+
return {
|
|
114
|
+
lines: edges.map(({ path }) => path.d).join(" "),
|
|
115
|
+
squares: squaresAt(at),
|
|
116
|
+
dots: at.map(({ x, y }) => `M ${x - 8} ${y} a 8 8 0 1 0 16 0 a 8 8 0 1 0 -16 0`).join(" "),
|
|
117
|
+
ports
|
|
118
|
+
};
|
|
119
|
+
}, [edges]);
|
|
120
|
+
const keyframes = /* @__PURE__ */ jsx("style", {
|
|
121
|
+
href: "dependency-graph-highlight",
|
|
122
|
+
precedence: "default",
|
|
123
|
+
children: KEYFRAMES
|
|
124
|
+
});
|
|
125
|
+
if (edges.length === 0) return keyframes;
|
|
126
|
+
const layer = (z, children) => /* @__PURE__ */ jsx("svg", {
|
|
127
|
+
"aria-hidden": "true",
|
|
128
|
+
overflow: "visible",
|
|
129
|
+
className: "absolute pointer-events-none",
|
|
130
|
+
style: { zIndex: z + zBase },
|
|
131
|
+
children
|
|
132
|
+
});
|
|
133
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
134
|
+
keyframes,
|
|
135
|
+
layer(-1, /* @__PURE__ */ jsx("path", {
|
|
136
|
+
d: lines,
|
|
137
|
+
stroke: CONNECTOR_COLOR,
|
|
138
|
+
strokeWidth: STROKE,
|
|
139
|
+
fill: "none",
|
|
140
|
+
className: fade
|
|
141
|
+
})),
|
|
142
|
+
layer(20, /* @__PURE__ */ jsx(LitEdges, { edges })),
|
|
143
|
+
layer(30, /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
144
|
+
/* @__PURE__ */ jsx("path", {
|
|
145
|
+
d: squares,
|
|
146
|
+
fill: PANEL_COLOR
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ jsx("path", {
|
|
149
|
+
d: dots,
|
|
150
|
+
fill: CONNECTOR_COLOR,
|
|
151
|
+
className: fade
|
|
152
|
+
}),
|
|
153
|
+
/* @__PURE__ */ jsx(LitDots, { ports })
|
|
154
|
+
] }))
|
|
155
|
+
] });
|
|
156
|
+
});
|
|
157
|
+
//#endregion
|
|
158
|
+
export { ConnectorLayers };
|
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import type { GraphLayout } from '../editing.js';
|
|
3
|
-
import type { MatrixGroup, WorkflowEngine } from '../engine.js';
|
|
4
2
|
import type { DependencyGraphEditor } from './editorApi.js';
|
|
5
3
|
import { type RunLink, type WorkflowJob } from './types.js';
|
|
6
4
|
export type ViewMode = 'dag' | 'tree';
|
|
7
5
|
export declare const addCleanupSteps: ({ jobs }: {
|
|
8
6
|
jobs: Record<string, WorkflowJob>;
|
|
9
7
|
}) => Record<string, WorkflowJob>;
|
|
10
|
-
export declare function computeGraphLayout(engine: WorkflowEngine, jobs: Record<string, WorkflowJob>, matrixGroups: Record<string, MatrixGroup>, layout?: GraphLayout): {
|
|
11
|
-
dependencyCols: string[][][];
|
|
12
|
-
/** Each box's row; above a box, rows the layout leaves empty stay as space. */
|
|
13
|
-
rowSlots: number[][];
|
|
14
|
-
/** Each column's number; between two drawn columns, numbers skipped stay as space. */
|
|
15
|
-
colSlots: number[];
|
|
16
|
-
directDeps: Record<string, string[]>;
|
|
17
|
-
displayJobs: Record<string, WorkflowJob>;
|
|
18
|
-
ancestorDists: Record<string, Map<string, number>>;
|
|
19
|
-
descendantDists: Record<string, Map<string, number>>;
|
|
20
|
-
};
|
|
21
|
-
export declare function intoView(start: number, end: number, low: number, high: number): number;
|
|
22
8
|
interface GraphRun {
|
|
23
9
|
id?: string | undefined;
|
|
24
10
|
number?: number | undefined;
|
|
@@ -44,7 +30,7 @@ export default function DependencyGraph({ run, preview, removeBorder, viewMode,
|
|
|
44
30
|
/** The workflow as written, for what the editor's expressions can read. */
|
|
45
31
|
workflow?: Record<string, unknown> | undefined;
|
|
46
32
|
fixedHeight?: string | undefined;
|
|
47
|
-
}):
|
|
33
|
+
}): import("react").JSX.Element | undefined;
|
|
48
34
|
export declare function DependencyGraphPreview(inputs: {
|
|
49
35
|
yml?: Record<string, unknown>;
|
|
50
36
|
removeBorder?: boolean;
|
|
@@ -52,5 +38,5 @@ export declare function DependencyGraphPreview(inputs: {
|
|
|
52
38
|
editor?: DependencyGraphEditor | undefined;
|
|
53
39
|
/** A fixed panel height; without one the panel is only as tall as the graph. */
|
|
54
40
|
height?: string | undefined;
|
|
55
|
-
}):
|
|
41
|
+
}): import("react").JSX.Element;
|
|
56
42
|
export {};
|