@@ -3,17 +3,20 @@ import { computed, onMounted, ref } from "vue";
33import { RouterLink , useRoute , useRouter } from " vue-router" ;
44
55import { Button } from " @/components/ui/button" ;
6+ import {
7+ Card ,
8+ CardContent ,
9+ CardHeader ,
10+ CardTitle ,
11+ } from " @/components/ui/card" ;
612
713import { useSession } from " ../auth/useSession" ;
814import SpatialPageView from " ../spatial/SpatialPageView.vue" ;
915import { useUserTemplates } from " ../spatial/useUserTemplates" ;
1016import PageEditorBacklinksCard from " ./PageEditorBacklinksCard.vue" ;
1117import PageEditorCollabStatusCard from " ./PageEditorCollabStatusCard.vue" ;
1218import PageEditorManagementCard from " ./PageEditorManagementCard.vue" ;
13- import PageEditorPathCard from " ./PageEditorPathCard.vue" ;
1419import PageEditorSnapshotsCard from " ./PageEditorSnapshotsCard.vue" ;
15- import PageEditorTiptapCard from " ./PageEditorTiptapCard.vue" ;
16- import { Y_FRAG_PROSEMIRROR , Y_TEXT_DEFAULT } from " ./page-editor-constants" ;
1720import { createPageCollabDoc } from " ./page-yjs-doc" ;
1821import { usePageCollabEditor } from " ./usePageCollabEditor" ;
1922import { usePageManagement } from " ./usePageManagement" ;
@@ -23,6 +26,15 @@ import { usePageSnapshots } from "./usePageSnapshots";
2326
2427import type { SnapshotRow } from " ./page-snapshot-list" ;
2528
29+ function pagePathLabel(
30+ pid : string ,
31+ labels : Record <string , string >,
32+ ): string {
33+ const label = labels [pid ];
34+ if (label != null && label .length > 0 ) return label ;
35+ return ` [Page ${pid }] ` ;
36+ }
37+
2638const route = useRoute ();
2739const router = useRouter ();
2840const { client, isAuthenticated, user, bootstrapped } = useSession ();
@@ -159,96 +171,164 @@ onMounted(() => {
159171
160172<template >
161173 <div v-if =" !isAuthenticated" class =" text-muted-foreground text-sm" />
162- <div v-else class =" space-y-4" >
163- <div class =" flex flex-wrap items-center justify-between gap-2" >
164- <div >
165- <h1 class =" text-lg font-semibold" >Spatial page</h1 >
166- <p class =" text-muted-foreground text-xs" >
167- Pages are spatial worlds; this editor focuses the shared rich-text note
168- for collab (multi-note canvas parity follows legacy).
169- </p >
170- <p
171- v-if =" user"
172- class =" text-muted-foreground mt-1 font-mono text-xs break-all"
173- >
174- {{ pageId }} · group {{ collabGroupId ?? "—" }} · personal
175- {{ user.personalGroupId }}
176- </p >
177- </div >
178- <Button as-child size="sm" variant="outline">
179- <RouterLink to="/">Home</RouterLink >
180- </Button >
181- </div >
182-
183- <PageEditorPathCard
184- :path-loading =" pathLoading "
185- :path-error =" pathError "
186- :path-page-ids =" pathPageIds "
187- :path-page-labels =" pathPageLabels "
188- :page-prefs-loading =" pagePrefsLoading "
189- :is-favorite =" isFavorite "
190- :bump-message =" bumpMessage "
191- :favorite-message =" favoriteMessage "
192- @bump-as-starting =" bumpAsStarting ()"
193- @toggle-favorite =" toggleFavorite ()"
194- @remove-from-recent =" removeThisFromRecent ()"
195- />
196-
197- <p
198- v-if =" pageOpsMessage"
199- class =" text-muted-foreground border-border rounded-md border px-3 py-2 text-sm"
200- >
201- {{ pageOpsMessage }}
202- </p >
203-
204- <PageEditorSnapshotsCard
205- :snapshot-loading =" snapshotsLoading "
206- :snapshots =" snapshotList "
207- :collab-loading =" collabLoading "
208- :load-error =" loadError "
209- :crypto-error =" cryptoError "
210- @restore =" restoreFromSnapshot ($event )"
211- @remove =" deleteSnapshot ($event )"
212- @save-manual =" saveSnapshotManual ()"
213- />
214-
215- <PageEditorManagementCard
216- v-model :move-dest-group-id =" moveDestGroupId "
217- :collab-loading =" collabLoading "
218- :load-error =" loadError "
219- :crypto-error =" cryptoError "
220- :collab-group-id =" collabGroupId "
221- @move-to-group =" management .movePageToOtherGroup ()"
222- @set-as-main-page =" management .setAsGroupMainPage ()"
223- @soft-delete =" management .softDeleteThisPage ()"
224- @purge =" management .purgeThisPagePermanently ()"
225- />
226-
227- <PageEditorBacklinksCard :page-id =" pageId " />
228174
175+ <template v-else >
176+ <!-- === Main canvas slot === -->
229177 <SpatialPageView
230178 :ydoc =" ydoc "
231179 :default-note-template =" noteTemplate "
232180 :default-arrow-template =" arrowTemplate "
233181 />
234182
235- <PageEditorCollabStatusCard
236- :collab-loading =" collabLoading "
237- :load-error =" loadError "
238- :crypto-error =" cryptoError "
239- :collab-ws-live =" collabWsLive "
240- :collab-ws-error =" collabWsError "
241- :update-count =" updateCount "
242- :collab-last-index =" collabLastIndex "
243- :push-error =" pushError "
244- @unlock-with-password =" onUnlockWithPassword ($event )"
245- />
183+ <!-- === Toolbar center: breadcrumb path === -->
184+ <template #toolbar-center >
185+ <nav
186+ v-if =" !pathLoading && !pathError && pathPageIds.length > 0"
187+ class =" text-muted-foreground flex flex-wrap items-center justify-center gap-1 text-xs"
188+ >
189+ <template v-for =" (pid , i ) in pathPageIds " :key =" pid " >
190+ <span v-if =" i > 0" aria-hidden =" true" >/</span >
191+ <RouterLink
192+ v-if =" i < pathPageIds .length - 1 "
193+ class="text-primary hover:underline "
194+ :to =" ` /pages/${pid } ` "
195+ >
196+ {{ pagePathLabel(pid, pathPageLabels) }}
197+ </RouterLink >
198+ <span v-else class =" text-foreground font-medium" >
199+ {{ pagePathLabel(pid, pathPageLabels) }}
200+ </span >
201+ </template >
202+ </nav >
203+ <span v-else-if =" pathLoading" class =" text-muted-foreground text-xs" >
204+ Loading path…
205+ </span >
206+ <span v-else-if =" pathError" class =" text-destructive text-xs" >
207+ {{ pathError }}
208+ </span >
209+ </template >
246210
247- <PageEditorTiptapCard
248- :editor =" editor "
249- :y-state-bytes =" yStateBytes "
250- :y-frag-prosemirror =" Y_FRAG_PROSEMIRROR "
251- :y-text-default =" Y_TEXT_DEFAULT "
252- />
253- </div >
211+ <!-- === Left sidebar === -->
212+ <template #left-sidebar >
213+ <div class =" space-y-3" >
214+ <!-- Current path -->
215+ <Card >
216+ <CardHeader class="pb-2">
217+ <CardTitle class="text-sm">Path</CardTitle >
218+ </CardHeader >
219+ <CardContent class="space-y-2 text-xs">
220+ <p v-if =" pathLoading" class =" text-muted-foreground" >Loading…</p >
221+ <p v-else-if =" pathError" class =" text-destructive" >{{ pathError }}</p >
222+ <nav v-else class =" text-muted-foreground flex flex-wrap items-center gap-1" >
223+ <template v-for =" (pid , i ) in pathPageIds " :key =" pid " >
224+ <span v-if =" i > 0" >/</span >
225+ <RouterLink
226+ v-if =" i < pathPageIds .length - 1 "
227+ class="text-primary hover:underline "
228+ :to =" ` /pages/${pid } ` "
229+ >
230+ {{ pagePathLabel(pid, pathPageLabels) }}
231+ </RouterLink >
232+ <span v-else class =" text-foreground font-medium" >
233+ {{ pagePathLabel(pid, pathPageLabels) }}
234+ </span >
235+ </template >
236+ </nav >
237+ <div class =" flex flex-wrap gap-1" >
238+ <Button
239+ size="xs"
240+ variant="secondary"
241+ class="h-6 text-[10px]"
242+ :disabled =" pagePrefsLoading "
243+ @click =" bumpAsStarting ()"
244+ >
245+ Make starting
246+ </Button >
247+ <Button
248+ size="xs"
249+ variant="outline"
250+ class="h-6 text-[10px]"
251+ :disabled =" pagePrefsLoading "
252+ @click =" toggleFavorite ()"
253+ >
254+ {{ isFavorite ? "Unfavorite" : "Favorite" }}
255+ </Button >
256+ </div >
257+ <p v-if =" bumpMessage" class =" text-muted-foreground text-[10px]" >
258+ {{ bumpMessage }}
259+ </p >
260+ <p v-if =" favoriteMessage" class =" text-amber-700 dark:text-amber-300 text-[10px]" >
261+ {{ favoriteMessage }}
262+ </p >
263+ </CardContent >
264+ </Card >
265+
266+ <!-- Collab status mini -->
267+ <PageEditorCollabStatusCard
268+ :collab-loading =" collabLoading "
269+ :load-error =" loadError "
270+ :crypto-error =" cryptoError "
271+ :collab-ws-live =" collabWsLive "
272+ :collab-ws-error =" collabWsError "
273+ :update-count =" updateCount "
274+ :collab-last-index =" collabLastIndex "
275+ :push-error =" pushError "
276+ @unlock-with-password =" onUnlockWithPassword ($event )"
277+ />
278+ </div >
279+ </template >
280+
281+ <!-- === Right sidebar === -->
282+ <template #right-sidebar >
283+ <div class =" space-y-3" >
284+ <PageEditorSnapshotsCard
285+ :snapshot-loading =" snapshotsLoading "
286+ :snapshots =" snapshotList "
287+ :collab-loading =" collabLoading "
288+ :load-error =" loadError "
289+ :crypto-error =" cryptoError "
290+ @restore =" restoreFromSnapshot ($event )"
291+ @remove =" deleteSnapshot ($event )"
292+ @save-manual =" saveSnapshotManual ()"
293+ />
294+
295+ <PageEditorManagementCard
296+ v-model :move-dest-group-id =" moveDestGroupId "
297+ :collab-loading =" collabLoading "
298+ :load-error =" loadError "
299+ :crypto-error =" cryptoError "
300+ :collab-group-id =" collabGroupId "
301+ @move-to-group =" management .movePageToOtherGroup ()"
302+ @set-as-main-page =" management .setAsGroupMainPage ()"
303+ @soft-delete =" management .softDeleteThisPage ()"
304+ @purge =" management .purgeThisPagePermanently ()"
305+ />
306+
307+ <PageEditorBacklinksCard :page-id =" pageId " />
308+ </div >
309+ </template >
310+
311+ <!-- === Floating overlay === -->
312+ <template #floating-overlay >
313+ <!-- Bottom-right info -->
314+ <div
315+ class =" pointer-events-none absolute bottom-3 right-3 z-20 text-right text-xs"
316+ >
317+ <div class =" text-primary font-medium" >
318+ {{ pageId }}
319+ </div >
320+ <div v-if =" collabGroupId" class =" text-muted-foreground" >
321+ group {{ collabGroupId }}
322+ </div >
323+ </div >
324+
325+ <!-- Page ops message toast -->
326+ <div
327+ v-if =" pageOpsMessage"
328+ class =" bg-card border-border pointer-events-auto absolute top-3 left-1/2 z-20 max-w-md -translate-x-1/2 rounded-md border px-3 py-2 text-xs shadow-lg"
329+ >
330+ {{ pageOpsMessage }}
331+ </div >
332+ </template >
333+ </template >
254334</template >
0 commit comments