Skip to content

The studio's look - #164

Merged
kixelated merged 18 commits into
mainfrom
quest/m0/ux/kit
Nov 15, 2026
Merged

kixelated merged 18 commits into
mainfrom
quest/m0/ux/kit

Conversation

@kixelated

@kixelated kixelated commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Implements quest/m0/ux/kit.md: the studio's own look, which every page quest builds on.

  • Tokens (rs/moq-studio/src/kit.rs): the colors, radius, and faces of quest/m0/ux/mock/kit.css, installed into egui's style so its own widgets (text fields, menus, combo boxes) wear them too. One accent (ACCENT, blue until branding replaces that one value) is on air (the ON AIR tag, the tally frame, the program's glow, recording) and selection, toggles, and sliders; red (ERR) is errors alone, egui's error color, a clipping meter, and unhealthy health among them; amber (CUE) is cued, off air, muted, and degraded, and egui's warning color.
  • Faces (kit/fonts.rs, assets/fonts): Barlow Condensed for state and names, Inter (variable, set to 400/500/600 per family) for the interface, JetBrains Mono for numbers. Subset to Latin, Greek, and Cyrillic by sh/kit-assets.sh, about 515 KB together; egui's own faces stay behind them as fallbacks.
  • Icons (kit/icon.rs, assets/icons): 25 Lucide SVGs named by role (Icon::Undo, Icon::Listen, ...), drawn with egui_extras' SVG loader at any size and tint. Adding one is sh/kit-assets.sh <name> and a variant.
  • Widgets (kit/widgets.rs): icon button (names itself and its key in a tooltip), pill, tag, toggle, slider, fader, segmented meter, drag number field, popover, tooltip, sheet, keycap, scene pad, and tally frame, plus the stage wash as a radial gradient mesh. None keeps state; each takes its value from the caller.
  • Effects: Fx::follow takes on air, takes, and health once a frame, and the widgets draw their spots (keycap, tally frame, health) where they paint, so ui.rs no longer gathers spots. Effects read the tokens; the keycap's plastic is P3, the program's glow and the recording pulse are the accent, and the unhealthy electric is red.
  • Today's panels wear it: Preview and Program sit in tally frames on the stage wash (Preview tagged ON AIR when it shows the program, NEXT in amber when not); Scenes are pads with their key; Items take an eye button and drag fields; mutes, sliders, toggles, and hotkey buttons are the kit's; the guest join link opens in a sheet; health is a pill in the menu bar with a popover of relays and problems.
  • Color decision applied to the plans and mock-ups: mock/kit.css now has --accent (renamed from --sel) and --err in place of --air; every mock-up draws on air in the accent, a clip red, and a mute amber, and every frame is re-rendered. The ux epic's Decisions record the rule, and live.md, go-live.md, mix.md, and m1's multiview.md follow it.
  • Review fixes: the Audio panel's gain readout shares the slider's grid cell (it had shifted pan, delay, and the sends one column off their headings, so a send toggle sat under the wrong audio scene); a focused slider or fader locks the arrow keys; both sense drag only and edit only while dragged or on the release and when the value moves, and Gestures::of (ui.rs) counts the release as the drag's last frame, so one drag, its release-frame move included, is one undo. The gain's readout is an editable kit::Number in dB (typed, so it can go back to exactly 0 dB; a gain past +12 dB from a scene file is shown, not cut), and the menu bar's health pill lets an Unhealthy or Degraded verdict win over Not live, so a failed source shows without a relay (both found by Pullfrog).
  • Gallery: just shots gains kit (every widget in every state) and kit_sheet (the sheet over it), from kit/gallery.rs.

No public API or wire change: everything is in the moq-studio binary. moq-scene is untouched.

Views

After the color decision: the gallery (accent for on air, selection, toggles, and sliders; red for the clipped meter's top segment and the Unhealthy pill; amber for the mute, NEXT, and OFF AIR), and the sheet:

The mock-ups it draws from, re-rendered with the new kit.css tokens (shell-live, shell-hover, mix-strip):

Today's panels, before (main), with this PR's first look (red on air), and now. Every tab view shares the tally frames and pads, so studio, audio (the gain readout back in its column), and health stand for the rest; the earlier after column shows the others in red:

View Before (main) Red on air (superseded) Now
studio
audio
health
hotkeys, guests, graph, setup see the first look's views in this PR's history at 913fc40 same layout, accent in place of red

Validation

  • just ci passes (308 tests), including new tests: the faces load with Inter's weight axis covering 400 to 600; a slider follows the pointer and the arrow keys; a toggle flips (both through egui_kittest).

  • just shots renders all 14 views, the two new ones included.

  • UI frame time, main (0ad0e18) against this branch, in a real compositor: headless sway 1.12 at 1440x900@60, Mesa RADV on the host's AMD Radeon 610M iGPU (this host has no NVIDIA GPU, so the program was composited but not encoded), a show with an image, a color, a looping tone, and a clip, effects on. Four alternating 60 s rounds each; a patch never committed logged the frame interval, eframe's CPU time per frame, and the UI thread's on-CPU time per frame from /proc/thread-self/schedstat. Measured while a separate build loaded the host, its load average falling from about 22 to 3 over the runs, so the CPU numbers are noisy:

    Round main interval / UI thread on-CPU This branch interval / UI thread on-CPU
    1 16.76 / 1.34 ms 16.80 / 1.74 ms
    2 17.57 / 2.54 ms 16.76 / 1.44 ms
    3 16.79 / 1.50 ms 16.71 / 0.96 ms
    4 16.71 / 0.88 ms 16.71 / 0.96 ms

    Both hold 60 fps. In the quietest round the UI thread spends 0.88 ms per frame on main and 0.96 ms here; across rounds the difference is within the noise, so no regression measures. Re-measuring on an idle NVIDIA host with the encoder running would firm this up.

  • After the color decision (this iteration): just ci passes (310 tests, two new: a focused slider keeps the arrow keys, and a press on a slider starts its drag; both fail on the previous head's slider and pass now); just shots renders all 14 views, and the views were read back: the tally frames, ON AIR tags, pads, toggles, and sliders draw in the accent, the gallery's clipped meter lights its top segment red, its mute is amber, and its new Unhealthy pill is red. Every mock-up frame was re-rendered with mock/render.sh from the edited kit.css and read back (shell-live, mix-strip). The change is colors only, so the frame-time measurement above stands.

  • After the checkpoint answer (this iteration): just ci passes (312 tests, two new: a_slider_keeps_the_move_its_release_makes presses at 75%, moves to 50%, and releases at 25% in one frame, and a_drag_and_its_release_are_one_gesture checks every edit of such a drag, the release's included, carries one gesture). Each was run against the previous head's code first and failed: the slider missed the release position (the value assertion), and the release edit got its own undo step ([Some(1), Some(1), Some(1), None]). Then, for Pullfrog's findings: just ci passes (313 tests; a_typed_gain_is_exact starts at +20 dB, asserts rendering leaves it, then types 0 and gets gain 1.0 exactly; its first assertion failed before clamp_existing_to_range(false)), and just shots audio health was read back: the gain field sits beside the slider in the Gain column; the health view has no problems, so its pill still reads Not live.

Decisions

  • Faces subset, egui's kept as fallback. Latin, Greek, and Cyrillic cover the names a streamer types; anything else falls through to egui's bundled faces rather than tofu. sh/kit-assets.sh regenerates every asset from pinned sources (Google Fonts at a commit, lucide-static 1.73.0).
  • egui_extras for SVG icons brings resvg 0.45 beside the build script's 0.50 (two copies in the build, not the binary's behavior). The alternative, rasterizing the icons in build.rs, would fix their size; the loader draws them sharp at any scale.
  • Stateless widgets with a pose override (Pose, test only) so the gallery can show hover and press without a pointer.
  • Effects follow state, widgets draw spots: Fx::follow replaces the spot list ui.rs built, so a page that draws a keycap or tally frame gets its effect without wiring.
  • Join link in a sheet, health in a pill: the two places today's panels had their own windows or rows now use the kit's sheet and popover, as the mock-ups do.
  • Widgets no page uses yet (fader, gradient, the meter's vertical run, refused pad, the semibold face, most icons) stay, behind expect(dead_code) naming the quest that will use each, so the gallery shows the whole kit now.
  • Color (maintainer, comment): red means errors, not the program, replacing the quest Plan's "Red is the program and nothing else". ✅ One shared accent token, renamed from sel, for on air, selection, toggles, and sliders (rejected: a separate live token). ✅ A clipping meter is red and a muted channel amber (rejected: both red; both amber). ✅ The gallery's look is accepted as shown. Red against amber is settled by the first: red is errors only, amber cued, off air, and degraded.
  • What else turns red (the brief left it to the writer; listed for review): errors egui draws (error_fg_color, every colored_label of a failure) and the Unhealthy health verdict, whose problems are failures (relay unreachable, output stopped, source failed or stalled), are red, including the pill, the relay's unreachable dot, and the Health panel's electric flicker. Degraded, stalled and CPU-fallback source labels, and a guest offline or ended stay amber. Recording, which is not an error, is the accent, as are the mock-ups' recording dots. The app logo's red gradient and the lower third inside the fake pictures are content, not state, and keep their red.
  • The review's design checkpoint (maintainer, comment): ✅ continue and patch: the slider and fader apply the pointer on release too, so a release in the same frame as the last move keeps it, Gestures::of treats the release as part of the drag for undo, and a test releases at a new position (rejected: rebuilding kit::Slider on egui::Slider; merging as is).
  • The red split above (maintainer, same comment): ✅ keep it as recorded: the Unhealthy verdict is red; stalled, CPU fallback, and a guest offline or ended stay amber; recording is the accent (rejected: Sources' "stalled" red; the verdict amber on a stall).
  • The quest's Required section in live.md moved into its Plan, pointing the page at the kit, and the epic README now names the kit for painter work.

Models

Size: L
Implementer: Claude, claude-opus-5-5, unknown effort
Implementer: Claude Code, claude-opus-5-5, unknown effort (iterate, M)
Implementer: Claude Code, claude-opus-5-5, unknown effort (iterate, S)
Implementer: Claude Code, claude-opus-5-5, unknown effort (iterate, S)

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

Review

Rewritten by quest review after each round.

  • Head: 1b5604a

  • Base: origin/main at 768ad41

  • Risk: medium, the base has no RISK.md to name what is always high or customer-facing, so every change is medium

  • Classified: medium by default

  • Reviewer: Codex, gpt-6.1-sol, xhigh effort

  • Session: 01a1de18-d9d3-7fa2-ba9c-c4bfee066388, round 1

  • Score: 9/10, Source review found no actionable regressions; builds, tests, and quest context were not run, as instructed.

  • Rounds: 7; first round 2 (high 1, medium 1); findings 4 (high 1, medium 3): fixed 4, refuted 0, open 0

  • Findings: none

  • Reviewer: Codex, gpt-6.1-sol, high effort, shadow

  • Session: 01a1dcd9-67e2-73c2-8067-1cc4a1bed30c

  • Rounds: 2; first round 3 (high 1, medium 2); findings 3 (high 1, medium 2): fixed 3, refuted 0, open 0

kixelated and others added 6 commits November 14, 2026 12:56
Quest-Claim: 86434662537bd6a8
The kit (rs/moq-studio/src/kit) holds the tokens of the mock-ups' kit.css,
bundled Inter, Barlow Condensed, and JetBrains Mono, Lucide icons drawn
through egui_extras' SVG loader, and the studio's own widgets. Today's
panels wear it, the effects read its colors and draw as each widget does
rather than from spots the panels register, and `just shots kit` shows
every widget in every state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Contributor Author

Maintainer decision on the kit's open decisions (asked by the /quest-run coordinator, 2026-11-14):

  1. Red means errors, not the program. This replaces the Plan's "Red is the program and nothing else".
    • LIVE (the ON AIR tag, the tally frame, the Program monitor) takes the primary accent, which proper branding will change later.
    • ✅ One shared accent token: rename sel and use it for LIVE, selection, toggles, and sliders, so branding changes one value (rejected: a separate live token).
  2. Mute and clip: ✅ a clipping meter is red (an error) and a muted channel is amber (rejected: both red; both amber).
  3. Taste: ✅ the gallery's look is accepted as shown.
  4. Red against amber: settled by decision 1. Red is now reserved for errors, and amber covers cued, off air, and degraded.

Update the quest's Plan, the mock-ups' kit.css tokens, and any mock-up the kit follows to match, or record where they are left to the MIX quest.

(Written by Claude Opus 5.5 with high effort)

kixelated and others added 4 commits November 14, 2026 17:36
Per the maintainer's decision on #164: rename SEL to ACCENT and draw on
air (tally, ON AIR tag, program glow, recording) with it; red (ERR) is
errors alone, a clipping meter and unhealthy health among them; a mute
stays amber. The mock-ups' kit.css tokens, frames, and the quests'
plans follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and edit as one gesture

Review findings: the gain readout took a grid column of its own, shifting
pan, delay, and the sends from their headings; a focused slider let the
arrows move focus away; a press on the track edited before its drag
started, and the release edited again, so one drag took several undos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Contributor Author

Applied the maintainer's decision at 53f663f:

  1. kit::SEL is now kit::ACCENT and draws on air (the tally frame, ON AIR tags, the program's glow, recording) as well as selection, toggles, and sliders. Red is kit::ERR, errors alone: egui's error color, a clipping meter's top segment, and the Unhealthy health verdict. mock/kit.css has --accent and --err in place of --sel and --air, every mock-up frame is re-rendered, and the ux epic's Decisions, live.md, go-live.md, mix.md, and multiview.md follow.
  2. A clip is red, a mute amber, in the kit and the mock-ups.
  3. The gallery's look is kept.
  4. Amber stays cued, off air, muted, and degraded.

The PR stays a draft on one item: the review reached its design checkpoint on the slider's pointer handling, under ## Waiting with a recommendation.

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Contributor Author

Maintainer decision on the review's design checkpoint and the red scope (asked by the /quest-run coordinator, 2026-11-14):

  1. Slider and fader pointer: ✅ continue and patch it. Apply the pointer on release too, have the undo grouping treat the release as part of the drag, add a test that releases at a new position, then run another review round (rejected: rebuild the slider on egui's slider; merge as is).
  2. What turns red: ✅ keep the split as recorded under ## Decisions (rejected: make Sources' "stalled" label red; keep the verdict amber on a stall). The Unhealthy verdict is red; stalled, CPU, and a guest's offline or ended state stay amber; recording is the accent.

(Written by Claude Opus 5.5 with high effort)

A release in the same frame as the last pointer move dropped that move,
since the slider and fader edited only while dragged(). Apply the pointer
on drag_stopped() too, and count the release in Gestures::of so its edit
undoes with the drag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 15, 2026 04:42
@kixelated

Copy link
Copy Markdown
Contributor Author

Applied the checkpoint decision in e542acd: Slider and Fader apply the pointer on release too, Gestures::of counts the release as part of the drag for undo, and two new tests release at a new position (both failed on the previous head). The red split stays as recorded; both answers are in the PR body's ## Decisions. Codex's round 4 on e542acd: 10/10, no findings.

(Written by Claude Opus 5.5)

@coderabbitai

coderabbitai Bot commented Nov 15, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 33 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 92cc6670-007f-4383-9f9b-9973db8bf256
Commits

Reviewing files that changed from the base of the PR and between c934874 and 48fa0e0.

Files ignored due to path filters: 30

  • Cargo.lock is excluded by !**/*.lock
  • rs/moq-studio/assets/fonts/BarlowCondensed-Bold.ttf is excluded by !**/*.ttf
  • rs/moq-studio/assets/fonts/BarlowCondensed-SemiBold.ttf is excluded by !**/*.ttf
  • rs/moq-studio/assets/fonts/Inter.ttf is excluded by !**/*.ttf
  • rs/moq-studio/assets/fonts/JetBrainsMono-Medium.ttf is excluded by !**/*.ttf
  • rs/moq-studio/assets/icons/arrow-down.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/arrow-up.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/chevron-down.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/columns-2.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/copy.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/eye-off.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/eye.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/headphones.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/keyboard.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/link.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/mic.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/pencil.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/play.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/plus.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/redo-2.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/rotate-ccw.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/scissors.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/settings.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/shield.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/square.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/trash-2.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/undo-2.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/volume-2.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/volume-x.svg is excluded by !**/*.svg
  • rs/moq-studio/assets/icons/x.svg is excluded by !**/*.svg
Files selected for processing: 73

  • README.md
  • quest/m0/ux/README.md
  • quest/m0/ux/go-live.md
  • quest/m0/ux/kit.md
  • quest/m0/ux/live.md
  • quest/m0/ux/mix.md
  • quest/m0/ux/mock/add-apps.webp
  • quest/m0/ux/mock/add-cameras.webp
  • quest/m0/ux/mock/add-drop.webp
  • quest/m0/ux/mock/add-guests.webp
  • quest/m0/ux/mock/add-system.webp
  • quest/m0/ux/mock/add.html
  • quest/m0/ux/mock/firstrun-check.webp
  • quest/m0/ux/mock/firstrun-format.webp
  • quest/m0/ux/mock/firstrun-layout.webp
  • quest/m0/ux/mock/firstrun-sources.webp
  • quest/m0/ux/mock/firstrun.html
  • quest/m0/ux/mock/golive-health.webp
  • quest/m0/ux/mock/golive-library.webp
  • quest/m0/ux/mock/golive-preflight.webp
  • quest/m0/ux/mock/golive-ready.webp
  • quest/m0/ux/mock/golive.html
  • quest/m0/ux/mock/kit.css
  • quest/m0/ux/mock/kit.js
  • quest/m0/ux/mock/later-autolayout.webp
  • quest/m0/ux/mock/later-autoplace.webp
  • quest/m0/ux/mock/later-capturehealth.webp
  • quest/m0/ux/mock/later-drop.webp
  • quest/m0/ux/mock/later-dual.webp
  • quest/m0/ux/mock/later-mini.webp
  • quest/m0/ux/mock/later-multiview.webp
  • quest/m0/ux/mock/later-panels.webp
  • quest/m0/ux/mock/later-shield.webp
  • quest/m0/ux/mock/later.html
  • quest/m0/ux/mock/mix-mix.webp
  • quest/m0/ux/mock/mix-routes.webp
  • quest/m0/ux/mock/mix-strip.webp
  • quest/m0/ux/mock/mix.html
  • quest/m0/ux/mock/review-review.webp
  • quest/m0/ux/mock/review.html
  • quest/m0/ux/mock/settings-advanced.webp
  • quest/m0/ux/mock/settings-hotkeys.webp
  • quest/m0/ux/mock/settings-recording.webp
  • quest/m0/ux/mock/settings-show.webp
  • quest/m0/ux/mock/settings-stream.webp
  • quest/m0/ux/mock/settings.html
  • quest/m0/ux/mock/shell-build.webp
  • quest/m0/ux/mock/shell-compact.webp
  • quest/m0/ux/mock/shell-hover.webp
  • quest/m0/ux/mock/shell-live.webp
  • quest/m0/ux/mock/shell-offair.webp
  • quest/m0/ux/mock/shell-portrait.webp
  • quest/m0/ux/mock/shell-studio.webp
  • quest/m0/ux/mock/shell.html
  • quest/m1/multiview.md
  • rs/moq-studio/Cargo.toml
  • rs/moq-studio/assets/fonts/barlowcondensed.OFL.txt
  • rs/moq-studio/assets/fonts/inter.OFL.txt
  • rs/moq-studio/assets/fonts/jetbrainsmono.OFL.txt
  • rs/moq-studio/assets/icons/LICENSE
  • rs/moq-studio/src/fx.rs
  • rs/moq-studio/src/kit.rs
  • rs/moq-studio/src/kit/fonts.rs
  • rs/moq-studio/src/kit/gallery.rs
  • rs/moq-studio/src/kit/icon.rs
  • rs/moq-studio/src/kit/widgets.rs
  • rs/moq-studio/src/main.rs
  • rs/moq-studio/src/setup.rs
  • rs/moq-studio/src/shots.rs
  • rs/moq-studio/src/ui.rs
  • rs/moq-studio/src/ui/graph.rs
  • rs/moq-studio/src/ui/levels.rs
  • sh/kit-assets.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No blocking issues. Two regressions in today's panels are worth fixing before merge; details inline.

Reviewed changes

I reviewed the whole PR at e542acd: the kit's tokens, faces, icons and widgets, the effects rewiring, and today's panels moved onto the kit. quest context could not run because the review shell has no quest or nix binary.

  • Tokens and style (kit.rs): the kit.css colors go into egui's dark Style. ACCENT marks on air and selection, ERR marks errors only, and CUE marks cued, muted and degraded states, as the maintainer decided.
  • Faces and icons (kit/fonts.rs, kit/icon.rs, sh/kit-assets.sh): the fonts are subset. Inter's weights are set through its variable axis. Lucide SVGs are recolored white so egui_extras can tint them.
  • Widgets (kit/widgets.rs): the widgets keep no state of their own. The slider and fader sense drag only and apply the pointer position on release too. Gestures::of now counts the release frame as part of the drag.
  • Effects (fx.rs, ui.rs): Fx::follow keeps the on-air, take and verdict state each frame, and each widget draws its own effect. This replaces the Spots list, and a new Viewer wraps Panels for the dock. I checked the lock order: engine.state is released before the menu bar's health pill locks engine.health.
  • Panels: Preview and Program sit in tally frames, Scenes are pads, Items use eye buttons and drag fields, the join link opens in a Sheet, health is a pill with a popover, and the Audio gain is a kit::Slider with a readout.
  • Mock-ups and plans: --air/--sel are replaced by --accent/--err across kit.css, kit.js and the quest plans.
  • Gallery: kit and kit_sheet are added to just shots.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread rs/moq-studio/src/ui.rs Resolved
Comment thread rs/moq-studio/src/ui.rs Resolved
kixelated and others added 4 commits November 14, 2026 20:52
The gain moved to kit::Slider beside a plain readout, losing the exact
entry egui's slider had: the readout is now a kit::Number over dB. The
health pill showed Not live without relays even when a source failed;
an Unhealthy or Degraded verdict now wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pullfrog[bot]
pullfrog Bot previously approved these changes Nov 15, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. Both earlier findings are fixed.

Reviewed changes

This review covers the changes since the last Pullfrog review at e542acd.

  • Health pill: an Unhealthy or Degraded verdict now wins over Not live. A failed or stalled source, or a stopped output, now shows on the pill even when no relay is set.
  • Gain field: the gain readout is now an editable kit::Number in dB. Its parser reads -inf as silence, and clamp_existing_to_range(false) shows a gain past +12 dB from a scene file without cutting it. The slider's response and the field's response both go through gestures.of, so each edit still undoes as one step.
  • Test: a_typed_gain_is_exact checks that a +20 dB gain survives being drawn, and that typing 0 gives a gain of exactly 1.0.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

kixelated and others added 2 commits November 14, 2026 23:08
The value's button now spans the field, its label and unit drawn inside
it, so a press past a short numeral no longer misses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

This review covers the changes since the last Pullfrog review at 07ae291: the merge of main at 768ad41, which brings no net change to this PR's files, and one fix to kit::Number.

  • Whole-field kit::Number: the label and unit are now passed as DragValue::prefix/suffix atoms, and the button's interact_size is set to the field's size, so a press anywhere on the field drags or types. I checked this against egui 0.36.2's DragValue. When the value atom is mapped, it is rebuilt from RichText and loses its atom_grow. That leaves the trailing Atom::grow() as the only growing atom, so the text stays at the left of the field. In edit mode the TextEdit takes interact_size minus button_padding and fills the field exactly. As in egui's own DragValue, the label and unit are hidden while typing.
  • Test: a_number_drags_from_anywhere_on_its_field drags from 60% to 80% of a 120 px field, which is well past the numeral, and checks that the value changed.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@kixelated
kixelated merged commit 354ee7d into main Nov 15, 2026
4 checks passed
@kixelated
kixelated deleted the quest/m0/ux/kit branch November 15, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant