summaryrefslogtreecommitdiff
path: root/tools/spectral_editor/style.css
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-06 11:22:01 +0100
committerskal <pascal.massimino@gmail.com>2026-02-06 11:22:01 +0100
commitcf0775046c059fed1a4ed04d500f26397002667d (patch)
tree00e643a38e601114e755ec77c8b4901b5d045ff8 /tools/spectral_editor/style.css
parent5a1adde097e489c259bd052971546e95683c3596 (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.css459
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;
+}