Commit Graph

4 Commits

Author SHA1 Message Date
923532f5f7 Add block plugins: plugin_block node with sandboxed rendering and editing (#76)
All checks were successful
CD / Promote to Int (push) Successful in 9s
CI / Auth e2e pack (push) Successful in 3m56s
CI / Import/export fidelity gate (push) Successful in 43s
CI / Lint, typecheck, test (push) Successful in 2m53s
CI / Build container images (push) Has been skipped
CD / Build and push images (push) Successful in 3m9s
CD / Smoke tests against Test (push) Successful in 1m9s
CD / Deploy to Test (push) Successful in 12s
The powerful end of the plugin spectrum (ADR 0008 extension point `block`):

- Shared schema: the reserved `plugin_block` node — a block atom carrying
  pluginId, blockType, and the block data as a JSON object. Its DOM shape
  round-trips the full state in data attributes (clipboard-safe), markdown
  maps to a reserved fence (```dorfteich-plugin <plugin>/<type> + data
  JSON body, fence-escalated when the payload contains backticks), and the
  content-cache HTML renders a data-carrying neutral placeholder until the
  export fallbacks land (#79).
- Editor: a React NodeView hosts the #73 sandbox — render lifecycle on
  mount, an edit affordance switching the frame to the plugin's edit mode,
  and the blockData capability persisting through node attrs (a normal
  editor transaction, so Yjs replicates it; writes are refused on read-only
  editors, and the plugin's own attr echo is suppressed so its edit UI
  never resets mid-typing). Collaborator changes re-invoke the current
  lifecycle, keeping frames live. The page surface (ids, openPage) flows
  through a React context like the wikilink pattern; the toolbar gets an
  insert picker fed from the active code plugins' block extension points.
- Fallback: GET /plugins/:id/fallback resolves the manifest fallback from
  the stored snapshot — it survives uninstall as a tombstone, image
  fallbacks degrade to neutral once assets are gone. Signed-in only.
- e2e plugin-blocks.spec.ts covers all four acceptance criteria: insert →
  edit → reload round-trip, live two-user collab, disable → fallback →
  re-enable without document mutation, and copy/paste within and across
  pages (the markdown clipboard carries the reserved fence).

getBlock (cross-page block embedding) stays deferred as in #74: the
schema has no per-block ids yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-11 12:45:14 +02:00
2e96173d44 Add the section container node to the document model (#75, part 1)
Some checks failed
CD / Build and push images (push) Successful in 3m15s
CD / Deploy to Test (push) Successful in 9s
CI / Import/export fidelity gate (push) Successful in 47s
CI / Auth e2e pack (push) Successful in 4m12s
CD / Smoke tests against Test (push) Successful in 1m4s
CD / Promote to Int (push) Successful in 10s
CI / Build container images (push) Has been skipped
CI / Warm action cache (push) Successful in 10s
CI / Lint, typecheck, test (push) Failing after 1m27s
Document-model foundation for section-style plugins (ADR 0008 kind
section_style): a `section` block node carrying `pluginId` + `styleId`,
rendered as `<div class="dt-section dt-style-<pluginId>-<styleId>">` so a
plugin's sanitized, scoped CSS applies and — when the plugin is gone —
the class matches nothing and the content stays intact with neutral
styling. Round-trips to a Pandoc-style fenced div
(`::: {data-section-style="<pluginId>/<styleId>"}`), with a markdown-it
block rule that supports nesting.

This is the schema/markdown/HTML half of #75; the editor UI (wrap/style
picker/unwrap), the `section-styles-basic` reference plugin, per-pond CSS
injection, and PDF/office export styling follow in part 2. The node is
additive and backward-compatible (existing documents have none).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-11 10:00:39 +02:00
7244b89215 Add wikilink node with autocomplete (#46)
All checks were successful
CD / Build and push images (push) Successful in 3m2s
CI / Lint, typecheck, test (push) Successful in 2m15s
CI / Auth e2e pack (push) Successful in 2m42s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m13s
CD / Promote to Int (push) Successful in 11s
Introduce Obsidian-style `[[page links]]` (ADR 0004).

- shared: reserved `wikilink` inline atom in the editor schema (attrs
  `targetSlug`, optional `displayText`); markdown mapping `[[slug]]` /
  `[[slug|text]]` via a markdown-it inline rule + serializer node; plain-text
  and HTML derivation include the shown text. Round-trip + parse unit tests.
- web:
  - `Wikilink` node extension with a React NodeView: shows the explicit
    display text or the target's current title (so a rename updates the link),
    renders a missing target as a dashed phantom with a tooltip, navigates on
    click in read mode.
  - `[[` autocomplete popup (`WikilinkAutocomplete`), dependency-free: filters
    the pond's pages as you type with a create-new-page hint for misses,
    Enter/click inserts the node and removes the typed `[[query`; ↑/↓/Enter/Esc
    intercepted in the capture phase so ProseMirror does not act on them.
  - `WikilinkContext` provides the pond's pages (slug→title) for live
    resolution and the autocomplete, populated by the page editor.
  - i18n `editor.wikilink.*` (de + en); wikilink + phantom + popup styles.
- e2e `wikilink.spec.ts` (new CI pack): type `[[`, autocomplete filters and
  inserts a working link that resolves the target title and persists across a
  reload. Phantom → live resolution on page creation is verified in #47.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY
2026-07-09 12:42:01 +02:00
b89aa6bed0 Add editor document schema in packages/shared (#24)
Some checks failed
CD / Build and push images (push) Successful in 1m50s
CI / Lint, typecheck, test (push) Failing after 52s
CI / Auth e2e pack (push) Successful in 1m47s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m5s
CD / Promote to Int (push) Successful in 10s
ProseMirror schema (headings 1-4, lists incl. task lists, blockquote,
code block, tables via prosemirror-tables, images, hard breaks; bold/
italic/code/strikethrough/link marks) plus docToMarkdown, markdownToDoc,
docToPlainText, docToHtml, and extractOutline built on it. Markdown
parsing extends markdown-it's default preset with a token-stream
transform for GFM task lists and table-cell paragraph wrapping.
docToHtml hand-rolls escaping and link-protocol allowlisting with zero
DOM dependencies, so it runs in the API/collab server as well as the
browser.

Node names `wikilink` and `plugin_block` are reserved for later stories.

Closes #24
2026-07-05 21:59:12 +02:00