|
| 1 | +# Annotate DocGen — Pretext Scanline Reflow Engine |
| 2 | + |
| 3 | +- New `{{Annotate:}}` DocGen tag with canvas-based freehand annotation overlay |
| 4 | +- New `@text:` source mode renders text on-canvas with Pretext-style scanline reflow |
| 5 | +- Scanline engine (`reflowText`, `getFreeIntervals`) samples 1px pixel rows from an offscreen mask canvas to compute per-line free intervals |
| 6 | +- Real-time text reflow: as user draws, each stroke updates the mask and text re-routes live around strokes |
| 7 | +- `layoutNextLine()` pattern used per-row: per-line width narrows when a stroke occupies that row's x-range |
| 8 | +- Offscreen mask canvas approach: strokes drawn at full resolution, scanline reads `getImageData(0, y, width, 1)` — O(width) per row, ~0.5–1.5ms total |
| 9 | +- Added `↩ Undo` button with stroke history stack (Ctrl+Z equivalent) |
| 10 | +- Added `🗑 Clear` button (no confirm dialog) with undo stack support |
| 11 | +- Added `📥 PNG` button — exports composite canvas (strokes + text) as PNG download |
| 12 | +- Added `📖 Present` button — calls `M.setViewMode('preview')` to hide editor and enter live reading/drawing mode |
| 13 | +- `Present` auto-scrolls annotation card into view after mode switch |
| 14 | +- Removed blocking `confirm()` dialog from Clear; instant clear with undo restore |
| 15 | +- Canvas auto-resizes to match `ann-source-text-reflow` element height or defaults to 320px |
| 16 | +- `redrawAll` handles dual-layer rendering: text layer first, strokes on top |
| 17 | +- Fixed: `data-text` attribute stripped by DOMPurify — added `data-text`, `data-reflow` to `ADD_ATTR` whitelist in `renderer.js` |
| 18 | +- Fixed: Text now stored as hidden `<span class="ann-reflow-text">` textContent inside the reflow div — textContent always survives DOMPurify regardless of attribute whitelist |
| 19 | +- Fixed: `initCanvas` reads from `textSpan.textContent` first (bulletproof), falls back to `dataset.text` |
| 20 | +- Fixed: `M.insertAtCursor` replaced with `M.setViewMode('preview')` for the insert action — annotation stays live and drawable instead of becoming a static image |
| 21 | +- Fixed: CORS/SecurityError on external images handled with try/catch and fallback to annotation-only export |
| 22 | +- Fixed: Image insertion uses `gen-img:ID` registry pattern (same as `draw-docgen.js`) to bypass DOMPurify's stripping of `data:` URLs |
| 23 | +- Added `.ann-present-btn` CSS with green gradient, `font-weight: 600`, and hover transform |
| 24 | +- Added `.ann-reflow-text` CSS: visually hidden span (position absolute, 1×1px clip) so stored text is invisible but accessible to canvas reader |
| 25 | +- New `public/pretext-reflow-demo.html` — 4-tab interactive demo: Float Image, Draw Exclusion, Both Together, How It Works |
| 26 | +- Float Image tab: image float left/right with width %, top offset sliders, image picker (picsum.photos), and own-image upload |
| 27 | +- Draw Exclusion tab: side-by-side DOM (text buried) vs Pretext (scanline reflow) comparison |
| 28 | +- Both Together tab: image float + freehand strokes combined, both exclude text simultaneously |
| 29 | +- How It Works tab: annotated `layoutNextLine()` code explanation with image and scanline approach |
| 30 | +- New `css/annotate-docgen.css` with full card UI, toolbar, color swatches, size slider, dark mode |
| 31 | +- New `js/annotate-docgen.js` (~710 lines): parser, canvas init, scanline engine, stroke management, export, present |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## Summary |
| 36 | + |
| 37 | +Integrates a real-time, pixel-precise Pretext-style text reflow engine into the `{{Annotate:}}` DocGen tag. Text flows live around freehand annotations using a scanline mask canvas algorithm (O(width) per row, ~0.5–1.5ms/frame). The "Present" button transitions TextAgent to preview-only mode, keeping the annotation card fully drawable while reading. A companion interactive demo (`pretext-reflow-demo.html`) illustrates all three reflow scenarios: image float, freehand, and both combined. |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## 1. Annotate DocGen Tag — New `{{Annotate:}}` tag |
| 42 | + |
| 43 | +**Files:** `js/annotate-docgen.js`, `css/annotate-docgen.css` |
| 44 | +**What:** Full DocGen card with toolbar (pen/highlighter/eraser/line/arrow/rect/circle), color swatches, size slider, canvas overlay over any `@text:`, `@img:`, or `@url:` source. Undo/Clear/PNG/Present buttons in header. |
| 45 | +**Impact:** Authors can write `{{Annotate: Title @text: body text}}` in markdown and get a live, interactive annotation canvas with real-time Pretext text reflow. |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +## 2. Scanline Reflow Engine |
| 50 | + |
| 51 | +**Files:** `js/annotate-docgen.js` (`reflowText`, `getFreeIntervals`, `buildMask`) |
| 52 | +**What:** Offscreen mask canvas accumulates stroke paths. Per text row, `getImageData(0, y, width, 1)` scans a single pixel row for occupied x-ranges. `getFreeIntervals` returns free segments. `reflowText` packs words into free intervals using `canvas.measureText()` for width arithmetic — exactly the Pretext `layoutNextLine()` pattern. |
| 53 | +**Impact:** Text wraps with pixel precision around any freehand shape drawn by the user. Performance: ~0.5–1.5ms per full reflow at typical canvas sizes. |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +## 3. DOMPurify Fix — `data-text` Whitelist + Hidden Span |
| 58 | + |
| 59 | +**Files:** `js/renderer.js`, `js/annotate-docgen.js`, `css/annotate-docgen.css` |
| 60 | +**What:** `data-text` and `data-reflow` added to `ADD_ATTR` in `renderer.js`. Additionally, text now stored inside a hidden `<span class="ann-reflow-text">` (position absolute, 1×1px clip) as textContent — survives DOMPurify with zero whitelisting needed. `initCanvas` reads textContent first, `dataset.text` as fallback. |
| 61 | +**Impact:** Fixed core bug where `@text:` content was silently dropped by DOMPurify, leaving a blank canvas with no text rendered. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +## 4. Present Mode — Live Reading + Drawing |
| 66 | + |
| 67 | +**Files:** `js/annotate-docgen.js`, `css/annotate-docgen.css` |
| 68 | +**What:** "📖 Present" button replaces "Insert". Calls `M.setViewMode('preview')` to hide the editor and expand preview to full width. Annotation card remains a live canvas — user reads and draws simultaneously. Falls back to manual CSS hide of `.editor-pane` if `M.setViewMode` is unavailable. |
| 69 | +**Impact:** Completes the author workflow: write → annotate → present. The annotation is never "frozen" as a static image — it stays interactive. |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | +## 5. Pretext Reflow Demo |
| 74 | + |
| 75 | +**Files:** `public/pretext-reflow-demo.html` |
| 76 | +**What:** Self-contained 4-tab demo showing: (1) float image with `layoutNextLine()` per-line width narrowing, (2) freehand scanline reflow vs DOM comparison, (3) image + strokes combined, (4) API explainer. Image picker uses picsum.photos; own-image upload supported. Performance metrics shown live. |
| 77 | +**Impact:** Demonstrates all three `layoutNextLine()` reflow patterns in an interactive standalone page. Accessible at `/pretext-reflow-demo.html`. |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## Files Changed (6 total) |
| 82 | + |
| 83 | +| File | Lines Changed | Type | |
| 84 | +|------|:---:|------| |
| 85 | +| `js/annotate-docgen.js` | +710 | New module — full Annotate DocGen | |
| 86 | +| `css/annotate-docgen.css` | +340 | New stylesheet — card, toolbar, reflow badge | |
| 87 | +| `js/renderer.js` | +2 | DOMPurify ADD_ATTR: added `data-text`, `data-reflow` | |
| 88 | +| `src/main.js` | +5 | Phase lazy-load registration for annotate-docgen | |
| 89 | +| `public/pretext-reflow-demo.html` | +720 | New interactive Pretext reflow demo | |
| 90 | +| `public/annotate-example.md` | +12 | Example markdown using `{{Annotate:}}` tag | |
0 commit comments