@arronqzy/vue-view 0.1.24 → 0.1.26

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/README.md CHANGED
@@ -49,17 +49,7 @@ import {
49
49
  } from "@arronqzy/vue-view";
50
50
  ```
51
51
 
52
- 创建 / 同步发出 `workspace:add` / `workspace:sync`,删除工作区发出 `workspace:delete`(参数 `{ ids, nameSpace }`)。`parseWorkspaceData` 的 `value` 可作 `initialWorkspace`。
53
-
54
- 浏览器里的本地数据可以读取和清理,最后一个参数都是 `nameSpace`:
55
-
56
- - 工作区:`listWorkspaceProjects` / `getWorkspaceProject` / `deleteWorkspaceProject` / `clearWorkspaceProjects`
57
- - 蓝图库:`listBlueprintLibrary` / `getBlueprintLibraryRecord` / `deleteBlueprintLibraryRecord` / `clearBlueprintLibrary`
58
- - 蓝图执行日志:`listExecutionRunRecords` / `getExecutionRunRecord` / `deleteExecutionRunRecord` / `clearAllExecutionRunRecords` / `purgeExecutionRunsOlderThan` / `trimExecutionRunRecordsToMax`,保留策略用 `readExecutionLogSettings` / `writeExecutionLogSettings`
59
- - 接口集合:`listApiCollectionRecords` / `deleteApiCollection` / `clearApiCollections`
60
- - `clearAbuilderStorage`:一次清空以上四类,返回各自删除数量
61
-
62
- 工作区、蓝图库的写入接口不导出(由编辑器自己维护)。`getPreviewSnapshot` 只在 React 包实现。
52
+ 创建 / 同步发出 `workspace:add` / `workspace:sync`(载荷含 `nameSpace`),删除发出 `workspace:delete`。另有 `blueprint-library:changed` / `api-collection:changed`。`parseWorkspaceData` 的 `value` 可作 `initialWorkspace`;带 `id` 时默认同步进 IndexedDB。也支持 `workspaceId` prop、`ref`(`AbuilderViewHandle`)以及 `put*` / `openWorkspace` / `getCurrentWorkspace` / `getPreviewSnapshot` 等与 React 对齐的公开 API。
63
53
 
64
54
  ## 和 React 版的差别
65
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/vue-view",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Vue 3 visual view editor for Abuilder (Ant Design Vue)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,13 +23,13 @@
23
23
  "selecto": "^1.26.3",
24
24
  "uuid": "^13.0.0",
25
25
  "vue": "^3.5.13",
26
- "@arronqzy/i18n": "0.1.17",
27
26
  "@arronqzy/rx-store": "1.0.8",
28
27
  "@arronqzy/blueprint-dsl": "1.0.17",
29
28
  "@arronqzy/view-scene3d": "0.1.5",
29
+ "@arronqzy/vue-blueprint": "0.1.20",
30
30
  "@arronqzy/view-table": "0.1.8",
31
31
  "@arronqzy/vue-rx-store": "0.1.6",
32
- "@arronqzy/vue-blueprint": "0.1.18"
32
+ "@arronqzy/i18n": "0.1.17"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "vue": "^3.4.0"
package/src/index.ts CHANGED
@@ -16,17 +16,24 @@ export { createWorkspaceProjectId } from "./panel/library/workspace-project-db";
16
16
  export {
17
17
  listWorkspaceProjects,
18
18
  getWorkspaceProject,
19
+ putWorkspaceProject,
20
+ upsertWorkspaceProject,
19
21
  deleteWorkspaceProject,
20
22
  clearWorkspaceProjects,
21
23
  } from "./panel/library/workspace-storage";
22
- export type { WorkspaceProjectListItem } from "./panel/library/workspace-storage";
24
+ export type {
25
+ WorkspaceProjectListItem,
26
+ PutWorkspaceProjectOptions,
27
+ } from "./panel/library/workspace-storage";
23
28
  export {
24
29
  listBlueprintLibrary,
25
30
  getBlueprintLibraryRecord,
31
+ putBlueprintLibraryRecord,
26
32
  deleteBlueprintLibraryRecord,
27
33
  clearBlueprintLibrary,
28
34
  listExecutionRunRecords,
29
35
  getExecutionRunRecord,
36
+ putExecutionRunRecord,
30
37
  deleteExecutionRunRecord,
31
38
  clearAllExecutionRunRecords,
32
39
  purgeExecutionRunsOlderThan,
@@ -34,6 +41,8 @@ export {
34
41
  readExecutionLogSettings,
35
42
  writeExecutionLogSettings,
36
43
  listApiCollectionRecords,
44
+ putApiCollection,
45
+ upsertApiCollectionFromJson,
37
46
  deleteApiCollection,
38
47
  clearApiCollections,
39
48
  clearAbuilderStorage,
@@ -45,6 +54,8 @@ export type {
45
54
  ExecutionRunRecord,
46
55
  ExecutionLogSettings,
47
56
  ApiCollectionRecord,
57
+ UpsertApiCollectionInput,
58
+ UpsertApiCollectionResult,
48
59
  } from "./panel/library/abuilder-storage";
49
60
  export {
50
61
  addEventSubscription,
@@ -53,14 +64,27 @@ export {
53
64
  export type {
54
65
  AbuilderEventName,
55
66
  AbuilderEventPayloadMap,
67
+ AddEventSubscriptionOptions,
56
68
  WorkspaceAddEventPayload,
57
69
  WorkspaceSyncEventPayload,
58
70
  WorkspaceDeleteEventPayload,
71
+ BlueprintLibraryChangedEventPayload,
59
72
  ApiCollectionUploadStartEventPayload,
60
73
  ApiCollectionUploadSuccessEventPayload,
74
+ ApiCollectionChangedEventPayload,
61
75
  WorkspaceData,
62
76
  } from "./panel/library/event-subscription";
77
+ export {
78
+ getCurrentWorkspace,
79
+ openWorkspace,
80
+ createWorkspace,
81
+ saveWorkspace,
82
+ syncWorkspace,
83
+ } from "./panel/library/abuilder-view-handle";
84
+ export type { AbuilderViewHandle } from "./panel/library/abuilder-view-handle";
63
85
  export type {
64
86
  ParseCheckResult,
65
87
  WorkspaceParseCheckResult,
66
88
  } from "./panel/library/parse-workspace-data";
89
+ export { getPreviewSnapshot } from "./panel/library/preview-snapshot";
90
+ export type { GetPreviewSnapshotOptions } from "./panel/library/preview-snapshot";
@@ -40,6 +40,8 @@ import {
40
40
  resolvePreviewLayerElements,
41
41
  } from "./utils/panelStateIO";
42
42
  import { applyPreviewSceneFill, readOutputScale } from "./utils/outputScale";
43
+ import { registerPreviewSnapshotProvider } from "./library/preview-snapshot";
44
+ import { capturePreviewSceneElement } from "./library/capture-preview-snapshot";
43
45
 
44
46
  const { t, locale } = useI18n();
45
47
  useBlueprintDslLocaleSync();
@@ -292,6 +294,29 @@ onMounted(() => {
292
294
  onUnmounted(() => window.removeEventListener("resize", onResize));
293
295
  });
294
296
 
297
+ watch(
298
+ [layoutReady, sceneRef, sceneBounds, fillScale, outputScale],
299
+ () => {
300
+ if (!layoutReady.value || !sceneRef.value) {
301
+ registerPreviewSnapshotProvider(null);
302
+ return;
303
+ }
304
+ registerPreviewSnapshotProvider(async () => {
305
+ const scene = sceneRef.value;
306
+ if (!scene) return null;
307
+ const width = outputScale.value
308
+ ? sceneBounds.value.width
309
+ : sceneBounds.value.width * fillScale.value.scaleX;
310
+ const height = outputScale.value
311
+ ? sceneBounds.value.height
312
+ : sceneBounds.value.height * fillScale.value.scaleY;
313
+ return capturePreviewSceneElement(scene, width, height);
314
+ });
315
+ },
316
+ { immediate: true }
317
+ );
318
+ onUnmounted(() => registerPreviewSnapshotProvider(null));
319
+
295
320
  function noopUpdate() {}
296
321
  function noopSelect() {}
297
322
  </script>
@@ -1,5 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
3
+ import {
4
+ registerAbuilderViewHandle,
5
+ type AbuilderViewHandle,
6
+ } from "./library/abuilder-view-handle";
7
+ import {
8
+ captureEditorPreviewSnapshot,
9
+ registerPreviewSnapshotProvider,
10
+ } from "./library/preview-snapshot";
11
+ import { readOutputScale } from "./utils/outputScale";
3
12
  import type { State } from "@arronqzy/rx-store";
4
13
  import {
5
14
  useBlueprintDslLocaleSync,
@@ -41,9 +50,9 @@ import {
41
50
  Modal,
42
51
  Slider,
43
52
  Space,
44
- Switch,
45
53
  Tabs,
46
54
  Tag,
55
+ Tooltip,
47
56
  message,
48
57
  } from "ant-design-vue";
49
58
  import { usePanelElements } from "./hooks/usePanelElements";
@@ -90,6 +99,11 @@ const props = withDefaults(
90
99
  initialZoom?: number;
91
100
  nameSpace?: string | null;
92
101
  initialWorkspace?: WorkspaceProjectRecord | null;
102
+ /**
103
+ * 按 IndexedDB id 打开工作区(不 remount)。
104
+ * 与 initialWorkspace 同时存在时:首次优先完整记录;之后 id 变化从 IndexedDB 打开。
105
+ */
106
+ workspaceId?: string | null;
93
107
  /** 顶栏「设置 → 关于」展示的 npm 包名 */
94
108
  packageName?: string | null;
95
109
  /** 顶栏「设置 → 关于」展示的包版本号 */
@@ -99,6 +113,7 @@ const props = withDefaults(
99
113
  initialZoom: 1,
100
114
  nameSpace: null,
101
115
  initialWorkspace: null,
116
+ workspaceId: null,
102
117
  packageName: null,
103
118
  packageVersion: null,
104
119
  }
@@ -173,8 +188,8 @@ const editingLayerId = ref<string | null>(null);
173
188
  const editingLayerName = ref("");
174
189
  const isMergingLayers = ref(false);
175
190
  const mergeLayerName = ref("");
176
- const leftWidth = ref(240);
177
- const rightWidth = ref(300);
191
+ const leftWidth = ref(220);
192
+ const rightWidth = ref(280);
178
193
  const importInputRef = ref<HTMLInputElement | null>(null);
179
194
  const blueprintImportInputRef = ref<HTMLInputElement | null>(null);
180
195
  const apiCollectionManagerOpen = ref(false);
@@ -470,9 +485,39 @@ const workspaceProjects = useWorkspaceProjects({
470
485
  panelRevision: computed(() => `${historyCursor.value}|${allElements.value.length}`),
471
486
  onProjectApplied: handleWorkspaceProjectApplied,
472
487
  initialWorkspace: () => props.initialWorkspace,
488
+ workspaceId: () => props.workspaceId,
473
489
  nameSpace: () => props.nameSpace,
474
490
  });
475
491
 
492
+ const viewHandle: AbuilderViewHandle = {
493
+ getNameSpace: () => props.nameSpace ?? null,
494
+ getCurrentWorkspace: () => workspaceProjects.getCurrentWorkspaceRecord(),
495
+ openWorkspace: (idOrRecord) => workspaceProjects.handleOpenProject(idOrRecord),
496
+ createWorkspace: () => workspaceProjects.handleCreateProject(),
497
+ saveWorkspace: () => workspaceProjects.handleSaveProject(),
498
+ syncWorkspace: () => workspaceProjects.handleSyncProject(),
499
+ };
500
+
501
+ defineExpose(viewHandle);
502
+ onUnmounted(registerAbuilderViewHandle(viewHandle));
503
+
504
+ watch(
505
+ [canvasEl, allElements, layers, activeLayerId, () => props.nameSpace],
506
+ () => {
507
+ registerPreviewSnapshotProvider(async () =>
508
+ captureEditorPreviewSnapshot({
509
+ canvasRoot: canvasEl.value,
510
+ allElements: allElements.value,
511
+ layers: layers.value,
512
+ activeLayerId: activeLayerId.value,
513
+ outputScale: readOutputScale(props.nameSpace),
514
+ })
515
+ );
516
+ },
517
+ { immediate: true }
518
+ );
519
+ onUnmounted(() => registerPreviewSnapshotProvider(null));
520
+
476
521
  const blueprintLifecycleToken = computed(() => {
477
522
  const doc = blueprintGraph.value.document;
478
523
  const nodes = doc.nodes;
@@ -646,7 +691,10 @@ function handleApiCollectionUploadStart(info: {
646
691
  fileNames: string[];
647
692
  count: number;
648
693
  }) {
649
- emitAbuilderEvent(AbuilderEvents.apiCollectionUploadStart, info);
694
+ emitAbuilderEvent(AbuilderEvents.apiCollectionUploadStart, {
695
+ ...info,
696
+ nameSpace: props.nameSpace ?? null,
697
+ });
650
698
  }
651
699
 
652
700
  function handleApiCollectionUploadSuccess(info: {
@@ -657,7 +705,10 @@ function handleApiCollectionUploadSuccess(info: {
657
705
  persistLocal: boolean;
658
706
  }>;
659
707
  }) {
660
- emitAbuilderEvent(AbuilderEvents.apiCollectionUploadSuccess, info);
708
+ emitAbuilderEvent(AbuilderEvents.apiCollectionUploadSuccess, {
709
+ ...info,
710
+ nameSpace: props.nameSpace ?? null,
711
+ });
661
712
  }
662
713
 
663
714
  function handleApiCollectionSync(record: {
@@ -807,9 +858,9 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
807
858
 
808
859
  <template>
809
860
  <Layout class="vue-view-panel h-screen overflow-hidden" :class="props.class" :key="locale">
810
- <Layout.Header class="flex h-auto flex-wrap items-center gap-2 bg-[#001529] px-3 py-2">
861
+ <Layout.Header class="flex h-10 flex-wrap items-center gap-2 border-b border-border/70 !bg-background/80 px-3 py-0 leading-none backdrop-blur-md">
811
862
  <Dropdown :trigger="['click']">
812
- <Button type="text" class="!text-white">{{ t("panel.menubar.file") }}</Button>
863
+ <Button type="text" class="!text-foreground">{{ t("panel.menubar.file") }}</Button>
813
864
  <template #overlay>
814
865
  <Menu>
815
866
  <Menu.Item @click="handleExport">{{ t("panel.menubar.exportPanelJson") }}</Menu.Item>
@@ -818,7 +869,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
818
869
  </template>
819
870
  </Dropdown>
820
871
  <Dropdown :trigger="['click']">
821
- <Button type="text" class="!text-white">{{ t("panel.menubar.edit") }}</Button>
872
+ <Button type="text" class="!text-foreground">{{ t("panel.menubar.edit") }}</Button>
822
873
  <template #overlay>
823
874
  <Menu>
824
875
  <Menu.Item :disabled="!canUndo" @click="undo">{{ t("panel.menubar.undo") }}</Menu.Item>
@@ -834,7 +885,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
834
885
  </template>
835
886
  </Dropdown>
836
887
  <Dropdown :trigger="['click']">
837
- <Button type="text" class="!text-white">{{ t("panel.menubar.blueprint") }}</Button>
888
+ <Button type="text" class="!text-foreground">{{ t("panel.menubar.blueprint") }}</Button>
838
889
  <template #overlay>
839
890
  <Menu>
840
891
  <Menu.Item @click="openBlueprintExport">{{ t("panel.menubar.exportBlueprint") }}</Menu.Item>
@@ -857,7 +908,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
857
908
  @preview-project="openOnlinePreviewForProject"
858
909
  />
859
910
  <Dropdown :trigger="['click']">
860
- <Button type="text" class="!text-white">{{ t("panel.menubar.settings") }}</Button>
911
+ <Button type="text" class="!text-foreground">{{ t("panel.menubar.settings") }}</Button>
861
912
  <template #overlay>
862
913
  <Menu>
863
914
  <Menu.ItemGroup :title="t('panel.theme.language')">
@@ -879,7 +930,7 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
879
930
  </template>
880
931
  </Dropdown>
881
932
  <Dropdown :trigger="['click']">
882
- <Button type="text" class="!text-white">{{ t("panel.theme.language") }}</Button>
933
+ <Button type="text" class="!text-foreground">{{ t("panel.theme.language") }}</Button>
883
934
  <template #overlay>
884
935
  <Menu>
885
936
  <Menu.Item :class="{ 'ant-menu-item-selected': locale === 'zh-CN' }" @click="setLocale('zh-CN')">{{ t("panel.theme.zhCN") }}</Menu.Item>
@@ -914,12 +965,12 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
914
965
 
915
966
  <Layout.Content class="relative min-h-0 min-w-0">
916
967
  <div class="flex h-full min-h-0 flex-col">
917
- <div class="flex flex-wrap items-center gap-2 border-b bg-white px-3 py-2">
968
+ <div class="flex flex-wrap items-center gap-2 border-b border-border/60 bg-muted/15 px-3 py-1.5">
918
969
  <Input
919
970
  :value="displayProductName"
920
971
  size="small"
921
972
  @update:value="(value: string) => (productName = value)"
922
- class="w-[200px]"
973
+ class="w-[180px]"
923
974
  :placeholder="t('panel.menubar.productName')"
924
975
  />
925
976
  <span class="text-xs text-gray-500">{{ t("panel.menubar.zoom") }}</span>
@@ -933,10 +984,35 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
933
984
  />
934
985
  <Tag v-if="activeLayer">{{ activeLayer.name }}</Tag>
935
986
  <Tag color="blue">{{ t("panel.menubar.selectedCount", { count: selectedIds.length }) }}</Tag>
936
- <div data-blueprint-toggle class="ml-auto flex items-center gap-2">
937
- <span class="text-xs text-gray-500">{{ t("panel.menubar.blueprint") }}</span>
938
- <Switch v-model:checked="blueprintOpen" size="small" />
939
- </div>
987
+ <Tooltip :title="t('panel.menubar.showBlueprintPanel')">
988
+ <button
989
+ type="button"
990
+ data-blueprint-toggle
991
+ class="ml-auto inline-flex h-7 w-7 items-center justify-center rounded-md border transition-colors"
992
+ :class="
993
+ blueprintOpen
994
+ ? 'border-primary/50 bg-primary/15 text-primary'
995
+ : 'border-border/70 bg-background/80 text-muted-foreground hover:bg-accent hover:text-accent-foreground'
996
+ "
997
+ :aria-pressed="blueprintOpen"
998
+ :aria-label="t('panel.menubar.showBlueprintPanel')"
999
+ @click="blueprintOpen = !blueprintOpen"
1000
+ >
1001
+ <svg
1002
+ viewBox="0 0 24 24"
1003
+ class="h-3.5 w-3.5"
1004
+ fill="none"
1005
+ stroke="currentColor"
1006
+ :stroke-width="blueprintOpen ? 2.2 : 2"
1007
+ >
1008
+ <rect x="3" y="4" width="18" height="16" rx="2" />
1009
+ <circle cx="8" cy="10" r="1.5" :fill="blueprintOpen ? 'currentColor' : 'none'" />
1010
+ <circle cx="16" cy="10" r="1.5" :fill="blueprintOpen ? 'currentColor' : 'none'" />
1011
+ <circle cx="12" cy="16" r="1.5" :fill="blueprintOpen ? 'currentColor' : 'none'" />
1012
+ <path d="M9.2 10.8 10.8 14.2M14.8 10.8 13.2 14.2" />
1013
+ </svg>
1014
+ </button>
1015
+ </Tooltip>
940
1016
  </div>
941
1017
 
942
1018
  <WorkspaceStageSplit
@@ -1005,63 +1081,112 @@ onUnmounted(() => window.removeEventListener("keydown", onKeyDown));
1005
1081
  </PanelCanvas>
1006
1082
  </WorkspaceStageSplit>
1007
1083
 
1008
- <div class="border-t bg-white px-2 py-2">
1009
- <div class="mb-2 flex flex-wrap items-center gap-2">
1010
- <span class="text-xs font-semibold text-gray-500">{{ t("panel.layers.title") }}</span>
1011
- <Button size="small" @click="addLayer()">{{ t("panel.layers.addShort") }}</Button>
1012
- <Button size="small" :disabled="!canMergeLayers" @click="isMergingLayers = true">{{ t("panel.layers.mergeShort") }}</Button>
1084
+ <div class="border-t border-border/50 bg-muted/5 px-1.5 py-0.5">
1085
+ <div class="flex min-w-0 flex-wrap items-center gap-1">
1086
+ <Tooltip :title="t('panel.layers.title')">
1087
+ <span class="inline-flex h-6 items-center px-1 text-[11px] font-semibold text-muted-foreground">
1088
+ {{ t("panel.layers.title") }}
1089
+ </span>
1090
+ </Tooltip>
1091
+ <Tooltip :title="t('panel.layers.add')">
1092
+ <Button size="small" class="!h-6 !px-2 !text-[11px]" @click="addLayer()">
1093
+ {{ t("panel.layers.addShort") }}
1094
+ </Button>
1095
+ </Tooltip>
1096
+ <Tooltip :title="canMergeLayers ? t('panel.layers.merge') : t('panel.layers.mergeNeedTwo')">
1097
+ <Button
1098
+ size="small"
1099
+ class="!h-6 !px-2 !text-[11px]"
1100
+ :disabled="!canMergeLayers"
1101
+ @click="isMergingLayers = true"
1102
+ >
1103
+ {{ t("panel.layers.mergeShort") }}
1104
+ </Button>
1105
+ </Tooltip>
1106
+ <Tabs
1107
+ size="small"
1108
+ class="min-w-0 flex-1"
1109
+ :active-key="activeLayerId"
1110
+ type="card"
1111
+ @update:active-key="(k: string | number) => setActiveLayer(String(k))"
1112
+ >
1113
+ <Tabs.TabPane v-for="layer in layers" :key="layer.id">
1114
+ <template #tab>
1115
+ <span class="inline-flex max-w-[110px] items-center gap-1 text-[11px] font-medium">
1116
+ <span class="truncate">{{ layer.name }}</span>
1117
+ <Tag v-if="layer.isPrimary" color="green" class="!m-0 !px-1 !text-[10px]">
1118
+ {{ t("panel.layers.primary") }}
1119
+ </Tag>
1120
+ </span>
1121
+ </template>
1122
+ </Tabs.TabPane>
1123
+ </Tabs>
1013
1124
  </div>
1014
- <Tabs
1015
- size="small"
1016
- :active-key="activeLayerId"
1017
- type="card"
1018
- @update:active-key="(k: string | number) => setActiveLayer(String(k))"
1125
+
1126
+ <div
1127
+ v-if="activeLayer"
1128
+ class="mt-1 flex flex-wrap items-center gap-1 rounded-md border border-border/50 bg-card/80 px-1.5 py-0.5 text-[11px]"
1019
1129
  >
1020
- <Tabs.TabPane v-for="layer in layers" :key="layer.id">
1021
- <template #tab>
1022
- <span class="inline-flex items-center gap-1">
1023
- {{ layer.name }}
1024
- <Tag v-if="layer.isMapping" color="blue" class="!m-0">{{ t("panel.layers.mapping") }}</Tag>
1025
- <Tag v-if="layer.isPrimary" color="green" class="!m-0">{{ t("panel.layers.primary") }}</Tag>
1026
- </span>
1027
- </template>
1028
- </Tabs.TabPane>
1029
- </Tabs>
1030
-
1031
- <div v-if="activeLayer" class="mt-2 flex flex-wrap items-center gap-2 rounded border px-2 py-1.5 text-xs">
1032
1130
  <template v-if="editingLayerId === activeLayer.id">
1033
1131
  <Input
1034
1132
  v-model:value="editingLayerName"
1035
1133
  size="small"
1036
- class="w-40"
1134
+ class="!h-6 w-28"
1037
1135
  @press-enter="commitRenameLayer"
1038
1136
  />
1039
- <Button size="small" type="primary" @click="commitRenameLayer">{{ t("common.ok") }}</Button>
1137
+ <Tooltip :title="t('common.ok')">
1138
+ <Button size="small" type="primary" class="!h-6 !px-2" @click="commitRenameLayer">
1139
+ {{ t("common.ok") }}
1140
+ </Button>
1141
+ </Tooltip>
1040
1142
  </template>
1041
1143
  <template v-else>
1042
- <span class="font-medium">{{ activeLayer.name }}</span>
1043
- <Button size="small" type="link" @click="startRenameLayer(activeLayer.id, activeLayer.name)">{{ t("panel.layers.renameShort") }}</Button>
1144
+ <span class="max-w-[100px] truncate font-semibold">{{ activeLayer.name }}</span>
1145
+ <Tooltip :title="t('panel.layers.rename')">
1146
+ <Button
1147
+ size="small"
1148
+ type="link"
1149
+ class="!h-6 !px-1 !text-[11px]"
1150
+ @click="startRenameLayer(activeLayer.id, activeLayer.name)"
1151
+ >
1152
+ {{ t("panel.layers.renameShort") }}
1153
+ </Button>
1154
+ </Tooltip>
1044
1155
  </template>
1045
- <Button size="small" @click="toggleLayerLock(activeLayer.id)">
1046
- {{ activeLayer.locked ? t("panel.layers.unlockShort") : t("panel.layers.lockShort") }}
1047
- </Button>
1048
- <Checkbox
1049
- :checked="Boolean(activeLayer.mergeSelected)"
1050
- @change="() => toggleLayerMergeSelected(activeLayer!.id)"
1051
- >
1052
- {{ t("panel.layers.joinMerge") }}
1053
- </Checkbox>
1054
- <Button size="small" :disabled="activeLayer.isPrimary" @click="setPrimaryLayer(activeLayer.id)">
1055
- {{ t("panel.layers.setPrimary") }}
1056
- </Button>
1057
- <Button
1058
- size="small"
1059
- danger
1060
- :disabled="!activeLayer.editable"
1061
- @click="deleteLayer(activeLayer.id)"
1062
- >
1063
- {{ t("panel.layers.delete") }}
1064
- </Button>
1156
+ <Tooltip :title="activeLayer.locked ? t('panel.layers.unlock') : t('panel.layers.lock')">
1157
+ <Button size="small" class="!h-6 !px-2 !text-[11px]" @click="toggleLayerLock(activeLayer.id)">
1158
+ {{ activeLayer.locked ? t("panel.layers.unlockShort") : t("panel.layers.lockShort") }}
1159
+ </Button>
1160
+ </Tooltip>
1161
+ <Tooltip :title="t('panel.layers.joinMerge')">
1162
+ <Checkbox
1163
+ :checked="Boolean(activeLayer.mergeSelected)"
1164
+ @change="() => toggleLayerMergeSelected(activeLayer!.id)"
1165
+ >
1166
+ <span class="text-[11px]">{{ t("panel.layers.joinMerge") }}</span>
1167
+ </Checkbox>
1168
+ </Tooltip>
1169
+ <Tooltip :title="t('panel.layers.setPrimaryHint')">
1170
+ <Button
1171
+ size="small"
1172
+ class="!h-6 !px-2 !text-[11px]"
1173
+ :disabled="activeLayer.isPrimary"
1174
+ @click="setPrimaryLayer(activeLayer.id)"
1175
+ >
1176
+ {{ t("panel.layers.setPrimary") }}
1177
+ </Button>
1178
+ </Tooltip>
1179
+ <Tooltip :title="t('panel.layers.delete')">
1180
+ <Button
1181
+ size="small"
1182
+ danger
1183
+ class="!h-6 !px-2 !text-[11px]"
1184
+ :disabled="!activeLayer.editable"
1185
+ @click="deleteLayer(activeLayer.id)"
1186
+ >
1187
+ {{ t("panel.layers.delete") }}
1188
+ </Button>
1189
+ </Tooltip>
1065
1190
  </div>
1066
1191
  </div>
1067
1192
  </div>
@@ -189,7 +189,7 @@ function hasViewEvent(el: PanelElement) {
189
189
  :class="[
190
190
  'absolute select-none',
191
191
  previewMode ? '' : 'rv-selectable',
192
- !previewMode && selectedIds.includes(el.id) ? 'ring-2 ring-blue-500/90 ring-offset-0' : '',
192
+ !previewMode && selectedIds.includes(el.id) ? 'rv-node-selected' : '',
193
193
  hasViewEvent(el) ? 'cursor-pointer' : '',
194
194
  ]"
195
195
  :data-element-id="el.id"