Files
CGW-Quote-Builder/planning/VISUAL_NAVIGATION_EDITOR.md
T
2026-04-11 00:04:09 -05:00

80 KiB
Raw Blame History

Visual Navigation Editor for navigation.json

Concept Overview

A drag-and-drop visual interface for creating and editing the navigation flow, similar to Talend's workflow editor. This would replace manual JSON editing with an intuitive node-based editor.

Similar Systems & Industry Parallels

This visual navigation editor concept follows proven patterns from industrial automation and visual programming tools:

Node-RED (Industrial IoT & Automation)

  • Flow-based programming for IoT devices and industrial automation
  • Drag-and-drop nodes with input/output connections
  • Sub-flows: Reusable flow components that can be nested and shared
  • Used in manufacturing, building automation, industrial control systems
  • Key Feature: Double-click sub-flow to drill down and edit internal logic

LabVIEW (National Instruments - Industrial Control)

  • Visual programming for measurement and control systems
  • SubVIs: Reusable virtual instruments (similar to functions/methods)
  • Used in aerospace, automotive testing, manufacturing quality control
  • Hierarchical design: Complex systems broken into manageable sub-components
  • Key Feature: Icon view shows high-level, double-click to edit internal diagram

PLC Ladder Logic (Industrial Machine Control)

  • Visual programming for Programmable Logic Controllers
  • Function Blocks: Reusable logic blocks for common operations
  • Used in factory automation, assembly lines, industrial machinery
  • Modular design allows sharing common patterns across machines

Unreal Engine Blueprints (Game Development)

  • Visual scripting for game logic
  • Blueprint Functions/Macros: Reusable logic components
  • Collapse to Function: Select nodes, create reusable component
  • Used by game developers worldwide for complex interactive systems

Power Automate / Microsoft Flow (Business Process Automation)

  • No-code workflow automation
  • Visual flow designer with connectors and conditions
  • Used for business process automation in enterprises

Why This Pattern Works

  • Visual Clarity: See the entire flow at a glance
  • Complexity Management: Nest/collapse to manage large systems
  • Reusability: Build once, use everywhere
  • Collaboration: Team members can understand and contribute
  • Lower Barrier: Non-programmers can build complex logic

Your navigation editor would bring these industrial-strength patterns to product configuration workflows!


🏗️ Foundational Architecture: Component-Based Rendering System

Overview

The navigation system will use a component-based architecture where each question node contains an ordered array of components that render sequentially from top to bottom. This replaces the current type-based system (inputType: "button" vs inputType: "form") with a more flexible, composable approach.

Key Principle

Components set variables, navigation controls flow.

  • Toggle/Field components: Set variables in the application state
  • Navigation components: Control which page appears next
  • Notes/Separator components: Provide information and visual structure

Components can be combined in any order, allowing for maximum flexibility without mixing concerns (e.g., navigation buttons remain separate from data input controls).

Current System vs. Component-Based System

Current System (Limited)

{
  "q-door-type": {
    "type": "question",
    "inputType": "button",  // ← Only navigation OR only form
    "title": "What type of door?",
    "answers": [...]
  },
  "q-dimensions": {
    "type": "question",
    "inputType": "form",  // ← Can't mix with navigation
    "measurementType": {...},
    "fields": [...]
  }
}

Limitations:

  • inputType: "button" = Only navigation buttons
  • inputType: "form" = Only input fields (no navigation buttons)
  • Can't have multiple toggle groups
  • Navigation always at bottom
  • Hard to add new input types

Component-Based System (Flexible)

{
  "q-configure-door": {
    "type": "question",
    "title": "Configure Your Door",
    "subtitle": "Set your preferences and continue",
    "components": [
      {
        "type": "toggle",
        "variable": "includeHardware",
        "label": "Include Hardware?",
        "options": [
          { "value": "yes", "label": "Yes", "default": true },
          { "value": "no", "label": "No" }
        ]
      },
      {
        "type": "fields",
        "label": "Installation Details",
        "fields": [
          { "name": "installDate", "label": "Preferred Installation Date", "type": "date" },
          { "name": "notes", "label": "Special Instructions", "type": "text", "optional": true }
        ]
      },
      {
        "type": "toggle",
        "variable": "measurementType",
        "label": "Measurement Type",
        "options": [
          { "value": "opening-size", "label": "Opening Size", "icon": "📏", "default": true },
          { "value": "tip-to-tip", "label": "Tip-to-tip", "icon": "📏" }
        ]
      },
      {
        "type": "fields",
        "fields": [
          { "name": "width", "label": "Width (inches)", "type": "number", "required": true },
          { "name": "height", "label": "Height (inches)", "type": "number", "required": true }
        ]
      },
      {
        "type": "navigation",
        "answers": [
          { "caption": "Continue", "next": "results", "primary": true },
          { "caption": "Skip Optional Settings", "next": "q-accessories" }
        ]
      },
      {
        "type": "notes",
        "content": "<p><strong>Tip:</strong> For accurate measurements, refer to our <a href='#'>measuring guide</a>.</p>"
      }
    ]
  }
}

Advantages:

  • Mix any components in any order
  • Multiple toggle groups in same question
  • Multiple field groups in same question
  • Navigation can be anywhere (top, middle, bottom)
  • Easy to add new component types
  • Clean separation of concerns
  • Editor-friendly (drag components to reorder)

Component Type Reference

1. Toggle Component

Sets a variable by presenting mutually exclusive options (radio button style).

{
  "type": "toggle",
  "variable": "rushOrder",
  "label": "Rush Order?",
  "options": [
    { "value": "yes", "label": "Rush (3-5 days)", "icon": "⚡", "default": false },
    { "value": "no", "label": "Standard (2-3 weeks)", "icon": "📦", "default": true }
  ]
}

Properties:

  • variable (string, required): Variable name to store selection
  • label (string, optional): Section heading
  • options (array, required): Available choices
    • value: Stored value
    • label: Display text
    • icon: Optional emoji/icon
    • default: Whether this is the default selection
    • disabled: Whether this option is disabled

2. Fields Component

Group of input fields for collecting user data.

{
  "type": "fields",
  "label": "Product Dimensions",
  "fields": [
    { 
      "name": "width", 
      "label": "Width (inches)", 
      "type": "number", 
      "required": true,
      "min": 12,
      "max": 120,
      "placeholder": "26"
    },
    { 
      "name": "height", 
      "label": "Height (inches)", 
      "type": "number", 
      "required": true,
      "placeholder": "81"
    }
  ]
}

Properties:

  • label (string, optional): Group heading
  • fields (array, required): Input fields
    • name: Variable name
    • label: Field label
    • type: Input type (text, number, date, email, tel, textarea)
    • required: Whether field is required
    • placeholder: Placeholder text
    • min, max: Constraints for number/date fields
    • pattern: Regex validation pattern

3. Navigation Component

Navigation buttons to control flow to next question/page.

{
  "type": "navigation",
  "answers": [
    { 
      "caption": "Continue", 
      "next": "results",
      "primary": true,
      "filter": { "configComplete": true }
    },
    { 
      "caption": "Add Accessories", 
      "next": "q-accessories"
    },
    { 
      "caption": "Start Over", 
      "next": "start",
      "style": "secondary"
    }
  ]
}

Properties:

  • answers (array, required): Navigation buttons
    • caption: Button text (can include HTML)
    • image: Optional icon/emoji
    • next: Target question ID
    • primary: Style as primary action button
    • style: Button style (primary, secondary, danger)
    • filter: Filter object to apply on click
    • dimensions: Dimension object to set on click

4. Notes Component

Static informational content (tips, instructions, links).

{
  "type": "notes",
  "content": "<p><strong>How to Measure:</strong></p><ul><li>Measure the rough opening</li><li>Round down to nearest inch</li></ul>",
  "style": "info"
}

Properties:

  • content (string, required): HTML content
  • style (string, optional): Visual style (info, warning, tip, default)

5. Separator Component (Optional)

Visual divider between sections.

{
  "type": "separator",
  "style": "line"
}

Properties:

  • style: Separator style (line, space, none)

Rendering Order Example

┌─────────────────────────────────────────┐
│  Configure Your Door                    │ ← Title/Subtitle
├─────────────────────────────────────────┤
│  Include Hardware?                      │
│  ● Yes    ○ No                          │ ← Toggle Component #1
├─────────────────────────────────────────┤
│  Installation Details                   │
│  [Preferred Installation Date: ____]    │
│  [Special Instructions: _________]      │ ← Fields Component #1
├─────────────────────────────────────────┤
│  Measurement Type                       │
│  ● Opening Size  ○ Tip-to-tip          │ ← Toggle Component #2
├─────────────────────────────────────────┤
│  Width (inches): [____]                 │
│  Height (inches): [____]                │ ← Fields Component #2
├─────────────────────────────────────────┤
│  [Continue]  [Add Accessories]          │ ← Navigation Component
├─────────────────────────────────────────┤
│  💡 Tip: For accurate measurements...   │ ← Notes Component
└─────────────────────────────────────────┘

Rendering Logic Implementation

function renderQuestion(questionData) {
  // Render title/subtitle
  renderHeader(questionData.title, questionData.subtitle);
  
  // Iterate through components array
  questionData.components.forEach((component, index) => {
    const container = document.createElement('div');
    container.className = `component component-${component.type}`;
    container.dataset.componentIndex = index;
    
    switch(component.type) {
      case 'toggle':
        renderToggleComponent(container, component);
        break;
      case 'fields':
        renderFieldsComponent(container, component);
        break;
      case 'navigation':
        renderNavigationComponent(container, component);
        break;
      case 'notes':
        renderNotesComponent(container, component);
        break;
      case 'separator':
        renderSeparatorComponent(container, component);
        break;
      default:
        console.warn(`Unknown component type: ${component.type}`);
    }
    
    document.getElementById('question-container').appendChild(container);
  });
}

function handleFormSubmit(questionData) {
  const variables = {};
  
  // Collect all variable values from toggle and field components
  questionData.components.forEach(component => {
    if (component.type === 'toggle') {
      const selected = document.querySelector(`input[name="${component.variable}"]:checked`);
      variables[component.variable] = selected ? selected.value : null;
    }
    if (component.type === 'fields') {
      component.fields.forEach(field => {
        const input = document.querySelector(`[name="${field.name}"]`);
        variables[field.name] = input ? input.value : null;
      });
    }
  });
  
  // Validate all required fields
  if (!validateComponents(questionData.components)) {
    return false;
  }
  
  // Save variables to userAnswers
  Object.assign(userAnswers, variables);
  
  // Navigation is handled by navigation component button clicks
  return true;
}

Migration Path from Current System

To maintain backwards compatibility during transition:

  1. Phase 1: Dual Support - Support both old and new formats
  2. Phase 2: Auto-conversion - Auto-convert old format to components on load
  3. Phase 3: Deprecation - Remove old format support
function normalizeQuestion(questionData) {
  // If already using components, return as-is
  if (questionData.components) {
    return questionData;
  }
  
  // Auto-convert old format to components
  const components = [];
  
  if (questionData.inputType === 'button') {
    // Convert to navigation component
    components.push({
      type: 'navigation',
      answers: questionData.answers
    });
  }
  
  if (questionData.inputType === 'form') {
    // Convert measurementType to toggle component
    if (questionData.measurementType) {
      components.push({
        type: 'toggle',
        variable: 'measurementType',
        options: questionData.measurementType.options
      });
    }
    
    // Convert fields to fields component
    if (questionData.fields) {
      components.push({
        type: 'fields',
        fields: questionData.fields
      });
    }
    
    // Add navigation if next exists
    if (questionData.next) {
      components.push({
        type: 'navigation',
        answers: [
          { caption: 'Continue', next: questionData.next }
        ]
      });
    }
  }
  
  // Add notes component if notes exist
  if (questionData.notes) {
    components.push({
      type: 'notes',
      content: questionData.notes
    });
  }
  
  return {
    ...questionData,
    components
  };
}

Benefits for Visual Editor

This component-based architecture makes the visual editor dramatically simpler to implement:

Component Palette

Drag components from palette onto canvas:

  • 🎚️ Toggle
  • 📝 Fields
  • 🔗 Navigation
  • 📄 Notes
  • Separator

Component List View

Within each tile, show reorderable list of components:

📋 Configure Door
  🎚️ Toggle: Include Hardware?
  📝 Fields: Installation Details (2 fields)
  🎚️ Toggle: Measurement Type
  📝 Fields: Dimensions (2 fields)
  🔗 Navigation: 2 buttons
  📄 Notes: Measuring tip
  [+ Add Component]

Drag to Reorder

Users can drag components up/down within the list to change render order.

Click to Configure

Clicking a component opens the config panel with type-specific options.

Editor UI Mockup

┌─────────────────────────────────────────────────────────────────┐
│ Component Palette                                                │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐                            │
│ │🎚️ │ │📝  │ │🔗  │ │📄  │ │➖  │                            │
│ └────┘ └────┘ └────┘ └────┘ └────┘                            │
├─────────────────────────────────────────────────────────────────┤
│ Selected Tile: q-configure-door                                  │
│                                                                   │
│ ┌─────────────────────────────────────────────┐                 │
│ │ 🎚️ Toggle: Include Hardware?        [↑][↓] │                 │
│ │ 📝 Fields: Installation Details      [↑][↓] │                 │
│ │ 🎚️ Toggle: Measurement Type         [↑][↓] │                 │
│ │ 📝 Fields: Dimensions                [↑][↓] │   ┌────────┐   │
│ │ 🔗 Navigation: 2 buttons             [↑][↓] │   │ Config │   │
│ │ 📄 Notes: Measuring tip              [↑][↓] │   │ Panel  │   │
│ │                                              │   │        │   │
│ │ [+ Add Component ▼]                          │   │        │   │
│ └─────────────────────────────────────────────┘   └────────┘   │
└─────────────────────────────────────────────────────────────────┘

Implementation Considerations

Validation:

  • Ensure at least one navigation component exists in reachable nodes
  • Validate variable naming (no conflicts)
  • Ensure required fields are marked

State Management:

  • All variables collected before navigation
  • Variables persist across question navigation
  • Can reference previous answers in conditional logic

Extensibility:

  • Easy to add new component types (checkbox group, radio group, slider, datepicker, file upload, etc.)
  • Plugin architecture for custom components
  • Community-contributed component library

Editor Features:

  • Duplicate component
  • Copy/paste components between tiles
  • Component templates/presets
  • Import component from library

UI Layout

Two-Panel Design

Panel 1: Canvas/Drop Area (Left/Main Panel)

  • Drag-and-drop workspace where navigation nodes (tiles) can be moved around
  • Tiles/Nodes represent each question/step in the navigation
  • Connection arrows show the flow between steps
  • Colored circles (connection points) on tiles indicate multiple possible outputs
    • Number of circles determined by configuration (buttons, alternatives, etc.)
    • Circles appear when tile is selected
    • Different colors for different types of connections
  • Grid/snap system for clean alignment
  • Zoom and pan controls for large navigation flows
  • Minimap for overview of complex flows

Panel 2: Configuration Panel (Right/Sidebar)

  • Tile Properties editor (activated when a tile is selected)
  • Dynamic form fields based on question type:
    • Question ID, title, subtitle
    • Question type selector (buttons, dimensions, multi-select, etc.)
    • Type-specific options (button labels, icons, validation, etc.)
  • Output/Connection Groups section:
    • Group buttons together that share the same output path
    • Add/remove connection groups
    • Each group maps to a colored circle on the tile
    • Default output selection (for ungrouped actions)
  • Default Values section:
    • List of variable assignments
    • Each row: [Variable Dropdown] : [Value Field] [Remove Button]
    • [Add Another] button to create new default assignments
  • Validation section for input requirements
  • Conditional Logic editor for complex routing

Features

Node/Tile Management

  • Visual representation of each navigation step
    • Title displayed on tile
    • Icon/badge showing question type
    • Color coding by category
  • Multiple outputs via colored circles:
    • Small circles appear on tile edges when selected
    • Each circle represents a possible path forward
    • Example: Door size question has 2 circles (predefined sizes, custom dimensions)
  • Drag and drop positioning
  • Auto-layout option to organize nodes cleanly
  • Duplicate node feature for similar questions
  • Delete with confirmation and automatic connection cleanup

Connection System

Option A: Click-and-Drag (Visual)

  • Click a circle on source tile
  • Drag to create arrow/line
  • Drop on target tile to connect
  • Arrow shows direction of flow
  • Hover shows connection metadata (condition, button labels)

Option B: Dropdown Selection (Form-based)

  • Select a connection group in config panel
  • Dropdown shows all available destination tiles
  • Select target from list
  • Visual arrow updates automatically
  • Both methods available
  • Click-drag for quick visual connections
  • Dropdown for precise selection or when tiles are far apart
  • Arrows can be clicked to edit connection details

Button/Output Grouping

Buttons within a tile can be grouped to share the same output:

Example 1: Door Size Question

  • Group 1: "All predefined size buttons" → Circle 1 (blue) → Next standard question
  • Group 2: "Custom dimensions button" → Circle 2 (orange) → Alternative dimension entry
  • Default: Any ungrouped actions → Circle 3 (gray) → Fallback route

Example 2: Simple Yes/No Question

  • Group 1: "Yes button" → Circle 1 → Path A
  • Group 2: "No button" → Circle 2 → Path B

Configuration UI:

Output Groups:
┌─────────────────────────────────────┐
│ Group 1: Predefined Sizes           │
│ Buttons: [32x80, 36x80, other...]  │
│ → Connects to: [q-hinges ▼]        │
│ Circle color: [🔵 Blue]             │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Group 2: Custom Entry               │
│ Buttons: [Enter Custom]             │
│ → Connects to: [q-alt-path ▼]      │
│ Circle color: [🟠 Orange]           │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Default Output: [q-next-step ▼]    │
└─────────────────────────────────────┘

Default Values System

Users need to set default variable values that apply when reaching certain nodes.

Configuration UI:

Default Values to Set:
┌──────────────────────────────────────────────┐
│ [doorType ▼]        : [storm door          ] [✖] │
│ [measurementSystem ▼]: [imperial           ] [✖] │
│ [region ▼]           : [north-america      ] [✖] │
└──────────────────────────────────────────────┘
[+ Add Another Default Value]

Features:

  • Variable dropdown populated from:
    • Previously used variables across all navigation nodes
    • Manual entry option to add new variables
    • Suggesting common variables based on question type
  • Value field accepts:
    • Static values
    • References to other variables (e.g., ${previousAnswer})
    • Expressions for calculations
  • Remove button to delete a default value assignment
  • Add button to create additional assignments

Technical Implementation

Technology Stack Options

Option 1: Web-Based Standalone Tool

Frontend:

  • React or Vue.js for UI framework
  • React Flow or Vue Flow for node-based canvas
    • Built-in drag-drop, zoom, pan
    • Customizable nodes and connections
    • Good performance with large graphs
  • Tailwind CSS or Material-UI for styling
  • Monaco Editor for any direct JSON editing fallback

Backend:

  • Python Flask (existing stack) with endpoints:
    • GET /api/editor/navigation - Load current navigation.json
    • POST /api/editor/navigation - Save edited navigation
    • GET /api/editor/validate - Validate navigation structure
    • GET /api/editor/variables - Get list of known variables
  • File system operations to read/write navigation.json
  • Validation logic to ensure navigation structure is valid

Pros:

  • Can be integrated into existing Flask app
  • Accessible from any browser
  • No installation required
  • Easy to share and collaborate

Cons:

  • Requires server to be running
  • Limited offline capability

Option 2: Desktop Application

Framework:

  • Electron (JavaScript-based desktop app)
    • Wrap the web-based editor
    • Direct file system access
    • Can bundle Python scripts
  • Tauri (Rust-based, lighter alternative to Electron)

Pros:

  • Better file system integration
  • Can work offline
  • More desktop-native experience
  • Better performance

Cons:

  • Requires installation
  • More complex deployment
  • Additional development effort

Option 3: VS Code Extension

Framework:

  • VS Code Extension API
  • Webview for the visual editor
  • Language server for validation

Pros:

  • Integrates with existing development environment
  • Can use VS Code's existing features (git, etc.)
  • Access to VS Code extension marketplace
  • Syntax highlighting fallback to JSON

Cons:

  • Limited to VS Code users
  • Extension development learning curve
  • More restrictive UI capabilities

Most practical for this project given the existing Flask infrastructure.

Implementation Phases

Phase 1: Core Canvas (MVP)

Goals:

  • Display existing navigation.json as visual nodes
  • Basic drag-and-drop positioning
  • Simple click-to-select nodes
  • Read-only view to validate the concept

Deliverables:

  • Canvas component with nodes
  • JSON parser to convert navigation.json to node graph
  • Basic visual styling for nodes

Estimated Effort: 20-30 hours

Phase 2: Basic Editing

Goals:

  • Configuration panel for editing node properties
  • Add/delete nodes
  • Simple one-to-one connections
  • Save back to navigation.json

Deliverables:

  • Config panel UI
  • Node CRUD operations
  • JSON serializer to convert graph back to navigation.json
  • Save functionality

Estimated Effort: 30-40 hours

Phase 3: Advanced Connections

Goals:

  • Multiple output circles per node
  • Button grouping system
  • Visual connection management (drag-drop arrows)
  • Connection validation

Deliverables:

  • Output group configuration UI
  • Connection point rendering
  • Arrow drawing and management
  • Validation logic

Estimated Effort: 25-35 hours

Phase 4: Default Values & Variables

Goals:

  • Default values configuration section
  • Variable management system
  • Variable autocomplete/suggestions

Deliverables:

  • Default values UI
  • Variable tracking system
  • Smart suggestions

Estimated Effort: 15-20 hours

Phase 5: Polish & Features

Goals:

  • Auto-layout algorithm
  • Zoom/pan controls
  • Minimap
  • Undo/redo
  • Export/import
  • Validation reporting
  • Help tooltips

Deliverables:

  • All polish features
  • Documentation
  • User guide

Estimated Effort: 20-30 hours

Total Estimated Effort: 110-155 hours (3-4 weeks full-time development)

Data Structure Mapping

Current navigation.json Structure (Runtime Format)

{
  "start": {
    "type": "question",
    "inputType": "button",
    "title": "What are you looking for?",
    "subtitle": "Select the product category",
    "answers": [
      {
        "caption": "Door",
        "image": "🚪",
        "next": "q-door-type",
        "filter": { "baseType": "Door" }
      },
      {
        "caption": "Window",
        "image": "🪟",
        "next": "q-window-type",
        "filter": { "baseType": "Window" }
      }
    ]
  },
  "q-door-type": {
    "type": "question",
    "inputType": "button",
    "title": "What type of door?",
    "answers": [...]
  }
}

Project File Structure (Editor Format - .navproj.json)

The editor saves this format, which includes all visual layout and editor-specific data:

{
  "version": "1.0",
  "metadata": {
    "created": "2026-04-02",
    "lastModified": "2026-04-02",
    "description": "Product Finder Navigation Flow"
  },
  "nodes": [
    {
      "id": "node-1",
      "tileId": "start",
      "type": "NavigationTile",
      "position": { "x": 100, "y": 200 },
      "config": {
        "type": "question",
        "inputType": "button",
        "title": "What are you looking for?",
        "subtitle": "Select the product category",
        "notes": "<p><strong>Quick Tips...</p>",
        "buttons": [
          {
            "caption": "Door",
            "image": "🚪",
            "outputGroup": "output-door",
            "filter": { "baseType": "Door" }
          },
          {
            "caption": "Window",
            "image": "🪟",
            "outputGroup": "output-window",
            "filter": { "baseType": "Window" }
          }
        ]
      },
      "outputs": [
        {
          "id": "output-door",
          "color": "#3b82f6",
          "label": "Door Path",
          "targetTileId": "q-door-type"
        },
        {
          "id": "output-window",
          "color": "#10b981",
          "label": "Window Path",
          "targetTileId": "q-window-type"
        }
      ],
      "defaults": []
    }
  ],
  "connections": [
    {
      "id": "conn-1",
      "from": "node-1",
      "fromOutput": "output-door",
      "to": "node-2"
    },
    {
      "id": "conn-2",
      "from": "node-1",
      "fromOutput": "output-window",
      "to": "node-3"
    }
  ],
  "editorSettings": {
    "zoom": 1.0,
    "panX": 0,
    "panY": 0,
    "selectedNodeId": null
  },
  "customTiles": [
    {
      "id": "custom-size-flow",
      "name": "Size Selection Flow",
      "description": "Handles both standard sizes and custom dimensions",
      "icon": "📏",
      "version": "1.0",
      "entryPoint": "node-size-entry",
      "exitPoints": [
        {
          "id": "exit-standard",
          "label": "Standard Size Selected",
          "color": "#3b82f6"
        },
        {
          "id": "exit-custom",
          "label": "Custom Dimensions",
          "color": "#10b981"
        }
      ],
      "nodes": [
        {
          "id": "node-size-entry",
          "tileId": "entry",
          "type": "EntryPoint",
          "position": { "x": 50, "y": 150 },
          "config": {},
          "outputs": [
            {
              "id": "to-size-display",
              "targetTileId": "node-size-buttons"
            }
          ]
        },
        {
          "id": "node-size-buttons",
          "tileId": "size-options",
          "type": "NavigationTile",
          "position": { "x": 250, "y": 150 },
          "config": {
            "title": "Select Size",
            "buttons": [
              {
                "caption": "30\" x 81\"",
                "outputGroup": "standard",
                "dimensions": { "width": 30, "height": 81 }
              },
              {
                "caption": "32\" x 81\"",
                "outputGroup": "standard",
                "dimensions": { "width": 32, "height": 81 }
              },
              {
                "caption": "Custom Size",
                "outputGroup": "custom"
              }
            ]
          },
          "outputs": [
            {
              "id": "standard",
              "exitPoint": "exit-standard"
            },
            {
              "id": "custom",
              "targetTileId": "node-dimension-form"
            }
          ]
        },
        {
          "id": "node-dimension-form",
          "tileId": "custom-dimensions",
          "type": "FormTile",
          "position": { "x": 450, "y": 250 },
          "config": {
            "title": "Enter Custom Dimensions",
            "fields": [
              { "name": "width", "type": "number" },
              { "name": "height", "type": "number" }
            ]
          },
          "outputs": [
            {
              "id": "form-complete",
              "exitPoint": "exit-custom"
            }
          ]
        }
      ],
      "connections": [
        {
          "from": "node-size-entry",
          "to": "node-size-buttons"
        },
        {
          "from": "node-size-buttons",
          "fromOutput": "custom",
          "to": "node-dimension-form"
        }
      ]
    }
  ]
}

Custom Tiles in Project File:

  • customTiles array: Stores all reusable sub-flows as separate entities
  • Entry/Exit Points: Define how the custom tile interfaces with the main flow
  • Internal nodes: Complete flow definition within the custom tile
  • Versioning: Track custom tile versions for compatibility
  • Metadata: Name, description, icon for library display

When a custom tile is used in the main flow:

{
  "id": "node-5",
  "tileId": "use-size-flow",
  "type": "CustomTile",
  "customTileRef": "custom-size-flow",  // References the custom tile
  "position": { "x": 500, "y": 300 },
  "outputs": [
    {
      "id": "standard-path",
      "exitPoint": "exit-standard",  // Maps to custom tile's exit
      "targetTileId": "results"
    },
    {
      "id": "custom-path",
      "exitPoint": "exit-custom",
      "targetTileId": "q-accessories"
    }
  ]
}
}

Key Differences:

  • Project file includes: positions, colors, output groups, editor state
  • Navigation file includes: only runtime data needed by the app
  • Export process converts project format → navigation format using class export methods
  • Import process can convert existing navigation.json → project format

Tile Class Architecture

Each tile type is a class with its own export logic:

// Base class for all tiles
abstract class BaseTile {
  id: string;
  tileId: string;
  position: Position;
  config: any;
  outputs: Output[];
  defaults: DefaultValue[];
  
  // Each tile type implements its own export logic
  abstract export(): object;
  
  // Validate tile configuration before export
  abstract validate(): ValidationResult;
}

// Navigation tile - standard question with buttons
class NavigationTile extends BaseTile {
  export(): object {
    return {
      type: this.config.type,
      inputType: this.config.inputType,
      title: this.config.title,
      subtitle: this.config.subtitle,
      notes: this.config.notes,
      answers: this.config.buttons.map(btn => ({
        caption: btn.caption,
        image: btn.image,
        next: this.outputs.find(o => o.id === btn.outputGroup)?.targetTileId,
        filter: btn.filter,
        dimensions: btn.dimensions
      }))
    };
  }
}

// Form tile - custom input fields
class FormTile extends BaseTile {
  export(): object {
    return {
      type: "question",
      inputType: "form",
      title: this.config.title,
      subtitle: this.config.subtitle,
      notes: this.config.notes,
      measurementType: this.config.measurementType,
      fields: this.config.fields,
      next: this.outputs[0]?.targetTileId || "results"
    };
  }
}

// Results tile - terminal node
class ResultsTile extends BaseTile {
  export(): object {
    return {
      type: "results",
      title: this.config.title,
      // No outputs - this is terminal
    };
  }
}

// Custom tile - references a reusable sub-flow
class CustomTile extends BaseTile {
  customTileRef: string;  // ID of custom tile definition
  customTileDef: CustomTileDefinition;  // Reference to the actual custom tile
  
  export(): object {
    // Custom tiles export by "flattening" their internal flow
    // OR by exporting as a single node that gets expanded at runtime
    
    // Option A: Flatten (embed internal nodes into main navigation)
    // This makes the exported JSON have no "custom tile" concept
    const flattened = {};
    
    // Generate unique tile IDs for internal nodes
    this.customTileDef.nodes.forEach(node => {
      const uniqueId = `${this.id}_${node.tileId}`;
      flattened[uniqueId] = node.export();
    });
    
    // Wire entry point to first internal node
    const entryNode = this.customTileDef.entryPoint;
    // Wire exit points back to main flow
    
    return flattened;
    
    // Option B: Keep as reference (requires app to support custom tiles)
    // return {
    //   type: "customTile",
    //   ref: this.customTileRef,
    //   outputs: this.outputs.map(o => ({
    //     exitPoint: o.exitPoint,
    //     next: o.targetTileId
    //   }))
    // };
  }
  
  validate(): ValidationResult {
    const errors = [];
    
    // Validate all outputs map to exit points
    this.outputs.forEach(output => {
      const exitExists = this.customTileDef.exitPoints.find(
        ep => ep.id === output.exitPoint
      );
      if (!exitExists) {
        errors.push(`Output references non-existent exit point: ${output.exitPoint}`);
      }
    });
    
    // Validate internal flow is complete
    const internalValidation = this.customTileDef.validate();
    errors.push(...internalValidation.errors);
    
    return {
      valid: errors.length === 0,
      errors
    };
  }
}

Export Workflow

class NavigationProject {
  nodes: BaseTile[];
  connections: Connection[];
  
  // Export entire project to navigation.json
  exportToNavigation(): object {
    const navigation: any = {};
    
    // Each node exports itself to JSON
    this.nodes.forEach(node => {
      navigation[node.tileId] = node.export();
    });
    
    return navigation;
  }
  
  // Import existing navigation.json to project format
  static importFromNavigation(navData: object): NavigationProject {
    const project = new NavigationProject();
    const layout = new AutoLayout(); // Auto-position nodes
    
    Object.entries(navData).forEach(([tileId, data], index) => {
      const position = layout.getNextPosition(index);
      const tile = this.createTileFromData(tileId, data, position);
      project.nodes.push(tile);
    });
    
    // Reconstruct connections from 'next' references
    project.reconstructConnections();
    
    return project;
  }
  
  // Validate entire project before export
  validate(): ValidationReport {
    const report = new ValidationReport();
    
    this.nodes.forEach(node => {
      const result = node.validate();
      if (!result.isValid) {
        report.addError(node.tileId, result.errors);
      }
    });
    
    // Check for orphaned nodes
    const connectedNodes = new Set(
      this.connections.flatMap(c => [c.from, c.to])
    );
    this.nodes.forEach(node => {
      if (!connectedNodes.has(node.id) && node.tileId !== 'start') {
        report.addWarning(node.tileId, 'Node is not connected');
      }
    });
    
    return report;
  }
}

File Operations

// Save project file
async function saveProject(project: NavigationProject, filepath: string) {
  const projectData = {
    version: "1.0",
    metadata: {
      created: project.metadata.created,
      lastModified: new Date().toISOString(),
      description: project.metadata.description
    },
    nodes: project.nodes.map(n => n.toJSON()),
    connections: project.connections,
    editorSettings: project.editorSettings
  };
  
  await fs.writeFile(filepath, JSON.stringify(projectData, null, 2));
}

// Export to navigation.json
async function exportNavigation(project: NavigationProject, filepath: string) {
  // Validate first
  const validation = project.validate();
  if (!validation.isValid) {
    throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
  }
  
  // Export
  const navData = project.exportToNavigation();
  await fs.writeFile(filepath, JSON.stringify(navData, null, 2));
}

// Load project file
async function loadProject(filepath: string): Promise<NavigationProject> {
  const data = JSON.parse(await fs.readFile(filepath, 'utf-8'));
  return NavigationProject.fromJSON(data);
}

## Key Challenges & Solutions

### Challenge 1: Complex Button Grouping
**Problem:** Multiple buttons can map to different outputs, and the structure can get complex.

**Solution A: Color-Coded Output Groups** (Recommended)
- Each output path gets a unique color (suggested max ~5 colors per tile)
- User can set/choose the color for each output group
- Buttons using that output show:
  - Colored rect/border around the button in the config panel
  - Optional: button background tint in that color (browser support varies)
- Connection arrows from the tile use the corresponding color
- Provides immediate visual clarity for which buttons lead where

**Solution B: Per-Button Output Selector**
- Each button in the config panel has an "Output Group" dropdown near it
- Dropdown shows:
  - Existing output groups (with their target tiles)
  - "Add New Output" option
- Creates output circles on-demand based on unique selections
- More flexible but requires more user interactions per button

**Config Panel Structure (Type-Specific Fields):**

Button Type: [navigate ▼]

IF navigate: Output Group: [🟢 Color-1: Next Step ▼] (Add New) Target Tile: [Select Storm Door Size ▼] Button Label: [Field] ... other options

IF toggle: Toggle Group: [☐ Group A ☑ Group B ☐ Group C] (Add New Toggle Group) Variable to Set: [doorType ▼] Value or Label: [Field] ... other options


**Visual Representation with Colored Outputs:**

┌─────────────────┐ │ Door Type │ │ ┌──────────┐ │ 🟢 (Green - Predefined route) │ │ Standard │───┼──🟢 │ │ Custom │───┼──🔵 │ │ Skip │───┼──🔴 │ └──────────┘ │ 🔵 (Blue - Custom route) └─────────────────┘ 🔴 (Red - Skip to end)


### Challenge 2: Maintaining JSON Compatibility
**Problem:** The visual editor creates its own structure, but must export to the existing navigation.json format.

**Solution: Class-Based Export Architecture**

Each tile in the visual editor is a class object with an export function that generates clean JSON. This creates two separate file types:

#### 1. Project File (`.navproj.json` or similar)
Saved/loaded by the editor, contains all editor-specific data:
```json
{
  "version": "1.0",
  "metadata": {
    "created": "2026-04-02",
    "lastModified": "2026-04-02",
    "author": "User"
  },
  "nodes": [
    {
      "id": "node-1",
      "tileId": "start",
      "type": "NavigationTile",
      "position": { "x": 100, "y": 200 },
      "config": {
        "type": "question",
        "inputType": "button",
        "title": "What are you looking for?",
        "subtitle": "Select the product category",
        "notes": "<p><strong>Quick Tips:...</p>",
        "buttons": [
          {
            "caption": "Door",
            "image": "🚪",
            "outputGroup": "output-1",
            "filter": { "baseType": "Door" }
          },
          {
            "caption": "Window",
            "image": "🪟",
            "outputGroup": "output-2",
            "filter": { "baseType": "Window" }
          }
        ]
      },
      "outputs": [
        {
          "id": "output-1",
          "color": "#3b82f6",
          "label": "Door Path",
          "targetTileId": "q-door-type"
        },
        {
          "id": "output-2",
          "color": "#10b981",
          "label": "Window Path",
          "targetTileId": "q-window-type"
        }
      ],
      "defaults": []
    }
  ],
  "connections": [
    {
      "id": "conn-1",
      "from": "node-1",
      "fromOutput": "output-1",
      "to": "node-2"
    }
  ],
  "editorSettings": {
    "zoom": 1.0,
    "panX": 0,
    "panY": 0
  }
}

2. Export File (navigation.json)

Generated from project file during export, clean JSON for runtime:

{
  "start": {
    "type": "question",
    "inputType": "button",
    "title": "What are you looking for?",
    "subtitle": "Select the product category",
    "notes": "<p><strong>Quick Tips:...</p>",
    "answers": [
      {
        "caption": "Door",
        "image": "🚪",
        "next": "q-door-type",
        "filter": {
          "baseType": "Door"
        }
      },
      {
        "caption": "Window",
        "image": "🪟",
        "next": "q-window-type",
        "filter": {
          "baseType": "Window"
        }
      }
    ]
  }
}

Export Function Example (TypeScript/JavaScript)

class NavigationTile {
  id: string;
  config: TileConfig;
  outputs: Output[];
  
  // Export this tile to navigation.json format
  export(): object {
    const navigationNode: any = {
      type: this.config.type,
      inputType: this.config.inputType,
      title: this.config.title,
      subtitle: this.config.subtitle
    };
    
    // Add optional fields
    if (this.config.notes) {
      navigationNode.notes = this.config.notes;
    }
    
    // Convert buttons with output groups to answers with next references
    if (this.config.buttons) {
      navigationNode.answers = this.config.buttons.map(button => {
        const output = this.outputs.find(o => o.id === button.outputGroup);
        return {
          caption: button.caption,
          image: button.image,
          next: output?.targetTileId || null,
          filter: button.filter
        };
      });
    }
    
    // Add conditional logic if present
    if (this.config.conditional) {
      navigationNode.conditional = this.config.conditional;
    }
    
    return navigationNode;
  }
  
  // Import from navigation.json to project format
  static import(tileId: string, data: any, position: Position): NavigationTile {
    // Convert navigation.json format back to editor format
    // ...
  }
}

// Export entire project to navigation.json
function exportToNavigation(project: ProjectFile): object {
  const navigation: any = {};
  
  project.nodes.forEach(node => {
    const tile = new NavigationTile(node);
    navigation[node.tileId] = tile.export();
  });
  
  return navigation;
}

Benefits of This Approach:

Separation of Concerns - Editor data (positions, colors) separate from runtime data
Clean Exports - Generated navigation.json contains only what the app needs
Version Control Friendly - Project file can track editor-specific changes
Validation - Export process can validate each tile before generating JSON
Backwards Compatible - Can import existing navigation.json files
Extensible - Easy to add new tile types with their own export logic

Workflow:

  1. Edit Mode: User works with project file (.navproj.json)
  2. Export: Click "Export" → validates and generates navigation.json
  3. Import: Can load existing navigation.json → converts to project format
  4. Save/Load: Project file preserves all editor state between sessions

Challenge 3: Large Navigation Flows & Reusability

Problem: Complex products might have 50+ questions, making the canvas crowded. Common patterns (size selection, color/material combos) are duplicated across flows.

Solution: Hierarchical Sub-Flows (Custom Tiles)

Implement a nested flow architecture similar to Node-RED sub-flows, LabVIEW SubVIs, or PLC function blocks used in industrial automation systems.

Concept: Custom Tiles

  1. Create a Sub-Flow: Select 3-5 related tiles and group them into a reusable sub-flow
  2. Save as Custom Tile: Name it (e.g., "Size Selection Flow", "Material-Color Picker")
  3. Use in Main Flow: The custom tile appears as a single node with defined inputs/outputs
  4. Drill Down to Edit: Double-click the custom tile to open its internal flow in a separate view
  5. Propagate Changes: Edits to the custom tile automatically update all instances

Example Use Cases

Size Selection Flow:

[Entry Point]
   ↓
[Show Standard Sizes] ──→ [Size Button 1] → [Exit: Standard]
                      ──→ [Size Button 2] → [Exit: Standard]
                      ──→ [Size Button 3] → [Exit: Standard]
                      ──→ [Custom Size] → [Dimension Form] → [Exit: Custom]

This 5-tile flow becomes one custom tile in the main flow:

[Color Selection] → [Size Selection Flow] → [Results]

Material-Color Combo Flow:

[Entry Point]
   ↓
[Material Selection] → [Aluminum] → [Aluminum Colors] → [Exit]
                    → [Vinyl] → [Vinyl Colors] → [Exit]
                    → [Wood] → [Wood Stains] → [Exit]

Glass Options Flow:

[Entry Point]
   ↓
[Glass Type] → [Clear] → [Thickness Options] → [Exit]
            → [Tinted] → [Tint Colors] → [Exit]
            → [Obscure] → [Pattern Selection] → [Exit]

UI/UX for Sub-Flows

Main Flow View:

  • Custom tiles show with special icon (e.g., 📦 or collapsed group icon)
  • Display name: "Size Selection Flow"
  • Show entry point (green circle) and exit points (colored circles per output group)
  • Quick preview badge: "5 nodes"

Switching Views:

  • Option A: Dropdown menu at top: Main Flow ▼ → Select custom tile name to switch
  • Option B: Double-click custom tile to drill down into its internal flow
  • Option C: Breadcrumb navigation: Main Flow > Size Selection Flow

Inside Custom Tile View:

  • Special entry node: "Entry Point" (where main flow enters)
  • Special exit nodes: "Exit: Standard", "Exit: Custom" (where flow returns to main)
  • Edit the internal flow just like the main flow
  • Changes save automatically

Custom Tile Library

┌─────────────────────────┐
│ Custom Tiles Library    │
├─────────────────────────┤
│ 📦 Size Selection Flow  │  [Edit] [Delete]
│ 📦 Material-Color Combo │  [Edit] [Delete]
│ 📦 Glass Options Flow   │  [Edit] [Delete]
│                         │
│ [+ Create New Custom Tile]
└─────────────────────────┘

Implementation Benefits

Reusability - Create once, use many times across different product flows
Maintainability - Update the custom tile once, all instances update automatically
Complexity Management - Main flow stays clean; complex logic hidden in sub-flows
Organization - Group related questions logically
Scalability - Handle 100+ node flows by keeping main flow at 10-20 high-level tiles
Collaboration - Team members can work on different custom tiles independently

Additional Organization Features

  • Minimap for navigation in large flows
  • Search/filter to find specific nodes or custom tiles
  • Auto-layout algorithm to organize nodes cleanly
  • Zoom controls (fit to screen, zoom to selection)
  • Layers or tags for categorizing nodes (e.g., "Storm Doors", "Patio Doors")

Challenge 4: Path Validation

Problem: Ensuring all paths lead somewhere valid and no orphaned nodes.

Solution:

  • Real-time validation as user edits
  • Visual indicators: red border on nodes with issues
  • Validation report showing:
    • Orphaned nodes (no incoming connections)
    • Dead ends (no outgoing connections)
    • Missing required fields
    • Circular references
  • Test mode: simulate walking through the navigation

User Experience Flow

Creating a New Node

  1. Click "Add Node" button or drag from palette
  2. Node appears on canvas with default settings
  3. User clicks node to select
  4. Config panel opens on right
  5. User fills in: Question ID, title, type
  6. Based on type, additional fields appear (buttons, input fields, etc.)
  7. User configures output groups
  8. User saves (auto or manual)
  9. Node updates visually on canvas

Connecting Two Nodes

  1. User selects source node
  2. Output circles appear on node edges
  3. User clicks and drags from a circle
  4. Arrow follows mouse cursor
  5. User drops on target node
  6. Connection is created
  7. Arrow persists showing the connection
  8. Clicking arrow allows editing connection details

Setting Default Values

  1. User selects a node
  2. Config panel shows "Default Values" section
  3. User clicks "[+ Add Default Value]"
  4. New row appears with dropdowns and field
  5. User selects variable from dropdown (or types new name)
  6. User enters value in field
  7. Value is stored with the node
  8. When navigation reaches this node, values are set

Testing the Navigation

  1. User clicks "Test Mode" button
  2. Canvas switches to interactive mode
  3. Simulated UI appears showing the current question
  4. User can answer questions, seeing the path highlighted
  5. Path taken is shown with animated arrows
  6. User can reset and try different paths
  7. Exit test mode to return to editing

Additional Features to Consider

Versioning & History

  • Save snapshots of navigation.json before changes
  • Git integration for version control
  • Undo/redo with unlimited history
  • Compare versions side-by-side

Collaboration

  • Multi-user editing with real-time sync
  • Comments on nodes for team discussions
  • Lock nodes being edited by others
  • Change notifications

Import/Export

  • Export to different formats (JSON, YAML, diagram image)
  • Import from other tools or formats
  • Export to documentation (PDF, HTML)

Templates & Snippets

  • Save common question patterns as templates
  • Library of pre-built question flows
  • Duplicate and modify existing flows

Analytics Integration

  • Show usage statistics on nodes (if tracking is implemented)
  • Highlight most/least used paths
  • Identify bottlenecks or drop-off points

Accessibility

  • Keyboard shortcuts for all operations
  • Screen reader support
  • High contrast mode
  • Zoom accessibility

Additional Considerations & Potential Issues

Critical Features Not Yet Detailed

1. Undo/Redo System ⚠️ HIGH PRIORITY

Why Critical: Visual editors require robust undo/redo because users make lots of experimental changes, accidental deletions, and need quick recovery from mistakes.

Implementation Approach:

  • Command Pattern: Each action (move node, add connection, edit property) is a reversible command
  • Action History Stack: Maintain array of completed actions, track current position
  • Supported Actions:
    • Node: Create, Delete, Move, Duplicate, Edit Properties
    • Connection: Add, Remove, Reroute
    • Group Operations: Multi-node moves, bulk edits
  • UI: Ctrl+Z (undo), Ctrl+Y/Ctrl+Shift+Z (redo), visual history panel (optional)
  • State Management: Snapshot approach vs incremental changes
    • Snapshot: Save full state after each action (simple but memory-intensive)
    • Incremental: Store delta changes (complex but efficient)

Recommendation: Start with snapshot approach for MVP, optimize to incremental if performance becomes an issue.

Estimated Effort: 10-15 hours


2. Copy/Paste & Duplication

Why Important: Users frequently need to duplicate similar question nodes, copy patterns, or reuse configurations.

Features Needed:

  • Copy Single Node: Ctrl+C copies selected node and its config
  • Copy Multiple Nodes: Shift-click or drag-select to copy groups
  • Copy with Connections: Option to include/exclude connection information
  • Paste: Ctrl+V pastes at cursor position or offset from original
  • Duplicate: Ctrl+D creates copy immediately (no clipboard)
  • Between Flows: Copy from main flow, paste into custom tile (and vice versa)
  • ID Conflict Resolution: Auto-generate new unique IDs for pasted nodes
  • Connection Handling:
    • Internal connections (between pasted nodes) are preserved
    • External connections (to non-copied nodes) are broken/cleared

Clipboard Format:

{
  "type": "copilot-nav-editor",
  "version": "1.0",
  "nodes": [...],
  "connections": [...],
  "boundingBox": { "width": 500, "height": 300 }
}

Estimated Effort: 8-12 hours


3. Keyboard Shortcuts

Why Important: Power users expect keyboard efficiency. Mouse-only interfaces feel slow after extended use.

Essential Shortcuts:

Navigation & Selection:
  Tab / Shift+Tab    - Select next/previous node
  Arrow Keys         - Move selected node (hold Shift for 10px increments)
  Ctrl+A             - Select all nodes
  Ctrl+Click         - Multi-select nodes
  Shift+Click        - Select range between last selected

Editing:
  Ctrl+Z             - Undo
  Ctrl+Y / Ctrl+Shift+Z - Redo
  Ctrl+C             - Copy
  Ctrl+V             - Paste
  Ctrl+X             - Cut
  Ctrl+D             - Duplicate
  Delete / Backspace - Delete selected
  Enter              - Edit selected node (open config panel)
  Esc                - Deselect / Close panel / Cancel drag

View:
  Spacebar (hold)    - Pan/Hand tool (drag canvas)
  Ctrl+0             - Zoom to fit all
  Ctrl++ / Ctrl+-    - Zoom in/out
  Ctrl+Scroll        - Zoom at cursor
  Ctrl+F             - Search/Find node

File Operations:
  Ctrl+S             - Save project
  Ctrl+Shift+S       - Save As
  Ctrl+E             - Export to navigation.json
  Ctrl+N             - New project
  Ctrl+O             - Open project

Implementation: Hook keyboard events at canvas level, prevent conflicts with browser shortcuts.

Estimated Effort: 5-8 hours


4. Connection Routing Algorithm

Why Important: As flows grow complex, straight-line arrows overlap nodes and cross each other, creating visual mess.

Options:

Option A: Straight Lines (Simple)

  • Direct point-to-point lines
  • Easy to implement
  • Fast rendering
  • Overlaps and crosses in complex flows

Option B: Bezier Curves (Smooth)

  • Curved connections using cubic Bezier paths
  • Visually appealing
  • Some automatic avoidance via control points
  • ⚠️ Can still overlap nodes
  • React Flow default option

Option C: Orthogonal Routing (Clean)

  • Only horizontal and vertical lines (like electrical diagrams)
  • Very clean, professional look
  • Better node avoidance possible
  • More complex algorithm
  • May require more space

Option D: Intelligent Routing (Advanced)

  • Pathfinding algorithm (A*) to route around nodes
  • Best visual clarity
  • No overlaps
  • Most complex
  • Performance considerations with many connections

Recommendation for MVP: Start with Bezier curves (React Flow default), add orthogonal routing in Phase 5 if needed.

Estimated Effort:

  • Bezier (built-in): 0 hours
  • Orthogonal: 15-20 hours
  • Intelligent: 30-40 hours

5. Auto-Save vs Manual Save

Why Important: Browser crashes, accidental closures, and forgotten saves can lose hours of work.

Recommended Strategy: Hybrid Approach

Auto-Save to Local Storage:

  • Save to localStorage every 30 seconds (debounced after changes)
  • Keep last 3 auto-save versions with timestamps
  • Recover on next load if detected
  • ⚠️ localStorage limit: ~5-10MB (should be sufficient for most flows)

Manual Save to File:

  • Ctrl+S saves project file to disk (.navproj.json)
  • Visual indicator: "Saved" checkmark vs "Unsaved changes" dot
  • Prompt before closing window if unsaved changes exist

Cloud Sync (Future):

  • Optional integration with cloud storage (Google Drive, Dropbox, OneDrive)
  • Version history and conflict resolution

UI Feedback:

┌─────────────────────────────────────┐
│ 🟢 Auto-saved 30 seconds ago       │
│ ⚫ Unsaved manual changes           │
│ [Save Project] [Export Navigation] │
└─────────────────────────────────────┘

Browser Close Warning:

window.addEventListener('beforeunload', (e) => {
  if (hasUnsavedChanges()) {
    e.preventDefault();
    e.returnValue = 'You have unsaved changes. Are you sure?';
  }
});

Estimated Effort: 6-10 hours


6. Performance at Scale ⚠️ IMPORTANT

Why Important: A navigation with 100+ nodes and 200+ connections could slow down rendering and interactions.

Potential Issues:

  • Slow drag-and-drop response
  • Lag when adding/removing connections
  • Sluggish panning and zooming
  • Memory consumption

React Flow Performance Characteristics:

  • Generally handles 100-300 nodes well
  • Slows down around 500+ nodes
  • Connections are more performant than nodes

Optimization Strategies:

Level 1: Built-in React Flow Optimizations (Free)

  • Virtual rendering (only render visible nodes)
  • Memoization of node components
  • Connection edge optimization

Level 2: Custom Optimizations (If Needed)

  • Lazy Load Custom Tiles: Don't load internal nodes until opened
  • Connection Simplification: Reduce curve resolution for distant connections
  • Level of Detail: Show simplified node representation when zoomed out
  • Pagination/Filtering: Show subset of nodes at a time (by category, search)
  • Canvas Sectioning: Divide large flows into "pages" or "zones"

Performance Testing Plan:

  1. Create test navigation with 50, 100, 200, 500 nodes
  2. Measure frame rate during drag operations (target: 60 FPS)
  3. Measure zoom/pan responsiveness
  4. Measure initial load time
  5. Test on lower-end hardware

Recommendation: Test with realistic data size early (Phase 2), optimize only if needed.

Estimated Effort:

  • Testing: 3-5 hours
  • Optimizations: 10-20 hours (if needed)

7. Circular Dependency Detection

Why Important: Node A → Node B → Node C → Node A creates infinite loops, breaking navigation.

Detection Algorithm:

function detectCycles(nodes: Node[], connections: Connection[]): string[] {
  const cycles: string[] = [];
  
  // Build adjacency list
  const graph = buildGraph(connections);
  
  // Depth-first search with path tracking
  const visited = new Set<string>();
  const recursionStack = new Set<string>();
  
  function dfs(nodeId: string, path: string[]): boolean {
    visited.add(nodeId);
    recursionStack.add(nodeId);
    path.push(nodeId);
    
    const neighbors = graph.get(nodeId) || [];
    for (const neighbor of neighbors) {
      if (!visited.has(neighbor)) {
        if (dfs(neighbor, path)) return true;
      } else if (recursionStack.has(neighbor)) {
        // Cycle detected!
        const cycleStart = path.indexOf(neighbor);
        const cycle = path.slice(cycleStart).concat(neighbor);
        cycles.push(cycle.join(' → '));
        return true;
      }
    }
    
    recursionStack.delete(nodeId);
    return false;
  }
  
  // Check all nodes
  nodes.forEach(node => {
    if (!visited.has(node.id)) {
      dfs(node.id, []);
    }
  });
  
  return cycles;
}

User Experience:

  • Real-time Detection: Run cycle check after each connection change
  • Visual Warning: Highlight nodes involved in cycle with red border
  • Error Message: "Cycle detected: Start → Door Type → Color → Start"
  • Suggestion: "This creates an infinite loop. Remove one of these connections."
  • Prevent Export: Don't allow exporting navigation with cycles

Estimated Effort: 4-6 hours


8. Backwards Compatibility Strategy

Why Important: As the .navproj.json format evolves (v1.0 → v1.1 → v2.0), old project files must still load.

Versioning Strategy:

{
  "version": "1.2",
  "formatVersion": "1.2",  // Semantic versioning
  "nodes": [...]
}

Migration System:

const migrations = {
  "1.0-to-1.1": (data) => {
    // Add default values structure if missing
    data.nodes.forEach(node => {
      if (!node.defaults) node.defaults = [];
    });
    return data;
  },
  "1.1-to-1.2": (data) => {
    // Add custom tiles structure
    if (!data.customTiles) data.customTiles = [];
    return data;
  }
};

function migrateProjectFile(data: any): any {
  const currentVersion = data.version || "1.0";
  const targetVersion = "1.2";
  
  // Apply migrations sequentially
  let migrated = data;
  const path = getMigrationPath(currentVersion, targetVersion);
  
  for (const step of path) {
    migrated = migrations[step](migrated);
  }
  
  migrated.version = targetVersion;
  return migrated;
}

Loading Process:

  1. Detect version in loaded file
  2. If old version, show: "Upgrading project from v1.0 to v1.2..."
  3. Apply migrations sequentially
  4. Create backup of original file before overwriting
  5. Ask user to save upgraded version

Testing:

  • Keep example projects from each version
  • Automated tests to ensure all old versions load correctly

Estimated Effort: 5-8 hours (initial setup), 2-3 hours per future migration


9. Error Handling & Recovery

Why Important: Corrupted files, network issues, browser crashes require graceful handling.

Scenarios & Solutions:

Corrupted Project File:

function loadProject(filepath: string): Result<Project, Error> {
  try {
    const data = JSON.parse(fileContent);
    
    // Schema validation
    const validation = validateProjectSchema(data);
    if (!validation.valid) {
      return Error(`Invalid project structure: ${validation.errors}`);
    }
    
    return Ok(Project.fromJSON(data));
    
  } catch (e) {
    // JSON parse error
    if (autoSaveExists()) {
      return showRecoveryDialog();
    } else {
      return Error("Could not load project. File may be corrupted.");
    }
  }
}

Auto-Save Recovery Dialog:

┌───────────────────────────────────────────┐
│ ⚠️  Project File Could Not Be Loaded     │
│                                           │
│ The file may be corrupted. However, we   │
│ found auto-saved versions:                │
│                                           │
│ 🔵 Auto-save from 2 minutes ago         │
│ 🔵 Auto-save from 5 minutes ago         │
│ 🔵 Auto-save from 10 minutes ago        │
│                                           │
│ [Recover] [Cancel]                        │
└───────────────────────────────────────────┘

Export Validation Error:

┌───────────────────────────────────────────┐
│ ❌ Cannot Export - Validation Errors      │
│                                           │
│ The following issues must be fixed:       │
│                                           │
│ • Node "q-dimensions" has no connections │
│ • Cycle detected: start → door → start   │
│ • Node "q-color" missing required title  │
│                                           │
│ [Show Details] [Close]                    │
└───────────────────────────────────────────┘

Backup Strategy:

  • Before any save operation, create backup: project.navproj.backup.json
  • Keep last 3 backups rotating
  • Manual "Restore from Backup" option in File menu

Estimated Effort: 8-12 hours


10. Search/Filter in Canvas

Why Important: Finding specific nodes in a 50+ node flow is time-consuming.

Search Features:

Quick Search (Ctrl+F):

┌─────────────────────────────────────┐
│ 🔍 Search: door___                 │
│                                     │
│ Results (3):                        │
│ • q-door-type (Question)            │
│ • q-door-size (Form)                │
│ • door-accessories (Results)        │
└─────────────────────────────────────┘

Search Criteria:

  • Node title
  • Node ID (tileId)
  • Question type
  • Button captions
  • Notes content

Results Interaction:

  • Click result → zoom to node and highlight
  • Arrow down/up to cycle through results
  • Esc to close search

Advanced Filter:

Filter Nodes:
☑ Questions   ☑ Forms   ☑ Results   ☑ Custom Tiles
☑ Connected   ☐ Orphaned
Text: [          ]

Visual Highlighting:

  • Matching nodes: bright blue border
  • Dimmed view: non-matching nodes become semi-transparent
  • Match count badge: "3 of 47 nodes match"

Estimated Effort: 6-10 hours


Nice-to-Have Features

11. Comments & Annotations

Use Case: Team collaboration, leaving TODO notes, explaining complex logic.

Implementation:

  • Node-level Notes: Add "Notes" field in config panel (already exists in data)
  • Canvas Comments: Floating text boxes on canvas (like sticky notes)
  • Connection Labels: Add text labels to connection arrows
  • Visual Indicators: 💬 icon on nodes with comments

Estimated Effort: 5-8 hours


12. Grouping/Visual Containers

Use Case: Visually organize related nodes without creating a custom tile.

Implementation:

  • Background Rectangle: Dotted/dashed border around group of nodes
  • Group Label: Title at top of container
  • Color Coding: Different colors for different product categories
  • Collapse/Expand: Optional ability to collapse group to single icon
  • Move Group: Drag group title to move all contained nodes

Example:

╭─ Storm Door Questions ──────────────╮
│                                     │
│  [Color] → [Size] → [Accessories]  │
│                                     │
╰─────────────────────────────────────╯

Estimated Effort: 10-15 hours


13. Diff/Compare View

Use Case: Understanding what changed between versions, code review, debugging.

Implementation:

  • Side-by-side Canvas: Show two versions simultaneously
  • Visual Diff Indicators:
    • 🟢 Green: New nodes
    • 🔴 Red: Deleted nodes
    • 🟡 Yellow: Modified nodes
    • Updated connections shown with dashed lines
  • Change Summary: "5 nodes added, 3 modified, 1 deleted, 7 connections changed"

Estimated Effort: 15-20 hours


14. Accessibility

Why Important: Legal compliance (ADA/WCAG), inclusive design.

Requirements:

Keyboard Navigation:

  • Tab through all interactive elements
  • Arrow keys to navigate between nodes
  • Enter to activate/edit
  • Full functionality without mouse

Screen Reader Support:

  • Proper ARIA labels on all elements
  • Announce node selections
  • Describe connections meaningfully
  • Accessible forms in config panel

Visual Accessibility:

  • High contrast mode
  • Color-blind friendly palette options
  • Configurable font sizes
  • Focus indicators (visible keyboard focus)

Estimated Effort: 15-25 hours


15. Testing/Debugging Mode (Enhanced Details)

Why Important: Validate navigation logic before deployment, catch errors early.

Features:

Interactive Simulation:

  • Start at "start" node
  • Display actual question UI as users would see it
  • Click buttons/fill forms to progress
  • Visual path highlighting shows route taken

Breakpoints:

  • Set breakpoints on specific nodes
  • Execution pauses when reached
  • Inspect current state

Variable Inspector:

  • Show all current variable values
  • Display filters applied
  • Show which products match current filter

Path History:

Path Taken:
1. start → Door
2. q-door-type → Storm Door
3. q-color → White
4. q-dimensions → Width: 32, Height: 81
5. results → 3 products match

Test Cases:

  • Save common test scenarios
  • Replay to verify behavior after changes
  • Automated regression testing

Estimated Effort: 20-30 hours


16. Export to Diagram Image

Use Case: Documentation, presentations, stakeholder communication.

Export Formats:

  • PNG: Raster image, good for presentations
  • SVG: Vector image, scalable, good for print
  • PDF: Multi-page for large flows

Export Options:

  • Full canvas or selected region
  • Current viewport only
  • Include/exclude connection labels
  • Background color/transparency
  • Resolution/DPI settings

Implementation:

function exportToPNG(canvas: CanvasElement, options: ExportOptions) {
  const svgData = canvas.toSVG();
  const canvas2d = createCanvas(options.width, options.height);
  const ctx = canvas2d.getContext('2d');
  
  // Render SVG to canvas
  renderSVGToCanvas(svgData, ctx);
  
  // Export as PNG
  return canvas2d.toDataURL('image/png');
}

Estimated Effort: 8-12 hours


Security & Collaboration Concerns

17. Multi-User Collaboration (Future Consideration)

Why Important: Teams working together on complex flows.

Challenges:

  • Simultaneous Edits: Two people editing same node
  • Conflict Resolution: Merge conflicts like Git
  • Real-time Sync: Show others' cursors and selections
  • Locking: Prevent conflicts by locking edited nodes

Implementation Options:

  • Operational Transform (Google Docs approach)
  • CRDT (Conflict-Free Replicated Data Types)
  • Last-Write-Wins (simplest, but loses data)

Recommendation: Not for MVP. Add in Phase 6+ if team collaboration is critical.

Estimated Effort: 80-120 hours (complex)


18. Validation & Security

Why Important: Prevent malicious or corrupted files from breaking the editor.

Validation on Load:

function validateProjectFile(data: any): ValidationResult {
  const errors: string[] = [];
  
  // Schema validation (JSON Schema)
  if (!data.version) errors.push("Missing version");
  if (!data.nodes || !Array.isArray(data.nodes)) {
    errors.push("Invalid nodes structure");
  }
  
  // Security checks
  data.nodes?.forEach(node => {
    // Sanitize HTML in notes field
    if (node.config?.notes) {
      node.config.notes = sanitizeHTML(node.config.notes);
    }
    
    // Check for excessively large data
    const nodeSize = JSON.stringify(node).length;
    if (nodeSize > 100000) {
      errors.push(`Node ${node.id} exceeds size limit`);
    }
  });
  
  // File size limit (prevent DoS)
  const totalSize = JSON.stringify(data).length;
  if (totalSize > 10000000) {  // 10MB limit
    errors.push("Project file too large");
  }
  
  return {
    valid: errors.length === 0,
    errors
  };
}

HTML Sanitization:

  • Use library like DOMPurify for notes field
  • Prevent XSS attacks via malicious HTML

Estimated Effort: 5-8 hours


Implementation Priority Recommendations

Phase 1 (MVP Must-Haves):

  • Core canvas with drag-drop nodes
  • Basic config panel
  • Connections (simple)
  • Save/Load project files
  • Export to navigation.json
  • Undo/Redo (even basic implementation)
  • Keyboard shortcuts (at least Ctrl+Z, Ctrl+S, Delete)
  • Auto-save to localStorage
  • Error handling for corrupted files

Estimated: 40-50 hours


Phase 2 (Important for Usability):

  • Button grouping & output colors
  • Default values configuration
  • Connection routing (Bezier curves)
  • Copy/Paste
  • Full keyboard shortcuts
  • Canvas search
  • Circular dependency detection
  • Basic validation & error messages

Estimated: 50-65 hours


Phase 3 (Advanced Features):

  • Custom tiles (sub-flows)
  • Hierarchical navigation
  • Performance testing & optimization (if needed)
  • Backwards compatibility system
  • Testing/debugging mode
  • Auto-layout algorithm

Estimated: 50-70 hours


Phase 4 (Polish & Optional):

  • Comments & annotations
  • Grouping/containers
  • Export to image
  • Accessibility improvements
  • Diff/compare view
  • Advanced routing algorithms

Estimated: 40-60 hours


Future/Optional:

  • Multi-user collaboration (complex, separate project)
  • Cloud storage integration
  • Mobile responsive design
  • Version control UI (Git integration)
  • Analytics dashboard

Bottom Line: Gaps Addressed

Critical Gaps (must be added to plan):

  1. Undo/Redo - Absolutely essential, add to Phase 1
  2. Copy/Paste - Users will expect this, add to Phase 2
  3. Auto-Save - Prevent data loss, add to Phase 1
  4. Error Handling - Graceful failures, add to Phase 1

Important Gaps (should be added): 5. Keyboard Shortcuts - Power user efficiency, Phase 2 6. Performance Strategy - Test early, optimize if needed 7. Circular Dependencies - Validation requirement, Phase 2 8. Search/Filter - Usability for large flows, Phase 2

Nice-to-Have (can be deferred): 9. Comments - Collaboration aid, Phase 4 10. Grouping - Visual organization, Phase 4 11. Diff View - Advanced feature, Phase 4 12. Accessibility - Important but can be iterative 13. Export to Image - Documentation aid, Phase 4

Out of Scope for Now: 14. Multi-User Collaboration - Too complex for initial versions 15. Mobile App - Desktop-first approach recommended

All critical and important gaps are now documented in this comprehensive planning document!


Conclusion

Feasibility: Highly Feasible

This visual editor is definitely possible and would significantly improve the maintainability of complex navigation flows.

Complexity: Medium-High

  • Core functionality (nodes, connections, config panel): Medium complexity
  • Advanced features (grouping, validation, testing): Higher complexity
  • Total project could be completed in 3-4 weeks of focused development

Value Proposition: Very High

  • Dramatically reduces time to create/edit navigation flows
  • Visual representation makes logic much clearer
  • Reduces errors from manual JSON editing
  • Lowers barrier to entry for non-technical users
  • Makes complex branching logic manageable
  1. Prototype: Build a simple proof-of-concept with React Flow to validate the approach
  2. Feedback: Show prototype to potential users for feedback
  3. Iterate: Refine the UI/UX based on feedback
  4. MVP: Build Phase 1 & 2 (core canvas + basic editing)
  5. Evaluate: Assess if it's worth continuing to advanced features
  6. Complete: Finish remaining phases based on needs

Alternative: Lower-Effort Options

If a full visual editor is too much effort:

  1. JSON Schema Editor: Use existing JSON form builders with validation
  2. Spreadsheet Import: Design navigation in Excel/Google Sheets, import to JSON
  3. Template Generator: Web form to fill in, generates navigation.json
  4. Better Documentation: Improve JSON comments and examples for manual editing

This visual editor would transform navigation.json management from a manual, error-prone process into an intuitive, visual workflow—similar to how Talend transformed data integration pipelines.