feat(canvas): Studio Enhancement Phase 3 & 4 - undo/redo and Monaco editor
Phase 3 - Undo/Redo System: - Create generic useUndoRedo hook with configurable options - Add localStorage persistence for per-study history (max 30 steps) - Create useSpecUndoRedo hook integrating with useSpecStore - Add useUndoRedoKeyboard hook for Ctrl+Z/Ctrl+Y shortcuts - Add undo/redo buttons to canvas header with tooltips - Debounced history recording (1s delay after changes) Phase 4 - Monaco Code Editor: - Create CodeEditorPanel component with Monaco editor - Add Python syntax highlighting and auto-completion - Include pyNastran/OP2 specific completions - Add Claude AI code generation integration (placeholder) - Include code validation/run functionality - Show output variables preview section - Add copy-to-clipboard and generation prompt UI Dependencies: - Add @monaco-editor/react package Technical: - All TypeScript checks pass - All 15 unit tests pass - Production build successful
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { ClipboardList, Download, Trash2, Layers, Home, ChevronRight, Save, RefreshCw, Zap, MessageSquare, X, Folder, SlidersHorizontal } from 'lucide-react';
|
||||
import { ClipboardList, Download, Trash2, Layers, Home, ChevronRight, Save, RefreshCw, Zap, MessageSquare, X, Folder, SlidersHorizontal, Undo2, Redo2 } from 'lucide-react';
|
||||
import { AtomizerCanvas } from '../components/canvas/AtomizerCanvas';
|
||||
import { SpecRenderer } from '../components/canvas/SpecRenderer';
|
||||
import { NodePalette } from '../components/canvas/palette/NodePalette';
|
||||
@@ -12,6 +12,7 @@ import { NodeConfigPanelV2 } from '../components/canvas/panels/NodeConfigPanelV2
|
||||
import { ChatPanel } from '../components/canvas/panels/ChatPanel';
|
||||
import { useCanvasStore } from '../hooks/useCanvasStore';
|
||||
import { useSpecStore, useSpec, useSpecLoading, useSpecIsDirty, useSelectedNodeId } from '../hooks/useSpecStore';
|
||||
import { useSpecUndoRedo, useUndoRedoKeyboard } from '../hooks/useSpecUndoRedo';
|
||||
import { useStudy } from '../context/StudyContext';
|
||||
import { useChat } from '../hooks/useChat';
|
||||
import { CanvasTemplate } from '../lib/canvas/templates';
|
||||
@@ -54,6 +55,13 @@ export function CanvasView() {
|
||||
const { setSelectedStudy, studies } = useStudy();
|
||||
const { clearSpec, setSpecFromWebSocket } = useSpecStore();
|
||||
|
||||
// Undo/Redo for spec mode
|
||||
const undoRedo = useSpecUndoRedo();
|
||||
const { undo, redo, canUndo, canRedo, historyLength } = undoRedo;
|
||||
|
||||
// Enable keyboard shortcuts for undo/redo (Ctrl+Z, Ctrl+Y)
|
||||
useUndoRedoKeyboard(undoRedo);
|
||||
|
||||
// Active study ID comes ONLY from URL - don't auto-load from context
|
||||
// This ensures /canvas shows empty canvas, /canvas/{id} shows the study
|
||||
const activeStudyId = urlStudyId;
|
||||
@@ -318,6 +326,39 @@ export function CanvasView() {
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Undo/Redo Buttons (spec mode only) */}
|
||||
{useSpecMode && activeStudyId && (
|
||||
<>
|
||||
<div className="w-px h-6 bg-dark-600" />
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={undo}
|
||||
disabled={!canUndo}
|
||||
className={`p-1.5 rounded-lg transition-colors ${
|
||||
canUndo
|
||||
? 'text-dark-200 hover:bg-dark-700 hover:text-white'
|
||||
: 'text-dark-600 cursor-not-allowed'
|
||||
}`}
|
||||
title={`Undo (Ctrl+Z)${historyLength > 0 ? ` - ${historyLength} steps` : ''}`}
|
||||
>
|
||||
<Undo2 size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={redo}
|
||||
disabled={!canRedo}
|
||||
className={`p-1.5 rounded-lg transition-colors ${
|
||||
canRedo
|
||||
? 'text-dark-200 hover:bg-dark-700 hover:text-white'
|
||||
: 'text-dark-600 cursor-not-allowed'
|
||||
}`}
|
||||
title="Redo (Ctrl+Y)"
|
||||
>
|
||||
<Redo2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => setShowTemplates(true)}
|
||||
className="px-3 py-1.5 bg-primary-600 hover:bg-primary-500 text-white text-sm rounded-lg transition-colors flex items-center gap-1.5"
|
||||
|
||||
Reference in New Issue
Block a user