@arronqzy/react-view 1.0.38 → 1.0.40

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.38",
3
+ "version": "1.0.40",
4
4
  "files": [
5
5
  "src",
6
6
  "dist",
@@ -31,13 +31,13 @@
31
31
  "@codemirror/theme-one-dark": "^6.1.2",
32
32
  "@codemirror/view": "^6.36.2",
33
33
  "@arronqzy/blueprint-dsl": "1.0.17",
34
- "@arronqzy/ui": "1.0.10",
34
+ "@arronqzy/ui": "1.0.11",
35
35
  "@arronqzy/i18n": "0.1.17",
36
- "@arronqzy/react-blueprint": "1.0.23",
36
+ "@arronqzy/react-blueprint": "1.0.25",
37
37
  "@arronqzy/rx-store": "1.0.8",
38
38
  "@arronqzy/view-scene3d": "0.1.5",
39
- "@arronqzy/webllm-assistant": "0.1.16",
40
- "@arronqzy/view-table": "0.1.8"
39
+ "@arronqzy/view-table": "0.1.8",
40
+ "@arronqzy/webllm-assistant": "0.1.16"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/react": "^19.0.1",
package/readme.md CHANGED
@@ -57,7 +57,8 @@ export function Editor({ workspace }: { workspace?: WorkspaceProjectRecord | nul
57
57
  |------|------|------|
58
58
  | `className` | 根节点 class | — |
59
59
  | `initialZoom` | 初始缩放 | `1` |
60
- | `initialWorkspace` | 挂载后加载的完整工作区。空则空白画布 | — |
60
+ | `initialWorkspace` | 挂载后加载的完整工作区。空则空白画布。带 `id` 时默认同步进 IndexedDB | — |
61
+ | `workspaceId` | 按 IndexedDB id 打开(不 remount) | — |
61
62
 
62
63
  ## 在线预览
63
64
 
@@ -89,7 +90,7 @@ parseOnlinePreviewSearchParams("?preview=online&projectId=xxx");
89
90
 
90
91
  ## 工作区事件
91
92
 
92
- 回调带完整工作区,用来写自己的后端。IndexedDB 的增删改查不导出。
93
+ 回调带完整工作区,用来写自己的后端。`initialWorkspace` 会默认同步进 IndexedDB;本地副本可用导出的 list / get / delete / clear 清理。
93
94
 
94
95
  ```ts
95
96
  import {
@@ -111,10 +112,12 @@ syncSub.unsubscribe();
111
112
  | 事件 | 常量 | 何时触发 |
112
113
  |------|------|----------|
113
114
  | `workspace:add` | `AbuilderEvents.workspaceAdd` | 创建成功 |
114
- | `workspace:sync` | `AbuilderEvents.workspaceSync` | 同步成功 |
115
+ | `workspace:sync` | `AbuilderEvents.workspaceSync` | 同步成功(载荷含 `nameSpace`) |
115
116
  | `workspace:delete` | `AbuilderEvents.workspaceDelete` | 删除工作区,参数 `{ ids, nameSpace }` |
117
+ | `blueprint-library:changed` | `AbuilderEvents.blueprintLibraryChanged` | 蓝图库变更 |
118
+ | `api-collection:changed` | `AbuilderEvents.apiCollectionChanged` | 接口集合变更 |
116
119
 
117
- `workspace:add` / `workspace:sync` 的参数字段:`id`、`name`、`createdAt`、`updatedAt`、`panelState`、`blueprintDocument`、`blueprintMeta`、`productName`、`titleIconDataUrl`。
120
+ `workspace:add` / `workspace:sync` 的参数字段:上述工作区字段 + `nameSpace`。可用 `addEventSubscription(event, cb, { nameSpace })` 过滤。
118
121
 
119
122
  校验:
120
123
 
@@ -155,14 +158,14 @@ const dataUrl = await getPreviewSnapshot({
155
158
  | `addEventSubscription` / `AbuilderEvents` | 工作区事件 |
156
159
  | `getPreviewSnapshot` | 预览截图 data URL |
157
160
  | `parseWorkspaceData` 等 | 校验 / 解析 / 空工作区 |
158
- | `listWorkspaceProjects` / `getWorkspaceProject` / `deleteWorkspaceProject` / `clearWorkspaceProjects` | 浏览器 IndexedDB 中保存的工作区 |
159
- | `listBlueprintLibrary` / `getBlueprintLibraryRecord` / `deleteBlueprintLibraryRecord` / `clearBlueprintLibrary` | 蓝图库;删除后已挂载的面板自动刷新 |
160
- | `listExecutionRunRecords` / `getExecutionRunRecord` / `deleteExecutionRunRecord` / `clearAllExecutionRunRecords` / `purgeExecutionRunsOlderThan` / `trimExecutionRunRecordsToMax` | 蓝图执行日志 |
161
- | `readExecutionLogSettings` / `writeExecutionLogSettings` | 执行日志保留策略 |
162
- | `listApiCollectionRecords` / `deleteApiCollection` / `clearApiCollections` | 接口集合 |
163
- | `clearAbuilderStorage` | 一次清空以上四类数据,返回各自删除数量;不动偏好设置 |
164
-
165
- 本地数据函数的最后一个参数都是 `nameSpace`,要和面板的 `nameSpace` 一致。
161
+ | `putWorkspaceProject` / `upsertWorkspaceProject` / `list` / `get` / `delete` / `clear` | 工作区完整 CRUD |
162
+ | `putBlueprintLibraryRecord` / `list` / `get` / `delete` / `clear` | 蓝图库完整 CRUD |
163
+ | `putExecutionRunRecord` / `list` / `get` / `delete` / `clear` / `purge*` / `trim*` | 执行日志 |
164
+ | `putApiCollection` / `upsertApiCollectionFromJson` / `list` / `delete` / `clear` | 接口集合 |
165
+ | `getCurrentWorkspace` / `openWorkspace` / `createWorkspace` / `saveWorkspace` / `syncWorkspace` | 命令式操作已挂载面板 |
166
+ | `clearAbuilderStorage` | 一次清空四类数据 |
167
+
168
+ 本地数据函数的 `nameSpace` 要和面板一致。面板 `ref` 类型为 `AbuilderViewHandle`。
166
169
  | `createView` / `createAction` | 视图 DSL 扩展点(`src/core`) |
167
170
 
168
171
  ## 样式
package/src/index.ts CHANGED
@@ -12,28 +12,38 @@ export {
12
12
  export type {
13
13
  AbuilderEventName,
14
14
  AbuilderEventPayloadMap,
15
+ AddEventSubscriptionOptions,
15
16
  WorkspaceAddEventPayload,
16
17
  WorkspaceSyncEventPayload,
17
18
  WorkspaceDeleteEventPayload,
19
+ BlueprintLibraryChangedEventPayload,
18
20
  ApiCollectionUploadStartEventPayload,
19
21
  ApiCollectionUploadSuccessEventPayload,
22
+ ApiCollectionChangedEventPayload,
20
23
  WorkspaceData,
21
24
  } from "./panel/library/event-subscription";
22
25
  export type { WorkspaceProjectRecord } from "./panel/library/workspace-project-db";
23
26
  export {
24
27
  listWorkspaceProjects,
25
28
  getWorkspaceProject,
29
+ putWorkspaceProject,
30
+ upsertWorkspaceProject,
26
31
  deleteWorkspaceProject,
27
32
  clearWorkspaceProjects,
28
33
  } from "./panel/library/workspace-storage";
29
- export type { WorkspaceProjectListItem } from "./panel/library/workspace-storage";
34
+ export type {
35
+ WorkspaceProjectListItem,
36
+ PutWorkspaceProjectOptions,
37
+ } from "./panel/library/workspace-storage";
30
38
  export {
31
39
  listBlueprintLibrary,
32
40
  getBlueprintLibraryRecord,
41
+ putBlueprintLibraryRecord,
33
42
  deleteBlueprintLibraryRecord,
34
43
  clearBlueprintLibrary,
35
44
  listExecutionRunRecords,
36
45
  getExecutionRunRecord,
46
+ putExecutionRunRecord,
37
47
  deleteExecutionRunRecord,
38
48
  clearAllExecutionRunRecords,
39
49
  purgeExecutionRunsOlderThan,
@@ -41,6 +51,8 @@ export {
41
51
  readExecutionLogSettings,
42
52
  writeExecutionLogSettings,
43
53
  listApiCollectionRecords,
54
+ putApiCollection,
55
+ upsertApiCollectionFromJson,
44
56
  deleteApiCollection,
45
57
  clearApiCollections,
46
58
  clearAbuilderStorage,
@@ -52,6 +64,16 @@ export type {
52
64
  ExecutionRunRecord,
53
65
  ExecutionLogSettings,
54
66
  ApiCollectionRecord,
67
+ UpsertApiCollectionInput,
68
+ UpsertApiCollectionResult,
55
69
  } from "./panel/library/abuilder-storage";
70
+ export {
71
+ getCurrentWorkspace,
72
+ openWorkspace,
73
+ createWorkspace,
74
+ saveWorkspace,
75
+ syncWorkspace,
76
+ } from "./panel/library/abuilder-view-handle";
77
+ export type { AbuilderViewHandle } from "./panel/library/abuilder-view-handle";
56
78
  export { getPreviewSnapshot } from "./panel/library/preview-snapshot";
57
79
  export type { GetPreviewSnapshotOptions } from "./panel/library/preview-snapshot";
@@ -1,4 +1,16 @@
1
- import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from "react";
10
+ import {
11
+ registerAbuilderViewHandle,
12
+ type AbuilderViewHandle,
13
+ } from "./library/abuilder-view-handle";
2
14
  import { useI18n } from "@arronqzy/i18n/react";
3
15
  import { relocalizeMessage, type Locale } from "@arronqzy/i18n";
4
16
  import type { State } from "@arronqzy/rx-store";
@@ -99,13 +111,16 @@ import { PANEL_Z_INDEX } from "./constants/zIndex";
99
111
  import { useRafThrottledScroll } from "./hooks/useRafThrottledScroll";
100
112
  import {
101
113
  IconBackward,
114
+ IconBlueprint,
102
115
  IconBringFront,
103
116
  IconClose,
104
117
  IconForward,
105
118
  IconImage,
106
119
  IconLayers,
120
+ IconMoon,
107
121
  IconRedo,
108
122
  IconSendBack,
123
+ IconSun,
109
124
  IconUndo,
110
125
  } from "./icons";
111
126
  import {
@@ -126,7 +141,6 @@ import {
126
141
  ResizableHandle,
127
142
  ResizablePanel,
128
143
  ResizablePanelGroup,
129
- Switch,
130
144
  Toaster,
131
145
  Tooltip,
132
146
  TooltipContent,
@@ -141,6 +155,11 @@ export type ReactViewPanelProps = {
141
155
  className?: string;
142
156
  /** 外部传入的完整工作区数据,挂载后按这份数据完整渲染;空则显示空白,不自动打开 IndexedDB 记录 */
143
157
  initialWorkspace?: WorkspaceProjectRecord | null;
158
+ /**
159
+ * 按 IndexedDB id 打开工作区(不 remount)。
160
+ * 与 `initialWorkspace` 同时存在时:首次优先完整记录;之后 id 变化从 IndexedDB 打开。
161
+ */
162
+ workspaceId?: string | null;
144
163
  /** 界面语言;省略时按 localStorage / 浏览器语言解析 */
145
164
  locale?: Locale | null;
146
165
  onLocaleChange?: (locale: Locale) => void;
@@ -156,14 +175,18 @@ export type ReactViewPanelProps = {
156
175
  packageVersion?: string | null;
157
176
  };
158
177
 
159
- function ReactViewPanelInner({
178
+ const ReactViewPanelInner = forwardRef<AbuilderViewHandle, ReactViewPanelProps>(function ReactViewPanelInner(
179
+ {
160
180
  initialZoom = 1,
161
181
  className,
162
182
  initialWorkspace = null,
183
+ workspaceId = null,
163
184
  nameSpace = null,
164
185
  packageName = null,
165
186
  packageVersion = null,
166
- }: ReactViewPanelProps) {
187
+ }: ReactViewPanelProps,
188
+ ref
189
+ ) {
167
190
  const { t, locale, setLocale } = useI18n();
168
191
  const messages = useMemo(() => getPanelMessages(t), [t]);
169
192
  const THEME_STORAGE_KEY = appStorageKey("panel:theme", nameSpace);
@@ -1076,9 +1099,26 @@ function ReactViewPanelInner({
1076
1099
  panelRevision: `${historyCursor}|${allElements.length}`,
1077
1100
  onProjectApplied: handleWorkspaceProjectApplied,
1078
1101
  initialWorkspace,
1102
+ workspaceId,
1079
1103
  nameSpace,
1080
1104
  });
1081
1105
 
1106
+ const viewHandle = useMemo<AbuilderViewHandle>(
1107
+ () => ({
1108
+ getNameSpace: () => nameSpace ?? null,
1109
+ getCurrentWorkspace: () => workspaceProjects.getCurrentWorkspaceRecord(),
1110
+ openWorkspace: (idOrRecord) => workspaceProjects.handleOpenProject(idOrRecord),
1111
+ createWorkspace: () => workspaceProjects.handleCreateProject(),
1112
+ saveWorkspace: () => workspaceProjects.handleSaveProject(),
1113
+ syncWorkspace: () => workspaceProjects.handleSyncProject(),
1114
+ }),
1115
+ [nameSpace, workspaceProjects]
1116
+ );
1117
+
1118
+ useImperativeHandle(ref, () => viewHandle, [viewHandle]);
1119
+
1120
+ useEffect(() => registerAbuilderViewHandle(viewHandle), [viewHandle]);
1121
+
1082
1122
  useEffect(() => {
1083
1123
  registerPreviewSnapshotProvider(async () => {
1084
1124
  return captureEditorPreviewSnapshot({
@@ -1869,13 +1909,37 @@ function ReactViewPanelInner({
1869
1909
  </Tooltip>
1870
1910
  </TooltipProvider>
1871
1911
  <style>{`
1872
- .panel-font-root :is(span, label, p, strong, button, input, textarea, select, option, a, li) {
1912
+ /* 功能说明视觉层次:主标签醒目、辅助说明更轻(类苹果) */
1913
+ .panel-font-root {
1914
+ font-size: var(--panel-font-px);
1915
+ font-weight: 400;
1916
+ letter-spacing: -0.011em;
1917
+ }
1918
+ .panel-font-root [data-typo="title"] {
1919
+ font-size: calc(var(--panel-font-px) * 1.08) !important;
1920
+ font-weight: 600 !important;
1921
+ letter-spacing: -0.022em;
1922
+ }
1923
+ .panel-font-root [data-typo="label"] {
1873
1924
  font-size: var(--panel-font-px) !important;
1925
+ font-weight: 510 !important;
1926
+ letter-spacing: -0.015em;
1927
+ }
1928
+ .panel-font-root [data-typo="caption"] {
1929
+ font-size: calc(var(--panel-font-px) * 0.85) !important;
1930
+ font-weight: 400 !important;
1931
+ letter-spacing: -0.008em;
1932
+ }
1933
+ .panel-font-root [data-typo="micro"] {
1934
+ font-size: calc(var(--panel-font-px) * 0.77) !important;
1935
+ font-weight: 400 !important;
1874
1936
  }
1875
1937
  /* 画布节点、富文本内容使用各自的字号,不被编辑器 UI 字号强制覆盖 */
1876
1938
  .panel-font-root [data-workspace-region="view"] :is(span, label, p, strong, button, input, textarea, select, option, a, li),
1877
1939
  .panel-font-root [data-panel-user-text] :is(span, label, p, strong, button, input, textarea, select, option, a, li) {
1878
1940
  font-size: inherit !important;
1941
+ font-weight: inherit !important;
1942
+ letter-spacing: inherit !important;
1879
1943
  }
1880
1944
  .panel-font-root :is(input, textarea):focus,
1881
1945
  .panel-font-root :is(input, textarea):focus-visible {
@@ -1909,7 +1973,7 @@ function ReactViewPanelInner({
1909
1973
  box-shadow: 0 0 0 1px hsl(var(--primary) / 0.45) inset !important;
1910
1974
  }
1911
1975
  `}</style>
1912
- <nav className="panel-font-root relative z-30 flex items-center gap-2 border-b border-border bg-background/95 px-2 text-foreground">
1976
+ <nav className="panel-font-root relative z-30 flex h-10 shrink-0 items-center gap-2.5 border-b border-border/70 bg-background/80 px-3 text-foreground backdrop-blur-md supports-[backdrop-filter]:bg-background/70">
1913
1977
  <PanelMenubar
1914
1978
  t={t}
1915
1979
  handleExport={handleExport}
@@ -1963,8 +2027,8 @@ function ReactViewPanelInner({
1963
2027
  </Button>
1964
2028
  </nav>
1965
2029
  <ResizablePanelGroup direction="horizontal" className="min-h-0 w-full flex-1">
1966
- <ResizablePanel defaultSize={15} minSize={15}>
1967
- <div className="panel-font-root h-full">
2030
+ <ResizablePanel defaultSize={16} minSize={12} maxSize={28}>
2031
+ <div className="panel-font-root h-full min-w-0">
1968
2032
  <MaterialSidebar
1969
2033
  layers={layers}
1970
2034
  allElements={allElements}
@@ -2035,8 +2099,8 @@ function ReactViewPanelInner({
2035
2099
  />
2036
2100
  </div>
2037
2101
  </ResizablePanel>
2038
- <ResizableHandle withHandle />
2039
- <ResizablePanel defaultSize={65} minSize={10}>
2102
+ <ResizableHandle withHandle className="opacity-70 transition-opacity hover:opacity-100" />
2103
+ <ResizablePanel defaultSize={68} minSize={40}>
2040
2104
  {/* Center workspace */}
2041
2105
  <div
2042
2106
  className="panel-font-root min-w-0 h-full"
@@ -2054,18 +2118,18 @@ function ReactViewPanelInner({
2054
2118
  clearSelection();
2055
2119
  }}
2056
2120
  >
2057
- <div className="relative flex h-full flex-col overflow-hidden border border-border bg-background">
2121
+ <div className="relative flex h-full flex-col overflow-hidden bg-background">
2058
2122
  <div
2059
- className="relative flex items-center gap-2 border-b border-border bg-background/90 px-3 py-1.5 text-foreground"
2123
+ className="relative flex items-center gap-2 border-b border-border/60 bg-muted/15 px-3 py-1.5 text-foreground"
2060
2124
  style={{ zIndex: PANEL_Z_INDEX.toolbar }}
2061
2125
  >
2062
2126
  <Input
2063
2127
  value={displayProductName}
2064
2128
  onChange={(e) => setProductName(e.target.value)}
2065
2129
  placeholder={t("panel.menubar.productNamePlaceholder")}
2066
- className="h-7 w-[220px] text-xs focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none"
2130
+ className="h-7 w-[200px] rounded-md border-border/70 bg-background/80 text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none"
2067
2131
  />
2068
- <span className="text-[11px] text-muted-foreground">{t("panel.menubar.productName")}</span>
2132
+ <span className="hidden text-[11px] text-muted-foreground sm:inline">{t("panel.menubar.productName")}</span>
2069
2133
  <TooltipProvider delayDuration={150}>
2070
2134
  <Tooltip>
2071
2135
  <TooltipTrigger asChild>
@@ -2200,60 +2264,45 @@ function ReactViewPanelInner({
2200
2264
  </DropdownMenuContent>
2201
2265
  </DropdownMenu>
2202
2266
  <div className="flex-1" />
2203
- <div
2204
- data-blueprint-toggle
2205
- className="flex shrink-0 items-center gap-2 rounded-md border border-border bg-muted/30 px-2 py-1"
2206
- >
2207
- <span className="text-[11px] text-muted-foreground">{t("panel.menubar.blueprint")}</span>
2208
- <Switch
2209
- checked={blueprintOpen}
2210
- onCheckedChange={setBlueprintOpen}
2211
- aria-label={t("panel.menubar.showBlueprintPanel")}
2212
- />
2213
- </div>
2214
2267
  <TooltipProvider delayDuration={150}>
2215
- <Tooltip>
2216
- <TooltipTrigger asChild>
2217
- <div className="relative">
2218
- <Switch
2219
- checked={isDark}
2220
- onCheckedChange={applyTheme}
2221
- aria-label={t("panel.menubar.toggleTheme")}
2222
- className="data-[state=checked]:bg-primary/80 data-[state=unchecked]:bg-secondary"
2223
- />
2224
- <div
2268
+ <div className="inline-flex items-center gap-1">
2269
+ <Tooltip>
2270
+ <TooltipTrigger asChild>
2271
+ <button
2272
+ type="button"
2273
+ data-blueprint-toggle
2274
+ onClick={() => setBlueprintOpen((v) => !v)}
2275
+ aria-label={t("panel.menubar.showBlueprintPanel")}
2276
+ aria-pressed={blueprintOpen}
2225
2277
  className={[
2226
- "pointer-events-none absolute left-0.5 top-0.5 flex h-5 w-5 items-center justify-center transition-transform duration-200",
2227
- isDark ? "translate-x-5" : "translate-x-0",
2278
+ "inline-flex h-7 w-7 items-center justify-center rounded-md border transition-colors",
2279
+ blueprintOpen
2280
+ ? "border-primary/50 bg-primary/15 text-primary"
2281
+ : "border-border/70 bg-background/80 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
2228
2282
  ].join(" ")}
2229
2283
  >
2230
- {isDark ? (
2231
- <svg
2232
- viewBox="0 0 24 24"
2233
- className="h-3 w-3 text-foreground"
2234
- fill="none"
2235
- stroke="currentColor"
2236
- strokeWidth="1.8"
2237
- >
2238
- <path d="M21 12.79A9 9 0 1 1 11.21 3c0 .66.08 1.3.22 1.92A7 7 0 0 0 19.08 12c.62.14 1.26.22 1.92.22Z" />
2239
- </svg>
2240
- ) : (
2241
- <svg
2242
- viewBox="0 0 24 24"
2243
- className="h-3 w-3 text-foreground"
2244
- fill="none"
2245
- stroke="currentColor"
2246
- strokeWidth="1.8"
2247
- >
2248
- <circle cx="12" cy="12" r="4" />
2249
- <path d="M12 2v2.5M12 19.5V22M4.93 4.93l1.77 1.77M17.3 17.3l1.77 1.77M2 12h2.5M19.5 12H22M4.93 19.07 6.7 17.3M17.3 6.7l1.77-1.77" />
2250
- </svg>
2251
- )}
2252
- </div>
2253
- </div>
2254
- </TooltipTrigger>
2255
- <TooltipContent className="z-[10000]">{t("panel.menubar.toggleThemeHint")}</TooltipContent>
2256
- </Tooltip>
2284
+ <IconBlueprint active={blueprintOpen} />
2285
+ </button>
2286
+ </TooltipTrigger>
2287
+ <TooltipContent className="z-[10000]">
2288
+ {t("panel.menubar.showBlueprintPanel")}
2289
+ </TooltipContent>
2290
+ </Tooltip>
2291
+ <Tooltip>
2292
+ <TooltipTrigger asChild>
2293
+ <button
2294
+ type="button"
2295
+ onClick={() => applyTheme(!isDark)}
2296
+ aria-label={t("panel.menubar.toggleTheme")}
2297
+ aria-pressed={isDark}
2298
+ className="inline-flex h-7 w-7 items-center justify-center rounded-md border border-border/70 bg-background/80 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
2299
+ >
2300
+ {isDark ? <IconMoon /> : <IconSun />}
2301
+ </button>
2302
+ </TooltipTrigger>
2303
+ <TooltipContent className="z-[10000]">{t("panel.menubar.toggleThemeHint")}</TooltipContent>
2304
+ </Tooltip>
2305
+ </div>
2257
2306
  <Tooltip>
2258
2307
  <TooltipTrigger asChild>
2259
2308
  <button
@@ -2266,13 +2315,11 @@ function ReactViewPanelInner({
2266
2315
  </TooltipTrigger>
2267
2316
  <TooltipContent className="z-[10000]">{t("panel.menubar.zoomOutCanvas")}</TooltipContent>
2268
2317
  </Tooltip>
2269
- </TooltipProvider>
2270
- <span className="w-20 text-center text-xs">
2271
- {zoom.x === zoom.y
2272
- ? `${(zoom.x * 100).toFixed(0)}%`
2273
- : `${(zoom.x * 100).toFixed(0)}%×${(zoom.y * 100).toFixed(0)}%`}
2274
- </span>
2275
- <TooltipProvider delayDuration={150}>
2318
+ <span className="w-20 text-center text-xs">
2319
+ {zoom.x === zoom.y
2320
+ ? `${(zoom.x * 100).toFixed(0)}%`
2321
+ : `${(zoom.x * 100).toFixed(0)}%×${(zoom.y * 100).toFixed(0)}%`}
2322
+ </span>
2276
2323
  <Tooltip>
2277
2324
  <TooltipTrigger asChild>
2278
2325
  <button
@@ -2704,9 +2751,9 @@ function ReactViewPanelInner({
2704
2751
  </div>
2705
2752
  </div>
2706
2753
  </ResizablePanel>
2707
- <ResizableHandle withHandle />
2708
- <ResizablePanel defaultSize={20} minSize={20}>
2709
- <div className="panel-font-root h-full">
2754
+ <ResizableHandle withHandle className="opacity-70 transition-opacity hover:opacity-100" />
2755
+ <ResizablePanel defaultSize={20} minSize={16} maxSize={32}>
2756
+ <div className="panel-font-root h-full min-w-0">
2710
2757
  <WorkspaceConfigSidebar
2711
2758
  configFocus={sidebarConfigFocus}
2712
2759
  executionLog={executionLogView}
@@ -2798,10 +2845,16 @@ function ReactViewPanelInner({
2798
2845
  open={apiCollectionManagerOpen}
2799
2846
  onOpenChange={setApiCollectionManagerOpen}
2800
2847
  onUploadStart={(info) => {
2801
- emitAbuilderEvent(AbuilderEvents.apiCollectionUploadStart, info);
2848
+ emitAbuilderEvent(AbuilderEvents.apiCollectionUploadStart, {
2849
+ ...info,
2850
+ nameSpace: nameSpace ?? null,
2851
+ });
2802
2852
  }}
2803
2853
  onUploadSuccess={(info) => {
2804
- emitAbuilderEvent(AbuilderEvents.apiCollectionUploadSuccess, info);
2854
+ emitAbuilderEvent(AbuilderEvents.apiCollectionUploadSuccess, {
2855
+ ...info,
2856
+ nameSpace: nameSpace ?? null,
2857
+ });
2805
2858
  }}
2806
2859
  onSyncCollection={(record) => {
2807
2860
  const patches = buildApiCollectionSyncPatches({
@@ -2846,17 +2899,14 @@ function ReactViewPanelInner({
2846
2899
  <Toaster />
2847
2900
  </div>
2848
2901
  );
2849
- }
2850
-
2851
- export function ReactViewPanel({
2852
- locale,
2853
- onLocaleChange,
2854
- nameSpace,
2855
- ...rest
2856
- }: ReactViewPanelProps) {
2857
- return (
2858
- <I18nRoot locale={locale} onLocaleChange={onLocaleChange} nameSpace={nameSpace}>
2859
- <ReactViewPanelInner nameSpace={nameSpace} {...rest} />
2860
- </I18nRoot>
2861
- );
2862
- }
2902
+ });
2903
+
2904
+ export const ReactViewPanel = forwardRef<AbuilderViewHandle, ReactViewPanelProps>(
2905
+ function ReactViewPanel({ locale, onLocaleChange, nameSpace, ...rest }, ref) {
2906
+ return (
2907
+ <I18nRoot locale={locale} onLocaleChange={onLocaleChange} nameSpace={nameSpace}>
2908
+ <ReactViewPanelInner ref={ref} nameSpace={nameSpace} {...rest} />
2909
+ </I18nRoot>
2910
+ );
2911
+ }
2912
+ );
@@ -957,7 +957,7 @@ const PanelElementNode = React.memo(function PanelElementNode({
957
957
  className={[
958
958
  "absolute select-none",
959
959
  previewMode ? "" : "rv-selectable",
960
- !previewMode && isSelected ? "ring-2 ring-blue-500/90 ring-offset-0" : "",
960
+ !previewMode && isSelected ? "rv-node-selected" : "",
961
961
  hasViewEvent ? "cursor-pointer" : "",
962
962
  ].join(" ")}
963
963
  data-element-id={el.id}