Fix reader and Go Live validation regressions#5
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses a cluster of regressions across the reader (Learn + Teleprompter) and Go Live flows, while stabilizing UI test interactions (notably Settings custom-select behavior) and aligning runtime contracts (stylesheets, navigation, destinations) with current behavior.
Changes:
- Introduces a shared
SettingsSelectcomponent (plus Playwright/bUnit drivers) to stabilize Settings select interactions in tests. - Refactors Go Live destination modeling toward persisted
ExternalDestinations, updates routing normalization, and adds live/idle indicator coverage. - Fixes reader-related regressions: Learn sentence-boundary context windows + RSVP layout contract, teleprompter stylesheet load timing, punctuation token attachment, and timing assertions.
Reviewed changes
Copilot reviewed 130 out of 130 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/TestData/Scripts/test-reader-timing.tps | Adds a TPS fixture for reader timing probes with speed offsets. |
| tests/TestData/Scripts/test-learn-wpm-boundary.tps | Adds a TPS fixture to probe Learn WPM boundary transitions. |
| tests/PrompterOne.Core.Tests/Workspace/LearnSettingsDefaultsTests.cs | Verifies Learn settings default construction aligns with workspace defaults. |
| tests/PrompterOne.Core.Tests/Tps/TpsRoundTripTests.cs | Adds coverage for attaching punctuation-only tokens to adjacent words. |
| tests/PrompterOne.Core.Tests/Streaming/GoLiveDestinationRoutingTests.cs | Updates tests for ExternalDestinations-driven target normalization/routing. |
| tests/PrompterOne.App.UITests/Teleprompter/TeleprompterStylesheetFlowTests.cs | Ensures teleprompter stylesheet is registered before entering teleprompter route. |
| tests/PrompterOne.App.UITests/Teleprompter/TeleprompterSettingsFlowTests.cs | Uses shared select driver and updates default reader width seed. |
| tests/PrompterOne.App.UITests/Teleprompter/TeleprompterFullFlowTests.cs | Updates rendering assertions for new card/word styling contracts. |
| tests/PrompterOne.App.UITests/Teleprompter/TeleprompterFidelityTests.cs | Adds fidelity checks for emphasis grouping, width max, and punctuation tokenization. |
| tests/PrompterOne.App.UITests/Support/SettingsSelectDriver.cs | Playwright helper for interacting with the custom Settings select. |
| tests/PrompterOne.App.UITests/Support/BrowserTestLibrarySeedData.cs | Seeds additional scripts for UI tests (timing + Learn WPM boundary). |
| tests/PrompterOne.App.UITests/Support/BrowserTestConstants.ScreenFlows.cs | Adds constants for reader timing probes and adjusts expected values. |
| tests/PrompterOne.App.UITests/Support/BrowserTestConstants.cs | Expands routes/scripts/constants for new probes and Go Live destination modeling. |
| tests/PrompterOne.App.UITests/Settings/SettingsCloudStorageFlowTests.cs | Migrates to custom select driver + value attribute assertion. |
| tests/PrompterOne.App.UITests/Scenarios/StudioWorkflowScenarioTests.cs | Migrates camera resolution selection to custom select driver. |
| tests/PrompterOne.App.UITests/Media/synthetic-media-harness.js | Adds label override support to simulate empty device labels in UI tests. |
| tests/PrompterOne.App.UITests/Media/MediaRuntimeIntegrationTests.cs | Adds integration coverage for blank browser device labels. |
| tests/PrompterOne.App.UITests/Media/BrowserTestConstants.Media.cs | Adds constants/scripts for label clearing and fabricated label assertions. |
| tests/PrompterOne.App.UITests/Learn/LearnWordLaneStabilityTests.cs | Updates Learn stability measurement to ORP anchoring + visible context gaps. |
| tests/PrompterOne.App.UITests/Learn/LearnFidelityTests.cs | Replaces “advance count” timing probe with duration-to-word measurement. |
| tests/PrompterOne.App.UITests/Learn/EditorLearnScreenFlowTests.cs | Aligns expected speed text with centralized constants. |
| tests/PrompterOne.App.UITests/GoLive/GoLiveShellSessionFlowTests.cs | Aligns navigation expectations with the new Go Live back behavior. |
| tests/PrompterOne.App.UITests/GoLive/GoLiveLiveIndicatorsFlowTests.cs | Adds Playwright coverage for idle vs recording live indicators. |
| tests/PrompterOne.App.UITests/GoLive/GoLiveFlowTests.cs | Updates Go Live flow assertions (operational toggles, back routing, provider surface). |
| tests/PrompterOne.App.UITests/Editor/EditorFloatingToolbarLayoutTests.cs | Adds a helper to reliably navigate/wait for editor input before actions. |
| tests/PrompterOne.App.Tests/Teleprompter/TeleprompterFidelityTests.cs | Adds bUnit fidelity checks: max width default, emphasis grouping, visible letter-spacing. |
| tests/PrompterOne.App.Tests/Support/TestSupport.cs | Registers StreamingPublishDescriptorResolver for tests. |
| tests/PrompterOne.App.Tests/Support/BunitSettingsSelectDriver.cs | bUnit helper for interacting with the custom Settings select. |
| tests/PrompterOne.App.Tests/Support/AppTestLibrarySeedData.cs | Seeds additional documents for app tests (timing + Learn WPM boundary). |
| tests/PrompterOne.App.Tests/Support/AppTestData.cs | Adds routes and builders for streaming destinations in app tests. |
| tests/PrompterOne.App.Tests/Settings/SettingsInteractionTests.cs | Migrates Settings tests to use custom select interactions; adds external destination rendering test. |
| tests/PrompterOne.App.Tests/Reader/ReaderStylesheetContractTests.cs | Updates stylesheet ownership expectations (teleprompter host-loaded). |
| tests/PrompterOne.App.Tests/Reader/ReaderStartupStateTests.cs | Tightens startup assertions to avoid placeholder text reliance. |
| tests/PrompterOne.App.Tests/GoLive/GoLiveSessionInteractionTests.cs | Moves tests to ExternalDestinations and covers relay-only “not live” session behavior. |
| tests/PrompterOne.App.Tests/GoLive/GoLiveLiveIndicatorsTests.cs | Adds bUnit coverage for idle vs recording indicator state. |
| tests/PrompterOne.App.Tests/GoLive/GoLiveCameraPreviewTests.cs | Updates empty-scene primary chip fallback label behavior. |
| tests/PrompterOne.App.Tests/AppShell/ScreenShellContractTests.cs | Updates shell contract expectations for teleprompter + Go Live provider cards. |
| tests/PrompterOne.App.Tests/AppShell/AppBootstrapperMediaSceneTests.cs | Verifies bootstrapper sanitizes persisted media scene labels. |
| tests/PrompterOne.App.Tests/AppShell/AppBootstrapperLearnSettingsTests.cs | Verifies bootstrapper migrates legacy Learn WPM defaults correctly. |
| src/PrompterOne.Shared/wwwroot/media/browser-media.js | Removes fabricated fallback device names; normalizes device DTOs toward empty labels. |
| src/PrompterOne.Shared/wwwroot/learn/learn-rsvp-layout.js | Moves layout contract strings out of JS and into C# parameters; updates extent-based layout. |
| src/PrompterOne.Shared/wwwroot/design/modules/settings/20-reference.css | Adjusts preview opacity styling for Settings panels. |
| src/PrompterOne.Shared/wwwroot/design/modules/reader/00-shell.css | Updates reader visuals (gradient, max width, color tuning, letter-spacing ranges). |
| src/PrompterOne.Shared/wwwroot/design/modules/30-rsvp.css | Refactors RSVP row layout to grid + extent-based context spacing. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderWordStyling.cs | Updates letter-spacing constants for visible speed styling. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderRendering.cs | Adds gradient class builder and group-level emphasis styling. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderPlayback.cs | Aligns activation/alignment sequencing to avoid visible “settling” drift. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderModels.cs | Updates chunk models to carry emphasis at group level. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderContent.cs | Implements continuous emphasis groups and adjusts emotion class fallback behavior. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderAlignment.cs | Adds deferred transition restoration for instant alignment operations. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.razor.cs | Defaults reader width to max; removes placeholder “empty card” behavior; manages gradient transition enablement. |
| src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.razor | Removes route-time teleprompter stylesheet injection; uses gradient class builder. |
| src/PrompterOne.Shared/Settings/Models/SettingsStreamingText.cs | Adds centralized labels/messages for streaming settings UI. |
| src/PrompterOne.Shared/Settings/Models/SettingsStreamingLocalTargetCatalog.cs | Defines local output targets for streaming settings cards. |
| src/PrompterOne.Shared/Settings/Models/SettingsStreamingCardIds.cs | Adds stable IDs for streaming cards (local + external). |
| src/PrompterOne.Shared/Settings/Models/SettingsNavigationText.cs | Centralizes Settings navigation labels. |
| src/PrompterOne.Shared/Settings/Components/SettingsStreamingSourcePicker.razor | Sanitizes camera source labels when displayed in Settings routing pickers. |
| src/PrompterOne.Shared/Settings/Components/SettingsStreamingPanel.razor.cs | Implements settings-side streaming panel logic around local targets + external destinations. |
| src/PrompterOne.Shared/Settings/Components/SettingsSelectOption.cs | Adds an option model for the custom Settings select component. |
| src/PrompterOne.Shared/Settings/Components/SettingsSelect.razor.css | Adds styling for the custom Settings select component. |
| src/PrompterOne.Shared/Settings/Components/SettingsSelect.razor | Implements a custom select UI used across Settings panels. |
| src/PrompterOne.Shared/Settings/Components/SettingsRecordingSection.razor | Migrates multiple <select> elements to SettingsSelect. |
| src/PrompterOne.Shared/Settings/Components/SettingsMicrophoneLevelCard.razor | Sanitizes microphone label display. |
| src/PrompterOne.Shared/Settings/Components/SettingsFilesSection.razor | Migrates <select> elements to SettingsSelect and centralizes section title text. |
| src/PrompterOne.Shared/Settings/Components/SettingsCloudSection.razor.cs | Builds options lists for custom selects; adds change handlers for CloudKit fields. |
| src/PrompterOne.Shared/Settings/Components/SettingsCloudSection.razor | Migrates cloud provider/environment/database selects to SettingsSelect. |
| src/PrompterOne.Shared/Settings/Components/SettingsCameraPreviewCard.razor | Sanitizes camera label display. |
| src/PrompterOne.Shared/Settings/Components/SettingsAppearanceSection.razor | Migrates teleprompter font select to SettingsSelect. |
| src/PrompterOne.Shared/Settings/Components/SettingsAiSection.razor | Migrates AI model selects to SettingsSelect and centralizes section title text. |
| src/PrompterOne.Shared/Services/StreamingPublishDescriptorResolver.cs | Adds resolver to map streaming profiles to provider descriptors. |
| src/PrompterOne.Shared/Services/StreamingPlatformPresentationCatalog.cs | Adds presentation metadata for streaming platforms (labels/tone/icons). |
| src/PrompterOne.Shared/Media/Services/MediaDeviceLabelSanitizer.cs | Adds reusable sanitizer for vendor/product suffixes in device labels. |
| src/PrompterOne.Shared/Media/Services/BrowserMediaDeviceService.cs | Uses sanitization and removes fabricated “Unnamed device” fallback labels. |
| src/PrompterOne.Shared/Media/Components/CameraPreviewTile.razor | Sanitizes camera labels in preview tiles. |
| src/PrompterOne.Shared/Learn/Services/LearnRsvpLayoutInterop.cs | Passes explicit contract strings and focus refs into RSVP layout JS. |
| src/PrompterOne.Shared/Learn/Services/LearnRsvpLayoutContract.cs | Centralizes RSVP layout JS contract string constants. |
| src/PrompterOne.Shared/Learn/Pages/LearnPage.razor.cs | Aligns defaults to LearnSettingsDefaults, fixes context window logic, and uses new layout interop signature. |
| src/PrompterOne.Shared/Learn/Pages/LearnPage.razor | Adds refs needed for layout interop and wraps context lanes for new layout CSS. |
| src/PrompterOne.Shared/Learn/Pages/LearnPage.Playback.cs | Persists HasCustomizedWordsPerMinute on speed changes; refactors delay computation. |
| src/PrompterOne.Shared/Learn/Pages/LearnPage.DisplayState.cs | Switches sentence range resolution to precomputed indices + bounded context windows. |
| src/PrompterOne.Shared/GoLive/Services/GoLiveOutputRequestFactory.cs | Resolves LiveKit destination from external destinations (with legacy migration). |
| src/PrompterOne.Shared/GoLive/Pages/GoLivePage.Runtime.cs | Uses session title for recording stem and removes unused constant set. |
| src/PrompterOne.Shared/GoLive/Pages/GoLivePage.razor.cs | Refactors text/constants toward GoLiveText, adds back routing via shell tracking. |
| src/PrompterOne.Shared/GoLive/Pages/GoLivePage.razor | Implements back routing + screen title test id; updates sources/preview to include live state. |
| src/PrompterOne.Shared/GoLive/Pages/GoLivePage.Descriptors.cs | Uses GoLiveText for route target labels. |
| src/PrompterOne.Shared/GoLive/Pages/GoLivePage.Bootstrap.cs | Updates diagnostics messages/ops to GoLiveText; simplifies shell metadata. |
| src/PrompterOne.Shared/GoLive/Models/GoLiveText.cs | Centralizes Go Live UI copy and operational strings. |
| src/PrompterOne.Shared/GoLive/Components/GoLiveStudioSidebar.razor.css | Adds dot styling for new tones (twitch/relay). |
| src/PrompterOne.Shared/GoLive/Components/GoLiveSourcesCard.razor.css | Improves rail flex/overflow behavior. |
| src/PrompterOne.Shared/GoLive/Components/GoLiveSourcesCard.razor | Sanitizes labels and adds explicit badge/live-state contract for sources. |
| src/PrompterOne.Shared/GoLive/Components/GoLiveProgramFeedCard.razor.css | Adjusts layout to fixed aspect ratio for program monitor/feed. |
| src/PrompterOne.Shared/GoLive/Components/GoLiveDestinationSourcePicker.razor | Sanitizes labels in Go Live destination source picker. |
| src/PrompterOne.Shared/GoLive/Components/GoLiveCameraPreviewCard.razor.css | Refines preview live dot styling and transitions. |
| src/PrompterOne.Shared/GoLive/Components/GoLiveCameraPreviewCard.razor | Adds live-state contract/test ids and sanitizes preview camera label. |
| src/PrompterOne.Shared/Contracts/UiTestIds.cs | Adds IDs for new Settings select options and Go Live back/live indicator elements. |
| src/PrompterOne.Shared/AppShell/Services/PrompterOneServiceCollectionExtensions.cs | Registers StreamingPublishDescriptorResolver in DI. |
| src/PrompterOne.Shared/AppShell/Services/GoLiveSessionState.cs | Extracts Start/Stop APIs and reimplements toggles via them. |
| src/PrompterOne.Shared/AppShell/Services/AppShellService.cs | Tracks navigation to support “Back” behavior from Go Live to prior screen. |
| src/PrompterOne.Shared/AppShell/Services/AppBootstrapper.cs | Normalizes legacy Learn settings and sanitizes persisted media scene labels. |
| src/PrompterOne.Shared/AppShell/Layout/MainLayout.razor.cs | Tracks navigation via shell service; updates Go Live shell metadata sync. |
| src/PrompterOne.Core/Workspace/Models/StudioSettings.cs | Adds ExternalDestinations to streaming settings. |
| src/PrompterOne.Core/Workspace/Models/ReaderSettingsDefaults.cs | Defaults reader text width to full width. |
| src/PrompterOne.Core/Workspace/Models/LearnSettingsDefaults.cs | Introduces centralized Learn defaults (including legacy default). |
| src/PrompterOne.Core/Workspace/Models/LearnSettings.cs | Adds HasCustomizedWordsPerMinute and uses defaults catalog. |
| src/PrompterOne.Core/Workspace/Models/GoLiveTargetCatalog.cs | Splits local target IDs/definitions from external destinations. |
| src/PrompterOne.Core/Tps/Services/TpsTokenTextRules.cs | Adds rules for detecting/attaching punctuation-only tokens. |
| src/PrompterOne.Core/Tps/Services/ScriptCompiler.cs | Attaches standalone punctuation tokens to adjacent words during compilation. |
| src/PrompterOne.Core/Streaming/Services/GoLiveDestinationRouting.cs | Normalizes routing based on local targets + persisted external destination IDs. |
| src/PrompterOne.Core/Streaming/Models/StreamingProfile.cs | Expands platform modeling (platform kind, catalog, destination helpers). |
| src/PrompterOne.Core/Streaming/Models/StreamingDestinationFieldIds.cs | Centralizes field IDs for external destination editing. |
| src/PrompterOne.App/wwwroot/index.html | Loads teleprompter stylesheet from host document head (first-paint contract). |
| docs/Features/ReaderRuntime.md | Documents updated reader contracts (stylesheet timing, explicit word emotion styling, punctuation handling). |
| docs/Features/GoLiveRuntime.md | Documents updated Go Live contracts (idle/live indicators, external destinations). |
| docs/Architecture.md | Documents shell route tracking for Go Live back navigation. |
| design/rsvp.html | Updates design reference default WPM to 250. |
| design/app.js | Updates design reference default Learn WPM to 250. |
| AGENTS.md | Updates repo rules around JS contracts, fallback device labels, reader timing, and Go Live navigation/indicators. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| previousWord.CleanText += TpsTokenTextRules.BuildStandalonePunctuationSuffix(punctuationToken); | ||
| previousWord.CharacterCount = previousWord.CleanText.Length; |
There was a problem hiding this comment.
AttachStandalonePunctuation mutates the previous word’s CleanText/CharacterCount but leaves ORPPosition and DisplayDuration computed from the pre-attachment text. This can skew ORP alignment and timing (e.g., short words like "No" becoming "No!" should cross ORP thresholds and may need duration recompute). Consider recomputing ORPPosition/DisplayDuration after appending punctuation, or folding standalone punctuation into the word before initial CompiledWord creation so all derived fields stay consistent.
| previousWord.CharacterCount = previousWord.CleanText.Length; | |
| previousWord.CharacterCount = previousWord.CleanText.Length; | |
| // Recalculate ORP position and display duration now that the word text has changed. | |
| var metadata = previousWord.Metadata; | |
| var effectiveWpm = metadata?.SpeedOverride | |
| ?? ClampWpm((int)Math.Round(DefaultWpm * (metadata?.SpeedMultiplier ?? 1f))); | |
| previousWord.ORPPosition = CalculateORP(previousWord.CleanText); | |
| previousWord.DisplayDuration = CalculateDisplayDuration(previousWord.CleanText, effectiveWpm); |
|
|
||
| - 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 |
There was a problem hiding this comment.
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.
| - 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 |
Summary
Verification