@calliopelabs/ui 0.7.0 → 0.12.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 +266 -3
- package/dist/appearance/AppearancePicker.js +4 -2
- package/dist/appearance/AppearanceProvider.d.ts +10 -2
- package/dist/appearance/AppearanceProvider.js +6 -2
- package/dist/appearance/AppearanceScript.d.ts +15 -4
- package/dist/appearance/AppearanceScript.js +11 -5
- package/dist/appearance/appearance.js +1 -1
- package/dist/appearance/boot.d.ts +13 -2
- package/dist/appearance/boot.js +35 -5
- package/dist/appearance/product.d.ts +13 -0
- package/dist/appearance/product.js +2 -2
- package/dist/appearance/runtime-brand.d.ts +106 -0
- package/dist/appearance/runtime-brand.js +420 -0
- package/dist/css/appearance.js +147 -0
- package/dist/css/calliope.css +1221 -0
- package/dist/css/components.css +729 -0
- package/dist/css/tokens.css +491 -0
- package/dist/data/DefinitionList.d.ts +16 -0
- package/dist/data/DefinitionList.js +9 -0
- package/dist/data/Stat.d.ts +25 -0
- package/dist/data/Stat.js +19 -0
- package/dist/data/Tag.d.ts +21 -0
- package/dist/data/Tag.js +18 -0
- package/dist/data/types.d.ts +3 -2
- package/dist/data/use-local-table.d.ts +36 -0
- package/dist/data/use-local-table.js +127 -0
- package/dist/index.d.ts +44 -3
- package/dist/index.js +40 -1
- package/dist/nav/Nav.d.ts +2 -1
- package/dist/nav/Nav.js +34 -15
- package/dist/nav/model.d.ts +21 -0
- package/dist/page/PageHeader.d.ts +5 -3
- package/dist/page/PageHeader.js +5 -4
- package/dist/page/Panel.d.ts +19 -0
- package/dist/page/Panel.js +12 -0
- package/dist/primitives/Avatar.d.ts +37 -0
- package/dist/primitives/Avatar.js +43 -0
- package/dist/primitives/Field.d.ts +33 -0
- package/dist/primitives/Field.js +30 -0
- package/dist/primitives/Segmented.d.ts +30 -0
- package/dist/primitives/Segmented.js +20 -0
- package/dist/primitives/Select.d.ts +40 -0
- package/dist/primitives/Select.js +47 -0
- package/dist/primitives/Switch.d.ts +7 -0
- package/dist/primitives/Switch.js +8 -0
- package/dist/primitives/Textarea.d.ts +3 -0
- package/dist/primitives/Textarea.js +6 -0
- package/dist/provider/messages.d.ts +430 -0
- package/dist/provider/messages.js +666 -0
- package/dist/shell/BrandMark.d.ts +25 -3
- package/dist/shell/BrandMark.js +34 -4
- package/dist/shell/RailPanel.js +2 -2
- package/dist/shell/RailTree.js +2 -2
- package/dist/shell/Sidebar.d.ts +9 -2
- package/dist/shell/Sidebar.js +10 -4
- package/dist/shell/UserMenu.d.ts +3 -1
- package/dist/shell/UserMenu.js +3 -11
- package/dist/shell/rail-boot.d.ts +28 -0
- package/dist/shell/rail-boot.js +45 -0
- package/dist/shell/rail-state.d.ts +6 -1
- package/dist/shell/rail-state.js +18 -2
- package/dist/styles/preset.css +4 -0
- package/dist/styles/theme.css +36 -13
- package/dist/theme/css-layer.d.ts +19 -0
- package/dist/theme/css-layer.js +171 -0
- package/dist/theme/css.d.ts +1 -0
- package/dist/theme/css.js +45 -9
- package/dist/workflow/ArtifactCard.d.ts +47 -0
- package/dist/workflow/ArtifactCard.js +45 -0
- package/dist/workflow/DocumentReview.d.ts +74 -0
- package/dist/workflow/DocumentReview.js +54 -0
- package/dist/workflow/FormReview.d.ts +53 -0
- package/dist/workflow/FormReview.js +83 -0
- package/dist/workflow/GateBadge.d.ts +11 -0
- package/dist/workflow/GateBadge.js +14 -0
- package/dist/workflow/MediaReview.d.ts +63 -0
- package/dist/workflow/MediaReview.js +166 -0
- package/dist/workflow/ReviewPanel.d.ts +41 -0
- package/dist/workflow/ReviewPanel.js +59 -0
- package/dist/workflow/RunTimeline.d.ts +25 -0
- package/dist/workflow/RunTimeline.js +56 -0
- package/dist/workflow/WorkflowCard.d.ts +39 -0
- package/dist/workflow/WorkflowCard.js +56 -0
- package/dist/workflow/WorkflowGallery.d.ts +43 -0
- package/dist/workflow/WorkflowGallery.js +69 -0
- package/dist/workflow/WorkflowQuestionCard.d.ts +18 -0
- package/dist/workflow/WorkflowQuestionCard.js +78 -0
- package/dist/workflow/canvas/WorkflowCanvas.d.ts +39 -0
- package/dist/workflow/canvas/WorkflowCanvas.js +648 -0
- package/dist/workflow/definition.d.ts +408 -0
- package/dist/workflow/definition.js +411 -0
- package/dist/workflow/editor/WorkflowDiff.d.ts +17 -0
- package/dist/workflow/editor/WorkflowDiff.js +25 -0
- package/dist/workflow/editor/WorkflowImportPreview.d.ts +19 -0
- package/dist/workflow/editor/WorkflowImportPreview.js +50 -0
- package/dist/workflow/editor/WorkflowInspector.d.ts +40 -0
- package/dist/workflow/editor/WorkflowInspector.js +349 -0
- package/dist/workflow/editor/WorkflowJsonView.d.ts +16 -0
- package/dist/workflow/editor/WorkflowJsonView.js +40 -0
- package/dist/workflow/editor/WorkflowPalette.d.ts +16 -0
- package/dist/workflow/editor/WorkflowPalette.js +36 -0
- package/dist/workflow/editor/WorkflowProblems.d.ts +20 -0
- package/dist/workflow/editor/WorkflowProblems.js +51 -0
- package/dist/workflow/editor/WorkflowWorkspace.d.ts +49 -0
- package/dist/workflow/editor/WorkflowWorkspace.js +154 -0
- package/dist/workflow/gallery.d.ts +21 -0
- package/dist/workflow/gallery.js +29 -0
- package/dist/workflow/layout.d.ts +49 -0
- package/dist/workflow/layout.js +162 -0
- package/dist/workflow/model.d.ts +50 -0
- package/dist/workflow/model.js +7 -0
- package/dist/workflow/palette.d.ts +34 -0
- package/dist/workflow/palette.js +70 -0
- package/dist/workflow/portable.d.ts +162 -0
- package/dist/workflow/portable.js +547 -0
- package/dist/workflow/problems.d.ts +38 -0
- package/dist/workflow/problems.js +409 -0
- package/dist/workflow/question.d.ts +26 -0
- package/dist/workflow/question.js +1 -0
- package/dist/workflow/run.d.ts +94 -0
- package/dist/workflow/run.js +31 -0
- package/dist/workflow/workspace.d.ts +33 -0
- package/dist/workflow/workspace.js +17 -0
- package/package.json +6 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { LayoutGridIcon, ListIcon, PlusIcon, SearchIcon, UploadIcon } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { PageHeader } from "../page/PageHeader.js";
|
|
7
|
+
import { Button } from "../primitives/Button.js";
|
|
8
|
+
import { Input } from "../primitives/Input.js";
|
|
9
|
+
import { Segmented } from "../primitives/Segmented.js";
|
|
10
|
+
import { Select } from "../primitives/Select.js";
|
|
11
|
+
import { useUI } from "../provider/UIProvider.js";
|
|
12
|
+
import { EmptyState } from "../states/EmptyState.js";
|
|
13
|
+
import { ErrorState } from "../states/ErrorState.js";
|
|
14
|
+
import { ANY_FILTER, DEFAULT_WORKFLOW_FILTERS, isFiltered, } from "./gallery.js";
|
|
15
|
+
import { WorkflowCard, WorkflowCardSkeleton } from "./WorkflowCard.js";
|
|
16
|
+
const SCOPES = ["private", "project", "organization", "shared"];
|
|
17
|
+
const STATUSES = ["draft", "published"];
|
|
18
|
+
/**
|
|
19
|
+
* The workflow gallery: a full page of saved definitions over WorkflowCard,
|
|
20
|
+
* with search, a module filter, scope and status filters, grid and list
|
|
21
|
+
* views, and create and import when the host permits them. Host-driven: the
|
|
22
|
+
* filters are controlled and the host returns the matching workflows (a host
|
|
23
|
+
* holding the whole set uses `filterWorkflows`). Empty and no-matches are
|
|
24
|
+
* different states.
|
|
25
|
+
*/
|
|
26
|
+
export function WorkflowGallery({ title, description, workflows, modules, filters, onFiltersChange, view: viewProp, defaultView = "grid", onViewChange, loading = false, error, onRetry, onCreate, onImport, actionsFor, renderPreview, className, }) {
|
|
27
|
+
const { messages } = useUI();
|
|
28
|
+
const t = messages.workflow;
|
|
29
|
+
const [ownView, setOwnView] = React.useState(defaultView);
|
|
30
|
+
const view = viewProp ?? ownView;
|
|
31
|
+
const searchId = React.useId();
|
|
32
|
+
const heading = title ?? t.galleryTitle;
|
|
33
|
+
const label = typeof heading === "string" ? heading : t.galleryTitle;
|
|
34
|
+
const filtered = isFiltered(filters);
|
|
35
|
+
const set = (patch) => onFiltersChange({ ...filters, ...patch });
|
|
36
|
+
const create = onCreate && (_jsxs(Button, { onClick: onCreate, children: [_jsx(PlusIcon, { "aria-hidden": true }), t.create] }));
|
|
37
|
+
const importButton = onImport && (_jsxs(Button, { variant: "outline", onClick: onImport, children: [_jsx(UploadIcon, { "aria-hidden": true }), t.import] }));
|
|
38
|
+
let body;
|
|
39
|
+
if (error) {
|
|
40
|
+
body = _jsx(ErrorState, { title: t.loadFailed, error: error, onRetry: onRetry });
|
|
41
|
+
}
|
|
42
|
+
else if (loading) {
|
|
43
|
+
body = (_jsxs("div", { "aria-busy": true, className: view === "grid" ? GRID : LIST, children: [_jsx("p", { role: "status", className: "sr-only", children: messages.states.loading }), Array.from({ length: 6 }, (_, i) => (_jsx(WorkflowCardSkeleton, { layout: view === "grid" ? "card" : "row" }, i)))] }));
|
|
44
|
+
}
|
|
45
|
+
else if (workflows.length === 0) {
|
|
46
|
+
body = filtered ? (_jsx(EmptyState, { icon: _jsx(SearchIcon, {}), title: t.noMatches, description: t.noMatchesDescription, secondary: _jsx(Button, { variant: "outline", onClick: () => onFiltersChange(DEFAULT_WORKFLOW_FILTERS), children: t.clearFilters }) })) : (_jsx(EmptyState, { title: t.empty, description: t.emptyDescription, secondary: (create || importButton) && (_jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [create, importButton] })) }));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
body = (_jsx("ul", { "aria-label": label, className: view === "grid" ? GRID : LIST, children: workflows.map((workflow) => (_jsx("li", { className: "flex min-w-0", children: _jsx(WorkflowCard, { workflow: workflow, layout: view === "grid" ? "card" : "row", headingLevel: "h2", preview: renderPreview?.(workflow), className: "flex-1", ...actionsFor?.(workflow) }) }, workflow.id))) }));
|
|
50
|
+
}
|
|
51
|
+
return (_jsxs("div", { "data-slot": "workflow-gallery", className: cn("flex min-w-0 flex-col gap-4", className), children: [_jsx(PageHeader, { title: heading, description: description, primaryAction: (create || importButton) && (_jsxs("div", { className: "flex flex-wrap gap-2", children: [importButton, create] })) }), _jsxs("div", { role: "search", "aria-label": t.filters, className: "flex min-w-0 flex-col gap-3 md:flex-row md:flex-wrap md:items-center", children: [_jsxs("div", { className: "relative min-w-0 md:w-64", children: [_jsx(SearchIcon, { "aria-hidden": true, className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" }), _jsx("label", { htmlFor: searchId, className: "sr-only", children: t.search }), _jsx(Input, { id: searchId, type: "search", value: filters.query, placeholder: t.search, onChange: (event) => set({ query: event.target.value }), className: "pl-8" })] }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx(Select, { "aria-label": t.scopeFilter, size: "sm", value: filters.scope, onValueChange: (scope) => set({ scope: scope }), options: [
|
|
52
|
+
{ value: ANY_FILTER, label: t.anyScope },
|
|
53
|
+
...SCOPES.map((scope) => ({ value: scope, label: t.scope(scope) })),
|
|
54
|
+
] }), _jsx(Select, { "aria-label": t.statusFilter, size: "sm", value: filters.status, onValueChange: (status) => set({ status: status }), options: [
|
|
55
|
+
{ value: ANY_FILTER, label: t.anyStatus },
|
|
56
|
+
...STATUSES.map((status) => ({ value: status, label: t.status(status) })),
|
|
57
|
+
] }), _jsx(Segmented, { label: t.view, iconOnly: true, size: "sm", value: view, onValueChange: (next) => {
|
|
58
|
+
setOwnView(next);
|
|
59
|
+
onViewChange?.(next);
|
|
60
|
+
}, options: [
|
|
61
|
+
{ value: "grid", label: t.grid, icon: LayoutGridIcon },
|
|
62
|
+
{ value: "list", label: t.list, icon: ListIcon },
|
|
63
|
+
] })] }), modules.length > 0 && (_jsx(Segmented, { label: t.moduleFilter, size: "sm", value: filters.module, onValueChange: (module) => set({ module }), options: [
|
|
64
|
+
{ value: ANY_FILTER, label: t.anyModule },
|
|
65
|
+
...modules.map((module) => ({ value: module.id, label: module.label })),
|
|
66
|
+
], className: "flex-wrap md:basis-full" }))] }), !loading && !error && (_jsx("p", { role: "status", className: "font-mono text-xs text-muted-foreground", children: t.count(workflows.length) })), body] }));
|
|
67
|
+
}
|
|
68
|
+
const GRID = "grid min-w-0 grid-cols-[repeat(auto-fill,minmax(min(100%,17rem),1fr))] gap-3";
|
|
69
|
+
const LIST = "flex min-w-0 flex-col gap-2";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { WorkflowQuestion, WorkflowQuestionAnswers, WorkflowQuestionState, WorkflowQuestionSubmission } from "./question.js";
|
|
3
|
+
export interface WorkflowQuestionCardProps {
|
|
4
|
+
question: WorkflowQuestion | null;
|
|
5
|
+
state: WorkflowQuestionState;
|
|
6
|
+
answers?: WorkflowQuestionAnswers;
|
|
7
|
+
onAnswersChange?: (answers: WorkflowQuestionAnswers) => void;
|
|
8
|
+
/** Synchronous intent notification. Set the controlled state before starting delivery. */
|
|
9
|
+
onSubmit?: (submission: WorkflowQuestionSubmission) => void;
|
|
10
|
+
/** Read current delivery state. This action never submits an answer. */
|
|
11
|
+
onRefresh?: () => void;
|
|
12
|
+
canAnswer?: boolean;
|
|
13
|
+
reason?: string;
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Controlled projection: a submit callback is never evidence of runtime acknowledgment. */
|
|
18
|
+
export declare function WorkflowQuestionCard(props: WorkflowQuestionCardProps): React.JSX.Element;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { Button } from "../primitives/Button.js";
|
|
6
|
+
import { Checkbox } from "../primitives/Checkbox.js";
|
|
7
|
+
import { Field } from "../primitives/Field.js";
|
|
8
|
+
import { Textarea } from "../primitives/Textarea.js";
|
|
9
|
+
import { Skeleton } from "../primitives/Skeleton.js";
|
|
10
|
+
import { useUI } from "../provider/UIProvider.js";
|
|
11
|
+
import { StatusBadge } from "../status/StatusBadge.js";
|
|
12
|
+
const TONE = {
|
|
13
|
+
waiting: "warn",
|
|
14
|
+
submitting: "info",
|
|
15
|
+
accepted: "info",
|
|
16
|
+
acknowledged: "ok",
|
|
17
|
+
expired: "muted",
|
|
18
|
+
"delivery-unknown": "warn",
|
|
19
|
+
unavailable: "error",
|
|
20
|
+
};
|
|
21
|
+
function textValid(value) {
|
|
22
|
+
return value.trim().length > 0 && Array.from(value).length <= 4000 && !value.includes("\0");
|
|
23
|
+
}
|
|
24
|
+
function answerValid(field, value) {
|
|
25
|
+
if (field.multiple)
|
|
26
|
+
return (value !== undefined &&
|
|
27
|
+
typeof value !== "string" &&
|
|
28
|
+
value.length > 0 &&
|
|
29
|
+
value.length <= 8 &&
|
|
30
|
+
new Set(value).size === value.length &&
|
|
31
|
+
value.every(textValid));
|
|
32
|
+
return typeof value === "string" && textValid(value);
|
|
33
|
+
}
|
|
34
|
+
/** Controlled projection: a submit callback is never evidence of runtime acknowledgment. */
|
|
35
|
+
export function WorkflowQuestionCard(props) {
|
|
36
|
+
return (_jsx(QuestionBody, { ...props }, JSON.stringify([props.question?.id, props.question?.revision])));
|
|
37
|
+
}
|
|
38
|
+
function QuestionBody({ question, state, answers = {}, onAnswersChange, onSubmit, onRefresh, canAnswer = true, reason, loading = false, className, }) {
|
|
39
|
+
const t = useUI().messages.workflowQuestion;
|
|
40
|
+
const id = React.useId();
|
|
41
|
+
const [attempted, setAttempted] = React.useState(false);
|
|
42
|
+
const editable = !loading && state === "waiting" && canAnswer && !!onSubmit && !!onAnswersChange;
|
|
43
|
+
const set = (field, value) => {
|
|
44
|
+
if (editable)
|
|
45
|
+
onAnswersChange?.({ ...answers, [field]: value });
|
|
46
|
+
};
|
|
47
|
+
const submit = (event) => {
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
if (!editable || !question?.fields.length)
|
|
50
|
+
return;
|
|
51
|
+
setAttempted(true);
|
|
52
|
+
if (question.fields.some((field) => !answerValid(field, answers[field.id])))
|
|
53
|
+
return;
|
|
54
|
+
const selected = Object.fromEntries(question.fields.map((field) => {
|
|
55
|
+
const value = answers[field.id];
|
|
56
|
+
return [field.id, typeof value === "string" ? value : [...value]];
|
|
57
|
+
}));
|
|
58
|
+
onSubmit?.({ questionId: question.id, revision: question.revision, answers: selected });
|
|
59
|
+
};
|
|
60
|
+
return (_jsxs("section", { "data-slot": "workflow-question-card", "aria-labelledby": `${id}-title`, "aria-busy": loading || state === "submitting", className: cn("space-y-4 rounded-lg border bg-card p-4 text-card-foreground", className), children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsx("h3", { id: `${id}-title`, className: "font-medium break-words", children: question?.title ?? t.title }), question?.worker && (_jsx("p", { className: "text-sm text-muted-foreground", children: t.worker(question.worker) }))] }), !loading && (_jsx(StatusBadge, { tone: TONE[state], pulse: state === "submitting" || state === "accepted", children: t.state(state) }))] }), _jsx("p", { role: "status", className: "text-sm text-muted-foreground", children: loading ? t.loading : t.description(state) }), reason && _jsx("p", { className: "text-sm text-muted-foreground", children: reason }), loading ? (_jsxs("div", { className: "space-y-3", children: [_jsx(Skeleton, { className: "h-5 w-1/2" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : !question?.fields.length ? (state === "waiting" ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t.empty })) : null) : (_jsxs("form", { onSubmit: submit, noValidate: true, className: "space-y-5", children: [question.fields.map((field, index) => {
|
|
61
|
+
const value = answers[field.id];
|
|
62
|
+
const selected = value !== undefined && typeof value !== "string" ? value : [];
|
|
63
|
+
const labels = new Set(field.options?.map((option) => option.label));
|
|
64
|
+
const custom = selected.filter((answer) => !labels.has(answer));
|
|
65
|
+
const error = attempted && !answerValid(field, value) ? t.invalidAnswer : undefined;
|
|
66
|
+
return (_jsxs("fieldset", { disabled: !editable, className: "min-w-0 space-y-3", children: [_jsx("legend", { className: "mb-3 text-sm font-medium break-words whitespace-pre-wrap", children: field.text }), !!field.options?.length && (_jsx("div", { className: "flex flex-wrap gap-2", children: field.options.map((option, optionIndex) => field.multiple ? (_jsxs("label", { htmlFor: `${id}-${index}-${optionIndex}`, className: "flex max-w-full min-w-0 items-start gap-2 rounded-lg border p-2 text-sm", children: [_jsx(Checkbox, { id: `${id}-${index}-${optionIndex}`, disabled: !editable, checked: selected.includes(option.label), onCheckedChange: (checked) => set(field.id, checked === true
|
|
67
|
+
? [
|
|
68
|
+
...selected.filter((answer) => answer !== option.label),
|
|
69
|
+
option.label,
|
|
70
|
+
]
|
|
71
|
+
: selected.filter((answer) => answer !== option.label)) }), _jsxs("span", { className: "min-w-0 break-words", children: [option.label, option.description && (_jsxs("span", { className: "mt-1 block text-xs text-muted-foreground", children: [" ", option.description] }))] })] }, option.label)) : (_jsx(Button, { type: "button", variant: "outline", disabled: !editable, className: "h-auto max-w-full min-w-0 py-2 text-left", "aria-pressed": value === option.label, onClick: () => set(field.id, option.label), children: _jsxs("span", { className: "min-w-0 break-words whitespace-normal", children: [option.label, option.description && (_jsxs("span", { className: "mt-1 block text-xs text-muted-foreground", children: [" ", option.description] }))] }) }, option.label))) })), _jsx(Field, { label: field.multiple ? (field.options?.length ? t.otherAnswers : t.answers) : t.answer, hint: field.multiple ? t.multipleHint : undefined, error: error, children: _jsx(Textarea, { readOnly: !editable, disabled: !editable, rows: 3, value: field.multiple ? custom.join("\n") : typeof value === "string" ? value : "", onChange: (event) => set(field.id, field.multiple
|
|
72
|
+
? [
|
|
73
|
+
...selected.filter((answer) => labels.has(answer)),
|
|
74
|
+
...(event.target.value === "" ? [] : event.target.value.split("\n")),
|
|
75
|
+
]
|
|
76
|
+
: event.target.value) }) })] }, field.id));
|
|
77
|
+
}), !canAnswer && state === "waiting" && !reason && (_jsx("p", { className: "text-sm text-muted-foreground", children: t.readOnly })), onSubmit && (state === "waiting" || state === "submitting") && (_jsx(Button, { type: "submit", disabled: !editable, children: state === "submitting" ? t.sending : t.send }))] })), onRefresh && (state === "delivery-unknown" || state === "unavailable") && (_jsx(Button, { type: "button", variant: "outline", onClick: onRefresh, children: t.refresh }))] }));
|
|
78
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type WorkflowDefinition, type WorkflowRegistry, type WorkflowSelection } from "../definition.js";
|
|
3
|
+
import type { WorkflowProblem } from "../problems.js";
|
|
4
|
+
/** The canvas never zooms out past this: below it a node's words stop being readable. */
|
|
5
|
+
export declare const READABLE_ZOOM = 0.7;
|
|
6
|
+
export interface WorkflowCanvasProps {
|
|
7
|
+
definition: WorkflowDefinition;
|
|
8
|
+
/** The flow to draw; the main flow when omitted. */
|
|
9
|
+
flow?: string;
|
|
10
|
+
registry: WorkflowRegistry;
|
|
11
|
+
/** Every edit, as the whole next definition. Omit it, or set `readOnly`, for a viewer. */
|
|
12
|
+
onChange?: (definition: WorkflowDefinition) => void;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
selection?: WorkflowSelection | null;
|
|
15
|
+
onSelectionChange?: (selection: WorkflowSelection | null) => void;
|
|
16
|
+
/** From `checkDefinition`: nodes and edges with problems are marked. */
|
|
17
|
+
problems?: readonly WorkflowProblem[];
|
|
18
|
+
/** Folded regions by id (`regionsOf`). Uncontrolled from `defaultFolded` when omitted. */
|
|
19
|
+
folded?: readonly string[];
|
|
20
|
+
defaultFolded?: readonly string[];
|
|
21
|
+
onFoldedChange?: (folded: string[]) => void;
|
|
22
|
+
/** Sub-flow nodes shown in place, by id. */
|
|
23
|
+
defaultInPlace?: readonly string[];
|
|
24
|
+
/** Names the canvas; the editor's copy by default. */
|
|
25
|
+
label?: string;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One flow of a workflow definition, drawn and edited on a pannable,
|
|
30
|
+
* zoomable canvas. The run goes left to right: work comes into a node's
|
|
31
|
+
* input on the left and leaves by its exits on the right; typed inputs and
|
|
32
|
+
* outputs sit in rows below the header; a loop leaves from the bottom right
|
|
33
|
+
* and goes back into an earlier node's bottom left. `canConnect` decides
|
|
34
|
+
* what snaps, so an invalid edge never lands. Loop bodies and fan-out
|
|
35
|
+
* branches fold; a sub-flow shows in place. Every drag has a keyboard
|
|
36
|
+
* equivalent: Tab to a node, Enter to select, arrows to move, Delete to
|
|
37
|
+
* remove; Add and Connect in the toolbar.
|
|
38
|
+
*/
|
|
39
|
+
export declare function WorkflowCanvas(props: WorkflowCanvasProps): React.JSX.Element;
|