# Phase 2 — Builder Upgrade Design **Status:** Approved **Date:** 2026-02-13 **Scope:** Drag & drop field reordering, side-by-side live preview, snapshot-based undo/redo --- ## Overview Phase 2 upgrades the FormBuilderModal with three features that transform it from a basic editor into a polished builder experience. All changes are scoped to `form-builder.ts` and `styles.css`, with no new modules needed. --- ## 1. Drag & Drop Field Reordering ### Implementation Native HTML5 Drag & Drop on `.ff-builder-field` elements: - Each field gets `draggable="true"` and a drag handle (`⠿` gripper icon) in the header - `dragstart`: Stores source index in instance variable `dragSourceIndex` - `dragover`: Calculates drop position (above/below element center), shows CSS indicator - `dragleave`: Removes drop indicator - `drop`: Reorders `draft.fields` array, calls `this.render()` - Instance variable `dragSourceIndex: number | null` tracks the dragged field ### Existing Up/Down buttons remain They serve as accessibility fallback for keyboard-only users. ### CSS Classes - `.ff-drag-handle` — Gripper icon, `cursor: grab`, left of field number - `.ff-builder-field.ff-dragging` — `opacity: 0.4` during drag - `.ff-builder-field.ff-drop-above` — Blue top border indicator line - `.ff-builder-field.ff-drop-below` — Blue bottom border indicator line --- ## 2. Side-by-Side Live Preview ### Layout The `render()` method creates a two-column grid layout: ``` ┌─────────────────────────────────────────────┐ │ Form Settings (h2) │ ├──────────────────────┬──────────────────────┤ │ Editor (left) │ Preview (right) │ │ - General settings │ - Form title │ │ - Fields list │ - Rendered fields │ │ - Add field button │ - Submit button │ ├──────────────────────┴──────────────────────┤ │ Footer: Undo/Redo | Cancel | Save │ └─────────────────────────────────────────────┘ ``` ### Preview Panel - Calls existing `renderField()` from `field-renderers.ts` for each field - Fields are fully interactive (users can test how they feel) - Shows form title and a disabled pseudo-submit button - Automatically updates on every `render()` call (no separate mechanism) - Uses `this.app` for note-link and folder-picker autocomplete ### CSS Changes - `.ff-builder-layout` — `display: grid; grid-template-columns: 1fr 1fr; gap: 24px` - `.ff-builder-preview` — `border-left`, `padding-left`, sticky "Preview" header - `.ff-builder-modal` — `max-width` increased from 700px to 1100px - Footer spans full width below the grid --- ## 3. Snapshot-Based Undo/Redo ### State New instance variables on `FormBuilderModal`: ```typescript private history: FormDefinition[] = []; private historyIndex: number = 0; ``` ### Methods - `pushSnapshot()` — `structuredClone(this.draft)` pushed to history. Truncates forward history (everything after `historyIndex`). Max 30 entries. - `undo()` — Decrements `historyIndex`, restores draft from snapshot, calls `render()` - `redo()` — Increments `historyIndex`, restores draft from snapshot, calls `render()` ### When snapshots are pushed Before mutations that trigger `render()`: - Add field - Delete field - Move field (drag & drop, up/down) - Change field type - Change form mode (create/update) NOT on every keystroke — only on `change` events (blur/enter) for text inputs. ### Keyboard Shortcuts - `Ctrl+Z` / `Cmd+Z` → undo - `Ctrl+Shift+Z` / `Cmd+Shift+Z` → redo - Keydown listener on `contentEl` ### Visual Indicators - Undo (`↩`) and Redo (`↪`) buttons in footer, left-aligned (before Cancel/Save) - Buttons are disabled when at start/end of history stack ### Initialization Constructor pushes first snapshot of original form state. --- ## Files Changed | File | Change | |------|--------| | `src/ui/form-builder.ts` | Drag & drop, preview panel, undo/redo — major rewrite | | `styles.css` | DnD indicators, grid layout, preview panel, undo/redo buttons | ## Out of Scope - Undo for individual text keystrokes (browser-native handles this) - External undo manager utility - Drag & drop between forms - Preview in a separate modal/window - Collapsible preview panel