@arronqzy/react-blueprint 1.0.3 → 1.0.5

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.
Files changed (32) hide show
  1. package/index.ts +2 -1
  2. package/package.json +7 -6
  3. package/src/BlueprintContextMenu.tsx +5 -3
  4. package/src/BlueprintNodeConfigSidebar.tsx +56 -57
  5. package/src/components/BlueprintExecutionLogPanel.tsx +30 -28
  6. package/src/components/BlueprintLibrarySelect.tsx +11 -4
  7. package/src/components/BlueprintMetaDialog.tsx +20 -10
  8. package/src/components/BlueprintNodeSwitchTaskDialog.tsx +14 -8
  9. package/src/components/BlueprintPanelToolbar.tsx +50 -42
  10. package/src/components/ClockNodeConfigPanel.tsx +19 -12
  11. package/src/components/FetchNodeConfigPanel.tsx +36 -33
  12. package/src/components/FetchUrlAutocomplete.tsx +3 -1
  13. package/src/components/JsonNodeConfigPanel.tsx +15 -7
  14. package/src/components/LogicNodeConfigPanel.tsx +13 -7
  15. package/src/components/ViewElementMultiSelect.tsx +25 -18
  16. package/src/dev-seed.ts +2 -2
  17. package/src/fetch-config-task-store.ts +8 -2
  18. package/src/graph/document.ts +51 -14
  19. package/src/graph/node-summary.ts +32 -18
  20. package/src/hooks/useBlueprintPageLifecycle.ts +29 -1
  21. package/src/library/blueprint-io.ts +12 -3
  22. package/src/library/swagger-docs.ts +15 -5
  23. package/src/main.tsx +12 -5
  24. package/src/nodes/AndFlowNode.tsx +7 -5
  25. package/src/nodes/BlueprintFlowNode.tsx +4 -2
  26. package/src/nodes/BlueprintNodeCard.tsx +4 -2
  27. package/src/nodes/BlueprintNodeShell.tsx +4 -2
  28. package/src/nodes/ClockFlowNode.tsx +5 -3
  29. package/src/nodes/FetchFlowNode.tsx +4 -2
  30. package/src/nodes/JsonFlowNode.tsx +4 -2
  31. package/src/nodes/LifecycleFlowNode.tsx +5 -3
  32. package/src/nodes/LogicFlowNode.tsx +4 -2
@@ -11,6 +11,7 @@ import {
11
11
  Label,
12
12
  Textarea,
13
13
  } from "@arronqzy/ui";
14
+ import { useI18n } from "@arronqzy/i18n/react";
14
15
 
15
16
  import type { BlueprintMetaDraft } from "../library/types";
16
17
 
@@ -29,6 +30,7 @@ export function BlueprintMetaDialog({
29
30
  onOpenChange,
30
31
  onConfirm,
31
32
  }: BlueprintMetaDialogProps) {
33
+ const { t } = useI18n();
32
34
  const [name, setName] = useState(initialMeta.name);
33
35
  const [remark, setRemark] = useState(initialMeta.remark);
34
36
 
@@ -38,16 +40,20 @@ export function BlueprintMetaDialog({
38
40
  setRemark(initialMeta.remark);
39
41
  }, [initialMeta.name, initialMeta.remark, open]);
40
42
 
41
- const title = mode === "export" ? "导出蓝图" : "保存蓝图";
43
+ const title =
44
+ mode === "export"
45
+ ? t("blueprint.dialog.exportTitle")
46
+ : t("blueprint.dialog.saveTitle");
42
47
  const description =
43
48
  mode === "export"
44
- ? "填写蓝图名称与备注,将当前蓝图导出为 JSON 文件。"
45
- : "填写蓝图名称与备注,将当前蓝图保存到本地蓝图库。";
46
- const confirmLabel = mode === "export" ? "导出" : "保存";
49
+ ? t("blueprint.dialog.exportDescription")
50
+ : t("blueprint.dialog.saveDescription");
51
+ const confirmLabel =
52
+ mode === "export" ? t("blueprint.dialog.export") : t("blueprint.dialog.save");
47
53
 
48
54
  const handleConfirm = () => {
49
55
  onConfirm({
50
- name: name.trim() || "未命名蓝图",
56
+ name: name.trim() || t("blueprint.dialog.unnamedBlueprint"),
51
57
  remark: remark.trim(),
52
58
  });
53
59
  onOpenChange(false);
@@ -62,29 +68,33 @@ export function BlueprintMetaDialog({
62
68
  </DialogHeader>
63
69
  <div className="space-y-3 py-1">
64
70
  <div className="space-y-1.5">
65
- <Label htmlFor="blueprint-meta-name">蓝图名称</Label>
71
+ <Label htmlFor="blueprint-meta-name">
72
+ {t("blueprint.dialog.blueprintName")}
73
+ </Label>
66
74
  <Input
67
75
  id="blueprint-meta-name"
68
76
  value={name}
69
77
  onChange={(e) => setName(e.target.value)}
70
- placeholder="例如:首页初始化流程"
78
+ placeholder={t("blueprint.dialog.namePlaceholder")}
71
79
  autoFocus
72
80
  />
73
81
  </div>
74
82
  <div className="space-y-1.5">
75
- <Label htmlFor="blueprint-meta-remark">蓝图备注</Label>
83
+ <Label htmlFor="blueprint-meta-remark">
84
+ {t("blueprint.dialog.remark")}
85
+ </Label>
76
86
  <Textarea
77
87
  id="blueprint-meta-remark"
78
88
  value={remark}
79
89
  onChange={(e) => setRemark(e.target.value)}
80
- placeholder="可选:描述蓝图用途、触发条件等"
90
+ placeholder={t("blueprint.dialog.remarkPlaceholder")}
81
91
  rows={3}
82
92
  />
83
93
  </div>
84
94
  </div>
85
95
  <DialogFooter>
86
96
  <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
87
- 取消
97
+ {t("common.cancel")}
88
98
  </Button>
89
99
  <Button type="button" onClick={handleConfirm}>
90
100
  {confirmLabel}
@@ -7,6 +7,7 @@ import {
7
7
  DialogHeader,
8
8
  DialogTitle,
9
9
  } from "@arronqzy/ui";
10
+ import { useI18n } from "@arronqzy/i18n/react";
10
11
 
11
12
  export type BlueprintNodeSwitchTaskDialogProps = {
12
13
  open: boolean;
@@ -25,31 +26,36 @@ export function BlueprintNodeSwitchTaskDialog({
25
26
  onKeepTaskAndSwitch,
26
27
  onCancelTaskAndSwitch,
27
28
  }: BlueprintNodeSwitchTaskDialogProps) {
29
+ const { t } = useI18n();
28
30
  const targetLabel =
29
- toNodeId === null ? "取消选中" : `节点 ${toNodeId}`;
31
+ toNodeId === null
32
+ ? t("blueprint.dialog.cancelSelection")
33
+ : t("blueprint.dialog.nodeTarget", { id: toNodeId });
30
34
 
31
35
  return (
32
36
  <Dialog open={open} onOpenChange={onOpenChange}>
33
37
  <DialogContent className="sm:max-w-md">
34
38
  <DialogHeader>
35
- <DialogTitle>节点正在执行任务</DialogTitle>
39
+ <DialogTitle>{t("blueprint.dialog.taskRunningTitle")}</DialogTitle>
36
40
  <DialogDescription>
37
- 节点 <span className="font-mono text-foreground">{fromNodeId}</span>{" "}
38
- 正在执行任务(Swagger 解析或请求调试)。是否切换到 {targetLabel}?
41
+ {t("blueprint.dialog.taskRunningDescription", {
42
+ fromNodeId,
43
+ target: targetLabel,
44
+ })}
39
45
  </DialogDescription>
40
46
  </DialogHeader>
41
47
  <p className="text-xs text-muted-foreground">
42
- 选择「保留并切换」将在后台继续执行,回到该节点时仍能看到进度;选择「取消并切换」会中止当前任务。
48
+ {t("blueprint.dialog.taskSwitchHint")}
43
49
  </p>
44
50
  <DialogFooter className="gap-2 sm:gap-0">
45
51
  <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
46
- 留在此节点
52
+ {t("blueprint.dialog.stayOnNode")}
47
53
  </Button>
48
54
  <Button type="button" variant="secondary" onClick={onCancelTaskAndSwitch}>
49
- 取消并切换
55
+ {t("blueprint.dialog.cancelAndSwitch")}
50
56
  </Button>
51
57
  <Button type="button" onClick={onKeepTaskAndSwitch}>
52
- 保留并切换
58
+ {t("blueprint.dialog.keepAndSwitch")}
53
59
  </Button>
54
60
  </DialogFooter>
55
61
  </DialogContent>
@@ -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 { PAGE_LIFECYCLE_LABELS, type PageLifecyclePhase } from "@arronqzy/blueprint-dsl";
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>重命名蓝图</DialogTitle>
54
- <DialogDescription>修改蓝图库中该项的显示名称。</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">蓝图名称</Label>
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>删除蓝图</DialogTitle>
185
+ <DialogTitle>{t("blueprint.dialog.deleteTitle")}</DialogTitle>
182
186
  <DialogDescription>
183
- 确定从蓝图库中删除「{blueprintName}」吗?此操作不可恢复。
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={`重命名「${item.name}」`}>
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={`重命名 ${item.name}`}
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={`从蓝图库删除「${item.name}」`}>
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={`删除 ${item.name}`}
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
- ? `同步更新蓝图库中的「${activeItem.name}」`
519
- : "将当前编辑同步到蓝图库"
526
+ ? t("blueprint.toolbar.syncUpdateNamed", { name: activeItem.name })
527
+ : t("blueprint.toolbar.syncToLibrary")
520
528
  : activeItem
521
- ? `「${activeItem.name}」已与库中版本一致`
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="">模拟场景</option>
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
- ? ` · ${PAGE_LIFECYCLE_LABELS[node.phase as PageLifecyclePhase] ?? node.phase}`
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">时钟节点</div>
76
+ <div className="font-medium text-foreground">
77
+ {t("blueprint.config.clockTitle")}
78
+ </div>
75
79
  <p className="text-[11px] text-muted-foreground">
76
- 收到<strong>真信号</strong>后才开始计时输出;每次输出向下游发出
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">时钟信号间隔(秒)</span>
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
- 两次输出之间的间隔 n 秒;多次输出或未开启「立即发送」时须大于 0。
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">输出次数</span>
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
- 收到真信号后总共输出的次数,默认 1。
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
- 收到信号立即发送:开启且输出次数大于 1 时,会立刻执行第 1 次,剩余次数按间隔 m
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">时间格式</span>
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
- 支持 YYYY、MM、DD、HH、mm、ss,默认 YYYY-MM-DD HH:mm:ss。
141
+ {t("blueprint.config.timeFormatHint")}
135
142
  </p>
136
143
  </label>
137
144
  </div>