diff --git a/website/public/script.js b/website/public/script.js new file mode 100644 index 0000000..ad19a37 --- /dev/null +++ b/website/public/script.js @@ -0,0 +1,54 @@ +const toggle = document.getElementById('theme-toggle'); +const root = document.documentElement; +const stored = localStorage.getItem('theme'); +const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; +const isLight = stored ? stored === 'light' : !prefersDark; + +if (isLight) { + root.classList.add('light'); +} +toggle.textContent = isLight ? 'DARK' : 'LIGHT'; + +toggle.addEventListener('click', () => { + root.classList.toggle('light'); + const light = root.classList.contains('light'); + toggle.textContent = light ? 'DARK' : 'LIGHT'; + localStorage.setItem('theme', light ? 'light' : 'dark'); + highlightForth(); +}); + +function highlightForth() { + const words = ['note', 'sound', 'lpf', 'hpf', 'chorus', 'verb', 'distort', 'speed']; + const notes = ['c4']; + const chords = ['min7']; + const samples = ['kkick', 'sine', 'saw']; + const isLight = document.documentElement.classList.contains('light'); + const numColor = isLight ? '#a855f7' : '#e8a0e8'; + const dotColor = isLight ? '#0284c7' : '#7dd3fc'; + const wordColor = isLight ? '#65a30d' : '#a3e635'; + const noteColor = isLight ? '#d97706' : '#fbbf24'; + const chordColor = isLight ? '#15803d' : '#4ade80'; + const sampleColor = isLight ? '#dc2626' : '#f87171'; + document.querySelectorAll('pre').forEach(pre => { + const text = pre.dataset.source || pre.textContent; + pre.dataset.source = text; + pre.innerHTML = text + .split(/(\s+)/) + .map(t => { + if (t === '.') return `.`; + if (/^-?\d+\.?\d*$/.test(t)) return `${t}`; + if (words.includes(t)) return `${t}`; + if (notes.includes(t)) return `${t}`; + if (chords.includes(t)) return `${t}`; + if (samples.includes(t)) return `${t}`; + return t; + }) + .join(''); + }); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', highlightForth); +} else { + highlightForth(); +} diff --git a/website/public/style.css b/website/public/style.css new file mode 100644 index 0000000..615cea4 --- /dev/null +++ b/website/public/style.css @@ -0,0 +1,127 @@ +@font-face { + font-family: 'CozetteVector'; + src: url('/CozetteVector.ttf') format('truetype'); +} + +:root { + --bg: #000; + --surface: #121212; + --text: #fff; + --text-dim: #b4b4b4; + --text-muted: #787878; +} + +:root.light { + --bg: #fff; + --surface: #f0f0f0; + --text: #000; + --text-dim: #505050; + --text-muted: #8c8c8c; +} + +body { + font-family: 'CozetteVector', monospace; + background: var(--bg); + color: var(--text); + max-width: 800px; + margin: 0 auto; + padding: 1rem; + line-height: 1.3; + font-size: 0.9rem; +} + +h1 { + color: var(--text); + margin: 0; +} + +h2 { + color: var(--text); + margin-top: 1rem; + margin-bottom: 0.25rem; + font-size: 1rem; +} + +p { margin: 0.25rem 0; } + +video { + max-width: 100%; + margin: 0.5rem 0; + display: block; +} + +a { color: var(--text-dim); } + +ul { + padding-left: 1.5rem; + margin: 0.25rem 0; +} + +li { margin: 0.1rem 0; } + +pre { + background: var(--surface); + padding: 0.5rem; + overflow-x: auto; + margin: 0.25rem 0; +} + +.downloads-table { + border-collapse: collapse; + margin: 0.5rem 0; + width: 100%; +} + +.downloads-table th, +.downloads-table td { + padding: 0.25rem 0.75rem; + text-align: left; +} + +.downloads-table th { + color: var(--text); +} + +.downloads-table td:first-child { + color: var(--text-muted); +} + +.downloads-table tr:nth-child(even) { + background: var(--surface); +} + +.note { + color: var(--text-muted); + font-size: 0.8rem; + font-style: italic; +} + +.note a { + color: var(--text-muted); +} + +.support { + background: var(--surface); + padding: 0.5rem; + margin: 0.5rem 0; + color: var(--text-dim); +} + +.support a { + color: var(--text); +} + +#theme-toggle { + font-family: 'CozetteVector', monospace; + background: none; + color: var(--text-muted); + border: none; + padding: 0; + cursor: pointer; + font-size: inherit; + text-decoration: underline; +} + +#theme-toggle:hover { + color: var(--text); +} diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 6b76c0f..9485050 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -18,137 +18,12 @@ - +
-Cagire is free and open source. If you find it useful, consider supporting the project on Ko-fi.
| .tar.gz |
All releases on GitHub. You can also compile the software yourself or get it from Cargo!
+All releases are available on GitHub. You can also compile the software yourself by getting it from Cargo!
Cagire is a step sequencer where each step contains a Forth script instead of typical note data. When the sequencer reaches a step, it runs the script. Scripts can produce sound, trigger samples, apply effects, or do nothing at all. You are free to define what your scripts will do. Cagire includes a built-in audio engine called Doux. No external software is needed to make sound. It comes with oscillators, sample players, filters, reverb, delay, distortion, and more.
+Cagire is a step sequencer where each step contains a Forth script instead of typical note data. When the sequencer reaches a step, it runs the associated script. Scripts can produce sound, trigger samples, apply effects, or do nothing at all. You are free to define what your scripts will do. Cagire includes a built-in audio engine called Doux. No external software is needed to make sound. It comes with oscillators, sample players, filters, reverb, delay, distortion, and more.
A minimal script that plays a middle C note using a sine wave:
c4 note sine sound .+
And now let's make it polyphonic and add different parameters per voice:
+c4 min7 note +sine sound +0.1 chorus +500 1000 1500 lpf +.+
Sawtooth wave with lowpass filter, chorus and reverb:
saw sound 1200 lpf 0.2 chorus 0.8 verb .+
Pitched-down kick drum sample with distortion:
kkick sound 1.5 distort 0.8 speed .+
AGPL-3.0 License
+AGPL-3.0 License ·
- +