Recipe: Journal

A Collection for the entries plus a Document for preferences. Key entries by a sortable id so listing is naturally chronological. Assume the singleton app store and light-DOM routes around this.

// www/store/app.ts
type Entry = { title: string; body: string; date: string /* ISO */ }
type Prefs = { defaultMood: string }

const store = new Store(backend, {
  documents: ['prefs'],
  collections: ['entries'],
  versions: [/* … */],
})

export class App {
  prefs = store.document<Prefs>('prefs', { defaultMood: 'neutral' })
  entries = store.collection<Entry>('entries')
  // …preload both in #init…

  write(title: string, body: string) {
    // date-prefixed id → list() returns entries in date order
    const id = `${new Date().toISOString()}_${crypto.randomUUID().slice(0, 8)}`
    return this.entries.set(id, { title, body, date: new Date().toISOString() })
  }
  async recent(limit = 20) {
    const ids = (await this.entries.list()).sort().reverse().slice(0, limit)
    const all = this.entries.value ?? new Map()
    return ids.map((id) => ({ id, entry: all.get(id)! }))
  }
}

The pattern generalizes to any “log of things over time” app — workouts, expenses, reading notes. Use getHistory(id) if you want to show how a single entry was edited over time (the CRDT keeps it).


Next: Gallery swaps JSON entries for binary images with @civility/blobs.