Archive Management: - Moved RALPH_LOOP, CANVAS, and dashboard implementation plans to archive/review/ for CEO review - Moved completed restructuring plan and protocol v1 to archive/historical/ - Moved old session summaries to archive/review/ New HQ Documentation (docs/hq/): - README.md: Overview of Atomizer-HQ multi-agent optimization team - PROJECT_STRUCTURE.md: Standard KB-integrated project layout with Hydrotech reference - KB_CONVENTIONS.md: Knowledge Base accumulation principles with generation tracking - AGENT_WORKFLOWS.md: Project lifecycle phases and agent handoffs (OP_09 integration) - STUDY_CONVENTIONS.md: Technical study execution standards and atomizer_spec.json format Index Update: - Reorganized docs/00_INDEX.md with HQ docs prominent - Updated structure to reflect new agent-focused organization - Maintained core documentation access for engineers No files deleted, only moved to appropriate archive locations.
12 KiB
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:
- Resizable Panels - Right pane (chat/config) is fixed at 384px, cannot be adjusted
- Disabled Palette Items - Model, Solver, Algorithm, Surrogate are grayed out and not draggable
- 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:
- Add a Model node when creating a new study from scratch
- Configure the Solver type
- Choose an Algorithm
- 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
- Allow selection of solver engine type
- Configure solution type
- 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"