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
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ Local `AGENTS.md` files may tighten these values, but they must not loosen them
- String literals are forbidden in implementation code. Declare them once as named constants, enums, configuration entries, or dedicated value objects, then reuse those symbols.
- Avoid magic literals. Extract shared values into constants, enums, configuration, or dedicated types.
- URLs, storage keys, JS interop identifiers, route fragments, and user-visible fallback strings are implementation literals too. They MUST live behind named constants or localized catalogs.
- JS bridges must not invent repo-owned CSS custom-property names, DOM selectors, or feature data-attribute names inside `.js` files when Blazor/C# can own and pass those contract strings explicitly.
- Fallback device names, fallback device labels, and other invented media-device placeholders are forbidden in runtime and tests; use real device metadata when it exists, otherwise keep the field empty or assert on explicit no-device state instead of fabricating names.
- Design boundaries so real behaviour can be tested through public interfaces.
- The repo-root `.editorconfig` is the source of truth for formatting, naming, style, and analyzer severity. Use nested `.editorconfig` files only when they clearly serve a subtree-specific purpose.

Expand All @@ -304,10 +306,27 @@ Repo-specific design rules:
- Repo-owned manifests, scripts, workflows, and project files that track third-party runtime JavaScript SDKs MUST point to concrete GitHub release versions and asset URLs, never floating references.
- Any vendored runtime JavaScript SDK that tracks an upstream GitHub repo MUST have an automated watcher job that checks new GitHub releases and opens a repo issue describing the required update when a newer release appears.
- Teleprompter TPS speed modifiers MUST affect both playback timing and subtle word- or phrase-level letter spacing, so slower spans open up slightly and faster spans tighten slightly without hurting readability.
- Teleprompter default reader width MUST start at the maximum readable width from the design unless the user explicitly narrows it; shipping a visibly narrower default is a regression.
- Teleprompter speed styling MUST produce a visible but tasteful letter-spacing or kerning change: slower text opens up slightly and faster text tightens slightly, not a no-op.
- Teleprompter reader word styling MUST mirror TPS/editor inline semantics: explicit inline TPS tags control per-word emphasis and color, while section or block emotion sets card context and must not recolor every reader word.
- Teleprompter underline or highlight treatments that span a phrase or block MUST render as one continuous block-level treatment; separate per-word underlines inside the same phrase are forbidden.
- Teleprompter read-state styling MUST mute phrase-level underline or highlight accents once the emphasized text has been read; bright lingering underline accents on already-read text are forbidden.
- Teleprompter reader text MUST appear on the focal guide immediately when a word or block becomes active; visible post-appearance drift or settling onto the guide is forbidden.
- Teleprompter route styles MUST be present on the first paint; a flash of unstyled or late-styled reader UI during route entry is a regression.
- Teleprompter block transitions MUST stay visually consistent: outgoing cards move upward and incoming cards rise from below in the same direction every time; alternating up/down travel is forbidden, and extra settling, bounce, or intermediate card states are forbidden.
- Teleprompter focus treatment MUST stay visually calm: the active focus word may be emphasized, but surrounding text should be gently dimmed instead of creating a bright moving blot, fake box, or attention-grabbing patch that flies up and down.
- Teleprompter emotion styling may tint the surface or accents, but reader text itself MUST stay easy to read and must not become harsh, over-bright, or saturated enough to hurt readability.
- Learn and Teleprompter playback timing MUST align with real word-by-word progression in the browser: WPM, speed modifiers, and word counting must match the emitted words, and timing work is not done until a browser-level word-sequence check proves it.
- Reader and Learn tokenization MUST treat punctuation-only tokens such as commas, periods, and dashes as punctuation attached to nearby words or pauses, never as standalone counted words.
- App-shell logo navigation MUST always lead to the main home/library screen; it must not deep-link into Go Live, Teleprompter, or another feature-specific route.
- Learn rehearsal speed MUST default to about 250 WPM and stay user-adjustable upward from that baseline; shipping a 300 WPM startup default is too aggressive.
- Go Live `ON AIR` badges and preview live dots MUST appear only while recording or streaming is actually active; idle selected or armed sources must stay visually non-live.
- Go Live chrome MUST stay operational and generic; do not surface the loaded script title or script preview subtitle in the Go Live header/session bar just because a script is open.
- Go Live back navigation MUST return to the actual previous in-app screen when known, and only fall back to library when there is no valid in-app return target; it must never hardcode teleprompter as the back target.
- Learn and Teleprompter are separate screens with separate style ownership; do not bundle RSVP and teleprompter reader feature styles into one shared screen stylesheet or let one page inherit the other page's visual treatment.
- User preferences persistence MUST sit behind a platform-agnostic user-settings abstraction, with browser storage implemented via local storage and room for other platform-specific implementations; theme, teleprompter layout preferences, camera/scene preferences, and similar saved settings belong there instead of ad-hoc feature stores.
- Streaming destination/platform configuration MUST be user-defined and persisted in settings; Settings and Go Live must not ship hardcoded platform instances, seeded destination accounts, or fixed fake provider rows beyond real runtime capabilities.
- Runtime screens must not keep inline seeded operational data, fake demo rows, or screen-local platform/source presets in page/component code; reusable labels and presets belong in shared contracts or catalogs, while rendered rows must come from persisted settings, workspace state, or live session state.
- Build quality gates must stay green under `-warnaserror`.
- GitHub Pages is the expected CI publish target for the standalone WebAssembly app; publish automation must keep the app browser-only and Pages-compatible.
- GitHub Actions MUST keep separate, clearly named workflows for pull-request validation and release automation; vague workflow names are forbidden.
Expand Down Expand Up @@ -359,6 +378,7 @@ Ask first:
### Dislikes

- backend creep in the standalone runtime
- hardcoded fallback reader/test fixtures such as inline `Ready` chunks, fake word models, or synthetic UI state embedded directly in tests when the same behavior can be exercised through shared script fixtures, builders, or production-owned constants
- agent-started local servers taking shared user ports or using ports outside the reserved `5050-5070` agent range
- brittle selectors without `data-testid`
- progress updates that imply a fix is done before there is concrete implementation and verification evidence; keep status factual and let the user verify final behavior personally
Expand All @@ -368,8 +388,14 @@ Ask first:
- made-up About/team content or stale attribution; About must point to real Managed Code ownership and official links
- any visible typing latency in the editor; plain input must feel immediate with no observable delay
- teleprompter controls that fade so much they become hard to see during real reading
- teleprompter starting with a narrowed text width instead of the design-max default
- teleprompter paragraph repositioning, line hopping, or per-word vertical transform updates that make the text jump; `design/teleprompter.html` motion is the required reference, with steady bottom-to-top movement and no extra animation layers beyond the reference
- teleprompter words or blocks appearing away from the focus line and only then drifting onto it; activation must look immediate
- teleprompter section changes that introduce odd transition motion instead of the straight reference direction
- any green teleprompter shell or background treatment; Teleprompter must stay on its dark reader palette and use emotion only for accents, not green screen-wide fills
- fragmented per-word underline styling where the intended emphasis should read as one continuous block
- punctuation showing up or being counted as standalone words in Learn or Teleprompter flows
- app logo clicks landing on a feature route instead of the main home/library screen
- Learn and Teleprompter style boundaries bleeding through a shared feature stylesheet; their visuals must stay isolated by page-owned style manifests
- Learn RSVP compositions that shift when shorter or longer words render; changing word length must not move the overall RSVP component or its anchored centerline
- teleprompter camera starting enabled by default; default reader startup should keep the camera off until the user explicitly enables it
Expand Down
4 changes: 2 additions & 2 deletions design/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function updateAppHeader(screenId) {
break;
case 'rsvp':
center.innerHTML = `${backBtn}<span class="top-bar-title">Product Launch</span><span style="color:var(--text-4);font-size:12px">Intro / Opening Block</span>`;
right.innerHTML = `<span class="top-bar-title" style="font-size:13px">300 WPM</span>${goLiveBtn}`;
right.innerHTML = `<span class="top-bar-title" style="font-size:13px">250 WPM</span>${goLiveBtn}`;
break;
case 'teleprompter':
center.innerHTML = `${backBtn}<span class="top-bar-title">Product Launch</span><span style="color:var(--text-4);font-size:12px" id="rd-header-segment">Intro · Opening Block</span>`;
Expand All @@ -93,7 +93,7 @@ function updateAppHeader(screenId) {
// RSVP (Learn mode — simple word-by-word)
// ============================================

let rsvpSpeed = 300;
let rsvpSpeed = 250;
let rsvpPlaying = true;

function changeRsvpSpeed(delta) {
Expand Down
4 changes: 2 additions & 2 deletions design/rsvp.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
<div class="rsvp-bottom">
<div class="rsvp-speed">
<button class="rsvp-btn" onclick="changeRsvpSpeed(-10)"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
<div class="rsvp-speed-num"><span id="rsvp-speed">300</span><small>WPM</small></div>
<div class="rsvp-speed-num"><span id="rsvp-speed">250</span><small>WPM</small></div>
<button class="rsvp-btn" onclick="changeRsvpSpeed(10)"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
</div>
<div class="rsvp-play-row">
Expand All @@ -108,4 +108,4 @@

<script src="app.js"></script>
</body>
</html>
</html>
1 change: 1 addition & 0 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ flowchart LR
- `BrowserThemeService` is the first concrete remote consumer and keeps shell appearance aligned across tabs without reload.
- `GoLiveSessionService` is the current publisher and consumer for active `Go Live` session snapshots, including startup catch-up requests.
- `MainLayout` consumes `GoLiveSessionService` and renders the global shell `Go Live` status for every screen.
- `AppShellService` owns the current in-app route and the last valid non-`Go Live` return target so the `Go Live` back control can return to the actual previous screen instead of a hardcoded reader route.
- The contract must stay message-based; tabs do not share live .NET memory.

## Library Contracts
Expand Down
17 changes: 10 additions & 7 deletions docs/Features/GoLiveRuntime.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@
The current page layout is a production-style studio surface:

- the routed `Go Live` page owns its own studio chrome and suppresses the shared app header while the route is active, so `design/golive.html` remains the only topbar on that screen
- top session bar follows `design/golive.html`: back to Read, script title + session badge, centered session timer, panel toggles, mode switch, settings shortcut, REC, and the main stream action on the far right
- top session bar follows `design/golive.html`: back to Library, script title + session badge, centered session timer, panel toggles, mode switch, settings shortcut, REC, and the main stream action on the far right
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This section says the Go Live top bar goes “back to Library”, but the implementation now tracks the previous in-app route and returns there when valid (falling back to Library only when unknown). Updating this description would keep the doc aligned with the runtime navigation contract.

Suggested change
- top session bar follows `design/golive.html`: back to Library, script title + session badge, centered session timer, panel toggles, mode switch, settings shortcut, REC, and the main stream action on the far right
- top session bar follows `design/golive.html`: back to the previous in-app location when known (falling back to Library when unknown), script title + session badge, centered session timer, panel toggles, mode switch, settings shortcut, REC, and the main stream action on the far right

Copilot uses AI. Check for mistakes.
- the studio shell follows the same three-column grid as `design/golive.html`: a compact left input rail, a dominant center canvas/program stage, and a dedicated right operational rail
- left input rail for scene cameras, add-camera action, utility sources, and microphone route status
- center program stage for the selected program source and current script/session state
- scene controls bar for scene chips, layout controls, transitions, and the primary `Take To Air` action
- right rail for the current live preview plus compact stream, audio, and room/runtime panels
- source-card `ON AIR` badges and the preview red live dot only turn on when recording or streaming is actually active; idle routing and armed sources stay visually non-live
- full-program mode collapses both side rails so the center canvas follows the design's focused monitor state
- destination cards that arm OBS, recording, LiveKit, and YouTube from persisted settings instead of editing credentials inline
- destination cards are built from persisted local outputs plus a dynamic browser-stored `ExternalDestinations` list; seeded fake provider rows are forbidden

The runtime now owns real browser media outputs for the composed program scene and the current audio bus:

- `Go Live` auto-seeds the first available browser camera into the scene when the scene is empty and the browser exposes a real camera list
- the center program stage always shows the currently selected scene camera, while the right preview rail shows the currently on-air camera until the operator takes the selected source live
- the center program stage always shows the currently selected scene camera, while the right preview rail shows the current program source and only marks it live once recording or streaming is active
- `Go Live` builds one browser-side program stream from the scene camera cards by drawing the selected primary camera full-frame and then layering additional included cameras as positioned overlays on a canvas
- the scene `AudioBus` is mixed into one program audio track through `AudioContext`, delay, and gain nodes before the final program stream is published or recorded
- OBS browser output stays browser-only and exposes the composed program audio inside an OBS Browser Source environment
Expand All @@ -34,7 +35,7 @@ Relay-only destinations stay configuration surfaces:
- YouTube, Twitch, custom RTMP, and similar RTMP-style targets still persist credentials and routing in browser storage
- `Go Live` only exposes quick arm/disarm toggles and readiness summaries for those targets
- detailed destination credentials, ingest URLs, and provider-specific configuration live in `Settings`
- these targets do not publish directly from the browser runtime; they require an external relay or ingest layer outside this standalone WASM app
- these targets do not publish directly from the browser runtime; they require an external relay or ingest layer outside this standalone WASM app, and `Go Live` must not mark the session live unless a direct browser live output actually starts

It is separate from:

Expand Down Expand Up @@ -62,8 +63,8 @@ sequenceDiagram
User->>Settings: Configure camera, FPS, mic, sync
Settings->>Studio: Persist device preferences
Settings->>Scene: Persist scene cameras and audio bus
User->>Settings: Configure LiveKit / YouTube / recording settings
Settings->>Studio: Persist provider credentials and destinations
User->>Settings: Add external destinations and configure provider credentials
Settings->>Studio: Persist local outputs and external destination list
User->>GoLive: Open Go Live
GoLive->>Studio: Load live routing settings
GoLive->>Scene: Load current scene sources
Expand Down Expand Up @@ -181,14 +182,16 @@ flowchart LR
- `Settings` must expose a visible CTA into `Go Live` so device setup and live routing stay discoverable as separate flows.
- the shared header shell must keep `Go Live` reachable from every non-`Go Live` routed page because it is a primary studio action
- `Go Live` may arm multiple destinations at the same time.
- hardcoded destination instances are forbidden; the external destination list must come from persisted browser settings and may contain zero, one, or many platform entries
- `Go Live` must reuse the browser-composed scene and not invent a separate media graph.
- `Go Live` must auto-seed the first available browser camera into the scene when the scene is empty and devices are available.
- `Go Live` must show the selected program source in the center monitor and the currently on-air source in the right preview rail until the operator explicitly takes the selected source live.
- `Go Live` must not render `ON AIR` source badges or red preview live dots while the session is idle; those indicators only represent active recording or streaming.
- `Go Live` must show a stable empty preview state instead of mounting camera interop when the current scene has no cameras.
- the routed `Go Live` page must not stack the shared app header above the studio topbar; the studio topbar is the only route chrome on that screen
- any shared `Go Live` localized copy must come from `PrompterOne.Shared.Localization.UiTextCatalog`, so supported browser cultures localize the studio surface without feature-local string copies.
- quick destination cards must only expose honest readiness summaries and arm/disarm toggles; fake in-page credential editors are forbidden on the operational studio surface
- legacy streaming settings must normalize to the current included program cameras so existing browser storage keeps working
- legacy streaming settings must normalize to the current included program cameras and migrate legacy provider fields into the canonical external destination list so existing browser storage keeps working
- `VirtualCamera` mode normalizes to OBS armed by default, so browser sessions keep the legacy desktop-capture workflow unless the user explicitly turns OBS off
- Camera source inclusion is persisted through `MediaSceneState`.
- Destination credentials and endpoints are persisted only in browser storage for this standalone runtime.
Expand Down
Loading
Loading