diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-06 11:22:01 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-06 11:22:01 +0100 |
| commit | cf0775046c059fed1a4ed04d500f26397002667d (patch) | |
| tree | 00e643a38e601114e755ec77c8b4901b5d045ff8 /tools/spectral_editor/style.css | |
| parent | 5a1adde097e489c259bd052971546e95683c3596 (diff) | |
feat(tools): Add Spectral Brush Editor UI (Phase 2 of Task #5)
Implement web-based editor for procedural audio tracing.
New Files:
- tools/spectral_editor/index.html - Main UI structure
- tools/spectral_editor/style.css - VSCode-inspired dark theme
- tools/spectral_editor/script.js - Editor logic (~1200 lines)
- tools/spectral_editor/dct.js - IDCT/DCT implementation (reused)
- tools/spectral_editor/README.md - Complete user guide
Features:
- Dual-layer canvas (reference + procedural spectrograms)
- Bezier curve editor (click to place, drag to adjust, right-click to delete)
- Profile controls (Gaussian sigma slider)
- Real-time audio playback (Key 1=procedural, Key 2=original, Space=stop)
- Undo/Redo system (50-action history with snapshots)
- File I/O:
- Load .wav/.spec files (FFT/STFT or binary parser)
- Save procedural_params.txt (human-readable, re-editable)
- Generate C++ code (copy-paste ready for runtime)
- Keyboard shortcuts (Ctrl+Z/Shift+Z, Ctrl+S/Shift+S, Ctrl+O, ?)
- Help modal with shortcut reference
Technical:
- Pure HTML/CSS/JS (no dependencies)
- Web Audio API for playback (32 kHz sample rate)
- Canvas 2D for visualization (log-scale frequency)
- Linear Bezier interpolation matching C++ runtime
- IDCT with overlap-add synthesis
Next: Phase 3 (currently integrated in Phase 2)
- File loading already implemented
- Export already implemented
- Ready for user testing!
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/spectral_editor/style.css')
| -rw-r--r-- | tools/spectral_editor/style.css | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/tools/spectral_editor/style.css b/tools/spectral_editor/style.css new file mode 100644 index 0000000..36b4eb3 --- /dev/null +++ b/tools/spectral_editor/style.css @@ -0,0 +1,459 @@ +/* Spectral Brush Editor Styles */ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: #1e1e1e; + color: #d4d4d4; + overflow: hidden; + height: 100vh; +} + +#app { + display: flex; + flex-direction: column; + height: 100vh; +} + +/* Header */ +header { + background: #252526; + padding: 12px 20px; + border-bottom: 1px solid #3e3e42; + display: flex; + justify-content: space-between; + align-items: center; +} + +header h1 { + font-size: 18px; + font-weight: 600; + color: #cccccc; +} + +.header-controls { + display: flex; + align-items: center; + gap: 15px; +} + +.file-info { + font-size: 13px; + color: #858585; +} + +/* Main content area */ +.main-content { + display: flex; + flex: 1; + overflow: hidden; +} + +/* Canvas container (80% width) */ +.canvas-container { + flex: 1; + position: relative; + background: #1e1e1e; + border-right: 1px solid #3e3e42; +} + +#spectrogramCanvas { + width: 100%; + height: 100%; + display: block; + cursor: crosshair; +} + +.canvas-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: rgba(30, 30, 30, 0.9); + pointer-events: none; +} + +.canvas-overlay.hidden { + display: none; +} + +.canvas-overlay p { + font-size: 16px; + margin: 8px 0; +} + +.canvas-overlay .hint { + font-size: 13px; + color: #858585; +} + +/* Toolbar (20% width) */ +.toolbar { + width: 250px; + background: #252526; + padding: 15px; + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; +} + +.toolbar h3 { + font-size: 14px; + font-weight: 600; + color: #cccccc; + margin-bottom: 5px; +} + +.btn-toolbar { + padding: 8px 12px; + background: #0e639c; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; + font-size: 13px; + display: flex; + align-items: center; + gap: 6px; + transition: background 0.2s; +} + +.btn-toolbar:hover { + background: #1177bb; +} + +.btn-toolbar:disabled { + background: #3e3e42; + color: #858585; + cursor: not-allowed; +} + +.btn-toolbar.btn-danger { + background: #a82d2d; +} + +.btn-toolbar.btn-danger:hover:not(:disabled) { + background: #c94242; +} + +.curve-list { + margin-top: 10px; + display: flex; + flex-direction: column; + gap: 5px; +} + +.curve-item { + padding: 8px 10px; + background: #2d2d30; + border-radius: 3px; + cursor: pointer; + font-size: 13px; + transition: background 0.2s; + border: 1px solid transparent; +} + +.curve-item:hover { + background: #3e3e42; +} + +.curve-item.selected { + background: #094771; + border-color: #0e639c; +} + +/* Control panel (bottom) */ +.control-panel { + background: #252526; + border-top: 1px solid #3e3e42; + padding: 12px 20px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 20px; +} + +.control-section { + display: flex; + align-items: center; + gap: 10px; +} + +.control-section label { + font-size: 13px; + color: #cccccc; + white-space: nowrap; +} + +.select-input { + padding: 4px 8px; + background: #3c3c3c; + color: #cccccc; + border: 1px solid #3e3e42; + border-radius: 3px; + font-size: 13px; + cursor: pointer; +} + +.slider { + width: 150px; + height: 4px; + -webkit-appearance: none; + appearance: none; + background: #3e3e42; + border-radius: 2px; + outline: none; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 14px; + height: 14px; + background: #0e639c; + border-radius: 50%; + cursor: pointer; +} + +.slider::-moz-range-thumb { + width: 14px; + height: 14px; + background: #0e639c; + border-radius: 50%; + cursor: pointer; + border: none; +} + +.number-input { + width: 60px; + padding: 4px 6px; + background: #3c3c3c; + color: #cccccc; + border: 1px solid #3e3e42; + border-radius: 3px; + font-size: 13px; +} + +.playback-controls { + gap: 8px; +} + +.btn-playback { + padding: 6px 12px; + background: #0e639c; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; + font-size: 12px; + display: flex; + align-items: center; + gap: 6px; + transition: background 0.2s; +} + +.btn-playback:hover:not(:disabled) { + background: #1177bb; +} + +.btn-playback:disabled { + background: #3e3e42; + color: #858585; + cursor: not-allowed; +} + +.btn-playback kbd { + background: rgba(255, 255, 255, 0.1); + padding: 2px 5px; + border-radius: 3px; + font-size: 11px; +} + +/* Action bar (bottom) */ +.action-bar { + background: #2d2d30; + border-top: 1px solid #3e3e42; + padding: 10px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.action-group { + display: flex; + gap: 8px; +} + +.btn-action { + padding: 6px 12px; + background: #3c3c3c; + color: #cccccc; + border: 1px solid #3e3e42; + border-radius: 3px; + cursor: pointer; + font-size: 12px; + display: flex; + align-items: center; + gap: 6px; + transition: background 0.2s, border-color 0.2s; +} + +.btn-action:hover:not(:disabled) { + background: #505050; + border-color: #0e639c; +} + +.btn-action:disabled { + color: #858585; + cursor: not-allowed; +} + +.btn-primary { + padding: 6px 16px; + background: #0e639c; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; + font-size: 13px; + transition: background 0.2s; +} + +.btn-primary:hover { + background: #1177bb; +} + +/* Icon styling */ +.icon { + font-size: 14px; + line-height: 1; +} + +/* Modal */ +.modal { + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; +} + +.modal-content { + background-color: #252526; + padding: 30px; + border: 1px solid #3e3e42; + border-radius: 5px; + width: 600px; + max-height: 80vh; + overflow-y: auto; + position: relative; +} + +.modal-close { + position: absolute; + right: 15px; + top: 15px; + font-size: 28px; + font-weight: bold; + color: #858585; + cursor: pointer; + line-height: 1; +} + +.modal-close:hover { + color: #cccccc; +} + +.modal-content h2 { + font-size: 20px; + margin-bottom: 20px; + color: #cccccc; +} + +.modal-content h3 { + font-size: 16px; + margin-top: 20px; + margin-bottom: 10px; + color: #cccccc; +} + +.shortcuts-table { + width: 100%; + border-collapse: collapse; + margin-bottom: 20px; +} + +.shortcuts-table th, +.shortcuts-table td { + padding: 8px 12px; + text-align: left; + border-bottom: 1px solid #3e3e42; +} + +.shortcuts-table th { + background: #2d2d30; + font-weight: 600; + color: #cccccc; +} + +.shortcuts-table td { + color: #d4d4d4; +} + +.shortcuts-table kbd { + background: #3c3c3c; + border: 1px solid #3e3e42; + padding: 3px 8px; + border-radius: 3px; + font-family: monospace; + font-size: 12px; +} + +.modal-content ul { + list-style: none; + padding-left: 0; +} + +.modal-content li { + padding: 5px 0; + color: #d4d4d4; +} + +.modal-content li strong { + color: #cccccc; +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-track { + background: #1e1e1e; +} + +::-webkit-scrollbar-thumb { + background: #3e3e42; + border-radius: 5px; +} + +::-webkit-scrollbar-thumb:hover { + background: #4e4e52; +} |
