Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions apps/marketing/scripts/bake-sounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
AudioBuffer as NodeAudioBuffer,
AudioContext as NodeAudioContext,
Expand Down Expand Up @@ -126,7 +126,11 @@ async function bakePatch(
patch: PatchId,
): Promise<Manifest["patches"][PatchId]> {
const patchPath = join(patchesDir, `${patch}.json`);
const data = (await import(patchPath, { with: { type: "json" } }))
const patchUrl = pathToFileURL(patchPath).href;
const data = (await import(
patchUrl,
{ with: { type: "json" } } as unknown as ImportCallOptions,
))
.default as {
name: string;
sounds: Record<string, RawDefinition>;
Expand Down
30 changes: 30 additions & 0 deletions apps/web/app/editor/page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.page {
max-width: 1120px;
padding: 2rem 1rem 3rem;
margin: 0 auto;
}

.header {
margin-bottom: 1rem;
}

.title {
font-size: clamp(1.5rem, 2vw, 2rem);
line-height: 1.2;
letter-spacing: -0.02em;
}

.description {
margin-top: 0.5rem;
color: var(--ds-gray-10);
}

.referenceSection {
margin-top: 1.5rem;
}

.referenceTitle {
margin-bottom: 0.75rem;
font-size: 1rem;
color: var(--ds-gray-11);
}
33 changes: 33 additions & 0 deletions apps/web/app/editor/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import { DawEditor } from "@/components/daw-editor";
import styles from "./page.module.css";

export const metadata: Metadata = {
title: "Editor",
description: "Design and test custom UI sound patches in real time.",
alternates: { canonical: "https://audio.raphaelsalaja.com/editor" },
openGraph: {
title: "Editor",
description: "Design and test custom UI sound patches in real time.",
url: "https://audio.raphaelsalaja.com/editor",
},
};

export default function EditorPage() {
return (
<main className={styles.page}>
<header className={styles.header}>
<h1 className={styles.title}>Editor</h1>
<p className={styles.description}>
Build and preview custom UI sound patches.
</p>
</header>
<DawEditor />

{/* <section className={styles.referenceSection}>
<h2 className={styles.referenceTitle}>Builder (Reference)</h2>
<SoundBuilder />
</section> */}
</main>
);
}
50 changes: 50 additions & 0 deletions apps/web/components/controls/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,53 @@ export function CheckIcon({ size = 14 }: { size?: number }) {
</svg>
);
}

export function DiceIcon({ size = 14 }: { size?: number }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 14 14"
fill="none"
aria-hidden="true"
>
<rect
x="2"
y="2"
width="10"
height="10"
rx="2"
stroke="currentColor"
strokeWidth="1.2"
/>
<circle cx="5" cy="5" r="0.9" fill="currentColor" />
<circle cx="9" cy="5" r="0.9" fill="currentColor" />
<circle cx="7" cy="9" r="0.9" fill="currentColor" />
</svg>
);
}

export function PowerIcon({ size = 14 }: { size?: number }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 14 14"
fill="none"
aria-hidden="true"
>
<path
d="M7 1.8v4.4"
stroke="currentColor"
strokeWidth="1.4"
strokeLinecap="round"
/>
<path
d="M4.1 3.3a4.6 4.6 0 1 0 5.8 0"
stroke="currentColor"
strokeWidth="1.4"
strokeLinecap="round"
/>
</svg>
);
}
200 changes: 200 additions & 0 deletions apps/web/components/daw-editor/code-output.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/* ── Header ── */

.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.55rem;
}

.title {
display: flex;
gap: 0.4rem;
align-items: center;
font-size: 0.75rem;
font-weight: 600;
color: var(--ds-gray-10);
text-transform: uppercase;
letter-spacing: 0.06em;
}

.dirtyDot {
display: inline-block;
flex-shrink: 0;
width: 5px;
height: 5px;
background: var(--ds-orange-9);
border-radius: 50%;
}

.actions {
display: inline-flex;
gap: 0.4rem;
align-items: center;
}

.actionBtn {
display: inline-flex;
gap: 0.3rem;
align-items: center;
padding: 0.25rem 0.6rem;
font-size: 0.72rem;
font-weight: 500;
color: var(--ds-gray-10);
cursor: pointer;
background: var(--ds-gray-3);
border: 1px solid var(--ds-gray-alpha-4);
border-radius: 6px;
transition:
background 120ms,
color 120ms,
border-color 120ms;

&:disabled {
cursor: default;
opacity: 0.4;
}

&:not(:disabled):hover {
color: var(--ds-gray-12);
background: var(--ds-gray-4);
}
}

.actionBtnPrimary {
color: var(--ds-gray-1);
background: var(--ds-gray-12);
border-color: transparent;

&:not(:disabled):hover {
color: var(--ds-gray-1);
background: var(--ds-gray-11);
}
}

/* ── Outer frame ── */

.preFrame {
display: flex;
overflow-x: auto;
/* max-height: 380px; */
overflow-y: auto;
scrollbar-width: thin;
background: var(--ds-gray-1);
border-radius: 8px;
}

/* ── Line-number gutter ── */

.gutter {
position: sticky;
left: 0;
z-index: 2;
flex-shrink: 0;
padding: 0.75rem 0.6rem 0.75rem 0.75rem;
font-family: var(--font-mono, monospace);
font-size: 0.72rem;
line-height: 1.65;
text-align: right;
user-select: none;
background: var(--ds-gray-3);
}

.gutterLine {
display: block;
min-width: 2ch;
}

/* ── Code area: stacked pre + textarea ── */

.codeArea {
display: grid;
flex: 1;
min-width: 0;
}

/* ─ Typography — identical on both layers.
Declared together so they can never drift. ─ */

.pre,
.editorInput {
box-sizing: border-box;
grid-area: 1 / 1;
padding: 0.75rem 1.5rem 0.75rem 0.75rem;
margin: 0;
font-family: var(--font-mono, monospace);
font-size: 0.72rem;
line-height: 1.65;
tab-size: 2;
white-space: pre;
}

/* ─ Highlighted layer ─ */

.pre {
align-self: start; /* don't stretch — let pre set the row height */
width: max-content;
min-width: 100%;
min-height: 120px;
pointer-events: none; /* don't stretch — let pre set the row height */
}

.line {
display: block;
white-space: pre;
}

.pre span {
color: var(--shiki-light);
}

:global(.dark) .pre span {
color: var(--shiki-dark);
}

/* ─ Editable layer ─ */

.editorInput {
z-index: 1;
align-self: stretch;
/* fills the grid cell — height matches pre automatically */
width: 100%;
overflow: hidden;
color: transparent;
caret-color: var(--ds-gray-12);
resize: none;
outline: none;
background: transparent;
border: none;
}

/* ── Error + hint ── */

.errorText {
padding: 0.35rem 0.6rem;
margin: 0.45rem 0 0;
font-size: 0.71rem;
line-height: 1.5;
color: var(--ds-red-10);
background: color-mix(in oklab, var(--ds-red-3) 80%, transparent);
border: 1px solid var(--ds-red-a5);
border-radius: 6px;
}

.hint {
margin: 0.4rem 0 0;
font-size: 0.68rem;
line-height: 1.5;
color: var(--ds-gray-8);
}

.hint kbd {
display: inline-block;
padding: 0.05em 0.35em;
font-family: var(--font-mono, monospace);
font-size: 0.68rem;
line-height: 1.4;
color: var(--ds-gray-10);
background: var(--ds-gray-3);
border-radius: 4px;
}
Loading
Loading