Files
Momento/_bmad-output/implementation-artifacts/spec-us4-db-inline-redesign.md
Antigravity 96e7902f01
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m22s
CI / Deploy production (on server) (push) Has been skipped
feat: publication IA (magazine/brief/essay) + fixes critique
Publication IA:
- 4 templates (magazine, brief, essay, simple) avec CSS riche
- Rewrite IA (article/exercises/tutorial/reference/mixed)
- Modération avec timeout 12s + fallback safe
- Quotas publish_enhance par tier (basic=2, pro=15, business=100)
- Détection contenu stale (hash)
- Migration DB publishedContent/publishedTemplate/publishedSourceHash

Fixes:
- cheerio v1.2: Element -> AnyNode (domhandler), decodeEntities cast
- _isShared ajouté au type Note (champ virtuel serveur)
- callout colors PDF export: extraction fonction pure testable
- admin/published: guard note.userId null
- Cmd+S fonctionne en mode dialog (pas seulement fullPage)

i18n:
- 23 clés publish* traduites dans les 15 locales
- Extension Web Clipper: 13 locales mise à jour

Tests:
- callout-colors.test.ts (6 tests)
- note-visible-in-view.test.ts (5 tests)
- entitlements.test.ts + byok-entitlements.test.ts: mock usageLog + unstubAllEnvs
- 199/199 tests passent

Tracker: user-stories.md sync avec sprint-status.yaml
2026-06-28 07:32:57 +00:00

11 KiB
Raw Blame History

title, type, created, status, baseline_commit, completedDate, context, note
title type created status baseline_commit completedDate context note
US-4 Redesign — Embedded Structured View Block (replaces authors/works demo) refactor 2026-05-27 done CURRENT 2026-05-30
docs/story-nextgen-editor-us4-redesign.md
memento-note/lib/structured-views/types.ts
Implementation complete with TWO modes: 1. Local Database (isLocal: true) — Notion-like inline table with editable columns/rows 2. Notebook Linked View (isLocal: false) — Live Structured Views integration Plus: Analytics panel, Memory Echo semantic search, Convert-to-notebook feature. Implementation exceeds original spec scope.

Intent

Problem: The current /database slash command inserts a self-contained "Authors & Works" relational block with hardcoded Jules Verne / Liu Cixin demo data stored as JSON blobs in TipTap node attributes — completely disconnected from Structured Views (NotebookSchema / NoteProperty), duplicating a data model and confusing users who expect a real notebook-linked view.

Approach: Replace the current databaseBlock TipTap node and its three files (extension, editor component, types) with a new structuredViewBlock node that stores only a reference (notebookId, displayMode, filter), then reads live data from the existing Structured Views API — or falls back gracefully when the notebook has no schema.

Boundaries & Constraints

Always:

  • The block stores only a reference (notebookId + metadata attrs) — never serialises note rows/property values into TipTap HTML.
  • The editor must receive notebookId from its parent (note-content-area.tsx) for the block to resolve its schema.
  • i18n: all labels go through useLanguage() with keys in EN/FR minimum; no hardcoded strings.
  • Mutations (add note, edit property value) use optimistic updates via NOTE_REQUEST_SAVE_EVENT — no full revalidatePath.
  • If the current notebook has no NotebookSchema, show a contextual callout ("This notebook has no structured view — set one up from the notebook header") instead of a blank block; never auto-insert demo data.
  • Migration: silently drop legacy databaseBlock nodes on load (treat as unknown node, ProseMirror drops them or show an "outdated block" placeholder).
  • On delete of the block: only the TipTap reference node is removed; no Prisma records are affected.
  • RTL (fa, ar): the block wrapper must respect dir="auto".

Ask First:

  • Whether the block should support inline editing of property values (clicking a cell to edit) or be read-only with a "Open in notebook" CTA only — founder decides before coding begins.
  • Whether Kanban view is in scope for v1 inline (complex drag-and-drop inside a TipTap NodeView) or deferred to v2.

Never:

  • Insert any demo data (no Jules Verne, no placeholders rows, no fake schema).
  • Create a second data storage system parallel to NotebookSchema/NoteProperty.
  • Store note rows or property values in TipTap node attributes.
  • Copy the DatabaseBlockEditor or database-block-types.ts pattern.
  • Use revalidatePath for mutations from inside the block.

I/O & Edge-Case Matrix

Scenario Input / State Expected Output / Behavior Error Handling
Happy path — structured notebook Note in structured notebook, user types /vue → insert block Block renders table of notes with their schema properties
No schema on notebook Note is in a plain notebook (no NotebookSchema) Block shows callout "Set up a structured view from the notebook header" with link CTA links to notebook toolbar wizard
Note not in any notebook Note has no notebookId Block shows "This block requires a notebook. Move this note to a notebook to use it." Graceful inline message
Legacy databaseBlock node found on load HTML contains data-database-block="true" ProseMirror skips the node (unknown type after extension removal); optionally show 1-line deprecation placeholder No crash, no data loss on other content
API error fetching schema GET /api/notebooks/:id/schema 500 Block shows error state with retry button Console.error, no spinner freeze
Inline edit (if in scope) — optimistic patch User edits a cell → PATCH /api/notes/:id/properties Optimistic update on cell, save triggered via NOTE_REQUEST_SAVE_EVENT Rollback cell on API error

Code Map

  • memento-note/components/tiptap-database-block-extension.tsx -- DELETE — legacy authors/works TipTap node
  • memento-note/components/database-block-editor.tsx -- DELETE — legacy authors/works editor
  • memento-note/lib/editor/database-block-types.ts -- DELETE — legacy types with Verne/Cixin data
  • memento-note/components/tiptap-structured-view-block-extension.tsx -- NEW — TipTap Node extension (structuredViewBlock, attrs: notebookId, displayMode: 'table'|'gallery', filterJson)
  • memento-note/components/structured-view-block-embed.tsx -- NEW — React NodeView component; fetches schema via SWR(/api/notebooks/${notebookId}/schema) and renders NotesStructuredTable or NotesGalleryView in read-only mode (or editable if founder approves inline edit)
  • memento-note/components/note-editor/note-content-area.tsx -- MODIFY — pass notebookId={note.notebookId} to <RichTextEditor>
  • memento-note/components/rich-text-editor.tsx -- MODIFY — accept notebookId?: string prop; pass it to editor storage; register StructuredViewBlockExtension in place of DatabaseBlockExtension; update slash command entry (label, keywords, handler); update block action menu's "Transform into" option
  • memento-note/components/block-action-menu.tsx -- MODIFY — replace database option with structuredView; import from new extension
  • memento-note/locales/en.json + fr.json -- MODIFY — add keys: structuredViewBlock.insertLabel, structuredViewBlock.insertDesc, structuredViewBlock.noSchema, structuredViewBlock.noNotebook, structuredViewBlock.openInNotebook, structuredViewBlock.displayModeTable, structuredViewBlock.displayModeGallery; remove all databaseBlock.* keys (after verifying no other consumer)

Tasks & Acceptance

Execution:

  • memento-note/lib/editor/database-block-types.ts -- DELETE file -- eliminates Verne/Cixin hardcoded data and legacy type definitions
  • memento-note/components/database-block-editor.tsx -- DELETE file -- removes authors/works editor UI
  • memento-note/components/tiptap-database-block-extension.tsx -- DELETE file -- removes legacy TipTap node extension
  • memento-note/components/tiptap-structured-view-block-extension.tsx -- CREATE — TipTap Node structuredViewBlock with attrs { notebookId: string, displayMode: 'table'|'gallery', filterJson: string }, ReactNodeViewRenderer(StructuredViewBlockEmbed)
  • memento-note/components/structured-view-block-embed.tsx -- CREATE — NodeView wrapper: SWR fetch schema, conditional rendering (no-schema callout / no-notebook callout / table or gallery view), RTL-safe wrapper, dir="auto"
  • memento-note/components/note-editor/note-content-area.tsx -- MODIFY — add notebookId={note.notebookId ?? undefined} prop to both <RichTextEditor> instances
  • memento-note/components/rich-text-editor.tsx -- MODIFY — (a) add notebookId?: string to props, store in editor.storage.structuredViewBlock = { notebookId }; (b) swap DatabaseBlockExtension for StructuredViewBlockExtension; (c) update slash entry: title key structuredViewBlock.insertLabel, description key structuredViewBlock.insertDesc, keywords ['vue', 'tableau', 'structuré', 'structured', 'view', 'database', 'db']; (d) update slash handler to call insertStructuredViewBlockAtSelection(editor, notebookId)
  • memento-note/components/block-action-menu.tsx -- MODIFY — replace database transform option with structuredView; update import
  • memento-note/locales/en.json + fr.json -- MODIFY — add new i18n keys (see Code Map); remove databaseBlock.* keys

Acceptance Criteria:

  • Given a note in a structured notebook, when user types /vue or /structured, then a structuredViewBlock node is inserted showing the notebook's notes and their properties — no demo data appears.
  • Given the block is inserted, when the notebook has no schema, then the block shows a callout with a link to the notebook header wizard — no crash, no empty white box.
  • Given the note has no notebookId, when /vue is triggered, then the block shows an inline "requires a notebook" message.
  • Given a note with a legacy data-database-block node, when the note is opened, then the legacy block is silently dropped (or shown as deprecated placeholder) and the rest of the note content is intact.
  • Given the block is visible, when user resizes or the app language is Persian (fa, RTL), then the block layout is mirrored correctly.
  • Given the slash command menu opens, when user searches "database" or "db", then the new structuredViewBlock entry appears (backward-compatible keywords).
  • Given "Transform into" is opened in the block action menu, the "Database" option is replaced by "Vue structurée" / "Structured View".

Spec Change Log

Design Notes

Why reference-only attrs? Storing note rows in TipTap HTML scales to O(notes × properties) per keystroke — unacceptable for perf. The reference pattern (notebookId only) is how Notion "linked database" blocks work and aligns with Memento's already-existing API layer.

notebookId propagation: The editor currently receives only noteId. The smallest change is to add notebookId as a prop to RichTextEditor (already used by note-content-area.tsx). No context change needed.

Legacy migration: ProseMirror silently drops unknown node types when the corresponding extension is not registered. Removing DatabaseBlockExtension from the extensions array is sufficient for new sessions. For existing notes with the old HTML, the data-database-block div will parse as an unrecognised block and ProseMirror will omit it. If this is too silent, add a parseHTML rule in the new extension that matches div[data-database-block] and converts it to a deprecated-placeholder paragraph.

Verification

Commands:

  • npm run lint --prefix memento-note -- expected: 0 errors (no imports from deleted files)
  • npm run build --prefix memento-note -- expected: build succeeds, no missing module errors

Manual checks (if no CLI):

  • Open a note in a structured notebook → type /vue → confirm block appears with real notes (not Jules Verne).
  • Open a note in a plain notebook → type /vue → confirm "no schema" callout appears.
  • Open a note with a saved legacy databaseBlock → confirm note loads cleanly, no crash.
  • Switch app language to fa → confirm block layout is RTL-mirrored.