Files
2026-04-17 16:52:51 -04:00

873 lines
17 KiB
CSS

/* ============================================
STRUDEL MUSIC CODING INTERFACE STYLES
============================================
Integrates Strudel REPL with app day/night modes
============================================ */
/* ============================================
STRUDEL THEME BRIDGE
Let Strudel's built-in themes handle all styling.
syncStrudelTheme() switches between themes based on day/night mode.
============================================ */
/* Global visualization canvas - make it visible above page content */
#test-canvas {
z-index: 2;
pointer-events: none;
}
/* Main Container - 90% width, centered */
.strudel-container {
display: flex;
flex-direction: column;
height: calc(100vh - 200px);
width: 90%;
max-width: 1400px;
margin: 0 auto;
padding: 10px;
overflow-x: hidden;
overflow-y: auto;
}
/* Editor Section */
.strudel-editor-section {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
overflow: visible;
background: transparent;
}
/* Editor Wrapper */
.strudel-editor-wrapper {
flex: 1;
position: relative;
min-height: 400px;
}
/* Let strudel-editor use default styling */
strudel-editor {
display: block;
}
/* Control Panel */
.strudel-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
padding: 20px;
background: var(--secondary-color);
width: 100%;
box-sizing: border-box;
}
/* Buttons - use derived button variables from client.css */
.strudel-button {
padding: 10px 20px;
font-size: 16px;
font-family: var(--font-family);
border: var(--button-border-width) solid var(--button-border-color);
background: var(--button-background-color);
color: var(--button-color);
cursor: pointer;
flex: 1;
text-align: center;
border-radius: var(--button-border-radius);
}
/* Editor background - set on scroller so selection layer behind cm-content is visible */
strudel-editor + div .cm-editor {
background: var(--secondary-color) !important;
}
/* cm-content must be transparent so the cm-selectionLayer behind it can show through */
strudel-editor + div .cm-content {
background: transparent !important;
}
/* Gutter background */
strudel-editor + div .cm-gutters,
strudel-editor + div .cm-gutterElement {
background: var(--secondary-color) !important;
}
/* Text selection visibility - CodeMirror 6 uses .cm-selectionBackground divs in a layer behind cm-content */
strudel-editor + div .cm-selectionBackground,
strudel-editor + div .cm-selectionLayer .cm-selectionBackground {
background: color-mix(in srgb, var(--accent-color) 40%, var(--secondary-color)) !important;
}
/* ============================================
AI CHAT PANEL
============================================ */
.strudel-ai-panel {
display: flex;
flex-direction: column;
height: 250px;
border-top: 3px solid var(--primary-color);
background: var(--secondary-color);
}
.strudel-ai-history {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
.strudel-ai-input {
display: flex;
gap: 10px;
padding: 10px;
border-top: 3px solid var(--primary-color);
background: var(--secondary-color);
}
.strudel-ai-input textarea {
flex: 1;
padding: 10px;
font-family: var(--font-family);
font-size: 14px;
border: 3px solid var(--primary-color);
background: var(--secondary-color);
color: var(--primary-color);
resize: none;
}
.strudel-ai-input textarea:focus {
outline: none;
border-color: var(--accent-color);
}
/* AI Message bubbles */
.ai-message {
padding: 8px 12px;
border-radius: 5px;
max-width: 80%;
word-wrap: break-word;
font-size: 14px;
line-height: 1.4;
}
.ai-message-user {
align-self: flex-end;
background: var(--primary-color);
color: var(--secondary-color);
border: 3px solid var(--primary-color);
}
.ai-message-assistant {
align-self: flex-start;
background: var(--secondary-color);
color: var(--primary-color);
border: 3px solid var(--primary-color);
}
.ai-message-error {
align-self: center;
background: #ffcccc;
color: #cc0000;
border: 3px solid #cc0000;
}
/* ============================================
MIDI SECTION IN SIDENAV
============================================ */
#divMIDISection {
padding: 10px;
border-bottom: 3px solid var(--primary-color);
}
#divMIDISectionTitle {
font-weight: bold;
margin-bottom: 10px;
color: var(--primary-color);
}
#divMIDISettings {
display: flex;
flex-direction: column;
gap: 8px;
}
#divMIDISettings button {
padding: 8px;
font-family: var(--font-family);
font-size: 12px;
border: var(--button-border-width) solid var(--button-border-color);
background: var(--button-background-color);
color: var(--button-color);
border-radius: var(--button-border-radius);
cursor: pointer;
}
#divMIDISettings button:hover {
border-color: var(--button-hover-color);
color: var(--button-hover-color);
}
#divMIDISettings button:active {
border-color: var(--button-click-color);
color: var(--button-click-color);
}
#divMIDISettings button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
#divMIDISettings button.midi-learn-active {
border-color: var(--accent-color);
color: var(--accent-color);
animation: midi-pulse 1s ease-in-out infinite;
}
@keyframes midi-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
#divMIDIDeviceList {
font-size: 11px;
color: var(--primary-color);
line-height: 1.6;
}
.midi-device-group {
margin-bottom: 6px;
}
.midi-device-label {
font-weight: bold;
font-size: 11px;
color: var(--primary-color);
margin-bottom: 2px;
}
.midi-device-name {
font-size: 11px;
padding: 2px 4px;
background: var(--muted-color);
margin-bottom: 2px;
display: flex;
align-items: center;
gap: 4px;
}
.midi-device-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent-color);
flex-shrink: 0;
}
#divMIDIControlMap {
font-size: 10px;
color: var(--primary-color);
max-height: 150px;
overflow-y: auto;
border: 1px solid var(--muted-color);
padding: 4px;
display: none;
}
#divMIDIControlMap.has-controls {
display: block;
}
.midi-control-entry {
display: flex;
justify-content: space-between;
padding: 1px 0;
border-bottom: 1px solid var(--muted-color);
font-size: 10px;
}
.midi-control-label {
color: var(--primary-color);
flex: 1;
}
.midi-control-value {
color: var(--accent-color);
font-family: monospace;
font-size: 10px;
}
#divMIDILearnLog {
font-size: 10px;
color: var(--accent-color);
font-family: monospace;
min-height: 16px;
}
/* ============================================
AI SETTINGS IN SIDENAV
============================================ */
#divAISection {
padding: 10px;
border-bottom: 3px solid var(--primary-color);
}
#divAISectionTitle {
font-weight: bold;
margin-bottom: 10px;
color: var(--primary-color);
}
#divAISettings {
display: flex;
flex-direction: column;
gap: 10px;
}
#divAISettings input,
#divAISettings select {
padding: 8px;
font-family: var(--font-family);
font-size: 12px;
border: 3px solid var(--primary-color);
background: var(--secondary-color);
color: var(--primary-color);
width: 100%;
box-sizing: border-box;
}
#divAISettings input:focus,
#divAISettings select:focus {
outline: none;
border-color: var(--accent-color);
}
#divAISettings select {
cursor: pointer;
}
#divAISettings select option {
background: var(--secondary-color);
color: var(--primary-color);
}
#divAISettings button {
padding: 8px;
font-family: var(--font-family);
font-size: 12px;
border: var(--button-border-width) solid var(--button-border-color);
background: var(--button-background-color);
color: var(--button-color);
border-radius: var(--button-border-radius);
cursor: pointer;
}
#divAISettings button:hover {
border-color: var(--button-hover-color);
color: var(--button-hover-color);
}
#divAISettings button:active {
border-color: var(--button-click-color);
color: var(--button-click-color);
}
#divAISettings button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* ============================================
CODE TEXT COLOR OVERRIDES
Colors are now handled by the client theme
which uses CSS variables directly.
Theme colors:
- primary-color: text, keywords, properties
- accent-color: strings, numbers, functions, types, operators
- muted-color: comments, brackets, line numbers
============================================ */
/* Only override selection background to use accent color */
strudel-editor + div .cm-editor .cm-selectionBackground,
strudel-editor + div .cm-editor .cm-selectionLayer .cm-selectionBackground {
background: color-mix(in srgb, var(--accent-color) 40%, var(--secondary-color)) !important;
}
/* ============================================
VISUALIZATION WIDGET LAYOUT
Make scope/punchcard/pianoroll etc. appear below the line
============================================ */
strudel-editor + div .cm-line .cm-widget-container {
display: block;
width: 100%;
}
/* ============================================
NOTE HIGHLIGHTING OVERRIDE
The outline color when notes play
============================================ */
strudel-editor + div .cm-content [style*="outline"] {
outline-color: var(--strudel-highlight-color, var(--accent-color)) !important;
}
/* Alternative: target the specific highlight style */
strudel-editor + div .cm-content .strudel-highlight-active {
outline: solid 2px var(--strudel-highlight-color, var(--accent-color)) !important;
}
.strudel-button:hover:not(:disabled) {
color: var(--button-hover-color);
border-color: var(--button-hover-color);
}
.strudel-button:active:not(:disabled) {
color: var(--button-click-color);
border-color: var(--button-click-color);
}
.strudel-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Error Display */
.strudel-error {
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
max-width: 600px;
width: 90%;
background: #dc3545;
color: white;
padding: 1rem 1.5rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
z-index: 1000;
display: none;
animation: slideUp 0.3s ease;
}
.strudel-error.show {
display: block;
}
@keyframes slideUp {
from {
transform: translateX(-50%) translateY(20px);
opacity: 0;
}
to {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
}
.strudel-error-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.strudel-error-title {
font-weight: bold;
font-size: 16px;
}
.strudel-error-close {
background: none;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
line-height: 1;
}
.strudel-error-close:hover {
opacity: 0.8;
}
.strudel-error-message {
font-family: 'Courier New', monospace;
font-size: 14px;
white-space: pre-wrap;
word-break: break-word;
}
/* Loading State */
.strudel-loading {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 18px;
color: var(--text-color);
}
.strudel-loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: var(--accent-color);
animation: spin 1s linear infinite;
margin-right: 0.5rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive Design */
@media (max-width: 1024px) {
.strudel-container {
/* height: auto; */
min-height: 400px;
}
.strudel-editor-section {
min-height: 400px;
}
}
@media (max-width: 768px) {
.strudel-controls {
flex-wrap: wrap;
gap: 0.5rem;
}
.strudel-button {
padding: 0.5rem 1rem;
font-size: 14px;
min-width: 100px;
}
.strudel-error {
bottom: 60px;
width: 95%;
padding: 0.75rem 1rem;
}
}
/* ============================================
MIDI SECTION IN SIDENAV
============================================ */
#divMIDISection {
padding: 10px;
border-bottom: 3px solid var(--primary-color);
}
#divMIDISectionTitle {
font-weight: bold;
margin-bottom: 10px;
color: var(--primary-color);
}
#divMIDISettings {
display: flex;
flex-direction: column;
gap: 8px;
}
#divMIDIDeviceList {
font-size: 11px;
}
.midi-device-group {
margin-bottom: 8px;
}
.midi-device-label {
font-weight: bold;
color: var(--accent-color);
margin-bottom: 4px;
}
.midi-device-name {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 2px;
}
.midi-device-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent-color);
}
#divMIDILearnLog {
font-size: 11px;
color: var(--accent-color);
min-height: 16px;
}
#divMIDIControlMap {
font-size: 10px;
}
#divMIDIControlMap.has-controls {
border: 1px solid var(--primary-color);
padding: 5px;
margin-top: 5px;
}
.midi-control-entry {
display: flex;
justify-content: space-between;
margin-bottom: 2px;
}
.midi-control-label {
color: var(--primary-color);
}
.midi-control-value {
color: var(--accent-color);
font-family: monospace;
}
#midiScanButton,
#midiLearnButton,
#midiClearMapButton {
font-size: 11px;
padding: 4px 8px;
}
#midiLearnButton.midi-learn-active {
background: var(--accent-color);
color: var(--primary-color);
}
/* ============================================
AI SECTION IN SIDENAV
============================================ */
#divAISection {
padding: 10px;
border-bottom: 3px solid var(--primary-color);
}
#divAISectionTitle {
font-weight: bold;
margin-bottom: 10px;
color: var(--primary-color);
}
#divAISettings {
display: flex;
flex-direction: column;
gap: 8px;
}
#divAISettings input,
#divAISettings select {
font-size: 11px;
padding: 4px;
}
#aiRefreshModels,
#aiSaveSettings,
#aiClearChat {
font-size: 11px;
padding: 4px 8px;
}
/* ============================================
AI CHAT PANEL (MAIN)
============================================ */
.strudel-ai-panel {
display: flex;
flex-direction: column;
height: 250px;
border-top: 3px solid var(--primary-color);
background: var(--secondary-color);
}
.strudel-ai-history {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
.strudel-ai-input {
display: flex;
gap: 10px;
padding: 10px;
border-top: 3px solid var(--primary-color);
background: var(--secondary-color);
}
.strudel-ai-input textarea {
flex: 1;
padding: 10px;
font-family: var(--font-family);
font-size: 14px;
border: 3px solid var(--primary-color);
background: var(--secondary-color);
color: var(--primary-color);
resize: none;
}
.strudel-ai-input textarea:focus {
outline: none;
border-color: var(--accent-color);
}
/* AI Message bubbles */
.ai-message {
padding: 8px 12px;
border-radius: 5px;
max-width: 80%;
word-wrap: break-word;
font-size: 14px;
line-height: 1.4;
}
.ai-message-user {
align-self: flex-end;
background: var(--primary-color);
color: var(--secondary-color);
border: 3px solid var(--primary-color);
}
.ai-message-assistant {
align-self: flex-start;
background: var(--secondary-color);
color: var(--primary-color);
border: 3px solid var(--primary-color);
}
.ai-message-error {
align-self: center;
background: #ffcccc;
color: #cc0000;
border: 3px solid #cc0000;
}
/* ============================================
SONGS/INSTRUMENTS SIDEBAR SECTIONS
============================================ */
#divSongsSection,
#divInstrumentsSection {
padding: 10px;
border-bottom: 3px solid var(--primary-color);
}
#divSongsSectionTitle,
#divInstrumentsSectionTitle {
font-weight: bold;
margin-bottom: 10px;
color: var(--primary-color);
}
#divSongsSaveRow,
#divInstrumentsSaveRow {
display: flex;
gap: 8px;
margin-bottom: 8px;
}
#divSongsSaveRow input,
#divInstrumentsSaveRow input {
flex: 1;
font-size: 11px;
padding: 4px;
}
#songsSaveButton,
#songsUpdateButton,
#instrumentsSaveButton,
#instrumentsUpdateButton {
font-size: 11px;
padding: 4px 8px;
}
#divSongsList,
#divInstrumentsList {
max-height: 150px;
overflow-y: auto;
font-size: 11px;
}
#divInstrumentsHint {
font-size: 10px;
color: var(--muted-color);
margin-bottom: 8px;
}
.library-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 6px;
margin-bottom: 4px;
background: var(--secondary-color);
border: 1px solid var(--primary-color);
cursor: pointer;
}
.library-item:hover {
background: var(--accent-color);
color: var(--primary-color);
}
.library-item-delete {
background: none;
border: none;
color: #ff4444;
cursor: pointer;
font-size: 14px;
padding: 0 4px;
}
.library-item-delete:hover {
background: #ff4444;
color: white;
}
/* Error line highlighting in CodeMirror */
.strudel-error-line {
background: color-mix(in srgb, var(--accent-color) 30%, transparent) !important;
border-left: 3px solid var(--accent-color);
}
.cm-line.strudel-error-line {
background: color-mix(in srgb, var(--accent-color) 30%, transparent) !important;
}
/* More specific selector for CodeMirror lines within strudel-editor */
strudel-editor + div .cm-line.strudel-error-line,
strudel-editor .cm-line.strudel-error-line,
.cm-editor .cm-line.strudel-error-line {
background: color-mix(in srgb, var(--accent-color) 30%, transparent) !important;
}
/* Also target via attribute for maximum specificity */
.cm-content .cm-line.strudel-error-line {
background: color-mix(in srgb, var(--accent-color) 30%, transparent) !important;
}