From 47504e50199577a10f23db460e547f7af6412f3f Mon Sep 17 00:00:00 2001 From: codewithwan Date: Thu, 16 Jul 2026 21:01:07 +0700 Subject: [PATCH] fix(web): the sing beat, and a page a phone can actually use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lyrics never appeared. rasterize picks the karaoke line *or* the caption strip, never both, and the karaoke line is half the size and sits up at his shoulder rather than under the ground. The preview drew the song's intro, in the caption strip, at caption size, forever — so the three lines somebody had just typed were never once on screen. Ported properly: same scale, same place, same 30-tick cycle, and the strip stays empty like it does in the file. Verified against a real render at four ticks — identical, both bands. Drag did nothing on a phone. A finger starting on the handle can't be told apart from a scroll until it has already moved, and by then the page has gone, so the gesture never reached dnd-kit: touch-action: none on the handle, and a touch sensor that waits 180ms. Hold then drag; move first and it scrolls, which is what a finger on a long page usually wants. The handle also got bigger, because 8px of tap target is a joke on glass. Keyboard sensor too — a list you can only reorder by dragging is a list some people can't reorder. And the page was 15px wider than the phone. Grid and flex children are min-width:auto, so they refuse to shrink under their content, and the canvas carries an intrinsic 1056px however hard w-full argues. min-w-0 where it matters: zero overflow on all three steps now. --- web/src/App.tsx | 2 +- web/src/lib/sample.ts | 6 +++++- web/src/stage/Meter.tsx | 2 +- web/src/stage/Reel.tsx | 2 +- web/src/stage/Stage.tsx | 16 ++++++++-------- web/src/stage/text.ts | 35 ++++++++++++++++++++++++++++++++++ web/src/steps/StepIdentity.tsx | 2 +- web/src/steps/StepStory.tsx | 6 +++--- web/src/story/SceneList.tsx | 30 ++++++++++++++++++++++++++--- web/src/story/SceneRow.tsx | 7 +++++-- web/src/ui/Card.tsx | 2 +- 11 files changed, 88 insertions(+), 22 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index 84d2f06..5bb68de 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -28,7 +28,7 @@ export function App() {
-
+
{at === 0 && } {at === 1 && ( > & { song?: string; artist?: string }; +export type Tokens = Partial> & { + song?: string; + artist?: string; + lyrics?: string[]; +}; /** Fill the `{tokens}` a caption carries. * diff --git a/web/src/stage/Meter.tsx b/web/src/stage/Meter.tsx index c02f6dd..772f134 100644 --- a/web/src/stage/Meter.tsx +++ b/web/src/stage/Meter.tsx @@ -20,7 +20,7 @@ export function Meter({ story, at, solo, onPick }: { story: Scene[]; at: number; const band = BANDS.find((b) => secs <= b.max)!; return ( -
+
{secs.toFixed(0)}s {band.label} diff --git a/web/src/stage/Reel.tsx b/web/src/stage/Reel.tsx index b9278cf..b1b2d49 100644 --- a/web/src/stage/Reel.tsx +++ b/web/src/stage/Reel.tsx @@ -58,7 +58,7 @@ export function Reel({ } return ( -
+
); @@ -72,11 +77,6 @@ export function Stage({ reel, story, tick, id }: Props) { function captionOf(scene: Scene | undefined, k: number, leaving: boolean, id: Tokens): string { if (leaving) return "thanks for stopping by ~"; if (!scene) return ""; - if (scene.act === "sing") { - const song = id.song?.trim() || "an old favourite"; - const artist = id.artist?.trim() || "someone great"; - return fill(`my fav song "${song}" - ${artist}`, id); - } const line = scene.then && k >= GLOW_AT ? scene.then : (scene.say ?? ""); return fill(line, id); } diff --git a/web/src/stage/text.ts b/web/src/stage/text.ts index 788b34e..972fbb1 100644 --- a/web/src/stage/text.ts +++ b/web/src/stage/text.ts @@ -5,6 +5,12 @@ import { glyph, icon } from "../wasm/awan_wasm"; export const SCALE = 3; export const GLYPH = 8 * SCALE; export const CAPTION_H = 56; +/** The karaoke line is smaller and sits up beside him, not under the ground — + * every number from gif.rs. */ +export const LYRIC_SCALE = 2; +export const LYRIC_LIMIT = 18 * 33; +/** Ticks a lyric holds before the next one, from script.rs. */ +export const LYRIC_HOLD = 30; export const INK = "#9696a0"; // [150, 150, 160] in gif.rs — convert, don't eyeball export const ACCENT = "#e6b464"; // [230, 180, 100] @@ -79,3 +85,32 @@ export function drawStreak(ctx: CanvasRenderingContext2D, streak: number, w: num drawBits(ctx, icon("fire"), x, 12, SCALE, ACCENT); drawText(ctx, num, x + 8 * SCALE + SCALE * 2, 12, SCALE, ACCENT); } + +/** One karaoke line down the left while he sings on the right. + * + * Not a caption: `rasterize` picks *either* the strip under the ground *or* + * this, never both, and this one is half the size and up at his shoulder. The + * preview drew the intro line, in the caption strip, at caption size, forever + * — which meant the lyrics somebody had just typed never appeared at all. + */ +export function drawKaraoke( + ctx: CanvasRenderingContext2D, + k: number, + ground: number, + song: string, + artist: string, + lyrics: string[], +) { + const step = Math.floor(k / LYRIC_HOLD); + const [iconName, text] = + step === 0 + ? ["star", `my fav song "${song || "an old favourite"}" - ${artist || "someone great"}`] + : lyrics.length + ? ["globe", lyrics[(step - 1) % lyrics.length]] + : ["globe", "la la la ~"]; + + const fit = Math.floor(Math.max(LYRIC_LIMIT - 24, 0) / (8 * LYRIC_SCALE)); + const y = Math.floor(ground / 2) - 4 * LYRIC_SCALE; + drawBits(ctx, icon(iconName), 24, y, LYRIC_SCALE, ACCENT); + drawText(ctx, [...text].slice(0, fit).join(""), 24 + 8 * LYRIC_SCALE + 6, y, LYRIC_SCALE, INK); +} diff --git a/web/src/steps/StepIdentity.tsx b/web/src/steps/StepIdentity.tsx index 9971499..5eb5900 100644 --- a/web/src/steps/StepIdentity.tsx +++ b/web/src/steps/StepIdentity.tsx @@ -20,7 +20,7 @@ const FIELDS: { key: keyof Identity; label: string; hint: string }[] = [ export function StepIdentity({ id, onChange }: { id: Identity; onChange: (id: Identity) => void }) { const filled = Object.values(id).some((v) => (Array.isArray(v) ? v.length : v)); return ( -
+
diff --git a/web/src/ui/Card.tsx b/web/src/ui/Card.tsx index db5115b..bfa1f64 100644 --- a/web/src/ui/Card.tsx +++ b/web/src/ui/Card.tsx @@ -15,7 +15,7 @@ export function Card({ children: ReactNode; }) { return ( -
+
{title && (

{title}