Description
When editing fields inside a Portable Text block (e.g., changing a headline in a Hero block, updating an image in a CTA block), the auto-save mechanism does not fire. The editor does not detect the change as dirty.
However, if you then make any edit to the surrounding Portable Text content (e.g., add a <p> tag, type a space in a text block), auto-save fires and persists both the text change and the previously-undetected block field changes.
This suggests that block field edits don't propagate as TipTap document changes, so onUpdate never fires and isDirty remains false.
Steps to reproduce
- Open a content item that has a
portableText field with custom blocks (e.g., a page with Hero, Feature, or CTA blocks)
- Click into a block and edit one of its fields (change a title, swap an image, toggle a setting)
- Wait for auto-save (2+ seconds of inactivity)
- Expected: Auto-save fires, "Saved" indicator appears
- Actual: Nothing happens. The editor does not detect the change. No save request is sent.
Workaround: Make any edit to the portable text content outside the block (add/remove a character in a paragraph). This triggers TipTap's onUpdate, which serializes the entire document including the modified block data, and auto-save fires normally.
Analysis
Based on reading ContentEditor.tsx:
- Auto-save uses
serializeEditorState() to compare currentData vs lastSavedData
isDirty only becomes true when the serialized form state changes
- For
portableText fields, changes propagate via TipTap's onUpdate → onChange → handleFieldChange → setFormData
- Block fields are rendered as custom TipTap node views. When their internal data (stored as node attributes) changes, TipTap may not consider this a document-level change — the node type, position, and structure haven't changed, only its attributes
- Without
onUpdate firing, the PortableTextEditor's onChange callback never executes, so formData never updates, isDirty stays false, and auto-save never triggers
Suggested fix
The block node view (or the field widget renderer inside it) should call editor.commands.updateAttributes() or dispatch a TipTap transaction when block field values change. This would make TipTap recognize the attribute change as a document update, triggering onUpdate → onChange → auto-save.
Alternatively, block field changes could bypass TipTap entirely and directly call the parent handleFieldChange to update formData for the portable text field with the new serialized document.
Environment
- EmDash version: 0.13.0
- Astro adapter:
@emdash-cms/cloudflare
- Platform: Cloudflare Workers (local dev via wrangler/miniflare)
- Browser: Chromium-based (reproducible across browsers)
Related issues
Description
When editing fields inside a Portable Text block (e.g., changing a headline in a Hero block, updating an image in a CTA block), the auto-save mechanism does not fire. The editor does not detect the change as dirty.
However, if you then make any edit to the surrounding Portable Text content (e.g., add a
<p>tag, type a space in a text block), auto-save fires and persists both the text change and the previously-undetected block field changes.This suggests that block field edits don't propagate as TipTap document changes, so
onUpdatenever fires andisDirtyremains false.Steps to reproduce
portableTextfield with custom blocks (e.g., a page with Hero, Feature, or CTA blocks)Workaround: Make any edit to the portable text content outside the block (add/remove a character in a paragraph). This triggers TipTap's
onUpdate, which serializes the entire document including the modified block data, and auto-save fires normally.Analysis
Based on reading
ContentEditor.tsx:serializeEditorState()to comparecurrentDatavslastSavedDataisDirtyonly becomestruewhen the serialized form state changesportableTextfields, changes propagate via TipTap'sonUpdate→onChange→handleFieldChange→setFormDataonUpdatefiring, thePortableTextEditor'sonChangecallback never executes, soformDatanever updates,isDirtystays false, and auto-save never triggersSuggested fix
The block node view (or the field widget renderer inside it) should call
editor.commands.updateAttributes()or dispatch a TipTap transaction when block field values change. This would make TipTap recognize the attribute change as a document update, triggeringonUpdate→onChange→ auto-save.Alternatively, block field changes could bypass TipTap entirely and directly call the parent
handleFieldChangeto updateformDatafor the portable text field with the new serialized document.Environment
@emdash-cms/cloudflareRelated issues