@gntik-ai/templates 0.9.0 → 0.10.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/dist/index.d.ts +13 -5
- package/dist/index.js +93 -67
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +1 -1
- package/src/keyboard-shortcuts/Page.test.tsx +39 -1
- package/src/keyboard-shortcuts/ShortcutSheet.tsx +3 -2
- package/src/keyboard-shortcuts/data.ts +28 -14
- package/src/resource-detail/Page.labels.test.tsx +67 -0
- package/src/resource-detail/Page.tsx +18 -7
- package/src/resource-detail/examples/Labels.tsx +18 -0
- package/src/resource-detail/examples/Spanish.tsx +11 -0
- package/src/resource-detail/template.meta.ts +1 -1
- package/src/settings-profile/fixtures/editable.snap +1 -1
- package/src/shared/ConsoleShell.test.tsx +13 -0
- package/src/shared/ConsoleShell.tsx +35 -30
- package/src/workflow-builder/Page.test.tsx +103 -2
- package/src/workflow-builder/Page.tsx +34 -8
- package/src/workflow-builder/examples/SaveGraph.tsx +17 -0
- package/src/workflow-builder/template.meta.ts +1 -1
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CodePanel,
|
|
3
|
+
builderNodes,
|
|
4
|
+
builderEdges,
|
|
5
|
+
cleanFlowGraph,
|
|
3
6
|
type CodePanelProps,
|
|
4
7
|
FlowBuilder,
|
|
5
8
|
type ConsoleEntry,
|
|
6
9
|
type FlowBuilderProps,
|
|
7
10
|
type FlowEdge,
|
|
11
|
+
type FlowGraph,
|
|
8
12
|
type FlowNode,
|
|
9
13
|
type FlowNodeKind,
|
|
10
14
|
type PaletteItem,
|
|
11
15
|
} from '@gntik-ai/blocks';
|
|
12
16
|
import { Save, Upload } from '@gntik-ai/icons';
|
|
13
|
-
import { Breadcrumbs, Button, CanvasLayout, StatusTag, type BreadcrumbItem } from '@gntik-ai/ui';
|
|
14
|
-
import { useState } from 'react';
|
|
17
|
+
import { Breadcrumbs, Button, CanvasLayout, StatusTag, useI18n, type BreadcrumbItem } from '@gntik-ai/ui';
|
|
18
|
+
import { useRef, useState } from 'react';
|
|
15
19
|
import { flowBuilderBreadcrumbs, flowBuilderContent } from './data';
|
|
16
20
|
|
|
17
21
|
export interface FlowBuilderPageProps<Kind extends string = FlowNodeKind> extends Pick<
|
|
@@ -25,6 +29,8 @@ export interface FlowBuilderPageProps<Kind extends string = FlowNodeKind> extend
|
|
|
25
29
|
| 'mode'
|
|
26
30
|
| 'nodeStatus'
|
|
27
31
|
| 'onNodeSelect'
|
|
32
|
+
| 'graph'
|
|
33
|
+
| 'onGraphChange'
|
|
28
34
|
> {
|
|
29
35
|
/** Optional companion code panel; run mode forces readOnly. */
|
|
30
36
|
codePanel?: CodePanelProps;
|
|
@@ -43,7 +49,7 @@ export interface FlowBuilderPageProps<Kind extends string = FlowNodeKind> extend
|
|
|
43
49
|
defaultConsoleEntries: ConsoleEntry[];
|
|
44
50
|
onConsoleChange: (entries: ConsoleEntry[]) => void;
|
|
45
51
|
onConsoleClear: () => void;
|
|
46
|
-
onSave: () => void | Promise<void>;
|
|
52
|
+
onSave: (graph: FlowGraph<Kind>) => void | Promise<void>;
|
|
47
53
|
onPublish: () => void;
|
|
48
54
|
breadcrumbs: BreadcrumbItem[];
|
|
49
55
|
/** Builder height in px on wide screens. */
|
|
@@ -61,8 +67,8 @@ export default function FlowBuilderPage<Kind extends string = FlowNodeKind>(
|
|
|
61
67
|
const {
|
|
62
68
|
title = flowBuilderContent.title,
|
|
63
69
|
status = flowBuilderContent.status,
|
|
64
|
-
nodes,
|
|
65
|
-
edges,
|
|
70
|
+
nodes = builderNodes,
|
|
71
|
+
edges = builderEdges,
|
|
66
72
|
palette,
|
|
67
73
|
onRun,
|
|
68
74
|
consoleEntries,
|
|
@@ -80,19 +86,32 @@ export default function FlowBuilderPage<Kind extends string = FlowNodeKind>(
|
|
|
80
86
|
mode,
|
|
81
87
|
nodeStatus,
|
|
82
88
|
onNodeSelect,
|
|
89
|
+
graph,
|
|
90
|
+
onGraphChange,
|
|
83
91
|
codePanel,
|
|
84
92
|
breadcrumbs = flowBuilderBreadcrumbs,
|
|
85
93
|
height = 640,
|
|
86
94
|
fullScreen = true,
|
|
87
95
|
} = props;
|
|
88
96
|
const [saving, setSaving] = useState(false);
|
|
89
|
-
const [
|
|
97
|
+
const [initialGraph] = useState<FlowGraph<Kind>>(() => ({ nodes, edges }));
|
|
98
|
+
const [latestGraph, setLatestGraph] = useState<FlowGraph<Kind>>();
|
|
99
|
+
const [saveState, setSaveState] = useState<'initial' | 'unsaved' | 'saved'>('initial');
|
|
100
|
+
const revision = useRef(0);
|
|
101
|
+
const { t } = useI18n();
|
|
102
|
+
const savedNote =
|
|
103
|
+
saveState === 'initial'
|
|
104
|
+
? flowBuilderContent.lastSaved
|
|
105
|
+
: t(saveState === 'unsaved' ? 'actionBar.unsaved' : 'actionBar.saved');
|
|
90
106
|
|
|
91
107
|
const save = async () => {
|
|
92
108
|
setSaving(true);
|
|
109
|
+
const savedRevision = revision.current;
|
|
93
110
|
try {
|
|
94
|
-
await onSave?.();
|
|
95
|
-
|
|
111
|
+
await onSave?.(cleanFlowGraph(graph ?? latestGraph ?? initialGraph));
|
|
112
|
+
if (revision.current === savedRevision) setSaveState('saved');
|
|
113
|
+
} catch {
|
|
114
|
+
// The host owns persistence errors; retain the unsaved note and allow a retry.
|
|
96
115
|
} finally {
|
|
97
116
|
setSaving(false);
|
|
98
117
|
}
|
|
@@ -133,6 +152,13 @@ export default function FlowBuilderPage<Kind extends string = FlowNodeKind>(
|
|
|
133
152
|
title={title}
|
|
134
153
|
nodes={nodes}
|
|
135
154
|
edges={edges}
|
|
155
|
+
graph={graph}
|
|
156
|
+
onGraphChange={(next, change) => {
|
|
157
|
+
revision.current += 1;
|
|
158
|
+
setLatestGraph(next);
|
|
159
|
+
setSaveState('unsaved');
|
|
160
|
+
onGraphChange?.(next, change);
|
|
161
|
+
}}
|
|
136
162
|
palette={palette}
|
|
137
163
|
onRun={onRun}
|
|
138
164
|
consoleEntries={consoleEntries}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { builderNodes, builderEdges, cleanFlowGraph, type FlowGraph } from '@gntik-ai/blocks';
|
|
3
|
+
import WorkflowBuilderPage from '../Page';
|
|
4
|
+
|
|
5
|
+
/** Save the graph currently on screen to browser storage; products own persistence. */
|
|
6
|
+
export default function SaveGraph() {
|
|
7
|
+
const [graph, setGraph] = useState<FlowGraph>(() => cleanFlowGraph({ nodes: builderNodes, edges: builderEdges }));
|
|
8
|
+
return (
|
|
9
|
+
<WorkflowBuilderPage
|
|
10
|
+
graph={graph}
|
|
11
|
+
onGraphChange={setGraph}
|
|
12
|
+
onSave={(next) => {
|
|
13
|
+
localStorage.setItem('workflow-builder-example', JSON.stringify(next));
|
|
14
|
+
}}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -6,7 +6,7 @@ export const meta: TemplateMeta = {
|
|
|
6
6
|
priority: 'P3',
|
|
7
7
|
status: 'experimental',
|
|
8
8
|
description:
|
|
9
|
-
'Full-screen workflow editor with optional nodeTypes, onConnectEdge, onConnect, isValidConnection, onConnectionRejected, validationIssues, mode="run", nodeStatus and onNodeSelect passed to FlowBuilder; codePanel passes readOnly, markers, onValidityChange, onMonacoReady and loader to an optional CodePanel (branch, parallel, sequence and worker-backed YAML examples). The app must import \'@gntik-ai/flow/styles.css\' once for the canvas styles. Optional nodeTypes, onConnectEdge, onConnect, isValidConnection, onConnectionRejected, validationIssues, mode, nodeStatus and onNodeSelect pass to FlowBuilder unchanged (see its branch, parallel and sequence examples). mode="run" hides Save, Publish and the saved notice, and preserves focus and node selection. Optional codePanel renders a companion CodePanel and passes all its extensions: readOnly, markers, onValidityChange, onMonacoReady and loader; run mode forces its readOnly to true. For example, WorkflowBuilderPage mode="run" nodeStatus={{step:"running"}} onNodeSelect={openDetails}; codePanel={{files,problems:[],loader:yamlLoader,markers}} uses consumer-owned same-origin YAML workers and inline schemas.',
|
|
9
|
+
'onSave(graph) receives the graph on screen (controlled graph, latest reported edit, or initial starter graph), without selected, dragging or measured node fields. Zero-argument handlers remain valid. Optional graph and onGraphChange pass through to FlowBuilder. Committed edits show actionBar.unsaved, a resolved save shows actionBar.saved, and a rejected save retains unsaved status and releases loading. Edits during a pending save remain unsaved. examples/SaveGraph.tsx shows a controlled graph and onSave={(graph) => localStorage.setItem("workflow-builder-example", JSON.stringify(graph))}; hosts own persistence. Full-screen workflow editor with optional nodeTypes, onConnectEdge, onConnect, isValidConnection, onConnectionRejected, validationIssues, mode="run", nodeStatus and onNodeSelect passed to FlowBuilder; codePanel passes readOnly, markers, onValidityChange, onMonacoReady and loader to an optional CodePanel (branch, parallel, sequence and worker-backed YAML examples). The app must import \'@gntik-ai/flow/styles.css\' once for the canvas styles. Optional nodeTypes, onConnectEdge, onConnect, isValidConnection, onConnectionRejected, validationIssues, mode, nodeStatus and onNodeSelect pass to FlowBuilder unchanged (see its branch, parallel and sequence examples). mode="run" hides Save, Publish and the saved notice, and preserves focus and node selection. Optional codePanel renders a companion CodePanel and passes all its extensions: readOnly, markers, onValidityChange, onMonacoReady and loader; run mode forces its readOnly to true. For example, WorkflowBuilderPage mode="run" nodeStatus={{step:"running"}} onNodeSelect={openDetails}; codePanel={{files,problems:[],loader:yamlLoader,markers}} uses consumer-owned same-origin YAML workers and inline schemas.',
|
|
10
10
|
layout: 'CanvasLayout',
|
|
11
11
|
blocks: ['flow-builder', 'code-panel'],
|
|
12
12
|
};
|