feat: multiple pages

This commit is contained in:
2026-03-16 16:33:11 +01:00
parent 9d38b1df2b
commit 3de9a33074
3 changed files with 681 additions and 48 deletions

View File

@@ -10,6 +10,19 @@ export type { StoredGraphState }
/** BlockNote document: array of blocks (PartialBlock). Stored as JSON. */
export type StoredLogosContent = Record<string, unknown>[]
/** Id for a Logos page or subpage. */
export type LogosPageId = string
/** Metadata for one page or subpage in the Logos hierarchy (one level: page → subpages). */
export type LogosPageMeta = {
id: LogosPageId
title: string
/** null = top-level page; non-null = subpage under that page. */
parentId: LogosPageId | null
/** Order among siblings (same parent). */
position: number
}
/** One entry in the render-output cache (one per rendering node, overwritten on each update). */
export type RenderOutputCacheEntry = {
nodeId: string
@@ -36,6 +49,8 @@ export const RECOLLECTION_VERSION = 1
const GRAPH_KEY_PREFIX = 'zui_graph_'
const LOGOS_KEY_PREFIX = 'zui_logos_'
const LOGOS_PAGE_TREE_PREFIX = 'zui_logos_pagetree_'
const LOGOS_PAGE_CONTENT_PREFIX = 'zui_logos_page_'
const RENDER_CACHE_KEY_PREFIX = 'zui_render_cache_'
function getGraphKey(recollectionId: string): string {
@@ -46,6 +61,14 @@ function getLogosKey(recollectionId: string): string {
return `${LOGOS_KEY_PREFIX}${recollectionId}`
}
function getLogosPageTreeKey(recollectionId: string): string {
return `${LOGOS_PAGE_TREE_PREFIX}${recollectionId}`
}
function getLogosPageContentKey(recollectionId: string, pageId: LogosPageId): string {
return `${LOGOS_PAGE_CONTENT_PREFIX}${recollectionId}_${pageId}`
}
function getRenderCacheKey(recollectionId: string): string {
return `${RENDER_CACHE_KEY_PREFIX}${recollectionId}`
}
@@ -89,6 +112,61 @@ export function setLogosContent(recollectionId: string, content: StoredLogosCont
localStorage.setItem(getLogosKey(recollectionId), JSON.stringify(content))
}
/** Logos page tree: ordered list of page metas (pages and subpages). */
export function getLogosPageTree(recollectionId: string): LogosPageMeta[] {
try {
const raw = localStorage.getItem(getLogosPageTreeKey(recollectionId))
if (!raw) return []
const data = JSON.parse(raw) as unknown
if (!Array.isArray(data)) return []
return data.filter(
(item): item is LogosPageMeta =>
item != null &&
typeof item === 'object' &&
typeof (item as LogosPageMeta).id === 'string' &&
typeof (item as LogosPageMeta).title === 'string' &&
((item as LogosPageMeta).parentId === null || typeof (item as LogosPageMeta).parentId === 'string') &&
typeof (item as LogosPageMeta).position === 'number'
) as LogosPageMeta[]
} catch {
return []
}
}
export function setLogosPageTree(recollectionId: string, tree: LogosPageMeta[]): void {
localStorage.setItem(getLogosPageTreeKey(recollectionId), JSON.stringify(tree))
}
/** Per-page Logos content (for page/subpage hierarchy). */
export function getLogosContentForPage(
recollectionId: string,
pageId: LogosPageId
): StoredLogosContent | null {
try {
const raw = localStorage.getItem(getLogosPageContentKey(recollectionId, pageId))
if (!raw) return null
const data = JSON.parse(raw) as unknown
if (!Array.isArray(data)) return null
if (!data.every((item) => item != null && typeof item === 'object')) return null
return data as StoredLogosContent
} catch {
return null
}
}
export function setLogosContentForPage(
recollectionId: string,
pageId: LogosPageId,
content: StoredLogosContent
): void {
localStorage.setItem(getLogosPageContentKey(recollectionId, pageId), JSON.stringify(content))
}
/** Remove stored content for a single page (e.g. when deleting that page). */
export function removeLogosPageContent(recollectionId: string, pageId: LogosPageId): void {
localStorage.removeItem(getLogosPageContentKey(recollectionId, pageId))
}
/** Render output cache: one entry per rendering node (keyed by nodeId). Used by Logos "Insert from Flux" block. */
export function getRenderOutputCache(recollectionId: string): RenderOutputCacheEntry[] {
try {
@@ -120,9 +198,14 @@ export function upsertRenderOutputEntry(recollectionId: string, entry: RenderOut
)
}
/** Removes both graph, logos, and render cache data for the recollection. */
/** Removes both graph, logos (legacy + page tree + all per-page content), and render cache for the recollection. */
export function removeRecollectionData(recollectionId: string): void {
localStorage.removeItem(getGraphKey(recollectionId))
localStorage.removeItem(getLogosKey(recollectionId))
const tree = getLogosPageTree(recollectionId)
for (const page of tree) {
removeLogosPageContent(recollectionId, page.id)
}
localStorage.removeItem(getLogosPageTreeKey(recollectionId))
localStorage.removeItem(getRenderCacheKey(recollectionId))
}