From fdcc92d8ff708df28909fab64dbefe84025b2ded Mon Sep 17 00:00:00 2001 From: Josh Head Date: Wed, 11 Jun 2025 05:37:32 -0700 Subject: [PATCH 01/12] Save Windows compatibility changes before switching branches --- .gitattributes | 0 .gitignore | 0 CHANGELOG.md | 0 README.md | 0 .../20250603_124545/Workstation_OEW_components.tsx | 0 electron-launch.mjs | 0 electron/AudioAnalysis/analyze_audio.py | 0 electron/audioAnalysis.ts | 0 electron/contextMenu.ts | 0 electron/handlers.ts | 0 electron/main.ts | 0 electron/menu.ts | 0 electron/preload.ts | 0 electron/tsconfig.json | 0 nodemon.json | 0 package.json | 0 public/vite.svg | 0 scripts/auto-sync-version.js | 0 scripts/check-npm-dependencies.js | 0 scripts/cleanup-duplicates.sh | 0 scripts/consolidate-repo.sh | 0 scripts/create-backup.js | 0 scripts/fix-macos-dependencies.sh | 0 scripts/fix-maps-dependency.js | 0 scripts/fix-permissions.js | 0 scripts/fix-python-deps.js | 0 scripts/fix-start-electron.sh | 0 scripts/fix-tokenizers-macos.py | 0 scripts/fix-tokenizers-rust.sh | 0 scripts/fix-vite-not-found.sh | 0 scripts/fix-vite-startup.sh | 0 scripts/install-vite-deps.js | 0 scripts/refactor-imports-auto.js | 0 scripts/refactor-sample.js | 0 scripts/run-visual-tests.sh | 0 scripts/setup-aliases.js | 0 scripts/setup-dbus.sh | 0 scripts/setup-electron-symlinks.sh | 0 scripts/setup-x11.sh | 0 scripts/skip-failing-visual-tests.js | 0 scripts/start-electron.sh | 0 scripts/start-electron.sh.bak | 0 scripts/start-headless.sh | 0 scripts/system-check.js | 0 scripts/update-all.sh | 0 scripts/update-submodules.js | 0 scripts/update-workspace.js | 0 tsconfig.app.json | 0 tsconfig.node.json | 0 vite.config.d.ts | 0 vite.config.js | 0 vite.config.ts | 0 vitest.config.ts | 0 workstation/backend/direct_graphql_test.py | 0 workstation/backend/scripts/fix_ipfs_dependency.py | 0 workstation/backend/setup_venv.sh | 0 workstation/backend/test_graphql.py | 0 workstation/frontend/src/App.tsx | 0 58 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 .gitattributes mode change 100755 => 100644 .gitignore mode change 100755 => 100644 CHANGELOG.md mode change 100755 => 100644 README.md mode change 100755 => 100644 archives/workstation-backups/20250603_124545/Workstation_OEW_components.tsx mode change 100755 => 100644 electron-launch.mjs mode change 100755 => 100644 electron/AudioAnalysis/analyze_audio.py mode change 100755 => 100644 electron/audioAnalysis.ts mode change 100755 => 100644 electron/contextMenu.ts mode change 100755 => 100644 electron/handlers.ts mode change 100755 => 100644 electron/main.ts mode change 100755 => 100644 electron/menu.ts mode change 100755 => 100644 electron/preload.ts mode change 100755 => 100644 electron/tsconfig.json mode change 100755 => 100644 nodemon.json mode change 100755 => 100644 package.json mode change 100755 => 100644 public/vite.svg mode change 100755 => 100644 scripts/auto-sync-version.js mode change 100755 => 100644 scripts/check-npm-dependencies.js mode change 100755 => 100644 scripts/cleanup-duplicates.sh mode change 100755 => 100644 scripts/consolidate-repo.sh mode change 100755 => 100644 scripts/create-backup.js mode change 100755 => 100644 scripts/fix-macos-dependencies.sh mode change 100755 => 100644 scripts/fix-maps-dependency.js mode change 100755 => 100644 scripts/fix-permissions.js mode change 100755 => 100644 scripts/fix-python-deps.js mode change 100755 => 100644 scripts/fix-start-electron.sh mode change 100755 => 100644 scripts/fix-tokenizers-macos.py mode change 100755 => 100644 scripts/fix-tokenizers-rust.sh mode change 100755 => 100644 scripts/fix-vite-not-found.sh mode change 100755 => 100644 scripts/fix-vite-startup.sh mode change 100755 => 100644 scripts/install-vite-deps.js mode change 100755 => 100644 scripts/refactor-imports-auto.js mode change 100755 => 100644 scripts/refactor-sample.js mode change 100755 => 100644 scripts/run-visual-tests.sh mode change 100755 => 100644 scripts/setup-aliases.js mode change 100755 => 100644 scripts/setup-dbus.sh mode change 100755 => 100644 scripts/setup-electron-symlinks.sh mode change 100755 => 100644 scripts/setup-x11.sh mode change 100755 => 100644 scripts/skip-failing-visual-tests.js mode change 100755 => 100644 scripts/start-electron.sh mode change 100755 => 100644 scripts/start-electron.sh.bak mode change 100755 => 100644 scripts/start-headless.sh mode change 100755 => 100644 scripts/system-check.js mode change 100755 => 100644 scripts/update-all.sh mode change 100755 => 100644 scripts/update-submodules.js mode change 100755 => 100644 scripts/update-workspace.js mode change 100755 => 100644 tsconfig.app.json mode change 100755 => 100644 tsconfig.node.json mode change 100755 => 100644 vite.config.d.ts mode change 100755 => 100644 vite.config.js mode change 100755 => 100644 vite.config.ts mode change 100755 => 100644 vitest.config.ts mode change 100755 => 100644 workstation/backend/direct_graphql_test.py mode change 100755 => 100644 workstation/backend/scripts/fix_ipfs_dependency.py mode change 100755 => 100644 workstation/backend/setup_venv.sh mode change 100755 => 100644 workstation/backend/test_graphql.py mode change 100755 => 100644 workstation/frontend/src/App.tsx diff --git a/.gitattributes b/.gitattributes old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 diff --git a/archives/workstation-backups/20250603_124545/Workstation_OEW_components.tsx b/archives/workstation-backups/20250603_124545/Workstation_OEW_components.tsx old mode 100755 new mode 100644 diff --git a/electron-launch.mjs b/electron-launch.mjs old mode 100755 new mode 100644 diff --git a/electron/AudioAnalysis/analyze_audio.py b/electron/AudioAnalysis/analyze_audio.py old mode 100755 new mode 100644 diff --git a/electron/audioAnalysis.ts b/electron/audioAnalysis.ts old mode 100755 new mode 100644 diff --git a/electron/contextMenu.ts b/electron/contextMenu.ts old mode 100755 new mode 100644 diff --git a/electron/handlers.ts b/electron/handlers.ts old mode 100755 new mode 100644 diff --git a/electron/main.ts b/electron/main.ts old mode 100755 new mode 100644 diff --git a/electron/menu.ts b/electron/menu.ts old mode 100755 new mode 100644 diff --git a/electron/preload.ts b/electron/preload.ts old mode 100755 new mode 100644 diff --git a/electron/tsconfig.json b/electron/tsconfig.json old mode 100755 new mode 100644 diff --git a/nodemon.json b/nodemon.json old mode 100755 new mode 100644 diff --git a/package.json b/package.json old mode 100755 new mode 100644 diff --git a/public/vite.svg b/public/vite.svg old mode 100755 new mode 100644 diff --git a/scripts/auto-sync-version.js b/scripts/auto-sync-version.js old mode 100755 new mode 100644 diff --git a/scripts/check-npm-dependencies.js b/scripts/check-npm-dependencies.js old mode 100755 new mode 100644 diff --git a/scripts/cleanup-duplicates.sh b/scripts/cleanup-duplicates.sh old mode 100755 new mode 100644 diff --git a/scripts/consolidate-repo.sh b/scripts/consolidate-repo.sh old mode 100755 new mode 100644 diff --git a/scripts/create-backup.js b/scripts/create-backup.js old mode 100755 new mode 100644 diff --git a/scripts/fix-macos-dependencies.sh b/scripts/fix-macos-dependencies.sh old mode 100755 new mode 100644 diff --git a/scripts/fix-maps-dependency.js b/scripts/fix-maps-dependency.js old mode 100755 new mode 100644 diff --git a/scripts/fix-permissions.js b/scripts/fix-permissions.js old mode 100755 new mode 100644 diff --git a/scripts/fix-python-deps.js b/scripts/fix-python-deps.js old mode 100755 new mode 100644 diff --git a/scripts/fix-start-electron.sh b/scripts/fix-start-electron.sh old mode 100755 new mode 100644 diff --git a/scripts/fix-tokenizers-macos.py b/scripts/fix-tokenizers-macos.py old mode 100755 new mode 100644 diff --git a/scripts/fix-tokenizers-rust.sh b/scripts/fix-tokenizers-rust.sh old mode 100755 new mode 100644 diff --git a/scripts/fix-vite-not-found.sh b/scripts/fix-vite-not-found.sh old mode 100755 new mode 100644 diff --git a/scripts/fix-vite-startup.sh b/scripts/fix-vite-startup.sh old mode 100755 new mode 100644 diff --git a/scripts/install-vite-deps.js b/scripts/install-vite-deps.js old mode 100755 new mode 100644 diff --git a/scripts/refactor-imports-auto.js b/scripts/refactor-imports-auto.js old mode 100755 new mode 100644 diff --git a/scripts/refactor-sample.js b/scripts/refactor-sample.js old mode 100755 new mode 100644 diff --git a/scripts/run-visual-tests.sh b/scripts/run-visual-tests.sh old mode 100755 new mode 100644 diff --git a/scripts/setup-aliases.js b/scripts/setup-aliases.js old mode 100755 new mode 100644 diff --git a/scripts/setup-dbus.sh b/scripts/setup-dbus.sh old mode 100755 new mode 100644 diff --git a/scripts/setup-electron-symlinks.sh b/scripts/setup-electron-symlinks.sh old mode 100755 new mode 100644 diff --git a/scripts/setup-x11.sh b/scripts/setup-x11.sh old mode 100755 new mode 100644 diff --git a/scripts/skip-failing-visual-tests.js b/scripts/skip-failing-visual-tests.js old mode 100755 new mode 100644 diff --git a/scripts/start-electron.sh b/scripts/start-electron.sh old mode 100755 new mode 100644 diff --git a/scripts/start-electron.sh.bak b/scripts/start-electron.sh.bak old mode 100755 new mode 100644 diff --git a/scripts/start-headless.sh b/scripts/start-headless.sh old mode 100755 new mode 100644 diff --git a/scripts/system-check.js b/scripts/system-check.js old mode 100755 new mode 100644 diff --git a/scripts/update-all.sh b/scripts/update-all.sh old mode 100755 new mode 100644 diff --git a/scripts/update-submodules.js b/scripts/update-submodules.js old mode 100755 new mode 100644 diff --git a/scripts/update-workspace.js b/scripts/update-workspace.js old mode 100755 new mode 100644 diff --git a/tsconfig.app.json b/tsconfig.app.json old mode 100755 new mode 100644 diff --git a/tsconfig.node.json b/tsconfig.node.json old mode 100755 new mode 100644 diff --git a/vite.config.d.ts b/vite.config.d.ts old mode 100755 new mode 100644 diff --git a/vite.config.js b/vite.config.js old mode 100755 new mode 100644 diff --git a/vite.config.ts b/vite.config.ts old mode 100755 new mode 100644 diff --git a/vitest.config.ts b/vitest.config.ts old mode 100755 new mode 100644 diff --git a/workstation/backend/direct_graphql_test.py b/workstation/backend/direct_graphql_test.py old mode 100755 new mode 100644 diff --git a/workstation/backend/scripts/fix_ipfs_dependency.py b/workstation/backend/scripts/fix_ipfs_dependency.py old mode 100755 new mode 100644 diff --git a/workstation/backend/setup_venv.sh b/workstation/backend/setup_venv.sh old mode 100755 new mode 100644 diff --git a/workstation/backend/test_graphql.py b/workstation/backend/test_graphql.py old mode 100755 new mode 100644 diff --git a/workstation/frontend/src/App.tsx b/workstation/frontend/src/App.tsx old mode 100755 new mode 100644 From 6a994f0173ab7401b5e443d82493db98a96e5e97 Mon Sep 17 00:00:00 2001 From: Josh Head Date: Wed, 11 Jun 2025 12:44:34 -0700 Subject: [PATCH 02/12] WIP: Save test configuration and Windows compatibility changes --- package.json | 4 ++- scripts/run-vitest-ui-windows.bat | 25 +++++++++++++++ scripts/run-vitest-ui-windows.ps1 | 52 +++++++++++++++++++++++++++++++ src/test/environment.test.ts | 18 +++++++++++ src/test/helpers/screenshot.ts | 3 -- vitest.config.ts | 15 +++++++-- 6 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 scripts/run-vitest-ui-windows.bat create mode 100644 scripts/run-vitest-ui-windows.ps1 create mode 100644 src/test/environment.test.ts diff --git a/package.json b/package.json index fef964a..aa492d6 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,9 @@ "start": "node electron-launch.js", "test": "vitest run", "test:watch": "vitest", - "test:ui": "vitest --ui --open", + "test:ui": "vitest --ui --port 3333", + "test:ui:windows": "powershell -ExecutionPolicy Bypass -File scripts/run-vitest-ui-windows.ps1", + "test:ui:safe": "vitest --ui --port 3333 --host 127.0.0.1 --open false", "test:ui:full": "concurrently \"pnpm run test:ui\" \"pnpm run test:visual:ui\" \"pnpm run test:python:ui\"", "test:integration": "vitest run --config vitest.integration.config.ts", "test:integration:ui": "vitest --ui --config vitest.integration.config.ts --open", diff --git a/scripts/run-vitest-ui-windows.bat b/scripts/run-vitest-ui-windows.bat new file mode 100644 index 0000000..eda421a --- /dev/null +++ b/scripts/run-vitest-ui-windows.bat @@ -0,0 +1,25 @@ +@echo off +echo Starting Vitest UI on Windows... +echo. +echo Checking for available ports... +netstat -ano | findstr :3333 > nul +if %errorlevel% == 0 ( + echo Port 3333 is in use, trying port 3334... + set TEST_PORT=3334 +) else ( + echo Port 3333 is available + set TEST_PORT=3333 +) + +echo. +echo Starting Vitest UI on port %TEST_PORT%... +npx vitest --ui --port %TEST_PORT% --host 127.0.0.1 + +echo. +echo If you see permission errors, try: +echo 1. Running as Administrator +echo 2. Checking Windows Firewall settings +echo 3. Using a different port +echo. +echo The UI should be available at: http://127.0.0.1:%TEST_PORT% +pause diff --git a/scripts/run-vitest-ui-windows.ps1 b/scripts/run-vitest-ui-windows.ps1 new file mode 100644 index 0000000..50765df --- /dev/null +++ b/scripts/run-vitest-ui-windows.ps1 @@ -0,0 +1,52 @@ +# PowerShell script to run Vitest UI with proper Windows port handling +Write-Host "Starting Vitest UI on Windows..." -ForegroundColor Green +Write-Host "" + +# Function to check if port is available +function Test-Port { + param([int]$Port) + try { + $listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Any, $Port) + $listener.Start() + $listener.Stop() + return $true + } + catch { + return $false + } +} + +# Find available port +$testPort = 3333 +if (-not (Test-Port $testPort)) { + Write-Host "Port $testPort is in use, trying port 3334..." -ForegroundColor Yellow + $testPort = 3334 + if (-not (Test-Port $testPort)) { + Write-Host "Port $testPort is also in use, trying port 3335..." -ForegroundColor Yellow + $testPort = 3335 + } +} + +Write-Host "Using port: $testPort" -ForegroundColor Green +Write-Host "" + +# Set environment variables for the process +$env:VITEST_UI_PORT = $testPort + +try { + Write-Host "Starting Vitest UI on port $testPort..." -ForegroundColor Cyan + Write-Host "UI will be available at: http://127.0.0.1:$testPort" -ForegroundColor Yellow + Write-Host "" + + # Run vitest with explicit configuration + & npx vitest --ui --port $testPort --host "127.0.0.1" --open false +} +catch { + Write-Host "Error starting Vitest UI: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "" + Write-Host "Troubleshooting steps:" -ForegroundColor Yellow + Write-Host "1. Check if Windows Firewall is blocking the port" -ForegroundColor White + Write-Host "2. Try running PowerShell as Administrator" -ForegroundColor White + Write-Host "3. Check if any antivirus software is blocking Node.js" -ForegroundColor White + Write-Host "4. Try a different port by editing vitest.config.ts" -ForegroundColor White +} diff --git a/src/test/environment.test.ts b/src/test/environment.test.ts new file mode 100644 index 0000000..d5adb3c --- /dev/null +++ b/src/test/environment.test.ts @@ -0,0 +1,18 @@ +// Simple test to verify test environment works +import { describe, it, expect } from 'vitest'; + +describe('Test Environment', () => { + it('should be able to run basic tests', () => { + expect(1 + 1).toBe(2); + }); + + it('should have access to global expect', () => { + expect(expect).toBeDefined(); + }); + + it('should be able to handle async tests', async () => { + const promise = Promise.resolve(42); + const result = await promise; + expect(result).toBe(42); + }); +}); diff --git a/src/test/helpers/screenshot.ts b/src/test/helpers/screenshot.ts index ad1d36b..cc8b5e6 100644 --- a/src/test/helpers/screenshot.ts +++ b/src/test/helpers/screenshot.ts @@ -1,5 +1,3 @@ -<<<<<<< HEAD -======= /** * Screenshot test helper for visual regression testing */ @@ -177,4 +175,3 @@ async function takeScreenshot( return Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIHWNgAAIAAAUAAY27m/MAAAAASUVORK5CYII=', 'base64'); } } ->>>>>>> feature/server-agnostic-config diff --git a/vitest.config.ts b/vitest.config.ts index 0f46407..24dc272 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -54,8 +54,19 @@ export default defineConfig({ html: "./test-results/html/index.html", }, reporters: ["default", "html"], - deps: { - inline: ["jest-image-snapshot"], + server: { + deps: { + optimizer: { + web: { + include: ["jest-image-snapshot"], + }, + }, + }, + }, + ui: { + port: 3333, + host: '127.0.0.1', + open: false, // Let's not auto-open to avoid permission issues }, exclude: ["**/node_modules/**", "**/__snapshots__/**"], testTimeout: 10000, // Increased timeout for visual tests From 444492210e2c5aa246cba05473d5ddd9c156a623 Mon Sep 17 00:00:00 2001 From: Josh Head Date: Wed, 11 Jun 2025 13:57:11 -0700 Subject: [PATCH 03/12] Add critical UI architecture documentation - Document importance of OEW-main UI module separation - Add comprehensive UI_ARCHITECTURE_PRINCIPLES.md guide - Update README with critical UI modularity warning - Create UI architecture safety check scripts (Windows + Unix) - Add npm scripts for UI integrity verification Critical: OEW-main folder contains primary DAW interface and must remain modular --- .gitmodules | 2 +- README.md | 15 +- docs/UI_ARCHITECTURE_PRINCIPLES.md | 263 +++++++++++++++++++++++++++++ package.json | 2 + scripts/check-ui-architecture.bat | 68 ++++++++ scripts/check-ui-architecture.sh | 65 +++++++ workstation/frontend/OEW-main | 2 +- 7 files changed, 412 insertions(+), 5 deletions(-) create mode 100644 docs/UI_ARCHITECTURE_PRINCIPLES.md create mode 100644 scripts/check-ui-architecture.bat create mode 100644 scripts/check-ui-architecture.sh diff --git a/.gitmodules b/.gitmodules index 229380c..90d89f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "workstation/frontend/OEW-main"] path = workstation/frontend/OEW-main url = https://github.com/jhead12/orpheus-engine.git - branch = feature/server-agnostic-config + branch = feature/server-agnostic-config-clean diff --git a/README.md b/README.md index e7412f1..1698127 100644 --- a/README.md +++ b/README.md @@ -243,19 +243,28 @@ npm run build # Creates packaged app in dist/ ### Architecture Notes -1. **Frontend**: +> **🚨 CRITICAL: UI Modularity** +> The `workstation/frontend/OEW-main/` directory contains the **primary DAW user interface** and must remain as a separate, modular component. This architectural separation enables independent UI development, enhanced testing capabilities, and future plugin/theme systems. **NEVER delete or merge this folder** - see [`docs/UI_ARCHITECTURE_PRINCIPLES.md`](docs/UI_ARCHITECTURE_PRINCIPLES.md) for detailed guidelines. + +1. **UI Architecture**: + - **OEW-main**: Primary React-based DAW interface (modular & independent) + - **Service Layer**: Clean API bridge between UI and backend services + - **Component Isolation**: Reusable UI components with comprehensive testing + - **Plugin Ready**: Architecture supports future plugins and themes + +2. **Frontend**: - Electron for native desktop features - React for UI components with hooks and context - TypeScript for type safety and better DX - Vite for fast development and building -2. **Testing**: +3. **Testing**: - Vitest for unit and integration tests - Visual regression testing with screenshots - Comprehensive component test coverage - Import alias resolution for clean test structure -3. **Development Experience**: +4. **Development Experience**: - Hot module replacement for instant feedback - TypeScript integration with zero compilation errors - ESLint for code quality and consistency diff --git a/docs/UI_ARCHITECTURE_PRINCIPLES.md b/docs/UI_ARCHITECTURE_PRINCIPLES.md new file mode 100644 index 0000000..5816d64 --- /dev/null +++ b/docs/UI_ARCHITECTURE_PRINCIPLES.md @@ -0,0 +1,263 @@ +# UI Architecture Principles - OEW-main Separation + +**Critical Design Decision: Maintaining UI Modularity** + +*Version 1.0 - Created June 11, 2025* + +## Executive Summary + +The `workstation/frontend/OEW-main` directory contains the **primary user interface** for the Orpheus Engine DAW and must be maintained as a **separate, modular component**. This architectural decision is fundamental to the project's scalability, maintainability, and future enhancement capabilities. + +## 🚨 CRITICAL WARNING + +**NEVER DELETE OR MERGE THE OEW-main FOLDER** + +The OEW-main folder is **NOT** expendable - it contains: +- Primary React components for the DAW interface +- Audio mixer and workstation controls +- Track management and timeline functionality +- Real-time audio processing UI +- Visual feedback systems +- User interaction handlers + +## Core Architectural Principles + +### 1. Separation of Concerns +``` +Main Repository (orpheus-engine-stagging) +├── Backend services (Python, Electron) +├── Build configuration +├── Documentation +└── workstation/frontend/OEW-main/ ← **PRIMARY UI MODULE** + ├── src/ ← Core UI components + ├── components/ ← Reusable UI elements + ├── screens/workstation/ ← Main DAW interface + └── services/ ← UI-specific services +``` + +### 2. Modular Development Benefits + +**Independent Development Cycles** +- UI can be updated without affecting backend +- Different teams can work on UI vs core engine +- Faster iteration on user experience improvements + +**Technology Stack Isolation** +- React/TypeScript UI stack separate from Python backend +- Independent dependency management +- Easier to upgrade UI frameworks without system-wide changes + +**Testing Isolation** +- UI tests run independently of backend tests +- Visual regression testing contained within UI module +- Component-level testing without full system setup + +## Why This Architecture Matters + +### 1. **Scalability** +```typescript +// UI can scale independently +OEW-main/ +├── src/components/ ← Atomic design components +├── src/screens/ ← Feature-specific screens +├── src/services/ ← UI business logic +└── src/test/ ← Comprehensive UI testing +``` + +### 2. **Maintainability** +- Clear boundaries between UI and backend concerns +- Easier to onboard new developers (UI vs backend specialization) +- Reduced coupling means fewer breaking changes + +### 3. **Enhancement Capabilities** +- Plugin architecture can extend UI without core changes +- Theme systems can be developed independently +- New UI features don't require backend modifications + +### 4. **Deployment Flexibility** +- UI can be deployed as static assets +- Backend services can run independently +- Enables distributed deployment strategies + +## Current UI Structure (OEW-main) + +### Critical Components + +**Core Workstation Interface** +``` +src/screens/workstation/ +├── components/ +│ ├── Mixer.tsx ← Primary audio mixer +│ ├── TrackVolumeSlider.tsx ← Volume controls +│ ├── Timeline.tsx ← Audio timeline +│ └── EffectsRack.tsx ← Audio effects UI +├── contexts/ +│ ├── WorkstationContext.tsx ← State management +│ └── AudioContext.tsx ← Audio engine bridge +└── services/ + ├── audioService.ts ← Audio processing interface + └── trackService.ts ← Track management +``` + +**Reusable Components** +``` +src/components/ +├── widgets/ ← Audio widgets (knobs, sliders) +├── visualization/ ← Audio visualizers +└── common/ ← Shared UI elements +``` + +### Testing Infrastructure +``` +src/test/ +├── helpers/ ← Test utilities +├── visual/ ← Visual regression tests +└── utils/ ← Mock implementations +``` + +## Integration Patterns + +### 1. **Service Layer Bridge** +```typescript +// UI communicates with backend through service layer +import { audioService } from '@orpheus/services'; + +const processAudio = async (audioData) => { + return await audioService.process(audioData); // ← Calls Python backend +}; +``` + +### 2. **Event-Driven Communication** +```typescript +// UI emits events, backend responds +workstationContext.emit('track.add', trackData); +workstationContext.on('track.processed', handleProcessedTrack); +``` + +### 3. **State Synchronization** +```typescript +// UI state syncs with backend state through defined interfaces +interface WorkstationState { + tracks: Track[]; + effects: Effect[]; + timeline: TimelineState; +} +``` + +## Development Guidelines + +### ✅ DO +- Keep UI logic within OEW-main +- Use the service layer for backend communication +- Maintain component isolation and reusability +- Write comprehensive tests for UI components +- Document UI architecture decisions + +### ❌ DON'T +- Mix backend logic with UI components +- Delete or merge the OEW-main folder +- Create tight coupling between UI and backend +- Bypass the service layer for backend calls +- Ignore UI testing requirements + +## Enhancement Strategies + +### 1. **Plugin Architecture** +```typescript +// UI can load plugins dynamically +interface UIPlugin { + name: string; + component: React.ComponentType; + integration: 'workstation' | 'sidebar' | 'modal'; +} +``` + +### 2. **Theme System** +```typescript +// Separate styling from component logic +const theme = { + mixer: { knobColor: '#00ff00' }, + timeline: { waveformColor: '#0066cc' } +}; +``` + +### 3. **Micro-Frontend Ready** +```typescript +// UI structure supports micro-frontend architecture +export const WorkstationApp = () => ( + + + + + +); +``` + +## Maintenance Protocols + +### 1. **Version Control Strategy** +- OEW-main tracks its own feature branches +- Integration testing happens at the main repo level +- UI changes are merged through proper review process + +### 2. **Dependency Management** +- UI dependencies managed independently +- Shared utilities defined in service layer +- Clear API contracts between UI and backend + +### 3. **Testing Strategy** +- Unit tests for individual components +- Integration tests for component interactions +- Visual regression tests for UI consistency +- End-to-end tests at the full system level + +## Future Considerations + +### 1. **Multi-Platform UI** +- OEW-main structure supports web, desktop, and mobile +- Component abstraction enables platform-specific implementations +- Shared business logic through service layer + +### 2. **Collaborative Features** +- UI can support real-time collaboration +- State synchronization through WebSocket services +- Multi-user interface components + +### 3. **AI Integration** +- UI components can integrate AI-powered features +- Smart suggestions and automated workflow assistance +- Machine learning enhanced user interactions + +## Conclusion + +The separation of the OEW-main UI module is **fundamental** to the Orpheus Engine's architecture. This modular approach: + +- **Enables rapid UI development** without backend dependencies +- **Supports independent testing** and quality assurance +- **Facilitates team collaboration** with clear component boundaries +- **Prepares for future enhancements** like plugins and themes +- **Maintains system scalability** as the project grows + +**Remember:** The OEW-main folder is the heart of the user experience. Protect it, nurture it, and let it evolve independently while maintaining clean integration with the backend services. + +--- + +## Quick Reference + +**Key Directories:** +- `workstation/frontend/OEW-main/src/` - Primary UI source code +- `workstation/frontend/OEW-main/src/screens/workstation/` - Main DAW interface +- `workstation/frontend/OEW-main/src/components/` - Reusable UI components +- `workstation/frontend/OEW-main/src/services/` - UI-backend bridge + +**Integration Points:** +- Service layer APIs +- Event-driven communication +- State management contexts +- WebSocket connections (future) + +**Testing Approach:** +- Component isolation tests +- Visual regression validation +- Integration test suites +- End-to-end system tests diff --git a/package.json b/package.json index aa492d6..3aed636 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,8 @@ "fix:permissions:unix": "node scripts/fix-permissions.js", "setup:electron": "chmod +x scripts/setup-electron-symlinks.sh && ./scripts/setup-electron-symlinks.sh", "setup:electron:win": "scripts\\setup-electron.bat", + "check:ui": "chmod +x scripts/check-ui-architecture.sh && ./scripts/check-ui-architecture.sh", + "check:ui:win": "scripts\\check-ui-architecture.bat", "install:all": "node scripts/install-all.js", "install:ffmpeg": "node scripts/install-ffmpeg.js", "install:vite": "node scripts/install-vite-deps.js", diff --git a/scripts/check-ui-architecture.bat b/scripts/check-ui-architecture.bat new file mode 100644 index 0000000..ffae12e --- /dev/null +++ b/scripts/check-ui-architecture.bat @@ -0,0 +1,68 @@ +@echo off +REM UI Architecture Safety Check for Windows +REM This script verifies the OEW-main UI module integrity + +echo 🔍 Checking UI Architecture Integrity... +echo. + +REM Check if OEW-main exists +if not exist "workstation\frontend\OEW-main" ( + echo ❌ CRITICAL: OEW-main UI module is missing! + echo This directory contains the primary DAW interface + echo Run: git submodule update --init --recursive + exit /b 1 +) + +REM Check if main UI components exist +set "components_missing=0" + +if not exist "workstation\frontend\OEW-main\src\screens\workstation\components\Mixer.tsx" ( + echo ⚠️ WARNING: Mixer.tsx component missing + set "components_missing=1" +) + +if not exist "workstation\frontend\OEW-main\src\contexts\WorkstationContext.tsx" ( + echo ⚠️ WARNING: WorkstationContext.tsx missing + set "components_missing=1" +) + +if not exist "workstation\frontend\OEW-main\src\services" ( + echo ⚠️ WARNING: UI services directory missing + set "components_missing=1" +) + +if %components_missing%==0 ( + echo ✅ UI Architecture: All critical components present +) else ( + echo. + echo 📖 See docs\UI_ARCHITECTURE_PRINCIPLES.md for guidance +) + +REM Check package.json exists +if not exist "workstation\frontend\OEW-main\package.json" ( + echo ❌ Missing OEW-main package.json - UI module incomplete + exit /b 1 +) else ( + echo ✅ UI Module: Package configuration present +) + +REM Check test infrastructure +if not exist "workstation\frontend\OEW-main\src\test" ( + echo ⚠️ WARNING: UI test infrastructure missing +) else ( + echo ✅ UI Testing: Test infrastructure present +) + +echo. +if %components_missing%==0 ( + echo 🏗️ UI Architecture Status: HEALTHY +) else ( + echo 🏗️ UI Architecture Status: NEEDS ATTENTION +) + +echo. +echo 📚 Resources: +echo - UI Architecture Guide: docs\UI_ARCHITECTURE_PRINCIPLES.md +echo - Test Infrastructure: workstation\frontend\OEW-main\src\test\ +echo - Main UI Components: workstation\frontend\OEW-main\src\screens\workstation\ +echo. diff --git a/scripts/check-ui-architecture.sh b/scripts/check-ui-architecture.sh new file mode 100644 index 0000000..ff36900 --- /dev/null +++ b/scripts/check-ui-architecture.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# UI Architecture Safety Check +# This script verifies the OEW-main UI module integrity + +echo "🔍 Checking UI Architecture Integrity..." +echo "" + +# Check if OEW-main exists +if [ ! -d "workstation/frontend/OEW-main" ]; then + echo "❌ CRITICAL: OEW-main UI module is missing!" + echo " This directory contains the primary DAW interface" + echo " Run: git submodule update --init --recursive" + exit 1 +fi + +# Check if main UI components exist +ui_components=( + "workstation/frontend/OEW-main/src/screens/workstation/components/Mixer.tsx" + "workstation/frontend/OEW-main/src/screens/workstation/components/Timeline.tsx" + "workstation/frontend/OEW-main/src/contexts/WorkstationContext.tsx" + "workstation/frontend/OEW-main/src/services" +) + +missing_components=() + +for component in "${ui_components[@]}"; do + if [ ! -e "$component" ]; then + missing_components+=("$component") + fi +done + +if [ ${#missing_components[@]} -gt 0 ]; then + echo "⚠️ WARNING: Some UI components are missing:" + for component in "${missing_components[@]}"; do + echo " - $component" + done + echo "" + echo "📖 See docs/UI_ARCHITECTURE_PRINCIPLES.md for guidance" +else + echo "✅ UI Architecture: All critical components present" +fi + +# Check package.json exists +if [ ! -f "workstation/frontend/OEW-main/package.json" ]; then + echo "❌ Missing OEW-main package.json - UI module incomplete" + exit 1 +else + echo "✅ UI Module: Package configuration present" +fi + +# Check test infrastructure +if [ ! -d "workstation/frontend/OEW-main/src/test" ]; then + echo "⚠️ WARNING: UI test infrastructure missing" +else + echo "✅ UI Testing: Test infrastructure present" +fi + +echo "" +echo "🏗️ UI Architecture Status: $([ ${#missing_components[@]} -eq 0 ] && echo "HEALTHY" || echo "NEEDS ATTENTION")" +echo "" +echo "📚 Resources:" +echo " - UI Architecture Guide: docs/UI_ARCHITECTURE_PRINCIPLES.md" +echo " - Test Infrastructure: workstation/frontend/OEW-main/src/test/" +echo " - Main UI Components: workstation/frontend/OEW-main/src/screens/workstation/" +echo "" diff --git a/workstation/frontend/OEW-main b/workstation/frontend/OEW-main index 7bf40d1..142efd8 160000 --- a/workstation/frontend/OEW-main +++ b/workstation/frontend/OEW-main @@ -1 +1 @@ -Subproject commit 7bf40d1be69084e7edd786127cf3840e9c554d91 +Subproject commit 142efd835fd2fb10f8b700f63ed12d6077801ca9 From f2ef6000ec70999bb7ca8102c4846f32fbf312aa Mon Sep 17 00:00:00 2001 From: Josh Head Date: Wed, 11 Jun 2025 14:33:19 -0700 Subject: [PATCH 04/12] Add Windows Port Access Troubleshooting Guide and Diagnostic Script --- docs/WINDOWS_PORT_TROUBLESHOOTING.md | 259 +++++++++++++++++++++++++++ package.json | 2 + scripts/check-ui-architecture.bat | 10 +- scripts/diagnose-ports.bat | 41 +++++ 4 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 docs/WINDOWS_PORT_TROUBLESHOOTING.md create mode 100644 scripts/diagnose-ports.bat diff --git a/docs/WINDOWS_PORT_TROUBLESHOOTING.md b/docs/WINDOWS_PORT_TROUBLESHOOTING.md new file mode 100644 index 0000000..5f380b8 --- /dev/null +++ b/docs/WINDOWS_PORT_TROUBLESHOOTING.md @@ -0,0 +1,259 @@ +# Windows Port Access Troubleshooting Guide + +**Resolving Node.js Port Permission Issues on Windows** + +*Version 1.0 - Created June 11, 2025* + +## Common Windows Port Blocking Issues + +### 1. **Windows Firewall** (Most Common) +Windows Defender Firewall often blocks Node.js applications from binding to ports. + +### 2. **Port Reservation** +Windows reserves certain port ranges for system services. + +### 3. **Antivirus Software** +Third-party antivirus may block Node.js network access. + +### 4. **User Account Control (UAC)** +Insufficient privileges for port binding. + +### 5. **Hyper-V Port Exclusions** +Hyper-V can reserve port ranges, conflicting with development servers. + +--- + +## Diagnostic Commands + +### Check Port Availability +```cmd +# Check if port is in use +netstat -ano | findstr :3333 +netstat -ano | findstr :51204 + +# Check Windows reserved ports +netsh int ipv4 show excludedportrange protocol=tcp +``` + +### Check Windows Firewall Rules +```powershell +# List firewall rules for Node.js +Get-NetFirewallRule | Where-Object {$_.DisplayName -like "*Node*"} + +# Check if Windows Firewall is blocking +Get-NetFirewallProfile | Select-Object Name,Enabled +``` + +--- + +## Solutions (In Order of Likelihood) + +### Solution 1: Configure Windows Firewall ⭐ **MOST LIKELY FIX** + +#### Option A: Add Firewall Exception (Recommended) +```powershell +# Run PowerShell as Administrator, then: + +# Allow Node.js through firewall +New-NetFirewallRule -DisplayName "Node.js Development Server" -Direction Inbound -Protocol TCP -LocalPort 3333 -Action Allow +New-NetFirewallRule -DisplayName "Vitest UI Server" -Direction Inbound -Protocol TCP -LocalPort 3333 -Action Allow + +# Allow npm/pnpm processes +New-NetFirewallRule -DisplayName "npm Development" -Direction Inbound -Program "C:\Program Files\nodejs\node.exe" -Action Allow +``` + +#### Option B: Temporarily Disable Windows Firewall (Testing Only) +```powershell +# ⚠️ Only for testing - remember to re-enable! +Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False + +# To re-enable later: +Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True +``` + +### Solution 2: Check Port Reservations + +#### Check Reserved Ports +```cmd +# Run Command Prompt as Administrator +netsh int ipv4 show excludedportrange protocol=tcp +netsh int ipv6 show excludedportrange protocol=tcp +``` + +#### Exclude Ports from Hyper-V (if needed) +```cmd +# If Hyper-V is reserving your ports: +netsh int ipv4 add excludedportrange protocol=tcp startport=3333 numberofports=1 +``` + +### Solution 3: Antivirus Configuration + +#### Common Antivirus Solutions: +- **Windows Defender**: Add Node.js to exclusions +- **McAfee/Norton**: Allow Node.js network access +- **Kaspersky**: Add to trusted applications + +#### Add Node.js to Windows Defender Exclusions: +```powershell +# Run as Administrator +Add-MpPreference -ExclusionProcess "node.exe" +Add-MpPreference -ExclusionProcess "npm.exe" +Add-MpPreference -ExclusionProcess "pnpm.exe" +``` + +### Solution 4: Run as Administrator + +#### Quick Test - Run PowerShell as Administrator: +```powershell +# Right-click PowerShell -> "Run as Administrator" +cd D:\github\orpheus-engine-stagging +pnpm run test:ui:safe +``` + +### Solution 5: Use Alternative Ports + +#### Try Different Port Ranges: +```powershell +# Try ports that are less likely to be blocked +npx vitest --ui --port 8080 --host 127.0.0.1 --open false +npx vitest --ui --port 9000 --host 127.0.0.1 --open false +npx vitest --ui --port 4000 --host 127.0.0.1 --open false +``` + +--- + +## Step-by-Step Troubleshooting + +### Step 1: Quick Firewall Fix (Try This First) +```powershell +# Open PowerShell as Administrator +# Copy and paste this entire block: + +Write-Host "Adding Node.js firewall exceptions..." -ForegroundColor Green +New-NetFirewallRule -DisplayName "Node.js Dev Server (Vitest)" -Direction Inbound -Protocol TCP -LocalPort 3333 -Action Allow -ErrorAction SilentlyContinue +New-NetFirewallRule -DisplayName "Node.js Dev Server (Alt)" -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow -ErrorAction SilentlyContinue +New-NetFirewallRule -DisplayName "Node.js Application" -Direction Inbound -Program "C:\Program Files\nodejs\node.exe" -Action Allow -ErrorAction SilentlyContinue + +Write-Host "Adding Windows Defender exclusions..." -ForegroundColor Green +Add-MpPreference -ExclusionProcess "node.exe" -ErrorAction SilentlyContinue +Add-MpPreference -ExclusionProcess "pnpm.exe" -ErrorAction SilentlyContinue + +Write-Host "Configuration complete! Try running tests again." -ForegroundColor Cyan +``` + +### Step 2: Test Port Availability +```cmd +# Test if the port is now available +telnet 127.0.0.1 3333 +# If it says "Could not open connection", the port is available for binding +``` + +### Step 3: Run Test UI +```powershell +cd D:\github\orpheus-engine-stagging +pnpm run test:ui:safe +``` + +--- + +## Automated Fix Script + +Save this as `scripts/fix-windows-ports.ps1`: + +```powershell +# Windows Port Access Fix Script +param( + [int]$Port = 3333, + [string]$NodePath = "C:\Program Files\nodejs\node.exe" +) + +Write-Host "🔧 Fixing Windows port access issues..." -ForegroundColor Yellow +Write-Host "" + +# Check if running as Administrator +if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + Write-Host "❌ This script requires Administrator privileges." -ForegroundColor Red + Write-Host " Right-click PowerShell and select 'Run as Administrator'" -ForegroundColor White + exit 1 +} + +Write-Host "✅ Running with Administrator privileges" -ForegroundColor Green + +# Add firewall rules +Write-Host "🔥 Configuring Windows Firewall..." -ForegroundColor Cyan +try { + New-NetFirewallRule -DisplayName "Vitest UI Server" -Direction Inbound -Protocol TCP -LocalPort $Port -Action Allow -ErrorAction Stop + Write-Host " ✅ Added firewall rule for port $Port" -ForegroundColor Green +} catch { + Write-Host " ⚠️ Firewall rule may already exist" -ForegroundColor Yellow +} + +try { + New-NetFirewallRule -DisplayName "Node.js Development" -Direction Inbound -Program $NodePath -Action Allow -ErrorAction Stop + Write-Host " ✅ Added firewall rule for Node.js" -ForegroundColor Green +} catch { + Write-Host " ⚠️ Node.js firewall rule may already exist" -ForegroundColor Yellow +} + +# Add Windows Defender exclusions +Write-Host "🛡️ Configuring Windows Defender..." -ForegroundColor Cyan +try { + Add-MpPreference -ExclusionProcess "node.exe" -ErrorAction Stop + Write-Host " ✅ Added Node.js to Defender exclusions" -ForegroundColor Green +} catch { + Write-Host " ⚠️ Node.js may already be excluded" -ForegroundColor Yellow +} + +# Check port availability +Write-Host "🔍 Checking port availability..." -ForegroundColor Cyan +$portTest = Test-NetConnection -ComputerName "127.0.0.1" -Port $Port -InformationLevel Quiet +if ($portTest) { + Write-Host " ⚠️ Port $Port is currently in use" -ForegroundColor Yellow +} else { + Write-Host " ✅ Port $Port is available" -ForegroundColor Green +} + +Write-Host "" +Write-Host "🎉 Configuration complete!" -ForegroundColor Green +Write-Host " Try running: pnpm run test:ui:safe" -ForegroundColor White +Write-Host "" +``` + +--- + +## hosts File (Usually Not the Issue) + +The Windows hosts file (`C:\Windows\System32\drivers\etc\hosts`) typically doesn't affect localhost port binding, but you can check: + +```cmd +# View hosts file +type C:\Windows\System32\drivers\etc\hosts + +# Should contain: +# 127.0.0.1 localhost +``` + +--- + +## Quick Commands to Try Right Now + +### Option 1: Add Firewall Rule (Recommended) +```powershell +# Open PowerShell as Administrator and run: +New-NetFirewallRule -DisplayName "Vitest UI" -Direction Inbound -Protocol TCP -LocalPort 3333 -Action Allow +``` + +### Option 2: Try Different Port +```bash +# In your regular terminal: +npx vitest --ui --port 8080 --host 127.0.0.1 --open false +``` + +### Option 3: Check What's Blocking +```cmd +# Check what's using ports: +netstat -ano | findstr :3333 +netstat -ano | findstr :51204 +``` + +The **Windows Firewall** is the most common culprit. Try the firewall rule first! diff --git a/package.json b/package.json index 3aed636..fc7ae7d 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ "setup:electron:win": "scripts\\setup-electron.bat", "check:ui": "chmod +x scripts/check-ui-architecture.sh && ./scripts/check-ui-architecture.sh", "check:ui:win": "scripts\\check-ui-architecture.bat", + "check:ports": "scripts\\diagnose-ports.bat", + "fix:ports": "powershell -ExecutionPolicy Bypass -File scripts/fix-windows-ports.ps1", "install:all": "node scripts/install-all.js", "install:ffmpeg": "node scripts/install-ffmpeg.js", "install:vite": "node scripts/install-vite-deps.js", diff --git a/scripts/check-ui-architecture.bat b/scripts/check-ui-architecture.bat index ffae12e..9930d95 100644 --- a/scripts/check-ui-architecture.bat +++ b/scripts/check-ui-architecture.bat @@ -22,8 +22,14 @@ if not exist "workstation\frontend\OEW-main\src\screens\workstation\components\M ) if not exist "workstation\frontend\OEW-main\src\contexts\WorkstationContext.tsx" ( - echo ⚠️ WARNING: WorkstationContext.tsx missing - set "components_missing=1" + if not exist "workstation\frontend\src\contexts\WorkstationContext.tsx" ( + echo ⚠️ WARNING: WorkstationContext.tsx missing from expected locations + set "components_missing=1" + ) else ( + echo ✅ WorkstationContext.tsx found in main frontend directory + ) +) else ( + echo ✅ WorkstationContext.tsx found in OEW-main ) if not exist "workstation\frontend\OEW-main\src\services" ( diff --git a/scripts/diagnose-ports.bat b/scripts/diagnose-ports.bat new file mode 100644 index 0000000..207378c --- /dev/null +++ b/scripts/diagnose-ports.bat @@ -0,0 +1,41 @@ +@echo off +echo 🔍 Windows Port Diagnostic Report +echo ================================ +echo. + +echo 📊 Current Port Usage: +echo ---------------------- +netstat -ano | findstr :3333 +if %errorlevel% neq 0 ( + echo ✅ Port 3333 is available +) else ( + echo ⚠️ Port 3333 is in use +) + +netstat -ano | findstr :8080 +if %errorlevel% neq 0 ( + echo ✅ Port 8080 is available +) else ( + echo ⚠️ Port 8080 is in use +) + +echo. +echo 🔒 Windows Firewall Status: +echo ---------------------------- +netsh advfirewall show allprofiles state + +echo. +echo 🚫 Reserved Port Ranges: +echo ------------------------ +echo The following ports are reserved by Windows: +netsh int ipv4 show excludedportrange protocol=tcp | findstr "51" + +echo. +echo 💡 Recommendations: +echo ------------------- +echo 1. Port 51204 (original error) is in reserved range 51214-51313 +echo 2. Use port 3333, 8080, or 9000 instead +echo 3. If still failing, run: .\scripts\fix-windows-ports.ps1 as Administrator +echo. + +pause From 271f59529dbefe9dc0f4d8c96621d471c532a52f Mon Sep 17 00:00:00 2001 From: Josh Head Date: Fri, 13 Jun 2025 17:57:27 -0700 Subject: [PATCH 05/12] Implement installation recursion prevention and simplify postinstall process --- docs/INSTALLATION_LOOP_FIX.md | 84 ++++++++++++++++++++++++++++++++++ package.json | 10 ++-- scripts/install-all-wrapper.js | 61 ++++++++++++++++++++++++ scripts/install-all.js | 44 +++++++++++++++--- scripts/postinstall.js | 66 ++++++++++++++++++++++++++ scripts/prevent-recursion.js | 44 ++++++++++++++++++ 6 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 docs/INSTALLATION_LOOP_FIX.md create mode 100644 scripts/install-all-wrapper.js create mode 100644 scripts/postinstall.js create mode 100644 scripts/prevent-recursion.js diff --git a/docs/INSTALLATION_LOOP_FIX.md b/docs/INSTALLATION_LOOP_FIX.md new file mode 100644 index 0000000..4ca7d2a --- /dev/null +++ b/docs/INSTALLATION_LOOP_FIX.md @@ -0,0 +1,84 @@ +# Resolving Package Installation Infinite Loop + +## The Problem + +The Orpheus Engine project previously had an issue with infinite loops during installation. This occurred due to a circular dependency in the npm scripts: + +1. When running `npm install`, the `postinstall` script would run automatically +2. The `postinstall` script would call `node scripts/install-all.js` +3. The `install-all.js` script would install dependencies in the root and subdirectories +4. When these package managers ran, they would trigger their own `postinstall` hooks +5. This created an infinite loop + +## The Solution + +We implemented a multi-layered approach to prevent infinite loops during installation: + +### 1. Recursion Detection + +Created a utility (`prevent-recursion.js`) that: +- Creates a lock file during installation to detect recursion +- Checks for the presence of the lock file before starting installation +- Prevents installation if a recent lock file exists (indicating recursion) +- Handles stale lock files (older than 5 minutes) + +### 2. Safer Postinstall Script + +Created a safer `postinstall.js` script that: +- Uses recursion detection to avoid infinite loops +- Only performs essential operations (fixing permissions) +- Doesn't run package installations recursively +- Always cleans up lock files when done + +### 3. Installation Wrapper + +Created an `install-all-wrapper.js` that: +- Wraps the original installation script with recursion protection +- Prevents running installations within installations +- Ensures lock files are properly cleaned up + +### 4. Environment Variables + +Modified `runCommand` in `install-all.js` to: +- Set an environment variable `SKIP_POSTINSTALL=1` when running child processes +- This signals to npm/yarn/pnpm to skip postinstall hooks in child processes + +## Usage + +The installation process now works as follows: + +1. **Initial Installation**: `npm install` + - This runs a minimal `postinstall` script that only fixes permissions + - No dependency installation is performed in subdirectories + +2. **Full Installation**: `npm run install:all` + - This runs the full installation process with recursion protection + - Installs dependencies in all directories + - Installs Python dependencies + +## Troubleshooting + +If you encounter installation issues: + +1. **Delete Lock File**: If you need to force an installation: + ```bash + rm -f .install_lock && npm run install:all + ``` + +2. **Manual Installation**: You can also install dependencies manually in each directory: + ```bash + npm install + cd workstation/frontend && npm install + cd ../backend && npm install + # etc. + ``` + +3. **Check Logs**: If installation fails, check for error messages indicating why. + +## Why This Approach Works + +This solution: +- Prevents infinite loops by detecting recursion +- Separates the minimal `postinstall` from the full installation process +- Uses environment variables to prevent nested postinstall hooks +- Provides proper cleanup to avoid lock files blocking future installations diff --git a/package.json b/package.json index fc7ae7d..c7aa91e 100644 --- a/package.json +++ b/package.json @@ -43,11 +43,11 @@ "check:ui:win": "scripts\\check-ui-architecture.bat", "check:ports": "scripts\\diagnose-ports.bat", "fix:ports": "powershell -ExecutionPolicy Bypass -File scripts/fix-windows-ports.ps1", - "install:all": "node scripts/install-all.js", - "install:ffmpeg": "node scripts/install-ffmpeg.js", - "install:vite": "node scripts/install-vite-deps.js", - "install:monitor": "node scripts/install-monitor-backend.js", - "postinstall": "node scripts/install-all.js", + "install:all": "node scripts/install-all-wrapper.js", + "install:ffmpeg": "node scripts/install-ffmpeg.js", + "install:vite": "node scripts/install-vite-deps.js", + "install:monitor": "node scripts/install-monitor-backend.js", + "postinstall": "node scripts/postinstall.js", "setup": "pnpm run fix:permissions && pnpm install && pnpm run install:all && pnpm run setup:electron", "demo": "node scripts/setup-demo.js", "demo:notebook": "cd demo && jupyter notebook OrpheusWebDemo.ipynb", diff --git a/scripts/install-all-wrapper.js b/scripts/install-all-wrapper.js new file mode 100644 index 0000000..eb65506 --- /dev/null +++ b/scripts/install-all-wrapper.js @@ -0,0 +1,61 @@ +// This is a wrapper script that calls the original install-all.js +// after performing recursion checks +import { fileURLToPath } from 'url'; +import path from 'path'; +import { checkForRecursion, removeLockFile } from './prevent-recursion.js'; + +// Convert __dirname equivalent for ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Colors for output +const colors = { + reset: "\x1b[0m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + cyan: "\x1b[36m", + bold: "\x1b[1m" +}; + +function log(message, color = colors.reset) { + console.log(`${color}${message}${colors.reset}`); +} + +async function safeInstallAll() { + // Check for recursion + if (checkForRecursion()) { + log('Skipping installation to prevent infinite loop', colors.yellow); + return false; + } + + try { + // Import the original install-all script + const { installAll } = await import('./install-all.js'); + + // Run the installation + return await installAll(); + } catch (error) { + log(`❌ Installation failed: ${error.message}`, colors.red); + return false; + } finally { + // Always clean up the lock file + removeLockFile(); + } +} + +// Run the safe installation +if (import.meta.url === `file://${process.argv[1]}`) { + safeInstallAll() + .then((success) => { + process.exit(success ? 0 : 1); + }) + .catch(err => { + log(`❌ Installation wrapper failed: ${err.message}`, colors.red); + removeLockFile(); + process.exit(1); + }); +} + +export { safeInstallAll }; diff --git a/scripts/install-all.js b/scripts/install-all.js index 6b24b24..da08127 100644 --- a/scripts/install-all.js +++ b/scripts/install-all.js @@ -58,7 +58,11 @@ function runCommand(command, cwd = process.cwd(), description = '') { execSync(command, { cwd, stdio: 'inherit', - env: { ...process.env } + env: { + ...process.env, + // Prevent npm postinstall recursion + SKIP_POSTINSTALL: '1' + } }); log(`✅ ${displayCmd} completed successfully`, colors.green); return true; @@ -183,13 +187,35 @@ function installPythonDependencies() { return success; } +// Detect if we're running in a recursive postinstall +function isRunningFromPostinstall() { + return process.env.npm_lifecycle_event === 'postinstall'; +} + +// Create a lock file to prevent recursive installations +function createLockFile() { + const lockFile = path.join(projectRoot, '.install_lock'); + fs.writeFileSync(lockFile, Date.now().toString()); + return lockFile; +} + +function removeLockFile(lockFile) { + if (fs.existsSync(lockFile)) { + fs.unlinkSync(lockFile); + } +} + // Main installation function async function installAll() { - logHeader('Orpheus Engine Installation'); + // Prevent recursive calls + const lockFile = createLockFile(); - // Step 1: Fix permissions - log('Step 1: Fixing permissions...', colors.cyan); - await fixPermissions(); + try { + logHeader('Orpheus Engine Installation'); + + // Step 1: Fix permissions + log('Step 1: Fixing permissions...', colors.cyan); + await fixPermissions(); // Step 2: Determine package manager let packageManager = 'npm'; @@ -242,7 +268,10 @@ async function installAll() { if (!workstationSuccess) log(' - Workstation dependencies failed', colors.red); if (!pythonSuccess) log(' - Python dependencies failed', colors.red); } - + + // Clean up lock file + removeLockFile(lockFile); + return allSuccess; } @@ -287,6 +316,9 @@ if (import.meta.url === `file://${process.argv[1]}`) { }) .catch(err => { log(`❌ Installation failed: ${err.message}`, colors.red); + // Make sure to clean up the lock file even on error + const lockFile = path.join(projectRoot, '.install_lock'); + removeLockFile(lockFile); process.exit(1); }); } diff --git a/scripts/postinstall.js b/scripts/postinstall.js new file mode 100644 index 0000000..5de808c --- /dev/null +++ b/scripts/postinstall.js @@ -0,0 +1,66 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { checkForRecursion, removeLockFile } from './prevent-recursion.js'; + +// Convert __dirname equivalent for ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const projectRoot = path.dirname(__dirname); + +// Colors for output +const colors = { + reset: "\x1b[0m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + cyan: "\x1b[36m", + bold: "\x1b[1m" +}; + +function log(message, color = colors.reset) { + console.log(`${color}${message}${colors.reset}`); +} + +// Main postinstall function - much simpler and safer +async function safePostinstall() { + // Check for recursion + if (checkForRecursion()) { + log('Skipping postinstall to prevent infinite loop', colors.yellow); + return; + } + + try { + log('Running safe postinstall operations...', colors.cyan); + + // Only fix permissions - other operations can be done manually + try { + log('Fixing script permissions...', colors.blue); + const { execSync } = await import('child_process'); + execSync('node scripts/fix-permissions.js', { + cwd: projectRoot, + stdio: 'inherit', + }); + } catch (error) { + log(`Warning: Could not fix permissions: ${error.message}`, colors.yellow); + } + + log('✅ Safe postinstall completed successfully', colors.green); + log('For full dependency installation, run: npm run install:all', colors.green); + } catch (error) { + log(`❌ Postinstall error: ${error.message}`, colors.red); + } finally { + // Always remove lock file to prevent blocking future runs + removeLockFile(); + } +} + +// Run the postinstall +if (import.meta.url === `file://${process.argv[1]}`) { + safePostinstall() + .catch(err => { + log(`❌ Postinstall failed: ${err.message}`, colors.red); + // Don't exit with error to allow npm to continue + }); +} diff --git a/scripts/prevent-recursion.js b/scripts/prevent-recursion.js new file mode 100644 index 0000000..d2f2d0f --- /dev/null +++ b/scripts/prevent-recursion.js @@ -0,0 +1,44 @@ +// Simple utility to prevent recursive installations +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +// Convert __dirname equivalent for ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const projectRoot = path.dirname(__dirname); + +// Lock file path +const LOCK_FILE = path.join(projectRoot, '.install_lock'); + +export function checkForRecursion() { + // If lock file exists, we're in a recursive call + if (fs.existsSync(LOCK_FILE)) { + const lockTime = parseInt(fs.readFileSync(LOCK_FILE, 'utf-8') || '0', 10); + const currentTime = Date.now(); + + // If lock is older than 5 minutes, assume it's stale + if (currentTime - lockTime < 5 * 60 * 1000) { + console.log('\x1b[33mRECURSION DETECTED: Avoiding infinite loop in dependency installation\x1b[0m'); + console.log('\x1b[33mIf this is unexpected, delete the .install_lock file and try again\x1b[0m'); + return true; + } else { + console.log('\x1b[33mStale lock file found, removing it\x1b[0m'); + fs.unlinkSync(LOCK_FILE); + } + } + + // Create lock file + fs.writeFileSync(LOCK_FILE, Date.now().toString()); + return false; +} + +export function removeLockFile() { + try { + if (fs.existsSync(LOCK_FILE)) { + fs.unlinkSync(LOCK_FILE); + } + } catch (err) { + console.log(`\x1b[33mWarning: Could not remove lock file: ${err.message}\x1b[0m`); + } +} From 85069ab736ea80cf3daf6e4e12a8522556ccc5a4 Mon Sep 17 00:00:00 2001 From: Josh Head Date: Fri, 13 Jun 2025 18:31:08 -0700 Subject: [PATCH 06/12] Manually fix submodule workstation/frontend/OEW-main --- workstation/frontend/OEW-main | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workstation/frontend/OEW-main b/workstation/frontend/OEW-main index 142efd8..a362198 160000 --- a/workstation/frontend/OEW-main +++ b/workstation/frontend/OEW-main @@ -1 +1 @@ -Subproject commit 142efd835fd2fb10f8b700f63ed12d6077801ca9 +Subproject commit a362198c7dfad14d10d6e8722a02f5d58e203b0e From e306952eb368bb307295926c24b97c44b3e83c7f Mon Sep 17 00:00:00 2001 From: Josh Head Date: Fri, 13 Jun 2025 19:52:11 -0700 Subject: [PATCH 07/12] update --- docs/VITEST_UI_FIX.md | 30 + docs/VITEST_UPDATE.md | 37 + package.json | 4 +- scripts/run-vitest-ui-windows.ps1 | 34 +- vitest.config.ts | 3 +- workstation/frontend/.eslintrc.json | 35 - workstation/frontend/.gitmodules | 4 + workstation/frontend/ELECTRON_STATUS.md | 111 --- .../frontend/EXPORT_INTEGRATION_README.md | 106 --- workstation/frontend/README.md | 42 - workstation/frontend/index.html | 12 - workstation/frontend/main.js | 168 ---- workstation/frontend/package.json | 65 -- workstation/frontend/preload.js | 31 - workstation/frontend/public/favicon.ico | 1 - workstation/frontend/public/index.html | 11 - workstation/frontend/public/startup.html | 403 --------- .../frontend/scripts/audio_analysis_bridge.py | 0 workstation/frontend/src/App.css | 46 - workstation/frontend/src/App.tsx | 204 ----- .../frontend/src/apollo/ApolloWrapper.tsx | 21 - workstation/frontend/src/apollo/client.ts | 19 - .../src/assets/icons/icon-128x128.png | Bin 2899 -> 0 bytes .../frontend/src/assets/icons/icon-16x16.png | Bin 535 -> 0 bytes .../frontend/src/assets/icons/icon-24x24.png | Bin 840 -> 0 bytes .../src/assets/icons/icon-256x256.png | Bin 6034 -> 0 bytes .../frontend/src/assets/icons/icon-32x32.png | Bin 885 -> 0 bytes .../frontend/src/assets/icons/icon-48x48.png | Bin 1333 -> 0 bytes .../src/assets/icons/icon-512x512.png | Bin 8067 -> 0 bytes .../frontend/src/assets/icons/icon-64x64.png | Bin 1448 -> 0 bytes .../frontend/src/assets/icons/icon.icns | Bin 7426 -> 0 bytes .../frontend/src/assets/icons/icon.ico | Bin 6034 -> 0 bytes .../frontend/src/assets/icons/icon.png | Bin 7426 -> 0 bytes .../src/components/AudioClipComponent.tsx | 536 ----------- .../components/AudioInputDeviceSelector.tsx | 538 ----------- .../components/AudioLibrary/AudioLibrary.tsx | 71 -- .../src/components/AudioLibrary/index.ts | 1 - .../frontend/src/components/AudioSearch.tsx | 42 - .../src/components/DynamicComponent.tsx | 28 - .../src/components/JudgeEvaluationPanel.css | 0 .../src/components/JudgeEvaluationPanel.tsx | 0 .../frontend/src/components/Metronome.tsx | 194 ---- .../frontend/src/components/PaneResize.tsx | 197 ---- .../src/components/PluginManagerDialog.tsx | 660 -------------- .../src/components/PluginMarketplace.tsx | 24 - .../frontend/src/components/Preferences.tsx | 128 --- .../src/components/TransportControls.tsx | 249 ----- .../frontend/src/components/Waveform.tsx | 84 -- .../frontend/src/components/Workstation.tsx | 46 - .../frontend/src/components/ZoomControls.tsx | 196 ---- .../__tests__/JudgeEvaluationPanel.test.tsx | 0 .../src/components/daw/AudioAnalyzer.tsx | 144 --- .../src/components/daw/AudioExport.test.tsx | 158 ---- .../src/components/daw/AudioRecorder.test.tsx | 56 -- .../daw/AudioRecorderComponent.test.tsx | 77 -- .../components/daw/AudioRecorderComponent.tsx | 287 ------ .../components/daw/ClipManipulation.test.tsx | 103 --- .../src/components/daw/EffectsRack.tsx | 40 - .../frontend/src/components/daw/Mixer.tsx | 68 -- .../src/components/daw/MixerControls.tsx | 33 - .../daw/MultiSourceRecorder.test.tsx | 77 -- .../daw/MultiSourceRecorderComponent.test.tsx | 176 ---- .../daw/MultiSourceRecorderComponent.tsx | 265 ------ .../frontend/src/components/daw/PianoRoll.tsx | 119 --- .../daw/ProjectFileOperations.test.tsx | 163 ---- .../frontend/src/components/daw/Timeline.tsx | 198 ---- .../frontend/src/components/daw/Track.tsx | 39 - .../src/components/daw/TrackControls.tsx | 87 -- .../frontend/src/components/daw/Transport.tsx | 58 -- .../frontend/src/components/daw/Waveform.tsx | 0 .../__tests__/AudioRecorderComponent.test.tsx | 90 -- .../src/components/editor/AudioClipEditor.tsx | 102 --- workstation/frontend/src/components/index.ts | 7 - .../src/components/mixer/MixerPanel.tsx | 40 - .../src/components/placeholders/Editor.tsx | 21 - .../src/components/placeholders/Header.tsx | 18 - .../src/components/placeholders/Mixer.tsx | 31 - .../components/settings/SettingsManager.tsx | 260 ------ .../src/components/workstation/Lane.tsx | 3 - .../components/workstation/SidePanel.test.tsx | 3 - .../src/components/workstation/SidePanel.tsx | 3 - .../src/contexts/ClipboardContext.tsx | 58 -- .../frontend/src/contexts/DAWContext.tsx | 260 ------ .../frontend/src/contexts/MixerContext.tsx | 142 --- .../src/contexts/PreferencesContext.tsx | 88 -- .../src/contexts/WorkstationContext.tsx | 58 -- .../src/contexts/WorkstationProvider.tsx | 125 --- .../frontend/src/electron/windowManager.ts | 29 - .../frontend/src/hooks/useAudioLibrary.ts | 185 ---- .../frontend/src/hooks/useMCPAnalysis.ts | 83 -- workstation/frontend/src/index.css | 36 - workstation/frontend/src/index.tsx | 13 - workstation/frontend/src/main.tsx | 40 - .../frontend/src/plugins/Web3StoragePlugin.js | 169 ---- .../frontend/src/plugins/Web3StoragePlugin.ts | 211 ----- workstation/frontend/src/react-app-env.d.ts | 1 - .../src/screens/workstation/Editor.tsx | 847 ------------------ .../components/TimelineRulerGrid.tsx | 359 -------- .../workstation/components/Waveform.tsx | 40 - .../frontend/src/services/AudioService.ts | 275 ------ .../__tests__/judgeEvaluationService.test.ts | 0 .../src/services/ai/AIWorkspaceManager.ts | 52 -- .../frontend/src/services/ai/RagService.ts | 91 -- .../audio/__tests__/audioExporter.test.ts | 291 ------ .../audio/__tests__/audioRecorder.test.ts | 123 --- .../src/services/audio/audioExporter.test.ts | 363 -------- .../src/services/audio/audioExporter.ts | 492 ---------- .../src/services/audio/audioRecorder.ts | 116 --- .../src/services/audio/audioService.ts | 86 -- .../src/services/audio/multiSourceRecorder.ts | 156 ---- .../daw/__tests__/clipService.test.ts | 186 ---- .../daw/__tests__/projectFileService.test.ts | 162 ---- .../frontend/src/services/daw/clipService.ts | 350 -------- .../src/services/daw/projectFileService.ts | 110 --- .../src/services/electron/channels.ts | 13 - .../frontend/src/services/electron/utils.ts | 69 -- .../src/services/hooks/useClickAway.ts | 28 - .../src/services/mcp/MCPServerService.ts | 19 - .../src/services/mcp/webhookService.ts | 29 - .../plugins/AIEnhancedPluginManager.ts | 374 -------- .../src/services/plugins/PluginManager.ts | 365 -------- .../audio-input/AudioInputPluginManager.ts | 428 --------- .../services/plugins/audio-input/README.md | 287 ------ .../built-in/MADIAudioInputPlugin.ts | 424 --------- .../built-in/NRFAudioInputPlugin.ts | 549 ------------ .../built-in/USBAudioInputPlugin.ts | 303 ------- .../src/services/plugins/audio-input/index.ts | 25 - .../src/services/plugins/audio-input/types.ts | 260 ------ .../plugins/built-in/CloudStoragePlugin.ts | 675 -------------- .../services/plugins/built-in/IPFSPlugin.ts | 116 --- .../plugins/built-in/LocalFilePlugin.ts | 102 --- .../plugins/built-in/NodeJSExportPlugin.ts | 192 ---- .../plugins/built-in/ReactExportPlugin.ts | 57 -- .../plugins/built-in/StoryProtocolPlugin.ts | 110 --- .../src/services/plugins/graphql/client.ts | 0 .../src/services/plugins/graphql/hooks.ts | 0 .../src/services/plugins/graphql/types.ts | 120 --- .../frontend/src/services/plugins/types.ts | 226 ----- .../frontend/src/services/pythonBridge.ts | 52 -- .../services/settings/SettingsProvider.tsx | 69 -- .../src/services/settings/categories/audio.ts | 77 -- .../src/services/settings/index.fixed.ts | 138 --- .../frontend/src/services/settings/index.ts | 100 --- .../services/storage/cloudStorageClient.ts | 55 -- .../src/services/storage/ipfsClient.ts | 49 - .../frontend/src/services/types/types.ts | 593 ------------ .../src/services/utils/TimelinePosition.ts | 65 -- .../frontend/src/services/utils/audio.ts | 114 --- .../frontend/src/services/utils/general.ts | 195 ---- .../frontend/src/services/utils/utils.test.ts | 100 --- .../frontend/src/services/utils/utils.ts | 497 ---------- workstation/frontend/src/styles/App.css | 247 ----- .../frontend/src/styles/AudioLibrary.css | 19 - workstation/frontend/src/styles/editor.css | 44 - workstation/frontend/src/styles/themes.css | 27 - .../frontend/src/styles/workstation.css | 50 -- workstation/frontend/src/test-setup.ts | 9 - workstation/frontend/src/test/setup.ts | 104 --- workstation/frontend/src/types/electron.d.ts | 28 - workstation/frontend/src/types/index.ts | 12 - workstation/frontend/src/types/plugin.ts | 11 - workstation/frontend/src/types/types.ts | 18 - .../frontend/src/types/workstation/plugin.ts | 12 - workstation/frontend/src/utils/electron.ts | 21 - workstation/frontend/tsconfig.json | 40 - workstation/frontend/vite.config.js | 15 - workstation/frontend/vite.config.ts | 25 - workstation/frontend/vitest.config.ts | 26 - 168 files changed, 77 insertions(+), 20210 deletions(-) create mode 100644 docs/VITEST_UI_FIX.md create mode 100644 docs/VITEST_UPDATE.md delete mode 100644 workstation/frontend/.eslintrc.json create mode 100644 workstation/frontend/.gitmodules delete mode 100644 workstation/frontend/ELECTRON_STATUS.md delete mode 100644 workstation/frontend/EXPORT_INTEGRATION_README.md delete mode 100644 workstation/frontend/README.md delete mode 100644 workstation/frontend/index.html delete mode 100644 workstation/frontend/main.js delete mode 100644 workstation/frontend/package.json delete mode 100644 workstation/frontend/preload.js delete mode 100644 workstation/frontend/public/favicon.ico delete mode 100644 workstation/frontend/public/index.html delete mode 100644 workstation/frontend/public/startup.html delete mode 100644 workstation/frontend/scripts/audio_analysis_bridge.py delete mode 100644 workstation/frontend/src/App.css delete mode 100644 workstation/frontend/src/App.tsx delete mode 100644 workstation/frontend/src/apollo/ApolloWrapper.tsx delete mode 100644 workstation/frontend/src/apollo/client.ts delete mode 100644 workstation/frontend/src/assets/icons/icon-128x128.png delete mode 100644 workstation/frontend/src/assets/icons/icon-16x16.png delete mode 100644 workstation/frontend/src/assets/icons/icon-24x24.png delete mode 100644 workstation/frontend/src/assets/icons/icon-256x256.png delete mode 100644 workstation/frontend/src/assets/icons/icon-32x32.png delete mode 100644 workstation/frontend/src/assets/icons/icon-48x48.png delete mode 100644 workstation/frontend/src/assets/icons/icon-512x512.png delete mode 100644 workstation/frontend/src/assets/icons/icon-64x64.png delete mode 100644 workstation/frontend/src/assets/icons/icon.icns delete mode 100644 workstation/frontend/src/assets/icons/icon.ico delete mode 100644 workstation/frontend/src/assets/icons/icon.png delete mode 100644 workstation/frontend/src/components/AudioClipComponent.tsx delete mode 100644 workstation/frontend/src/components/AudioInputDeviceSelector.tsx delete mode 100644 workstation/frontend/src/components/AudioLibrary/AudioLibrary.tsx delete mode 100644 workstation/frontend/src/components/AudioLibrary/index.ts delete mode 100644 workstation/frontend/src/components/AudioSearch.tsx delete mode 100644 workstation/frontend/src/components/DynamicComponent.tsx delete mode 100644 workstation/frontend/src/components/JudgeEvaluationPanel.css delete mode 100644 workstation/frontend/src/components/JudgeEvaluationPanel.tsx delete mode 100644 workstation/frontend/src/components/Metronome.tsx delete mode 100644 workstation/frontend/src/components/PaneResize.tsx delete mode 100644 workstation/frontend/src/components/PluginManagerDialog.tsx delete mode 100644 workstation/frontend/src/components/PluginMarketplace.tsx delete mode 100644 workstation/frontend/src/components/Preferences.tsx delete mode 100644 workstation/frontend/src/components/TransportControls.tsx delete mode 100644 workstation/frontend/src/components/Waveform.tsx delete mode 100644 workstation/frontend/src/components/Workstation.tsx delete mode 100644 workstation/frontend/src/components/ZoomControls.tsx delete mode 100644 workstation/frontend/src/components/__tests__/JudgeEvaluationPanel.test.tsx delete mode 100644 workstation/frontend/src/components/daw/AudioAnalyzer.tsx delete mode 100644 workstation/frontend/src/components/daw/AudioExport.test.tsx delete mode 100644 workstation/frontend/src/components/daw/AudioRecorder.test.tsx delete mode 100644 workstation/frontend/src/components/daw/AudioRecorderComponent.test.tsx delete mode 100644 workstation/frontend/src/components/daw/AudioRecorderComponent.tsx delete mode 100644 workstation/frontend/src/components/daw/ClipManipulation.test.tsx delete mode 100644 workstation/frontend/src/components/daw/EffectsRack.tsx delete mode 100644 workstation/frontend/src/components/daw/Mixer.tsx delete mode 100644 workstation/frontend/src/components/daw/MixerControls.tsx delete mode 100644 workstation/frontend/src/components/daw/MultiSourceRecorder.test.tsx delete mode 100644 workstation/frontend/src/components/daw/MultiSourceRecorderComponent.test.tsx delete mode 100644 workstation/frontend/src/components/daw/MultiSourceRecorderComponent.tsx delete mode 100644 workstation/frontend/src/components/daw/PianoRoll.tsx delete mode 100644 workstation/frontend/src/components/daw/ProjectFileOperations.test.tsx delete mode 100644 workstation/frontend/src/components/daw/Timeline.tsx delete mode 100644 workstation/frontend/src/components/daw/Track.tsx delete mode 100644 workstation/frontend/src/components/daw/TrackControls.tsx delete mode 100644 workstation/frontend/src/components/daw/Transport.tsx delete mode 100644 workstation/frontend/src/components/daw/Waveform.tsx delete mode 100644 workstation/frontend/src/components/daw/__tests__/AudioRecorderComponent.test.tsx delete mode 100644 workstation/frontend/src/components/editor/AudioClipEditor.tsx delete mode 100644 workstation/frontend/src/components/index.ts delete mode 100644 workstation/frontend/src/components/mixer/MixerPanel.tsx delete mode 100644 workstation/frontend/src/components/placeholders/Editor.tsx delete mode 100644 workstation/frontend/src/components/placeholders/Header.tsx delete mode 100644 workstation/frontend/src/components/placeholders/Mixer.tsx delete mode 100644 workstation/frontend/src/components/settings/SettingsManager.tsx delete mode 100644 workstation/frontend/src/components/workstation/Lane.tsx delete mode 100644 workstation/frontend/src/components/workstation/SidePanel.test.tsx delete mode 100644 workstation/frontend/src/components/workstation/SidePanel.tsx delete mode 100644 workstation/frontend/src/contexts/ClipboardContext.tsx delete mode 100644 workstation/frontend/src/contexts/DAWContext.tsx delete mode 100644 workstation/frontend/src/contexts/MixerContext.tsx delete mode 100644 workstation/frontend/src/contexts/PreferencesContext.tsx delete mode 100644 workstation/frontend/src/contexts/WorkstationContext.tsx delete mode 100644 workstation/frontend/src/contexts/WorkstationProvider.tsx delete mode 100644 workstation/frontend/src/electron/windowManager.ts delete mode 100644 workstation/frontend/src/hooks/useAudioLibrary.ts delete mode 100644 workstation/frontend/src/hooks/useMCPAnalysis.ts delete mode 100644 workstation/frontend/src/index.css delete mode 100644 workstation/frontend/src/index.tsx delete mode 100644 workstation/frontend/src/main.tsx delete mode 100644 workstation/frontend/src/plugins/Web3StoragePlugin.js delete mode 100644 workstation/frontend/src/plugins/Web3StoragePlugin.ts delete mode 100644 workstation/frontend/src/react-app-env.d.ts delete mode 100644 workstation/frontend/src/screens/workstation/Editor.tsx delete mode 100644 workstation/frontend/src/screens/workstation/components/TimelineRulerGrid.tsx delete mode 100644 workstation/frontend/src/screens/workstation/components/Waveform.tsx delete mode 100644 workstation/frontend/src/services/AudioService.ts delete mode 100644 workstation/frontend/src/services/__tests__/judgeEvaluationService.test.ts delete mode 100644 workstation/frontend/src/services/ai/AIWorkspaceManager.ts delete mode 100644 workstation/frontend/src/services/ai/RagService.ts delete mode 100644 workstation/frontend/src/services/audio/__tests__/audioExporter.test.ts delete mode 100644 workstation/frontend/src/services/audio/__tests__/audioRecorder.test.ts delete mode 100644 workstation/frontend/src/services/audio/audioExporter.test.ts delete mode 100644 workstation/frontend/src/services/audio/audioExporter.ts delete mode 100644 workstation/frontend/src/services/audio/audioRecorder.ts delete mode 100644 workstation/frontend/src/services/audio/audioService.ts delete mode 100644 workstation/frontend/src/services/audio/multiSourceRecorder.ts delete mode 100644 workstation/frontend/src/services/daw/__tests__/clipService.test.ts delete mode 100644 workstation/frontend/src/services/daw/__tests__/projectFileService.test.ts delete mode 100644 workstation/frontend/src/services/daw/clipService.ts delete mode 100644 workstation/frontend/src/services/daw/projectFileService.ts delete mode 100644 workstation/frontend/src/services/electron/channels.ts delete mode 100644 workstation/frontend/src/services/electron/utils.ts delete mode 100644 workstation/frontend/src/services/hooks/useClickAway.ts delete mode 100644 workstation/frontend/src/services/mcp/MCPServerService.ts delete mode 100644 workstation/frontend/src/services/mcp/webhookService.ts delete mode 100644 workstation/frontend/src/services/plugins/AIEnhancedPluginManager.ts delete mode 100644 workstation/frontend/src/services/plugins/PluginManager.ts delete mode 100644 workstation/frontend/src/services/plugins/audio-input/AudioInputPluginManager.ts delete mode 100644 workstation/frontend/src/services/plugins/audio-input/README.md delete mode 100644 workstation/frontend/src/services/plugins/audio-input/built-in/MADIAudioInputPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/audio-input/built-in/NRFAudioInputPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/audio-input/built-in/USBAudioInputPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/audio-input/index.ts delete mode 100644 workstation/frontend/src/services/plugins/audio-input/types.ts delete mode 100644 workstation/frontend/src/services/plugins/built-in/CloudStoragePlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/built-in/IPFSPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/built-in/LocalFilePlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/built-in/NodeJSExportPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/built-in/ReactExportPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/built-in/StoryProtocolPlugin.ts delete mode 100644 workstation/frontend/src/services/plugins/graphql/client.ts delete mode 100644 workstation/frontend/src/services/plugins/graphql/hooks.ts delete mode 100644 workstation/frontend/src/services/plugins/graphql/types.ts delete mode 100644 workstation/frontend/src/services/plugins/types.ts delete mode 100644 workstation/frontend/src/services/pythonBridge.ts delete mode 100644 workstation/frontend/src/services/settings/SettingsProvider.tsx delete mode 100644 workstation/frontend/src/services/settings/categories/audio.ts delete mode 100644 workstation/frontend/src/services/settings/index.fixed.ts delete mode 100644 workstation/frontend/src/services/settings/index.ts delete mode 100644 workstation/frontend/src/services/storage/cloudStorageClient.ts delete mode 100644 workstation/frontend/src/services/storage/ipfsClient.ts delete mode 100644 workstation/frontend/src/services/types/types.ts delete mode 100644 workstation/frontend/src/services/utils/TimelinePosition.ts delete mode 100644 workstation/frontend/src/services/utils/audio.ts delete mode 100644 workstation/frontend/src/services/utils/general.ts delete mode 100644 workstation/frontend/src/services/utils/utils.test.ts delete mode 100644 workstation/frontend/src/services/utils/utils.ts delete mode 100644 workstation/frontend/src/styles/App.css delete mode 100644 workstation/frontend/src/styles/AudioLibrary.css delete mode 100644 workstation/frontend/src/styles/editor.css delete mode 100644 workstation/frontend/src/styles/themes.css delete mode 100644 workstation/frontend/src/styles/workstation.css delete mode 100644 workstation/frontend/src/test-setup.ts delete mode 100644 workstation/frontend/src/test/setup.ts delete mode 100644 workstation/frontend/src/types/electron.d.ts delete mode 100644 workstation/frontend/src/types/index.ts delete mode 100644 workstation/frontend/src/types/plugin.ts delete mode 100644 workstation/frontend/src/types/types.ts delete mode 100644 workstation/frontend/src/types/workstation/plugin.ts delete mode 100644 workstation/frontend/src/utils/electron.ts delete mode 100644 workstation/frontend/tsconfig.json delete mode 100644 workstation/frontend/vite.config.js delete mode 100644 workstation/frontend/vite.config.ts delete mode 100644 workstation/frontend/vitest.config.ts diff --git a/docs/VITEST_UI_FIX.md b/docs/VITEST_UI_FIX.md new file mode 100644 index 0000000..095be66 --- /dev/null +++ b/docs/VITEST_UI_FIX.md @@ -0,0 +1,30 @@ +# Vitest UI Configuration Fix + +## Issue + +When running `pnpm run test:ui`, an error was encountered: + +``` +CACError: Unknown option `--port` +``` + +This occurred because recent versions of Vitest UI no longer support the `--port` option in the command line. + +## Changes Made + +1. Removed the `port` configuration from the `ui` section in `vitest.config.ts`: + ```typescript + ui: { + // port: 3333, <-- Removed this line + host: '127.0.0.1', + open: false, // Let's not auto-open to avoid permission issues + }, + ``` + +2. Updated the `scripts/run-vitest-ui-windows.ps1` script to remove port detection and configuration. + +## Notes + +- Vitest UI now automatically selects an available port +- The host and open settings were kept as they are still supported +- The TypeScript errors in vitest.config.ts are related to dependency version conflicts and not directly related to the port issue diff --git a/docs/VITEST_UPDATE.md b/docs/VITEST_UPDATE.md new file mode 100644 index 0000000..40f51be --- /dev/null +++ b/docs/VITEST_UPDATE.md @@ -0,0 +1,37 @@ +# Vitest UI Configuration Update + +## Issue Resolution: Removed `--port` option + +The `--port` option was causing errors with the current version of Vitest UI (v1.6.1). This option is no longer supported in recent versions of Vitest UI. + +## Changes Made + +1. Removed the `--port 3333` option from the following scripts in `package.json`: + - `test:ui` + - `test:ui:safe` + +2. Updated the `scripts/run-vitest-ui-windows.ps1` PowerShell script: + - Removed port detection and configuration + - Removed the `--port` option from the vitest command + - Simplified the script to focus on running Vitest UI without port customization + +## How to Run Tests UI + +To run the Vitest UI, use one of these commands: + +```bash +# Basic UI +pnpm run test:ui + +# UI with safe defaults (no auto-opening browser) +pnpm run test:ui:safe + +# For Windows users with proper PowerShell handling +pnpm run test:ui:windows +``` + +Vitest UI will automatically select an available port and display the URL in the terminal output. + +## Note + +If you need to customize the port in the future, you might need to configure it through Vitest's configuration files rather than command line arguments, depending on what the current version supports. diff --git a/package.json b/package.json index c7aa91e..e6e4429 100644 --- a/package.json +++ b/package.json @@ -64,9 +64,9 @@ "start": "node electron-launch.js", "test": "vitest run", "test:watch": "vitest", - "test:ui": "vitest --ui --port 3333", + "test:ui": "vitest --ui", "test:ui:windows": "powershell -ExecutionPolicy Bypass -File scripts/run-vitest-ui-windows.ps1", - "test:ui:safe": "vitest --ui --port 3333 --host 127.0.0.1 --open false", + "test:ui:safe": "vitest --ui --host 127.0.0.1 --open false", "test:ui:full": "concurrently \"pnpm run test:ui\" \"pnpm run test:visual:ui\" \"pnpm run test:python:ui\"", "test:integration": "vitest run --config vitest.integration.config.ts", "test:integration:ui": "vitest --ui --config vitest.integration.config.ts --open", diff --git a/scripts/run-vitest-ui-windows.ps1 b/scripts/run-vitest-ui-windows.ps1 index 50765df..a0931c7 100644 --- a/scripts/run-vitest-ui-windows.ps1 +++ b/scripts/run-vitest-ui-windows.ps1 @@ -3,43 +3,15 @@ Write-Host "Starting Vitest UI on Windows..." -ForegroundColor Green Write-Host "" # Function to check if port is available -function Test-Port { - param([int]$Port) - try { - $listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Any, $Port) - $listener.Start() - $listener.Stop() - return $true - } - catch { - return $false - } -} - -# Find available port -$testPort = 3333 -if (-not (Test-Port $testPort)) { - Write-Host "Port $testPort is in use, trying port 3334..." -ForegroundColor Yellow - $testPort = 3334 - if (-not (Test-Port $testPort)) { - Write-Host "Port $testPort is also in use, trying port 3335..." -ForegroundColor Yellow - $testPort = 3335 - } -} - -Write-Host "Using port: $testPort" -ForegroundColor Green Write-Host "" -# Set environment variables for the process -$env:VITEST_UI_PORT = $testPort - try { - Write-Host "Starting Vitest UI on port $testPort..." -ForegroundColor Cyan - Write-Host "UI will be available at: http://127.0.0.1:$testPort" -ForegroundColor Yellow + Write-Host "Starting Vitest UI..." -ForegroundColor Cyan + Write-Host "UI will be available at the address displayed in the terminal output" -ForegroundColor Yellow Write-Host "" # Run vitest with explicit configuration - & npx vitest --ui --port $testPort --host "127.0.0.1" --open false + & npx vitest --ui --host "127.0.0.1" --open false } catch { Write-Host "Error starting Vitest UI: $($_.Exception.Message)" -ForegroundColor Red diff --git a/vitest.config.ts b/vitest.config.ts index 24dc272..bb80296 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -45,7 +45,7 @@ export default defineConfig({ "workstation/frontend/OEW-main/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}" ], globals: true, - setupFiles: ["workstation/frontend/OEW-main/src/setupTests.ts"], + setupFiles: ["workstation/frontend/src/test/setup.ts"], coverage: { reporter: ["text", "json", "html"], reportsDirectory: "./test-results/coverage", @@ -64,7 +64,6 @@ export default defineConfig({ }, }, ui: { - port: 3333, host: '127.0.0.1', open: false, // Let's not auto-open to avoid permission issues }, diff --git a/workstation/frontend/.eslintrc.json b/workstation/frontend/.eslintrc.json deleted file mode 100644 index a249c49..0000000 --- a/workstation/frontend/.eslintrc.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true, - "jest": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "react", - "@typescript-eslint", - "react-hooks" - ], - "settings": { - "react": { - "version": "detect" - } - }, - "rules": { - "react/react-in-jsx-scope": "off" - } -} diff --git a/workstation/frontend/.gitmodules b/workstation/frontend/.gitmodules new file mode 100644 index 0000000..e97dfc1 --- /dev/null +++ b/workstation/frontend/.gitmodules @@ -0,0 +1,4 @@ +[submodule "workstation/frontend/OEW-main"] + path = workstation/frontend/OEW-main + url = https://github.com/jhead12/orpheus-engine.git + branch = feature/server-agnostic-config-clean diff --git a/workstation/frontend/ELECTRON_STATUS.md b/workstation/frontend/ELECTRON_STATUS.md deleted file mode 100644 index 8bad78d..0000000 --- a/workstation/frontend/ELECTRON_STATUS.md +++ /dev/null @@ -1,111 +0,0 @@ -# Electron Setup for Orpheus Engine Workstation - -## Status: ✅ WORKING - -Electron has been successfully configured for the Orpheus Engine Workstation frontend. - -## What's Configured - -### 📦 Dependencies Installed -- `electron` v36.3.1 - Main Electron framework -- `electron-builder` v26.0.12 - For building distributable packages -- `electron-is-dev` v3.0.1 - Development environment detection -- `concurrently` v9.1.2 - Run multiple commands simultaneously -- `wait-on` v8.0.3 - Wait for services to be ready - -### 🔧 Files Created -- `main.js` - Main Electron process with window management, menus, and IPC -- `preload.js` - Secure bridge between main and renderer processes -- `src/types/electron.d.ts` - TypeScript definitions for Electron APIs - -### 📝 Scripts Available -```bash -# Development - runs React dev server + Electron together -npm run electron-dev - -# Production - build React app and run in Electron -npm run electron-build - -# Just Electron (requires built React app) -npm run electron - -# Build distributable packages -npm run dist -``` - -### 🎯 Features Implemented -- **Menu System**: File, Edit, View, and Audio menus with keyboard shortcuts -- **IPC Communication**: Secure communication between main and renderer processes -- **Window Management**: Proper window creation, sizing, and state management -- **Cross-platform**: Configured for Windows, macOS, and Linux builds -- **TypeScript Support**: Full type safety for Electron APIs - -## How to Use - -### Development Mode -```bash -# Terminal 1: Start React development server -npm run dev - -# Terminal 2: Start Electron (after React server is running) -npx electron . - -# OR use the combined command: -npm run electron-dev -``` - -### Production Build -```bash -# Build React app and run in Electron -npm run electron-build - -# Create distributable packages -npm run dist -``` - -## Environment Limitations - -⚠️ **Codespaces/Headless Environments**: -- Electron GUI cannot display in headless environments like GitHub Codespaces -- Electron functionality is fully working but requires a display to show the window -- All code, scripts, and build processes work correctly - -## Integration with React - -The React app has been updated to: -- Detect when running in Electron vs browser -- Display environment information (Electron version, platform) -- Listen for menu events from Electron -- Use Electron APIs securely through the preload script - -## File Structure - -``` -frontend/ -├── main.js # Electron main process -├── preload.js # Secure IPC bridge -├── src/ -│ ├── App.tsx # Updated with Electron integration -│ └── types/ -│ └── electron.d.ts # TypeScript definitions -└── package.json # Updated with Electron scripts and config -``` - -## Next Steps - -1. **Local Development**: Clone this repository to a local machine with a display to see the Electron GUI -2. **Icon Assets**: Add application icons to the `build/` directory -3. **Auto-updater**: Implement automatic updates for production releases -4. **Native Integrations**: Add OS-specific features like system tray, notifications - -## Verification - -✅ Electron v36.3.1 installed and functional -✅ Main process created with proper window management -✅ Preload script for secure IPC -✅ React app builds successfully -✅ TypeScript integration working -✅ Build scripts configured -✅ Cross-platform build configuration ready - -The Electron integration is **complete and ready for use**! diff --git a/workstation/frontend/EXPORT_INTEGRATION_README.md b/workstation/frontend/EXPORT_INTEGRATION_README.md deleted file mode 100644 index 6f1fcf6..0000000 --- a/workstation/frontend/EXPORT_INTEGRATION_README.md +++ /dev/null @@ -1,106 +0,0 @@ -# Audio Clip Export Integration - -## ✅ Successfully Integrated Export Functionality - -The `AudioClipComponent.tsx` has been successfully updated to include comprehensive export functionality using the existing plugin system. - -### What Was Added - -1. **Plugin Manager Integration** - - Imported `AudioExportPluginManager` from the plugin system - - Added state management for export operations - - Plugin manager is initialized on component mount - -2. **Export Methods** - - `handleExportToLocal()` - Exports audio clips to local filesystem - - `handleExportToCloud()` - Exports to AWS S3 cloud storage - - `handleExportToIPFS()` - Exports to IPFS decentralized storage - - `handleExportWithStoryProtocol()` - Registers intellectual property on blockchain - -3. **User Interface** - - **Double-click** any audio clip to open the export menu - - Clean export menu with 4 export options - - Loading states and error handling - - Disabled states when no audio buffer is available - -### Available Export Plugins Used - -✅ **Local File Plugin** - Save audio files locally -✅ **Cloud Storage Plugin** - Upload to AWS S3 -✅ **IPFS Plugin** - Decentralized storage -✅ **Story Protocol Plugin** - Blockchain IP registration - -### How to Use - -1. **Open the DAW** and load audio clips -2. **Double-click** any audio clip waveform -3. **Choose export option** from the popup menu: - - 📁 Export Locally - - ☁️ Export to Cloud - - 🌐 Export to IPFS - - 🔗 Register IP (Story Protocol) -4. **Wait for completion** - success/error alerts will show - -### Export Configuration - -Each export method uses optimized settings: - -```typescript -// Local Export -{ - storage: { provider: 'local' }, - audioFormat: 'wav', - quality: 'high' -} - -// Cloud Export -{ - storage: { - provider: 'aws-s3', - bucket: 'orpheus-audio-exports', - path: 'clips' - }, - audioFormat: 'wav', - quality: 'high' -} - -// IPFS Export -{ - storage: { provider: 'ipfs' }, - audioFormat: 'wav', - quality: 'high' -} - -// Story Protocol Export -{ - storage: { provider: 'ipfs' }, - blockchain: { - storyProtocol: { - enabled: true, - registerIP: true, - licenseTerms: 'CC BY-SA 4.0' - } - }, - audioFormat: 'wav', - quality: 'lossless' -} -``` - -### Technical Implementation - -- **Type Safety**: All exports properly typed with `ExportPluginOptions` -- **Error Handling**: Try-catch blocks with user-friendly error messages -- **Loading States**: UI feedback during export operations -- **Plugin System**: Uses the existing comprehensive plugin architecture -- **Metadata**: Includes track name, clip ID, and artist information - -### Next Steps - -The audio clip export functionality is now fully integrated and ready to use. Users can: - -1. Export individual audio clips in multiple formats -2. Store clips locally or in cloud/decentralized storage -3. Register audio clips as intellectual property on blockchain -4. Maintain full metadata and quality control - -All export operations leverage the existing, well-architected plugin system that was previously unused by the UI components. diff --git a/workstation/frontend/README.md b/workstation/frontend/README.md deleted file mode 100644 index e6e3f74..0000000 --- a/workstation/frontend/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Orpheus Engine Workstation Frontend - -This directory contains the frontend application for the Orpheus Engine Workstation, built using React and TypeScript. - -## Getting Started - -To get started with the frontend application, follow these steps: - -1. **Install Dependencies**: Make sure you have Node.js installed. Then, navigate to the `frontend` directory and run: - ``` - npm install - ``` - -2. **Run the Application**: After installing the dependencies, you can start the development server with: - ``` - npm start - ``` - -3. **Build for Production**: To create a production build of the application, run: - ``` - npm run build - ``` - -## Project Structure - -- **src/**: Contains the source code for the React application. - - **index.tsx**: The entry point for the React application. - - **App.tsx**: The main application component. - - **components/**: Contains reusable components, such as `AudioSearch.tsx`. - - **types/**: Contains TypeScript type definitions used throughout the application. - -- **package.json**: Lists the dependencies and scripts for the frontend application. - -- **tsconfig.json**: TypeScript configuration file. - -## Contributing - -If you would like to contribute to the project, please fork the repository and submit a pull request with your changes. - -## License - -This project is licensed under the MIT License. See the LICENSE file for more details. \ No newline at end of file diff --git a/workstation/frontend/index.html b/workstation/frontend/index.html deleted file mode 100644 index 472a7dc..0000000 --- a/workstation/frontend/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Orpheus Engine Workstation - - -
- - - diff --git a/workstation/frontend/main.js b/workstation/frontend/main.js deleted file mode 100644 index 54ab833..0000000 --- a/workstation/frontend/main.js +++ /dev/null @@ -1,168 +0,0 @@ -const { app, BrowserWindow, Menu, ipcMain } = require('electron'); -const path = require('path'); -const isDev = require('electron-is-dev'); - -// Keep a global reference of the window object -let mainWindow; - -function createWindow() { - // Create the browser window - mainWindow = new BrowserWindow({ - width: 1200, - height: 800, - minWidth: 800, - minHeight: 600, - webPreferences: { - nodeIntegration: false, - contextIsolation: true, - enableRemoteModule: false, - preload: path.join(__dirname, 'preload.js') - }, - icon: path.join(__dirname, 'assets/icon.png'), // Add icon later - title: 'Orpheus Engine Workstation', - show: false // Don't show until ready - }); - - // Load the app - const startUrl = isDev - ? 'http://localhost:5174' - : `file://${path.join(__dirname, 'OEW-main/index.html')}`; - - mainWindow.loadURL(startUrl); - - // Show window when ready to prevent visual flash - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - - // Open DevTools in development - if (isDev) { - mainWindow.webContents.openDevTools(); - } - }); - - // Emitted when the window is closed - mainWindow.on('closed', () => { - mainWindow = null; - }); - - // Handle window controls - mainWindow.on('maximize', () => { - mainWindow.webContents.send('window-maximized'); - }); - - mainWindow.on('unmaximize', () => { - mainWindow.webContents.send('window-unmaximized'); - }); -} - -// Create menu -function createMenu() { - const template = [ - { - label: 'File', - submenu: [ - { - label: 'New Project', - accelerator: 'CmdOrCtrl+N', - click: () => { - mainWindow.webContents.send('menu-new-project'); - } - }, - { - label: 'Open Project', - accelerator: 'CmdOrCtrl+O', - click: () => { - mainWindow.webContents.send('menu-open-project'); - } - }, - { type: 'separator' }, - { - label: 'Exit', - accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q', - click: () => { - app.quit(); - } - } - ] - }, - { - label: 'Edit', - submenu: [ - { role: 'undo' }, - { role: 'redo' }, - { type: 'separator' }, - { role: 'cut' }, - { role: 'copy' }, - { role: 'paste' } - ] - }, - { - label: 'View', - submenu: [ - { role: 'reload' }, - { role: 'forceReload' }, - { role: 'toggleDevTools' }, - { type: 'separator' }, - { role: 'resetZoom' }, - { role: 'zoomIn' }, - { role: 'zoomOut' }, - { type: 'separator' }, - { role: 'togglefullscreen' } - ] - }, - { - label: 'Audio', - submenu: [ - { - label: 'Start Recording', - accelerator: 'CmdOrCtrl+R', - click: () => { - mainWindow.webContents.send('menu-start-recording'); - } - }, - { - label: 'Stop Recording', - accelerator: 'CmdOrCtrl+Shift+R', - click: () => { - mainWindow.webContents.send('menu-stop-recording'); - } - } - ] - } - ]; - - const menu = Menu.buildFromTemplate(template); - Menu.setApplicationMenu(menu); -} - -// App event listeners -app.whenReady().then(() => { - createWindow(); - createMenu(); - - app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - } - }); -}); - -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit(); - } -}); - -// IPC handlers -ipcMain.handle('get-app-version', () => { - return app.getVersion(); -}); - -ipcMain.handle('show-message-box', async (event, options) => { - const { dialog } = require('electron'); - const result = await dialog.showMessageBox(mainWindow, options); - return result; -}); - -// Handle app protocol for deep linking (future feature) -app.setAsDefaultProtocolClient('orpheus-engine'); diff --git a/workstation/frontend/package.json b/workstation/frontend/package.json deleted file mode 100644 index 0943bb6..0000000 --- a/workstation/frontend/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "orpheus-engine-workstation-frontend", - "version": "1.0.18", - "description": "Frontend for the Orpheus Engine Workstation, providing a user interface for audio search and retrieval.", - "main": "src/index.tsx", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "test": "vitest run", - "test:watch": "vitest", - "electron-dev": "concurrently \"VITE_DEV_SERVER_URL=http://localhost:5174 vite\" \"wait-on tcp:5174 && electron ../../electron/main.js\"", - "electron-build": "tsc && vite build && electron-builder" - }, - "dependencies": { - "@apollo/client": "^3.13.8", - "@emotion/react": "^11.11.3", - "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.15.6", - "@mui/material": "^5.15.6", - "axios": "^1.6.5", - "graphql": "^16.11.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-dropzone": "^14.2.3", - "react-query": "^3.39.3", - "react-router-dom": "^6.30.1", - "wavesurfer.js": "^7.6.4", - "zustand": "^4.5.0" - }, - "devDependencies": { - "@electron/typescript-definitions": "^8.10.0", - "@jest/globals": "^30.0.0-beta.3", - "@testing-library/jest-dom": "^6.2.0", - "@testing-library/react": "^14.1.2", - "@types/jest": "^29.5.14", - "@types/node": "^18.15.11", - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", - "@typescript-eslint/eslint-plugin": "^8.33.1", - "@typescript-eslint/parser": "^8.33.1", - "@vitejs/plugin-react": "^4.5.0", - "eslint": "^8.56.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", - "jsdom": "^23.2.0", - "typescript": "^5.3.3", - "vite": "^5.4.19", - "vitest": "^1.2.1", - "wait-on": "^8.0.3" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/workstation/frontend/preload.js b/workstation/frontend/preload.js deleted file mode 100644 index 54f796d..0000000 --- a/workstation/frontend/preload.js +++ /dev/null @@ -1,31 +0,0 @@ -const { contextBridge, ipcRenderer } = require('electron'); - -// Expose protected methods that allow the renderer process to use -// the ipcRenderer without exposing the entire object -contextBridge.exposeInMainWorld('electronAPI', { - // App info - getAppVersion: () => ipcRenderer.invoke('get-app-version'), - - // Dialog methods - showMessageBox: (options) => ipcRenderer.invoke('show-message-box', options), - - // Window controls - onWindowMaximized: (callback) => ipcRenderer.on('window-maximized', callback), - onWindowUnmaximized: (callback) => ipcRenderer.on('window-unmaximized', callback), - - // Menu events - onMenuNewProject: (callback) => ipcRenderer.on('menu-new-project', callback), - onMenuOpenProject: (callback) => ipcRenderer.on('menu-open-project', callback), - onMenuStartRecording: (callback) => ipcRenderer.on('menu-start-recording', callback), - onMenuStopRecording: (callback) => ipcRenderer.on('menu-stop-recording', callback), - - // Remove listeners - removeAllListeners: (channel) => ipcRenderer.removeAllListeners(channel) -}); - -// Expose a simple API for the renderer -contextBridge.exposeInMainWorld('orpheusAPI', { - platform: process.platform, - isElectron: true, - version: process.versions.electron -}); diff --git a/workstation/frontend/public/favicon.ico b/workstation/frontend/public/favicon.ico deleted file mode 100644 index f50efb3..0000000 --- a/workstation/frontend/public/favicon.ico +++ /dev/null @@ -1 +0,0 @@ - diff --git a/workstation/frontend/public/index.html b/workstation/frontend/public/index.html deleted file mode 100644 index a1b88b3..0000000 --- a/workstation/frontend/public/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Electron Quick Start - - -

Hello World!

-

This is a basic Electron application.

- - \ No newline at end of file diff --git a/workstation/frontend/public/startup.html b/workstation/frontend/public/startup.html deleted file mode 100644 index d855523..0000000 --- a/workstation/frontend/public/startup.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - Orpheus Engine - Starting Up - - - -
- -
Digital Audio Workstation & AI Music Studio
-
- -
-
-
- - Initializing services... -
-
-
-
-
0 / 0 services ready
-
- -
- -
- -
-

🚀 All systems ready!

-

Opening Orpheus Engine DAW...

-
-
- - - - - - diff --git a/workstation/frontend/scripts/audio_analysis_bridge.py b/workstation/frontend/scripts/audio_analysis_bridge.py deleted file mode 100644 index e69de29..0000000 diff --git a/workstation/frontend/src/App.css b/workstation/frontend/src/App.css deleted file mode 100644 index 2ddb950..0000000 --- a/workstation/frontend/src/App.css +++ /dev/null @@ -1,46 +0,0 @@ -.app-container { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; -} - -.app-header { - background-color: #1a1a1a; - padding: 1rem; - border-bottom: 1px solid #333; - color: white; -} - -.app-header h1 { - margin: 0; - font-size: 1.5rem; -} - -.app-main { - flex: 1; - padding: 1rem; - overflow: auto; -} - -.welcome-message { - text-align: center; - margin: 2rem auto; - max-width: 600px; -} - -.main-controls { - margin: 1rem 0; - padding: 1rem; - background-color: #2a2a2a; - border-radius: 8px; -} - -.app-footer { - padding: 0.5rem 1rem; - background-color: #1a1a1a; - border-top: 1px solid #333; - text-align: center; - font-size: 0.8rem; - color: #888; -} diff --git a/workstation/frontend/src/App.tsx b/workstation/frontend/src/App.tsx deleted file mode 100644 index 285e224..0000000 --- a/workstation/frontend/src/App.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { BrowserRouter, MemoryRouter, Routes, Route } from "react-router-dom"; -import { ClipboardProvider } from "./contexts/ClipboardContext"; -import WorkstationProvider from "./contexts/WorkstationProvider"; -import { MixerProvider } from "./contexts/MixerContext"; -import { PreferencesProvider } from "./contexts/PreferencesContext"; -import { DAWProvider } from "./contexts/DAWContext"; -import Workstation from "./components/Workstation"; -import Preferences from "./components/Preferences"; -import SettingsProvider from "./components/settings/SettingsManager"; -import { ApolloWrapper } from "./apollo/ApolloWrapper"; -import { AudioLibrary } from "./components/AudioLibrary"; -import "./styles/App.css"; - -// Import DAW components -import AudioRecorderComponent from './components/daw/AudioRecorderComponent'; -import AudioAnalyzer from './components/daw/AudioAnalyzer'; -import Timeline from './components/daw/Timeline'; -import MixerControls from './components/daw/MixerControls'; -import Transport from './components/daw/Transport'; -import { isElectron } from './utils/electron'; -import { TimelinePosition } from './services/types/types'; - -interface AppProps { - onReady?: () => void; -} - -function App({ onReady }: AppProps): React.ReactElement { - const [isLoaded, setIsLoaded] = useState(false); - const isDesktopMode = isElectron(); - - useEffect(() => { - // Initialize Electron integration if available - if (typeof window !== 'undefined' && (window as any).electronAPI) { - console.log('🚀 Orpheus Engine Workstation - Electron Mode'); - console.log('Platform:', (window as any).orpheusAPI?.platform); - - // Try to get version safely - const electronAPI = (window as any).electronAPI; - if (electronAPI.getAppVersion) { - electronAPI.getAppVersion().then((version: string) => { - console.log('Version:', version); - }).catch((err: any) => console.warn('Could not get version:', err)); - } - } - - // Workaround for Electron bug with input focus and text selection - function handleFocusOut(e: FocusEvent) { - const relatedTarget = e.relatedTarget as HTMLElement; - - if (!relatedTarget || (relatedTarget.tagName !== "INPUT" && relatedTarget.tagName !== "TEXTAREA")) { - const selection = window.getSelection(); - if (selection) { - if (selection.rangeCount > 0) { - selection.collapseToEnd(); - } else { - selection.removeAllRanges(); - } - } - } - } - - document.addEventListener("focusout", handleFocusOut, { capture: true }); - return () => document.removeEventListener("focusout", handleFocusOut, { capture: true }); - }, []); - - useEffect(() => { - // Signal that the app is ready once mounted - setIsLoaded(true); - if (onReady) { - onReady(); - } - }, [onReady]); - - // Electron/Desktop Mode Render - const renderElectronMode = () => ( - - - -
-
- console.log("Tempo changed:", tempo)} - /> - { - console.log("Position changed:", pos); - }} - /> -
- - -
- -
-
- - } - /> -
- -
- ); - - // Web Mode Render - const renderWebMode = () => ( - - -
-
-

Orpheus Engine Workstation

-
-
-
-

Digital Audio Workstation

-

Welcome to Orpheus Engine! Your audio production hub is {isLoaded ? "ready" : "loading..."}

-
- -
- -
- -
- -
- - {/* Audio Library Section */} -
-

Audio Library

- -
-
-
-

Orpheus Engine v1.0.9

-
-
-
-
- ); - - return ( - - - - - {isDesktopMode ? ( -
- -
- ) : ( - - - -
-
-

Orpheus Engine Workstation

-
-
-
-

Digital Audio Workstation

-

Welcome to Orpheus Engine! Your audio production hub is {isLoaded ? "ready" : "loading..."}

-
- -
- -
- -
- -
- - {/* Audio Library Section */} -
-

Audio Library

- -
-
-
-

Orpheus Engine v1.0.9

-
-
-
-
-
- )} -
-
-
-
- ); -} - -export default App; diff --git a/workstation/frontend/src/apollo/ApolloWrapper.tsx b/workstation/frontend/src/apollo/ApolloWrapper.tsx deleted file mode 100644 index 7478475..0000000 --- a/workstation/frontend/src/apollo/ApolloWrapper.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, { ReactNode } from 'react'; -import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; - -// Create the Apollo client -const client = new ApolloClient({ - uri: 'http://localhost:7008/api/graphql', - cache: new InMemoryCache() -}); - -interface ApolloWrapperProps { - children: ReactNode; -} - -// Apollo wrapper component that can be used in App.tsx -export function ApolloWrapper({ children }: ApolloWrapperProps) { - return ( - - {children} - - ); -} diff --git a/workstation/frontend/src/apollo/client.ts b/workstation/frontend/src/apollo/client.ts deleted file mode 100644 index 85bc001..0000000 --- a/workstation/frontend/src/apollo/client.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; - -// Create an HTTP link to the GraphQL API -const httpLink = createHttpLink({ - uri: 'http://localhost:7008/api/graphql', // Adjust to match your backend URL -}); - -// Create the Apollo Client instance -const client = new ApolloClient({ - link: httpLink, - cache: new InMemoryCache(), - defaultOptions: { - watchQuery: { - fetchPolicy: 'cache-and-network', - }, - }, -}); - -export default client; diff --git a/workstation/frontend/src/assets/icons/icon-128x128.png b/workstation/frontend/src/assets/icons/icon-128x128.png deleted file mode 100644 index 742453c2367e3707e2a229bb8c444e17d1f2f5c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2899 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 diff --git a/workstation/frontend/src/assets/icons/icon-32x32.png b/workstation/frontend/src/assets/icons/icon-32x32.png deleted file mode 100644 index 47d92fdaec2cd79bf471177cca955d83d3a02c83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 885 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 diff --git a/workstation/frontend/src/assets/icons/icon.ico b/workstation/frontend/src/assets/icons/icon.ico deleted file mode 100644 index 6f8750de045398a90b8d1d358b6b2ee9ad48f7d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6034 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 diff --git a/workstation/frontend/src/assets/icons/icon.png b/workstation/frontend/src/assets/icons/icon.png deleted file mode 100644 index b328c2ea31d0d356d94cc965c1735e78708c63f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7426 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 diff --git a/workstation/frontend/src/components/AudioClipComponent.tsx b/workstation/frontend/src/components/AudioClipComponent.tsx deleted file mode 100644 index bb660b5..0000000 --- a/workstation/frontend/src/components/AudioClipComponent.tsx +++ /dev/null @@ -1,536 +0,0 @@ -import { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; -import { useWorkstation } from "../contexts/WorkstationContext"; -import { TimelinePosition, TrackType, WaveformLODLevel, Clip, Track } from "../services/types/types"; -import Waveform from "../../OEW-main/src/screens/workstation/components/Waveform"; -import { audioBufferToBuffer, audioContext, reverseAudio } from "../services/utils/audio"; -import { AudioExportPluginManager } from "../services/plugins/PluginManager"; -import { ExportPluginOptions } from "../services/plugins/types"; - -export const WAVEFORM_CHUNK_SIZE = 2048; - -// Define WaveformLevelsOfDetail interface locally -interface WaveformLevelsOfDetail { - ultraLow: Float32Array[]; - low: Float32Array[]; - medium: Float32Array[]; - high: Float32Array[]; -} - -// Define AudioClipComponentProps interface -interface AudioClipComponentProps { - clip: Clip; - height: number; - onChangeLane: (clip: Clip, track: Track) => void; - onSetClip: (clip: Clip) => void; - track: Track; -} - -interface AudioClipWaveformProps { - copyFrom?: { canvas: HTMLCanvasElement }; - data?: Float32Array[] | null; - height: number; - offset: number; - width: number; - onDraw?: (canvas: HTMLCanvasElement | null) => void; - offscreenDrawing?: boolean; -} - -function AudioClipWaveform({ copyFrom, data, height, offset, width, onDraw, offscreenDrawing }: AudioClipWaveformProps) { - const canvasRef = useRef(null); - - useLayoutEffect(() => { - if (copyFrom && canvasRef.current) { - const ctx = canvasRef.current.getContext("2d", { willReadFrequently: true }); - if (ctx) { - ctx.drawImage(copyFrom.canvas, 0, 0, width, height); - } - } - }, [copyFrom, width, height]); - - const handleDraw = (canvas: HTMLCanvasElement | null) => { - if (onDraw) onDraw(canvas); - }; - - return ( -
- {copyFrom ? ( - - ) : ( - data && - )} - {data && ( -
-
-
- )} -
- ); -} - -function AudioClipComponent({ clip, height, onChangeLane, onSetClip, track }: AudioClipComponentProps) { - const { timelineSettings } = useWorkstation(); - - const [copyFrom, setCopyFrom] = useState<{ canvas: HTMLCanvasElement }>(); - const [spriteOffset, setSpriteOffset] = useState(0); - const [waveformLevelsOfDetail, setWaveformLevelsOfDetail] = useState(null); - const [isExporting, setIsExporting] = useState(false); - const [showExportMenu, setShowExportMenu] = useState(false); - const [pluginManager] = useState(() => new AudioExportPluginManager()); - - const audioRef = useRef(null); - const clipAudio = clip.audio!; - const audioWidth = clipAudio.end.diffInMargin(clipAudio.start); - - const url = useMemo(() => { - if (clipAudio.buffer) { - return URL.createObjectURL(new Blob([clipAudio.buffer], { type: clipAudio.type })); - } - return ''; - }, [clipAudio.buffer, clipAudio.type]); - - useEffect(() => { - if (audioWidth > WAVEFORM_CHUNK_SIZE) { - setCopyFrom(undefined); - } - }, [audioWidth]); - - const waveformData = useMemo(() => { - if (waveformLevelsOfDetail) { - const samplesPerPixel = waveformLevelsOfDetail.high[0].length / audioWidth; - - if (samplesPerPixel > 250) return waveformLevelsOfDetail.ultraLow; - if (samplesPerPixel > 100) return waveformLevelsOfDetail.low; - if (samplesPerPixel > 20) return waveformLevelsOfDetail.medium; - return waveformLevelsOfDetail.high; - } - return null; - }, [audioWidth, waveformLevelsOfDetail]); - - useEffect(() => { - loadAudioData(); - }, [clipAudio.audioBuffer]); - - useEffect(() => { - updateSpriteOffset(clip.start); - }, [timelineSettings, clip.start]); - - useEffect(() => { - // Initialize export plugins on component mount - pluginManager.loadPlugins().catch(console.error); - }, []); - - useEffect(() => { - if (audioRef.current && clipAudio.sourceDuration) { - const duration = TimelinePosition.fromSpan(clipAudio.end.diff(clipAudio.start)).toSeconds(); - let playbackRate = clipAudio.sourceDuration / duration; - - if (playbackRate > 16) playbackRate = 16; - if (playbackRate < 0.0625) playbackRate = 0.0625; - - audioRef.current.playbackRate = playbackRate; - } - }, [clipAudio, timelineSettings]); - - function downsampleWaveformData(audioBuffer: AudioBuffer, targetLength: number) { - const newWaveformData: Float32Array[] = []; - - for (let i = 0; i < audioBuffer.numberOfChannels; i++) { - const data = audioBuffer.getChannelData(i); - const samplesPerPixel = Math.ceil(data.length / targetLength); - const channelData = new Float32Array(targetLength * 2); - - for (let j = 0; j < targetLength; j++) { - let min = 0, max = 0; - - for (let k = 0; k < samplesPerPixel; k++) { - if (j * samplesPerPixel + k < data.length) { - const datum = data[j * samplesPerPixel + k]; - if (datum < min) min = datum; - if (datum > max) max = datum; - } - } - - channelData[j * 2] = max; - channelData[j * 2 + 1] = min; - } - - newWaveformData.push(channelData); - } - - return newWaveformData; - } - - function handleDraw(canvas: HTMLCanvasElement | null) { - if (canvas && audioWidth < WAVEFORM_CHUNK_SIZE) { - setCopyFrom({ canvas }); - } - } - - async function loadAudioData() { - if (!clipAudio.audioBuffer && clipAudio.buffer) { - let arrayBuffer: ArrayBuffer; - - if (clipAudio.buffer instanceof ArrayBuffer) { - arrayBuffer = clipAudio.buffer; - } else if (clipAudio.buffer instanceof Uint8Array) { - const buffer = clipAudio.buffer.buffer; - arrayBuffer = buffer instanceof ArrayBuffer - ? buffer.slice(clipAudio.buffer.byteOffset, clipAudio.buffer.byteOffset + clipAudio.buffer.byteLength) - : new ArrayBuffer(0); - } else { - console.error('Unsupported buffer type'); - return; - } - - try { - const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); - onSetClip({ ...clip, audio: { ...clipAudio, audioBuffer } }); - } catch (error) { - console.error('Failed to decode audio data:', error); - } - } else if (clipAudio.audioBuffer) { - const data = clipAudio.audioBuffer.getChannelData(0); - setWaveformLevelsOfDetail({ - ultraLow: downsampleWaveformData(clipAudio.audioBuffer, Math.floor(data.length / 25000)), - low: downsampleWaveformData(clipAudio.audioBuffer, Math.floor(data.length / 12500)), - medium: downsampleWaveformData(clipAudio.audioBuffer, Math.floor(data.length / 2500)), - high: downsampleWaveformData(clipAudio.audioBuffer, Math.floor(data.length / 200)) - }); - } - } - - function onResize(data: { edge: { x: string }, coords: { startX: number } }) { - if (data.edge.x === "left") { - updateSpriteOffset(TimelinePosition.fromMargin(data.coords.startX)); - } - } - - function updateSpriteOffset(pos: TimelinePosition) { - setSpriteOffset(-pos.diffInMargin(clipAudio.start)); - } - - // Export functionality - const handleExportToLocal = async () => { - if (!clipAudio.audioBuffer) return; - - setIsExporting(true); - try { - const result = await pluginManager.export(clip, { - storage: { provider: 'local' }, - audioFormat: 'wav', - quality: 'high', - metadata: { - title: `Clip_${clip.id}`, - artist: 'Orpheus Engine', - album: track.name - } - }); - console.log('Local export completed:', result); - alert(`Export successful! File saved locally.`); - } catch (error) { - console.error('Export failed:', error); - alert(`Export failed: ${error}`); - } finally { - setIsExporting(false); - setShowExportMenu(false); - } - }; - - const handleExportToCloud = async () => { - if (!clipAudio.audioBuffer) return; - - setIsExporting(true); - try { - const result = await pluginManager.export(clip, { - storage: { - provider: 'aws-s3', - bucket: 'orpheus-audio-exports', - path: 'clips' - }, - audioFormat: 'wav', - quality: 'high', - metadata: { - title: `Clip_${clip.id}`, - artist: 'Orpheus Engine', - album: track.name - } - }); - console.log('Cloud export completed:', result); - alert(`Export successful! File uploaded to cloud: ${result.url}`); - } catch (error) { - console.error('Cloud export failed:', error); - alert(`Cloud export failed: ${error}`); - } finally { - setIsExporting(false); - setShowExportMenu(false); - } - }; - - const handleExportToIPFS = async () => { - if (!clipAudio.audioBuffer) return; - - setIsExporting(true); - try { - const result = await pluginManager.export(clip, { - storage: { provider: 'ipfs' }, - audioFormat: 'wav', - quality: 'high', - metadata: { - title: `Clip_${clip.id}`, - artist: 'Orpheus Engine', - album: track.name - } - }); - console.log('IPFS export completed:', result); - alert(`Export successful! IPFS Hash: ${result.ipfsHash}`); - } catch (error) { - console.error('IPFS export failed:', error); - alert(`IPFS export failed: ${error}`); - } finally { - setIsExporting(false); - setShowExportMenu(false); - } - }; - - const handleExportWithStoryProtocol = async () => { - if (!clipAudio.audioBuffer) return; - - setIsExporting(true); - try { - const result = await pluginManager.export(clip, { - storage: { provider: 'ipfs' }, - blockchain: { - storyProtocol: { - enabled: true, - registerIP: true, - licenseTerms: 'CC BY-SA 4.0', - metadata: { - title: `Audio Clip ${clip.id}`, - creator: 'Orpheus Engine User', - license: 'Creative Commons Attribution-ShareAlike 4.0' - } - } - }, - audioFormat: 'wav', - quality: 'lossless', - metadata: { - title: `Clip_${clip.id}`, - artist: 'Orpheus Engine', - album: track.name - } - }); - console.log('Story Protocol export completed:', result); - alert(`Export successful! IP registered on Story Protocol. ID: ${result.storyProtocolId}`); - } catch (error) { - console.error('Story Protocol export failed:', error); - alert(`Story Protocol export failed: ${error}`); - } finally { - setIsExporting(false); - setShowExportMenu(false); - } - }; - - const waveformProps = (height: number, isCopy: boolean) => ({ - copyFrom: isCopy ? copyFrom : undefined, - data: waveformData, - height, - offscreenDrawing: isCopy ? false : audioWidth < WAVEFORM_CHUNK_SIZE, - offset: spriteOffset, - onDraw: isCopy ? undefined : handleDraw, - width: audioWidth - }); - - // Simple wrapper component that implements the expected interface - const SimpleClipComponent = ({ - automationSprite, - clip, - height, - onChangeLane, - onResize, - onSetClip, - sprite, - track - }: { - automationSprite?: (height: number) => React.ReactNode; - clip: Clip; - height: number; - onChangeLane: (clip: Clip, track: Track) => void; - onResize: (data: { edge: { x: string }, coords: { startX: number } }) => void; - onSetClip: (clip: Clip) => void; - sprite?: (height: number) => React.ReactNode; - track: Track; - }) => { - return ( -
setShowExportMenu(!showExportMenu)} - > - {sprite && sprite(height)} - {automationSprite && ( -
- {automationSprite(height)} -
- )} - - {/* Export Menu */} - {showExportMenu && ( -
-
- Export Audio Clip -
- - - - - - - - - - - - {isExporting && ( -
- Exporting... -
- )} -
- )} -
- ); - }; - - return ( - <> - } - clip={clip} - height={height} - onChangeLane={onChangeLane} - onResize={onResize} - onSetClip={onSetClip} - sprite={(height: number) => } - track={track} - /> -