@arronqzy/react-view 1.0.21 → 1.0.23

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 (34) hide show
  1. package/package.json +11 -11
  2. package/readme.md +6 -1
  3. package/src/panel/I18nRoot.tsx +14 -2
  4. package/src/panel/ReactViewOnlinePreview.tsx +29 -18
  5. package/src/panel/ReactViewPanel.tsx +154 -1006
  6. package/src/panel/components/PanelConfigSidebar.tsx +297 -3712
  7. package/src/panel/components/PanelHistoryPopover.tsx +163 -0
  8. package/src/panel/components/PanelLayerDock.tsx +412 -0
  9. package/src/panel/components/PanelMenubar.tsx +149 -0
  10. package/src/panel/components/PanelWorkspaceDialogs.tsx +271 -0
  11. package/src/panel/components/config/PanelConfigAudioSection.tsx +334 -0
  12. package/src/panel/components/config/PanelConfigChartSection.tsx +799 -0
  13. package/src/panel/components/config/PanelConfigGeometrySection.tsx +233 -0
  14. package/src/panel/components/config/PanelConfigGridChildSpan.tsx +70 -0
  15. package/src/panel/components/config/PanelConfigGridSection.tsx +118 -0
  16. package/src/panel/components/config/PanelConfigMultiSelect.tsx +1291 -0
  17. package/src/panel/components/config/PanelConfigNodeInfo.tsx +169 -0
  18. package/src/panel/components/config/PanelConfigReferenceSection.tsx +99 -0
  19. package/src/panel/components/config/PanelConfigScene3dSection.tsx +6 -2
  20. package/src/panel/components/config/PanelConfigStyleSections.tsx +249 -0
  21. package/src/panel/components/config/PanelConfigTextSection.tsx +268 -0
  22. package/src/panel/components/config/PanelConfigVideoSection.tsx +133 -0
  23. package/src/panel/components/config/PanelConfigViewportSection.tsx +139 -0
  24. package/src/panel/components/config/helpers.ts +85 -0
  25. package/src/panel/hooks/useWorkspaceProjects.ts +112 -34
  26. package/src/panel/icons.tsx +160 -0
  27. package/src/panel/index.tsx +15 -1
  28. package/src/panel/library/parse-workspace-data.ts +142 -0
  29. package/src/panel/library/workspace-project-cache.ts +13 -4
  30. package/src/panel/library/workspace-project-db.ts +24 -13
  31. package/src/panel/library/workspace-project-sync.ts +17 -6
  32. package/src/panel/utils/outputScale.ts +9 -4
  33. package/src/panel/utils/panelSelection.ts +57 -0
  34. package/src/panel/utils/panelStateIO.ts +18 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/react-view",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "files": [
5
5
  "src",
6
6
  "dist",
@@ -30,14 +30,14 @@
30
30
  "@codemirror/lang-json": "^6.0.1",
31
31
  "@codemirror/theme-one-dark": "^6.1.2",
32
32
  "@codemirror/view": "^6.36.2",
33
- "@arronqzy/ui": "1.0.6",
34
- "@arronqzy/blueprint-dsl": "1.0.10",
35
- "@arronqzy/i18n": "0.1.8",
36
- "@arronqzy/react-blueprint": "1.0.12",
37
- "@arronqzy/rx-store": "1.0.5",
38
- "@arronqzy/webllm-assistant": "0.1.10",
39
- "@arronqzy/view-table": "0.1.5",
40
- "@arronqzy/view-scene3d": "0.1.2"
33
+ "@arronqzy/ui": "1.0.7",
34
+ "@arronqzy/blueprint-dsl": "1.0.12",
35
+ "@arronqzy/react-blueprint": "1.0.14",
36
+ "@arronqzy/view-table": "0.1.6",
37
+ "@arronqzy/view-scene3d": "0.1.3",
38
+ "@arronqzy/i18n": "0.1.9",
39
+ "@arronqzy/webllm-assistant": "0.1.11",
40
+ "@arronqzy/rx-store": "1.0.6"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/react": "^19.0.1",
@@ -46,8 +46,8 @@
46
46
  "eslint": "^8.57.0",
47
47
  "typescript": "^5.5.4",
48
48
  "@arronqzy/eslint-config": "0.0.0",
49
- "@arronqzy/typescript-config": "1.0.0",
50
- "@arronqzy/tailwind": "1.0.2"
49
+ "@arronqzy/tailwind": "1.0.2",
50
+ "@arronqzy/typescript-config": "1.0.0"
51
51
  },
52
52
  "scripts": {
53
53
  "lint": "eslint \"src/**/*.{ts,tsx}\"",
package/readme.md CHANGED
@@ -147,7 +147,12 @@ const dataUrl = await getPreviewSnapshot({
147
147
  | `parseOnlinePreviewSearchParams` | 解析预览 URL 参数 |
148
148
  | `addEventSubscription` | 订阅工作区事件,返回 `{ unsubscribe }` |
149
149
  | `AbuilderEvents` | 工作区事件名常量 |
150
- | `getPreviewSnapshot` | 获取预览画面 base64 缩略图 |
150
+ | `getPreviewSnapshot` | 获取预览画面 base64 缩略图(仅本包 / `@arronqzy/abuilder`) |
151
+ | `parseWorkspaceData` / `validateWorkspaceData` | 解析或校验完整工作区;`parse` 的 `value` 可作 `initialWorkspace` |
152
+ | `parseViewData` / `validateViewData` | 解析或校验视图 `panelState` |
153
+ | `parseBlueprintData` / `validateBlueprintData` | 解析或校验蓝图文档 |
154
+ | `createEmptyWorkspace` | 构造空工作区记录 |
155
+ | `createWorkspaceProjectId` | 生成工作区 ID |
151
156
  | `WorkspaceData` / `WorkspaceProjectRecord` | 完整工作区数据类型 |
152
157
  | `createView` / `createAction` 等 | 视图 DSL 与指令扩展(`src/core`) |
153
158
 
@@ -1,4 +1,6 @@
1
1
  import { I18nProvider } from "@arronqzy/i18n/react";
2
+ import { LOCALE_STORAGE_KEY } from "@arronqzy/i18n";
3
+ import { appStorageKey } from "@arronqzy/blueprint-dsl";
2
4
  import type { Locale } from "@arronqzy/i18n";
3
5
  import type { ReactNode } from "react";
4
6
 
@@ -6,12 +8,22 @@ export type I18nRootProps = {
6
8
  children: ReactNode;
7
9
  locale?: Locale | null;
8
10
  onLocaleChange?: (locale: Locale) => void;
11
+ nameSpace?: string | null;
9
12
  };
10
13
 
11
14
  /** Ensures panel / preview subtree always has i18n context (host apps, duplicate package graphs). */
12
- export function I18nRoot({ children, locale, onLocaleChange }: I18nRootProps) {
15
+ export function I18nRoot({
16
+ children,
17
+ locale,
18
+ onLocaleChange,
19
+ nameSpace,
20
+ }: I18nRootProps) {
13
21
  return (
14
- <I18nProvider locale={locale} onLocaleChange={onLocaleChange}>
22
+ <I18nProvider
23
+ locale={locale}
24
+ onLocaleChange={onLocaleChange}
25
+ storageKey={appStorageKey(LOCALE_STORAGE_KEY, nameSpace)}
26
+ >
15
27
  {children}
16
28
  </I18nProvider>
17
29
  );
@@ -54,6 +54,7 @@ export type ReactViewOnlinePreviewProps = {
54
54
  workspace?: WorkspaceProjectRecord | null;
55
55
  locale?: Locale | null;
56
56
  onLocaleChange?: (locale: Locale) => void;
57
+ nameSpace?: string | null;
57
58
  };
58
59
 
59
60
  function applyTitleIcon(titleIconDataUrl?: string) {
@@ -68,20 +69,24 @@ function applyTitleIcon(titleIconDataUrl?: string) {
68
69
  }
69
70
  }
70
71
 
71
- async function loadWorkspaceRecord(projectId: string): Promise<WorkspaceProjectRecord | null> {
72
- const fromDb = await getWorkspaceProject(projectId);
72
+ async function loadWorkspaceRecord(
73
+ projectId: string,
74
+ nameSpace?: string | null
75
+ ): Promise<WorkspaceProjectRecord | null> {
76
+ const fromDb = await getWorkspaceProject(projectId, nameSpace);
73
77
  if (fromDb) return fromDb;
74
- return readWorkspacePreviewCache(projectId);
78
+ return readWorkspacePreviewCache(projectId, nameSpace);
75
79
  }
76
80
 
77
81
  export function ReactViewOnlinePreview({
78
82
  locale,
79
83
  onLocaleChange,
84
+ nameSpace,
80
85
  ...rest
81
86
  }: ReactViewOnlinePreviewProps) {
82
87
  return (
83
- <I18nRoot locale={locale} onLocaleChange={onLocaleChange}>
84
- <ReactViewOnlinePreviewInner {...rest} />
88
+ <I18nRoot locale={locale} onLocaleChange={onLocaleChange} nameSpace={nameSpace}>
89
+ <ReactViewOnlinePreviewInner nameSpace={nameSpace} {...rest} />
85
90
  </I18nRoot>
86
91
  );
87
92
  }
@@ -90,6 +95,7 @@ function ReactViewOnlinePreviewInner({
90
95
  projectId,
91
96
  previewInstanceId,
92
97
  workspace = null,
98
+ nameSpace = null,
93
99
  }: ReactViewOnlinePreviewProps) {
94
100
  const { t } = useI18n();
95
101
 
@@ -102,7 +108,7 @@ function ReactViewOnlinePreviewInner({
102
108
  >([]);
103
109
  const [layoutRevision, setLayoutRevision] = useState(0);
104
110
  const [layoutReady, setLayoutReady] = useState(false);
105
- const [outputScale, setOutputScale] = useState(() => readOutputScale());
111
+ const [outputScale, setOutputScale] = useState(() => readOutputScale(nameSpace));
106
112
  const [fillScale, setFillScale] = useState({ scaleX: 1, scaleY: 1 });
107
113
 
108
114
  const sceneRef = useRef<HTMLDivElement | null>(null);
@@ -122,7 +128,9 @@ function ReactViewOnlinePreviewInner({
122
128
  setPanelState(normalized);
123
129
  setBlueprintGraph(BlueprintGraph.fromDocument(record.blueprintDocument));
124
130
  document.title =
125
- record.productName.trim() || record.name || t("panel.workspace.previewDocTitle");
131
+ (typeof record.productName === "string" ? record.productName : "").trim() ||
132
+ record.name ||
133
+ t("panel.workspace.previewDocTitle");
126
134
  applyTitleIcon(record.titleIconDataUrl);
127
135
  setProjectRevision((v) => v + 1);
128
136
  return true;
@@ -141,7 +149,7 @@ function ReactViewOnlinePreviewInner({
141
149
  return;
142
150
  }
143
151
 
144
- const record = await loadWorkspaceRecord(projectId);
152
+ const record = await loadWorkspaceRecord(projectId, nameSpace);
145
153
  if (!record) {
146
154
  setLoadError(t("panel.messages.workspaceNotFound"));
147
155
  setPanelState(null);
@@ -149,7 +157,7 @@ function ReactViewOnlinePreviewInner({
149
157
  }
150
158
 
151
159
  applyWorkspaceRecord(record);
152
- }, [applyWorkspaceRecord, projectId, t, workspace]);
160
+ }, [applyWorkspaceRecord, nameSpace, projectId, t, workspace]);
153
161
 
154
162
  useEffect(() => {
155
163
  void loadProject();
@@ -159,8 +167,8 @@ function ReactViewOnlinePreviewInner({
159
167
  if (workspace || !projectId) return;
160
168
  return subscribeWorkspaceProjectUpdates(projectId, () => {
161
169
  void loadProject();
162
- });
163
- }, [loadProject, projectId, workspace]);
170
+ }, nameSpace);
171
+ }, [loadProject, nameSpace, projectId, workspace]);
164
172
 
165
173
  const layers = useMemo(
166
174
  () => (panelState ? parsePanelLayers(panelState) : []),
@@ -177,13 +185,13 @@ function ReactViewOnlinePreviewInner({
177
185
 
178
186
  useEffect(() => {
179
187
  let cancelled = false;
180
- void listBlueprintLibrary().then((items) => {
188
+ void listBlueprintLibrary(nameSpace).then((items) => {
181
189
  if (!cancelled) setBlueprintLibraryItems(items);
182
190
  });
183
191
  return () => {
184
192
  cancelled = true;
185
193
  };
186
- }, [projectRevision]);
194
+ }, [nameSpace, projectRevision]);
187
195
 
188
196
  const blueprintLibraryNameById = useMemo(
189
197
  () => new Map(blueprintLibraryItems.map((item) => [item.id, item.name])),
@@ -192,13 +200,13 @@ function ReactViewOnlinePreviewInner({
192
200
 
193
201
  const resolveLibraryBlueprint = useCallback<LibraryBlueprintResolver>(
194
202
  async (libraryBlueprintId) => {
195
- const record = await getBlueprintLibraryRecord(libraryBlueprintId);
203
+ const record = await getBlueprintLibraryRecord(libraryBlueprintId, nameSpace);
196
204
  if (!record) return null;
197
- const items = await listBlueprintLibrary();
205
+ const items = await listBlueprintLibrary(nameSpace);
198
206
  const nameById = new Map(items.map((item) => [item.id, item.name]));
199
207
  return documentToRunnableGraph(record.document, { libraryNameById: nameById });
200
208
  },
201
- []
209
+ [nameSpace]
202
210
  );
203
211
 
204
212
  const handleViewScopeUpdate = useCallback(
@@ -251,7 +259,7 @@ function ReactViewOnlinePreviewInner({
251
259
  );
252
260
 
253
261
  const applySceneFit = useCallback(() => {
254
- const enabled = readOutputScale();
262
+ const enabled = readOutputScale(nameSpace);
255
263
  setOutputScale(enabled);
256
264
  const fill = applyPreviewSceneFill(
257
265
  sceneRef.current,
@@ -262,7 +270,7 @@ function ReactViewOnlinePreviewInner({
262
270
  setFillScale({ scaleX: fill.scaleX, scaleY: fill.scaleY });
263
271
  setLayoutRevision((v) => v + 1);
264
272
  notifyPreviewLayoutChanged();
265
- }, [sceneBounds.height, sceneBounds.width]);
273
+ }, [nameSpace, sceneBounds.height, sceneBounds.width]);
266
274
 
267
275
  useLayoutEffect(() => {
268
276
  if (!panelState || layerElements.length === 0) return;
@@ -451,13 +459,16 @@ function ReactViewOnlinePreviewInner({
451
459
  export function parseOnlinePreviewSearchParams(search: string): {
452
460
  projectId: string;
453
461
  previewInstanceId?: string;
462
+ nameSpace?: string;
454
463
  } | null {
455
464
  const params = new URLSearchParams(search);
456
465
  if (params.get("preview") !== "online") return null;
457
466
  const projectId = params.get("projectId") ?? params.get("id");
458
467
  if (!projectId) return null;
468
+ const nameSpace = (params.get("ns") ?? params.get("nameSpace") ?? "").trim();
459
469
  return {
460
470
  projectId,
461
471
  previewInstanceId: params.get("pid") ?? undefined,
472
+ nameSpace: nameSpace || undefined,
462
473
  };
463
474
  }