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
This commit is contained in:
2026-01-24 15:17:34 -05:00
parent 2cb8dccc3a
commit a3f18dc377
38 changed files with 1172 additions and 2570 deletions

View File

@@ -0,0 +1,452 @@
# Canvas UX Design - Study Management Flow
**Created**: January 16, 2026
**Status**: Design Phase
---
## Problem Statement
The Canvas Builder needs clear answers to these questions:
1. **When processing a canvas**, does it overwrite the current study or create a new one?
2. **How do users start fresh** - create a new study from scratch?
3. **How does the Home page** handle study selection vs. creation?
4. **What's the relationship** between Canvas, Dashboard, and study context?
---
## Proposed Solution: Study-Aware Canvas
### Core Concepts
| Concept | Description |
|---------|-------------|
| **Study Context** | Global state tracking which study is "active" |
| **Canvas Mode** | Either "editing existing" or "creating new" |
| **Process Dialog** | Explicit choice: update vs. create new |
| **Study Browser** | Unified view for selecting/creating studies |
---
## User Flows
### Flow 1: Open Existing Study → View/Edit → Run
```
Home Page
├── Study List shows all studies with status
│ • bracket_v3 [Running] 47/100 trials
│ • mirror_wfe_v2 [Paused] 23/50 trials
│ • beam_freq [Complete] 100/100 trials
└── User clicks "bracket_v3"
┌─────────────────────────────────────┐
│ Study: bracket_v3 │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Dashboard │ Results │ Canvas │ │
│ └─────────────────────────────────┘ │
│ │
│ [Run Optimization] [Edit Canvas] │
└─────────────────────────────────────┘
├── "Dashboard" → Live monitoring
├── "Results" → Reports & analysis
└── "Canvas" → View/edit workflow
Canvas loads from optimization_config.json
Shows all nodes + connections
User can modify and re-process
```
### Flow 2: Create New Study from Scratch
```
Home Page
└── User clicks [+ New Study]
┌─────────────────────────────────────┐
│ Create New Study │
│ │
│ Study Name: [my_new_bracket_____] │
│ │
│ Category: [Bracket ▼] │
│ ├─ Bracket │
│ ├─ Beam │
│ ├─ Mirror │
│ └─ + New Category... │
│ │
│ Start with: │
│ ● Blank Canvas │
│ ○ Template: [Mass Minimization ▼] │
│ ○ Copy from: [bracket_v3 ▼] │
│ │
│ [Cancel] [Create Study] │
└─────────────────────────────────────┘
Backend creates folder structure:
studies/Bracket/my_new_bracket/
├── 1_config/
├── 2_iterations/
└── 3_results/
Opens Canvas in "New Study" mode
Header shows: "Creating: my_new_bracket"
Canvas is blank (or has template)
```
### Flow 3: Canvas → Process → Create/Update Decision
```
User is in Canvas (either mode)
└── Clicks [Process with Claude]
┌─────────────────────────────────────────────┐
│ Generate Optimization │
│ │
│ Claude will generate: │
│ • optimization_config.json │
│ • run_optimization.py │
│ │
│ ─────────────────────────────────────────── │
│ │
│ If editing existing study: │
│ ┌─────────────────────────────────────────┐ │
│ │ ● Update current study │ │
│ │ Will modify: bracket_v3 │ │
│ │ ⚠ Overwrites existing config │ │
│ │ │ │
│ │ ○ Create new study │ │
│ │ Name: [bracket_v4_____________] │ │
│ │ □ Copy model files from bracket_v3 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ If creating new study: │
│ ┌─────────────────────────────────────────┐ │
│ │ Creating: my_new_bracket │ │
│ │ │ │
│ │ Model files needed: │
│ │ □ Upload .prt file │
│ │ □ Upload .sim file │
│ │ - or - │
│ │ □ Copy from: [bracket_v3 ▼] │ │
│ └─────────────────────────────────────────┘ │
│ │
│ [Cancel] [Generate & Create] │
└─────────────────────────────────────────────┘
Claude processes intent:
1. Validates configuration
2. Generates optimization_config.json
3. Creates run_optimization.py
4. If new study: switches context to it
Success dialog:
┌─────────────────────────────────────┐
│ ✓ Study Created Successfully │
│ │
│ bracket_v4 is ready to run │
│ │
│ [View Dashboard] [Run Optimization]│
└─────────────────────────────────────┘
```
---
## UI Components
### 1. Home Page (Redesigned)
```
┌─────────────────────────────────────────────────────────────────┐
│ Atomizer [+ New Study]│
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Search studies... 🔍 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Recent Studies ││
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────┐││
│ │ │ 🟢 bracket_v3 Running │││
│ │ │ Bracket • 47/100 trials • Best: 2.34 kg │││
│ │ │ [Dashboard] [Canvas] [Results] │││
│ │ └─────────────────────────────────────────────────────────┘││
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────┐││
│ │ │ 🟡 mirror_wfe_v2 Paused │││
│ │ │ Mirror • 23/50 trials • Best: 5.67 nm │││
│ │ │ [Dashboard] [Canvas] [Results] │││
│ │ └─────────────────────────────────────────────────────────┘││
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────┐││
│ │ │ ✓ beam_freq_tuning Completed │││
│ │ │ Beam • 100/100 trials • Best: 0.12 Hz error │││
│ │ │ [Dashboard] [Canvas] [Results] │││
│ │ └─────────────────────────────────────────────────────────┘││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ Categories: [All] [Bracket] [Beam] [Mirror] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 2. Study Header (When Study Selected)
```
┌─────────────────────────────────────────────────────────────────┐
│ ← Back bracket_v3 🟢 Running 47/100 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┬──────────┬──────────┬──────────┐ │
│ │Dashboard │ Results │ Canvas │ Settings │ │
│ └──────────┴──────────┴──────────┴──────────┘ │
│ │
│ [Run Optimization ▶] [Pause ⏸] [Generate Report] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 3. Canvas Header (Context-Aware)
**When editing existing study:**
```
┌─────────────────────────────────────────────────────────────────┐
│ Canvas Builder Editing: bracket_v3 │
├─────────────────────────────────────────────────────────────────┤
│ [Templates] [Import] 5 nodes [Validate] [Process ▶] │
└─────────────────────────────────────────────────────────────────┘
```
**When creating new study:**
```
┌─────────────────────────────────────────────────────────────────┐
│ Canvas Builder Creating: my_new_bracket │
├─────────────────────────────────────────────────────────────────┤
│ [Templates] [Import] 0 nodes [Validate] [Create Study ▶] │
└─────────────────────────────────────────────────────────────────┘
```
### 4. Process/Create Dialog
```
┌─────────────────────────────────────────────────────────────────┐
│ Generate Optimization ✕ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude will analyze your workflow and generate: │
│ • optimization_config.json │
│ • run_optimization.py (using Atomizer protocols) │
│ │
│ ───────────────────────────────────────────────────────────────│
│ │
│ Save to: │
│ │
│ ○ Update existing study │
│ └─ bracket_v3 (overwrites current config) │
│ │
│ ● Create new study │
│ └─ Study name: [bracket_v4_______________] │
│ Category: [Bracket ▼] │
│ ☑ Copy model files from bracket_v3 │
│ │
│ ───────────────────────────────────────────────────────────────│
│ │
│ After creation: │
│ ☑ Open new study automatically │
│ ☐ Start optimization immediately │
│ │
│ [Cancel] [Generate] │
└─────────────────────────────────────────────────────────────────┘
```
---
## Technical Architecture
### 1. Global Study Context
```typescript
// contexts/StudyContext.tsx
interface Study {
path: string; // "Bracket/bracket_v3"
name: string; // "bracket_v3"
category: string; // "Bracket"
status: 'not_started' | 'running' | 'paused' | 'completed';
trialCount: number;
maxTrials: number;
bestValue?: number;
hasConfig: boolean;
}
interface StudyContextState {
// Current selection
currentStudy: Study | null;
isCreatingNew: boolean;
pendingStudyName: string | null;
// Actions
selectStudy: (studyPath: string) => Promise<void>;
createNewStudy: (name: string, category: string) => Promise<Study>;
closeStudy: () => void;
refreshStudies: () => Promise<void>;
// All studies
studies: Study[];
categories: string[];
}
```
### 2. Canvas Store Enhancement
```typescript
// hooks/useCanvasStore.ts additions
interface CanvasState {
// Existing...
nodes: Node[];
edges: Edge[];
// New: Study context
sourceStudyPath: string | null; // Where loaded from (null = new)
isModified: boolean; // Has unsaved changes
// Actions
loadFromStudy: (studyPath: string) => Promise<void>;
saveToStudy: (studyPath: string, overwrite: boolean) => Promise<void>;
markClean: () => void;
}
```
### 3. Backend API Additions
```python
# api/routes/studies.py
@router.post("/")
async def create_study(request: CreateStudyRequest):
"""Create new study folder structure."""
# Creates:
# studies/{category}/{name}/
# ├── 1_config/
# ├── 2_iterations/
# └── 3_results/
pass
@router.post("/{study_path}/copy-models")
async def copy_model_files(study_path: str, source_study: str):
"""Copy .prt, .sim, .fem files from another study."""
pass
@router.post("/{study_path}/generate")
async def generate_from_intent(study_path: str, intent: dict, overwrite: bool = False):
"""Generate optimization_config.json and run_optimization.py from canvas intent."""
pass
@router.get("/categories")
async def list_categories():
"""List all study categories."""
pass
```
### 4. File Structure
When a new study is created:
```
studies/
└── Bracket/
└── my_new_bracket/
├── 1_config/
│ └── (empty until "Process")
├── 2_iterations/
│ └── (empty until optimization runs)
└── 3_results/
└── (empty until optimization runs)
```
After "Process with Claude":
```
studies/
└── Bracket/
└── my_new_bracket/
├── 1_config/
│ ├── optimization_config.json ← Generated
│ └── workflow_config.json ← Generated (optional)
├── 2_iterations/
├── 3_results/
├── model.prt ← Copied from source
├── model_sim1.sim ← Copied from source
└── run_optimization.py ← Generated
```
---
## Workflow Summary
### Starting Points
| Entry Point | Context | Canvas Mode |
|-------------|---------|-------------|
| Home → Click study → Canvas | Existing study | Edit existing |
| Home → New Study → Blank | New study | Create new |
| Home → New Study → Template | New study | Create new (pre-filled) |
| Home → New Study → Copy from | New study | Create new (pre-filled) |
### Process Outcomes
| Canvas Mode | Process Choice | Result |
|-------------|----------------|--------|
| Edit existing | Update current | Overwrites config in same study |
| Edit existing | Create new | Creates new study, switches to it |
| Create new | (only option) | Creates files in new study |
### Post-Process Navigation
| Option | Action |
|--------|--------|
| Open Dashboard | Navigate to /dashboard with new study |
| Run Optimization | Start run_optimization.py, show Dashboard |
| Stay in Canvas | Keep editing (for iterations) |
---
## Benefits
1. **Clear mental model**: Users always know if they're editing or creating
2. **No accidental overwrites**: Explicit choice with warning
3. **Version control friendly**: Easy to create v2, v3, etc.
4. **Discoverable**: Home page shows all studies at a glance
5. **Flexible entry points**: Multiple ways to start/continue work
---
## Implementation Priority
1. **Home Page redesign** with study list and "New Study" button
2. **Study context** global state
3. **Create Study dialog** with options
4. **Process dialog** with update/create choice
5. **Canvas context awareness** (header shows current study)
6. **Backend endpoints** for study creation and file generation
---
*This design enables a clean, intuitive workflow from study discovery to optimization execution.*

View File

@@ -0,0 +1,619 @@
# Canvas V3 - Comprehensive Fix & Enhancement Plan
**Created**: January 16, 2026
**Status**: Planning
**Priority**: High
---
## Problem Analysis
### Critical Bugs (Must Fix)
| Issue | Impact | Root Cause (Likely) |
|-------|--------|---------------------|
| **Atomizer Assistant broken** | High - core feature unusable | WebSocket connection or chat hook error |
| **Cannot delete connections** | High - workflow editing blocked | Missing edge selection/delete handler |
| **Drag & drop positioning wrong** | Medium - UX frustration | Position calculation not accounting for scroll/zoom |
### Data Loading Issues
| Issue | Impact | Root Cause (Likely) |
|-------|--------|---------------------|
| **Auto-connect missing** | High - manual work required | `loadFromConfig` creates nodes but not edges |
| **Missing elements (OPD extractor)** | High - incomplete workflows | Incomplete parsing of `optimization_config.json` |
| **Constraints not shown** | High - incomplete workflows | Constraints not parsed from config |
| **Algorithm not pre-selected** | Medium - extra clicks | Algorithm node not created from config |
### UI/UX Issues
| Issue | Impact | Root Cause (Likely) |
|-------|--------|---------------------|
| **Interface too small** | Medium - wasted screen space | Fixed dimensions, not responsive |
| **Insufficient contrast** | Medium - accessibility | White text on light blue background |
| **Font size too small** | Low - readability | Hardcoded small font sizes |
### Feature Requests
| Feature | Value | Complexity |
|---------|-------|------------|
| **Auto-complete with Claude** | High - smart assistance | Medium |
| **Templates/guidelines** | Medium - onboarding | Low |
| **Canvas ↔ Assistant integration** | High - conversational control | High |
---
## Implementation Strategy
### Phased Approach
```
Phase 1: Critical Fixes (2 hours)
Phase 2: Data Loading (3 hours)
Phase 3: UI/UX Polish (2 hours)
Phase 4: Claude Integration (3 hours)
Phase 5: Testing & Commit (1 hour)
```
---
## Phase 1: Critical Bug Fixes
### 1.1 Fix Atomizer Assistant Error
**Investigation Steps:**
1. Check `ChatPanel.tsx` for error handling
2. Check `useCanvasChat.ts` hook for connection issues
3. Verify WebSocket endpoint `/api/chat/` is working
4. Check if `useChat.ts` base hook has errors
**Likely Fix:**
- Add error boundary around chat component
- Add null checks for WebSocket connection
- Provide fallback UI when chat unavailable
### 1.2 Enable Connection Deletion
**Current State:** Edges can't be selected or deleted
**Implementation:**
```tsx
// In AtomizerCanvas.tsx
<ReactFlow
edgesUpdatable={true}
edgesFocusable={true}
deleteKeyCode={['Backspace', 'Delete']}
onEdgeClick={(event, edge) => selectEdge(edge.id)}
/>
```
**Store Update:**
```typescript
// In useCanvasStore.ts
deleteEdge: (edgeId: string) => {
set((state) => ({
edges: state.edges.filter((e) => e.id !== edgeId),
}));
},
```
### 1.3 Fix Drag & Drop Positioning
**Problem:** New nodes appear at wrong position (not where dropped)
**Fix in AtomizerCanvas.tsx:**
```typescript
const onDrop = useCallback((event: DragEvent) => {
event.preventDefault();
if (!reactFlowInstance.current || !reactFlowWrapper.current) return;
const type = event.dataTransfer.getData('application/reactflow') as NodeType;
if (!type) return;
// Get correct position accounting for viewport transform
const bounds = reactFlowWrapper.current.getBoundingClientRect();
const position = reactFlowInstance.current.screenToFlowPosition({
x: event.clientX - bounds.left,
y: event.clientY - bounds.top,
});
addNode(type, position);
}, [addNode]);
```
---
## Phase 2: Data Loading Improvements
### 2.1 Enhanced `loadFromConfig` Function
**Goal:** When loading a study, create ALL nodes AND edges automatically.
**Current Problems:**
- Nodes created but not connected
- Some extractors/constraints missing
- Algorithm not created
**New Implementation Strategy:**
```typescript
loadFromConfig: (config: OptimizationConfig) => {
const nodes: Node[] = [];
const edges: Edge[] = [];
// Layout constants
const COLS = { model: 50, dvar: 50, solver: 250, extractor: 450, obj: 650, algo: 850 };
const ROW_HEIGHT = 100;
const START_Y = 100;
// Track IDs for connections
const nodeIds = {
model: '',
solver: '',
dvars: [] as string[],
extractors: {} as Record<string, string>, // extractor_id -> node_id
objectives: [] as string[],
constraints: [] as string[],
algorithm: '',
};
// 1. Create Model Node
if (config.nx_model) {
const id = `model_${Date.now()}`;
nodeIds.model = id;
nodes.push({
id,
type: 'model',
position: { x: COLS.model, y: START_Y },
data: {
type: 'model',
label: 'Model',
configured: true,
filePath: config.nx_model.sim_path || config.nx_model.prt_path,
fileType: config.nx_model.sim_path ? 'sim' : 'prt',
},
});
}
// 2. Create Solver Node
if (config.solver) {
const id = `solver_${Date.now()}`;
nodeIds.solver = id;
nodes.push({
id,
type: 'solver',
position: { x: COLS.solver, y: START_Y },
data: {
type: 'solver',
label: 'Solver',
configured: true,
solverType: `SOL${config.solver.solution_type}`,
},
});
// Connect Model → Solver
if (nodeIds.model) {
edges.push({
id: `e_model_solver`,
source: nodeIds.model,
target: nodeIds.solver,
animated: true,
});
}
}
// 3. Create Design Variables (connected to Model)
config.design_variables?.forEach((dv, i) => {
const id = `dvar_${i}_${Date.now()}`;
nodeIds.dvars.push(id);
nodes.push({
id,
type: 'designVar',
position: { x: COLS.dvar, y: START_Y + 150 + i * ROW_HEIGHT },
data: {
type: 'designVar',
label: dv.name,
configured: true,
expressionName: dv.nx_expression || dv.name,
minValue: dv.lower_bound,
maxValue: dv.upper_bound,
unit: dv.unit,
},
});
// Connect DVar → Model (or keep disconnected, they're inputs)
});
// 4. Create Extractors from objectives AND constraints
const allExtractors = new Set<string>();
config.objectives?.forEach(obj => allExtractors.add(obj.extractor_id));
config.constraints?.forEach(con => {
if (con.extractor_id) allExtractors.add(con.extractor_id);
});
let extractorRow = 0;
allExtractors.forEach((extractorId) => {
const id = `extractor_${extractorId}_${Date.now()}`;
nodeIds.extractors[extractorId] = id;
// Find extractor config
const objWithExt = config.objectives?.find(o => o.extractor_id === extractorId);
const conWithExt = config.constraints?.find(c => c.extractor_id === extractorId);
nodes.push({
id,
type: 'extractor',
position: { x: COLS.extractor, y: START_Y + extractorRow * ROW_HEIGHT },
data: {
type: 'extractor',
label: extractorId,
configured: true,
extractorId: extractorId,
extractorName: objWithExt?.name || conWithExt?.name || extractorId,
},
});
extractorRow++;
// Connect Solver → Extractor
if (nodeIds.solver) {
edges.push({
id: `e_solver_${extractorId}`,
source: nodeIds.solver,
target: id,
});
}
});
// 5. Create Objectives (connected to Extractors)
config.objectives?.forEach((obj, i) => {
const id = `obj_${i}_${Date.now()}`;
nodeIds.objectives.push(id);
nodes.push({
id,
type: 'objective',
position: { x: COLS.obj, y: START_Y + i * ROW_HEIGHT },
data: {
type: 'objective',
label: obj.name,
configured: true,
name: obj.name,
direction: obj.direction,
weight: obj.weight,
},
});
// Connect Extractor → Objective
const extractorNodeId = nodeIds.extractors[obj.extractor_id];
if (extractorNodeId) {
edges.push({
id: `e_ext_obj_${i}`,
source: extractorNodeId,
target: id,
});
}
});
// 6. Create Constraints (connected to Extractors)
config.constraints?.forEach((con, i) => {
const id = `con_${i}_${Date.now()}`;
nodeIds.constraints.push(id);
const objY = START_Y + (config.objectives?.length || 0) * ROW_HEIGHT;
nodes.push({
id,
type: 'constraint',
position: { x: COLS.obj, y: objY + i * ROW_HEIGHT },
data: {
type: 'constraint',
label: con.name,
configured: true,
name: con.name,
operator: con.type === 'upper' ? '<=' : con.type === 'lower' ? '>=' : '==',
value: con.upper_bound ?? con.lower_bound ?? con.target,
},
});
// Connect Extractor → Constraint
if (con.extractor_id) {
const extractorNodeId = nodeIds.extractors[con.extractor_id];
if (extractorNodeId) {
edges.push({
id: `e_ext_con_${i}`,
source: extractorNodeId,
target: id,
});
}
}
});
// 7. Create Algorithm Node
if (config.optimization) {
const id = `algo_${Date.now()}`;
nodeIds.algorithm = id;
nodes.push({
id,
type: 'algorithm',
position: { x: COLS.algo, y: START_Y },
data: {
type: 'algorithm',
label: 'Algorithm',
configured: true,
method: config.optimization.sampler || 'TPE',
maxTrials: config.optimization.n_trials || 100,
},
});
// Connect Objectives → Algorithm
nodeIds.objectives.forEach((objId, i) => {
edges.push({
id: `e_obj_algo_${i}`,
source: objId,
target: id,
});
});
}
// 8. Create Surrogate Node (if enabled)
if (config.surrogate?.enabled) {
const id = `surrogate_${Date.now()}`;
nodes.push({
id,
type: 'surrogate',
position: { x: COLS.algo, y: START_Y + 150 },
data: {
type: 'surrogate',
label: 'Surrogate',
configured: true,
enabled: true,
modelType: config.surrogate.type || 'MLP',
minTrials: config.surrogate.min_trials || 20,
},
});
}
// Apply to store
set({
nodes,
edges,
selectedNode: null,
validation: { valid: false, errors: [], warnings: [] },
});
};
```
### 2.2 Parse Full Config Structure
**Ensure we handle:**
- `nx_model.sim_path` / `prt_path` / `fem_path`
- `solver.solution_type`
- `design_variables[]` with all fields
- `objectives[]` with `extractor_id`, `name`, `direction`, `weight`
- `constraints[]` with `extractor_id`, `type`, `upper_bound`, `lower_bound`
- `optimization.sampler`, `n_trials`
- `surrogate.enabled`, `type`, `min_trials`
- `post_processing[]` (for future)
---
## Phase 3: UI/UX Polish
### 3.1 Responsive Full-Screen Canvas
**CanvasView.tsx:**
```tsx
export function CanvasView() {
return (
<div className="h-screen w-screen flex flex-col overflow-hidden">
{/* Minimal header */}
<header className="flex-shrink-0 h-10 bg-dark-900 border-b border-dark-700 px-4 flex items-center justify-between">
<span className="text-sm font-medium text-white">Canvas Builder</span>
<div className="flex gap-2">
<button>Templates</button>
<button>Import</button>
<button>Clear</button>
</div>
</header>
{/* Canvas fills remaining space */}
<main className="flex-1 min-h-0">
<AtomizerCanvas />
</main>
</div>
);
}
```
### 3.2 Fix Contrast Issues
**Problem:** White text on light blue is hard to read
**Solution:** Use darker backgrounds or different text colors
```css
/* Node backgrounds */
.bg-dark-850 /* #0A1525 - dark enough for white text */
/* Avoid light blue backgrounds with white text */
/* If using blue, use dark blue (#1e3a5f) or switch to light text */
/* Specific fixes */
.node-header {
background: #0F1E32; /* dark-800 */
color: #FFFFFF;
}
.node-content {
background: #0A1525; /* dark-850 */
color: #E2E8F0; /* light gray */
}
/* Badge/pill text */
.badge-primary {
background: rgba(0, 212, 230, 0.15); /* primary with low opacity */
color: #00D4E6; /* primary-400 */
border: 1px solid rgba(0, 212, 230, 0.3);
}
```
### 3.3 Increase Font Sizes
**Current vs New:**
| Element | Current | New |
|---------|---------|-----|
| Node label | 12px | 14px |
| Node detail | 10px | 12px |
| Palette item | 12px | 14px |
| Panel headers | 14px | 16px |
| Config labels | 10px | 12px |
---
## Phase 4: Claude Integration
### 4.1 Fix Chat Panel Connection
**Error Handling:**
```tsx
function ChatPanel({ onClose }: ChatPanelProps) {
const { messages, isConnected, isThinking, error } = useCanvasChat();
if (error) {
return (
<div className="flex flex-col items-center justify-center h-full p-4">
<AlertCircle className="text-red-400 mb-2" size={24} />
<p className="text-red-400 text-sm text-center">{error}</p>
<button
onClick={reconnect}
className="mt-4 px-3 py-1.5 bg-dark-700 rounded text-sm"
>
Retry Connection
</button>
</div>
);
}
// ... rest of component
}
```
### 4.2 Auto-Complete with Claude
**Concept:** A button that sends current canvas state to Claude and asks for suggestions.
**UI:**
- Button: "Complete with Claude" next to Validate
- Opens chat panel with Claude's analysis
- Claude suggests: missing nodes, connections, configuration improvements
**Implementation:**
```typescript
// In useCanvasChat.ts
const autoCompleteWithClaude = useCallback(async () => {
const intent = toIntent();
const message = `Analyze this Canvas workflow and suggest what's missing or could be improved:
\`\`\`json
${JSON.stringify(intent, null, 2)}
\`\`\`
Please:
1. Identify any missing required components
2. Suggest extractors that should be added based on the objectives
3. Recommend connections that should be made
4. Propose any configuration improvements
Be specific and actionable.`;
await sendMessage(message);
}, [toIntent, sendMessage]);
```
### 4.3 Canvas ↔ Assistant Integration (Future)
**Vision:** Claude can modify the canvas through conversation.
**Commands:**
- "Add a displacement extractor"
- "Connect the mass extractor to objective 1"
- "Set the algorithm to CMA-ES with 200 trials"
- "Load the bracket_v3 study"
**Implementation Approach:**
1. Define canvas manipulation actions as Claude tools
2. Parse Claude responses for action intents
3. Execute actions via store methods
**MCP Tools (New):**
- `canvas_add_node` - Add a node of specified type
- `canvas_remove_node` - Remove a node by ID
- `canvas_connect` - Connect two nodes
- `canvas_disconnect` - Remove a connection
- `canvas_configure` - Update node configuration
- `canvas_load_study` - Load a study into canvas
---
## Phase 5: Testing & Validation
### Test Cases
| Test | Steps | Expected |
|------|-------|----------|
| Load study with connections | Import → Select study → Load | All nodes + edges appear |
| Delete connection | Click edge → Press Delete | Edge removed |
| Drag & drop position | Drag node to specific spot | Node appears at drop location |
| Chat panel opens | Click chat icon | Panel opens without error |
| Full screen canvas | Open /canvas | Canvas fills window |
| Contrast readable | View all nodes | All text legible |
### Build Verification
```bash
cd atomizer-dashboard/frontend
npm run build
# Must pass without errors
```
---
## Files to Modify
| File | Changes |
|------|---------|
| `useCanvasStore.ts` | Enhanced `loadFromConfig`, `deleteEdge` |
| `AtomizerCanvas.tsx` | Edge deletion, drag/drop fix, responsive |
| `CanvasView.tsx` | Full-screen layout |
| `ChatPanel.tsx` | Error handling, reconnect |
| `useCanvasChat.ts` | Auto-complete function, error state |
| `BaseNode.tsx` | Font sizes, contrast |
| `NodePalette.tsx` | Font sizes |
| `NodeConfigPanel.tsx` | Font sizes, contrast |
---
## Summary
**Total Effort:** ~11 hours across 5 phases
**Priority Order:**
1. Fix Atomizer Assistant (blocking)
2. Fix connection deletion (blocking editing)
3. Fix data loading (core functionality)
4. UI/UX polish (user experience)
5. Claude integration (enhancement)
**Success Criteria:**
- [ ] All bugs from user report fixed
- [ ] Loading a study shows ALL elements with connections
- [ ] Canvas is responsive and readable
- [ ] Chat panel works without errors
- [ ] Build passes without errors
---
*Plan created for Ralph Loop autonomous execution.*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff