Pdf Processor
Postmortem: Four Discarded Plans for Editing Structured HTML in Place
TLDR
Building an in-place WYSIWYG block editor on top of structured HTML requires navigating browser collisions betweencontenteditable, CSS Grid layout rules, and HTML5 drag events. Four separate implementation iterations were discarded before arriving at a stable architecture: right-click intercept context menus, double-click text selection overrides, inline DOM drag handles, and global Monaco window bundle dependencies.
| Implementation Iteration | Architectural Assumption | Browser Collision | Resulting Fix |
|---|---|---|---|
| Plan 1: Right-Click Intercept | Context menu handles edit triggers | Overwrote image insertion menu | Integrated actions into existing menu |
| Plan 2: Double-Click Trigger | Double-click triggers edit dialog | Collided with contenteditable text selection | Removed double-click trigger |
| Plan 3: Flow Drag Handles | Drag handles prepended as HTML <span> | Became CSS Grid items, breaking layout | Switched to position: absolute handles |
| Plan 4: Monaco Global Window | window.monaco available globally | Vite ES bundler tree-shook window global | Imported monaco-editor ES module |
Technical lessons & retrospective findings
1. CSS grid layout isolation
Prepending inline DOM handles as flow children inside CSS Grid containers causes handles to act as grid items, altering document layout during editing passes.Remediation: Apply position: relative to grid containers and position drag handles using position: absolute:
/ Maintain identical visual layout across Edit and Selection modes /
.pdf-zone, .pdf-region {
position: relative;
}
.pdf-drag-handle { position: absolute; top: 4px; left: 4px; z-index: 100; }
2. Browser event behavior mapping
contenteditable surfaces manage word selection, cursor focus, and input bubbling natively. Custom drag-and-drop overlays must toggle contentEditable = "false" during selection and drag modes to prevent event collisions.
export function setEditorSelectionMode(containerElement, isSelectionActive) {
containerElement.classList.toggle('selection-mode', isSelectionActive);
// Disable contentEditable during drag mode to prevent native selection collisions
containerElement.contentEditable = isSelectionActive ? 'false' : 'true';
}
Rule of thumb: Map native browser behaviors (contenteditable, CSS Grid item auto-placement) before building interactive DOM overlay layers.
Read this post in the full Engineering Journal →