You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10 KiB

Divi-Like Visual Builder Plan

Overview

Transform the current PDF editor into a Divi-inspired visual builder with intuitive inline controls, drag-and-drop functionality, and seamless element manipulation.

Current State vs Target State

Current State

  • Toolbar-based insertion from sidebar
  • Basic selection with outline highlighting
  • No inline insertion controls
  • No drag-and-drop
  • No hover action menus

Target State (Divi-like)

  • Inline '+' buttons between elements for contextual insertion
  • Hover action menus with settings, duplicate, delete, move
  • Full drag-and-drop support with visual feedback
  • Ghost previews and drop zone indicators

Feature 1: Inline Insertion System

Description

Add '+' buttons that appear between elements and at the end of containers, allowing users to insert new elements at specific positions.

Visual Design

┌─────────────────────────────────────┐
│ Section                             │
│ ┌─────────────────────────────────┐ │
│ │ Row                             │ │
│ │ ┌───────────┐ ┌───────────┐    │ │
│ │ │ Column    │ │ Column    │    │ │
│ │ │ [Text]    │ │ [Image]   │    │ │
│ │ │           │ │           │    │ │
│ │ │    [+]    │ │    [+]    │    │ │
│ │ └───────────┘ └───────────┘    │ │
│ │              [+]                │ │ ← Add column button
│ └─────────────────────────────────┘ │
│              [+]                    │ ← Add row button
└─────────────────────────────────────┘

Components Needed

InsertButton.svelte

interface Props {
  position: 'before' | 'after' | 'inside'
  parentId: string
  index: number
  allowedTypes: NodeType[]
  oninsert: (type: NodeType, parentId: string, index: number) => void
}

InsertionPopup.svelte

  • Appears when clicking '+'
  • Shows allowed element types based on context
  • Respects hierarchy rules

Behavior

  1. Insert buttons appear on hover over containers
  2. Clicking '+' shows a popup with allowed types
  3. Selecting a type inserts at that exact position
  4. Buttons are context-aware (only show valid options)

Feature 2: Hover Action Menu

Description

Display a floating toolbar when hovering over elements with quick actions.

Visual Design

┌─────────────────────────────────────┐
│ [⚙️] [📋] [🗑️] [⋮]  ← Action menu  │
├─────────────────────────────────────┤
│                                     │
│         Element Content             │
│                                     │
└─────────────────────────────────────┘

Actions

Icon Action Description
⚙️ Settings Open properties panel for this element
📋 Duplicate Clone the element
🗑️ Delete Remove the element
⋮ More Expand for additional options (move up/down, copy, paste)

Components Needed

ElementActions.svelte

interface Props {
  nodeId: string
  nodeType: NodeType
  position: { x: number; y: number }
  onsettings: () => void
  onduplicate: () => void
  ondelete: () => void
  onmoveup: () => void
  onmovedown: () => void
}

Behavior

  1. Menu appears on element hover (with slight delay)
  2. Menu positioned at top-right of element
  3. Actions are context-aware (some may be disabled)
  4. Menu disappears when mouse leaves element

Feature 3: Drag and Drop System

Description

Allow elements to be reordered by dragging them to new positions.

Visual Design

Before Drop:
┌─────────────┐
│  Element A  │ ← Being dragged (ghost)
└─────────────┘
┌─────────────┐
│  Element B  │
└─────────────┘
┌─────────────┐
│  Element C  │
└─────────────┘
        ↓
    Drop zone indicator (blue line)

After Drop:
┌─────────────┐
│  Element B  │
└─────────────┘
┌─────────────┐
│  Element A  │ ← Dropped here
└─────────────┘
┌─────────────┐
│  Element C  │
└─────────────┘

Components Needed

Draggable.svelte

interface Props {
  nodeId: string
  disabled?: boolean
  ondragstart: (nodeId: string, event: DragEvent) => void
  ondragend: (nodeId: string, event: DragEvent) => void
}

DropZone.svelte

interface Props {
  parentId: string
  index: number
  acceptedTypes: NodeType[]
  ondrop: (draggedId: string, parentId: string, index: number) => void
}

Behavior

  1. Click and hold to start drag (with drag handle icon)
  2. Element becomes semi-transparent ghost
  3. Drop zones appear as blue indicator lines
  4. Invalid drop zones shown in red
  5. Release to drop at indicated position
  6. Escape cancels the drag operation

Feature 4: Visual Feedback System

Description

Provide clear visual feedback during all interactions.

Feedback Types

Hover State

  • Outline brightens
  • Action menu appears
  • Cursor changes to pointer

Selection State

  • Solid border (current)
  • Resize handles (future)

Drag State

  • Original element becomes ghost (50% opacity)
  • Drag preview follows cursor
  • Drop zones highlighted

Drop Zone State

  • Blue line indicator for valid drops
  • Red indicator for invalid drops
  • Pulsing animation for active zone

Insert Button State

  • Fade in on container hover
  • Scale up on button hover
  • Tooltip showing 'Add [element type]'

Implementation Architecture

New File Structure

src/lib/editor/
├── canvas/
│   ├── Canvas.svelte
│   ├── NodeRenderer.svelte
│   ├── Page.svelte
│   ├── InsertButton.svelte          # NEW
│   ├── InsertionPopup.svelte        # NEW
│   ├── ElementActions.svelte        # NEW
│   ├── DraggableWrapper.svelte      # NEW
│   └── DropZone.svelte              # NEW
├── toolbar/
│   ├── Toolbar.svelte
│   ├── BlockButton.svelte
│   └── ElementQuickMenu.svelte      # NEW
├── stores/
│   ├── drag.ts                      # NEW - drag state store
│   └── ...
└── Editor.svelte

Store Additions

dragStore (new)

interface DragState {
  isDragging: boolean
  draggedNodeId: string | null
  draggedNodeType: NodeType | null
  dropTargetId: string | null
  dropIndex: number | null
  isValidDrop: boolean
}

Event Flow Diagram

sequenceDiagram
    participant User
    participant NodeRenderer
    participant ElementActions
    participant Editor
    participant DocumentStore

    User->>NodeRenderer: Hover over element
    NodeRenderer->>ElementActions: Show action menu
    User->>ElementActions: Click drag handle
    ElementActions->>Editor: Start drag
    Editor->>DocumentStore: Set drag state
    User->>NodeRenderer: Drag over drop zone
    NodeRenderer->>Editor: Check valid drop
    Editor->>DocumentStore: Update drop target
    User->>NodeRenderer: Drop
    NodeRenderer->>Editor: Complete drop
    Editor->>DocumentStore: Move node
    DocumentStore->>NodeRenderer: Re-render

Implementation Phases

Phase 1: Inline Insertion

  1. Create InsertButton.svelte component
  2. Create InsertionPopup.svelte with element selector
  3. Modify NodeRenderer to show insert buttons
  4. Wire up insertion logic with correct positioning

Phase 2: Hover Actions

  1. Create ElementActions.svelte component
  2. Add hover detection with delay
  3. Implement duplicate, delete, settings actions
  4. Add move up/down functionality

Phase 3: Drag and Drop

  1. Create drag store for state management
  2. Create DraggableWrapper.svelte
  3. Create DropZone.svelte
  4. Implement drop validation logic
  5. Add visual feedback during drag

Phase 4: Polish

  1. Add animations and transitions
  2. Implement keyboard shortcuts
  3. Add undo/redo support for all operations
  4. Mobile touch support (future)

Technical Considerations

Performance

  • Use CSS transforms for drag preview (GPU accelerated)
  • Debounce hover events
  • Virtualize insertion popups (only one open at a time)

Accessibility

  • Keyboard navigation for action menus
  • Focus management during drag
  • ARIA labels for all interactive elements

Browser Compatibility

  • Use HTML5 Drag and Drop API
  • Fallback for touch devices
  • Handle Safari quirks

Files to Modify

Existing Files

File Changes
src/lib/editor/Editor.svelte Add drag state, coordinate drop logic
src/lib/editor/canvas/NodeRenderer.svelte Add action menu, insert buttons, drag handlers
src/lib/stores/index.ts Export new drag store

New Files to Create

File Purpose
src/lib/editor/canvas/InsertButton.svelte Inline insertion trigger
src/lib/editor/canvas/InsertionPopup.svelte Element type selector
src/lib/editor/canvas/ElementActions.svelte Hover action toolbar
src/lib/editor/canvas/DropZone.svelte Drop target indicator
src/lib/stores/drag.ts Drag state management

Success Criteria

  1. ✅ Users can insert elements at specific positions using inline buttons
  2. ✅ Hover menus provide quick access to common actions
  3. ✅ Drag and drop works smoothly with visual feedback
  4. ✅ All operations respect hierarchy constraints
  5. ✅ All operations are undoable
  6. ✅ Keyboard users can perform all actions

Powered by TurnKey Linux.