@abraca/nuxt 1.6.0 → 1.8.0

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 (43) hide show
  1. package/dist/module.d.mts +6 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +16 -2
  4. package/dist/runtime/assets/sources.css +1 -0
  5. package/dist/runtime/components/ADocumentTree.d.vue.ts +11 -1
  6. package/dist/runtime/components/ADocumentTree.vue +13 -6
  7. package/dist/runtime/components/ADocumentTree.vue.d.ts +11 -1
  8. package/dist/runtime/components/renderers/AChecklistRenderer.vue +22 -4
  9. package/dist/runtime/components/renderers/ADashboardRenderer.vue +4 -2
  10. package/dist/runtime/components/renderers/AGalleryRenderer.vue +97 -70
  11. package/dist/runtime/components/renderers/AGraphRenderer.vue +209 -58
  12. package/dist/runtime/components/renderers/AKanbanRenderer.vue +145 -34
  13. package/dist/runtime/components/renderers/AMediaRenderer.vue +27 -17
  14. package/dist/runtime/components/renderers/AOutlineRenderer.vue +38 -23
  15. package/dist/runtime/components/renderers/ASlidesRenderer.d.vue.ts +21 -0
  16. package/dist/runtime/components/renderers/ASlidesRenderer.vue +591 -0
  17. package/dist/runtime/components/renderers/ASlidesRenderer.vue.d.ts +21 -0
  18. package/dist/runtime/components/renderers/ASpatialRenderer.vue +23 -0
  19. package/dist/runtime/components/renderers/ATableRenderer.vue +20 -391
  20. package/dist/runtime/components/renderers/gallery/AGalleryItemCard.d.vue.ts +40 -0
  21. package/dist/runtime/components/renderers/gallery/AGalleryItemCard.vue +227 -0
  22. package/dist/runtime/components/renderers/gallery/AGalleryItemCard.vue.d.ts +40 -0
  23. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.d.vue.ts +16 -0
  24. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue +66 -0
  25. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue.d.ts +16 -0
  26. package/dist/runtime/components/renderers/table/ATableFlatMode.d.vue.ts +2 -0
  27. package/dist/runtime/components/renderers/table/ATableFlatMode.vue +184 -21
  28. package/dist/runtime/components/renderers/table/ATableFlatMode.vue.d.ts +2 -0
  29. package/dist/runtime/components/renderers/table/ATableHierarchyMode.d.vue.ts +26 -0
  30. package/dist/runtime/components/renderers/table/ATableHierarchyMode.vue +662 -0
  31. package/dist/runtime/components/renderers/table/ATableHierarchyMode.vue.d.ts +26 -0
  32. package/dist/runtime/composables/useAwareness.js +14 -3
  33. package/dist/runtime/composables/useBackgroundSync.js +19 -1
  34. package/dist/runtime/composables/useFileIndex.js +38 -17
  35. package/dist/runtime/composables/useSearchIndex.js +41 -16
  36. package/dist/runtime/composables/useSlidesNavigation.d.ts +45 -0
  37. package/dist/runtime/composables/useSlidesNavigation.js +185 -0
  38. package/dist/runtime/composables/useYDoc.d.ts +1 -1
  39. package/dist/runtime/composables/useYDoc.js +47 -9
  40. package/dist/runtime/locale.d.ts +38 -0
  41. package/dist/runtime/locale.js +41 -3
  42. package/dist/runtime/utils/docTypes.js +17 -0
  43. package/package.json +3 -3
package/dist/module.d.mts CHANGED
@@ -40,6 +40,7 @@ declare module '@nuxt/schema' {
40
40
  chat?: boolean;
41
41
  spatial?: boolean;
42
42
  media?: boolean;
43
+ slides?: boolean;
43
44
  };
44
45
  locale: AbracadabraLocale;
45
46
  auth: {
@@ -161,6 +162,11 @@ interface ModuleOptions {
161
162
  * Default: false.
162
163
  */
163
164
  media?: boolean;
165
+ /**
166
+ * Register slides presentation renderer with two-axis navigation.
167
+ * Default: true (lightweight, no extra deps).
168
+ */
169
+ slides?: boolean;
164
170
  };
165
171
  /**
166
172
  * Automatically add Vite resolve.dedupe entries for ProseMirror, TipTap, and Yjs.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "1.6.0",
7
+ "version": "1.8.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -31,7 +31,8 @@ const module$1 = defineNuxtModule({
31
31
  webrtc: false,
32
32
  chat: false,
33
33
  spatial: false,
34
- media: false
34
+ media: false,
35
+ slides: true
35
36
  },
36
37
  addViteDedupe: true,
37
38
  prefix: "",
@@ -241,7 +242,20 @@ const module$1 = defineNuxtModule({
241
242
  addComponentsDir({
242
243
  path: resolver.resolve("./runtime/components/renderers"),
243
244
  prefix: options.prefix ?? "",
244
- pathPrefix: false
245
+ pathPrefix: false,
246
+ // Subfolders contain internal sub-components that should NOT be auto-imported —
247
+ // they're imported explicitly by their parent renderer via relative paths.
248
+ ignore: [
249
+ "**/calendar/**",
250
+ "**/table/**",
251
+ "**/timeline/**",
252
+ "**/media/**",
253
+ "**/spatial/**",
254
+ "**/gallery/**",
255
+ "**/sheets/**",
256
+ "**/slides/**",
257
+ "**/chart/**"
258
+ ]
245
259
  });
246
260
  }
247
261
  addComponentsDir({
@@ -0,0 +1 @@
1
+ @source "../components/**/*.{vue,js,ts,mjs}";@source "../extensions/**/*.{vue,js,ts,mjs}";
@@ -3,7 +3,11 @@ type __VLS_Props = {
3
3
  editable?: boolean;
4
4
  selectedId?: string | null;
5
5
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
6
+ declare var __VLS_39: {};
7
+ type __VLS_Slots = {} & {
8
+ 'toolbar-start'?: (props: typeof __VLS_39) => any;
9
+ };
10
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
7
11
  handleExternalDrop: (e: DragEvent, parentId?: string | null) => Promise<void>;
8
12
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
13
  create: (parentId: string | null) => any;
@@ -14,5 +18,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
14
18
  }>, {
15
19
  editable: boolean;
16
20
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
17
22
  declare const _default: typeof __VLS_export;
18
23
  export default _default;
24
+ type __VLS_WithSlots<T, S> = T & {
25
+ new (): {
26
+ $slots: S;
27
+ };
28
+ };
@@ -1031,10 +1031,9 @@ defineExpose({
1031
1031
  </div>
1032
1032
  </div>
1033
1033
 
1034
- <UContextMenu
1034
+ <div
1035
1035
  v-else-if="!collapsed"
1036
- :items="treeAreaMenuItems()"
1037
- class="relative transition-colors duration-150 outline-none"
1036
+ class="flex flex-col min-h-0"
1038
1037
  :class="externalDragActive ? 'ring-2 ring-inset ring-(--ui-primary)/30 rounded-(--ui-radius) bg-(--ui-primary)/3' : ''"
1039
1038
  tabindex="0"
1040
1039
  role="tree"
@@ -1045,9 +1044,14 @@ defineExpose({
1045
1044
  @dragover="onTreeDragOver"
1046
1045
  @drop.prevent="onOuterDrop"
1047
1046
  >
1047
+ <UContextMenu :items="treeAreaMenuItems()">
1048
+ <div class="flex flex-col flex-1 min-h-0 overflow-hidden relative transition-colors duration-150 outline-none">
1048
1049
  <!-- Header -->
1049
- <div class="flex items-center justify-between px-3 py-1">
1050
- <span class="text-[11px] font-medium text-(--ui-text-dimmed) uppercase tracking-wider">Pages</span>
1050
+ <div class="flex items-center justify-between px-3 py-1 gap-1">
1051
+ <div class="flex items-center gap-1 min-w-0">
1052
+ <slot name="toolbar-start" />
1053
+ <span class="text-[11px] font-medium text-(--ui-text-dimmed) uppercase tracking-wider truncate">Pages</span>
1054
+ </div>
1051
1055
  <div class="flex items-center gap-0">
1052
1056
  <UTooltip :text="allExpanded ? 'Collapse all' : 'Expand all'">
1053
1057
  <UButton
@@ -1466,6 +1470,9 @@ defineExpose({
1466
1470
  </template>
1467
1471
  </ClientOnly>
1468
1472
 
1473
+ </div>
1474
+ </UContextMenu>
1475
+
1469
1476
  <!-- Overlay slideover -->
1470
1477
  <ANodePanel
1471
1478
  :node-id="overlayNodeId"
@@ -1474,7 +1481,7 @@ defineExpose({
1474
1481
  :doc-type="treeMap.get(overlayNodeId ?? '')?.type"
1475
1482
  @close="closeOverlay"
1476
1483
  />
1477
- </UContextMenu>
1484
+ </div>
1478
1485
  </template>
1479
1486
 
1480
1487
  <style scoped>
@@ -3,7 +3,11 @@ type __VLS_Props = {
3
3
  editable?: boolean;
4
4
  selectedId?: string | null;
5
5
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
6
+ declare var __VLS_39: {};
7
+ type __VLS_Slots = {} & {
8
+ 'toolbar-start'?: (props: typeof __VLS_39) => any;
9
+ };
10
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
7
11
  handleExternalDrop: (e: DragEvent, parentId?: string | null) => Promise<void>;
8
12
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
13
  create: (parentId: string | null) => any;
@@ -14,5 +18,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
14
18
  }>, {
15
19
  editable: boolean;
16
20
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
17
22
  declare const _default: typeof __VLS_export;
18
23
  export default _default;
24
+ type __VLS_WithSlots<T, S> = T & {
25
+ new (): {
26
+ $slots: S;
27
+ };
28
+ };
@@ -49,8 +49,14 @@ const flatItems = computed(() => {
49
49
  walk(null, 0);
50
50
  return result;
51
51
  });
52
- const filterMode = ref("all");
53
- const sortMode = ref("manual");
52
+ const filterMode = computed({
53
+ get: () => tree.treeMap.data?.[props.docId]?.meta?.checklistFilter ?? "all",
54
+ set: (v) => tree.updateMeta(props.docId, { checklistFilter: v })
55
+ });
56
+ const sortMode = computed({
57
+ get: () => tree.treeMap.data?.[props.docId]?.meta?.checklistSort ?? "manual",
58
+ set: (v) => tree.updateMeta(props.docId, { checklistSort: v })
59
+ });
54
60
  function isDescendantOf(item, ancestorId) {
55
61
  let current = item;
56
62
  while (current) {
@@ -88,6 +94,13 @@ const visibleItems = computed(() => {
88
94
  });
89
95
  const totalCount = computed(() => flatItems.value.length);
90
96
  const checkedCount = computed(() => flatItems.value.filter((i) => i.checked).length);
97
+ function isIndeterminate(item) {
98
+ if (!item.hasChildren) return false;
99
+ if (item.checked) return false;
100
+ const descendants = flatItems.value.filter((i) => isDescendantOf(i, item.id));
101
+ if (descendants.length === 0) return false;
102
+ return descendants.some((d) => d.checked);
103
+ }
91
104
  const percent = computed(
92
105
  () => totalCount.value === 0 ? 0 : Math.round(checkedCount.value / totalCount.value * 100)
93
106
  );
@@ -465,10 +478,10 @@ defineExpose({ connectedUsers });
465
478
  />
466
479
  </span>
467
480
 
468
- <!-- Checkbox -->
481
+ <!-- Checkbox (supports indeterminate state for parents with partial completion) -->
469
482
  <button
470
483
  class="shrink-0 size-4 rounded border flex items-center justify-center transition-colors"
471
- :class="item.checked ? 'bg-(--ui-primary) border-(--ui-primary)' : 'border-(--ui-border) hover:border-(--ui-primary)'"
484
+ :class="item.checked ? 'bg-(--ui-primary) border-(--ui-primary)' : isIndeterminate(item) ? 'bg-(--ui-primary)/40 border-(--ui-primary)/60' : 'border-(--ui-border) hover:border-(--ui-primary)'"
472
485
  :disabled="!editable"
473
486
  @click.stop="editable && tree.updateMeta(item.id, { checked: !item.checked })"
474
487
  >
@@ -477,6 +490,11 @@ defineExpose({ connectedUsers });
477
490
  name="i-lucide-check"
478
491
  class="size-2.5 text-white"
479
492
  />
493
+ <UIcon
494
+ v-else-if="isIndeterminate(item)"
495
+ name="i-lucide-minus"
496
+ class="size-2.5 text-white"
497
+ />
480
498
  </button>
481
499
 
482
500
  <!-- Label / inline edit -->
@@ -88,6 +88,7 @@ const widgetLoadingIds = ref(/* @__PURE__ */ new Set());
88
88
  const widgetErrors = reactive({});
89
89
  async function loadWidgetProvider(childId) {
90
90
  if (widgetProviders[childId] || widgetLoadingIds.value.has(childId)) return;
91
+ if (!childProviderRef.value) return;
91
92
  widgetLoadingIds.value.add(childId);
92
93
  delete widgetErrors[childId];
93
94
  try {
@@ -118,8 +119,9 @@ onErrorCaptured((err) => {
118
119
  }
119
120
  });
120
121
  watch(
121
- children,
122
- (items) => {
122
+ [children, childProviderRef],
123
+ ([items]) => {
124
+ if (!childProviderRef.value) return;
123
125
  items.forEach((item) => {
124
126
  const mode = item.meta?.deskMode;
125
127
  if ((mode === "widget-sm" || mode === "widget-lg") && !widgetProviders[item.id]) {
@@ -1,11 +1,13 @@
1
1
  <script setup>
2
- import { ref, computed, onBeforeUnmount } from "vue";
2
+ import { ref, computed, onBeforeUnmount, watch } from "vue";
3
+ import { useEventListener } from "@vueuse/core";
3
4
  import { useRuntimeConfig } from "#imports";
4
5
  import { useRendererBase } from "../../composables/useRendererBase";
5
6
  import { useTouchDrag } from "../../composables/useTouchDrag";
6
7
  import { useNodePanel } from "../../composables/useNodePanel";
7
8
  import { getMetaColor } from "../../utils/getMetaColor";
8
9
  import { DEFAULT_LOCALE } from "../../locale";
10
+ import AGalleryItemCard from "./gallery/AGalleryItemCard.vue";
9
11
  const props = defineProps({
10
12
  docId: { type: String, required: true },
11
13
  childProvider: { type: null, required: true },
@@ -29,7 +31,27 @@ const {
29
31
  closePanel
30
32
  } = useNodePanel(childProviderRef);
31
33
  const myClientId = computed(() => props.childProvider?.awareness?.clientID ?? 0);
32
- const items = computed(() => tree.childrenOf(null));
34
+ const rawItems = computed(() => tree.childrenOf(null));
35
+ const items = computed(() => {
36
+ const raw = rawItems.value;
37
+ const mode = gallerySortBy.value;
38
+ if (mode === "manual") return raw;
39
+ const arr = [...raw];
40
+ if (mode === "name") {
41
+ return arr.sort((a, b) => (a.label ?? "").localeCompare(b.label ?? ""));
42
+ }
43
+ if (mode === "rating") {
44
+ return arr.sort((a, b) => (b.meta?.rating ?? 0) - (a.meta?.rating ?? 0));
45
+ }
46
+ return arr.sort((a, b) => {
47
+ const da = a.meta?.datetimeStart ?? a.meta?.dateStart ?? a.meta?.dateTaken;
48
+ const db = b.meta?.datetimeStart ?? b.meta?.dateStart ?? b.meta?.dateTaken;
49
+ if (!da && !db) return 0;
50
+ if (!da) return 1;
51
+ if (!db) return -1;
52
+ return new Date(da).getTime() - new Date(db).getTime();
53
+ });
54
+ });
33
55
  const renameId = ref(null);
34
56
  const renameValue = ref("");
35
57
  let renameCooldown = false;
@@ -109,6 +131,30 @@ const gridClass = computed(() => {
109
131
  return "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4";
110
132
  }
111
133
  });
134
+ const docMeta = computed(() => tree.treeMap.data?.[props.docId]?.meta);
135
+ const ASPECT_MAP = {
136
+ square: "1 / 1",
137
+ "4:3": "4 / 3",
138
+ "3:2": "3 / 2",
139
+ "16:9": "16 / 9",
140
+ free: "auto"
141
+ };
142
+ const galleryAspect = computed(() => {
143
+ const v = docMeta.value?.galleryAspect ?? "4:3";
144
+ return ASPECT_MAP[v] ?? ASPECT_MAP["4:3"];
145
+ });
146
+ const cardStyle = computed(() => docMeta.value?.galleryCardStyle ?? "default");
147
+ const showLabels = computed(() => docMeta.value?.galleryShowLabels ?? true);
148
+ const galleryColumns = computed(() => {
149
+ const v = docMeta.value?.galleryColumns;
150
+ return typeof v === "number" && v >= 1 && v <= 6 ? v : null;
151
+ });
152
+ const gridStyle = computed(
153
+ () => galleryColumns.value ? { gridTemplateColumns: `repeat(${galleryColumns.value}, minmax(0, 1fr))` } : void 0
154
+ );
155
+ const gallerySortBy = computed(
156
+ () => docMeta.value?.gallerySortBy ?? "manual"
157
+ );
112
158
  const gridSizeItems = computed(() => [
113
159
  [
114
160
  { label: "Small", icon: "i-lucide-grid-3x3", onSelect: () => {
@@ -139,6 +185,35 @@ function openLightbox(item) {
139
185
  function closeLightbox() {
140
186
  lightboxItem.value = null;
141
187
  }
188
+ function lightboxNavigate(direction) {
189
+ if (!lightboxItem.value) return;
190
+ const list = items.value.filter((i) => i.meta?.coverUploadId);
191
+ const idx = list.findIndex((i) => i.id === lightboxItem.value.id);
192
+ if (idx === -1) return;
193
+ const next = list[(idx + direction + list.length) % list.length];
194
+ lightboxItem.value = {
195
+ id: next.id,
196
+ label: next.label,
197
+ uploadId: next.meta.coverUploadId,
198
+ mimeType: next.meta.coverMimeType
199
+ };
200
+ }
201
+ watch(lightboxItem, (v) => {
202
+ if (v) return;
203
+ });
204
+ useEventListener(typeof window !== "undefined" ? window : null, "keydown", (e) => {
205
+ if (!lightboxItem.value) return;
206
+ if (e.key === "ArrowLeft") {
207
+ e.preventDefault();
208
+ lightboxNavigate(-1);
209
+ } else if (e.key === "ArrowRight") {
210
+ e.preventDefault();
211
+ lightboxNavigate(1);
212
+ } else if (e.key === "Escape") {
213
+ e.preventDefault();
214
+ closeLightbox();
215
+ }
216
+ });
142
217
  function itemBorderColor(item) {
143
218
  const c = getMetaColor(item.meta);
144
219
  return c ? `border-color: ${c}` : void 0;
@@ -205,84 +280,36 @@ defineExpose({ connectedUsers });
205
280
  name="gallery"
206
281
  tag="div"
207
282
  class="grid gap-3"
208
- :class="gridClass"
283
+ :class="galleryColumns ? '' : gridClass"
284
+ :style="gridStyle"
209
285
  >
210
286
  <UContextMenu
211
287
  v-for="item in items"
212
288
  :key="item.id"
213
289
  :items="editable ? itemMenuItems(item) : []"
214
290
  >
215
- <div
216
- class="group relative rounded-lg border border-(--ui-border) hover:border-(--ui-primary) transition-colors overflow-hidden cursor-pointer"
217
- :class="{
218
- 'opacity-40 scale-95': dragId === item.id,
219
- 'ring-2 ring-(--ui-primary)/40': dragOverId === item.id
220
- }"
221
- :style="[
222
- itemFocusers(item.id).length ? `box-shadow: 0 0 0 2px ${itemFocusers(item.id)[0].user?.color ?? '#888'}` : void 0,
223
- itemBorderColor(item)
224
- ]"
291
+ <AGalleryItemCard
292
+ :item="item"
293
+ :gallery-aspect="galleryAspect"
294
+ :card-style="cardStyle"
295
+ :show-labels="showLabels"
296
+ :is-dragging="dragId === item.id"
297
+ :is-drag-over="dragOverId === item.id"
298
+ :focusers="itemFocusers(item.id)"
299
+ :can-write="editable"
300
+ :rename-id="renameId"
301
+ :rename-value="renameValue"
225
302
  :data-drag-id="item.id"
303
+ @update:rename-value="renameValue = $event"
304
+ @commit-rename="commitRename"
305
+ @cancel-rename="renameId = null"
226
306
  @pointerdown="editable && handlePointerDown($event, item.id)"
227
307
  @pointerenter="onItemPointerEnter(item.id)"
228
308
  @pointerleave="onItemPointerLeave"
229
309
  @click="openLightbox(item)"
310
+ @dblclick.stop="editable && startRename(item.id, item.label)"
230
311
  >
231
- <!-- Remote user badge -->
232
- <div
233
- v-if="itemFocusers(item.id).length"
234
- class="absolute top-1 right-1 z-10 flex gap-0.5"
235
- >
236
- <span
237
- v-for="f in itemFocusers(item.id)"
238
- :key="f.clientId"
239
- class="text-[10px] leading-none px-1.5 py-0.5 rounded-full text-white truncate max-w-20"
240
- :style="{ backgroundColor: f.user?.color ?? '#888' }"
241
- >
242
- {{ f.user?.name ?? "User" }}
243
- </span>
244
- </div>
245
-
246
- <!-- Cover or placeholder -->
247
- <div
248
- class="aspect-[4/3] bg-(--ui-bg-elevated) flex items-center justify-center overflow-hidden"
249
- :style="item.meta?.color ? { backgroundColor: item.meta.color + '22' } : {}"
250
- >
251
- <AGalleryCoverImage
252
- v-if="item.meta?.coverUploadId"
253
- :upload-id="item.meta.coverUploadId"
254
- :doc-id="item.id"
255
- :mime-type="item.meta.coverMimeType"
256
- />
257
- <UIcon
258
- v-else
259
- :name="item.meta?.icon || 'i-lucide-file-text'"
260
- class="size-8 text-(--ui-text-dimmed) opacity-40"
261
- />
262
- </div>
263
-
264
- <!-- Label -->
265
- <div class="p-2 border-t border-(--ui-border) flex items-center justify-between gap-1">
266
- <UInput
267
- v-if="editable && renameId === item.id"
268
- v-model="renameValue"
269
- size="xs"
270
- variant="none"
271
- class="flex-1 -mx-1"
272
- autofocus
273
- @keydown.enter="commitRename"
274
- @keydown.escape="renameId = null"
275
- @blur="commitRename"
276
- @click.stop
277
- />
278
- <p
279
- v-else
280
- class="text-xs font-medium truncate"
281
- @dblclick.stop="editable && startRename(item.id, item.label)"
282
- >
283
- {{ item.label }}
284
- </p>
285
-
312
+ <template #actions>
286
313
  <UDropdownMenu
287
314
  v-if="editable"
288
315
  :items="itemMenuItems(item)"
@@ -297,8 +324,8 @@ defineExpose({ connectedUsers });
297
324
  @click.stop
298
325
  />
299
326
  </UDropdownMenu>
300
- </div>
301
- </div>
327
+ </template>
328
+ </AGalleryItemCard>
302
329
  </UContextMenu>
303
330
  </TransitionGroup>
304
331
  </div>