/** * Declarative creation-specs for every rApp that stores dated records. * * Each spec says: "here's my doc pattern, here's where my records live, * here's how to pick a title / href / tags." The universal enumerator * consumes these to produce the rPast chronicle-of-self. * * Adding a new rApp: append a new `CreationSpec` literal below and * register it in `shared/markwhen/index.ts`. No bespoke code required. */ import type { CreationSpec } from './universal-enumerator'; const hrefFor = (module: string) => ({ space, id }: { space: string; id: string }) => `/${space}/${module}?focus=${encodeURIComponent(id)}`; export const rcalSpec: CreationSpec = { module: 'rcal', label: 'Calendar', icon: '📅', color: 'blue', docPatterns: ['{space}:cal:events'], collections: [ { path: 'events', recordType: 'event', href: hrefFor('rcal'), tags: r => { const t: string[] = []; if (Array.isArray(r.tags)) t.push(...(r.tags as string[])); if (typeof r.bookingStatus === 'string') t.push(r.bookingStatus as string); if (r.isVirtual) t.push('virtual'); return t.length ? t : undefined; }, description: r => (typeof r.description === 'string' ? (r.description as string).slice(0, 200) : undefined), }, { path: 'sources', recordType: 'calendar-source', href: hrefFor('rcal') }, { path: 'views', recordType: 'saved-view' }, ], }; export const rnotesSpec: CreationSpec = { module: 'rnotes', label: 'Notes', icon: '📓', color: 'green', docPatterns: ['{space}:rnotes:vaults:*'], collections: [ { path: 'notes', recordType: 'note', timestampField: 'lastModifiedAt', title: (r, id) => (typeof r.title === 'string' && r.title ? r.title as string : id), href: ({ space, docId, id }) => { const vaultId = docId.split(':').pop() ?? ''; return `/${space}/rnotes/${vaultId}/${encodeURIComponent(id)}`; }, tags: r => (Array.isArray(r.tags) ? (r.tags as string[]) : undefined), }, ], }; export const rtasksSpec: CreationSpec = { module: 'rtasks', label: 'Tasks', icon: '✅', color: 'orange', docPatterns: ['{space}:rtasks:boards:*'], collections: [ { path: 'tasks', recordType: 'task', href: hrefFor('rtasks'), tags: r => { const t: string[] = []; if (typeof r.status === 'string') t.push(r.status as string); if (typeof r.priority === 'string') t.push(r.priority as string); if (Array.isArray(r.tags)) t.push(...(r.tags as string[])); return t.length ? t : undefined; }, }, ], }; export const rvoteSpec: CreationSpec = { module: 'rvote', label: 'Votes', icon: '🗳️', color: 'purple', docPatterns: ['{space}:rvote:proposals:*'], collections: [ { path: 'proposals', recordType: 'proposal', href: hrefFor('rvote'), tags: r => (typeof r.status === 'string' ? [r.status as string] : undefined), }, { path: 'votes', recordType: 'vote' }, { path: 'finalVotes', recordType: 'final-vote' }, ], }; export const rphotosSpec: CreationSpec = { module: 'rphotos', label: 'Photos', icon: '📸', color: 'rose', docPatterns: ['{space}:rphotos:albums'], collections: [ { path: 'sharedAlbums', recordType: 'album', href: hrefFor('rphotos') }, { path: 'subAlbums', recordType: 'album', href: hrefFor('rphotos') }, { path: 'annotations', recordType: 'annotation' }, ], }; export const rfilesSpec: CreationSpec = { module: 'rfiles', label: 'Files', icon: '📁', color: 'teal', docPatterns: ['{space}:rfiles:library'], collections: [ { path: 'files', recordType: 'file', title: (r, id) => (typeof r.name === 'string' ? r.name as string : id), href: hrefFor('rfiles'), }, { path: 'memoryCards', recordType: 'memory-card', href: hrefFor('rfiles') }, ], }; export const rdocsSpec: CreationSpec = { module: 'rdocs', label: 'Docs', icon: '📄', color: 'indigo', docPatterns: ['{space}:rdocs:notebooks:*'], collections: [ { path: 'items', recordType: 'doc', href: hrefFor('rdocs') }, ], }; export const rsheetsSpec: CreationSpec = { module: 'rsheets', label: 'Sheets', icon: '📊', color: 'lime', docPatterns: ['{space}:rsheets:sheets:*'], collections: [ { path: 'rows', recordType: 'row' }, { path: 'columns', recordType: 'column' }, ], }; export const rtripsSpec: CreationSpec = { module: 'rtrips', label: 'Trips', icon: '🧳', color: 'amber', docPatterns: ['{space}:rtrips:trips:*'], collections: [ { path: 'stops', recordType: 'stop', href: hrefFor('rtrips') }, { path: 'expenses', recordType: 'expense' }, { path: 'packingList', recordType: 'packing' }, ], }; export const rchoicesSpec: CreationSpec = { module: 'rchoices', label: 'Choices', icon: '🎯', color: 'fuchsia', docPatterns: ['{space}:rchoices:sessions'], collections: [ { path: 'sessions', recordType: 'session', href: hrefFor('rchoices') }, { path: 'votes', recordType: 'vote' }, ], }; export const rinboxSpec: CreationSpec = { module: 'rinbox', label: 'Inbox', icon: '📧', color: 'gray', docPatterns: ['{space}:rinbox:mailboxes:*'], collections: [ { path: 'messages', recordType: 'message', title: r => (typeof r.subject === 'string' ? r.subject as string : '(no subject)'), href: hrefFor('rinbox'), }, ], }; export const rcartSpec: CreationSpec = { module: 'rcart', label: 'Cart', icon: '🛒', color: 'yellow', docPatterns: ['{space}:rcart:catalog', '{space}:rcart:orders:*'], collections: [ { path: 'items', recordType: 'item', href: hrefFor('rcart') }, { path: 'orders', recordType: 'order', title: (r, id) => `Order ${id.slice(0, 8)}`, href: hrefFor('rcart'), }, ], }; export const ALL_CREATION_SPECS: CreationSpec[] = [ rcalSpec, rnotesSpec, rtasksSpec, rvoteSpec, rphotosSpec, rfilesSpec, rdocsSpec, rsheetsSpec, rtripsSpec, rchoicesSpec, rinboxSpec, rcartSpec, ];