Files
Atomizer/docs/plans/CANVAS_UX_IMPROVEMENTS.md
Anto01 a3f18dc377 chore: Project cleanup and Canvas UX improvements (Phase 7-9)
## Cleanup (v0.5.0)
- Delete 102+ orphaned MCP session temp files
- Remove build artifacts (htmlcov, dist, __pycache__)
- Archive superseded plan docs (RALPH_LOOP V2/V3, CANVAS V3, etc.)
- Move debug/analysis scripts from tests/ to tools/analysis/
- Archive redundant NX journals to archive/nx_journals/
- Archive monolithic PROTOCOL.md to docs/archive/
- Update .gitignore with missing patterns
- Clean old study files (optimization_log_old.txt, run_optimization_old.py)

## Canvas UX (Phases 7-9)
- Phase 7: Resizable panels with localStorage persistence
  - Left sidebar: 200-400px, Right panel: 280-600px
  - New useResizablePanel hook and ResizeHandle component
- Phase 8: Enable all palette items
  - All 8 node types now draggable
  - Singleton logic for model/solver/algorithm/surrogate
- Phase 9: Solver configuration
  - Add SolverEngine type (nxnastran, mscnastran, python, etc.)
  - Add NastranSolutionType (SOL101-SOL200)
  - Engine/solution dropdowns in config panel
  - Python script path support

## Documentation
- Update CHANGELOG.md with recent versions
- Update docs/00_INDEX.md
- Create examples/README.md
- Add docs/plans/CANVAS_UX_IMPROVEMENTS.md
2026-01-24 15:17:34 -05:00

12 KiB

Canvas UX Improvements - Master Plan

Created: January 2026 Status: Planning Branch: feature/studio-enhancement

Overview

This plan addresses three major UX issues in the Canvas Builder:

  1. Resizable Panels - Right pane (chat/config) is fixed at 384px, cannot be adjusted
  2. Disabled Palette Items - Model, Solver, Algorithm, Surrogate are grayed out and not draggable
  3. Solver Type Selection - Solver node should allow selection of solver type (NX Nastran, Python, etc.)

Phase 7: Resizable Panels

Current State

  • Left sidebar: Fixed 240px (expanded) or 56px (collapsed)
  • Right panel (Chat/Config): Fixed 384px
  • Canvas: Takes remaining space

Requirements

  • Users should be able to drag panel edges to resize
  • Minimum/maximum constraints for usability
  • Persist panel sizes in localStorage
  • Smooth resize with proper cursor feedback

Implementation

7.1 Create Resizable Panel Hook

// hooks/useResizablePanel.ts
interface ResizablePanelState {
  width: number;
  isDragging: boolean;
  startDrag: (e: React.MouseEvent) => void;
}

function useResizablePanel(
  key: string,
  defaultWidth: number,
  minWidth: number,
  maxWidth: number
): ResizablePanelState

7.2 Update CanvasView Layout

  • Wrap left sidebar with resizer
  • Wrap right panel with resizer
  • Add visual drag handles (thin border that highlights on hover)
  • Add cursor: col-resize on hover

7.3 Files to Modify

File Changes
hooks/useResizablePanel.ts NEW - Resize hook with localStorage persistence
pages/CanvasView.tsx Add resizers to left/right panels
components/canvas/ResizeHandle.tsx NEW - Visual resize handle component

7.4 Constraints

Panel Min Default Max
Left (Palette/Files) 200px 240px 400px
Right (Chat/Config) 280px 384px 600px

Phase 8: Enable All Palette Items

Current State

  • Model, Solver, Algorithm, Surrogate are marked canAdd: false
  • They appear grayed out with "Auto-created" text
  • Users cannot drag them to canvas

Problem Analysis

These nodes were marked as "synthetic" because they're derived from:

  • Model: From spec.model.sim.path
  • Solver: From model's solution type
  • Algorithm: From spec.optimization.algorithm
  • Surrogate: From spec.optimization.surrogate

However, users need to:

  1. Add a Model node when creating a new study from scratch
  2. Configure the Solver type
  3. Choose an Algorithm
  4. Enable/configure Surrogate

Solution: Make All Items Draggable

8.1 Update NodePalette

// All items should be draggable
export const PALETTE_ITEMS: PaletteItem[] = [
  { 
    type: 'model', 
    label: 'Model', 
    canAdd: true,  // Changed from false
    description: 'NX/FEM model file',
  },
  { 
    type: 'solver', 
    label: 'Solver', 
    canAdd: true,  // Changed from false
    description: 'Analysis solver',
  },
  // ... etc
];

8.2 Handle "Singleton" Nodes

Some nodes should only exist once on the canvas:

  • Model (only one model per study)
  • Solver (one solver)
  • Algorithm (one algorithm config)
  • Surrogate (optional, one)

When user drags a singleton that already exists:

  • Option A: Show warning toast "Model already exists"
  • Option B: Select the existing node instead of creating new
  • Recommended: Option B (select existing)

8.3 Update SpecRenderer Drop Handler

const onDrop = useCallback(async (event: DragEvent) => {
  const type = event.dataTransfer.getData('application/reactflow');
  
  // Check if singleton already exists
  const SINGLETON_TYPES = ['model', 'solver', 'algorithm', 'surrogate'];
  if (SINGLETON_TYPES.includes(type)) {
    const existingNode = nodes.find(n => n.type === type);
    if (existingNode) {
      selectNode(existingNode.id);
      showNotification(`${type} already exists - selected it`);
      return;
    }
  }
  
  // Create new node...
}, [...]);

8.4 Default Data for New Node Types

function getDefaultNodeData(type: NodeType, position) {
  switch (type) {
    case 'model':
      return {
        name: 'Model',
        sim: { path: '', solver: 'nastran' },
        canvas_position: position,
      };
    case 'solver':
      return {
        name: 'Solver',
        type: 'nxnastran',  // Default solver
        solution_type: 'SOL101',
        canvas_position: position,
      };
    case 'algorithm':
      return {
        name: 'Algorithm',
        type: 'TPE',
        budget: { max_trials: 100 },
        canvas_position: position,
      };
    case 'surrogate':
      return {
        name: 'Surrogate',
        enabled: false,
        model_type: 'MLP',
        min_trials: 20,
        canvas_position: position,
      };
    // ... existing cases
  }
}

8.5 Files to Modify

File Changes
components/canvas/palette/NodePalette.tsx Set canAdd: true for all items
components/canvas/SpecRenderer.tsx Handle singleton logic in onDrop
lib/spec/converter.ts Ensure synthetic nodes have proper IDs
hooks/useSpecStore.ts Add model/solver/algorithm to addNode support

Phase 9: Solver Type Selection

Current State

  • Solver node shows auto-detected solution type (SOL101, etc.)
  • No ability to change solver engine or configure it

Requirements

  1. Allow selection of solver engine type
  2. Configure solution type
  3. Support future solver types

Solver Types to Support

Solver Description Status
nxnastran NX Nastran (built-in) Current
mscnastran MSC Nastran (external) Future
python Python-based solver Future
abaqus Abaqus (via Python API) Future
ansys ANSYS (via Python API) Future

Solution Types per Solver

NX Nastran / MSC Nastran:

  • SOL101 - Linear Static
  • SOL103 - Normal Modes
  • SOL105 - Buckling
  • SOL106 - Nonlinear Static
  • SOL111 - Frequency Response
  • SOL112 - Transient Response
  • SOL200 - Design Optimization

Python Solver:

  • Custom (user-defined)

Schema Updates

9.1 Update AtomizerSpec Types

// types/atomizer-spec.ts

export type SolverEngine = 
  | 'nxnastran' 
  | 'mscnastran' 
  | 'python'
  | 'abaqus'
  | 'ansys';

export type NastranSolutionType = 
  | 'SOL101' 
  | 'SOL103' 
  | 'SOL105' 
  | 'SOL106' 
  | 'SOL111' 
  | 'SOL112'
  | 'SOL200';

export interface SolverConfig {
  /** Solver engine type */
  engine: SolverEngine;
  
  /** Solution type (for Nastran) */
  solution_type?: NastranSolutionType;
  
  /** Custom solver script path (for Python solver) */
  script_path?: string;
  
  /** Additional solver options */
  options?: Record<string, unknown>;
}

export interface Model {
  sim?: {
    path: string;
    solver: SolverConfig;  // Changed from just 'nastran' string
  };
  // ...
}

9.2 Update SolverNode Component

// components/canvas/nodes/SolverNode.tsx

function SolverNodeComponent(props: NodeProps<SolverNodeData>) {
  const { data } = props;
  
  return (
    <BaseNode {...props} icon={<Cpu size={16} />} iconColor="text-violet-400">
      <div className="flex flex-col gap-1">
        <span className="text-sm font-medium">{data.engine || 'nxnastran'}</span>
        <span className="text-xs text-dark-400">
          {data.solution_type || 'Auto-detect'}
        </span>
      </div>
    </BaseNode>
  );
}

9.3 Solver Configuration Panel

Add to NodeConfigPanelV2.tsx:

function SolverNodeConfig({ spec }: SpecConfigProps) {
  const { patchSpec } = useSpecStore();
  const solver = spec.model?.sim?.solver || { engine: 'nxnastran' };
  
  const handleEngineChange = (engine: SolverEngine) => {
    patchSpec('model.sim.solver.engine', engine);
  };
  
  const handleSolutionTypeChange = (type: NastranSolutionType) => {
    patchSpec('model.sim.solver.solution_type', type);
  };
  
  return (
    <>
      <div>
        <label className={labelClass}>Solver Engine</label>
        <select 
          value={solver.engine}
          onChange={(e) => handleEngineChange(e.target.value as SolverEngine)}
          className={selectClass}
        >
          <option value="nxnastran">NX Nastran</option>
          <option value="mscnastran">MSC Nastran</option>
          <option value="python">Python Script</option>
        </select>
      </div>
      
      {(solver.engine === 'nxnastran' || solver.engine === 'mscnastran') && (
        <div>
          <label className={labelClass}>Solution Type</label>
          <select
            value={solver.solution_type || ''}
            onChange={(e) => handleSolutionTypeChange(e.target.value as NastranSolutionType)}
            className={selectClass}
          >
            <option value="">Auto-detect from model</option>
            <option value="SOL101">SOL101 - Linear Static</option>
            <option value="SOL103">SOL103 - Normal Modes</option>
            <option value="SOL105">SOL105 - Buckling</option>
            <option value="SOL106">SOL106 - Nonlinear Static</option>
            <option value="SOL111">SOL111 - Frequency Response</option>
            <option value="SOL112">SOL112 - Transient Response</option>
          </select>
        </div>
      )}
      
      {solver.engine === 'python' && (
        <div>
          <label className={labelClass}>Solver Script</label>
          <input
            type="text"
            value={solver.script_path || ''}
            onChange={(e) => patchSpec('model.sim.solver.script_path', e.target.value)}
            placeholder="/path/to/solver.py"
            className={inputClass}
          />
          <p className="text-xs text-dark-500 mt-1">
            Python script that runs the analysis
          </p>
        </div>
      )}
    </>
  );
}

9.4 Files to Modify

File Changes
types/atomizer-spec.ts Add SolverEngine, SolverConfig types
components/canvas/nodes/SolverNode.tsx Show engine and solution type
components/canvas/panels/NodeConfigPanelV2.tsx Add SolverNodeConfig
lib/canvas/schema.ts Update SolverNodeData
Backend: config/spec_models.py Add SolverConfig Pydantic model

Implementation Order

Phase Effort Priority Dependencies
7.1 Resizable Panel Hook 2h High None
7.2 CanvasView Resizers 2h High 7.1
8.1 Enable Palette Items 1h High None
8.2 Singleton Logic 2h High 8.1
8.3 Default Node Data 1h High 8.2
9.1 Schema Updates 2h Medium None
9.2 SolverNode UI 1h Medium 9.1
9.3 Solver Config Panel 2h Medium 9.1, 9.2

Total Estimated Effort: ~13 hours


Success Criteria

Phase 7 (Resizable Panels)

  • Left panel can be resized between 200-400px
  • Right panel can be resized between 280-600px
  • Resize handles show cursor feedback
  • Panel sizes persist across page reload
  • Double-click on handle resets to default

Phase 8 (Enable Palette Items)

  • All 8 node types are draggable from palette
  • Dragging singleton to canvas with existing node selects existing
  • Toast notification explains the behavior
  • New studies can start with empty canvas and add Model first

Phase 9 (Solver Selection)

  • Solver node shows engine type (nxnastran, python, etc.)
  • Clicking solver node opens config panel
  • Can select solver engine from dropdown
  • Nastran solvers show solution type dropdown
  • Python solver shows script path input
  • Changes persist to atomizer_spec.json

Future Considerations

Additional Solver Support

  • ANSYS integration via pyANSYS
  • Abaqus integration via abaqus-python
  • OpenFOAM for CFD
  • Custom Python solvers with standardized interface

Multi-Solver Workflows

  • Support for chained solvers (thermal → structural)
  • Co-simulation workflows
  • Parallel solver execution

Algorithm Node Enhancement

  • Similar to Solver, allow algorithm selection
  • Show algorithm-specific parameters
  • Support custom algorithms

Commit Strategy

# Phase 7
git commit -m "feat: Add resizable panels to canvas view"

# Phase 8
git commit -m "feat: Enable all palette items with singleton handling"

# Phase 9
git commit -m "feat: Add solver type selection and configuration"