Add Go CDN and playground server

Build Core and Handles into in-memory CDN modules, add the marketing site and tutorial docs, and provide a SQLite-backed WebGPU playground with TypeScript tooling and profiling.

Amp-Thread-ID: https://ampcode.com/threads/T-01a02485-5574-707c-bff4-5668d83bee8a
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-08-21 16:42:02 +00:00
co-authored by heaust
parent 53a53a8f3f
commit 7070799862
27 changed files with 3402 additions and 7 deletions
+138
View File
@@ -0,0 +1,138 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#f7fbff" />
<meta name="description" content="Write, run, profile, and share Yawn WebGPU scenes." />
<title>Yawn Playground</title>
<link rel="stylesheet" href="/assets/playground.css" />
<script src="/assets/monaco/vs/loader.js"></script>
<script type="module" src="/assets/playground.js"></script>
</head>
<body>
<main class="playground-app">
<header class="playground-header">
<a class="playground-brand" href="/" aria-label="Yawn home">
<span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span><strong>Yawn</strong>
</a>
<div class="header-divider"></div>
<div class="project-name">
<input id="project-title" maxlength="80" value="Sky triangle" aria-label="Playground title" />
<span id="dirty-indicator" title="Unsaved changes"></span>
<span id="revision-label">Draft</span>
</div>
<nav class="playground-nav" aria-label="Playground links">
<a href="/docs">Docs</a>
<a href="https://git.heaust.org/heaust/yawn">Source</a>
</nav>
<button id="share-button" class="tool-button" type="button" hidden>
<span aria-hidden="true"></span> Share
</button>
<button id="save-button" class="tool-button" type="button">
<span aria-hidden="true"></span> Save
</button>
<button id="run-button" class="run-button" type="button">
<span aria-hidden="true"></span> Run <kbd>Ctrl ↵</kbd>
</button>
</header>
<div class="playground-tools">
<div class="tool-group">
<button id="snippets-button" type="button"><span class="braces">{ }</span> Snippets</button>
<button id="reset-button" type="button"><span aria-hidden="true"></span> Reset</button>
<button id="format-button" type="button"><span aria-hidden="true"></span> Format</button>
</div>
<div class="tool-context">
<span class="language-dot"></span>
<span>TypeScript</span>
<span class="context-divider"></span>
<span id="type-status">Starting language service…</span>
</div>
<div class="tool-group tool-group-right">
<button id="inspector-button" type="button" aria-pressed="true"><span aria-hidden="true"></span> Inspector</button>
<button id="fullscreen-button" type="button" aria-label="Toggle fullscreen"><span aria-hidden="true"></span></button>
</div>
</div>
<div class="workbench" id="workbench">
<section class="editor-pane" aria-label="TypeScript editor">
<div class="pane-tabs">
<button class="pane-tab active" type="button"><span class="ts-icon">TS</span> scene.ts <i id="tab-dirty"></i></button>
</div>
<div id="editor" class="editor"><div class="editor-loading">Loading TypeScript editor…</div></div>
<footer class="editor-status">
<span id="cursor-status">Ln 1, Col 1</span>
<span>Spaces: 2</span>
<span>UTF-8</span>
<span class="status-spacer"></span>
<span id="editor-diagnostics"><i></i> No problems</span>
</footer>
</section>
<div class="resize-handle" id="resize-handle" role="separator" aria-orientation="vertical" aria-label="Resize editor and preview"></div>
<section class="stage-pane" aria-label="Yawn output">
<div class="stage-toolbar">
<div class="stage-tabs">
<button type="button" class="active">Preview</button>
</div>
<div class="stage-meta">
<span id="run-status"><i></i> Ready</span>
<span class="stage-divider"></span>
<span id="fps-status">0 FPS</span>
<span class="stage-divider"></span>
<span id="resolution-status"></span>
</div>
</div>
<div class="preview" id="preview">
<canvas id="canvas" aria-label="Yawn WebGPU preview"></canvas>
<div class="preview-message" id="preview-message">
<span class="preview-message-icon">Y</span>
<strong>Preparing your scene</strong>
<small>The first run compiles the render graph.</small>
</div>
</div>
<div class="inspector" id="inspector">
<div class="inspector-tabs" role="tablist">
<button type="button" class="active" role="tab" aria-selected="true" data-panel="console">Console <span id="console-count">0</span></button>
<button type="button" role="tab" aria-selected="false" data-panel="profile">GPU profiler</button>
<button id="clear-console" class="clear-console" type="button">Clear</button>
</div>
<div class="console-panel inspector-panel active" id="console-panel" role="tabpanel">
<div class="console-empty" id="console-empty"><span>_</span> Output from <code>log()</code> appears here.</div>
<ol id="console-lines"></ol>
</div>
<div class="profile-panel inspector-panel" id="profile-panel" role="tabpanel">
<div class="profile-empty" id="profile-empty">
<span class="pulse-icon"></span>
<div><strong>Waiting for GPU timestamps</strong><small>Run the scene to inspect its physical passes.</small></div>
</div>
<div class="profile-results" id="profile-results" hidden>
<div class="profile-summary"><div><small>Total GPU time</small><strong id="profile-total"></strong></div><div><small>Readback latency</small><strong id="profile-latency"></strong></div><div><small>Adapter</small><strong id="profile-adapter"></strong></div></div>
<table><thead><tr><th>Physical pass</th><th>GPU time</th></tr></thead><tbody id="profile-passes"></tbody></table>
</div>
</div>
</div>
</section>
</div>
</main>
<dialog id="snippets-dialog" class="snippets-dialog">
<div class="dialog-heading">
<div><span>Code library</span><h2>Snippets</h2><p>Insert a focused building block at your cursor.</p></div>
<button type="button" data-close-dialog aria-label="Close snippets">×</button>
</div>
<div class="snippet-search"><span></span><input type="search" placeholder="Find a snippet" aria-label="Find a snippet" /></div>
<div class="snippet-grid">
<button type="button" data-snippet="camera"><span class="snippet-icon"></span><div><strong>Orbit camera</strong><small>Pointer orbit and wheel zoom</small><code>ArcRotateCamera</code></div><b>+</b></button>
<button type="button" data-snippet="light"><span class="snippet-icon"></span><div><strong>Point light</strong><small>Warm, local illumination</small><code>PointLight</code></div><b>+</b></button>
<button type="button" data-snippet="instances"><span class="snippet-icon"></span><div><strong>Mesh instances</strong><small>Clone shared geometry</small><code>mesh.clone()</code></div><b>+</b></button>
<button type="button" data-snippet="rows"><span class="snippet-icon"></span><div><strong>Shared rows</strong><small>Add application-owned hot data</small><code>scene.ensureRows()</code></div><b>+</b></button>
<button type="button" data-snippet="post"><span class="snippet-icon"></span><div><strong>Post processing</strong><small>Color grading and FXAA</small><code>batchGraphUpdates()</code></div><b>+</b></button>
</div>
</dialog>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
</body>
</html>