Skip to content
Merged
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
48 changes: 41 additions & 7 deletions packages/www/src/components/Playground.astro
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,22 @@
.pg-split[data-view='render'] .pg-resizer,
.pg-split[data-view='render'] .pg-editor-wrap { display: none; }

/* Narrow screens: exactly one pane at a time — the editor (Code) or the paper (Render) — filling
the viewport below the topbar like an app screen. The pane scrolls internally (Monaco or the
Preview); the page behind it has nothing else to scroll (the play page hides its prose on
mobile). Split isn't offered here — the JS resolves it to Code. 4.9rem = topbar + the play
page's tightened vertical padding. */
@media (max-width: 880px) {
.pg-split { grid-template-columns: 1fr; height: auto; gap: 1rem; }
.pg-split { grid-template-columns: 1fr; height: max(420px, calc(100dvh - 4.9rem)); }
.pg-resizer { display: none; }
.pg-viewtab-split { display: none; }
}
/* Phone-width toolbar: the view tabs drop their labels (icons + `title` remain) so the example
selector keeps enough width to stay readable next to Run. */
@media (max-width: 680px) {
.pg-viewtab span { display: none; }
.pg-viewtab { padding: 0.22rem 0.5rem; }
}

.pg-resizer {
cursor: col-resize;
Expand Down Expand Up @@ -205,7 +216,6 @@
flex-wrap: nowrap;
}
.pg-editor { flex: 1; min-height: 0; height: auto; width: 100%; }
@media (max-width: 880px) { .pg-editor { flex: none; height: 320px; } }

.pg-select-wrap { position: relative; display: inline-flex; }
.pg-select {
Expand Down Expand Up @@ -263,6 +273,8 @@
.pg-output-wrap { display: flex; flex-direction: column; overflow: hidden; min-height: 260px; }

.pg-output-head {
/* `relative` so the metrics detail panel can anchor to the full header row on phones. */
position: relative;
display: flex; align-items: center; justify-content: space-between; gap: 0.6rem 1rem;
flex-wrap: wrap;
min-height: var(--pg-head, 2.3rem);
Expand Down Expand Up @@ -422,6 +434,20 @@
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.14); white-space: normal; line-height: 1.85;
}
#pg-metrics:hover .pg-metrics-detail { display: block; }
/* Phone-width Render header: only time · backend stay in the readout (the samples chip and its
separator hide), and the details panel re-anchors to the header row itself — spanning its full
width and scrolling internally — instead of hanging off the left edge of the screen. */
@media (max-width: 680px) {
#pg-metrics .m-wide { display: none; }
/* Extra ancestor for specificity: the scoped `#pg-metrics { position: relative }` compiles to
`#pg-metrics[data-astro-cid]` (1,1,0), which a bare `.pg-output-head #pg-metrics` only ties. */
.pg-output-head .pg-head-right #pg-metrics { position: static; }
#pg-metrics .pg-metrics-detail {
left: 0.4rem; right: 0.4rem;
min-width: 0; max-width: none;
max-height: min(65dvh, 30rem); overflow-y: auto;
}
}
#pg-metrics .pg-metrics-detail .mm-sec + .mm-sec {
margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--rule);
}
Expand Down Expand Up @@ -679,7 +705,9 @@
renderLineHighlight: 'line',
smoothScrolling: true,
automaticLayout: true,
scrollbar: { verticalScrollbarSize: 8, horizontalScrollbarSize: 8 },
// `alwaysConsumeMouseWheel: false` lets a wheel/trackpad scroll reach the page once the
// editor is at its own scroll limit — otherwise the editor is a scroll trap.
scrollbar: { verticalScrollbarSize: 8, horizontalScrollbarSize: 8, alwaysConsumeMouseWheel: false },
});

/** Paint the pre-run page: the paper header (from the frontmatter `meta`) + a "press run" body.
Expand Down Expand Up @@ -1306,9 +1334,15 @@
const warn = !!d && (d.gpu.shaderFailures > 0 || !d.crossOriginIsolated);

// --- compact summary (always visible) ---
const summary: string[] = [chip('time', fmtMs(res.elapsedMs))];
if (s.samples > 0) summary.push(chip('samples', fmtCount(s.samples)));
summary.push(chip('backend', backend, onGpu ? 'm-rate' : 'm-diag'));
// The samples chip travels with its separator inside `.m-wide` so narrow screens can drop the
// pair — the phone header keeps to one line: time · backend · details.
const summaryHtml =
chip('time', fmtMs(res.elapsedMs)) +
(s.samples > 0
? `<span class="m-wide"><span class="m-sep"> · </span>${chip('samples', fmtCount(s.samples))}</span>`
: '') +
`<span class="m-sep"> · </span>` +
chip('backend', backend, onGpu ? 'm-rate' : 'm-diag');

// --- hover detail (everything) ---
const section = (title: string, rows: string) =>
Expand Down Expand Up @@ -1361,7 +1395,7 @@
section('Engine notes', noteRows);

metricsEl.innerHTML =
`<span class="pg-metrics-summary ${warn ? 'has-warn' : ''}">${join(summary)}` +
`<span class="pg-metrics-summary ${warn ? 'has-warn' : ''}">${summaryHtml}` +
`<span class="mm-more">details ▾</span></span>` +
`<span class="pg-metrics-detail">${detail}</span>`;
metricsEl.hidden = false;
Expand Down
151 changes: 151 additions & 0 deletions packages/www/src/components/TopBar.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
---
// The shared site header (every page carries the same one): brand on the left, links on the
// right. On small screens the link row can't fit next to the brand, so it collapses behind a
// burger button that drops the links down as a full-width panel under the bar.
interface Props {
current?: 'play' | 'skill';
}
const { current } = Astro.props;

const links: { href: string; label: string; key?: 'play' | 'skill' }[] = [
{ href: '/#ideas', label: 'How it works' },
{ href: '/play', label: 'Playground', key: 'play' },
{ href: '/skill', label: 'Skills', key: 'skill' },
{ href: '/#author', label: 'About' },
{ href: 'https://github.com/manucorporat/noise-lang', label: 'GitHub' },
];
---

<header class="topbar" id="topbar">
<div class="wide topbar-inner">
<a class="brand" href="/"><img class="brand-mark" src="/favicon.svg" alt="" width="24" height="24" />noise<span class="brand-tilde">~</span><span class="brand-dim">lang</span></a>
<button
class="topbar-burger"
id="topbar-burger"
type="button"
aria-label="Open menu"
aria-expanded="false"
aria-controls="topbar-menu"
>
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
<g class="burger-lines">
<line x1="4" y1="7" x2="20" y2="7"></line>
<line x1="4" y1="12" x2="20" y2="12"></line>
<line x1="4" y1="17" x2="20" y2="17"></line>
</g>
<g class="burger-close">
<line x1="6" y1="6" x2="18" y2="18"></line>
<line x1="18" y1="6" x2="6" y2="18"></line>
</g>
</svg>
</button>
<nav class="topbar-links" id="topbar-menu">
{links.map((l) => (
<a href={l.href} aria-current={l.key && l.key === current ? 'page' : undefined}>{l.label}</a>
))}
</nav>
</div>
</header>

<style>
.topbar {
position: sticky;
top: 0;
z-index: 30;
background: rgba(251, 250, 246, 0.88);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border-bottom: 1px solid var(--rule);
}
.topbar-inner {
display: flex; align-items: baseline; justify-content: space-between;
padding-top: 0.7rem; padding-bottom: 0.7rem;
}
.brand { display: inline-flex; align-items: center; font-family: var(--serif); font-weight: 700; font-size: 1.1rem; color: var(--ink); }
.brand:hover { text-decoration: none; }
.brand-mark { display: block; width: 1.4em; height: 1.4em; margin-right: 0.5rem; }
.brand-dim { color: var(--ink-dim); }
.brand-tilde {
font-family: var(--mono);
font-weight: 500;
color: var(--accent);
margin: 0 0.04em;
vertical-align: baseline;
position: relative;
top: 0.02em;
}
.topbar-links { display: flex; gap: 1.3rem; }
.topbar-links a { color: var(--ink-dim); font-size: 0.92rem; }
.topbar-links a:hover { color: var(--ink); text-decoration: none; }
.topbar-links a[aria-current='page'] { color: var(--ink); }

/* The burger: hidden on wide screens, swaps its icon to an × while the menu is open. */
.topbar-burger {
display: none;
align-self: center;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
padding: 0.25rem 0.35rem;
cursor: pointer;
color: var(--ink);
line-height: 0;
}
.topbar-burger svg { stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; fill: none; }
.topbar-burger:focus-visible { outline: 2px solid var(--link); }
.topbar-burger .burger-close { display: none; }
.topbar.menu-open .topbar-burger .burger-lines { display: none; }
.topbar.menu-open .topbar-burger .burger-close { display: block; }

@media (max-width: 680px) {
.topbar-burger { display: inline-flex; }
/* The links become a dropdown panel under the (sticky) bar, toggled by the burger. */
.topbar-links {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
flex-direction: column;
gap: 0;
padding: 0.3rem 0 0.5rem;
background: var(--paper);
border-bottom: 1px solid var(--rule);
box-shadow: 0 12px 24px -14px rgba(0, 0, 0, 0.25);
}
.topbar.menu-open .topbar-links { display: flex; }
.topbar-links a {
padding: 0.7rem 1.5rem;
font-size: 1rem;
border-top: 1px solid var(--rule);
}
.topbar-links a:first-child { border-top: none; }
}
</style>

<script>
const bar = document.getElementById('topbar')!;
const burger = document.getElementById('topbar-burger')!;
const menu = document.getElementById('topbar-menu')!;

const setOpen = (open: boolean) => {
bar.classList.toggle('menu-open', open);
burger.setAttribute('aria-expanded', String(open));
burger.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
};

burger.addEventListener('click', (e) => {
e.stopPropagation();
setOpen(!bar.classList.contains('menu-open'));
});
// Following a link (including a same-page #anchor) should fold the menu away.
menu.addEventListener('click', (e) => {
if ((e.target as HTMLElement).closest('a')) setOpen(false);
});
document.addEventListener('click', (e) => {
if (bar.classList.contains('menu-open') && !bar.contains(e.target as Node)) setOpen(false);
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') setOpen(false);
});
</script>
46 changes: 2 additions & 44 deletions packages/www/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
import Layout from '../layouts/Layout.astro';
import TopBar from '../components/TopBar.astro';
import Gallery from '../components/Gallery.astro';
import ShaderFigure from '../components/ShaderFigure.astro';
import ConceptDemo from '../components/ConceptDemo.astro';
Expand Down Expand Up @@ -39,18 +40,7 @@ const ideas = [
---

<Layout>
<header class="topbar">
<div class="wide topbar-inner">
<a class="brand" href="/"><img class="brand-mark" src="/favicon.svg" alt="" width="24" height="24" />noise<span class="brand-tilde">~</span><span class="brand-dim">lang</span></a>
<nav class="topbar-links">
<a href="#ideas">How it works</a>
<a href="/play">Playground</a>
<a href="/skill">Skills</a>
<a href="#author">About</a>
<a href="https://github.com/manucorporat/noise-lang">GitHub</a>
</nav>
</div>
</header>
<TopBar />

<article class="doc">
<div class="measure masthead">
Expand Down Expand Up @@ -263,38 +253,6 @@ const ideas = [
</Layout>

<style>
.topbar {
position: sticky;
top: 0;
z-index: 30;
background: rgba(251, 250, 246, 0.88);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border-bottom: 1px solid var(--rule);
}
.topbar-inner {
display: flex; align-items: baseline; justify-content: space-between;
padding-top: 0.7rem; padding-bottom: 0.7rem;
}
.brand { display: inline-flex; align-items: center; font-family: var(--serif); font-weight: 700; font-size: 1.1rem; color: var(--ink); }
.brand:hover { text-decoration: none; }
.brand-mark { display: block; width: 1.4em; height: 1.4em; margin-right: 0.5rem; }
.brand-dim { color: var(--ink-dim); }
/* the serif tilde sits high and tiny; typeset it in mono, baseline-aligned, as the draw operator */
.brand-tilde {
font-family: var(--mono);
font-weight: 500;
color: var(--accent);
margin: 0 0.04em;
vertical-align: baseline;
position: relative;
top: 0.02em;
}
.topbar-links { display: flex; gap: 1.3rem; }
.topbar-links a { color: var(--ink-dim); font-size: 0.92rem; }
.topbar-links a:hover { color: var(--ink); text-decoration: none; }
@media (max-width: 680px) { .topbar-links a:not(:last-child) { display: none; } }

.doc { counter-reset: section; padding: 2.5rem 0 1rem; }

.masthead { padding-top: 1.5rem; }
Expand Down
54 changes: 9 additions & 45 deletions packages/www/src/pages/play.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
import Layout from '../layouts/Layout.astro';
import TopBar from '../components/TopBar.astro';
import Playground from '../components/Playground.astro';

// The embed snippet is plain text (TS, not Noise) — kept in frontmatter so no braces/backticks
Expand All @@ -20,18 +21,7 @@ console.log(result.output); // everything Print(...) emitted`;
title="Noise playground — run Noise in your browser"
description="The Noise playground: write probabilistic programs and run them in your browser on the real WebAssembly engine. Pick an example or share your own via link."
>
<header class="topbar">
<div class="wide topbar-inner">
<a class="brand" href="/"><img class="brand-mark" src="/favicon.svg" alt="" width="24" height="24" />noise<span class="brand-tilde">~</span><span class="brand-dim">lang</span></a>
<nav class="topbar-links">
<a href="/#ideas">How it works</a>
<a href="/play" aria-current="page">Playground</a>
<a href="/skill">Skills</a>
<a href="/#author">About</a>
<a href="https://github.com/manucorporat/noise-lang">GitHub</a>
</nav>
</div>
</header>
<TopBar current="play" />

<main class="play-main">
<div class="play-host">
Expand Down Expand Up @@ -72,42 +62,16 @@ console.log(result.output); // everything Print(...) emitted`;
</Layout>

<style>
/* Topbar — mirrors the home page (src/pages/index.astro). */
.topbar {
position: sticky;
top: 0;
z-index: 30;
background: rgba(251, 250, 246, 0.88);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border-bottom: 1px solid var(--rule);
}
.topbar-inner {
display: flex; align-items: baseline; justify-content: space-between;
padding-top: 0.7rem; padding-bottom: 0.7rem;
}
.brand { display: inline-flex; align-items: center; font-family: var(--serif); font-weight: 700; font-size: 1.1rem; color: var(--ink); }
.brand:hover { text-decoration: none; }
.brand-mark { display: block; width: 1.4em; height: 1.4em; margin-right: 0.5rem; }
.brand-dim { color: var(--ink-dim); }
.brand-tilde {
font-family: var(--mono);
font-weight: 500;
color: var(--accent);
margin: 0 0.04em;
vertical-align: baseline;
position: relative;
top: 0.02em;
}
.topbar-links { display: flex; gap: 1.3rem; }
.topbar-links a { color: var(--ink-dim); font-size: 0.92rem; }
.topbar-links a:hover { color: var(--ink); text-decoration: none; }
.topbar-links a[aria-current='page'] { color: var(--ink); }
@media (max-width: 680px) { .topbar-links a:not(:last-child):not([aria-current='page']) { display: none; } }

.play-main { padding: 1.6rem 0 1rem; }
/* the playground claims most of the viewport width (wider than the prose `.wide` column) */
.play-host { max-width: min(1720px, 98vw); margin: 0 auto; padding: 0 0.75rem; }
/* Mobile is just the playground, app-style: the pane fills the viewport below the topbar (its
height budget is set in Playground.astro — keep the two in sync) and everything after it (the
embed prose, the footer) is dropped so there's nothing else to scroll to. */
@media (max-width: 880px) {
.play-main { padding: 0.75rem 0; }
.embed, .doc-footer { display: none; }
}
@media (max-width: 680px) { .play-host { padding: 0 0.4rem; } }

/* "Put Noise in your own site" — quiet prose column under the playground. */
Expand Down
Loading
Loading