Skip to content

feat(ui,sdk,server,mcp): retire EditorJS — de-name export IR to block-native, creators emit blockdoc#100

Merged
eliotlim merged 8 commits into
mainfrom
feat/retire-editorjs
Jul 5, 2026
Merged

feat(ui,sdk,server,mcp): retire EditorJS — de-name export IR to block-native, creators emit blockdoc#100
eliotlim merged 8 commits into
mainfrom
feat/retire-editorjs

Conversation

@eliotlim

@eliotlim eliotlim commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Problem

The classic EditorJS editor was removed as an editing surface in June, but its name and shape lingered: a dead Vite prebundle + an orphaned helper + ~204 lines of dead CSS; an export intermediate-representation still named "EditorJS" (blocksToEditorJs/EditorJsOut/the .editorjs field); and the AI agent, MCP server, and plugin API still minting pages in the legacy {type,data} shape that depended on a migrator at first open. This is the "remove EditorJS altogether" cleanup. Board: Epic "Retire EditorJS" (OB-EJS S1/S2/S3); absorbs OB-364.

Solution

Three stacked, independently-reviewed phases:

  • S1 — dead-vestige deletion: removed the @editorjs/* Vite prebundle, the orphaned editorChrome.ts (0 importers), ~204 lines of dead .ce-*/.cdx-*/.codex-editor/.tc-* CSS (each class grep-verified dead), and stale docs/comments. No behavior change.
  • S2 — export IR rename: EditorJsOutExportDoc, blocksToEditorJsprojectBlocksForExport, blockSnapshotToEditorJsprojectSnapshotForExport, EditorJsBlock/RawBlock/AnyEditorJsBlockExportBlock. The persisted editorjs JSON key is deliberately UNCHANGED (documented back-compat alias) so no stored data is stranded. Exporter output is byte-identical.
  • S3 — block-native creators: the AI agent, MCP server, and plugin API now emit blockdoc directly (text stored as escaped runs — strictly safer than the old HTML-interpreted data.text); migrateEditorJsmigrateLegacyBlocks, kept as the on-open upgrade path for pre-existing legacy pages (a local-first store can't be centrally migrated).

Key files: blockeditor/exportBlocks.ts, export/documentModel.ts, export/toHtml.ts, sdk/content.ts, blockeditor/model.ts, server/ai/agent.ts, mcp/server.ts, ui/plugins/api.ts. New deps: none (the EditorJS runtime was already gone).

Before / After

Behavior Before After
Dead @editorjs Vite config / CSS / helper present (dead) removed
Export IR symbol names "EditorJS"-named block-native (ExportDoc / project… / ExportBlock)
Exporter output (MD/PDF/HTML/slides/site) byte-identical (guardrail tests)
AI / MCP / plugin new pages legacy {type,data}, migrated on open native blockdoc from birth
Legacy stored pages migrate-on-open (migrateEditorJs) migrate-on-open (migrateLegacyBlocks, renamed, kept)
MCP append_to_page on block pages refused appends (with read-back)
Persisted editorjs snapshot key present unchanged (back-compat read alias)

Test procedure

  • pnpm verify green on this integration branch (build:libs, typecheck 6/6, lint 6/6, unit sdk 158 / ui 848 / server 586, server e2e + MCP 40/40).
  • Export fidelity guardrails: export/__tests__/exportReactive.test.ts + blockExport.test.ts (byte-identical output).
  • Creator round-trip: sdk/content.test.ts + blockModel.test.ts (decode without the migrator); mcp/scripts/e2e.mts (append + read-back).
  • Legacy-page open still upgrades via migrateLegacyBlocks (BlockPageDocument load path).

Operational notes

  • No data migration and no stored-format break — the on-disk editorjs key is retained as a read alias; legacy pages upgrade lazily on open. Breaking changes are limited to internal API/type names + the public paragraphBlocks return shape (no in-repo consumers).
  • Behavior change: MCP append_to_page now writes to block pages (previously refused all, since blockeditor is the only editor) — same authz/write surface as append_blocks (security-reviewed).
  • Reviews cleared: S1 code (Quinn); S2 code (Quinn); S3 code (Quinn) + security (Sasha). Design gate not required (no rendered-output change; export byte-identical).

Footer: verify green (counts above); gates cleared — code (S1/S2/S3) + security (S3).

eliotlim and others added 8 commits July 5, 2026 13:34
Retire the legacy 'EditorJS' name from the internal export
intermediate-representation with ZERO change to exporter output:

- EditorJsOut -> ExportDoc, blocksToEditorJs -> projectBlocksForExport,
  blockSnapshotToEditorJs -> projectSnapshotForExport (exportBlocks.ts)
- EditorJsBlock / RawBlock / AnyEditorJsBlock re-declarations ->
  ExportBlock (exportAssets, documentModel, toHtml, sdk/mtime)
- Update all importers/consumers (export pipeline + BlockPageDocument +
  tests) to the new symbol names

PERSISTED FIELD: the on-disk/wire key PageSnapshot.editorjs is UNCHANGED
and now documented as a retained storage/back-compat alias; only in-memory
types/functions/vars were renamed, so old stored snapshots still read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, CSS)

- packages/app/vite.config.ts: drop the stale @editorjs/* optimizeDeps
  prebundle block (those packages are no longer dependencies) and the
  EditorJS example from the build-target comment.
- packages/ui/src/lib/editorChrome.ts: delete the file — its exports poked
  live EditorJS .ce-popover DOM and had zero importers.
- packages/ui/src/index.css: remove the dead .ce-*/.cdx-*/.codex-editor/.tc-*
  rules (verified emitted by no live source). Kept mixed rules' live
  selectors (.max-w-content, [data-placeholder], .ob-page-fonts headings)
  and all live .ob-mention/.reactive-block/.block-* rules.

No runtime behavior change; dead-code deletion only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- packages/app/vite.config.ts: drop the stale @editorjs/* optimizeDeps
  prebundle block (no longer dependencies) + the EditorJS example in the
  build-target comment.
- packages/ui/src/index.css: remove dead .ce-*/.cdx-*/.codex-editor/.tc-*
  rules (verified emitted by no live source). Kept mixed rules' live
  selectors (.max-w-content, [data-placeholder], .ob-page-fonts headings)
  and all live .ob-mention/.reactive-block/.block-* rules.

No runtime behavior change; dead-code deletion only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Text-only cleanup (no logic change):
- ARCHITECTURE.md: remove the retired EditorJS-document intro + the
  reactive/ and liveSync.ts subsections (those files no longer exist);
  reframe the block editor as the sole editor; drop the stale
  editorJsReactAdapter.ts reference; retitle the file-drop WKWebView note.
- TODOS.md: swap stale 'EditorJS block list' / editorjs-* plugin mentions.
- pageLinks.ts / emojiPicker.ts / i18n/index.ts / viewer/types.ts: clean
  stale EditorJS mentions in the live files' comments.

Left untouched (out of scope): the PageSnapshot.editorjs field and its
SDK/MCP export/import/migration machinery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-on-read

Creators emit blockdoc from birth so NO new legacy (no-blockdoc) pages are
made; the renamed migrator stays as the on-open path for pre-existing pages.

- sdk/content.ts: textSnapshot / paragraphBlocks / appendTextToSnapshot now
  produce block-native blockdoc ({editor:'blocks', blockdoc:{blocks}}), JSON-
  projection form (no CRDT update) that decodeSnapshot rebuilds without the
  migrator; appendTextToSnapshot handles block pages (merge into blockdoc,
  drop stale update) and keeps pre-existing LEGACY pages in their stored
  editorjs shape (no dialect mixing, no data loss); returns non-null now
- callers: agent.ts create page/db-host, mcp create_page, plugin pages.create
  route through textSnapshot; mcp append_to_page drops the dead block-page guard
- model.ts: migrateEditorJs -> migrateLegacyBlocks, EditorJsBlock -> LegacyBlock
  (kept as migrate-on-open for legacy stored snapshots); re-exports + call site
  (BlockPageDocument) + blockModel.test.ts updated
- tests: new sdk content.test.ts (creator shape + legacy-append), ui round-trip
  proving created blockdoc decodes without migrateLegacyBlocks, mcp e2e now
  asserts append_to_page works on a block page (read-back)

S2 back-compat intact: persisted editorjs stays readable; migrator still READS
legacy editorjs, creators WRITE blockdoc. Sanitizer (bookfile.ts) untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-endorsed follow-up to S1 (comment/doc-only, no logic change):
- index.css .block-callout placeholder comment: drop the EditorJS/.ce-paragraph
  reference, keep the 'why' (custom contenteditable needs an explicit placeholder).
- index.css .block-button__cta doubled-class comment: the EditorJS .ce-block a
  rationale is moot; reword to the truthful intent (specificity bump so a CTA
  never inherits link-underline). Doubled class kept (de-doubling is behavior).
- TODOS.md T6: fix the dangling 'The plugin' antecedent left after the Pros
  reword; also drop the dead liveSync reference in the same sentence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app.book.pub Ready Ready Preview, Comment Jul 5, 2026 8:37am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant