@arronqzy/react-view 1.0.36 → 1.0.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/react-view",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "files": [
5
5
  "src",
6
6
  "dist",
@@ -30,13 +30,13 @@
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/blueprint-dsl": "1.0.17",
34
33
  "@arronqzy/ui": "1.0.10",
34
+ "@arronqzy/blueprint-dsl": "1.0.17",
35
35
  "@arronqzy/i18n": "0.1.17",
36
- "@arronqzy/react-blueprint": "1.0.22",
37
36
  "@arronqzy/rx-store": "1.0.8",
38
37
  "@arronqzy/view-scene3d": "0.1.5",
39
38
  "@arronqzy/view-table": "0.1.8",
39
+ "@arronqzy/react-blueprint": "1.0.22",
40
40
  "@arronqzy/webllm-assistant": "0.1.16"
41
41
  },
42
42
  "devDependencies": {
@@ -45,8 +45,8 @@
45
45
  "@types/use-sync-external-store": "^1.5.0",
46
46
  "eslint": "^8.57.0",
47
47
  "typescript": "^5.5.4",
48
- "@arronqzy/typescript-config": "1.0.0",
49
48
  "@arronqzy/eslint-config": "0.0.0",
49
+ "@arronqzy/typescript-config": "1.0.0",
50
50
  "@arronqzy/tailwind": "1.0.2"
51
51
  },
52
52
  "scripts": {
package/readme.md CHANGED
@@ -154,6 +154,7 @@ const dataUrl = await getPreviewSnapshot({
154
154
  | `addEventSubscription` / `AbuilderEvents` | 工作区事件 |
155
155
  | `getPreviewSnapshot` | 预览截图 data URL |
156
156
  | `parseWorkspaceData` 等 | 校验 / 解析 / 空工作区 |
157
+ | `listWorkspaceProjects` / `deleteWorkspaceProject` / `clearWorkspaceProjects` | 列出 / 删除 / 清空浏览器 IndexedDB 中保存的工作区(参数 `nameSpace`) |
157
158
  | `createView` / `createAction` | 视图 DSL 扩展点(`src/core`) |
158
159
 
159
160
  ## 样式
package/src/index.ts CHANGED
@@ -19,5 +19,11 @@ export type {
19
19
  WorkspaceData,
20
20
  } from "./panel/library/event-subscription";
21
21
  export type { WorkspaceProjectRecord } from "./panel/library/workspace-project-db";
22
+ export {
23
+ listWorkspaceProjects,
24
+ deleteWorkspaceProject,
25
+ clearWorkspaceProjects,
26
+ } from "./panel/library/workspace-storage";
27
+ export type { WorkspaceProjectListItem } from "./panel/library/workspace-storage";
22
28
  export { getPreviewSnapshot } from "./panel/library/preview-snapshot";
23
29
  export type { GetPreviewSnapshotOptions } from "./panel/library/preview-snapshot";
@@ -5,14 +5,17 @@ import type { BlueprintDocument, BlueprintMetaDraft } from "@arronqzy/react-blue
5
5
  import type { State } from "@arronqzy/rx-store";
6
6
  import {
7
7
  createWorkspaceProjectId,
8
- deleteWorkspaceProject,
9
8
  getWorkspaceProject,
10
9
  listWorkspaceProjects,
11
10
  putWorkspaceProject,
12
11
  type WorkspaceProjectListItem,
13
12
  type WorkspaceProjectRecord,
14
13
  } from "../library/workspace-project-db";
15
- import { broadcastWorkspaceProjectUpdate } from "../library/workspace-project-sync";
14
+ import { deleteWorkspaceProject } from "../library/workspace-storage";
15
+ import {
16
+ broadcastWorkspaceProjectUpdate,
17
+ subscribeWorkspaceProjectsChanged,
18
+ } from "../library/workspace-project-sync";
16
19
  import { writeWorkspacePreviewCache, readWorkspacePreviewCache } from "../library/workspace-project-cache";
17
20
  import {
18
21
  cloneWorkspaceSnapshot,
@@ -159,6 +162,20 @@ export function useWorkspaceProjects({
159
162
  void refreshProjects();
160
163
  }, [refreshProjects]);
161
164
 
165
+ useEffect(() => {
166
+ return subscribeWorkspaceProjectsChanged((message) => {
167
+ const removedActive =
168
+ message.type === "cleared" ||
169
+ (activeProjectId !== null && message.projectIds.includes(activeProjectId));
170
+ if (removedActive) {
171
+ setActiveProjectId(null);
172
+ setActiveProjectName(null);
173
+ syncedSnapshotRef.current = null;
174
+ }
175
+ void refreshProjects();
176
+ }, nameSpace);
177
+ }, [activeProjectId, nameSpace, refreshProjects]);
178
+
162
179
  useEffect(() => {
163
180
  if (!pendingRevisionSyncRef.current) return;
164
181
  syncedPanelRevisionRef.current = panelRevision;
@@ -36,3 +36,30 @@ export function readWorkspacePreviewCache(
36
36
  return null;
37
37
  }
38
38
  }
39
+
40
+ export function removeWorkspacePreviewCache(
41
+ projectId: string,
42
+ nameSpace?: string | null
43
+ ): void {
44
+ try {
45
+ localStorage.removeItem(previewCacheKey(projectId, nameSpace));
46
+ } catch {
47
+ // ignore private mode
48
+ }
49
+ }
50
+
51
+ export function clearWorkspacePreviewCaches(nameSpace?: string | null): void {
52
+ try {
53
+ const suffix = appStorageKey("", nameSpace);
54
+ const keys: string[] = [];
55
+ for (let i = 0; i < localStorage.length; i += 1) {
56
+ const key = localStorage.key(i);
57
+ if (!key || !key.startsWith(CACHE_PREFIX)) continue;
58
+ // 无 nameSpace 时 key 不带 "__<ns>" 后缀,避免误删其他 nameSpace 的标记
59
+ if (suffix ? key.endsWith(suffix) : !key.includes("__")) keys.push(key);
60
+ }
61
+ keys.forEach((key) => localStorage.removeItem(key));
62
+ } catch {
63
+ // ignore private mode
64
+ }
65
+ }
@@ -286,3 +286,7 @@ export async function deleteWorkspaceProject(
286
286
  ): Promise<void> {
287
287
  await runTransaction<undefined>("readwrite", (store) => store.delete(id), nameSpace);
288
288
  }
289
+
290
+ export async function clearWorkspaceProjectStore(nameSpace?: string | null): Promise<void> {
291
+ await runTransaction<undefined>("readwrite", (store) => store.clear(), nameSpace);
292
+ }
@@ -1,6 +1,7 @@
1
1
  import { appStorageKey } from "@arronqzy/blueprint-dsl";
2
2
 
3
3
  const CHANNEL_PREFIX = "arronqzy-workspace-project";
4
+ const LIST_CHANNEL = "arronqzy-workspace-projects-changed";
4
5
 
5
6
  export type WorkspaceProjectSyncMessage = {
6
7
  type: "updated";
@@ -8,6 +9,36 @@ export type WorkspaceProjectSyncMessage = {
8
9
  updatedAt: number;
9
10
  };
10
11
 
12
+ export type WorkspaceProjectsChangedMessage =
13
+ | { type: "deleted"; projectIds: string[] }
14
+ | { type: "cleared" };
15
+
16
+ export function broadcastWorkspaceProjectsChanged(
17
+ message: WorkspaceProjectsChangedMessage,
18
+ nameSpace?: string | null
19
+ ): void {
20
+ if (typeof BroadcastChannel === "undefined") return;
21
+ const channel = new BroadcastChannel(appStorageKey(LIST_CHANNEL, nameSpace));
22
+ channel.postMessage(message);
23
+ channel.close();
24
+ }
25
+
26
+ export function subscribeWorkspaceProjectsChanged(
27
+ onChange: (message: WorkspaceProjectsChangedMessage) => void,
28
+ nameSpace?: string | null
29
+ ): () => void {
30
+ if (typeof BroadcastChannel === "undefined") {
31
+ return () => {};
32
+ }
33
+ const channel = new BroadcastChannel(appStorageKey(LIST_CHANNEL, nameSpace));
34
+ channel.onmessage = (event: MessageEvent<WorkspaceProjectsChangedMessage>) => {
35
+ const data = event.data;
36
+ if (!data || (data.type !== "deleted" && data.type !== "cleared")) return;
37
+ onChange(data);
38
+ };
39
+ return () => channel.close();
40
+ }
41
+
11
42
  export function getWorkspaceProjectChannelName(
12
43
  projectId: string,
13
44
  nameSpace?: string | null
@@ -0,0 +1,38 @@
1
+ import {
2
+ clearWorkspaceProjectStore,
3
+ deleteWorkspaceProject as deleteWorkspaceProjectRecord,
4
+ listWorkspaceProjects,
5
+ } from "./workspace-project-db";
6
+ import {
7
+ clearWorkspacePreviewCaches,
8
+ removeWorkspacePreviewCache,
9
+ } from "./workspace-project-cache";
10
+ import { broadcastWorkspaceProjectsChanged } from "./workspace-project-sync";
11
+
12
+ export { listWorkspaceProjects };
13
+ export type { WorkspaceProjectListItem } from "./workspace-project-db";
14
+
15
+ /**
16
+ * 删除 IndexedDB 中保存的单个工作区(及其 localStorage 预览标记)。
17
+ * `nameSpace` 需与 `ReactViewPanel` 的 `nameSpace` 一致;已挂载的面板会自动刷新工作区列表。
18
+ */
19
+ export async function deleteWorkspaceProject(
20
+ id: string,
21
+ nameSpace?: string | null
22
+ ): Promise<void> {
23
+ await deleteWorkspaceProjectRecord(id, nameSpace);
24
+ removeWorkspacePreviewCache(id, nameSpace);
25
+ broadcastWorkspaceProjectsChanged({ type: "deleted", projectIds: [id] }, nameSpace);
26
+ }
27
+
28
+ /**
29
+ * 清空该 `nameSpace` 下保存的全部工作区,返回删除的数量。
30
+ * 不影响蓝图库、执行日志、接口集合等其他存储。
31
+ */
32
+ export async function clearWorkspaceProjects(nameSpace?: string | null): Promise<number> {
33
+ const items = await listWorkspaceProjects(nameSpace);
34
+ await clearWorkspaceProjectStore(nameSpace);
35
+ clearWorkspacePreviewCaches(nameSpace);
36
+ broadcastWorkspaceProjectsChanged({ type: "cleared" }, nameSpace);
37
+ return items.length;
38
+ }