@arronqzy/react-blueprint 1.0.17 → 1.0.19
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/package.json +4 -4
- package/src/BlueprintContextMenu.tsx +1 -1
- package/src/BlueprintNodeConfigSidebar.tsx +17 -3
- package/src/components/BlueprintExecutionLogPanel.tsx +10 -2
- package/src/components/BlueprintLibrarySelect.tsx +1 -1
- package/src/components/BlueprintMetaDialog.tsx +1 -1
- package/src/components/BlueprintNodeSwitchTaskDialog.tsx +1 -1
- package/src/components/BlueprintPanelToolbar.tsx +10 -2
- package/src/components/ClockNodeConfigPanel.tsx +1 -1
- package/src/components/ConfigHintIcon.tsx +1 -1
- package/src/components/FetchNodeConfigPanel.tsx +1 -1
- package/src/components/FetchUrlAutocomplete.tsx +1 -1
- package/src/components/JsonNodeConfigPanel.tsx +55 -10
- package/src/components/LogicNodeConfigPanel.tsx +1 -1
- package/src/components/StorageKeyAutocomplete.tsx +142 -0
- package/src/components/StorageNodeConfigPanel.tsx +20 -13
- package/src/components/ViewElementMultiSelect.tsx +1 -1
- package/src/hooks/useBlueprintFlowState.ts +1 -1
- package/src/index.tsx +30 -27
- package/src/nodes/AndFlowNode.tsx +1 -1
- package/src/nodes/BlueprintFlowNode.tsx +1 -1
- package/src/nodes/BlueprintNodeCard.tsx +1 -1
- package/src/nodes/BlueprintNodeShell.tsx +1 -1
- package/src/nodes/ClockFlowNode.tsx +1 -1
- package/src/nodes/EventFlowNode.tsx +1 -1
- package/src/nodes/FetchFlowNode.tsx +1 -1
- package/src/nodes/JsonFlowNode.tsx +1 -1
- package/src/nodes/LifecycleFlowNode.tsx +1 -1
- package/src/nodes/LogicFlowNode.tsx +1 -1
- package/src/nodes/StorageFlowNode.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arronqzy/react-blueprint",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "",
|
|
5
5
|
"files": [
|
|
6
6
|
"index.ts",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"dayjs": "^1.11.13",
|
|
19
19
|
"react-dom": "^19",
|
|
20
20
|
"@xyflow/react": "^12.9.3",
|
|
21
|
-
"@arronqzy/blueprint-dsl": "1.0.
|
|
21
|
+
"@arronqzy/blueprint-dsl": "1.0.15",
|
|
22
22
|
"@arronqzy/react-rx-store": "1.1.8",
|
|
23
|
-
"@arronqzy/
|
|
24
|
-
"@arronqzy/
|
|
23
|
+
"@arronqzy/ui": "1.0.9",
|
|
24
|
+
"@arronqzy/i18n": "0.1.15"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/react": "^19.0.1",
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
type PageLifecyclePhase,
|
|
17
17
|
type ViewEventType,
|
|
18
18
|
} from "@arronqzy/blueprint-dsl";
|
|
19
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
19
|
+
import { EnsureI18nProvider, useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
20
20
|
|
|
21
21
|
import { FetchNodeConfigPanel } from "./components/FetchNodeConfigPanel";
|
|
22
22
|
import { ClockNodeConfigPanel } from "./components/ClockNodeConfigPanel";
|
|
@@ -96,7 +96,15 @@ const ROLE_LABEL_KEYS: Record<BlueprintNodeRole, string> = {
|
|
|
96
96
|
event: "blueprint.config.roleEvent",
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
-
export function BlueprintNodeConfigSidebar({
|
|
99
|
+
export function BlueprintNodeConfigSidebar(props: BlueprintNodeConfigSidebarProps) {
|
|
100
|
+
return (
|
|
101
|
+
<EnsureI18nProvider>
|
|
102
|
+
<BlueprintNodeConfigSidebarInner {...props} />
|
|
103
|
+
</EnsureI18nProvider>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function BlueprintNodeConfigSidebarInner({
|
|
100
108
|
node,
|
|
101
109
|
graphNodes = [],
|
|
102
110
|
graphEdges = [],
|
|
@@ -400,7 +408,13 @@ export function BlueprintNodeConfigSidebar({
|
|
|
400
408
|
) : null}
|
|
401
409
|
|
|
402
410
|
{configSource === "json" ? (
|
|
403
|
-
<JsonNodeConfigPanel
|
|
411
|
+
<JsonNodeConfigPanel
|
|
412
|
+
node={node}
|
|
413
|
+
graphNodes={graphNodes}
|
|
414
|
+
graphEdges={graphEdges}
|
|
415
|
+
traceEntries={traceEntries}
|
|
416
|
+
onUpdateNode={onUpdateNode}
|
|
417
|
+
/>
|
|
404
418
|
) : null}
|
|
405
419
|
|
|
406
420
|
{configSource === "storage" ? (
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
type ExecutionTraceEntry,
|
|
21
21
|
type PageLifecyclePhase,
|
|
22
22
|
} from "@arronqzy/blueprint-dsl";
|
|
23
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
23
|
+
import { EnsureI18nProvider, useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
24
24
|
import { getLifecyclePhaseLabel } from "../graph/document";
|
|
25
25
|
|
|
26
26
|
import type { ExecutionLogSettings } from "../library/execution-log-settings";
|
|
@@ -140,7 +140,15 @@ export type BlueprintExecutionLogPanelProps = {
|
|
|
140
140
|
lifecyclePhase?: string;
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
export function BlueprintExecutionLogPanel({
|
|
143
|
+
export function BlueprintExecutionLogPanel(props: BlueprintExecutionLogPanelProps) {
|
|
144
|
+
return (
|
|
145
|
+
<EnsureI18nProvider>
|
|
146
|
+
<BlueprintExecutionLogPanelInner {...props} />
|
|
147
|
+
</EnsureI18nProvider>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function BlueprintExecutionLogPanelInner({
|
|
144
152
|
entries,
|
|
145
153
|
settings,
|
|
146
154
|
onUpdateSettings,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import type { BlueprintLibraryListItem } from "../library/types";
|
|
19
19
|
import type { LifecycleNodeOption } from "../hooks/useBlueprintDebugSession";
|
|
20
20
|
import { type PageLifecyclePhase } from "@arronqzy/blueprint-dsl";
|
|
21
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
21
|
+
import { EnsureI18nProvider, useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
22
22
|
import { getLifecyclePhaseLabel } from "../graph/document";
|
|
23
23
|
|
|
24
24
|
export type BlueprintRenameDialogProps = {
|
|
@@ -393,7 +393,15 @@ function LibraryRow({
|
|
|
393
393
|
);
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
export function BlueprintPanelToolbar({
|
|
396
|
+
export function BlueprintPanelToolbar(props: BlueprintPanelToolbarProps) {
|
|
397
|
+
return (
|
|
398
|
+
<EnsureI18nProvider>
|
|
399
|
+
<BlueprintPanelToolbarInner {...props} />
|
|
400
|
+
</EnsureI18nProvider>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function BlueprintPanelToolbarInner({
|
|
397
405
|
items,
|
|
398
406
|
activeId,
|
|
399
407
|
currentBlueprintLabel,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, type ChangeEvent } from "react";
|
|
2
2
|
import { Input } from "@arronqzy/ui";
|
|
3
3
|
import type { ClockNodeConfig } from "@arronqzy/blueprint-dsl";
|
|
4
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
4
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
5
5
|
|
|
6
6
|
import type { BlueprintGraphNode } from "../graph/document";
|
|
7
7
|
import { resolveNodeClockConfig } from "../graph/document";
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
useFetchDebugTask,
|
|
46
46
|
useSwaggerLoadTask,
|
|
47
47
|
} from "../fetch-config-task-store";
|
|
48
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
48
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
49
49
|
|
|
50
50
|
import { ConfigFieldLabel, ConfigHintIcon, ConfigSectionTitle } from "./ConfigHintIcon";
|
|
51
51
|
import { FetchUrlAutocomplete } from "./FetchUrlAutocomplete";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { cn } from "@arronqzy/ui";
|
|
3
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
3
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
4
4
|
import {
|
|
5
5
|
buildEndpointSuggestions,
|
|
6
6
|
filterEndpointSuggestions,
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState, type ChangeEvent } from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type {
|
|
3
|
+
ExecutionTraceEntry,
|
|
4
|
+
JsonNodeConfig,
|
|
5
|
+
} from "@arronqzy/blueprint-dsl";
|
|
6
|
+
import {
|
|
7
|
+
latestTraceOutputsByNode,
|
|
8
|
+
resolveFetchIncomingScope,
|
|
9
|
+
resolveFetchScopeAutocompleteRoot,
|
|
10
|
+
validateJsonStringAllowingScope,
|
|
11
|
+
} from "@arronqzy/blueprint-dsl";
|
|
12
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
5
13
|
|
|
6
|
-
import type { BlueprintGraphNode } from "../graph/document";
|
|
14
|
+
import type { BlueprintGraphEdge, BlueprintGraphNode } from "../graph/document";
|
|
7
15
|
import { resolveNodeJsonConfig } from "../graph/document";
|
|
8
|
-
import { ConfigSectionTitle } from "./ConfigHintIcon";
|
|
16
|
+
import { ConfigHintIcon, ConfigSectionTitle } from "./ConfigHintIcon";
|
|
17
|
+
import { ScopeTemplateAutocompleteHost } from "./ScopeTemplateAutocompleteHost";
|
|
9
18
|
|
|
10
19
|
export type JsonNodeConfigPanelProps = {
|
|
11
20
|
node: BlueprintGraphNode;
|
|
21
|
+
graphNodes?: BlueprintGraphNode[];
|
|
22
|
+
graphEdges?: BlueprintGraphEdge[];
|
|
23
|
+
traceEntries?: ExecutionTraceEntry[];
|
|
12
24
|
onUpdateNode: (
|
|
13
25
|
nodeId: string,
|
|
14
26
|
patch: Partial<Pick<BlueprintGraphNode, "jsonConfig" | "configSource">>
|
|
@@ -27,25 +39,44 @@ function patchJsonConfig(
|
|
|
27
39
|
|
|
28
40
|
export function JsonNodeConfigPanel({
|
|
29
41
|
node,
|
|
42
|
+
graphNodes = [],
|
|
43
|
+
graphEdges = [],
|
|
44
|
+
traceEntries = [],
|
|
30
45
|
onUpdateNode,
|
|
31
46
|
}: JsonNodeConfigPanelProps) {
|
|
32
47
|
const { t } = useI18n();
|
|
48
|
+
const [formEl, setFormEl] = useState<HTMLDivElement | null>(null);
|
|
33
49
|
const jsonConfig = resolveNodeJsonConfig(node);
|
|
34
50
|
const [draftError, setDraftError] = useState<string | null>(null);
|
|
35
51
|
|
|
52
|
+
const incomingScope = useMemo(() => {
|
|
53
|
+
const outputs = latestTraceOutputsByNode(traceEntries);
|
|
54
|
+
return resolveFetchIncomingScope({
|
|
55
|
+
fetchNodeId: node.id,
|
|
56
|
+
nodes: graphNodes,
|
|
57
|
+
edges: graphEdges,
|
|
58
|
+
getOutput: (sourceId, port) => outputs[sourceId]?.[port],
|
|
59
|
+
});
|
|
60
|
+
}, [graphEdges, graphNodes, node.id, traceEntries]);
|
|
61
|
+
const autocompleteScope = resolveFetchScopeAutocompleteRoot(incomingScope);
|
|
62
|
+
|
|
36
63
|
const storedValidation = useMemo(
|
|
37
|
-
() =>
|
|
64
|
+
() => validateJsonStringAllowingScope(jsonConfig.jsonString),
|
|
38
65
|
[jsonConfig.jsonString]
|
|
39
66
|
);
|
|
40
67
|
|
|
41
68
|
const parseError =
|
|
42
69
|
draftError ??
|
|
43
70
|
(storedValidation.ok ? null : storedValidation.error);
|
|
71
|
+
const deferredScope =
|
|
72
|
+
storedValidation.ok && "deferred" in storedValidation
|
|
73
|
+
? storedValidation.deferred === true
|
|
74
|
+
: false;
|
|
44
75
|
|
|
45
76
|
const handleChange = useCallback(
|
|
46
77
|
(e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
47
78
|
const jsonString = e.target.value;
|
|
48
|
-
const result =
|
|
79
|
+
const result = validateJsonStringAllowingScope(jsonString);
|
|
49
80
|
setDraftError(result.ok ? null : result.error);
|
|
50
81
|
onUpdateNode(node.id, patchJsonConfig(node, { jsonString }));
|
|
51
82
|
},
|
|
@@ -53,15 +84,25 @@ export function JsonNodeConfigPanel({
|
|
|
53
84
|
);
|
|
54
85
|
|
|
55
86
|
return (
|
|
56
|
-
<div
|
|
87
|
+
<div
|
|
88
|
+
ref={setFormEl}
|
|
89
|
+
className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5"
|
|
90
|
+
>
|
|
91
|
+
<ScopeTemplateAutocompleteHost
|
|
92
|
+
scope={autocompleteScope}
|
|
93
|
+
container={formEl}
|
|
94
|
+
/>
|
|
57
95
|
<ConfigSectionTitle
|
|
58
96
|
title={t("blueprint.config.jsonTitle")}
|
|
59
97
|
hint={t("blueprint.config.jsonHint")}
|
|
60
98
|
/>
|
|
61
99
|
|
|
62
100
|
<label className="block space-y-1">
|
|
63
|
-
<span className="text-muted-foreground">
|
|
101
|
+
<span className="inline-flex items-center gap-1 text-muted-foreground">
|
|
64
102
|
{t("blueprint.config.jsonContent")}
|
|
103
|
+
<ConfigHintIcon label={t("blueprint.config.jsonContent")}>
|
|
104
|
+
{t("blueprint.config.jsonScopeHint")}
|
|
105
|
+
</ConfigHintIcon>
|
|
65
106
|
</span>
|
|
66
107
|
<textarea
|
|
67
108
|
value={jsonConfig.jsonString}
|
|
@@ -69,12 +110,16 @@ export function JsonNodeConfigPanel({
|
|
|
69
110
|
rows={12}
|
|
70
111
|
spellCheck={false}
|
|
71
112
|
className="w-full rounded-md border border-input bg-background px-2 py-1.5 font-mono text-[11px] leading-relaxed text-foreground shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-primary"
|
|
72
|
-
placeholder={'{\n "key": "value"\n}'}
|
|
113
|
+
placeholder={'{\n "key": "{scope?.value}"\n}'}
|
|
73
114
|
/>
|
|
74
115
|
{parseError ? (
|
|
75
116
|
<p className="text-[11px] text-destructive">
|
|
76
117
|
{t("blueprint.config.jsonFormatError", { error: parseError })}
|
|
77
118
|
</p>
|
|
119
|
+
) : deferredScope ? (
|
|
120
|
+
<p className="text-[11px] text-muted-foreground">
|
|
121
|
+
{t("blueprint.config.jsonScopeDeferred")}
|
|
122
|
+
</p>
|
|
78
123
|
) : (
|
|
79
124
|
<p className="text-[11px] text-muted-foreground">
|
|
80
125
|
{t("blueprint.config.jsonFormatOk")}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState, type ChangeEvent } from "react";
|
|
2
2
|
import { validateLogicSourceCode } from "@arronqzy/blueprint-dsl";
|
|
3
3
|
import type { LogicNodeConfig } from "@arronqzy/blueprint-dsl";
|
|
4
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
4
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
5
5
|
|
|
6
6
|
import type { BlueprintGraphNode } from "../graph/document";
|
|
7
7
|
import { resolveNodeLogicConfig } from "../graph/document";
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { cn } from "@arronqzy/ui";
|
|
3
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
4
|
+
import {
|
|
5
|
+
filterStorageKeySuggestions,
|
|
6
|
+
listBrowserStorageKeys,
|
|
7
|
+
type StorageKind,
|
|
8
|
+
} from "@arronqzy/blueprint-dsl";
|
|
9
|
+
|
|
10
|
+
export type StorageKeyAutocompleteProps = {
|
|
11
|
+
value: string;
|
|
12
|
+
/** One or more storage targets whose keys should be suggested. */
|
|
13
|
+
storages: StorageKind | readonly StorageKind[];
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
onChange: (value: string) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function StorageKeyAutocomplete({
|
|
19
|
+
value,
|
|
20
|
+
storages,
|
|
21
|
+
placeholder,
|
|
22
|
+
onChange,
|
|
23
|
+
}: StorageKeyAutocompleteProps) {
|
|
24
|
+
const { t } = useI18n();
|
|
25
|
+
const [open, setOpen] = useState(false);
|
|
26
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
27
|
+
const [keys, setKeys] = useState<string[]>([]);
|
|
28
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
29
|
+
|
|
30
|
+
const storageSignature = useMemo(() => {
|
|
31
|
+
const list = Array.isArray(storages) ? [...storages] : [storages];
|
|
32
|
+
return list.join(",");
|
|
33
|
+
}, [storages]);
|
|
34
|
+
|
|
35
|
+
const refreshKeys = useCallback(() => {
|
|
36
|
+
const list = Array.isArray(storages) ? storages : [storages];
|
|
37
|
+
setKeys(listBrowserStorageKeys(list));
|
|
38
|
+
}, [storages]);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
refreshKeys();
|
|
42
|
+
}, [refreshKeys, storageSignature]);
|
|
43
|
+
|
|
44
|
+
const filtered = useMemo(
|
|
45
|
+
() => filterStorageKeySuggestions(keys, value),
|
|
46
|
+
[keys, value]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setActiveIndex(0);
|
|
51
|
+
}, [value, filtered.length]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!open) return;
|
|
55
|
+
const onPointerDown = (event: MouseEvent) => {
|
|
56
|
+
if (!containerRef.current?.contains(event.target as globalThis.Node)) {
|
|
57
|
+
setOpen(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
window.addEventListener("pointerdown", onPointerDown);
|
|
61
|
+
return () => window.removeEventListener("pointerdown", onPointerDown);
|
|
62
|
+
}, [open]);
|
|
63
|
+
|
|
64
|
+
const showSuggestions = open && keys.length > 0;
|
|
65
|
+
|
|
66
|
+
const selectSuggestion = (index: number) => {
|
|
67
|
+
const item = filtered[index];
|
|
68
|
+
if (!item) return;
|
|
69
|
+
onChange(item);
|
|
70
|
+
setOpen(false);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const openDropdown = () => {
|
|
74
|
+
refreshKeys();
|
|
75
|
+
setOpen(true);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div ref={containerRef} className="relative">
|
|
80
|
+
<input
|
|
81
|
+
value={value}
|
|
82
|
+
placeholder={placeholder}
|
|
83
|
+
spellCheck={false}
|
|
84
|
+
onChange={(e) => {
|
|
85
|
+
onChange(e.target.value);
|
|
86
|
+
setOpen(true);
|
|
87
|
+
}}
|
|
88
|
+
onFocus={openDropdown}
|
|
89
|
+
onClick={openDropdown}
|
|
90
|
+
onKeyDown={(e) => {
|
|
91
|
+
if (!showSuggestions || filtered.length === 0) {
|
|
92
|
+
if (e.key === "Escape") setOpen(false);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (e.key === "ArrowDown") {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
setActiveIndex((prev) => Math.min(prev + 1, filtered.length - 1));
|
|
98
|
+
} else if (e.key === "ArrowUp") {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
setActiveIndex((prev) => Math.max(prev - 1, 0));
|
|
101
|
+
} else if (e.key === "Enter") {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
selectSuggestion(activeIndex);
|
|
104
|
+
} else if (e.key === "Escape") {
|
|
105
|
+
setOpen(false);
|
|
106
|
+
}
|
|
107
|
+
}}
|
|
108
|
+
className={cn(
|
|
109
|
+
"flex h-8 w-full rounded-md border border-input bg-background px-2 py-1 font-mono text-[11px] text-foreground shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-primary"
|
|
110
|
+
)}
|
|
111
|
+
/>
|
|
112
|
+
{showSuggestions ? (
|
|
113
|
+
<div className="absolute z-[10150] mt-1 max-h-52 w-full overflow-auto rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md">
|
|
114
|
+
{filtered.length === 0 ? (
|
|
115
|
+
<div className="px-2 py-1.5 text-[11px] text-muted-foreground">
|
|
116
|
+
{t("blueprint.config.noMatchingStorageKey")}
|
|
117
|
+
</div>
|
|
118
|
+
) : (
|
|
119
|
+
filtered.map((key, index) => (
|
|
120
|
+
<button
|
|
121
|
+
key={key}
|
|
122
|
+
type="button"
|
|
123
|
+
className={cn(
|
|
124
|
+
"flex w-full items-center px-2 py-1.5 text-left font-mono text-[11px] hover:bg-accent",
|
|
125
|
+
index === activeIndex && "bg-accent"
|
|
126
|
+
)}
|
|
127
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
128
|
+
onClick={() => selectSuggestion(index)}
|
|
129
|
+
>
|
|
130
|
+
{key}
|
|
131
|
+
</button>
|
|
132
|
+
))
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
) : open && keys.length === 0 ? (
|
|
136
|
+
<div className="absolute z-[10150] mt-1 w-full rounded-md border border-border bg-popover px-2 py-1.5 text-[11px] text-muted-foreground shadow-md">
|
|
137
|
+
{t("blueprint.config.noStorageKeys")}
|
|
138
|
+
</div>
|
|
139
|
+
) : null}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -20,12 +20,13 @@ import {
|
|
|
20
20
|
resolveFetchScopeAutocompleteRoot,
|
|
21
21
|
uniqueStorageKinds,
|
|
22
22
|
} from "@arronqzy/blueprint-dsl";
|
|
23
|
-
import { useI18n } from "@arronqzy/i18n/react";
|
|
23
|
+
import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
|
|
24
24
|
|
|
25
25
|
import type { BlueprintGraphEdge, BlueprintGraphNode } from "../graph/document";
|
|
26
26
|
import { resolveNodeStorageConfig } from "../graph/document";
|
|
27
27
|
import { ConfigFieldLabel, ConfigSectionTitle } from "./ConfigHintIcon";
|
|
28
28
|
import { ScopeTemplateAutocompleteHost } from "./ScopeTemplateAutocompleteHost";
|
|
29
|
+
import { StorageKeyAutocomplete } from "./StorageKeyAutocomplete";
|
|
29
30
|
|
|
30
31
|
export type StorageNodeConfigPanelProps = {
|
|
31
32
|
node: BlueprintGraphNode;
|
|
@@ -90,10 +91,10 @@ export function StorageNodeConfigPanel({
|
|
|
90
91
|
);
|
|
91
92
|
|
|
92
93
|
const handleReadKeyChange = useCallback(
|
|
93
|
-
(
|
|
94
|
+
(key: string) => {
|
|
94
95
|
onUpdateNode(
|
|
95
96
|
node.id,
|
|
96
|
-
patchStorageConfig(node, { read: { key
|
|
97
|
+
patchStorageConfig(node, { read: { key } })
|
|
97
98
|
);
|
|
98
99
|
},
|
|
99
100
|
[node, onUpdateNode]
|
|
@@ -115,10 +116,10 @@ export function StorageNodeConfigPanel({
|
|
|
115
116
|
);
|
|
116
117
|
|
|
117
118
|
const handleSetKeyChange = useCallback(
|
|
118
|
-
(
|
|
119
|
+
(key: string) => {
|
|
119
120
|
onUpdateNode(
|
|
120
121
|
node.id,
|
|
121
|
-
patchStorageConfig(node, { set: { key
|
|
122
|
+
patchStorageConfig(node, { set: { key } })
|
|
122
123
|
);
|
|
123
124
|
},
|
|
124
125
|
[node, onUpdateNode]
|
|
@@ -134,6 +135,14 @@ export function StorageNodeConfigPanel({
|
|
|
134
135
|
[node, onUpdateNode]
|
|
135
136
|
);
|
|
136
137
|
|
|
138
|
+
const setKeySuggestionStorages = useMemo(
|
|
139
|
+
() =>
|
|
140
|
+
storageConfig.set.storages.length > 0
|
|
141
|
+
? storageConfig.set.storages
|
|
142
|
+
: (["session", "local"] as const),
|
|
143
|
+
[storageConfig.set.storages]
|
|
144
|
+
);
|
|
145
|
+
|
|
137
146
|
return (
|
|
138
147
|
<div
|
|
139
148
|
ref={setFormEl}
|
|
@@ -174,12 +183,11 @@ export function StorageNodeConfigPanel({
|
|
|
174
183
|
</label>
|
|
175
184
|
<label className="block space-y-1">
|
|
176
185
|
<ConfigFieldLabel label={t("blueprint.config.storageKey")} />
|
|
177
|
-
<
|
|
186
|
+
<StorageKeyAutocomplete
|
|
178
187
|
value={storageConfig.read.key}
|
|
179
|
-
|
|
180
|
-
spellCheck={false}
|
|
181
|
-
className="h-8 font-mono text-[11px]"
|
|
188
|
+
storages={storageConfig.read.storage}
|
|
182
189
|
placeholder="key"
|
|
190
|
+
onChange={handleReadKeyChange}
|
|
183
191
|
/>
|
|
184
192
|
</label>
|
|
185
193
|
</div>
|
|
@@ -213,12 +221,11 @@ export function StorageNodeConfigPanel({
|
|
|
213
221
|
</div>
|
|
214
222
|
<label className="block space-y-1">
|
|
215
223
|
<ConfigFieldLabel label={t("blueprint.config.storageKey")} />
|
|
216
|
-
<
|
|
224
|
+
<StorageKeyAutocomplete
|
|
217
225
|
value={storageConfig.set.key}
|
|
218
|
-
|
|
219
|
-
spellCheck={false}
|
|
220
|
-
className="h-8 font-mono text-[11px]"
|
|
226
|
+
storages={setKeySuggestionStorages}
|
|
221
227
|
placeholder="key"
|
|
228
|
+
onChange={handleSetKeyChange}
|
|
222
229
|
/>
|
|
223
230
|
</label>
|
|
224
231
|
<label className="block space-y-1">
|
package/src/index.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type Node,
|
|
15
15
|
type Edge,
|
|
16
16
|
} from "@xyflow/react";
|
|
17
|
+
import { EnsureI18nProvider } from "@arronqzy/i18n/react";
|
|
17
18
|
import "@xyflow/react/dist/style.css";
|
|
18
19
|
import "./blueprint.css";
|
|
19
20
|
|
|
@@ -241,33 +242,35 @@ export const BluePrintReactRoot: FC<BluePrintReactRootProps> = ({
|
|
|
241
242
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
242
243
|
|
|
243
244
|
return (
|
|
244
|
-
<
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
<BlueprintCanvasProvider
|
|
256
|
-
onSelectNode={onSelectNode}
|
|
257
|
-
onAbortClock={onAbortClock}
|
|
245
|
+
<EnsureI18nProvider>
|
|
246
|
+
<div
|
|
247
|
+
ref={containerRef}
|
|
248
|
+
data-workspace-region="blueprint"
|
|
249
|
+
className="bp-canvas h-full w-full bg-background text-foreground"
|
|
250
|
+
style={{
|
|
251
|
+
width: "100%",
|
|
252
|
+
height: "100%",
|
|
253
|
+
minHeight: 0,
|
|
254
|
+
...style,
|
|
255
|
+
}}
|
|
258
256
|
>
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
257
|
+
<BlueprintCanvasProvider
|
|
258
|
+
onSelectNode={onSelectNode}
|
|
259
|
+
onAbortClock={onAbortClock}
|
|
260
|
+
>
|
|
261
|
+
<ReactFlowProvider>
|
|
262
|
+
<BlueprintCanvas
|
|
263
|
+
containerRef={containerRef}
|
|
264
|
+
graph={graph}
|
|
265
|
+
onGraphChange={onGraphChange}
|
|
266
|
+
selectedNodeId={selectedNodeId}
|
|
267
|
+
executionOverlay={executionOverlay}
|
|
268
|
+
onSelectNode={onSelectNode}
|
|
269
|
+
libraryNameById={libraryNameById}
|
|
270
|
+
/>
|
|
271
|
+
</ReactFlowProvider>
|
|
272
|
+
</BlueprintCanvasProvider>
|
|
273
|
+
</div>
|
|
274
|
+
</EnsureI18nProvider>
|
|
272
275
|
);
|
|
273
276
|
};
|