@abraca/nuxt 1.6.0 → 1.8.2
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/dist/module.d.mts +6 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +16 -2
- package/dist/runtime/assets/sources.css +1 -0
- package/dist/runtime/components/ADocumentTree.d.vue.ts +11 -1
- package/dist/runtime/components/ADocumentTree.vue +13 -6
- package/dist/runtime/components/ADocumentTree.vue.d.ts +11 -1
- package/dist/runtime/components/renderers/AChecklistRenderer.vue +22 -4
- package/dist/runtime/components/renderers/ADashboardRenderer.vue +4 -2
- package/dist/runtime/components/renderers/AGalleryRenderer.vue +97 -70
- package/dist/runtime/components/renderers/AGraphRenderer.vue +209 -58
- package/dist/runtime/components/renderers/AKanbanRenderer.vue +145 -34
- package/dist/runtime/components/renderers/AMediaRenderer.vue +27 -17
- package/dist/runtime/components/renderers/AOutlineRenderer.vue +38 -23
- package/dist/runtime/components/renderers/ASlidesRenderer.d.vue.ts +21 -0
- package/dist/runtime/components/renderers/ASlidesRenderer.vue +591 -0
- package/dist/runtime/components/renderers/ASlidesRenderer.vue.d.ts +21 -0
- package/dist/runtime/components/renderers/ASpatialRenderer.vue +23 -0
- package/dist/runtime/components/renderers/ATableRenderer.vue +20 -391
- package/dist/runtime/components/renderers/gallery/AGalleryItemCard.d.vue.ts +40 -0
- package/dist/runtime/components/renderers/gallery/AGalleryItemCard.vue +227 -0
- package/dist/runtime/components/renderers/gallery/AGalleryItemCard.vue.d.ts +40 -0
- package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.d.vue.ts +16 -0
- package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue +66 -0
- package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue.d.ts +16 -0
- package/dist/runtime/components/renderers/table/ATableFlatMode.d.vue.ts +2 -0
- package/dist/runtime/components/renderers/table/ATableFlatMode.vue +184 -21
- package/dist/runtime/components/renderers/table/ATableFlatMode.vue.d.ts +2 -0
- package/dist/runtime/components/renderers/table/ATableHierarchyMode.d.vue.ts +26 -0
- package/dist/runtime/components/renderers/table/ATableHierarchyMode.vue +662 -0
- package/dist/runtime/components/renderers/table/ATableHierarchyMode.vue.d.ts +26 -0
- package/dist/runtime/composables/useAwareness.js +14 -3
- package/dist/runtime/composables/useBackgroundSync.js +19 -1
- package/dist/runtime/composables/useFileIndex.js +38 -17
- package/dist/runtime/composables/useSearchIndex.js +41 -16
- package/dist/runtime/composables/useSlidesNavigation.d.ts +45 -0
- package/dist/runtime/composables/useSlidesNavigation.js +185 -0
- package/dist/runtime/composables/useYDoc.d.ts +1 -1
- package/dist/runtime/composables/useYDoc.js +47 -9
- package/dist/runtime/locale.d.ts +38 -0
- package/dist/runtime/locale.js +41 -3
- package/dist/runtime/utils/docTypes.js +17 -0
- package/package.json +9 -9
|
@@ -229,10 +229,86 @@ const labelScale = computed(() => {
|
|
|
229
229
|
return vb.value.w / w;
|
|
230
230
|
});
|
|
231
231
|
const labelOpacity = computed(() => {
|
|
232
|
+
if (!showLabels.value) return 0;
|
|
232
233
|
const pxPerUnit = (svgRef.value?.clientWidth ?? 1e3) / vb.value.w;
|
|
233
234
|
const apparentR = 16 * pxPerUnit;
|
|
234
235
|
return Math.max(0, Math.min(1, (apparentR - 8) / 8));
|
|
235
236
|
});
|
|
237
|
+
const SPACING_PRESETS = {
|
|
238
|
+
compact: { charge: -70, linkDist: 50, centerStrength: 0.1, collideExtra: 2 },
|
|
239
|
+
default: { charge: -350, linkDist: 180, centerStrength: 0.03, collideExtra: 15 },
|
|
240
|
+
spacious: { charge: -700, linkDist: 320, centerStrength: 0.01, collideExtra: 30 }
|
|
241
|
+
};
|
|
242
|
+
const SPACING_CYCLE = ["compact", "default", "spacious"];
|
|
243
|
+
const EDGE_THICKNESS_SCALE = { thin: 0.6, normal: 1, thick: 1.8 };
|
|
244
|
+
const savedDocMeta = computed(() => tree.treeMap.data?.[props.docId]?.meta);
|
|
245
|
+
const spacingLevel = ref(savedDocMeta.value?.graphSpacing ?? "default");
|
|
246
|
+
watch(() => savedDocMeta.value?.graphSpacing, (v) => {
|
|
247
|
+
if (v && v !== spacingLevel.value) spacingLevel.value = v;
|
|
248
|
+
});
|
|
249
|
+
const showLabels = ref(savedDocMeta.value?.graphShowLabels ?? true);
|
|
250
|
+
watch(() => savedDocMeta.value?.graphShowLabels, (v) => {
|
|
251
|
+
if (typeof v === "boolean" && v !== showLabels.value) showLabels.value = v;
|
|
252
|
+
});
|
|
253
|
+
const showRefEdges = ref(savedDocMeta.value?.showRefEdges ?? true);
|
|
254
|
+
watch(() => savedDocMeta.value?.showRefEdges, (v) => {
|
|
255
|
+
if (typeof v === "boolean" && v !== showRefEdges.value) showRefEdges.value = v;
|
|
256
|
+
});
|
|
257
|
+
const edgeThickness = ref(savedDocMeta.value?.graphEdgeThickness ?? "normal");
|
|
258
|
+
watch(() => savedDocMeta.value?.graphEdgeThickness, (v) => {
|
|
259
|
+
if (v && v !== edgeThickness.value) edgeThickness.value = v;
|
|
260
|
+
});
|
|
261
|
+
const edgeScale = computed(() => EDGE_THICKNESS_SCALE[edgeThickness.value]);
|
|
262
|
+
const spacingIcon = computed(() => {
|
|
263
|
+
const icons = {
|
|
264
|
+
compact: "i-lucide-dot",
|
|
265
|
+
default: "i-lucide-circle-dot",
|
|
266
|
+
spacious: "i-lucide-target"
|
|
267
|
+
};
|
|
268
|
+
return icons[spacingLevel.value];
|
|
269
|
+
});
|
|
270
|
+
function applySpacingForces() {
|
|
271
|
+
if (!sim) return;
|
|
272
|
+
const p = SPACING_PRESETS[spacingLevel.value];
|
|
273
|
+
try {
|
|
274
|
+
sim.force("charge")?.strength(p.charge);
|
|
275
|
+
const linkForce = sim.force("link");
|
|
276
|
+
if (linkForce) linkForce.distance(p.linkDist).strength(0.5);
|
|
277
|
+
sim.force("x")?.strength(p.centerStrength);
|
|
278
|
+
sim.force("y")?.strength(p.centerStrength);
|
|
279
|
+
sim.force("collide")?.radius((n) => nodeRadius(n) + p.collideExtra);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function setSpacing(level) {
|
|
284
|
+
spacingLevel.value = level;
|
|
285
|
+
tree.updateMeta(props.docId, { graphSpacing: level });
|
|
286
|
+
applySpacingForces();
|
|
287
|
+
if (sim) {
|
|
288
|
+
sim.alpha(0.5);
|
|
289
|
+
resumeSim();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function cycleSpacing() {
|
|
293
|
+
if (!props.editable) return;
|
|
294
|
+
const idx = SPACING_CYCLE.indexOf(spacingLevel.value);
|
|
295
|
+
setSpacing(SPACING_CYCLE[(idx + 1) % SPACING_CYCLE.length]);
|
|
296
|
+
}
|
|
297
|
+
function setShowLabels(v) {
|
|
298
|
+
showLabels.value = v;
|
|
299
|
+
tree.updateMeta(props.docId, { graphShowLabels: v });
|
|
300
|
+
}
|
|
301
|
+
function setShowRefEdges(v) {
|
|
302
|
+
showRefEdges.value = v;
|
|
303
|
+
tree.updateMeta(props.docId, { showRefEdges: v });
|
|
304
|
+
}
|
|
305
|
+
function setEdgeThickness(v) {
|
|
306
|
+
edgeThickness.value = v;
|
|
307
|
+
tree.updateMeta(props.docId, { graphEdgeThickness: v });
|
|
308
|
+
}
|
|
309
|
+
const viewSettingsActive = computed(
|
|
310
|
+
() => !showLabels.value || !showRefEdges.value || edgeThickness.value !== "normal"
|
|
311
|
+
);
|
|
236
312
|
const simNodes = ref([]);
|
|
237
313
|
const renderTick = ref(0);
|
|
238
314
|
let sim = null;
|
|
@@ -278,9 +354,9 @@ const { pause: pauseSim, resume: resumeSim } = useRafFn(() => {
|
|
|
278
354
|
}
|
|
279
355
|
renderTick.value++;
|
|
280
356
|
}, { immediate: false });
|
|
281
|
-
function restartSim() {
|
|
357
|
+
function restartSim(alpha = 0.12) {
|
|
282
358
|
if (!sim) return;
|
|
283
|
-
sim.alpha(Math.max(sim.alpha(),
|
|
359
|
+
sim.alpha(Math.max(sim.alpha(), alpha));
|
|
284
360
|
resumeSim();
|
|
285
361
|
}
|
|
286
362
|
function nodeRadius(n) {
|
|
@@ -343,7 +419,8 @@ function buildSimulation(oldPos = /* @__PURE__ */ new Map()) {
|
|
|
343
419
|
const edges = allEdges.value.filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target)).map((e) => ({ source: e.source, target: e.target }));
|
|
344
420
|
pauseSim();
|
|
345
421
|
sim?.stop();
|
|
346
|
-
|
|
422
|
+
const p = SPACING_PRESETS[spacingLevel.value];
|
|
423
|
+
sim = forceSimulation(nodes).force("charge", forceManyBody().strength(p.charge)).force("x", forceX(0).strength(p.centerStrength)).force("y", forceY(0).strength(p.centerStrength)).force("link", forceLink(edges).id((dd) => dd.id).distance(p.linkDist).strength(0.5)).force("collide", forceCollide().radius((n) => nodeRadius(n) + p.collideExtra).strength(0.85)).alphaDecay(0.025).velocityDecay(0.55).stop();
|
|
347
424
|
simNodes.value = nodes;
|
|
348
425
|
const hasUnpositioned = nodes.some((n) => !getGraphPos(n.id) && !oldPos.has(n.id));
|
|
349
426
|
if (hasUnpositioned) {
|
|
@@ -669,61 +746,131 @@ defineExpose({ connectedUsers });
|
|
|
669
746
|
</div>
|
|
670
747
|
|
|
671
748
|
<template v-else-if="d3">
|
|
672
|
-
<!--
|
|
673
|
-
<div class="
|
|
674
|
-
|
|
749
|
+
<!-- Floating toolbar (bottom-center) -->
|
|
750
|
+
<div class="graph-toolbar-bar">
|
|
751
|
+
<!-- Create -->
|
|
752
|
+
<div
|
|
675
753
|
v-if="editable"
|
|
676
|
-
|
|
677
|
-
size="xs"
|
|
678
|
-
variant="soft"
|
|
679
|
-
color="neutral"
|
|
680
|
-
:label="locale.addNode"
|
|
681
|
-
@click="addNodeAtCenter"
|
|
682
|
-
/>
|
|
683
|
-
<UTooltip
|
|
684
|
-
text="Zoom out"
|
|
685
|
-
:content="{ side: 'bottom' }"
|
|
686
|
-
>
|
|
687
|
-
<UButton
|
|
688
|
-
icon="i-lucide-minus"
|
|
689
|
-
size="xs"
|
|
690
|
-
variant="soft"
|
|
691
|
-
color="neutral"
|
|
692
|
-
@click="zoomOut"
|
|
693
|
-
/>
|
|
694
|
-
</UTooltip>
|
|
695
|
-
<UTooltip
|
|
696
|
-
text="Zoom in"
|
|
697
|
-
:content="{ side: 'bottom' }"
|
|
754
|
+
class="toolbar-group"
|
|
698
755
|
>
|
|
699
|
-
<
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
icon="i-lucide-focus"
|
|
713
|
-
size="xs"
|
|
714
|
-
variant="soft"
|
|
715
|
-
color="neutral"
|
|
716
|
-
@click="resetView"
|
|
717
|
-
/>
|
|
718
|
-
</UTooltip>
|
|
719
|
-
<UButton
|
|
720
|
-
icon="i-lucide-wind"
|
|
721
|
-
size="xs"
|
|
722
|
-
variant="soft"
|
|
723
|
-
color="neutral"
|
|
724
|
-
label="Shake"
|
|
725
|
-
@click="restartSim"
|
|
756
|
+
<UTooltip :text="locale.addNode">
|
|
757
|
+
<UButton
|
|
758
|
+
icon="i-lucide-plus"
|
|
759
|
+
size="sm"
|
|
760
|
+
variant="ghost"
|
|
761
|
+
color="neutral"
|
|
762
|
+
@click="addNodeAtCenter"
|
|
763
|
+
/>
|
|
764
|
+
</UTooltip>
|
|
765
|
+
</div>
|
|
766
|
+
<div
|
|
767
|
+
v-if="editable"
|
|
768
|
+
class="toolbar-divider"
|
|
726
769
|
/>
|
|
770
|
+
|
|
771
|
+
<!-- View -->
|
|
772
|
+
<div class="toolbar-group">
|
|
773
|
+
<UTooltip :text="locale.zoomOut">
|
|
774
|
+
<UButton
|
|
775
|
+
icon="i-lucide-minus"
|
|
776
|
+
size="sm"
|
|
777
|
+
variant="ghost"
|
|
778
|
+
color="neutral"
|
|
779
|
+
@click="zoomOut"
|
|
780
|
+
/>
|
|
781
|
+
</UTooltip>
|
|
782
|
+
<UTooltip :text="locale.zoomIn">
|
|
783
|
+
<UButton
|
|
784
|
+
icon="i-lucide-zoom-in"
|
|
785
|
+
size="sm"
|
|
786
|
+
variant="ghost"
|
|
787
|
+
color="neutral"
|
|
788
|
+
@click="zoomIn"
|
|
789
|
+
/>
|
|
790
|
+
</UTooltip>
|
|
791
|
+
<UTooltip :text="locale.fitView">
|
|
792
|
+
<UButton
|
|
793
|
+
icon="i-lucide-focus"
|
|
794
|
+
size="sm"
|
|
795
|
+
variant="ghost"
|
|
796
|
+
color="neutral"
|
|
797
|
+
@click="resetView"
|
|
798
|
+
/>
|
|
799
|
+
</UTooltip>
|
|
800
|
+
</div>
|
|
801
|
+
<div class="toolbar-divider" />
|
|
802
|
+
|
|
803
|
+
<!-- Simulation -->
|
|
804
|
+
<div class="toolbar-group">
|
|
805
|
+
<UTooltip :text="locale.shake">
|
|
806
|
+
<UButton
|
|
807
|
+
icon="i-lucide-wind"
|
|
808
|
+
size="sm"
|
|
809
|
+
variant="ghost"
|
|
810
|
+
color="neutral"
|
|
811
|
+
@click="() => restartSim(0.15)"
|
|
812
|
+
/>
|
|
813
|
+
</UTooltip>
|
|
814
|
+
<UTooltip :text="`${locale.spacing}: ${spacingLevel}`">
|
|
815
|
+
<UButton
|
|
816
|
+
:icon="spacingIcon"
|
|
817
|
+
size="sm"
|
|
818
|
+
variant="ghost"
|
|
819
|
+
color="neutral"
|
|
820
|
+
:disabled="!editable"
|
|
821
|
+
@click="cycleSpacing"
|
|
822
|
+
/>
|
|
823
|
+
</UTooltip>
|
|
824
|
+
</div>
|
|
825
|
+
<div class="toolbar-divider" />
|
|
826
|
+
|
|
827
|
+
<!-- View settings popover -->
|
|
828
|
+
<UPopover :content="{ side: 'top', align: 'end' }">
|
|
829
|
+
<UTooltip :text="locale.viewSettings">
|
|
830
|
+
<UButton
|
|
831
|
+
icon="i-lucide-sliders-horizontal"
|
|
832
|
+
size="sm"
|
|
833
|
+
:variant="viewSettingsActive ? 'soft' : 'ghost'"
|
|
834
|
+
:color="viewSettingsActive ? 'primary' : 'neutral'"
|
|
835
|
+
/>
|
|
836
|
+
</UTooltip>
|
|
837
|
+
<template #content>
|
|
838
|
+
<div class="flex flex-col gap-2 p-2 w-56">
|
|
839
|
+
<div class="flex items-center justify-between">
|
|
840
|
+
<span class="text-xs text-(--ui-text-muted)">{{ locale.showLabels }}</span>
|
|
841
|
+
<USwitch
|
|
842
|
+
:model-value="showLabels"
|
|
843
|
+
size="xs"
|
|
844
|
+
@update:model-value="setShowLabels($event)"
|
|
845
|
+
/>
|
|
846
|
+
</div>
|
|
847
|
+
<div class="flex items-center justify-between">
|
|
848
|
+
<span class="text-xs text-(--ui-text-muted)">{{ locale.showRefEdges }}</span>
|
|
849
|
+
<USwitch
|
|
850
|
+
:model-value="showRefEdges"
|
|
851
|
+
size="xs"
|
|
852
|
+
@update:model-value="setShowRefEdges($event)"
|
|
853
|
+
/>
|
|
854
|
+
</div>
|
|
855
|
+
<div class="flex items-center justify-between">
|
|
856
|
+
<span class="text-xs text-(--ui-text-muted)">{{ locale.edgeThickness }}</span>
|
|
857
|
+
<div class="flex gap-0.5">
|
|
858
|
+
<UButton
|
|
859
|
+
v-for="th in ['thin', 'normal', 'thick']"
|
|
860
|
+
:key="th"
|
|
861
|
+
size="xs"
|
|
862
|
+
:variant="edgeThickness === th ? 'soft' : 'ghost'"
|
|
863
|
+
:color="edgeThickness === th ? 'primary' : 'neutral'"
|
|
864
|
+
class="capitalize text-[10px] px-1.5"
|
|
865
|
+
@click="setEdgeThickness(th)"
|
|
866
|
+
>
|
|
867
|
+
{{ locale[`edge${th.charAt(0).toUpperCase() + th.slice(1)}`] }}
|
|
868
|
+
</UButton>
|
|
869
|
+
</div>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
</template>
|
|
873
|
+
</UPopover>
|
|
727
874
|
</div>
|
|
728
875
|
|
|
729
876
|
<!-- Empty state -->
|
|
@@ -815,7 +962,7 @@ defineExpose({ connectedUsers });
|
|
|
815
962
|
:x2="link.target.x"
|
|
816
963
|
:y2="link.target.y"
|
|
817
964
|
stroke="var(--ui-border)"
|
|
818
|
-
stroke-width="1.2"
|
|
965
|
+
:stroke-width="1.2 * edgeScale"
|
|
819
966
|
opacity="0.6"
|
|
820
967
|
/>
|
|
821
968
|
</g>
|
|
@@ -980,8 +1127,8 @@ defineExpose({ connectedUsers });
|
|
|
980
1127
|
</svg>
|
|
981
1128
|
|
|
982
1129
|
<!-- Hint -->
|
|
983
|
-
<p class="absolute
|
|
984
|
-
Drag
|
|
1130
|
+
<p class="absolute top-3 right-3 text-[10px] text-(--ui-text-muted) select-none pointer-events-none">
|
|
1131
|
+
Drag · Right-click · Scroll · Click · Dbl-click
|
|
985
1132
|
</p>
|
|
986
1133
|
|
|
987
1134
|
<!-- Context menu -->
|
|
@@ -1028,3 +1175,7 @@ defineExpose({ connectedUsers });
|
|
|
1028
1175
|
/>
|
|
1029
1176
|
</div>
|
|
1030
1177
|
</template>
|
|
1178
|
+
|
|
1179
|
+
<style scoped>
|
|
1180
|
+
.graph-toolbar-bar{align-items:center;backdrop-filter:blur(8px);background:var(--ui-bg);border:1px solid var(--ui-border);border-radius:10px;bottom:2rem;display:flex;gap:3px;left:50%;padding:3px;position:absolute;transform:translateX(-50%);z-index:20}.toolbar-group{align-items:center;display:flex;gap:1px}.toolbar-divider{background:hsla(0,0%,100%,.15);flex-shrink:0;height:20px;margin:0 2px;width:1px}
|
|
1181
|
+
</style>
|
|
@@ -30,6 +30,11 @@ const {
|
|
|
30
30
|
closePanel
|
|
31
31
|
} = useNodePanel(childProviderRef);
|
|
32
32
|
const columns = computed(() => tree.childrenOf(null));
|
|
33
|
+
const columnWidthClass = computed(() => {
|
|
34
|
+
const v = tree.treeMap.data?.[props.docId]?.meta?.kanbanColumnWidth;
|
|
35
|
+
const map = { narrow: "w-52", default: "w-64", wide: "w-80" };
|
|
36
|
+
return map[v ?? ""] ?? "w-64";
|
|
37
|
+
});
|
|
33
38
|
function orderBetween(list, targetIdx) {
|
|
34
39
|
const prev = list[targetIdx - 1];
|
|
35
40
|
const next = list[targetIdx];
|
|
@@ -145,6 +150,48 @@ function remoteHovers(cardId) {
|
|
|
145
150
|
}
|
|
146
151
|
return result;
|
|
147
152
|
}
|
|
153
|
+
function columnPresence(colId) {
|
|
154
|
+
const seen = /* @__PURE__ */ new Set();
|
|
155
|
+
const result = [];
|
|
156
|
+
for (const card of tree.childrenOf(colId)) {
|
|
157
|
+
for (const u of remoteHovers(card.id)) {
|
|
158
|
+
if (!seen.has(u.name)) {
|
|
159
|
+
seen.add(u.name);
|
|
160
|
+
result.push(u);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
const PRIORITY_LABELS = ["", "Low", "Medium", "High", "Urgent"];
|
|
167
|
+
const PRIORITY_COLORS = {
|
|
168
|
+
1: "neutral",
|
|
169
|
+
2: "info",
|
|
170
|
+
3: "warning",
|
|
171
|
+
4: "error"
|
|
172
|
+
};
|
|
173
|
+
function cardPriority(card) {
|
|
174
|
+
const p = card.meta?.priority;
|
|
175
|
+
if (!p || p < 1 || p > 4) return null;
|
|
176
|
+
return { label: PRIORITY_LABELS[p], color: PRIORITY_COLORS[p] };
|
|
177
|
+
}
|
|
178
|
+
function cardDueDate(card) {
|
|
179
|
+
const raw = card.meta?.dateEnd ?? card.meta?.datetimeEnd;
|
|
180
|
+
if (!raw) return null;
|
|
181
|
+
try {
|
|
182
|
+
const d = new Date(raw);
|
|
183
|
+
return d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function cardTags(card) {
|
|
189
|
+
const tags = card.meta?.tags;
|
|
190
|
+
return Array.isArray(tags) ? tags.slice(0, 2) : [];
|
|
191
|
+
}
|
|
192
|
+
function cardHasCover(card) {
|
|
193
|
+
return !!card.meta?.coverUploadId;
|
|
194
|
+
}
|
|
148
195
|
function cardBorderStyle(card) {
|
|
149
196
|
const hovers = remoteHovers(card.id);
|
|
150
197
|
if (hovers.length > 0) return `border-left: 3px solid ${hovers[0].color}`;
|
|
@@ -350,8 +397,9 @@ defineExpose({ connectedUsers });
|
|
|
350
397
|
v-for="col in columns"
|
|
351
398
|
:key="col.id"
|
|
352
399
|
:data-drag-id="col.id"
|
|
353
|
-
class="flex-shrink-0
|
|
400
|
+
class="flex-shrink-0 rounded-lg border transition-colors"
|
|
354
401
|
:class="[
|
|
402
|
+
columnWidthClass,
|
|
355
403
|
dragOverColumnId === col.id ? 'border-(--ui-primary) bg-(--ui-primary)/5' : remoteDragColor(col.id) ? 'bg-(--ui-primary)/5' : 'border-(--ui-border) bg-(--ui-bg-elevated)',
|
|
356
404
|
colDragOver === col.id ? 'border-l-4 border-l-(--ui-primary)' : '',
|
|
357
405
|
colDragId === col.id ? 'opacity-40' : ''
|
|
@@ -395,6 +443,17 @@ defineExpose({ connectedUsers });
|
|
|
395
443
|
variant="subtle"
|
|
396
444
|
size="xs"
|
|
397
445
|
/>
|
|
446
|
+
<div
|
|
447
|
+
v-if="columnPresence(col.id).length"
|
|
448
|
+
class="flex -space-x-1"
|
|
449
|
+
>
|
|
450
|
+
<span
|
|
451
|
+
v-for="u in columnPresence(col.id).slice(0, 4)"
|
|
452
|
+
:key="u.name"
|
|
453
|
+
class="size-2 rounded-full ring-1 ring-(--ui-bg)"
|
|
454
|
+
:style="{ backgroundColor: u.color }"
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
398
457
|
<UDropdownMenu
|
|
399
458
|
v-if="editable"
|
|
400
459
|
:items="colMenuItems(col)"
|
|
@@ -425,7 +484,7 @@ defineExpose({ connectedUsers });
|
|
|
425
484
|
>
|
|
426
485
|
<div
|
|
427
486
|
:data-drag-id="card.id"
|
|
428
|
-
class="group bg-(--ui-bg) rounded border
|
|
487
|
+
class="group bg-(--ui-bg) rounded border text-sm cursor-pointer hover:border-(--ui-primary) transition-colors relative overflow-hidden"
|
|
429
488
|
:class="{
|
|
430
489
|
'opacity-30': dragCardId === card.id,
|
|
431
490
|
'border-t-2 border-t-(--ui-primary)': dragOverCardId === card.id,
|
|
@@ -440,25 +499,90 @@ defineExpose({ connectedUsers });
|
|
|
440
499
|
@pointerleave="onCardPointerLeave"
|
|
441
500
|
@click="openNode(card.id, card.label)"
|
|
442
501
|
>
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
v-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
@keydown.enter.stop="commitRename"
|
|
451
|
-
@keydown.escape.stop="renameId = null"
|
|
452
|
-
@blur="commitRename"
|
|
453
|
-
@click.stop
|
|
502
|
+
<!-- Cover thumbnail (if set) -->
|
|
503
|
+
<AGalleryCoverImage
|
|
504
|
+
v-if="cardHasCover(card)"
|
|
505
|
+
:upload-id="card.meta.coverUploadId"
|
|
506
|
+
:doc-id="card.meta.coverDocId ?? card.id"
|
|
507
|
+
:mime-type="card.meta.coverMimeType"
|
|
508
|
+
class="w-full aspect-[3/2] object-cover bg-(--ui-bg-elevated)"
|
|
454
509
|
/>
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
510
|
+
|
|
511
|
+
<!-- Title row -->
|
|
512
|
+
<div class="flex items-center justify-between gap-1 px-3 py-2">
|
|
513
|
+
<UInput
|
|
514
|
+
v-if="renameId === card.id"
|
|
515
|
+
v-model="renameValue"
|
|
516
|
+
size="xs"
|
|
517
|
+
variant="none"
|
|
518
|
+
class="flex-1"
|
|
519
|
+
autofocus
|
|
520
|
+
@keydown.enter.stop="commitRename"
|
|
521
|
+
@keydown.escape.stop="renameId = null"
|
|
522
|
+
@blur="commitRename"
|
|
523
|
+
@click.stop
|
|
524
|
+
/>
|
|
525
|
+
<span
|
|
526
|
+
v-else
|
|
527
|
+
class="truncate flex items-center gap-1"
|
|
528
|
+
@dblclick.stop="editable ? startRename(card.id, card.label) : void 0"
|
|
529
|
+
>
|
|
530
|
+
<UIcon
|
|
531
|
+
v-if="card.meta?.icon"
|
|
532
|
+
:name="`i-lucide-${card.meta.icon}`"
|
|
533
|
+
class="size-3.5 shrink-0 text-(--ui-text-muted)"
|
|
534
|
+
/>
|
|
535
|
+
<span class="truncate">{{ card.label }}</span>
|
|
536
|
+
</span>
|
|
537
|
+
<UDropdownMenu
|
|
538
|
+
v-if="editable"
|
|
539
|
+
:items="cardMenuItems(card, col.id)"
|
|
540
|
+
:content="{ align: 'start' }"
|
|
541
|
+
>
|
|
542
|
+
<UButton
|
|
543
|
+
icon="i-lucide-ellipsis"
|
|
544
|
+
size="xs"
|
|
545
|
+
variant="ghost"
|
|
546
|
+
color="neutral"
|
|
547
|
+
class="md:opacity-0 md:group-hover:opacity-100 shrink-0"
|
|
548
|
+
@click.stop
|
|
549
|
+
/>
|
|
550
|
+
</UDropdownMenu>
|
|
551
|
+
</div>
|
|
552
|
+
|
|
553
|
+
<!-- Meta chips row -->
|
|
554
|
+
<div
|
|
555
|
+
v-if="cardPriority(card) || cardDueDate(card) || cardTags(card).length"
|
|
556
|
+
class="flex items-center flex-wrap gap-1 px-3 pb-2 -mt-1"
|
|
459
557
|
>
|
|
460
|
-
|
|
461
|
-
|
|
558
|
+
<UBadge
|
|
559
|
+
v-if="cardPriority(card)"
|
|
560
|
+
size="xs"
|
|
561
|
+
variant="subtle"
|
|
562
|
+
:color="cardPriority(card).color"
|
|
563
|
+
>
|
|
564
|
+
{{ cardPriority(card).label }}
|
|
565
|
+
</UBadge>
|
|
566
|
+
<UBadge
|
|
567
|
+
v-if="cardDueDate(card)"
|
|
568
|
+
size="xs"
|
|
569
|
+
variant="subtle"
|
|
570
|
+
color="neutral"
|
|
571
|
+
icon="i-lucide-calendar"
|
|
572
|
+
>
|
|
573
|
+
{{ cardDueDate(card) }}
|
|
574
|
+
</UBadge>
|
|
575
|
+
<UBadge
|
|
576
|
+
v-for="tag in cardTags(card)"
|
|
577
|
+
:key="tag"
|
|
578
|
+
size="xs"
|
|
579
|
+
variant="subtle"
|
|
580
|
+
color="neutral"
|
|
581
|
+
>
|
|
582
|
+
{{ tag }}
|
|
583
|
+
</UBadge>
|
|
584
|
+
</div>
|
|
585
|
+
|
|
462
586
|
<!-- Remote hover name badge -->
|
|
463
587
|
<span
|
|
464
588
|
v-if="remoteHovers(card.id).length > 0"
|
|
@@ -467,20 +591,6 @@ defineExpose({ connectedUsers });
|
|
|
467
591
|
>
|
|
468
592
|
{{ remoteHovers(card.id)[0].name }}
|
|
469
593
|
</span>
|
|
470
|
-
<UDropdownMenu
|
|
471
|
-
v-if="editable"
|
|
472
|
-
:items="cardMenuItems(card, col.id)"
|
|
473
|
-
:content="{ align: 'start' }"
|
|
474
|
-
>
|
|
475
|
-
<UButton
|
|
476
|
-
icon="i-lucide-ellipsis"
|
|
477
|
-
size="xs"
|
|
478
|
-
variant="ghost"
|
|
479
|
-
color="neutral"
|
|
480
|
-
class="md:opacity-0 md:group-hover:opacity-100"
|
|
481
|
-
@click.stop
|
|
482
|
-
/>
|
|
483
|
-
</UDropdownMenu>
|
|
484
594
|
</div>
|
|
485
595
|
</UContextMenu>
|
|
486
596
|
</TransitionGroup>
|
|
@@ -506,7 +616,8 @@ defineExpose({ connectedUsers });
|
|
|
506
616
|
<button
|
|
507
617
|
v-if="editable"
|
|
508
618
|
:key="'__add-col__'"
|
|
509
|
-
class="flex-shrink-0
|
|
619
|
+
class="flex-shrink-0 h-10 rounded-lg border-2 border-dashed border-(--ui-border) hover:border-(--ui-primary) text-xs text-(--ui-text-muted) hover:text-(--ui-primary) transition-colors"
|
|
620
|
+
:class="columnWidthClass"
|
|
510
621
|
@click="addColumn"
|
|
511
622
|
>
|
|
512
623
|
+ {{ locale.addColumn }}
|
|
@@ -5,6 +5,8 @@ import { useRendererBase } from "../../composables/useRendererBase";
|
|
|
5
5
|
import { useNodePanel } from "../../composables/useNodePanel";
|
|
6
6
|
import { useMediaExtractor } from "../../composables/useMediaExtractor";
|
|
7
7
|
import { useMediaPlayback } from "../../composables/useMediaPlayback";
|
|
8
|
+
import { useFileBlobStore } from "../../composables/useFileBlobStore";
|
|
9
|
+
import { useSyncedMap } from "../../composables/useYDoc";
|
|
8
10
|
import MediaPlaylist from "./media/MediaPlaylist.vue";
|
|
9
11
|
import MediaTransportBar from "./media/MediaTransportBar.vue";
|
|
10
12
|
import MediaSyncBar from "./media/MediaSyncBar.vue";
|
|
@@ -39,20 +41,28 @@ const { mediaItems, groupedMedia } = useMediaExtractor(
|
|
|
39
41
|
);
|
|
40
42
|
const mediaElementRef = shallowRef(null);
|
|
41
43
|
const myClientId = computed(() => props.childProvider?.awareness?.clientID ?? 0);
|
|
42
|
-
const getBlobUrl =
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const shuffleEnabled = ref(false);
|
|
44
|
+
const { getBlobUrl } = useFileBlobStore();
|
|
45
|
+
const savedDocMeta = computed(() => tree.treeMap.data?.[props.docId]?.meta);
|
|
46
|
+
const repeatMode = ref(savedDocMeta.value?.mediaRepeat ?? "off");
|
|
47
|
+
watch(() => savedDocMeta.value?.mediaRepeat, (v) => {
|
|
48
|
+
if (v && v !== repeatMode.value) repeatMode.value = v;
|
|
49
|
+
});
|
|
50
|
+
function setRepeatMode(v) {
|
|
51
|
+
repeatMode.value = v;
|
|
52
|
+
tree.updateMeta(props.docId, { mediaRepeat: v });
|
|
53
|
+
}
|
|
54
|
+
function cycleRepeatMode() {
|
|
55
|
+
setRepeatMode(repeatMode.value === "off" ? "all" : repeatMode.value === "all" ? "one" : "off");
|
|
56
|
+
}
|
|
57
|
+
const shuffleEnabled = ref(savedDocMeta.value?.mediaShuffle ?? false);
|
|
58
|
+
watch(() => savedDocMeta.value?.mediaShuffle, (v) => {
|
|
59
|
+
if (typeof v === "boolean" && v !== shuffleEnabled.value) shuffleEnabled.value = v;
|
|
60
|
+
});
|
|
61
|
+
function toggleShuffle() {
|
|
62
|
+
const next = !shuffleEnabled.value;
|
|
63
|
+
shuffleEnabled.value = next;
|
|
64
|
+
tree.updateMeta(props.docId, { mediaShuffle: next });
|
|
65
|
+
}
|
|
56
66
|
const playback = useMediaPlayback({
|
|
57
67
|
mediaElementRef,
|
|
58
68
|
childDoc,
|
|
@@ -60,7 +70,7 @@ const playback = useMediaPlayback({
|
|
|
60
70
|
repeatMode,
|
|
61
71
|
shuffle: shuffleEnabled,
|
|
62
72
|
getBlobUrl,
|
|
63
|
-
useSyncedMap
|
|
73
|
+
useSyncedMap,
|
|
64
74
|
onTrackChange: (track) => {
|
|
65
75
|
setLocalState({ "media:trackId": track.id });
|
|
66
76
|
}
|
|
@@ -146,14 +156,14 @@ defineExpose({ connectedUsers });
|
|
|
146
156
|
variant="ghost"
|
|
147
157
|
:color="shuffleEnabled ? 'primary' : 'neutral'"
|
|
148
158
|
size="xs"
|
|
149
|
-
@click="
|
|
159
|
+
@click="toggleShuffle"
|
|
150
160
|
/>
|
|
151
161
|
<UButton
|
|
152
162
|
:icon="repeatMode === 'one' ? 'i-lucide-repeat-1' : 'i-lucide-repeat'"
|
|
153
163
|
variant="ghost"
|
|
154
164
|
:color="repeatMode !== 'off' ? 'primary' : 'neutral'"
|
|
155
165
|
size="xs"
|
|
156
|
-
@click="
|
|
166
|
+
@click="cycleRepeatMode"
|
|
157
167
|
/>
|
|
158
168
|
</div>
|
|
159
169
|
</div>
|