@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
@@ -34,6 +34,8 @@ import {
34
34
  useFetchDebugTask,
35
35
  useSwaggerLoadTask,
36
36
  } from "../fetch-config-task-store";
37
+ import { useI18n } from "@arronqzy/i18n/react";
38
+
37
39
  import { FetchUrlAutocomplete } from "./FetchUrlAutocomplete";
38
40
 
39
41
  function SendIcon({ className }: { className?: string }) {
@@ -145,6 +147,7 @@ function FetchDebugResponsePanel({
145
147
  }: {
146
148
  task: ReturnType<typeof useFetchDebugTask>;
147
149
  }) {
150
+ const { t } = useI18n();
148
151
  const [expanded, setExpanded] = useState(false);
149
152
 
150
153
  if (task.status === "idle" || task.status === "loading") return null;
@@ -152,7 +155,7 @@ function FetchDebugResponsePanel({
152
155
  if (task.status === "error") {
153
156
  return (
154
157
  <div className="rounded-md border border-destructive/40 bg-destructive/5 p-2">
155
- <p className="text-[11px] text-destructive">{task.error ?? "请求失败"}</p>
158
+ <p className="text-[11px] text-destructive">{task.error ?? t("blueprint.config.requestFailed")}</p>
156
159
  </div>
157
160
  );
158
161
  }
@@ -173,7 +176,7 @@ function FetchDebugResponsePanel({
173
176
  onClick={() => setExpanded((value) => !value)}
174
177
  >
175
178
  <ChevronIcon className="h-3.5 w-3.5 shrink-0 text-muted-foreground" expanded={expanded} />
176
- <span className="text-[11px] font-medium text-foreground">调试响应</span>
179
+ <span className="text-[11px] font-medium text-foreground">{t("blueprint.config.debugResponse")}</span>
177
180
  <span
178
181
  className={cn(
179
182
  "rounded px-1.5 py-0.5 font-mono text-[10px]",
@@ -250,6 +253,7 @@ export function FetchNodeConfigPanel({
250
253
  node,
251
254
  onUpdateNode,
252
255
  }: FetchNodeConfigPanelProps) {
256
+ const { t } = useI18n();
253
257
  const fetchConfig = resolveNodeFetchConfig(node);
254
258
  const endpoints = fetchConfig.swaggerEndpoints ?? [];
255
259
  const hasSwaggerEndpoints = endpoints.length > 0;
@@ -275,7 +279,7 @@ export function FetchNodeConfigPanel({
275
279
  const handleLoadSwagger = useCallback(() => {
276
280
  const docsUrl = fetchConfig.swaggerDocsUrl?.trim();
277
281
  if (!docsUrl) {
278
- setValidationError("请先填写 Swagger 文档 URL");
282
+ setValidationError(t("blueprint.config.fillSwaggerUrlFirst"));
279
283
  return;
280
284
  }
281
285
 
@@ -305,7 +309,7 @@ export function FetchNodeConfigPanel({
305
309
  const handleSendFetchDebug = useCallback(() => {
306
310
  const url = fetchConfig.url?.trim();
307
311
  if (!url) {
308
- setFetchValidationError("请先填写请求 URL");
312
+ setFetchValidationError(t("blueprint.config.fillRequestUrlFirst"));
309
313
  return;
310
314
  }
311
315
 
@@ -323,14 +327,13 @@ export function FetchNodeConfigPanel({
323
327
 
324
328
  return (
325
329
  <div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
326
- <div className="font-medium text-foreground">数据源获取 (Fetch)</div>
330
+ <div className="font-medium text-foreground">{t("blueprint.config.fetchTitle")}</div>
327
331
  <p className="text-[11px] text-muted-foreground">
328
- 收到<strong>真信号</strong>后发起 HTTP 请求;可导入 Swagger 文档后从接口列表联想选择
329
- URL。
332
+ {t("blueprint.config.fetchHint")}
330
333
  </p>
331
334
 
332
335
  <label className="block space-y-1">
333
- <span className="text-muted-foreground">Swagger 文档 URL(可选)</span>
336
+ <span className="text-muted-foreground">{t("blueprint.config.swaggerUrlOptional")}</span>
334
337
  <div className="flex gap-1.5">
335
338
  <Input
336
339
  value={fetchConfig.swaggerDocsUrl ?? ""}
@@ -350,8 +353,8 @@ export function FetchNodeConfigPanel({
350
353
  variant="outline"
351
354
  size="icon"
352
355
  className="h-8 w-8 shrink-0 text-destructive hover:bg-destructive/10 hover:text-destructive"
353
- title="中止解析"
354
- aria-label="中止 Swagger 解析"
356
+ title={t("blueprint.config.abortParse")}
357
+ aria-label={t("blueprint.config.abortSwaggerAria")}
355
358
  onClick={handleAbortSwagger}
356
359
  >
357
360
  <StopIcon className="h-3.5 w-3.5" />
@@ -362,7 +365,7 @@ export function FetchNodeConfigPanel({
362
365
  variant="outline"
363
366
  size="icon"
364
367
  className="h-8 w-8 shrink-0"
365
- title="解析 Swagger 文档"
368
+ title={t("blueprint.config.parseSwagger")}
366
369
  onClick={() => handleLoadSwagger()}
367
370
  >
368
371
  <SendIcon className="h-4 w-4" />
@@ -372,7 +375,7 @@ export function FetchNodeConfigPanel({
372
375
  {loadingSwagger ? (
373
376
  <p className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
374
377
  <LoaderIcon className="h-3.5 w-3.5 shrink-0" />
375
- 正在解析 Swagger 文档,点击右侧按钮可中止…
378
+ {t("blueprint.config.parsingSwagger")}
376
379
  </p>
377
380
  ) : null}
378
381
  {swaggerError ? (
@@ -380,14 +383,14 @@ export function FetchNodeConfigPanel({
380
383
  ) : null}
381
384
  {endpoints.length > 0 ? (
382
385
  <p className="text-[11px] text-muted-foreground">
383
- 已解析 {endpoints.length} 个接口
386
+ {t("blueprint.config.parsedEndpoints", { count: endpoints.length })}
384
387
  </p>
385
388
  ) : null}
386
389
  </label>
387
390
 
388
391
  <label className="block space-y-1">
389
392
  <div className="flex items-center justify-between gap-2">
390
- <span className="text-muted-foreground">请求 URL</span>
393
+ <span className="text-muted-foreground">{t("blueprint.config.requestUrl")}</span>
391
394
  {hasSwaggerEndpoints ? (
392
395
  <div className="flex rounded-md border border-border p-0.5">
393
396
  <button
@@ -401,7 +404,7 @@ export function FetchNodeConfigPanel({
401
404
  )}
402
405
  onClick={() => setUrlInputMode("swagger")}
403
406
  >
404
- 接口联想
407
+ {t("blueprint.config.suggestApi")}
405
408
  </button>
406
409
  <button
407
410
  type="button"
@@ -414,7 +417,7 @@ export function FetchNodeConfigPanel({
414
417
  )}
415
418
  onClick={() => setUrlInputMode("manual")}
416
419
  >
417
- 手动输入
420
+ {t("blueprint.config.manualInput")}
418
421
  </button>
419
422
  </div>
420
423
  ) : null}
@@ -428,7 +431,7 @@ export function FetchNodeConfigPanel({
428
431
  )}
429
432
  >
430
433
  <label className="block space-y-1">
431
- <span className="text-muted-foreground">API 主机 / Base URL</span>
434
+ <span className="text-muted-foreground">{t("blueprint.config.apiHostBaseUrl")}</span>
432
435
  <Input
433
436
  value={fetchConfig.apiBaseUrl ?? ""}
434
437
  onChange={(e: ChangeEvent<HTMLInputElement>) =>
@@ -442,7 +445,7 @@ export function FetchNodeConfigPanel({
442
445
  />
443
446
  </label>
444
447
  <label className="block space-y-1">
445
- <span className="text-muted-foreground">选择接口</span>
448
+ <span className="text-muted-foreground">{t("blueprint.config.selectEndpoint")}</span>
446
449
  <div className="flex gap-1.5">
447
450
  <div className="min-w-0 flex-1">
448
451
  <FetchUrlAutocomplete
@@ -450,7 +453,7 @@ export function FetchNodeConfigPanel({
450
453
  apiBaseUrl={fetchConfig.apiBaseUrl ?? ""}
451
454
  endpoints={endpoints}
452
455
  selectOnly
453
- placeholder="点击选择或搜索接口"
456
+ placeholder={t("blueprint.config.selectOrSearchEndpoint")}
454
457
  onChange={(url) =>
455
458
  onUpdateNode(node.id, patchFetchConfig(node, { url }))
456
459
  }
@@ -470,10 +473,10 @@ export function FetchNodeConfigPanel({
470
473
  disabled={loadingSwagger}
471
474
  onSend={handleSendFetchDebug}
472
475
  onAbort={handleAbortFetchDebug}
473
- sendTitle="发送调试请求"
474
- abortTitle="中止请求"
475
- sendAriaLabel="发送调试请求"
476
- 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")}
477
480
  />
478
481
  </div>
479
482
  </label>
@@ -494,17 +497,17 @@ export function FetchNodeConfigPanel({
494
497
  disabled={loadingSwagger}
495
498
  onSend={handleSendFetchDebug}
496
499
  onAbort={handleAbortFetchDebug}
497
- sendTitle="发送调试请求"
498
- abortTitle="中止请求"
499
- sendAriaLabel="发送调试请求"
500
- 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")}
501
504
  />
502
505
  </div>
503
506
  )}
504
507
  {loadingFetchDebug ? (
505
508
  <p className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
506
509
  <LoaderIcon className="h-3.5 w-3.5 shrink-0" />
507
- 正在发送调试请求,点击右侧按钮可中止…
510
+ {t("blueprint.config.sendingDebug")}
508
511
  </p>
509
512
  ) : null}
510
513
  {fetchValidationError ? (
@@ -514,7 +517,7 @@ export function FetchNodeConfigPanel({
514
517
  </label>
515
518
 
516
519
  <label className="block space-y-1">
517
- <span className="text-muted-foreground">请求方法</span>
520
+ <span className="text-muted-foreground">{t("blueprint.config.requestMethod")}</span>
518
521
  <Select
519
522
  value={fetchConfig.method ?? "GET"}
520
523
  onValueChange={(value: string) =>
@@ -538,7 +541,7 @@ export function FetchNodeConfigPanel({
538
541
  </label>
539
542
 
540
543
  <label className="block space-y-1">
541
- <span className="text-muted-foreground">请求头 (JSON)</span>
544
+ <span className="text-muted-foreground">{t("blueprint.config.requestHeadersJson")}</span>
542
545
  <textarea
543
546
  defaultValue={JSON.stringify(fetchConfig.headers ?? {}, null, 2)}
544
547
  key={`${node.id}-headers-${JSON.stringify(fetchConfig.headers)}`}
@@ -560,7 +563,7 @@ export function FetchNodeConfigPanel({
560
563
  </label>
561
564
 
562
565
  <label className="block space-y-1">
563
- <span className="text-muted-foreground">请求体</span>
566
+ <span className="text-muted-foreground">{t("blueprint.config.requestBody")}</span>
564
567
  <textarea
565
568
  value={fetchConfig.body ?? ""}
566
569
  onChange={(e) =>
@@ -671,7 +674,7 @@ export function FetchNodeConfigPanel({
671
674
 
672
675
  <div className="grid grid-cols-2 gap-2">
673
676
  <label className="block space-y-1">
674
- <span className="text-muted-foreground">响应解析</span>
677
+ <span className="text-muted-foreground">{t("blueprint.config.responseParse")}</span>
675
678
  <Select
676
679
  value={fetchConfig.responseType ?? "json"}
677
680
  onValueChange={(value: string) =>
@@ -696,7 +699,7 @@ export function FetchNodeConfigPanel({
696
699
  </Select>
697
700
  </label>
698
701
  <label className="block space-y-1">
699
- <span className="text-muted-foreground">超时 (ms)</span>
702
+ <span className="text-muted-foreground">{t("blueprint.config.timeoutMs")}</span>
700
703
  <Input
701
704
  type="number"
702
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) => (
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useMemo, useState, type ChangeEvent } from "react";
2
2
  import { validateJsonString } from "@arronqzy/blueprint-dsl";
3
3
  import type { JsonNodeConfig } from "@arronqzy/blueprint-dsl";
4
+ import { useI18n } from "@arronqzy/i18n/react";
4
5
 
5
6
  import type { BlueprintGraphNode } from "../graph/document";
6
7
  import { resolveNodeJsonConfig } from "../graph/document";
@@ -27,6 +28,7 @@ export function JsonNodeConfigPanel({
27
28
  node,
28
29
  onUpdateNode,
29
30
  }: JsonNodeConfigPanelProps) {
31
+ const { t } = useI18n();
30
32
  const jsonConfig = resolveNodeJsonConfig(node);
31
33
  const [draftError, setDraftError] = useState<string | null>(null);
32
34
 
@@ -51,15 +53,17 @@ export function JsonNodeConfigPanel({
51
53
 
52
54
  return (
53
55
  <div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
54
- <div className="font-medium text-foreground">JSON 节点</div>
56
+ <div className="font-medium text-foreground">
57
+ {t("blueprint.config.jsonTitle")}
58
+ </div>
55
59
  <p className="text-[11px] text-muted-foreground">
56
- 收到<strong>真信号</strong>后,将下方 JSON 解析为 JavaScript object(或
57
- array)并从输出口发出<strong>真信号</strong>;解析失败则发出
58
- <strong>假信号</strong>。
60
+ {t("blueprint.config.jsonHint")}
59
61
  </p>
60
62
 
61
63
  <label className="block space-y-1">
62
- <span className="text-muted-foreground">JSON 内容</span>
64
+ <span className="text-muted-foreground">
65
+ {t("blueprint.config.jsonContent")}
66
+ </span>
63
67
  <textarea
64
68
  value={jsonConfig.jsonString}
65
69
  onChange={handleChange}
@@ -69,9 +73,13 @@ export function JsonNodeConfigPanel({
69
73
  placeholder={'{\n "key": "value"\n}'}
70
74
  />
71
75
  {parseError ? (
72
- <p className="text-[11px] text-destructive">JSON 格式错误:{parseError}</p>
76
+ <p className="text-[11px] text-destructive">
77
+ {t("blueprint.config.jsonFormatError", { error: parseError })}
78
+ </p>
73
79
  ) : (
74
- <p className="text-[11px] text-muted-foreground">JSON 格式正确</p>
80
+ <p className="text-[11px] text-muted-foreground">
81
+ {t("blueprint.config.jsonFormatOk")}
82
+ </p>
75
83
  )}
76
84
  </label>
77
85
  </div>
@@ -1,6 +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
5
 
5
6
  import type { BlueprintGraphNode } from "../graph/document";
6
7
  import { resolveNodeLogicConfig } from "../graph/document";
@@ -27,6 +28,7 @@ export function LogicNodeConfigPanel({
27
28
  node,
28
29
  onUpdateNode,
29
30
  }: LogicNodeConfigPanelProps) {
31
+ const { t } = useI18n();
30
32
  const logicConfig = resolveNodeLogicConfig(node);
31
33
  const [draftError, setDraftError] = useState<string | null>(null);
32
34
 
@@ -50,15 +52,17 @@ export function LogicNodeConfigPanel({
50
52
 
51
53
  return (
52
54
  <div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
53
- <div className="font-medium text-foreground">逻辑节点</div>
55
+ <div className="font-medium text-foreground">
56
+ {t("blueprint.config.logicTitle")}
57
+ </div>
54
58
  <p className="text-[11px] text-muted-foreground">
55
- 收到<strong>真信号</strong>后,将输入数据传入下方 <code>update</code>{" "}
56
- 函数并执行;返回值作为<strong>真信号</strong>输出。代码语法错误或运行时报错则发出
57
- <strong>假信号</strong>(错误信息为输出值)。
59
+ {t("blueprint.config.logicHint")}
58
60
  </p>
59
61
 
60
62
  <label className="block space-y-1">
61
- <span className="text-muted-foreground">JavaScript 代码</span>
63
+ <span className="text-muted-foreground">
64
+ {t("blueprint.config.jsCode")}
65
+ </span>
62
66
  <textarea
63
67
  value={logicConfig.sourceCode}
64
68
  onChange={handleChange}
@@ -69,10 +73,12 @@ export function LogicNodeConfigPanel({
69
73
  />
70
74
  {parseError ? (
71
75
  <p className="text-[11px] text-destructive">
72
- JavaScript 错误:{parseError}
76
+ {t("blueprint.config.jsError", { error: parseError })}
73
77
  </p>
74
78
  ) : (
75
- <p className="text-[11px] text-muted-foreground">JavaScript 语法正确</p>
79
+ <p className="text-[11px] text-muted-foreground">
80
+ {t("blueprint.config.jsSyntaxOk")}
81
+ </p>
76
82
  )}
77
83
  </label>
78
84
  </div>
@@ -12,6 +12,7 @@ import {
12
12
  PopoverContent,
13
13
  PopoverTrigger,
14
14
  } from "@arronqzy/ui";
15
+ import { useI18n } from "@arronqzy/i18n/react";
15
16
 
16
17
  export type ViewElementMultiSelectOption = {
17
18
  id: string;
@@ -45,35 +46,38 @@ function ChevronDownIcon({ className }: { className?: string }) {
45
46
  );
46
47
  }
47
48
 
48
- function resolveTriggerText(
49
- value: string[],
50
- labelById: Map<string, string>,
51
- placeholder: string
52
- ): string {
53
- if (value.length === 0) return placeholder;
54
- const labels = value.map((id) => labelById.get(id) ?? id);
55
- if (labels.length === 1) return labels[0]!;
56
- const joined = labels.join("、");
57
- if (joined.length <= 26) return joined;
58
- return `已选 ${value.length} 项`;
59
- }
60
-
61
49
  export function ViewElementMultiSelect({
62
50
  options,
63
51
  value,
64
52
  onChange,
65
- placeholder = "选择视图节点",
53
+ placeholder,
66
54
  disabled = false,
67
55
  }: ViewElementMultiSelectProps) {
56
+ const { t } = useI18n();
68
57
  const [open, setOpen] = useState(false);
58
+ const resolvedPlaceholder =
59
+ placeholder ?? t("blueprint.config.selectViewNode");
69
60
  const labelById = useMemo(
70
61
  () => new Map(options.map((opt) => [opt.id, opt.label])),
71
62
  [options]
72
63
  );
73
64
  const selectedSet = useMemo(() => new Set(value), [value]);
74
- const triggerText = resolveTriggerText(value, labelById, placeholder);
75
65
  const emptyOptions = options.length === 0;
76
66
 
67
+ let triggerText = resolvedPlaceholder;
68
+ if (value.length > 0) {
69
+ const labels = value.map((id) => labelById.get(id) ?? id);
70
+ if (labels.length === 1) {
71
+ triggerText = labels[0]!;
72
+ } else {
73
+ const joined = labels.join("、");
74
+ triggerText =
75
+ joined.length <= 26
76
+ ? joined
77
+ : t("blueprint.config.selectedCount", { count: value.length });
78
+ }
79
+ }
80
+
77
81
  const toggle = (id: string) => {
78
82
  onChange(
79
83
  selectedSet.has(id)
@@ -97,7 +101,7 @@ export function ViewElementMultiSelect({
97
101
  )}
98
102
  >
99
103
  <span className="min-w-0 flex-1 truncate text-left">
100
- {emptyOptions ? "视图画布暂无节点" : triggerText}
104
+ {emptyOptions ? t("blueprint.config.noViewOnCanvas") : triggerText}
101
105
  </span>
102
106
  <ChevronDownIcon className="h-4 w-4 shrink-0 opacity-50" />
103
107
  </Button>
@@ -107,9 +111,12 @@ export function ViewElementMultiSelect({
107
111
  align="start"
108
112
  >
109
113
  <Command>
110
- <CommandInput placeholder="搜索视图节点…" className="h-9 text-xs" />
114
+ <CommandInput
115
+ placeholder={t("blueprint.config.searchViewNodes")}
116
+ className="h-9 text-xs"
117
+ />
111
118
  <CommandList>
112
- <CommandEmpty>无匹配节点</CommandEmpty>
119
+ <CommandEmpty>{t("blueprint.config.noMatchingNodes")}</CommandEmpty>
113
120
  <CommandGroup>
114
121
  {options.map((opt) => {
115
122
  const selected = selectedSet.has(opt.id);
package/src/dev-seed.ts CHANGED
@@ -11,9 +11,9 @@ import {
11
11
  /** DevApp 初始图:两个节点 + 一条连线,便于直接验证边是否渲染 */
12
12
  export function createDevBlueprintGraph(): BlueprintGraph {
13
13
  let graph = BlueprintGraph.empty();
14
- graph = graph.addBlueprintNode({ x: 40, y: 80 }, "节点 A");
14
+ graph = graph.addBlueprintNode({ x: 40, y: 80 }, "Node A");
15
15
  const nodeA = graph.document.nodes[0]!;
16
- graph = graph.addBlueprintNode({ x: 300, y: 80 }, "节点 B");
16
+ graph = graph.addBlueprintNode({ x: 300, y: 80 }, "Node B");
17
17
  const nodeB = graph.document.nodes[1]!;
18
18
 
19
19
  const withEdge = addEdge(
@@ -6,6 +6,7 @@ import type {
6
6
  ParsedSwaggerDocument,
7
7
  } from "@arronqzy/blueprint-dsl";
8
8
  import { executeFetch } from "@arronqzy/blueprint-dsl";
9
+ import { resolveLocale, tForLocale } from "@arronqzy/i18n";
9
10
 
10
11
  import { loadSwaggerDocument } from "./library/swagger-docs";
11
12
 
@@ -143,7 +144,9 @@ export function startSwaggerLoadTask(params: {
143
144
  }
144
145
 
145
146
  const message =
146
- error instanceof Error ? error.message : "Swagger 文档解析失败";
147
+ error instanceof Error
148
+ ? error.message
149
+ : tForLocale(resolveLocale())("blueprint.config.swaggerParseFailed");
147
150
  swaggerTasks.set(params.nodeId, {
148
151
  record: { status: "error", error: message },
149
152
  abortController,
@@ -186,7 +189,10 @@ export function startFetchDebugTask(params: {
186
189
  return;
187
190
  }
188
191
 
189
- const message = error instanceof Error ? error.message : "请求失败";
192
+ const message =
193
+ error instanceof Error
194
+ ? error.message
195
+ : tForLocale(resolveLocale())("blueprint.config.requestFailed");
190
196
  fetchDebugTasks.set(params.nodeId, {
191
197
  record: { status: "error", error: message },
192
198
  abortController,
@@ -133,35 +133,72 @@ export function resolveRunnableNodeType(
133
133
  }
134
134
  }
135
135
 
136
- /** 画布节点顶部(拖拽区)展示的配置类型文案 */
137
- export const BLUEPRINT_CONFIG_TYPE_LABELS: Record<
136
+ import type { TranslateFn } from "@arronqzy/i18n";
137
+ import { resolveLocale, tForLocale } from "@arronqzy/i18n";
138
+
139
+ /** 画布节点顶部(拖拽区)配置类型对应的 i18n key */
140
+ export const BLUEPRINT_CONFIG_TYPE_LABEL_KEYS: Record<
138
141
  BlueprintConfigSource,
139
142
  string
140
143
  > = {
141
- blueprint: "蓝图",
142
- logic: "逻辑",
143
- and: "并运算",
144
- lifecycle: "生命周期",
145
- view: "视图",
146
- fetch: "数据源获取",
147
- json: "JSON 节点",
148
- clock: "时钟",
144
+ blueprint: "blueprint.node.typeBlueprint",
145
+ logic: "blueprint.node.typeLogic",
146
+ and: "blueprint.node.typeAnd",
147
+ lifecycle: "blueprint.node.typeLifecycle",
148
+ view: "blueprint.node.typeView",
149
+ fetch: "blueprint.node.typeFetch",
150
+ json: "blueprint.node.typeJson",
151
+ clock: "blueprint.node.typeClock",
149
152
  };
150
153
 
154
+ export const BLUEPRINT_LIFECYCLE_PHASE_KEYS: Record<PageLifecyclePhase, string> =
155
+ {
156
+ created: "blueprint.node.phaseCreated",
157
+ beforeMount: "blueprint.node.phaseBeforeMount",
158
+ mounted: "blueprint.node.phaseMounted",
159
+ updated: "blueprint.node.phaseUpdated",
160
+ beforeDestroy: "blueprint.node.phaseBeforeDestroy",
161
+ destroy: "blueprint.node.phaseDestroy",
162
+ activated: "blueprint.node.phaseActivated",
163
+ deactivated: "blueprint.node.phaseDeactivated",
164
+ blueprintActivated: "blueprint.node.phaseBlueprintActivated",
165
+ };
166
+
167
+ function defaultTranslate(): TranslateFn {
168
+ return tForLocale(resolveLocale());
169
+ }
170
+
171
+ export function getBlueprintNodeTypeLabel(
172
+ t: TranslateFn,
173
+ type: BlueprintConfigSource
174
+ ): string {
175
+ return t(BLUEPRINT_CONFIG_TYPE_LABEL_KEYS[type]);
176
+ }
177
+
178
+ export function getLifecyclePhaseLabel(
179
+ t: TranslateFn,
180
+ phase: PageLifecyclePhase
181
+ ): string {
182
+ const key = BLUEPRINT_LIFECYCLE_PHASE_KEYS[phase];
183
+ return key ? t(key) : phase;
184
+ }
185
+
151
186
  export function resolveBlueprintNodeTypeLabel(
152
187
  node: Pick<
153
188
  BlueprintGraphNode,
154
189
  "role" | "nodeType" | "configSource" | "viewElementId" | "viewElementIds"
155
- >
190
+ >,
191
+ t: TranslateFn = defaultTranslate()
156
192
  ): string {
157
- return BLUEPRINT_CONFIG_TYPE_LABELS[resolveBlueprintConfigSource(node)];
193
+ return getBlueprintNodeTypeLabel(t, resolveBlueprintConfigSource(node));
158
194
  }
159
195
 
160
196
  /** 新建节点时的默认显示名称(与类型文案一致) */
161
197
  export function defaultBlueprintNodeLabel(
162
- configSource: BlueprintConfigSource
198
+ configSource: BlueprintConfigSource,
199
+ t: TranslateFn = defaultTranslate()
163
200
  ): string {
164
- return BLUEPRINT_CONFIG_TYPE_LABELS[configSource];
201
+ return getBlueprintNodeTypeLabel(t, configSource);
165
202
  }
166
203
 
167
204
  export function resolveNodeFetchConfig(