@arronqzy/react-blueprint 1.0.21 → 1.0.22

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/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { BluePrintReactRoot } from "./src/index";
2
2
  export type { BluePrintReactRootProps } from "./src/index";
3
3
  export { BlueprintNodeConfigSidebar } from "./src/BlueprintNodeConfigSidebar";
4
+ export { BlueprintDslLocaleSync } from "./src/BlueprintDslLocaleSync";
4
5
  export type {
5
6
  BlueprintNodeConfigSidebarProps,
6
7
  BlueprintViewElementOption,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/react-blueprint",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
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.16",
22
- "@arronqzy/ui": "1.0.9",
23
- "@arronqzy/i18n": "0.1.16",
24
- "@arronqzy/react-rx-store": "1.1.8"
21
+ "@arronqzy/blueprint-dsl": "1.0.17",
22
+ "@arronqzy/react-rx-store": "1.1.8",
23
+ "@arronqzy/ui": "1.0.10",
24
+ "@arronqzy/i18n": "0.1.17"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/react": "^19.0.1",
@@ -34,9 +34,9 @@
34
34
  "eslint": "^8.57.0",
35
35
  "typescript": "^5.5.4",
36
36
  "vite": "^5.1.4",
37
+ "@arronqzy/tailwind": "1.0.2",
37
38
  "@arronqzy/eslint-config": "0.0.0",
38
- "@arronqzy/typescript-config": "1.0.0",
39
- "@arronqzy/tailwind": "1.0.2"
39
+ "@arronqzy/typescript-config": "1.0.0"
40
40
  },
41
41
  "keywords": [],
42
42
  "author": "",
@@ -0,0 +1,13 @@
1
+ import { setBlueprintDslLocale } from "@arronqzy/blueprint-dsl";
2
+ import { useI18nOptional } from "@arronqzy/i18n/react";
3
+ import type { ReactNode } from "react";
4
+
5
+ /**
6
+ * Keeps blueprint-dsl validation / runtime messages in the app locale. Sets it during
7
+ * render so descendants re-rendering for the same locale change already see it.
8
+ */
9
+ export function BlueprintDslLocaleSync({ children }: { children: ReactNode }) {
10
+ const { locale } = useI18nOptional();
11
+ setBlueprintDslLocale(locale);
12
+ return <>{children}</>;
13
+ }
@@ -284,18 +284,21 @@ function BlueprintNodeConfigSidebarInner({
284
284
  {t("blueprint.config.selectLibraryFirst")}
285
285
  </p>
286
286
  ) : null}
287
- <label className="flex items-start gap-2 pt-1">
287
+ <label className="flex items-center gap-2 pt-1">
288
288
  <input
289
289
  type="checkbox"
290
290
  checked={allowFalseSignalPropagation}
291
291
  onChange={(e) =>
292
292
  onUpdateAllowFalseSignalPropagation?.(e.target.checked)
293
293
  }
294
- className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
294
+ className="h-3.5 w-3.5 shrink-0 rounded border border-input"
295
295
  />
296
- <span className="text-[11px] leading-relaxed text-muted-foreground">
297
- {t("blueprint.config.allowFalsePropagateBlueprint")}
296
+ <span className="text-[11px] text-muted-foreground">
297
+ {t("blueprint.config.allowFalsePropagate")}
298
298
  </span>
299
+ <ConfigHintIcon label={t("blueprint.config.allowFalsePropagate")}>
300
+ {t("blueprint.config.allowFalsePropagateBlueprint")}
301
+ </ConfigHintIcon>
299
302
  </label>
300
303
  </div>
301
304
  ) : null}
@@ -454,18 +457,21 @@ function BlueprintNodeConfigSidebarInner({
454
457
  {configSource !== "blueprint" && configSource !== "and" ? (
455
458
  <div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
456
459
  <div className="font-medium text-foreground">{t("blueprint.config.taskChain")}</div>
457
- <label className="flex items-start gap-2">
460
+ <label className="flex items-center gap-2">
458
461
  <input
459
462
  type="checkbox"
460
463
  checked={allowFalseSignalPropagation}
461
464
  onChange={(e) =>
462
465
  onUpdateAllowFalseSignalPropagation?.(e.target.checked)
463
466
  }
464
- className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
467
+ className="h-3.5 w-3.5 shrink-0 rounded border border-input"
465
468
  />
466
- <span className="text-[11px] leading-relaxed text-muted-foreground">
467
- {t("blueprint.config.allowFalsePropagateDefault")}
469
+ <span className="text-[11px] text-muted-foreground">
470
+ {t("blueprint.config.allowFalsePropagate")}
468
471
  </span>
472
+ <ConfigHintIcon label={t("blueprint.config.allowFalsePropagate")}>
473
+ {t("blueprint.config.allowFalsePropagateDefault")}
474
+ </ConfigHintIcon>
469
475
  </label>
470
476
  </div>
471
477
  ) : null}
@@ -25,6 +25,7 @@ import {
25
25
  subscribeApiCollections,
26
26
  upsertApiCollectionFromJson,
27
27
  } from "../library/api-collection-store";
28
+ import { ConfigHintIcon } from "./ConfigHintIcon";
28
29
 
29
30
  export type ApiCollectionUploadStartInfo = {
30
31
  fileNames: string[];
@@ -282,6 +283,9 @@ export function ApiCollectionManagerDialog({
282
283
  onChange={(e) => setPersistLocalDefault(e.target.checked)}
283
284
  />
284
285
  {t("blueprint.apiCollection.persistLocal")}
286
+ <ConfigHintIcon label={t("blueprint.apiCollection.persistLocal")}>
287
+ {t("blueprint.apiCollection.persistLocalHint")}
288
+ </ConfigHintIcon>
285
289
  </label>
286
290
  <input
287
291
  ref={fileInputRef}
@@ -293,10 +297,6 @@ export function ApiCollectionManagerDialog({
293
297
  />
294
298
  </div>
295
299
 
296
- <p className="text-[11px] text-muted-foreground">
297
- {t("blueprint.apiCollection.persistLocalHint")}
298
- </p>
299
-
300
300
  {error ? (
301
301
  <pre className="max-h-24 overflow-auto whitespace-pre-wrap rounded border border-destructive/40 bg-destructive/5 p-2 text-[11px] text-destructive">
302
302
  {error}
@@ -5,7 +5,7 @@ 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";
8
- import { ConfigFieldLabel, ConfigSectionTitle } from "./ConfigHintIcon";
8
+ import { ConfigFieldLabel, ConfigHintIcon, ConfigSectionTitle } from "./ConfigHintIcon";
9
9
 
10
10
  export type ClockNodeConfigPanelProps = {
11
11
  node: BlueprintGraphNode;
@@ -109,16 +109,19 @@ export function ClockNodeConfigPanel({
109
109
  />
110
110
  </label>
111
111
 
112
- <label className="flex items-start gap-2">
112
+ <label className="flex items-center gap-2">
113
113
  <input
114
114
  type="checkbox"
115
115
  checked={clockConfig.emitImmediately}
116
116
  onChange={handleEmitImmediatelyChange}
117
- className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
117
+ className="h-3.5 w-3.5 shrink-0 rounded border border-input"
118
118
  />
119
- <span className="text-[11px] leading-relaxed text-muted-foreground">
119
+ <span className="text-[11px] text-muted-foreground">
120
120
  {t("blueprint.config.emitImmediately")}
121
121
  </span>
122
+ <ConfigHintIcon label={t("blueprint.config.emitImmediately")}>
123
+ {t("blueprint.config.emitImmediatelyHint")}
124
+ </ConfigHintIcon>
122
125
  </label>
123
126
 
124
127
  <label className="block space-y-1">
@@ -31,11 +31,24 @@ export function ConfigHintIcon({
31
31
  type="button"
32
32
  className={
33
33
  className ??
34
- "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border text-[10px] leading-none text-muted-foreground hover:bg-accent/50"
34
+ "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
35
35
  }
36
36
  aria-label={t("common.hintAria", { label: resolvedLabel })}
37
37
  >
38
- ?
38
+ <svg
39
+ viewBox="0 0 24 24"
40
+ fill="none"
41
+ stroke="currentColor"
42
+ strokeWidth={2}
43
+ strokeLinecap="round"
44
+ strokeLinejoin="round"
45
+ className="h-3.5 w-3.5"
46
+ aria-hidden="true"
47
+ >
48
+ <circle cx="12" cy="12" r="10" />
49
+ <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
50
+ <path d="M12 17h.01" />
51
+ </svg>
39
52
  </button>
40
53
  </TooltipTrigger>
41
54
  <TooltipContent
@@ -279,7 +279,7 @@ export function FetchNodeConfigPanel({
279
279
  traceEntries = [],
280
280
  onUpdateNode,
281
281
  }: FetchNodeConfigPanelProps) {
282
- const { t } = useI18n();
282
+ const { t, locale } = useI18n();
283
283
  const fetchConfig = resolveNodeFetchConfig(node);
284
284
  const apiCollections = useApiCollections();
285
285
  const endpoints = fetchConfig.swaggerEndpoints ?? [];
@@ -353,11 +353,11 @@ export function FetchNodeConfigPanel({
353
353
  }, [resolvedFetchConfig.body, usesScopeTemplate]);
354
354
  const headersJsonError = useMemo(
355
355
  () => getFetchHeadersValidationError(fetchConfig, incomingScope),
356
- [fetchConfig, incomingScope]
356
+ [fetchConfig, incomingScope, locale]
357
357
  );
358
358
  const bodyJsonError = useMemo(
359
359
  () => getFetchBodyValidationError(fetchConfig.body, resolvedFetchConfig.body),
360
- [fetchConfig.body, resolvedFetchConfig.body]
360
+ [fetchConfig.body, resolvedFetchConfig.body, locale]
361
361
  );
362
362
  const incomingHasPendingValue = useMemo(() => {
363
363
  if (!incomingScope || typeof incomingScope !== "object") return false;
@@ -599,8 +599,13 @@ export function FetchNodeConfigPanel({
599
599
  ) : null}
600
600
 
601
601
  <div className="space-y-1">
602
- <span className="text-[11px] text-muted-foreground">
602
+ <span className="inline-flex items-center gap-1 text-[11px] text-muted-foreground">
603
603
  {t("blueprint.config.fetchSourceMode")}
604
+ <ConfigHintIcon label={t("blueprint.config.fetchSourceMode")}>
605
+ {usingCollection
606
+ ? t("blueprint.config.fetchSourceCollectionHint")
607
+ : t("blueprint.config.fetchSourceDirectHint")}
608
+ </ConfigHintIcon>
604
609
  </span>
605
610
  <div className="flex rounded-md border border-border p-0.5">
606
611
  <button
@@ -628,11 +633,6 @@ export function FetchNodeConfigPanel({
628
633
  {t("blueprint.config.fetchSourceCollection")}
629
634
  </button>
630
635
  </div>
631
- <p className="text-[10px] text-muted-foreground">
632
- {usingCollection
633
- ? t("blueprint.config.fetchSourceCollectionHint")
634
- : t("blueprint.config.fetchSourceDirectHint")}
635
- </p>
636
636
  </div>
637
637
 
638
638
  <div className="space-y-1 rounded-md border border-border/60 bg-background/70 p-2">
@@ -689,9 +689,10 @@ export function FetchNodeConfigPanel({
689
689
  </Select>
690
690
  </label>
691
691
  <label className="block space-y-1">
692
- <span className="text-muted-foreground">
693
- {t("blueprint.config.apiCollectionEndpoint")}
694
- </span>
692
+ <ConfigFieldLabel
693
+ label={t("blueprint.config.apiCollectionEndpoint")}
694
+ hint={t("blueprint.config.apiCollectionFilledHint")}
695
+ />
695
696
  <div className="flex gap-1.5">
696
697
  <TooltipProvider delayDuration={200}>
697
698
  <Tooltip>
@@ -751,11 +752,6 @@ export function FetchNodeConfigPanel({
751
752
  />
752
753
  </div>
753
754
  </label>
754
- {selectedCollection ? (
755
- <p className="text-[10px] text-muted-foreground">
756
- {t("blueprint.config.apiCollectionFilledHint")}
757
- </p>
758
- ) : null}
759
755
  <label className="block space-y-1">
760
756
  <span className="text-muted-foreground">{t("blueprint.config.requestUrl")}</span>
761
757
  <Input
@@ -29,13 +29,13 @@ export function LogicNodeConfigPanel({
29
29
  node,
30
30
  onUpdateNode,
31
31
  }: LogicNodeConfigPanelProps) {
32
- const { t } = useI18n();
32
+ const { t, locale } = useI18n();
33
33
  const logicConfig = resolveNodeLogicConfig(node);
34
34
  const [draftError, setDraftError] = useState<string | null>(null);
35
35
 
36
36
  const storedValidation = useMemo(
37
37
  () => validateLogicSourceCode(logicConfig.sourceCode),
38
- [logicConfig.sourceCode]
38
+ [logicConfig.sourceCode, locale]
39
39
  );
40
40
 
41
41
  const parseError =
@@ -207,7 +207,7 @@ export function resolveRunnableNodeType(
207
207
  }
208
208
 
209
209
  import type { TranslateFn } from "@arronqzy/i18n";
210
- import { resolveLocale, tForLocale } from "@arronqzy/i18n";
210
+ import { matchMessageInAnyLocale, relocalizeMessage, resolveLocale, tForLocale } from "@arronqzy/i18n";
211
211
 
212
212
  /** 画布节点顶部(拖拽区)配置类型对应的 i18n key */
213
213
  export const BLUEPRINT_CONFIG_TYPE_LABEL_KEYS: Record<
@@ -289,16 +289,23 @@ export function defaultBlueprintNodeLabel(
289
289
  return getBlueprintNodeTypeLabel(t, configSource);
290
290
  }
291
291
 
292
- /** 当前名称是否仍是「类型默认名」(空或等于任一类型文案) */
292
+ const BLUEPRINT_TYPE_LABEL_KEY_LIST = Object.values(BLUEPRINT_CONFIG_TYPE_LABEL_KEYS);
293
+
294
+ /** 当前名称是否仍是「类型默认名」(空或等于任一语言下的任一类型文案) */
293
295
  export function isDefaultBlueprintNodeLabel(
294
296
  label: string | undefined | null,
295
297
  t: TranslateFn = defaultTranslate()
296
298
  ): boolean {
297
299
  const trimmed = (label ?? "").trim();
298
300
  if (!trimmed) return true;
299
- return (
300
- Object.keys(BLUEPRINT_CONFIG_TYPE_LABEL_KEYS) as BlueprintConfigSource[]
301
- ).some((src) => getBlueprintNodeTypeLabel(t, src) === trimmed);
301
+ return BLUEPRINT_TYPE_LABEL_KEY_LIST.some(
302
+ (key) => t(key) === trimmed || matchMessageInAnyLocale(trimmed, key) !== null
303
+ );
304
+ }
305
+
306
+ /** 类型默认名按当前语言显示;用户自定义名称保持不变 */
307
+ export function localizeBlueprintNodeLabel(label: string, t: TranslateFn): string {
308
+ return relocalizeMessage(label, BLUEPRINT_TYPE_LABEL_KEY_LIST, t);
302
309
  }
303
310
 
304
311
  export function resolveNodeFetchConfig(
package/src/index.tsx CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  type Edge,
18
18
  } from "@xyflow/react";
19
19
  import { EnsureI18nProvider } from "@arronqzy/i18n/react";
20
+ import { BlueprintDslLocaleSync } from "./BlueprintDslLocaleSync";
20
21
  import "@xyflow/react/dist/style.css";
21
22
  import "./blueprint.css";
22
23
 
@@ -39,6 +40,7 @@ export type { BlueprintExecutionOverlay } from "./runtime/execution-overlay";
39
40
 
40
41
  export type { BlueprintNodeConfigSidebarProps, BlueprintViewElementOption, BlueprintLibraryOption } from "./BlueprintNodeConfigSidebar";
41
42
  export { BlueprintNodeConfigSidebar } from "./BlueprintNodeConfigSidebar";
43
+ export { BlueprintDslLocaleSync } from "./BlueprintDslLocaleSync";
42
44
 
43
45
  export type BluePrintReactRootProps = {
44
46
  style?: React.CSSProperties;
@@ -66,7 +68,6 @@ function BlueprintCanvas({
66
68
  selectedNodeId = null,
67
69
  executionOverlay = null,
68
70
  onSelectNode,
69
- onAbortClock,
70
71
  libraryNameById,
71
72
  containerRef,
72
73
  }: BlueprintCanvasProps) {
@@ -253,34 +254,36 @@ export const BluePrintReactRoot: FC<BluePrintReactRootProps> = ({
253
254
 
254
255
  return (
255
256
  <EnsureI18nProvider>
256
- <div
257
- ref={containerRef}
258
- data-workspace-region="blueprint"
259
- className="bp-canvas h-full w-full bg-background text-foreground"
260
- style={{
261
- width: "100%",
262
- height: "100%",
263
- minHeight: 0,
264
- ...style,
265
- }}
266
- >
267
- <BlueprintCanvasProvider
268
- onSelectNode={onSelectNode}
269
- onAbortClock={onAbortClock}
257
+ <BlueprintDslLocaleSync>
258
+ <div
259
+ ref={containerRef}
260
+ data-workspace-region="blueprint"
261
+ className="bp-canvas h-full w-full bg-background text-foreground"
262
+ style={{
263
+ width: "100%",
264
+ height: "100%",
265
+ minHeight: 0,
266
+ ...style,
267
+ }}
270
268
  >
271
- <ReactFlowProvider>
272
- <BlueprintCanvas
273
- containerRef={containerRef}
274
- graph={graph}
275
- onGraphChange={onGraphChange}
276
- selectedNodeId={selectedNodeId}
277
- executionOverlay={executionOverlay}
278
- onSelectNode={onSelectNode}
279
- libraryNameById={libraryNameById}
280
- />
281
- </ReactFlowProvider>
282
- </BlueprintCanvasProvider>
283
- </div>
269
+ <BlueprintCanvasProvider
270
+ onSelectNode={onSelectNode}
271
+ onAbortClock={onAbortClock}
272
+ >
273
+ <ReactFlowProvider>
274
+ <BlueprintCanvas
275
+ containerRef={containerRef}
276
+ graph={graph}
277
+ onGraphChange={onGraphChange}
278
+ selectedNodeId={selectedNodeId}
279
+ executionOverlay={executionOverlay}
280
+ onSelectNode={onSelectNode}
281
+ libraryNameById={libraryNameById}
282
+ />
283
+ </ReactFlowProvider>
284
+ </BlueprintCanvasProvider>
285
+ </div>
286
+ </BlueprintDslLocaleSync>
284
287
  </EnsureI18nProvider>
285
288
  );
286
289
  };
@@ -1,6 +1,7 @@
1
1
  import { useI18nOptional as useI18n } from "@arronqzy/i18n/react";
2
2
  import { cn } from "@arronqzy/ui";
3
3
  import type { ReactNode } from "react";
4
+ import { localizeBlueprintNodeLabel } from "../graph/document";
4
5
 
5
6
  export type BlueprintNodeCardProps = {
6
7
  nodeId: string;
@@ -145,7 +146,8 @@ export function BlueprintNodeCard({
145
146
  onSelect,
146
147
  }: BlueprintNodeCardProps) {
147
148
  const { t } = useI18n();
148
- const showMeta = Boolean(meta && meta !== label);
149
+ const displayLabel = localizeBlueprintNodeLabel(label, t);
150
+ const showMeta = Boolean(meta && meta !== displayLabel);
149
151
 
150
152
  return (
151
153
  <div
@@ -172,7 +174,7 @@ export function BlueprintNodeCard({
172
174
  </span>
173
175
  <div className="bp-node-card__content">
174
176
  <div className="bp-node-card__title-row">
175
- <div className="bp-node-card__title">{label}</div>
177
+ <div className="bp-node-card__title">{displayLabel}</div>
176
178
  {progressLabel ? (
177
179
  <span
178
180
  className={cn(