This commit is contained in:
2025-10-15 16:52:39 +02:00
parent 1015e9e18f
commit 1b35c4ccc1
26 changed files with 1078 additions and 433 deletions

189
THEME.md Normal file
View File

@ -0,0 +1,189 @@
# Theme System
## Overview
The application uses a centralized CSS custom properties (variables) system defined in `src/theme.css`. This provides a consistent design language across all components and makes theme switching possible in the future.
## Architecture
### Theme Variables
All theme variables are defined at the `:root` level in `theme.css` and use the `--` prefix:
```css
:root {
--color-accent-primary: #646cff;
--space-md: 0.75rem;
--font-base: 0.875rem;
}
```
### Usage in Components
Components reference theme variables using `var()`:
```css
.my-component {
color: var(--color-text-primary);
padding: var(--space-md);
font-size: var(--font-base);
}
```
## Variable Categories
### Colors
#### Base & Surface
- `--color-bg-base`: Main background
- `--color-surface-0` through `--color-surface-3`: Elevated surfaces
#### Text Hierarchy
- `--color-text-primary`: Main text (87% opacity)
- `--color-text-secondary`: Secondary text (60% opacity)
- `--color-text-tertiary`: Labels and hints (40% opacity)
- `--color-text-disabled`: Disabled state (25% opacity)
#### Accent
- `--color-accent-primary`: Primary brand color (#646cff)
- `--color-accent-primary-hover`: Hover state
- `--color-accent-primary-subtle`: Transparent accent for backgrounds
#### Semantic
- `--color-success`: Success states (#10b981)
- `--color-warning`: Warning states (#fbbf24)
- `--color-error`: Error states (#ef4444)
- `--color-info`: Informational elements (#60a5fa)
#### Borders
- `--color-border-subtle`: Very subtle borders (6% opacity)
- `--color-border-default`: Standard borders (10% opacity)
- `--color-border-strong`: Emphasized borders (20% opacity)
#### Interactive States
- `--color-hover-overlay`: Hover effect
- `--color-active-overlay`: Active/pressed effect
- `--color-selected-overlay`: Selection background
### Spacing
Uses a consistent scale from `--space-xs` (0.25rem) to `--space-2xl` (2rem):
```
xs: 0.25rem
sm: 0.5rem
md: 0.75rem
lg: 1rem
xl: 1.5rem
2xl: 2rem
```
### Typography
#### Font Sizes
- `--font-xs` through `--font-lg` for consistent sizing
- Base size is `--font-base: 0.875rem`
#### Font Families
- `--font-mono`: Monospace font for code
- `--font-sans`: System UI font
#### Line Heights
- `--leading-tight`: 1.4
- `--leading-normal`: 1.5
- `--leading-relaxed`: 1.6
### Visual Effects
- `--radius-sm/md/lg`: Border radius values
- `--shadow-sm/md/lg`: Box shadow presets
- `--transition-fast/base/slow`: Animation durations
### Component-Specific
Specialized tokens for specific use cases:
- `--accordion-*`: Accordion styling
- `--input-*`: Form inputs
- `--tooltip-*`: Tooltip components
- `--code-*`: Code blocks
## Design Principles
### Color Usage
1. **Use accent colors sparingly**: Primary accent (#646cff) for:
- Interactive elements
- Primary actions
- Highlighted content (e.g., opcode names)
2. **Semantic colors for meaning**:
- Blue (`--color-info`) for informational data (e.g., rates)
- Green (`--color-success`) for success states
- Yellow (`--color-warning`) for warnings
- Red (`--color-error`) for errors
3. **Text hierarchy through opacity**:
- Use `--color-text-*` variables instead of custom opacity values
- Maintains consistency and improves readability
### Spacing
- Use the spacing scale consistently
- Avoid magic numbers - use defined spacing tokens
- Vertical rhythm: prefer consistent spacing between elements
### Borders
- Most borders should use `--color-border-subtle` for minimal visual weight
- Use `--color-border-default` for more emphasis
- Reserve `--color-border-strong` for important separators
## Adding a New Theme
To add a new theme (e.g., light mode):
1. Create a new CSS file (e.g., `theme-light.css`)
2. Override the root variables:
```css
:root {
--color-bg-base: #ffffff;
--color-text-primary: rgba(0, 0, 0, 0.87);
/* ... other overrides */
}
```
3. Conditionally import based on theme preference
4. All components automatically adapt to the new values
## Migration Guide
When updating existing components to use the theme system:
### Before
```css
.component {
background-color: #1a1a1a;
color: rgba(255, 255, 255, 0.87);
padding: 0.75rem;
border: 1px solid #3a3a3a;
}
```
### After
```css
.component {
background-color: var(--color-bg-base);
color: var(--color-text-primary);
padding: var(--space-md);
border: 1px solid var(--color-border-default);
}
```
## Benefits
1. **Consistency**: Single source of truth for design tokens
2. **Maintainability**: Change once, apply everywhere
3. **Theme switching**: Easy to add light/dark mode or custom themes
4. **Readability**: Semantic names instead of magic values
5. **Scalability**: Add new tokens without touching components

View File

@ -2,9 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>oldboy</title> <meta name="description" content="Online Csound Editor - Write, compile, and perform Csound code in your browser" />
<title>Online Csound Editor</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@ -38,6 +38,7 @@
"@codemirror/view": "^6.38.6", "@codemirror/view": "^6.38.6",
"@csound/browser": "7.0.0-beta11", "@csound/browser": "7.0.0-beta11",
"@hlolli/codemirror-lang-csound": "1.0.0-alpha10", "@hlolli/codemirror-lang-csound": "1.0.0-alpha10",
"@lezer/highlight": "^1.2.1",
"@replit/codemirror-vim": "^6.3.0", "@replit/codemirror-vim": "^6.3.0",
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"fuse.js": "^7.1.0", "fuse.js": "^7.1.0",

3
pnpm-lock.yaml generated
View File

@ -50,6 +50,9 @@ importers:
'@hlolli/codemirror-lang-csound': '@hlolli/codemirror-lang-csound':
specifier: 1.0.0-alpha10 specifier: 1.0.0-alpha10
version: 1.0.0-alpha10(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(codemirror@6.0.2) version: 1.0.0-alpha10(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(codemirror@6.0.2)
'@lezer/highlight':
specifier: ^1.2.1
version: 1.2.1
'@replit/codemirror-vim': '@replit/codemirror-vim':
specifier: ^6.3.0 specifier: ^6.3.0
version: 6.3.0(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6) version: 6.3.0(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)

14
public/favicon.svg Normal file
View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" fill="#1a1a1a"/>
<path d="M 4 16 Q 6 12, 8 16 T 12 16 T 16 16 T 20 16 T 24 16 T 28 16"
stroke="#646cff"
stroke-width="2"
fill="none"
stroke-linecap="round"/>
<path d="M 4 20 Q 7 14, 10 20 T 16 20 T 22 20 T 28 20"
stroke="#646cff"
stroke-width="1.5"
fill="none"
opacity="0.6"
stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 487 B

View File

@ -237,6 +237,13 @@
} }
} }
$effect(() => {
const theme = $editorSettings.theme;
if (typeof document !== 'undefined') {
document.documentElement.dataset.theme = theme;
}
});
$effect(() => { $effect(() => {
if (uiState.scopePopupVisible || uiState.spectrogramPopupVisible) { if (uiState.scopePopupVisible || uiState.spectrogramPopupVisible) {
analyserNode = csound.getAnalyserNode(); analyserNode = csound.getAnalyserNode();
@ -343,16 +350,16 @@
</button> </button>
{/snippet} {/snippet}
<button <button
onclick={() => uiState.openScope()} onclick={() => uiState.toggleScope()}
class="icon-button" class="icon-button"
title="Open audio scope" title="Toggle audio scope"
> >
<Activity size={18} /> <Activity size={18} />
</button> </button>
<button <button
onclick={() => uiState.openSpectrogram()} onclick={() => uiState.toggleSpectrogram()}
class="icon-button" class="icon-button"
title="Open spectrogram" title="Toggle spectrogram"
> >
<svg <svg
width="18" width="18"
@ -546,20 +553,20 @@
} }
.icon-button { .icon-button {
padding: 0.5rem; padding: var(--space-sm);
background-color: #333; background-color: var(--color-border-default);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
border: 1px solid #555; border: 1px solid #555;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: background-color 0.2s; transition: background-color var(--transition-base);
} }
.icon-button:hover:not(:disabled) { .icon-button:hover:not(:disabled) {
background-color: #444; background-color: #444;
border-color: #646cff; border-color: var(--color-accent-primary);
} }
.icon-button:disabled { .icon-button:disabled {
@ -568,7 +575,7 @@
} }
.icon-button.has-changes { .icon-button.has-changes {
color: #646cff; color: var(--color-accent-primary);
} }
.icon-button.stop-button:not(:disabled) { .icon-button.stop-button:not(:disabled) {
@ -582,18 +589,18 @@
h3 { h3 {
margin-top: 0; margin-top: 0;
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
p { p {
color: rgba(255, 255, 255, 0.7); color: var(--color-text-secondary);
line-height: 1.6; line-height: 1.6;
} }
.share-content { .share-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: var(--space-lg);
} }
.share-url-container { .share-url-container {
@ -602,33 +609,33 @@
.share-url-input { .share-url-input {
width: 100%; width: 100%;
padding: 0.75rem; padding: var(--space-md);
background-color: #2a2a2a; background-color: var(--color-surface-3);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
font-size: 0.875rem; font-size: var(--font-base);
font-family: monospace; font-family: monospace;
outline: none; outline: none;
box-sizing: border-box; box-sizing: border-box;
} }
.share-url-input:focus { .share-url-input:focus {
border-color: #646cff; border-color: var(--color-accent-primary);
} }
.share-instructions { .share-instructions {
font-size: 0.875rem; font-size: var(--font-base);
color: rgba(255, 255, 255, 0.5); color: var(--color-text-tertiary);
margin: 0; margin: 0;
} }
.audio-permission-content { .audio-permission-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: var(--space-lg);
align-items: center; align-items: center;
text-align: center; text-align: center;
padding: 1rem; padding: var(--space-lg);
} }
.audio-permission-content h3 { .audio-permission-content h3 {
@ -642,15 +649,15 @@
} }
.enable-audio-button { .enable-audio-button {
margin-top: 1rem; margin-top: var(--space-lg);
padding: 0.75rem 2rem; padding: var(--space-md) var(--space-2xl);
background-color: #646cff; background-color: var(--color-accent-primary);
color: white; color: white;
border: none; border: none;
font-size: 1rem; font-size: var(--font-lg);
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: background-color 0.2s; transition: background-color var(--transition-base);
} }
.enable-audio-button:hover { .enable-audio-button:hover {
@ -658,6 +665,6 @@
} }
.enable-audio-button:active { .enable-audio-button:active {
background-color: #424ab8; background-color: var(--color-accent-primary-active);
} }
</style> </style>

View File

@ -6,10 +6,9 @@
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search'; import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'; import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
import { foldGutter, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap } from '@codemirror/language'; import { foldGutter, indentOnInput, bracketMatching, foldKeymap } from '@codemirror/language';
import { lintKeymap } from '@codemirror/lint'; import { lintKeymap } from '@codemirror/lint';
import { csoundMode } from '@hlolli/codemirror-lang-csound'; import { csoundMode } from '@hlolli/codemirror-lang-csound';
import { oneDark } from '@codemirror/theme-one-dark';
import { vim } from '@replit/codemirror-vim'; import { vim } from '@replit/codemirror-vim';
import type { EditorSettingsStore } from '../../stores/editorSettings'; import type { EditorSettingsStore } from '../../stores/editorSettings';
import { import {
@ -20,6 +19,7 @@
getDocument getDocument
} from '../../editor/block-eval'; } from '../../editor/block-eval';
import { csoundTooltip, csoundTooltipTheme } from '../../csound-reference/csoundTooltips'; import { csoundTooltip, csoundTooltipTheme } from '../../csound-reference/csoundTooltips';
import { createCodeMirrorTheme } from '../../editor/codemirror-theme';
interface Props { interface Props {
value: string; value: string;
@ -44,6 +44,8 @@
const lineWrappingCompartment = new Compartment(); const lineWrappingCompartment = new Compartment();
const vimCompartment = new Compartment(); const vimCompartment = new Compartment();
const languageCompartment = new Compartment(); const languageCompartment = new Compartment();
const hoverTooltipCompartment = new Compartment();
const themeCompartment = new Compartment();
function handleExecute() { function handleExecute() {
if (!editorView) return; if (!editorView) return;
@ -93,14 +95,12 @@
drawSelection(), drawSelection(),
dropCursor(), dropCursor(),
indentOnInput(), indentOnInput(),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
bracketMatching(), bracketMatching(),
closeBrackets(), closeBrackets(),
autocompletion(), autocompletion(),
rectangularSelection(), rectangularSelection(),
crosshairCursor(), crosshairCursor(),
highlightSelectionMatches(), highlightSelectionMatches(),
csoundTooltip,
csoundTooltipTheme, csoundTooltipTheme,
keymap.of([ keymap.of([
...closeBracketsKeymap, ...closeBracketsKeymap,
@ -122,12 +122,13 @@
extensions: [ extensions: [
...baseExtensions, ...baseExtensions,
languageCompartment.of(csoundMode({ fileType })), languageCompartment.of(csoundMode({ fileType })),
oneDark, themeCompartment.of(createCodeMirrorTheme()),
evaluateKeymap, evaluateKeymap,
flashField(), flashField(),
lineNumbersCompartment.of(initSettings.showLineNumbers ? lineNumbers() : []), lineNumbersCompartment.of(initSettings.showLineNumbers ? lineNumbers() : []),
lineWrappingCompartment.of(initSettings.enableLineWrapping ? EditorView.lineWrapping : []), lineWrappingCompartment.of(initSettings.enableLineWrapping ? EditorView.lineWrapping : []),
vimCompartment.of(initSettings.vimMode ? vim() : []), vimCompartment.of(initSettings.vimMode ? vim() : []),
hoverTooltipCompartment.of(initSettings.enableHoverTooltips ? csoundTooltip : []),
EditorView.updateListener.of((update) => { EditorView.updateListener.of((update) => {
if (update.docChanged && onChange) { if (update.docChanged && onChange) {
onChange(update.state.doc.toString()); onChange(update.state.doc.toString());
@ -152,7 +153,8 @@
effects: [ effects: [
lineNumbersCompartment.reconfigure(settings.showLineNumbers ? lineNumbers() : []), lineNumbersCompartment.reconfigure(settings.showLineNumbers ? lineNumbers() : []),
lineWrappingCompartment.reconfigure(settings.enableLineWrapping ? EditorView.lineWrapping : []), lineWrappingCompartment.reconfigure(settings.enableLineWrapping ? EditorView.lineWrapping : []),
vimCompartment.reconfigure(settings.vimMode ? vim() : []) vimCompartment.reconfigure(settings.vimMode ? vim() : []),
hoverTooltipCompartment.reconfigure(settings.enableHoverTooltips ? csoundTooltip : [])
] ]
}); });
@ -160,6 +162,20 @@
editorView.dom.style.fontFamily = settings.fontFamily; editorView.dom.style.fontFamily = settings.fontFamily;
}); });
$effect(() => {
const theme = $editorSettings.theme;
if (!editorView) return;
// Wait for CSS variables to be recomputed after theme change
requestAnimationFrame(() => {
if (editorView) {
editorView.dispatch({
effects: themeCompartment.reconfigure(createCodeMirrorTheme())
});
}
});
});
$effect(() => { $effect(() => {
if (!editorView) return; if (!editorView) return;

View File

@ -15,6 +15,26 @@
</script> </script>
<div class="editor-settings"> <div class="editor-settings">
<div class="setting">
<span class="label-text">Theme</span>
<div class="theme-toggle">
<button
class="theme-option"
class:active={settings.theme === 'dark'}
onclick={() => updateSetting('theme', 'dark')}
>
Dark
</button>
<button
class="theme-option"
class:active={settings.theme === 'light'}
onclick={() => updateSetting('theme', 'light')}
>
Light
</button>
</div>
</div>
<div class="setting"> <div class="setting">
<label> <label>
<span class="label-text">Font Size: {settings.fontSize}px</span> <span class="label-text">Font Size: {settings.fontSize}px</span>
@ -87,17 +107,26 @@
/> />
<span>Enable line wrapping</span> <span>Enable line wrapping</span>
</label> </label>
<label class="checkbox-label">
<input
type="checkbox"
checked={settings.enableHoverTooltips}
onchange={(e) => updateSetting('enableHoverTooltips', e.currentTarget.checked)}
/>
<span>Enable hover tooltips</span>
</label>
</div> </div>
</div> </div>
<style> <style>
.editor-settings { .editor-settings {
padding: 0.5rem; padding: var(--space-sm);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
.setting { .setting {
margin-bottom: 1rem; margin-bottom: var(--space-lg);
} }
label { label {
@ -106,10 +135,44 @@
.label-text { .label-text {
display: block; display: block;
font-size: 0.75rem; font-size: var(--font-sm);
font-weight: 500; font-weight: 500;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
margin-bottom: 0.25rem; margin-bottom: var(--space-xs);
}
.theme-toggle {
display: flex;
gap: 0;
border: 1px solid var(--color-border-default);
overflow: hidden;
}
.theme-option {
flex: 1;
padding: var(--space-sm) var(--space-lg);
background-color: transparent;
color: var(--color-text-secondary);
border: none;
cursor: pointer;
font-size: var(--font-sm);
font-weight: 500;
transition: all var(--transition-base);
border-right: 1px solid var(--color-border-default);
}
.theme-option:last-child {
border-right: none;
}
.theme-option:hover:not(.active) {
background-color: var(--color-hover-overlay);
color: var(--color-text-primary);
}
.theme-option.active {
background-color: var(--color-accent-primary);
color: white;
} }
input[type="range"] { input[type="range"] {
@ -124,14 +187,14 @@
appearance: none; appearance: none;
width: 12px; width: 12px;
height: 12px; height: 12px;
background: #646cff; background: var(--color-accent-primary);
cursor: pointer; cursor: pointer;
} }
input[type="range"]::-moz-range-thumb { input[type="range"]::-moz-range-thumb {
width: 12px; width: 12px;
height: 12px; height: 12px;
background: #646cff; background: var(--color-accent-primary);
cursor: pointer; cursor: pointer;
border: none; border: none;
} }
@ -141,45 +204,45 @@
} }
input[type="range"]::-webkit-slider-thumb:hover { input[type="range"]::-webkit-slider-thumb:hover {
background: #818cf8; background: var(--color-accent-primary-hover);
} }
input[type="range"]::-moz-range-thumb:hover { input[type="range"]::-moz-range-thumb:hover {
background: #818cf8; background: var(--color-accent-primary-hover);
} }
select { select {
width: 100%; width: 100%;
background-color: #1a1a1a; background-color: var(--color-bg-base);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
border: 1px solid #555; border: 1px solid #555;
padding: 0.4rem; padding: 0.4rem;
font-size: 0.75rem; font-size: var(--font-sm);
cursor: pointer; cursor: pointer;
} }
select:hover { select:hover {
border-color: #646cff; border-color: var(--color-accent-primary);
} }
select:focus { select:focus {
outline: none; outline: none;
border-color: #646cff; border-color: var(--color-accent-primary);
} }
.checkboxes { .checkboxes {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.5rem; gap: var(--space-sm);
} }
.checkbox-label { .checkbox-label {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
font-size: 0.75rem; font-size: var(--font-sm);
font-weight: 500; font-weight: 500;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
cursor: pointer; cursor: pointer;
} }
@ -190,7 +253,7 @@
background-color: #4b5563; background-color: #4b5563;
border: 1px solid #6b7280; border: 1px solid #6b7280;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all var(--transition-base);
} }
input[type="checkbox"]:hover { input[type="checkbox"]:hover {
@ -199,13 +262,13 @@
} }
input[type="checkbox"]:checked { input[type="checkbox"]:checked {
background-color: #646cff; background-color: var(--color-accent-primary);
border-color: #646cff; border-color: var(--color-accent-primary);
} }
input[type="checkbox"]:checked:hover { input[type="checkbox"]:checked:hover {
background-color: #818cf8; background-color: var(--color-accent-primary-hover);
border-color: #818cf8; border-color: var(--color-accent-primary-hover);
} }
input[type="checkbox"]:checked::before { input[type="checkbox"]:checked::before {

View File

@ -113,15 +113,15 @@
.resize-divider { .resize-divider {
height: 4px; height: 4px;
background-color: #2a2a2a; background-color: var(--color-surface-3);
cursor: ns-resize; cursor: ns-resize;
transition: background-color 0.2s; transition: background-color var(--transition-base);
z-index: 10; z-index: 10;
} }
.resize-divider:hover, .resize-divider:hover,
.resize-divider:active { .resize-divider:active {
background-color: #646cff; background-color: var(--color-accent-primary);
} }
.logs-section { .logs-section {

View File

@ -188,86 +188,86 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #1a1a1a; background-color: var(--color-bg-base);
} }
.log-header { .log-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.5rem 0.75rem; padding: var(--space-sm) var(--space-md);
background-color: #2a2a2a; background-color: var(--color-surface-3);
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid var(--color-border-default);
cursor: pointer; cursor: pointer;
transition: background-color 0.2s; transition: background-color var(--transition-base);
} }
.log-header:hover { .log-header:hover {
background-color: #323232; background-color: var(--color-hover-overlay);
} }
.search-bar { .search-bar {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
padding: 0.5rem 0.75rem; padding: var(--space-sm) var(--space-md);
background-color: #252525; background-color: var(--color-surface-2);
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid var(--color-border-default);
} }
.search-bar :global(.search-icon) { .search-bar :global(.search-icon) {
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
flex-shrink: 0; flex-shrink: 0;
} }
.search-input { .search-input {
flex: 1; flex: 1;
background-color: #1a1a1a; background-color: var(--color-bg-base);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
padding: 0.375rem 0.5rem; padding: 0.375rem var(--space-sm);
font-size: 0.875rem; font-size: var(--font-base);
font-family: 'Courier New', monospace; font-family: var(--font-mono);
outline: none; outline: none;
transition: border-color 0.2s; transition: border-color var(--transition-base);
} }
.search-input:focus { .search-input:focus {
border-color: rgba(255, 255, 255, 0.4); border-color: var(--color-text-tertiary);
} }
.search-input::placeholder { .search-input::placeholder {
color: rgba(255, 255, 255, 0.3); color: var(--color-text-disabled);
} }
.log-title { .log-title {
font-size: 0.75rem; font-size: var(--font-sm);
font-weight: 600; font-weight: 600;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
} }
.log-actions { .log-actions {
display: flex; display: flex;
gap: 0.25rem; gap: var(--space-xs);
} }
.action-button { .action-button {
padding: 0.25rem; padding: var(--space-xs);
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
border: none; border: none;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: all 0.2s; transition: all var(--transition-base);
} }
.action-button:hover:not(:disabled) { .action-button:hover:not(:disabled) {
color: rgba(255, 255, 255, 0.9); color: var(--color-text-primary);
background-color: rgba(255, 255, 255, 0.1); background-color: var(--color-active-overlay);
} }
.action-button:disabled { .action-button:disabled {
@ -298,27 +298,27 @@
.log-content { .log-content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 0.5rem; padding: var(--space-sm);
font-family: 'Courier New', monospace; font-family: var(--font-mono);
font-size: 0.875rem; font-size: var(--font-base);
} }
.empty-state { .empty-state {
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
text-align: center; text-align: center;
padding: 2rem; padding: var(--space-2xl);
} }
.log-entry { .log-entry {
display: flex; display: flex;
gap: 0.75rem; gap: var(--space-md);
padding: 0.25rem 0.5rem; padding: var(--space-xs) var(--space-sm);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
border-bottom: 1px solid #2a2a2a; border-bottom: 1px solid var(--color-surface-3);
} }
.log-entry:hover { .log-entry:hover {
background-color: #252525; background-color: var(--color-surface-2);
} }
.log-entry.error { .log-entry.error {
@ -331,9 +331,9 @@
} }
.log-timestamp { .log-timestamp {
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
min-width: 6rem; min-width: 6rem;
font-size: 0.75rem; font-size: var(--font-sm);
} }
.log-message { .log-message {

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Search, ChevronDown, ChevronRight } from 'lucide-svelte'; import { Search, ChevronDown, ChevronRight, ChevronsDown } from 'lucide-svelte';
import { allCsoundReferences, getCsoundReferencesByCategory } from '../../csound-reference/csoundReference'; import { allCsoundReferences, getCsoundReferencesByCategory } from '../../csound-reference/csoundReference';
import type { CsoundReference } from '../../csound-reference/types'; import type { CsoundReference } from '../../csound-reference/types';
import Fuse from 'fuse.js'; import Fuse from 'fuse.js';
@ -57,6 +57,10 @@
expandedCategories = newSet; expandedCategories = newSet;
} }
function collapseAll() {
expandedCategories = new Set();
}
$effect(() => { $effect(() => {
if (searchTerm.trim() && filteredCategories().length > 0 && filteredCategories().length <= 3) { if (searchTerm.trim() && filteredCategories().length > 0 && filteredCategories().length <= 3) {
expandedCategories = new Set(filteredCategories()); expandedCategories = new Set(filteredCategories());
@ -75,6 +79,15 @@
bind:value={searchTerm} bind:value={searchTerm}
class="search-input" class="search-input"
/> />
{#if expandedCategories.size > 0}
<button
class="collapse-button"
onclick={collapseAll}
title="Collapse all categories"
>
<ChevronsDown size={16} />
</button>
{/if}
</div> </div>
<div class="reference-content"> <div class="reference-content">
@ -117,22 +130,15 @@
<div class="params-label"> <div class="params-label">
Parameters ({opcode.parameters.length}): Parameters ({opcode.parameters.length}):
</div> </div>
{#each opcode.parameters.slice(0, 5) as param} {#each opcode.parameters as param}
<div class="param"> <div class="param">
<span class="param-name">{param.name}</span> <div class="param-header">
<span class="param-type">({param.type})</span> <span class="param-name">{param.name}</span>
<span class="param-desc"> <span class="param-type">{param.type}</span>
{param.description.length > 80 </div>
? param.description.substring(0, 80) + '...' <div class="param-desc">{param.description}</div>
: param.description}
</span>
</div> </div>
{/each} {/each}
{#if opcode.parameters.length > 5}
<div class="param-more">
... and {opcode.parameters.length - 5} more
</div>
{/if}
</div> </div>
{/if} {/if}
</div> </div>
@ -164,67 +170,91 @@
.search-bar { .search-bar {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
padding: 0.75rem; padding: var(--space-md);
background-color: #252525; background-color: var(--color-surface-2);
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid var(--color-border-subtle);
} }
.search-bar :global(.search-icon) { .search-bar :global(.search-icon) {
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
flex-shrink: 0; flex-shrink: 0;
} }
.search-input { .search-input {
flex: 1; flex: 1;
background-color: #1a1a1a; background-color: var(--input-bg);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
border: 1px solid #3a3a3a; border: 1px solid var(--input-border);
padding: 0.5rem; padding: var(--space-sm);
font-size: 0.875rem; font-size: var(--font-base);
font-family: 'Courier New', monospace; font-family: var(--font-mono);
outline: none; outline: none;
transition: border-color 0.2s; transition: border-color var(--transition-base);
} }
.search-input:focus { .search-input:focus {
border-color: #646cff; border-color: var(--input-border-focus);
} }
.search-input::placeholder { .search-input::placeholder {
color: rgba(255, 255, 255, 0.3); color: var(--color-text-disabled);
}
.collapse-button {
padding: var(--space-sm);
background-color: transparent;
color: var(--color-text-secondary);
border: 1px solid var(--color-border-default);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-base);
flex-shrink: 0;
}
.collapse-button:hover {
color: var(--color-text-primary);
background-color: var(--color-hover-overlay);
border-color: var(--color-border-strong);
} }
.reference-content { .reference-content {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 0.5rem; padding: 0;
} }
.category { .category {
margin-bottom: 0.75rem; margin-bottom: 0;
border-bottom: 1px solid var(--accordion-border);
}
.category:last-child {
border-bottom: none;
} }
.category-header { .category-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
width: 100%; width: 100%;
padding: 0.5rem; padding: var(--space-md) var(--space-lg);
background-color: #2a2a2a; background-color: var(--accordion-header-bg);
border: none; border: none;
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
cursor: pointer; cursor: pointer;
transition: background-color 0.2s; transition: background-color var(--transition-base);
font-size: 0.875rem; font-size: var(--font-base);
} }
.category-header:hover { .category-header:hover {
background-color: #323232; background-color: var(--accordion-header-hover);
} }
.category-header :global(svg) { .category-header :global(svg) {
color: rgba(255, 255, 255, 0.6); color: var(--color-text-tertiary);
flex-shrink: 0; flex-shrink: 0;
} }
@ -235,112 +265,117 @@
} }
.category-count { .category-count {
font-size: 0.75rem; font-size: var(--font-xs);
color: rgba(255, 255, 255, 0.5); color: var(--color-text-primary);
background-color: #646cff; background-color: var(--color-accent-primary-subtle);
padding: 0.125rem 0.5rem; padding: 2px var(--space-sm);
border-radius: var(--radius-sm);
} }
.opcodes { .opcodes {
padding-left: 1.5rem; padding: var(--space-lg);
margin-top: 0.5rem;
} }
.opcode { .opcode {
background-color: #1a1a1a; background-color: transparent;
padding: 0.75rem; padding: var(--space-lg) 0;
margin-bottom: 0.5rem; margin-bottom: 0;
border-left: 2px solid #646cff; border-bottom: 1px solid var(--color-border-subtle);
}
.opcode:last-child {
border-bottom: none;
} }
.opcode-header { .opcode-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
margin-bottom: 0.5rem; margin-bottom: var(--space-sm);
} }
.opcode-name { .opcode-name {
font-family: 'Courier New', monospace; font-family: var(--font-mono);
font-size: 0.875rem; font-size: var(--font-base);
font-weight: 600; font-weight: 600;
color: #646cff; color: var(--color-accent-primary);
} }
.opcode-type { .opcode-type {
font-size: 0.75rem; font-size: var(--font-xs);
color: rgba(255, 255, 255, 0.5); color: var(--color-text-tertiary);
text-transform: uppercase; text-transform: lowercase;
} }
.opcode-description { .opcode-description {
color: rgba(255, 255, 255, 0.87); color: var(--color-text-secondary);
font-size: 0.8125rem; font-size: var(--font-md);
line-height: 1.5; line-height: var(--leading-relaxed);
margin: 0 0 0.5rem 0; margin: 0 0 var(--space-md) 0;
} }
.opcode-syntax { .opcode-syntax {
background-color: #000; background-color: var(--color-surface-2);
color: #fbbf24; color: var(--color-text-primary);
padding: 0.5rem; padding: var(--space-md);
font-size: 0.75rem; font-size: var(--font-sm);
font-family: 'Courier New', monospace; font-family: var(--font-mono);
margin: 0 0 0.5rem 0; margin: 0 0 var(--space-md) 0;
overflow-x: auto; overflow-x: auto;
white-space: pre-wrap; white-space: pre;
word-break: break-all; border: 1px solid var(--color-border-subtle);
border: 1px solid #2a2a2a; line-height: var(--leading-normal);
} }
.opcode-rates { .opcode-rates {
font-size: 0.75rem; font-size: var(--font-sm);
color: #60a5fa; color: var(--color-info);
margin-bottom: 0.5rem; margin-bottom: var(--space-md);
} }
.opcode-params { .opcode-params {
margin-top: 0.5rem; margin-top: var(--space-md);
} }
.params-label { .params-label {
font-size: 0.75rem; font-size: var(--font-xs);
color: rgba(255, 255, 255, 0.6); color: var(--color-text-tertiary);
margin-bottom: 0.375rem; margin-bottom: var(--space-sm);
text-transform: uppercase; text-transform: lowercase;
font-weight: 500; font-weight: 500;
} }
.param { .param {
display: flex; display: flex;
gap: 0.375rem; flex-direction: column;
font-size: 0.75rem; gap: var(--space-xs);
line-height: 1.4; font-size: var(--font-sm);
margin-bottom: 0.25rem; line-height: var(--leading-normal);
margin-bottom: var(--space-md);
}
.param-header {
display: flex;
align-items: center;
gap: var(--space-sm);
} }
.param-name { .param-name {
color: #10b981; color: var(--color-text-primary);
font-family: 'Courier New', monospace; font-family: var(--font-mono);
font-weight: 500; font-weight: 500;
min-width: 4rem; font-size: var(--font-sm);
} }
.param-type { .param-type {
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
font-size: 0.6875rem; font-size: var(--font-xs);
} }
.param-desc { .param-desc {
color: rgba(255, 255, 255, 0.7); color: var(--color-text-secondary);
flex: 1; font-size: var(--font-sm);
} line-height: var(--leading-normal);
.param-more {
font-size: 0.6875rem;
color: rgba(255, 255, 255, 0.5);
font-style: italic;
margin-top: 0.25rem;
} }
.empty-state { .empty-state {
@ -348,26 +383,26 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 3rem 1rem; padding: var(--space-2xl) var(--space-lg);
text-align: center; text-align: center;
color: rgba(255, 255, 255, 0.5); color: var(--color-text-tertiary);
} }
.empty-state :global(svg) { .empty-state :global(svg) {
margin-bottom: 1rem; margin-bottom: var(--space-lg);
opacity: 0.3; opacity: 0.3;
} }
.empty-state h3 { .empty-state h3 {
font-size: 1rem; font-size: var(--font-lg);
font-weight: 500; font-weight: 500;
margin: 0 0 0.5rem 0; margin: 0 0 var(--space-sm) 0;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
} }
.empty-state p { .empty-state p {
font-size: 0.875rem; font-size: var(--font-base);
margin: 0; margin: 0;
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
} }
</style> </style>

View File

@ -48,42 +48,42 @@
<style> <style>
.confirm-dialog p { .confirm-dialog p {
margin: 0 0 1.5rem 0; margin: 0 0 var(--space-xl) 0;
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
line-height: 1.5; line-height: 1.5;
} }
.button-group { .button-group {
display: flex; display: flex;
gap: 0.75rem; gap: var(--space-md);
justify-content: flex-end; justify-content: flex-end;
} }
.button { .button {
padding: 0.5rem 1rem; padding: var(--space-sm) var(--space-lg);
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 0.875rem; font-size: var(--font-base);
transition: all 0.2s; transition: all var(--transition-base);
} }
.button-primary { .button-primary {
background-color: #646cff; background-color: var(--color-accent-primary);
color: white; color: white;
} }
.button-primary:hover { .button-primary:hover {
background-color: #535bf2; background-color: var(--color-accent-primary-active);
} }
.button-secondary { .button-secondary {
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
} }
.button-secondary:hover { .button-secondary:hover {
background-color: rgba(255, 255, 255, 0.05); background-color: var(--color-hover-overlay);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
</style> </style>

View File

@ -235,46 +235,46 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: #1a1a1a; background-color: var(--color-bg-base);
} }
.browser-header { .browser-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.75rem; padding: var(--space-md);
background-color: #2a2a2a; background-color: var(--color-surface-3);
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid var(--color-border-default);
} }
.browser-title { .browser-title {
font-size: 0.75rem; font-size: var(--font-sm);
font-weight: 600; font-weight: 600;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
} }
.header-actions { .header-actions {
display: flex; display: flex;
gap: 0.25rem; gap: var(--space-xs);
} }
.action-button { .action-button {
padding: 0.375rem; padding: 0.375rem;
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 0.7); color: var(--color-text-secondary);
border: none; border: none;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: all 0.2s; transition: all var(--transition-base);
} }
.action-button:hover { .action-button:hover {
color: rgba(255, 255, 255, 1); color: var(--color-text-primary);
background-color: rgba(255, 255, 255, 0.1); background-color: var(--color-active-overlay);
} }
.browser-content { .browser-content {
@ -283,9 +283,9 @@
} }
.empty-state { .empty-state {
padding: 2rem 1rem; padding: var(--space-2xl) var(--space-lg);
text-align: center; text-align: center;
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
} }
.project-list { .project-list {
@ -296,24 +296,24 @@
.project-item { .project-item {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.75rem; gap: var(--space-md);
padding: 0.75rem; padding: var(--space-md);
border-bottom: 1px solid #2a2a2a; border-bottom: 1px solid var(--color-surface-3);
cursor: pointer; cursor: pointer;
transition: background-color 0.2s; transition: background-color var(--transition-base);
} }
.project-item:hover { .project-item:hover {
background-color: #252525; background-color: var(--color-surface-2);
} }
.project-item.selected { .project-item.selected {
background-color: rgba(100, 108, 255, 0.2); background-color: rgba(100, 108, 255, 0.2);
border-left: 3px solid #646cff; border-left: 3px solid var(--color-accent-primary);
} }
.project-icon { .project-icon {
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
display: flex; display: flex;
align-items: center; align-items: center;
} }
@ -324,24 +324,24 @@
} }
.project-title { .project-title {
font-size: 0.875rem; font-size: var(--font-base);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.delete-button { .delete-button {
padding: 0.25rem; padding: var(--space-xs);
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 0.4); color: var(--color-text-tertiary);
border: none; border: none;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
opacity: 0; opacity: 0;
transition: all 0.2s; transition: all var(--transition-base);
} }
.project-item:hover .delete-button { .project-item:hover .delete-button {
@ -354,50 +354,50 @@
} }
.metadata-editor { .metadata-editor {
border-top: 1px solid #2a2a2a; border-top: 1px solid var(--color-surface-3);
background-color: #1a1a1a; background-color: var(--color-bg-base);
padding: 1rem; padding: var(--space-lg);
} }
.metadata-header { .metadata-header {
font-size: 0.75rem; font-size: var(--font-sm);
font-weight: 600; font-weight: 600;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
margin-bottom: 1rem; margin-bottom: var(--space-lg);
} }
.metadata-fields { .metadata-fields {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: var(--space-md);
} }
.field { .field {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.25rem; gap: var(--space-xs);
} }
.field label { .field label {
font-size: 0.75rem; font-size: var(--font-sm);
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
} }
.field input, .field input,
.field select { .field select {
padding: 0.5rem; padding: var(--space-sm);
background-color: #2a2a2a; background-color: var(--color-surface-3);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
font-size: 0.875rem; font-size: var(--font-base);
outline: none; outline: none;
} }
.field input:focus, .field input:focus,
.field select:focus { .field select:focus {
border-color: #646cff; border-color: var(--color-accent-primary);
} }
.field select { .field select {
@ -405,11 +405,11 @@
} }
.field.readonly .readonly-value { .field.readonly .readonly-value {
padding: 0.5rem; padding: var(--space-sm);
background-color: #1a1a1a; background-color: var(--color-bg-base);
border: 1px solid #2a2a2a; border: 1px solid var(--color-surface-3);
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
font-size: 0.875rem; font-size: var(--font-base);
font-family: monospace; font-family: monospace;
} }
</style> </style>

View File

@ -64,58 +64,58 @@
.input-dialog { .input-dialog {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: var(--space-lg);
} }
label { label {
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
font-size: 0.875rem; font-size: var(--font-base);
} }
input { input {
padding: 0.5rem; padding: var(--space-sm);
background-color: #1a1a1a; background-color: var(--color-bg-base);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
font-size: 0.875rem; font-size: var(--font-base);
outline: none; outline: none;
} }
input:focus { input:focus {
border-color: #646cff; border-color: var(--color-accent-primary);
} }
.button-group { .button-group {
display: flex; display: flex;
gap: 0.75rem; gap: var(--space-md);
justify-content: flex-end; justify-content: flex-end;
} }
.button { .button {
padding: 0.5rem 1rem; padding: var(--space-sm) var(--space-lg);
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 0.875rem; font-size: var(--font-base);
transition: all 0.2s; transition: all var(--transition-base);
} }
.button-primary { .button-primary {
background-color: #646cff; background-color: var(--color-accent-primary);
color: white; color: white;
} }
.button-primary:hover { .button-primary:hover {
background-color: #535bf2; background-color: var(--color-accent-primary-active);
} }
.button-secondary { .button-secondary {
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
} }
.button-secondary:hover { .button-secondary:hover {
background-color: rgba(255, 255, 255, 0.05); background-color: var(--color-hover-overlay);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
</style> </style>

View File

@ -52,8 +52,8 @@
} }
.modal-content { .modal-content {
background-color: #2a2a2a; background-color: var(--color-surface-3);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
min-width: 300px; min-width: 300px;
max-width: 500px; max-width: 500px;
max-height: 80vh; max-height: 80vh;
@ -61,18 +61,18 @@
} }
.modal-header { .modal-header {
padding: 1rem 1.5rem; padding: var(--space-lg) var(--space-xl);
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid var(--color-border-default);
} }
.modal-header h3 { .modal-header h3 {
margin: 0; margin: 0;
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
font-size: 1rem; font-size: var(--font-lg);
font-weight: 600; font-weight: 600;
} }
.modal-body { .modal-body {
padding: 1.5rem; padding: var(--space-xl);
} }
</style> </style>

View File

@ -83,8 +83,8 @@
.popup { .popup {
position: fixed; position: fixed;
z-index: 9999; z-index: 9999;
background-color: #1a1a1a; background-color: var(--color-bg-base);
border: 1px solid #333; border: 1px solid var(--color-border-default);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -94,23 +94,23 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.75rem 1rem; padding: var(--space-md) var(--space-lg);
background-color: #252525; background-color: var(--color-surface-2);
border-bottom: 1px solid #333; border-bottom: 1px solid var(--color-border-default);
cursor: move; cursor: move;
user-select: none; user-select: none;
} }
.popup-title { .popup-title {
font-weight: 600; font-weight: 600;
font-size: 0.875rem; font-size: var(--font-base);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
.close-button { .close-button {
background: none; background: none;
border: none; border: none;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1; line-height: 1;
padding: 0; padding: 0;
@ -120,16 +120,16 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: color 0.2s; transition: color var(--transition-base);
} }
.close-button:hover { .close-button:hover {
color: rgba(255, 255, 255, 1); color: var(--color-text-primary);
} }
.popup-content { .popup-content {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
padding: 1rem; padding: var(--space-lg);
} }
</style> </style>

View File

@ -148,8 +148,8 @@
.resizable-popup { .resizable-popup {
position: fixed; position: fixed;
z-index: 9999; z-index: 9999;
background-color: #1a1a1a; background-color: var(--color-bg-base);
border: 1px solid #333; border: 1px solid var(--color-border-default);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -159,23 +159,23 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.75rem 1rem; padding: var(--space-md) var(--space-lg);
background-color: #252525; background-color: var(--color-surface-2);
border-bottom: 1px solid #333; border-bottom: 1px solid var(--color-border-default);
cursor: move; cursor: move;
user-select: none; user-select: none;
} }
.popup-title { .popup-title {
font-weight: 600; font-weight: 600;
font-size: 0.875rem; font-size: var(--font-base);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
.close-button { .close-button {
background: none; background: none;
border: none; border: none;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1; line-height: 1;
padding: 0; padding: 0;
@ -185,17 +185,17 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: color 0.2s; transition: color var(--transition-base);
} }
.close-button:hover { .close-button:hover {
color: rgba(255, 255, 255, 1); color: var(--color-text-primary);
} }
.popup-content { .popup-content {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
padding: 1rem; padding: var(--space-lg);
} }
.popup-content.no-padding { .popup-content.no-padding {

View File

@ -166,7 +166,7 @@
<style> <style>
.side-panel { .side-panel {
position: relative; position: relative;
background-color: #1a1a1a; background-color: var(--color-bg-base);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@ -177,22 +177,22 @@
} }
.side-panel.left { .side-panel.left {
border-right: 1px solid #333; border-right: 1px solid var(--color-border-default);
} }
.side-panel.right { .side-panel.right {
border-left: 1px solid #333; border-left: 1px solid var(--color-border-default);
} }
.side-panel.bottom { .side-panel.bottom {
width: 100%; width: 100%;
border-top: 1px solid #333; border-top: 1px solid var(--color-border-default);
} }
.resize-handle { .resize-handle {
position: absolute; position: absolute;
background-color: transparent; background-color: transparent;
transition: background-color 0.2s; transition: background-color var(--transition-base);
z-index: 10; z-index: 10;
} }
@ -222,75 +222,75 @@
.resize-handle:hover, .resize-handle:hover,
.resize-handle:active { .resize-handle:active {
background-color: #646cff; background-color: var(--color-accent-primary);
} }
.tabs { .tabs {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
border-bottom: 1px solid #333; border-bottom: 1px solid var(--color-border-default);
background-color: #1a1a1a; background-color: var(--color-surface-1);
} }
.panel-controls { .panel-controls {
display: flex; display: flex;
gap: 0.25rem; gap: var(--space-xs);
margin-left: auto; margin-left: auto;
padding-right: 0.5rem; padding-right: var(--space-sm);
} }
.control-button { .control-button {
padding: 0.375rem; padding: var(--space-sm);
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 0.7); color: var(--color-text-secondary);
border: none; border: none;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: all 0.2s; transition: all var(--transition-base);
} }
.control-button:hover { .control-button:hover {
color: rgba(255, 255, 255, 1); color: var(--color-text-primary);
background-color: rgba(255, 255, 255, 0.1); background-color: var(--color-hover-overlay);
} }
.panel-header-fallback { .panel-header-fallback {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
border-bottom: 1px solid #333; border-bottom: 1px solid var(--color-border-default);
background-color: #1a1a1a; background-color: var(--color-surface-1);
padding: 0.5rem 0; padding: var(--space-sm) 0;
} }
.tab { .tab {
padding: 0.75rem 1rem; padding: var(--space-md) var(--space-lg);
background: none; background: none;
border: none; border: none;
color: rgba(255, 255, 255, 0.6); color: var(--color-text-secondary);
cursor: pointer; cursor: pointer;
font-size: 0.875rem; font-size: var(--font-base);
font-weight: 500; font-weight: 500;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
transition: color 0.2s, border-color 0.2s; transition: color var(--transition-base), border-color var(--transition-base);
} }
.tab:hover { .tab:hover {
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
.tab.active { .tab.active {
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
border-bottom-color: #646cff; border-bottom-color: var(--color-accent-primary);
} }
.side-panel-content { .side-panel-content {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
padding: 1rem; padding: 0;
} }
.tab-content { .tab-content {

View File

@ -59,8 +59,8 @@
} }
.modal-content { .modal-content {
background-color: #1a1a1a; background-color: var(--color-bg-base);
border: 1px solid #3a3a3a; border: 1px solid var(--color-border-default);
width: 400px; width: 400px;
max-height: 80vh; max-height: 80vh;
display: flex; display: flex;
@ -71,22 +71,22 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 1rem; padding: var(--space-lg);
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid var(--color-border-default);
} }
h2 { h2 {
margin: 0; margin: 0;
font-size: 1rem; font-size: var(--font-lg);
font-weight: 600; font-weight: 600;
color: rgba(255, 255, 255, 0.9); color: var(--color-text-primary);
} }
.close-button { .close-button {
background: none; background: none;
border: none; border: none;
font-size: 2rem; font-size: 2rem;
color: rgba(255, 255, 255, 0.5); color: var(--color-text-secondary);
cursor: pointer; cursor: pointer;
padding: 0; padding: 0;
width: 2rem; width: 2rem;
@ -94,12 +94,12 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: color 0.2s; transition: color var(--transition-base);
line-height: 1; line-height: 1;
} }
.close-button:hover { .close-button:hover {
color: rgba(255, 255, 255, 0.9); color: var(--color-text-primary);
} }
.template-list { .template-list {
@ -108,19 +108,19 @@
} }
.template-item { .template-item {
padding: 1rem; padding: var(--space-lg);
background-color: transparent; background-color: transparent;
border: none; border: none;
border-bottom: 1px solid #2a2a2a; border-bottom: 1px solid var(--color-surface-3);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
transition: background-color 0.2s; transition: background-color var(--transition-base);
font-size: 0.875rem; font-size: var(--font-base);
} }
.template-item:hover { .template-item:hover {
background-color: #2a2a2a; background-color: var(--color-surface-3);
} }
.template-item:last-child { .template-item:last-child {

View File

@ -28,9 +28,9 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.75rem 1rem; padding: var(--space-md) var(--space-lg);
background-color: #1a1a1a; background-color: var(--color-bg-base);
border-bottom: 1px solid #333; border-bottom: 1px solid var(--color-border-default);
height: 48px; height: 48px;
box-sizing: border-box; box-sizing: border-box;
} }
@ -38,24 +38,24 @@
.left-section { .left-section {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
} }
.title-section { .title-section {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: var(--space-sm);
color: rgba(255, 255, 255, 0.87); color: var(--color-text-primary);
} }
.title { .title {
font-weight: 600; font-weight: 600;
font-size: 1rem; font-size: var(--font-lg);
} }
.actions { .actions {
display: flex; display: flex;
gap: 0.5rem; gap: var(--space-sm);
align-items: center; align-items: center;
} }
</style> </style>

View File

@ -90,32 +90,33 @@ export const csoundTooltip = hoverTooltip((view, pos) => {
const paramsList = document.createElement('div'); const paramsList = document.createElement('div');
paramsList.className = 'csound-tooltip-params'; paramsList.className = 'csound-tooltip-params';
reference.parameters.slice(0, 5).forEach(param => { reference.parameters.forEach(param => {
const paramItem = document.createElement('div'); const paramItem = document.createElement('div');
paramItem.className = 'csound-tooltip-param'; paramItem.className = 'csound-tooltip-param';
const paramHeader = document.createElement('div');
paramHeader.className = 'csound-tooltip-param-header';
const paramName = document.createElement('span'); const paramName = document.createElement('span');
paramName.className = 'csound-tooltip-param-name'; paramName.className = 'csound-tooltip-param-name';
paramName.textContent = param.name; paramName.textContent = param.name;
const paramDesc = document.createElement('span'); const paramType = document.createElement('span');
paramDesc.className = 'csound-tooltip-param-desc'; paramType.className = 'csound-tooltip-param-type';
paramDesc.textContent = param.description.length > 60 paramType.textContent = param.type;
? param.description.substring(0, 60) + '...'
: param.description;
paramItem.appendChild(paramName); paramHeader.appendChild(paramName);
paramHeader.appendChild(paramType);
const paramDesc = document.createElement('div');
paramDesc.className = 'csound-tooltip-param-desc';
paramDesc.textContent = param.description;
paramItem.appendChild(paramHeader);
paramItem.appendChild(paramDesc); paramItem.appendChild(paramDesc);
paramsList.appendChild(paramItem); paramsList.appendChild(paramItem);
}); });
if (reference.parameters.length > 5) {
const more = document.createElement('div');
more.className = 'csound-tooltip-more';
more.textContent = `... and ${reference.parameters.length - 5} more`;
paramsList.appendChild(more);
}
dom.appendChild(paramsList); dom.appendChild(paramsList);
} }
@ -126,114 +127,126 @@ export const csoundTooltip = hoverTooltip((view, pos) => {
export const csoundTooltipTheme = EditorView.theme({ export const csoundTooltipTheme = EditorView.theme({
'.cm-tooltip.cm-tooltip-hover': { '.cm-tooltip.cm-tooltip-hover': {
backgroundColor: 'rgba(0, 0, 0, 0.95)', backgroundColor: 'var(--tooltip-bg)',
backdropFilter: 'blur(8px)', backdropFilter: 'blur(8px)',
color: '#ffffff', color: 'var(--color-text-primary)',
fontSize: '12px', fontSize: 'var(--font-sm)',
fontFamily: 'monospace', fontFamily: 'var(--font-mono)',
padding: '0', padding: '0',
maxWidth: '500px', maxWidth: '500px',
border: '1px solid #3a3a3a', maxHeight: '400px',
overflowY: 'auto',
border: '1px solid var(--tooltip-border)',
borderRadius: 'var(--radius-md)',
boxShadow: 'var(--shadow-lg)',
zIndex: '100' zIndex: '100'
}, },
'.csound-tooltip': { '.csound-tooltip': {
padding: '10px 12px' padding: 'var(--space-md) var(--space-lg)'
}, },
'.csound-tooltip-header': { '.csound-tooltip-header': {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '8px', gap: 'var(--space-sm)',
marginBottom: '6px', marginBottom: 'var(--space-sm)',
paddingBottom: '6px', paddingBottom: 'var(--space-sm)',
borderBottom: '1px solid #3a3a3a' borderBottom: '1px solid var(--color-border-subtle)'
}, },
'.csound-tooltip-title': { '.csound-tooltip-title': {
fontWeight: '600', fontWeight: '600',
color: '#646cff', color: 'var(--color-accent-primary)',
fontSize: '14px' fontSize: 'var(--font-md)'
}, },
'.csound-tooltip-type': { '.csound-tooltip-type': {
fontSize: '10px', fontSize: 'var(--font-xs)',
color: '#9ca3af', color: 'var(--color-text-tertiary)',
backgroundColor: 'rgba(255, 255, 255, 0.1)', backgroundColor: 'transparent',
padding: '2px 6px', padding: '0',
textTransform: 'uppercase' textTransform: 'lowercase'
}, },
'.csound-tooltip-description': { '.csound-tooltip-description': {
color: '#e5e7eb', color: 'var(--color-text-secondary)',
lineHeight: '1.5', lineHeight: 'var(--leading-relaxed)',
marginBottom: '8px' marginBottom: 'var(--space-md)',
fontSize: 'var(--font-sm)'
}, },
'.csound-tooltip-syntax-label': { '.csound-tooltip-syntax-label': {
fontSize: '10px', fontSize: 'var(--font-xs)',
color: '#9ca3af', color: 'var(--color-text-tertiary)',
marginTop: '8px', marginTop: 'var(--space-md)',
marginBottom: '4px', marginBottom: 'var(--space-xs)',
textTransform: 'uppercase', textTransform: 'lowercase',
fontWeight: '500' fontWeight: '500'
}, },
'.csound-tooltip-syntax': { '.csound-tooltip-syntax': {
backgroundColor: 'rgba(0, 0, 0, 0.4)', backgroundColor: 'var(--code-bg)',
color: '#fbbf24', color: 'var(--code-text)',
padding: '6px 8px', padding: 'var(--space-sm) 0 var(--space-sm) var(--space-sm)',
fontSize: '11px', fontSize: 'var(--font-sm)',
fontFamily: 'monospace', fontFamily: 'var(--font-mono)',
margin: '0 0 8px 0', margin: '0 0 var(--space-md) 0',
whiteSpace: 'pre-wrap', whiteSpace: 'pre-wrap',
lineHeight: '1.4', lineHeight: 'var(--leading-normal)',
border: '1px solid #2a2a2a' border: 'none',
borderLeft: '2px solid var(--code-border)'
}, },
'.csound-tooltip-rates': { '.csound-tooltip-rates': {
fontSize: '11px', fontSize: 'var(--font-sm)',
color: '#60a5fa', color: 'var(--color-info)',
marginBottom: '8px' marginBottom: 'var(--space-md)'
}, },
'.csound-tooltip-params-label': { '.csound-tooltip-params-label': {
fontSize: '10px', fontSize: 'var(--font-xs)',
color: '#9ca3af', color: 'var(--color-text-tertiary)',
marginTop: '8px', marginTop: 'var(--space-md)',
marginBottom: '4px', marginBottom: 'var(--space-sm)',
textTransform: 'uppercase', textTransform: 'lowercase',
fontWeight: '500' fontWeight: '500'
}, },
'.csound-tooltip-params': { '.csound-tooltip-params': {
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: '4px' gap: 'var(--space-md)'
}, },
'.csound-tooltip-param': { '.csound-tooltip-param': {
display: 'flex', display: 'flex',
gap: '6px', flexDirection: 'column',
fontSize: '11px', gap: 'var(--space-xs)',
lineHeight: '1.4' fontSize: 'var(--font-sm)',
lineHeight: 'var(--leading-normal)'
},
'.csound-tooltip-param-header': {
display: 'flex',
alignItems: 'center',
gap: 'var(--space-sm)'
}, },
'.csound-tooltip-param-name': { '.csound-tooltip-param-name': {
color: '#10b981', color: 'var(--color-text-primary)',
fontWeight: '500', fontWeight: '500',
minWidth: '60px' fontSize: 'var(--font-sm)'
},
'.csound-tooltip-param-type': {
color: 'var(--color-text-tertiary)',
fontSize: 'var(--font-xs)'
}, },
'.csound-tooltip-param-desc': { '.csound-tooltip-param-desc': {
color: '#d1d5db', color: 'var(--color-text-secondary)',
flex: '1' fontSize: 'var(--font-sm)',
}, lineHeight: 'var(--leading-normal)'
'.csound-tooltip-more': {
fontSize: '10px',
color: '#9ca3af',
fontStyle: 'italic',
marginTop: '4px'
} }
}); });

View File

@ -0,0 +1,105 @@
import { EditorView } from '@codemirror/view';
import type { Extension } from '@codemirror/state';
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
import { tags as t } from '@lezer/highlight';
function getCSSVariable(name: string): string {
if (typeof window === 'undefined') return '';
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
export function createCodeMirrorTheme(): Extension {
const editorTheme = EditorView.theme({
'&': {
backgroundColor: getCSSVariable('--color-bg-base'),
color: getCSSVariable('--color-text-primary'),
},
'.cm-content': {
caretColor: getCSSVariable('--color-accent-primary'),
},
'.cm-cursor, .cm-dropCursor': {
borderLeftColor: getCSSVariable('--color-accent-primary'),
},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
backgroundColor: getCSSVariable('--color-selected-overlay'),
},
'.cm-panels': {
backgroundColor: getCSSVariable('--color-surface-1'),
color: getCSSVariable('--color-text-primary'),
},
'.cm-panels.cm-panels-top': {
borderBottom: `1px solid ${getCSSVariable('--color-border-default')}`,
},
'.cm-panels.cm-panels-bottom': {
borderTop: `1px solid ${getCSSVariable('--color-border-default')}`,
},
'.cm-searchMatch': {
backgroundColor: getCSSVariable('--color-warning'),
outline: `1px solid ${getCSSVariable('--color-border-accent')}`,
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: getCSSVariable('--color-accent-primary-subtle'),
},
'.cm-activeLine': {
backgroundColor: getCSSVariable('--color-hover-overlay'),
},
'.cm-selectionMatch': {
backgroundColor: getCSSVariable('--color-selected-overlay'),
},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: getCSSVariable('--color-active-overlay'),
},
'.cm-gutters': {
backgroundColor: getCSSVariable('--color-surface-1'),
color: getCSSVariable('--color-text-tertiary'),
border: 'none',
borderRight: `1px solid ${getCSSVariable('--color-border-subtle')}`,
},
'.cm-activeLineGutter': {
backgroundColor: getCSSVariable('--color-hover-overlay'),
color: getCSSVariable('--color-text-secondary'),
},
'.cm-foldPlaceholder': {
backgroundColor: getCSSVariable('--color-surface-2'),
border: `1px solid ${getCSSVariable('--color-border-default')}`,
color: getCSSVariable('--color-text-secondary'),
},
'.cm-tooltip': {
border: `1px solid ${getCSSVariable('--color-border-default')}`,
backgroundColor: getCSSVariable('--color-tooltip-bg'),
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: getCSSVariable('--color-border-default'),
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: getCSSVariable('--color-tooltip-bg'),
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
backgroundColor: getCSSVariable('--color-selected-overlay'),
color: getCSSVariable('--color-text-primary'),
},
},
}, { dark: getCSSVariable('--color-bg-base').startsWith('#1') || getCSSVariable('--color-bg-base').startsWith('#2') });
const highlightStyle = HighlightStyle.define([
{ tag: t.keyword, color: getCSSVariable('--color-accent-primary') },
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: getCSSVariable('--color-text-primary') },
{ tag: [t.function(t.variableName), t.labelName], color: getCSSVariable('--color-info') },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: getCSSVariable('--color-warning') },
{ tag: [t.definition(t.name), t.separator], color: getCSSVariable('--color-text-primary') },
{ tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: getCSSVariable('--color-warning') },
{ tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: getCSSVariable('--color-accent-primary-hover') },
{ tag: [t.meta, t.comment], color: getCSSVariable('--color-text-tertiary'), fontStyle: 'italic' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.link, color: getCSSVariable('--color-info'), textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: getCSSVariable('--color-accent-primary') },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: getCSSVariable('--color-warning') },
{ tag: [t.processingInstruction, t.string, t.inserted], color: getCSSVariable('--color-success') },
{ tag: t.invalid, color: getCSSVariable('--color-error') },
]);
return [editorTheme, syntaxHighlighting(highlightStyle)];
}

View File

@ -2,6 +2,8 @@ import { writable } from 'svelte/store';
const STORAGE_KEY = 'editorSettings'; const STORAGE_KEY = 'editorSettings';
export type Theme = 'dark' | 'light';
export interface EditorSettings { export interface EditorSettings {
fontSize: number; fontSize: number;
fontFamily: string; fontFamily: string;
@ -9,6 +11,8 @@ export interface EditorSettings {
enableLineWrapping: boolean; enableLineWrapping: boolean;
tabSize: number; tabSize: number;
vimMode: boolean; vimMode: boolean;
enableHoverTooltips: boolean;
theme: Theme;
} }
const defaultSettings: EditorSettings = { const defaultSettings: EditorSettings = {
@ -17,7 +21,9 @@ const defaultSettings: EditorSettings = {
showLineNumbers: true, showLineNumbers: true,
enableLineWrapping: false, enableLineWrapping: false,
tabSize: 2, tabSize: 2,
vimMode: false vimMode: false,
enableHoverTooltips: true,
theme: 'dark'
}; };
export interface EditorSettingsStore { export interface EditorSettingsStore {

View File

@ -30,10 +30,18 @@ export class UIState {
this.scopePopupVisible = true; this.scopePopupVisible = true;
} }
toggleScope() {
this.scopePopupVisible = !this.scopePopupVisible;
}
openSpectrogram() { openSpectrogram() {
this.spectrogramPopupVisible = true; this.spectrogramPopupVisible = true;
} }
toggleSpectrogram() {
this.spectrogramPopupVisible = !this.spectrogramPopupVisible;
}
showShare(url: string) { showShare(url: string) {
this.shareUrl = url; this.shareUrl = url;
this.sharePopupVisible = true; this.sharePopupVisible = true;

View File

@ -1,7 +1,22 @@
import { mount } from 'svelte' import { mount } from 'svelte'
import './theme.css'
import './app.css' import './app.css'
import App from './App.svelte' import App from './App.svelte'
// Apply saved theme before mounting app
const STORAGE_KEY = 'editorSettings';
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
const settings = JSON.parse(stored);
if (settings.theme) {
document.documentElement.dataset.theme = settings.theme;
}
} catch (e) {
console.error('Failed to parse editor settings:', e);
}
}
const app = mount(App, { const app = mount(App, {
target: document.getElementById('app')!, target: document.getElementById('app')!,
}) })

169
src/theme.css Normal file
View File

@ -0,0 +1,169 @@
/* Theme System */
/* Dark Theme - Default */
:root,
[data-theme="dark"] {
/* Base Colors */
--color-bg-base: #1a1a1a;
--color-bg-elevated: #242424;
--color-bg-overlay: #2a2a2a;
--color-bg-input: #1e1e1e;
/* Surface Colors */
--color-surface-0: #1a1a1a;
--color-surface-1: #202020;
--color-surface-2: #252525;
--color-surface-3: #2a2a2a;
/* Text Colors */
--color-text-primary: rgba(255, 255, 255, 0.87);
--color-text-secondary: rgba(255, 255, 255, 0.6);
--color-text-tertiary: rgba(255, 255, 255, 0.4);
--color-text-disabled: rgba(255, 255, 255, 0.25);
/* Accent Colors */
--color-accent-primary: #646cff;
--color-accent-primary-hover: #818cf8;
--color-accent-primary-active: #535bf2;
--color-accent-primary-subtle: rgba(100, 108, 255, 0.1);
/* Semantic Colors */
--color-success: #10b981;
--color-warning: #fbbf24;
--color-error: #ef4444;
--color-info: #60a5fa;
/* Border Colors */
--color-border-subtle: rgba(255, 255, 255, 0.06);
--color-border-default: rgba(255, 255, 255, 0.1);
--color-border-strong: rgba(255, 255, 255, 0.2);
--color-border-accent: var(--color-accent-primary);
/* Interactive States */
--color-hover-overlay: rgba(255, 255, 255, 0.05);
--color-active-overlay: rgba(255, 255, 255, 0.08);
--color-selected-overlay: rgba(100, 108, 255, 0.15);
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
--shadow-md: 0 2px 4px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.4);
/* Component Specific */
--accordion-header-bg: transparent;
--accordion-header-hover: var(--color-hover-overlay);
--accordion-border: var(--color-border-subtle);
--input-bg: var(--color-bg-input);
--input-border: var(--color-border-default);
--input-border-focus: var(--color-accent-primary);
--tooltip-bg: rgba(32, 32, 32, 0.98);
--tooltip-border: var(--color-border-default);
--code-bg: transparent;
--code-border: var(--color-border-subtle);
--code-text: var(--color-text-secondary);
}
/* Light Theme */
[data-theme="light"] {
/* Base Colors */
--color-bg-base: #fafafa;
--color-bg-elevated: #ffffff;
--color-bg-overlay: #f5f5f5;
--color-bg-input: #ffffff;
/* Surface Colors */
--color-surface-0: #fafafa;
--color-surface-1: #ffffff;
--color-surface-2: #f5f5f5;
--color-surface-3: #eeeeee;
/* Text Colors */
--color-text-primary: rgba(0, 0, 0, 0.87);
--color-text-secondary: rgba(0, 0, 0, 0.6);
--color-text-tertiary: rgba(0, 0, 0, 0.4);
--color-text-disabled: rgba(0, 0, 0, 0.25);
/* Accent Colors */
--color-accent-primary: #5865f2;
--color-accent-primary-hover: #4752c4;
--color-accent-primary-active: #3c45a5;
--color-accent-primary-subtle: rgba(88, 101, 242, 0.1);
/* Semantic Colors */
--color-success: #059669;
--color-warning: #d97706;
--color-error: #dc2626;
--color-info: #2563eb;
/* Border Colors */
--color-border-subtle: rgba(0, 0, 0, 0.08);
--color-border-default: rgba(0, 0, 0, 0.12);
--color-border-strong: rgba(0, 0, 0, 0.24);
--color-border-accent: var(--color-accent-primary);
/* Interactive States */
--color-hover-overlay: rgba(0, 0, 0, 0.04);
--color-active-overlay: rgba(0, 0, 0, 0.08);
--color-selected-overlay: rgba(88, 101, 242, 0.12);
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.08);
--shadow-md: 0 2px 4px rgba(0, 0, 0, 0.12);
--shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.15);
/* Component Specific */
--accordion-header-bg: transparent;
--accordion-header-hover: var(--color-hover-overlay);
--accordion-border: var(--color-border-subtle);
--input-bg: var(--color-bg-input);
--input-border: var(--color-border-default);
--input-border-focus: var(--color-accent-primary);
--tooltip-bg: rgba(255, 255, 255, 0.98);
--tooltip-border: var(--color-border-default);
--code-bg: transparent;
--code-border: var(--color-border-subtle);
--code-text: var(--color-text-secondary);
}
/* Shared Variables */
:root {
/* Spacing Scale */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 0.75rem;
--space-lg: 1rem;
--space-xl: 1.5rem;
--space-2xl: 2rem;
/* Border Radius */
--radius-sm: 2px;
--radius-md: 4px;
--radius-lg: 6px;
/* Font Sizes */
--font-xs: 0.6875rem;
--font-sm: 0.75rem;
--font-md: 0.8125rem;
--font-base: 0.875rem;
--font-lg: 1rem;
/* Font Families */
--font-mono: 'Courier New', Monaco, Consolas, monospace;
--font-sans: system-ui, -apple-system, sans-serif;
/* Line Heights */
--leading-tight: 1.4;
--leading-normal: 1.5;
--leading-relaxed: 1.6;
/* Transitions */
--transition-fast: 100ms ease;
--transition-base: 200ms ease;
--transition-slow: 300ms ease;
}