@arronqzy/react-blueprint 1.0.1 → 1.0.4
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 +103 -0
- package/index.ts +2 -1
- package/package.json +7 -6
- package/src/BlueprintContextMenu.tsx +5 -3
- package/src/BlueprintNodeConfigSidebar.tsx +59 -57
- package/src/blueprint.css +21 -15
- package/src/components/BlueprintExecutionLogPanel.tsx +32 -30
- package/src/components/BlueprintLibrarySelect.tsx +11 -4
- package/src/components/BlueprintMetaDialog.tsx +20 -10
- package/src/components/BlueprintNodeSwitchTaskDialog.tsx +14 -8
- package/src/components/BlueprintPanelToolbar.tsx +50 -42
- package/src/components/ClockNodeConfigPanel.tsx +19 -12
- package/src/components/FetchNodeConfigPanel.tsx +40 -34
- package/src/components/FetchUrlAutocomplete.tsx +3 -1
- package/src/components/JsonNodeConfigPanel.tsx +15 -7
- package/src/components/LogicNodeConfigPanel.tsx +13 -7
- package/src/components/ViewElementMultiSelect.tsx +25 -18
- package/src/dev-seed.ts +2 -2
- package/src/fetch-config-task-store.ts +8 -2
- package/src/graph/blueprint-graph.ts +5 -1
- package/src/graph/document.ts +97 -22
- package/src/graph/node-summary.ts +32 -18
- package/src/graph/sync-nodes.ts +3 -1
- package/src/hooks/useBlueprintDebugSession.ts +24 -22
- package/src/hooks/useBlueprintFlowColorMode.ts +30 -6
- package/src/hooks/useBlueprintPageLifecycle.ts +29 -1
- package/src/library/blueprint-io.ts +12 -3
- package/src/library/swagger-docs.ts +15 -5
- package/src/main.tsx +12 -5
- package/src/nodes/AndFlowNode.tsx +7 -5
- package/src/nodes/BlueprintFlowNode.tsx +4 -2
- package/src/nodes/BlueprintNodeCard.tsx +4 -2
- package/src/nodes/BlueprintNodeShell.tsx +4 -2
- package/src/nodes/ClockFlowNode.tsx +5 -3
- package/src/nodes/FetchFlowNode.tsx +4 -2
- package/src/nodes/JsonFlowNode.tsx +4 -2
- package/src/nodes/LifecycleFlowNode.tsx +5 -3
- package/src/nodes/LogicFlowNode.tsx +4 -2
- package/src/runtime/document-to-runnable-graph.ts +9 -6
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
|
|
18
18
|
import type { BlueprintLibraryListItem } from "../library/types";
|
|
19
19
|
import type { LifecycleNodeOption } from "../hooks/useBlueprintDebugSession";
|
|
20
|
-
import {
|
|
20
|
+
import { type PageLifecyclePhase } from "@arronqzy/blueprint-dsl";
|
|
21
|
+
import { useI18n } from "@arronqzy/i18n/react";
|
|
22
|
+
import { getLifecyclePhaseLabel } from "../graph/document";
|
|
21
23
|
|
|
22
24
|
export type BlueprintRenameDialogProps = {
|
|
23
25
|
open: boolean;
|
|
@@ -32,6 +34,7 @@ export function BlueprintRenameDialog({
|
|
|
32
34
|
onOpenChange,
|
|
33
35
|
onConfirm,
|
|
34
36
|
}: BlueprintRenameDialogProps) {
|
|
37
|
+
const { t } = useI18n();
|
|
35
38
|
const [name, setName] = useState(initialName);
|
|
36
39
|
|
|
37
40
|
useEffect(() => {
|
|
@@ -50,11 +53,11 @@ export function BlueprintRenameDialog({
|
|
|
50
53
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
51
54
|
<DialogContent className="sm:max-w-sm">
|
|
52
55
|
<DialogHeader>
|
|
53
|
-
<DialogTitle
|
|
54
|
-
<DialogDescription
|
|
56
|
+
<DialogTitle>{t("blueprint.dialog.renameTitle")}</DialogTitle>
|
|
57
|
+
<DialogDescription>{t("blueprint.dialog.renameDescription")}</DialogDescription>
|
|
55
58
|
</DialogHeader>
|
|
56
59
|
<div className="space-y-1.5 py-1">
|
|
57
|
-
<Label htmlFor="blueprint-rename-input"
|
|
60
|
+
<Label htmlFor="blueprint-rename-input">{t("blueprint.dialog.blueprintName")}</Label>
|
|
58
61
|
<Input
|
|
59
62
|
id="blueprint-rename-input"
|
|
60
63
|
value={name}
|
|
@@ -67,10 +70,10 @@ export function BlueprintRenameDialog({
|
|
|
67
70
|
</div>
|
|
68
71
|
<DialogFooter>
|
|
69
72
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
|
70
|
-
|
|
73
|
+
{t("common.cancel")}
|
|
71
74
|
</Button>
|
|
72
75
|
<Button type="button" onClick={handleConfirm} disabled={!name.trim()}>
|
|
73
|
-
|
|
76
|
+
{t("common.confirm")}
|
|
74
77
|
</Button>
|
|
75
78
|
</DialogFooter>
|
|
76
79
|
</DialogContent>
|
|
@@ -174,18 +177,19 @@ export function BlueprintDeleteDialog({
|
|
|
174
177
|
onOpenChange,
|
|
175
178
|
onConfirm,
|
|
176
179
|
}: BlueprintDeleteDialogProps) {
|
|
180
|
+
const { t } = useI18n();
|
|
177
181
|
return (
|
|
178
182
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
179
183
|
<DialogContent className="sm:max-w-sm">
|
|
180
184
|
<DialogHeader>
|
|
181
|
-
<DialogTitle
|
|
185
|
+
<DialogTitle>{t("blueprint.dialog.deleteTitle")}</DialogTitle>
|
|
182
186
|
<DialogDescription>
|
|
183
|
-
|
|
187
|
+
{t("blueprint.dialog.deleteDescription", { name: blueprintName })}
|
|
184
188
|
</DialogDescription>
|
|
185
189
|
</DialogHeader>
|
|
186
190
|
<DialogFooter>
|
|
187
191
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
|
188
|
-
|
|
192
|
+
{t("common.cancel")}
|
|
189
193
|
</Button>
|
|
190
194
|
<Button
|
|
191
195
|
type="button"
|
|
@@ -195,7 +199,7 @@ export function BlueprintDeleteDialog({
|
|
|
195
199
|
onOpenChange(false);
|
|
196
200
|
}}
|
|
197
201
|
>
|
|
198
|
-
|
|
202
|
+
{t("common.delete")}
|
|
199
203
|
</Button>
|
|
200
204
|
</DialogFooter>
|
|
201
205
|
</DialogContent>
|
|
@@ -335,6 +339,7 @@ function LibraryRow({
|
|
|
335
339
|
onRename: () => void;
|
|
336
340
|
onDelete: () => void;
|
|
337
341
|
}) {
|
|
342
|
+
const { t } = useI18n();
|
|
338
343
|
const nameButton = (
|
|
339
344
|
<button
|
|
340
345
|
type="button"
|
|
@@ -352,17 +357,17 @@ function LibraryRow({
|
|
|
352
357
|
}`}
|
|
353
358
|
>
|
|
354
359
|
{active ? (
|
|
355
|
-
<ToolbarTooltip content="
|
|
360
|
+
<ToolbarTooltip content={t("blueprint.toolbar.clickAgainReturnWorkspace")}>
|
|
356
361
|
{nameButton}
|
|
357
362
|
</ToolbarTooltip>
|
|
358
363
|
) : (
|
|
359
364
|
nameButton
|
|
360
365
|
)}
|
|
361
|
-
<ToolbarTooltip content={
|
|
366
|
+
<ToolbarTooltip content={t("blueprint.toolbar.renameTooltip", { name: item.name })}>
|
|
362
367
|
<button
|
|
363
368
|
type="button"
|
|
364
369
|
className="shrink-0 rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
365
|
-
aria-label={
|
|
370
|
+
aria-label={t("blueprint.toolbar.renameAriaLabel", { name: item.name })}
|
|
366
371
|
onClick={(e) => {
|
|
367
372
|
e.stopPropagation();
|
|
368
373
|
onRename();
|
|
@@ -371,11 +376,11 @@ function LibraryRow({
|
|
|
371
376
|
<IconPencil />
|
|
372
377
|
</button>
|
|
373
378
|
</ToolbarTooltip>
|
|
374
|
-
<ToolbarTooltip content={
|
|
379
|
+
<ToolbarTooltip content={t("blueprint.toolbar.deleteTooltip", { name: item.name })}>
|
|
375
380
|
<button
|
|
376
381
|
type="button"
|
|
377
382
|
className="shrink-0 rounded p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
|
|
378
|
-
aria-label={
|
|
383
|
+
aria-label={t("blueprint.toolbar.deleteAriaLabel", { name: item.name })}
|
|
379
384
|
onClick={(e) => {
|
|
380
385
|
e.stopPropagation();
|
|
381
386
|
onDelete();
|
|
@@ -400,6 +405,7 @@ export function BlueprintPanelToolbar({
|
|
|
400
405
|
canSync = false,
|
|
401
406
|
debug,
|
|
402
407
|
}: BlueprintPanelToolbarProps) {
|
|
408
|
+
const { t } = useI18n();
|
|
403
409
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
404
410
|
const [renameTarget, setRenameTarget] = useState<BlueprintLibraryListItem | null>(
|
|
405
411
|
null
|
|
@@ -413,7 +419,9 @@ export function BlueprintPanelToolbar({
|
|
|
413
419
|
const triggerLabel =
|
|
414
420
|
activeItem?.name ??
|
|
415
421
|
(currentBlueprintLabel?.trim() ||
|
|
416
|
-
(items.length > 0
|
|
422
|
+
(items.length > 0
|
|
423
|
+
? t("blueprint.toolbar.library")
|
|
424
|
+
: t("blueprint.toolbar.libraryEmpty")));
|
|
417
425
|
|
|
418
426
|
const handleRenameConfirm = (name: string) => {
|
|
419
427
|
if (!renameTarget) return;
|
|
@@ -426,7 +434,7 @@ export function BlueprintPanelToolbar({
|
|
|
426
434
|
<TooltipProvider delayDuration={200}>
|
|
427
435
|
<div className="flex min-w-0 flex-1 items-center gap-1.5">
|
|
428
436
|
<div className="relative shrink-0">
|
|
429
|
-
<ToolbarTooltip content="
|
|
437
|
+
<ToolbarTooltip content={t("blueprint.toolbar.openLibraryTooltip")}>
|
|
430
438
|
<span className={items.length === 0 ? "inline-flex" : undefined}>
|
|
431
439
|
<Button
|
|
432
440
|
type="button"
|
|
@@ -452,7 +460,7 @@ export function BlueprintPanelToolbar({
|
|
|
452
460
|
{saved.length > 0 ? (
|
|
453
461
|
<div className="mb-1">
|
|
454
462
|
<div className="px-2 py-1 text-[10px] font-semibold text-muted-foreground">
|
|
455
|
-
|
|
463
|
+
{t("blueprint.toolbar.saved")}
|
|
456
464
|
</div>
|
|
457
465
|
{saved.map((item) => (
|
|
458
466
|
<LibraryRow
|
|
@@ -472,7 +480,7 @@ export function BlueprintPanelToolbar({
|
|
|
472
480
|
{imported.length > 0 ? (
|
|
473
481
|
<div>
|
|
474
482
|
<div className="px-2 py-1 text-[10px] font-semibold text-muted-foreground">
|
|
475
|
-
|
|
483
|
+
{t("blueprint.toolbar.imported")}
|
|
476
484
|
</div>
|
|
477
485
|
{imported.map((item) => (
|
|
478
486
|
<LibraryRow
|
|
@@ -499,8 +507,8 @@ export function BlueprintPanelToolbar({
|
|
|
499
507
|
variant="outline"
|
|
500
508
|
size="icon"
|
|
501
509
|
className="h-7 w-7 shrink-0"
|
|
502
|
-
content="
|
|
503
|
-
aria-label="
|
|
510
|
+
content={t("blueprint.toolbar.saveToLocalLibrary")}
|
|
511
|
+
aria-label={t("blueprint.toolbar.saveToLocalAriaLabel")}
|
|
504
512
|
onClick={onSave}
|
|
505
513
|
>
|
|
506
514
|
<IconSave />
|
|
@@ -515,13 +523,13 @@ export function BlueprintPanelToolbar({
|
|
|
515
523
|
content={
|
|
516
524
|
canSync
|
|
517
525
|
? activeItem
|
|
518
|
-
?
|
|
519
|
-
: "
|
|
526
|
+
? t("blueprint.toolbar.syncUpdateNamed", { name: activeItem.name })
|
|
527
|
+
: t("blueprint.toolbar.syncToLibrary")
|
|
520
528
|
: activeItem
|
|
521
|
-
?
|
|
522
|
-
: "
|
|
529
|
+
? t("blueprint.toolbar.alreadyInSyncNamed", { name: activeItem.name })
|
|
530
|
+
: t("blueprint.toolbar.selectBlueprintFirst")
|
|
523
531
|
}
|
|
524
|
-
aria-label="
|
|
532
|
+
aria-label={t("blueprint.toolbar.syncAriaLabel")}
|
|
525
533
|
disabled={!canSync}
|
|
526
534
|
onClick={onSync}
|
|
527
535
|
>
|
|
@@ -531,19 +539,19 @@ export function BlueprintPanelToolbar({
|
|
|
531
539
|
|
|
532
540
|
{debug && debug.lifecycleNodes.length > 0 ? (
|
|
533
541
|
<>
|
|
534
|
-
<ToolbarTooltip content="
|
|
542
|
+
<ToolbarTooltip content={t("blueprint.toolbar.simulateSceneTooltip")}>
|
|
535
543
|
<select
|
|
536
544
|
value={debug.selectedLifecycleNodeId ?? ""}
|
|
537
545
|
onChange={(e) => debug.onSelectLifecycleNode(e.target.value || null)}
|
|
538
546
|
className="h-7 max-w-[180px] shrink-0 rounded-md border border-input bg-background px-2 text-[11px] text-foreground outline-none focus-visible:ring-1 focus-visible:ring-primary"
|
|
539
|
-
aria-label="
|
|
547
|
+
aria-label={t("blueprint.toolbar.simulateSceneAriaLabel")}
|
|
540
548
|
>
|
|
541
|
-
<option value=""
|
|
549
|
+
<option value="">{t("blueprint.toolbar.simulateScene")}</option>
|
|
542
550
|
{debug.lifecycleNodes.map((node) => (
|
|
543
551
|
<option key={node.id} value={node.id}>
|
|
544
552
|
{node.label}
|
|
545
553
|
{node.phase
|
|
546
|
-
? ` · ${
|
|
554
|
+
? ` · ${getLifecyclePhaseLabel(t, node.phase as PageLifecyclePhase)}`
|
|
547
555
|
: ""}
|
|
548
556
|
</option>
|
|
549
557
|
))}
|
|
@@ -555,8 +563,8 @@ export function BlueprintPanelToolbar({
|
|
|
555
563
|
variant="outline"
|
|
556
564
|
size="icon"
|
|
557
565
|
className="h-7 w-7 shrink-0"
|
|
558
|
-
content="
|
|
559
|
-
aria-label="
|
|
566
|
+
content={t("blueprint.toolbar.runAllTooltip")}
|
|
567
|
+
aria-label={t("blueprint.toolbar.runAllAriaLabel")}
|
|
560
568
|
disabled={!debug.selectedLifecycleNodeId || debug.running}
|
|
561
569
|
onClick={() => void debug.onRunAll()}
|
|
562
570
|
>
|
|
@@ -568,8 +576,8 @@ export function BlueprintPanelToolbar({
|
|
|
568
576
|
variant="outline"
|
|
569
577
|
size="icon"
|
|
570
578
|
className="h-7 w-7 shrink-0"
|
|
571
|
-
content="
|
|
572
|
-
aria-label="
|
|
579
|
+
content={t("blueprint.toolbar.resetToStartTooltip")}
|
|
580
|
+
aria-label={t("blueprint.toolbar.resetToStartAriaLabel")}
|
|
573
581
|
disabled={
|
|
574
582
|
!debug.selectedLifecycleNodeId ||
|
|
575
583
|
debug.running ||
|
|
@@ -585,8 +593,8 @@ export function BlueprintPanelToolbar({
|
|
|
585
593
|
variant="outline"
|
|
586
594
|
size="icon"
|
|
587
595
|
className="h-7 w-7 shrink-0"
|
|
588
|
-
content="
|
|
589
|
-
aria-label="
|
|
596
|
+
content={t("blueprint.toolbar.stepBackTooltip")}
|
|
597
|
+
aria-label={t("blueprint.toolbar.stepBackAriaLabel")}
|
|
590
598
|
disabled={
|
|
591
599
|
!debug.selectedLifecycleNodeId ||
|
|
592
600
|
debug.running ||
|
|
@@ -604,12 +612,12 @@ export function BlueprintPanelToolbar({
|
|
|
604
612
|
className="h-7 w-7 shrink-0"
|
|
605
613
|
content={
|
|
606
614
|
debug.falseSignalHalt
|
|
607
|
-
? "
|
|
615
|
+
? t("blueprint.toolbar.stepNextFalseHalt")
|
|
608
616
|
: debug.chainComplete
|
|
609
|
-
? "
|
|
610
|
-
: "
|
|
617
|
+
? t("blueprint.toolbar.stepNextChainComplete")
|
|
618
|
+
: t("blueprint.toolbar.stepNextTooltip")
|
|
611
619
|
}
|
|
612
|
-
aria-label="
|
|
620
|
+
aria-label={t("blueprint.toolbar.stepNextAriaLabel")}
|
|
613
621
|
disabled={
|
|
614
622
|
!debug.selectedLifecycleNodeId ||
|
|
615
623
|
debug.running ||
|
|
@@ -620,13 +628,13 @@ export function BlueprintPanelToolbar({
|
|
|
620
628
|
<IconStepNext className="h-3.5 w-3.5" />
|
|
621
629
|
</ToolbarTooltipButton>
|
|
622
630
|
|
|
623
|
-
<ToolbarTooltip content="
|
|
631
|
+
<ToolbarTooltip content={t("blueprint.toolbar.logPanelTooltip")}>
|
|
624
632
|
<Button
|
|
625
633
|
type="button"
|
|
626
634
|
variant={debug.logPanelOpen ? "default" : "outline"}
|
|
627
635
|
size="icon"
|
|
628
636
|
className="h-7 w-7 shrink-0"
|
|
629
|
-
aria-label="
|
|
637
|
+
aria-label={t("blueprint.toolbar.logPanelAriaLabel")}
|
|
630
638
|
aria-pressed={debug.logPanelOpen}
|
|
631
639
|
onClick={debug.onToggleLogPanel}
|
|
632
640
|
>
|
|
@@ -1,6 +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
5
|
|
|
5
6
|
import type { BlueprintGraphNode } from "../graph/document";
|
|
6
7
|
import { resolveNodeClockConfig } from "../graph/document";
|
|
@@ -27,6 +28,7 @@ export function ClockNodeConfigPanel({
|
|
|
27
28
|
node,
|
|
28
29
|
onUpdateNode,
|
|
29
30
|
}: ClockNodeConfigPanelProps) {
|
|
31
|
+
const { t } = useI18n();
|
|
30
32
|
const clockConfig = resolveNodeClockConfig(node);
|
|
31
33
|
|
|
32
34
|
const handleIntervalChange = useCallback(
|
|
@@ -71,15 +73,17 @@ export function ClockNodeConfigPanel({
|
|
|
71
73
|
|
|
72
74
|
return (
|
|
73
75
|
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
74
|
-
<div className="font-medium text-foreground"
|
|
76
|
+
<div className="font-medium text-foreground">
|
|
77
|
+
{t("blueprint.config.clockTitle")}
|
|
78
|
+
</div>
|
|
75
79
|
<p className="text-[11px] text-muted-foreground">
|
|
76
|
-
|
|
77
|
-
<strong>真信号</strong>,值包含当前时间(formatted / timestamp / isoTime)。
|
|
78
|
-
假信号会原样向下游传递。
|
|
80
|
+
{t("blueprint.config.clockHint")}
|
|
79
81
|
</p>
|
|
80
82
|
|
|
81
83
|
<label className="block space-y-1">
|
|
82
|
-
<span className="text-muted-foreground"
|
|
84
|
+
<span className="text-muted-foreground">
|
|
85
|
+
{t("blueprint.config.clockIntervalSeconds")}
|
|
86
|
+
</span>
|
|
83
87
|
<Input
|
|
84
88
|
type="number"
|
|
85
89
|
min={0}
|
|
@@ -89,12 +93,14 @@ export function ClockNodeConfigPanel({
|
|
|
89
93
|
className="h-8"
|
|
90
94
|
/>
|
|
91
95
|
<p className="text-[11px] text-muted-foreground">
|
|
92
|
-
|
|
96
|
+
{t("blueprint.config.clockIntervalHint")}
|
|
93
97
|
</p>
|
|
94
98
|
</label>
|
|
95
99
|
|
|
96
100
|
<label className="block space-y-1">
|
|
97
|
-
<span className="text-muted-foreground"
|
|
101
|
+
<span className="text-muted-foreground">
|
|
102
|
+
{t("blueprint.config.outputCount")}
|
|
103
|
+
</span>
|
|
98
104
|
<Input
|
|
99
105
|
type="number"
|
|
100
106
|
min={1}
|
|
@@ -104,7 +110,7 @@ export function ClockNodeConfigPanel({
|
|
|
104
110
|
className="h-8"
|
|
105
111
|
/>
|
|
106
112
|
<p className="text-[11px] text-muted-foreground">
|
|
107
|
-
|
|
113
|
+
{t("blueprint.config.outputCountHint")}
|
|
108
114
|
</p>
|
|
109
115
|
</label>
|
|
110
116
|
|
|
@@ -116,13 +122,14 @@ export function ClockNodeConfigPanel({
|
|
|
116
122
|
className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
|
|
117
123
|
/>
|
|
118
124
|
<span className="text-[11px] leading-relaxed text-muted-foreground">
|
|
119
|
-
|
|
120
|
-
秒依次执行;仅输出 1 次时也会立刻执行。关闭则每次(含首次)都先等待 m 秒。
|
|
125
|
+
{t("blueprint.config.emitImmediately")}
|
|
121
126
|
</span>
|
|
122
127
|
</label>
|
|
123
128
|
|
|
124
129
|
<label className="block space-y-1">
|
|
125
|
-
<span className="text-muted-foreground"
|
|
130
|
+
<span className="text-muted-foreground">
|
|
131
|
+
{t("blueprint.config.timeFormat")}
|
|
132
|
+
</span>
|
|
126
133
|
<Input
|
|
127
134
|
value={clockConfig.timeFormat}
|
|
128
135
|
onChange={handleFormatChange}
|
|
@@ -131,7 +138,7 @@ export function ClockNodeConfigPanel({
|
|
|
131
138
|
placeholder="YYYY-MM-DD HH:mm:ss"
|
|
132
139
|
/>
|
|
133
140
|
<p className="text-[11px] text-muted-foreground">
|
|
134
|
-
|
|
141
|
+
{t("blueprint.config.timeFormatHint")}
|
|
135
142
|
</p>
|
|
136
143
|
</label>
|
|
137
144
|
</div>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
FETCH_CREDENTIALS,
|
|
20
20
|
FETCH_HTTP_METHODS,
|
|
21
21
|
FETCH_MODES,
|
|
22
|
+
FETCH_NODE_TYPE,
|
|
22
23
|
FETCH_REDIRECTS,
|
|
23
24
|
FETCH_RESPONSE_TYPES,
|
|
24
25
|
} from "@arronqzy/blueprint-dsl";
|
|
@@ -33,6 +34,8 @@ import {
|
|
|
33
34
|
useFetchDebugTask,
|
|
34
35
|
useSwaggerLoadTask,
|
|
35
36
|
} from "../fetch-config-task-store";
|
|
37
|
+
import { useI18n } from "@arronqzy/i18n/react";
|
|
38
|
+
|
|
36
39
|
import { FetchUrlAutocomplete } from "./FetchUrlAutocomplete";
|
|
37
40
|
|
|
38
41
|
function SendIcon({ className }: { className?: string }) {
|
|
@@ -144,6 +147,7 @@ function FetchDebugResponsePanel({
|
|
|
144
147
|
}: {
|
|
145
148
|
task: ReturnType<typeof useFetchDebugTask>;
|
|
146
149
|
}) {
|
|
150
|
+
const { t } = useI18n();
|
|
147
151
|
const [expanded, setExpanded] = useState(false);
|
|
148
152
|
|
|
149
153
|
if (task.status === "idle" || task.status === "loading") return null;
|
|
@@ -151,7 +155,7 @@ function FetchDebugResponsePanel({
|
|
|
151
155
|
if (task.status === "error") {
|
|
152
156
|
return (
|
|
153
157
|
<div className="rounded-md border border-destructive/40 bg-destructive/5 p-2">
|
|
154
|
-
<p className="text-[11px] text-destructive">{task.error ?? "
|
|
158
|
+
<p className="text-[11px] text-destructive">{task.error ?? t("blueprint.config.requestFailed")}</p>
|
|
155
159
|
</div>
|
|
156
160
|
);
|
|
157
161
|
}
|
|
@@ -172,7 +176,7 @@ function FetchDebugResponsePanel({
|
|
|
172
176
|
onClick={() => setExpanded((value) => !value)}
|
|
173
177
|
>
|
|
174
178
|
<ChevronIcon className="h-3.5 w-3.5 shrink-0 text-muted-foreground" expanded={expanded} />
|
|
175
|
-
<span className="text-[11px] font-medium text-foreground"
|
|
179
|
+
<span className="text-[11px] font-medium text-foreground">{t("blueprint.config.debugResponse")}</span>
|
|
176
180
|
<span
|
|
177
181
|
className={cn(
|
|
178
182
|
"rounded px-1.5 py-0.5 font-mono text-[10px]",
|
|
@@ -238,8 +242,10 @@ function patchFetchConfig(
|
|
|
238
242
|
patch: Partial<FetchRequestConfig>
|
|
239
243
|
) {
|
|
240
244
|
return {
|
|
241
|
-
|
|
245
|
+
role: "fetch" as const,
|
|
246
|
+
nodeType: FETCH_NODE_TYPE,
|
|
242
247
|
configSource: "fetch" as const,
|
|
248
|
+
fetchConfig: { ...resolveNodeFetchConfig(node), ...patch },
|
|
243
249
|
};
|
|
244
250
|
}
|
|
245
251
|
|
|
@@ -247,6 +253,7 @@ export function FetchNodeConfigPanel({
|
|
|
247
253
|
node,
|
|
248
254
|
onUpdateNode,
|
|
249
255
|
}: FetchNodeConfigPanelProps) {
|
|
256
|
+
const { t } = useI18n();
|
|
250
257
|
const fetchConfig = resolveNodeFetchConfig(node);
|
|
251
258
|
const endpoints = fetchConfig.swaggerEndpoints ?? [];
|
|
252
259
|
const hasSwaggerEndpoints = endpoints.length > 0;
|
|
@@ -272,7 +279,7 @@ export function FetchNodeConfigPanel({
|
|
|
272
279
|
const handleLoadSwagger = useCallback(() => {
|
|
273
280
|
const docsUrl = fetchConfig.swaggerDocsUrl?.trim();
|
|
274
281
|
if (!docsUrl) {
|
|
275
|
-
setValidationError("
|
|
282
|
+
setValidationError(t("blueprint.config.fillSwaggerUrlFirst"));
|
|
276
283
|
return;
|
|
277
284
|
}
|
|
278
285
|
|
|
@@ -302,7 +309,7 @@ export function FetchNodeConfigPanel({
|
|
|
302
309
|
const handleSendFetchDebug = useCallback(() => {
|
|
303
310
|
const url = fetchConfig.url?.trim();
|
|
304
311
|
if (!url) {
|
|
305
|
-
setFetchValidationError("
|
|
312
|
+
setFetchValidationError(t("blueprint.config.fillRequestUrlFirst"));
|
|
306
313
|
return;
|
|
307
314
|
}
|
|
308
315
|
|
|
@@ -320,14 +327,13 @@ export function FetchNodeConfigPanel({
|
|
|
320
327
|
|
|
321
328
|
return (
|
|
322
329
|
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
323
|
-
<div className="font-medium text-foreground"
|
|
330
|
+
<div className="font-medium text-foreground">{t("blueprint.config.fetchTitle")}</div>
|
|
324
331
|
<p className="text-[11px] text-muted-foreground">
|
|
325
|
-
|
|
326
|
-
URL。
|
|
332
|
+
{t("blueprint.config.fetchHint")}
|
|
327
333
|
</p>
|
|
328
334
|
|
|
329
335
|
<label className="block space-y-1">
|
|
330
|
-
<span className="text-muted-foreground">
|
|
336
|
+
<span className="text-muted-foreground">{t("blueprint.config.swaggerUrlOptional")}</span>
|
|
331
337
|
<div className="flex gap-1.5">
|
|
332
338
|
<Input
|
|
333
339
|
value={fetchConfig.swaggerDocsUrl ?? ""}
|
|
@@ -347,8 +353,8 @@ export function FetchNodeConfigPanel({
|
|
|
347
353
|
variant="outline"
|
|
348
354
|
size="icon"
|
|
349
355
|
className="h-8 w-8 shrink-0 text-destructive hover:bg-destructive/10 hover:text-destructive"
|
|
350
|
-
title="
|
|
351
|
-
aria-label="
|
|
356
|
+
title={t("blueprint.config.abortParse")}
|
|
357
|
+
aria-label={t("blueprint.config.abortSwaggerAria")}
|
|
352
358
|
onClick={handleAbortSwagger}
|
|
353
359
|
>
|
|
354
360
|
<StopIcon className="h-3.5 w-3.5" />
|
|
@@ -359,7 +365,7 @@ export function FetchNodeConfigPanel({
|
|
|
359
365
|
variant="outline"
|
|
360
366
|
size="icon"
|
|
361
367
|
className="h-8 w-8 shrink-0"
|
|
362
|
-
title="
|
|
368
|
+
title={t("blueprint.config.parseSwagger")}
|
|
363
369
|
onClick={() => handleLoadSwagger()}
|
|
364
370
|
>
|
|
365
371
|
<SendIcon className="h-4 w-4" />
|
|
@@ -369,7 +375,7 @@ export function FetchNodeConfigPanel({
|
|
|
369
375
|
{loadingSwagger ? (
|
|
370
376
|
<p className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
|
|
371
377
|
<LoaderIcon className="h-3.5 w-3.5 shrink-0" />
|
|
372
|
-
|
|
378
|
+
{t("blueprint.config.parsingSwagger")}
|
|
373
379
|
</p>
|
|
374
380
|
) : null}
|
|
375
381
|
{swaggerError ? (
|
|
@@ -377,14 +383,14 @@ export function FetchNodeConfigPanel({
|
|
|
377
383
|
) : null}
|
|
378
384
|
{endpoints.length > 0 ? (
|
|
379
385
|
<p className="text-[11px] text-muted-foreground">
|
|
380
|
-
|
|
386
|
+
{t("blueprint.config.parsedEndpoints", { count: endpoints.length })}
|
|
381
387
|
</p>
|
|
382
388
|
) : null}
|
|
383
389
|
</label>
|
|
384
390
|
|
|
385
391
|
<label className="block space-y-1">
|
|
386
392
|
<div className="flex items-center justify-between gap-2">
|
|
387
|
-
<span className="text-muted-foreground"
|
|
393
|
+
<span className="text-muted-foreground">{t("blueprint.config.requestUrl")}</span>
|
|
388
394
|
{hasSwaggerEndpoints ? (
|
|
389
395
|
<div className="flex rounded-md border border-border p-0.5">
|
|
390
396
|
<button
|
|
@@ -398,7 +404,7 @@ export function FetchNodeConfigPanel({
|
|
|
398
404
|
)}
|
|
399
405
|
onClick={() => setUrlInputMode("swagger")}
|
|
400
406
|
>
|
|
401
|
-
|
|
407
|
+
{t("blueprint.config.suggestApi")}
|
|
402
408
|
</button>
|
|
403
409
|
<button
|
|
404
410
|
type="button"
|
|
@@ -411,7 +417,7 @@ export function FetchNodeConfigPanel({
|
|
|
411
417
|
)}
|
|
412
418
|
onClick={() => setUrlInputMode("manual")}
|
|
413
419
|
>
|
|
414
|
-
|
|
420
|
+
{t("blueprint.config.manualInput")}
|
|
415
421
|
</button>
|
|
416
422
|
</div>
|
|
417
423
|
) : null}
|
|
@@ -425,7 +431,7 @@ export function FetchNodeConfigPanel({
|
|
|
425
431
|
)}
|
|
426
432
|
>
|
|
427
433
|
<label className="block space-y-1">
|
|
428
|
-
<span className="text-muted-foreground">
|
|
434
|
+
<span className="text-muted-foreground">{t("blueprint.config.apiHostBaseUrl")}</span>
|
|
429
435
|
<Input
|
|
430
436
|
value={fetchConfig.apiBaseUrl ?? ""}
|
|
431
437
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
@@ -439,7 +445,7 @@ export function FetchNodeConfigPanel({
|
|
|
439
445
|
/>
|
|
440
446
|
</label>
|
|
441
447
|
<label className="block space-y-1">
|
|
442
|
-
<span className="text-muted-foreground"
|
|
448
|
+
<span className="text-muted-foreground">{t("blueprint.config.selectEndpoint")}</span>
|
|
443
449
|
<div className="flex gap-1.5">
|
|
444
450
|
<div className="min-w-0 flex-1">
|
|
445
451
|
<FetchUrlAutocomplete
|
|
@@ -447,7 +453,7 @@ export function FetchNodeConfigPanel({
|
|
|
447
453
|
apiBaseUrl={fetchConfig.apiBaseUrl ?? ""}
|
|
448
454
|
endpoints={endpoints}
|
|
449
455
|
selectOnly
|
|
450
|
-
placeholder="
|
|
456
|
+
placeholder={t("blueprint.config.selectOrSearchEndpoint")}
|
|
451
457
|
onChange={(url) =>
|
|
452
458
|
onUpdateNode(node.id, patchFetchConfig(node, { url }))
|
|
453
459
|
}
|
|
@@ -467,10 +473,10 @@ export function FetchNodeConfigPanel({
|
|
|
467
473
|
disabled={loadingSwagger}
|
|
468
474
|
onSend={handleSendFetchDebug}
|
|
469
475
|
onAbort={handleAbortFetchDebug}
|
|
470
|
-
sendTitle="
|
|
471
|
-
abortTitle="
|
|
472
|
-
sendAriaLabel="
|
|
473
|
-
abortAriaLabel="
|
|
476
|
+
sendTitle={t("blueprint.config.sendDebugRequest")}
|
|
477
|
+
abortTitle={t("blueprint.config.abortRequest")}
|
|
478
|
+
sendAriaLabel={t("blueprint.config.sendDebugRequest")}
|
|
479
|
+
abortAriaLabel={t("blueprint.config.abortDebugAria")}
|
|
474
480
|
/>
|
|
475
481
|
</div>
|
|
476
482
|
</label>
|
|
@@ -491,17 +497,17 @@ export function FetchNodeConfigPanel({
|
|
|
491
497
|
disabled={loadingSwagger}
|
|
492
498
|
onSend={handleSendFetchDebug}
|
|
493
499
|
onAbort={handleAbortFetchDebug}
|
|
494
|
-
sendTitle="
|
|
495
|
-
abortTitle="
|
|
496
|
-
sendAriaLabel="
|
|
497
|
-
abortAriaLabel="
|
|
500
|
+
sendTitle={t("blueprint.config.sendDebugRequest")}
|
|
501
|
+
abortTitle={t("blueprint.config.abortRequest")}
|
|
502
|
+
sendAriaLabel={t("blueprint.config.sendDebugRequest")}
|
|
503
|
+
abortAriaLabel={t("blueprint.config.abortDebugAria")}
|
|
498
504
|
/>
|
|
499
505
|
</div>
|
|
500
506
|
)}
|
|
501
507
|
{loadingFetchDebug ? (
|
|
502
508
|
<p className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
|
|
503
509
|
<LoaderIcon className="h-3.5 w-3.5 shrink-0" />
|
|
504
|
-
|
|
510
|
+
{t("blueprint.config.sendingDebug")}
|
|
505
511
|
</p>
|
|
506
512
|
) : null}
|
|
507
513
|
{fetchValidationError ? (
|
|
@@ -511,7 +517,7 @@ export function FetchNodeConfigPanel({
|
|
|
511
517
|
</label>
|
|
512
518
|
|
|
513
519
|
<label className="block space-y-1">
|
|
514
|
-
<span className="text-muted-foreground"
|
|
520
|
+
<span className="text-muted-foreground">{t("blueprint.config.requestMethod")}</span>
|
|
515
521
|
<Select
|
|
516
522
|
value={fetchConfig.method ?? "GET"}
|
|
517
523
|
onValueChange={(value: string) =>
|
|
@@ -535,7 +541,7 @@ export function FetchNodeConfigPanel({
|
|
|
535
541
|
</label>
|
|
536
542
|
|
|
537
543
|
<label className="block space-y-1">
|
|
538
|
-
<span className="text-muted-foreground"
|
|
544
|
+
<span className="text-muted-foreground">{t("blueprint.config.requestHeadersJson")}</span>
|
|
539
545
|
<textarea
|
|
540
546
|
defaultValue={JSON.stringify(fetchConfig.headers ?? {}, null, 2)}
|
|
541
547
|
key={`${node.id}-headers-${JSON.stringify(fetchConfig.headers)}`}
|
|
@@ -557,7 +563,7 @@ export function FetchNodeConfigPanel({
|
|
|
557
563
|
</label>
|
|
558
564
|
|
|
559
565
|
<label className="block space-y-1">
|
|
560
|
-
<span className="text-muted-foreground"
|
|
566
|
+
<span className="text-muted-foreground">{t("blueprint.config.requestBody")}</span>
|
|
561
567
|
<textarea
|
|
562
568
|
value={fetchConfig.body ?? ""}
|
|
563
569
|
onChange={(e) =>
|
|
@@ -668,7 +674,7 @@ export function FetchNodeConfigPanel({
|
|
|
668
674
|
|
|
669
675
|
<div className="grid grid-cols-2 gap-2">
|
|
670
676
|
<label className="block space-y-1">
|
|
671
|
-
<span className="text-muted-foreground"
|
|
677
|
+
<span className="text-muted-foreground">{t("blueprint.config.responseParse")}</span>
|
|
672
678
|
<Select
|
|
673
679
|
value={fetchConfig.responseType ?? "json"}
|
|
674
680
|
onValueChange={(value: string) =>
|
|
@@ -693,7 +699,7 @@ export function FetchNodeConfigPanel({
|
|
|
693
699
|
</Select>
|
|
694
700
|
</label>
|
|
695
701
|
<label className="block space-y-1">
|
|
696
|
-
<span className="text-muted-foreground"
|
|
702
|
+
<span className="text-muted-foreground">{t("blueprint.config.timeoutMs")}</span>
|
|
697
703
|
<Input
|
|
698
704
|
type="number"
|
|
699
705
|
min={0}
|
|
@@ -1,5 +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
4
|
import {
|
|
4
5
|
buildEndpointSuggestions,
|
|
5
6
|
filterEndpointSuggestions,
|
|
@@ -28,6 +29,7 @@ export function FetchUrlAutocomplete({
|
|
|
28
29
|
onChange,
|
|
29
30
|
onSelectEndpoint,
|
|
30
31
|
}: FetchUrlAutocompleteProps) {
|
|
32
|
+
const { t } = useI18n();
|
|
31
33
|
const [open, setOpen] = useState(false);
|
|
32
34
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
33
35
|
const [filterQuery, setFilterQuery] = useState("");
|
|
@@ -146,7 +148,7 @@ export function FetchUrlAutocomplete({
|
|
|
146
148
|
<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">
|
|
147
149
|
{filtered.length === 0 ? (
|
|
148
150
|
<div className="px-2 py-1.5 text-[11px] text-muted-foreground">
|
|
149
|
-
|
|
151
|
+
{t("blueprint.config.noMatchingEndpoint")}
|
|
150
152
|
</div>
|
|
151
153
|
) : (
|
|
152
154
|
filtered.map((item, index) => (
|