Skip to content

Commit 28d9d4d

Browse files
committed
fxies
1 parent a03bfa4 commit 28d9d4d

File tree

81 files changed

+3007
-1380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+3007
-1380
lines changed

AGENTS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ Local `AGENTS.md` files may tighten these values, but they must not loosen them
278278
- String literals are forbidden in implementation code. Declare them once as named constants, enums, configuration entries, or dedicated value objects, then reuse those symbols.
279279
- Avoid magic literals. Extract shared values into constants, enums, configuration, or dedicated types.
280280
- 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.
281+
- 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.
282+
- 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.
281283
- Design boundaries so real behaviour can be tested through public interfaces.
282284
- 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.
283285

@@ -309,11 +311,19 @@ Repo-specific design rules:
309311
- 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.
310312
- 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.
311313
- 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.
314+
- 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.
312315
- 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.
316+
- 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.
317+
- 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.
318+
- 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.
313319
- 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.
314320
- 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.
321+
- 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.
322+
- 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.
315323
- 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.
316324
- 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.
325+
- 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.
326+
- 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.
317327
- Build quality gates must stay green under `-warnaserror`.
318328
- GitHub Pages is the expected CI publish target for the standalone WebAssembly app; publish automation must keep the app browser-only and Pages-compatible.
319329
- GitHub Actions MUST keep separate, clearly named workflows for pull-request validation and release automation; vague workflow names are forbidden.
@@ -365,6 +375,7 @@ Ask first:
365375
### Dislikes
366376

367377
- backend creep in the standalone runtime
378+
- 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
368379
- agent-started local servers taking shared user ports or using ports outside the reserved `5050-5070` agent range
369380
- brittle selectors without `data-testid`
370381
- 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

design/app.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function updateAppHeader(screenId) {
7373
break;
7474
case 'rsvp':
7575
center.innerHTML = `${backBtn}<span class="top-bar-title">Product Launch</span><span style="color:var(--text-4);font-size:12px">Intro / Opening Block</span>`;
76-
right.innerHTML = `<span class="top-bar-title" style="font-size:13px">300 WPM</span>${goLiveBtn}`;
76+
right.innerHTML = `<span class="top-bar-title" style="font-size:13px">250 WPM</span>${goLiveBtn}`;
7777
break;
7878
case 'teleprompter':
7979
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>`;
@@ -93,7 +93,7 @@ function updateAppHeader(screenId) {
9393
// RSVP (Learn mode — simple word-by-word)
9494
// ============================================
9595

96-
let rsvpSpeed = 300;
96+
let rsvpSpeed = 250;
9797
let rsvpPlaying = true;
9898

9999
function changeRsvpSpeed(delta) {

design/rsvp.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
<div class="rsvp-bottom">
8787
<div class="rsvp-speed">
8888
<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>
89-
<div class="rsvp-speed-num"><span id="rsvp-speed">300</span><small>WPM</small></div>
89+
<div class="rsvp-speed-num"><span id="rsvp-speed">250</span><small>WPM</small></div>
9090
<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>
9191
</div>
9292
<div class="rsvp-play-row">
@@ -108,4 +108,4 @@
108108

109109
<script src="app.js"></script>
110110
</body>
111-
</html>
111+
</html>

docs/Features/GoLiveRuntime.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@
77
The current page layout is a production-style studio surface:
88

99
- 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
10-
- 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
10+
- 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
1111
- 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
1212
- left input rail for scene cameras, add-camera action, utility sources, and microphone route status
1313
- center program stage for the selected program source and current script/session state
1414
- scene controls bar for scene chips, layout controls, transitions, and the primary `Take To Air` action
1515
- right rail for the current live preview plus compact stream, audio, and room/runtime panels
16+
- 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
1617
- full-program mode collapses both side rails so the center canvas follows the design's focused monitor state
17-
- destination cards that arm OBS, recording, LiveKit, and YouTube from persisted settings instead of editing credentials inline
18+
- destination cards are built from persisted local outputs plus a dynamic browser-stored `ExternalDestinations` list; seeded fake provider rows are forbidden
1819

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

2122
- `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
22-
- 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
23+
- 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
2324
- `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
2425
- 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
2526
- OBS browser output stays browser-only and exposes the composed program audio inside an OBS Browser Source environment
@@ -34,7 +35,7 @@ Relay-only destinations stay configuration surfaces:
3435
- YouTube, Twitch, custom RTMP, and similar RTMP-style targets still persist credentials and routing in browser storage
3536
- `Go Live` only exposes quick arm/disarm toggles and readiness summaries for those targets
3637
- detailed destination credentials, ingest URLs, and provider-specific configuration live in `Settings`
37-
- these targets do not publish directly from the browser runtime; they require an external relay or ingest layer outside this standalone WASM app
38+
- 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
3839

3940
It is separate from:
4041

@@ -62,8 +63,8 @@ sequenceDiagram
6263
User->>Settings: Configure camera, FPS, mic, sync
6364
Settings->>Studio: Persist device preferences
6465
Settings->>Scene: Persist scene cameras and audio bus
65-
User->>Settings: Configure LiveKit / YouTube / recording settings
66-
Settings->>Studio: Persist provider credentials and destinations
66+
User->>Settings: Add external destinations and configure provider credentials
67+
Settings->>Studio: Persist local outputs and external destination list
6768
User->>GoLive: Open Go Live
6869
GoLive->>Studio: Load live routing settings
6970
GoLive->>Scene: Load current scene sources
@@ -181,14 +182,16 @@ flowchart LR
181182
- `Settings` must expose a visible CTA into `Go Live` so device setup and live routing stay discoverable as separate flows.
182183
- the shared header shell must keep `Go Live` reachable from every non-`Go Live` routed page because it is a primary studio action
183184
- `Go Live` may arm multiple destinations at the same time.
185+
- hardcoded destination instances are forbidden; the external destination list must come from persisted browser settings and may contain zero, one, or many platform entries
184186
- `Go Live` must reuse the browser-composed scene and not invent a separate media graph.
185187
- `Go Live` must auto-seed the first available browser camera into the scene when the scene is empty and devices are available.
186188
- `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.
189+
- `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.
187190
- `Go Live` must show a stable empty preview state instead of mounting camera interop when the current scene has no cameras.
188191
- 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
189192
- 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.
190193
- 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
191-
- legacy streaming settings must normalize to the current included program cameras so existing browser storage keeps working
194+
- 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
192195
- `VirtualCamera` mode normalizes to OBS armed by default, so browser sessions keep the legacy desktop-capture workflow unless the user explicitly turns OBS off
193196
- Camera source inclusion is persisted through `MediaSceneState`.
194197
- Destination credentials and endpoints are persisted only in browser storage for this standalone runtime.

docs/Features/ReaderRuntime.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The important contracts are:
1616
- Teleprompter pre-centers the next card before it slides in, so block transitions do not jump at the focal line.
1717
- Teleprompter block transitions always move in one upward direction: the outgoing card exits up and the incoming card rises from below.
1818
- Teleprompter controls stay readable at rest; they must not fade until they become unusable.
19+
- Teleprompter route styles must already be present on first paint from the app host document; a late style attach during route entry is a regression.
1920
- Teleprompter user-adjusted font size, text width, focal position, and camera preference survive reloads through the shared user-settings contract.
2021

2122
## Flow
@@ -54,10 +55,13 @@ flowchart LR
5455
- `teleprompter` forwards TPS pronunciation metadata to word-level `title` / `data-pronunciation` attributes.
5556
- `teleprompter` derives word-level pacing from the compiled TPS duration and carries effective WPM into the DOM for testable parity.
5657
- `teleprompter` preserves TPS front-matter speed offsets and `[normal]` resets when rebuilding reader blocks, so relative speed tags keep both their timing math and subtle word-level spacing cues.
58+
- `teleprompter` applies TPS inline emotion colors only when a word is explicitly tagged; untagged reader words must stay on the base reader palette instead of inheriting an implicit `neutral` word class.
5759
- `teleprompter` keeps TPS inline colors visible even when a phrase group is active or the active word is highlighted.
60+
- `teleprompter` keeps the active focus word calm: the active word may be brighter than its neighbors, but upcoming and read words stay gently dimmed and active-word glow stays restrained enough to avoid a bright moving patch.
5861
- `teleprompter` persists font scale, text width, focal point, and camera auto-start changes through `IUserSettingsStore` and restores them from stored `ReaderSettings` during bootstrap.
5962
- `teleprompter` prepositions the next card below the focal line before activation, so forward and backward block jumps both animate upward instead of alternating direction.
6063
- `teleprompter` uses one smooth paragraph realignment while words advance inside a card, but the first word of a newly entered card is already pre-centered so block changes do not trigger a second correction pass.
64+
- `teleprompter` loads its feature stylesheet from the initial host `<head>` instead of relying on route-time `HeadContent`, so direct opens and route transitions share the same first-paint styling.
6165

6266
## Verification
6367

@@ -73,5 +77,7 @@ flowchart LR
7377
- Playwright verifies there is no teleprompter overlay camera box and that phrase groups do not overflow.
7478
- Playwright verifies the teleprompter camera button attaches and detaches a real synthetic `MediaStream` on the background video layer.
7579
- Playwright verifies the full `Product Launch` teleprompter scenario, including visible controls, TPS formatting parity, screenshot artifacts, and aligned post-transition playback.
80+
- Playwright verifies a dedicated reader-timing probe for both `learn` and `teleprompter`, recording emitted words in the browser and checking that sequence order and elapsed delays match the rendered timing contract word by word.
81+
- Playwright verifies the teleprompter stylesheet is already registered in `document.styleSheets` before the app navigates into the teleprompter route.
7682
- Playwright verifies custom TPS speed offsets change computed teleprompter `letter-spacing` while `[normal]` words reset back to neutral spacing and timing.
7783
- Playwright verifies teleprompter width and focal settings survive a real browser reload and that backward block jumps keep the outgoing card on the upward exit path during the transition.

src/PrompterOne.App/wwwroot/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<link rel="stylesheet" href="_content/PrompterOne.Shared/design/tokens.css" />
1414
<link rel="stylesheet" href="_content/PrompterOne.Shared/design/components.css" />
1515
<link rel="stylesheet" href="_content/PrompterOne.Shared/design/styles.css" />
16+
<link rel="stylesheet" href="_content/PrompterOne.Shared/design/teleprompter.css" />
1617
<link rel="stylesheet" href="_content/PrompterOne.Shared/app.css" />
1718
<link rel="stylesheet" href="PrompterOne.App.styles.css" />
1819
</head>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace PrompterOne.Core.Models.Streaming;
2+
3+
public static class StreamingDestinationFieldIds
4+
{
5+
public const string Name = "name";
6+
public const string PublishUrl = "publish-url";
7+
public const string RoomName = "room-name";
8+
public const string RtmpUrl = "rtmp-url";
9+
public const string ServerUrl = "server-url";
10+
public const string StreamKey = "stream-key";
11+
public const string Token = "token";
12+
}

0 commit comments

Comments
 (0)