Skip to content

Commit 3649f3c

Browse files
committed
new design
1 parent 25f88b9 commit 3649f3c

115 files changed

Lines changed: 8594 additions & 1063 deletions

File tree

Some content is hidden

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

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,9 @@ Repo-specific design rules:
286286
- Keep shared package versions in `Directory.Packages.props`.
287287
- Keep the pinned SDK version in `global.json`.
288288
- Treat `new-design/index.html`, `new-design/tokens.css`, `new-design/components.css`, `new-design/styles.css`, and `new-design/app.js` as the exact design reference.
289+
- Treat every file under `new-design/` as a static design/prototype reference only. Production UI must be implemented as Blazor components in `src/PrompterLive.Shared`; do not ship raw `new-design` HTML as runtime UI.
289290
- Do not re-invent the UI when the answer should be “port the markup and classes from `new-design`”.
291+
- For parity tasks, port the full routed screen from its matching `new-design/*.html` reference, not just isolated high-signal blocks. Settings, Editor, Learn, Teleprompter, and Go Live must match the reference screen in layout and intended interaction while staying Blazor/C# owned.
290292
- Do not introduce a server host for the app runtime.
291293
- Preserve stable `data-testid` selectors on core flows because the Playwright suite depends on them.
292294
- Keep UI routes in shared route constants and keep `data-testid` names in shared UI contract constants.

docs/Architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ flowchart LR
6464
## Vertical Slice Layout
6565

6666
- `src/PrompterLive.Shared` keeps routed UI in feature slices: `AppShell`, `Diagnostics`, `Editor`, `Library`, `Learn`, `Teleprompter`, `GoLive`, `Settings`, and `Media`.
67-
- `src/PrompterLive.Core` keeps host-neutral behavior in matching domain slices: `Tps`, `Editor`, `Workspace`, `Library`, `Rsvp`, `Media`, `Streaming`, `Localization`, and `Samples`.
67+
- `src/PrompterLive.Core` keeps host-neutral behavior in matching domain slices: `Tps`, `Editor`, `Workspace`, `Library`, `Rsvp`, `Media`, `Streaming`, and `Localization`.
6868
- `tests/PrompterLive.Core.Tests`, `tests/PrompterLive.App.Tests`, and `tests/PrompterLive.App.UITests` mirror those feature slices and reserve `Support` or `Infrastructure` for shared harness code.
6969

7070
## Design And Structure Principles

docs/Features/GoLiveRuntime.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ The current page layout is a production-style studio surface:
1212
- right rail for live preview plus stream, audio, and network telemetry
1313
- lower routing decks for local outputs and cloud destinations
1414

15+
The runtime now owns real browser media outputs for the composed program scene and the current audio bus:
16+
17+
- `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
18+
- 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
19+
- OBS browser output stays browser-only and exposes the composed program audio inside an OBS Browser Source environment
20+
- LiveKit publishing uses the vendored browser SDK and publishes real composed `MediaStreamTrack` objects for video and audio
21+
- local recording uses the browser `MediaRecorder` API against the same composed program stream and prefers `showSaveFilePicker()` for real local file writing when the browser allows it, with download fallback otherwise
22+
- recording export profiles are honest browser probes only: the runtime checks `MediaRecorder.isTypeSupported()` and `MediaCapabilities.encodingInfo()` before choosing the resolved MIME type and falls back to a supported browser codec/container when the requested profile is unavailable
23+
- stream start, stop, recording start, recording stop, and source switching update the active browser output session instead of only flipping local UI state
24+
- the session timer and right-rail `Status` / `Runtime` cards are driven from live session/runtime state, not hardcoded demo telemetry
25+
26+
Relay-only destinations stay configuration surfaces:
27+
28+
- YouTube, Twitch, custom RTMP, and similar RTMP-style targets still persist credentials and routing in browser storage
29+
- these targets do not publish directly from the browser runtime; they require an external relay or ingest layer outside this standalone WASM app
30+
1531
It is separate from:
1632

1733
- `Settings`, which owns device setup such as camera selection, resolution, FPS, microphones, and audio sync
@@ -28,7 +44,9 @@ sequenceDiagram
2844
participant Routing as "GoLiveDestinationRouting"
2945
participant Studio as "StudioSettingsStore"
3046
participant Scene as "IMediaSceneService"
31-
participant Providers as "Streaming providers"
47+
participant Runtime as "GoLiveOutputRuntimeService"
48+
participant Browser as "go-live-output.js"
49+
participant Providers as "LiveKit / OBS runtime"
3250
participant Reader as "TeleprompterPage"
3351
3452
User->>Settings: Configure camera, FPS, mic, sync
@@ -43,6 +61,17 @@ sequenceDiagram
4361
User->>GoLive: Arm one or more destinations
4462
User->>GoLive: Select scene cameras per destination
4563
GoLive->>Studio: Persist output targets
64+
User->>GoLive: Start stream
65+
GoLive->>Runtime: Build request from scene snapshot + audio bus + recording export prefs
66+
Runtime->>Browser: Start or update browser output session
67+
Browser->>Browser: Compose program canvas + mix audio bus
68+
Browser->>Providers: Publish LiveKit tracks / attach OBS browser audio
69+
User->>GoLive: Start recording
70+
GoLive->>Runtime: Build request from scene snapshot + audio bus + recording export prefs
71+
Runtime->>Browser: Start MediaRecorder on the composed program session
72+
User->>GoLive: Switch source
73+
GoLive->>Runtime: Update scene-backed program request
74+
Browser->>Browser: Recompose the same program stream with the new primary source
4675
User->>Reader: Open teleprompter
4776
Reader->>Scene: Reuse same scene cameras under text
4877
```
@@ -61,10 +90,18 @@ flowchart LR
6190
Studio["StreamStudioSettings"]
6291
Routing["GoLiveDestinationRouting"]
6392
Scene["MediaSceneState"]
93+
AudioBus["AudioBusState"]
6494
CameraInterop["CameraPreviewInterop"]
95+
Runtime["GoLiveOutputRuntimeService"]
96+
RuntimeInterop["GoLiveOutputInterop"]
97+
OutputSupport["go-live-output-support.js"]
98+
BrowserRuntime["go-live-output.js"]
99+
Composer["go-live-media-compositor.js"]
65100
LiveKit["LiveKitOutputProvider"]
66101
Vdo["VdoNinjaOutputProvider"]
67102
Rtmp["RtmpStreamingOutputProvider"]
103+
LiveKitSdk["vendored livekit-client"]
104+
ObsBrowser["OBS Browser Source"]
68105
69106
Page --> SessionBar
70107
Page --> PreviewRail
@@ -75,12 +112,53 @@ flowchart LR
75112
Page --> Studio
76113
Page --> Routing
77114
Page --> Scene
115+
Page --> AudioBus
116+
Page --> Runtime
78117
Preview --> CameraInterop
118+
Runtime --> RuntimeInterop
119+
RuntimeInterop --> OutputSupport
120+
RuntimeInterop --> BrowserRuntime
121+
BrowserRuntime --> Composer
122+
BrowserRuntime --> LiveKitSdk
123+
BrowserRuntime --> ObsBrowser
79124
Page --> LiveKit
80125
Page --> Vdo
81126
Page --> Rtmp
82127
```
83128

129+
## Runtime Pipeline
130+
131+
```mermaid
132+
flowchart LR
133+
Scene["Scene cameras + transforms"]
134+
Audio["Audio bus inputs + gains + delay"]
135+
Factory["GoLiveOutputRequestFactory"]
136+
Runtime["GoLiveOutputRuntimeService"]
137+
Support["go-live-output-support.js"]
138+
Composer["go-live-media-compositor.js"]
139+
Canvas["canvas.captureStream()"]
140+
Mix["AudioContext + MediaStreamDestination"]
141+
Program["Composed MediaStream"]
142+
Recorder["MediaRecorder"]
143+
Save["File picker or download fallback"]
144+
LiveKit["LiveKit publishTrack(...)"]
145+
Obs["OBS browser audio bridge"]
146+
147+
Scene --> Factory
148+
Audio --> Factory
149+
Factory --> Runtime
150+
Runtime --> Support
151+
Runtime --> Composer
152+
Composer --> Canvas
153+
Composer --> Mix
154+
Canvas --> Program
155+
Mix --> Program
156+
Program --> Recorder
157+
Recorder --> Save
158+
Program --> LiveKit
159+
Program --> Obs
160+
```
161+
84162
## Rules
85163

86164
- `Settings` must not own live destination routing anymore.
@@ -93,6 +171,20 @@ flowchart LR
93171
- any shared `Go Live` localized copy must come from `PrompterLive.Shared.Localization.UiTextCatalog`, so supported browser cultures localize the studio surface without feature-local string copies.
94172
- each live destination must persist its own selected scene cameras, independent of the shared program feed source list
95173
- legacy streaming settings must normalize to the current included program cameras so existing browser storage keeps working
174+
- `VirtualCamera` mode normalizes to OBS armed by default, so browser sessions keep the legacy desktop-capture workflow unless the user explicitly turns OBS off
96175
- Camera source inclusion is persisted through `MediaSceneState`.
97176
- Destination credentials and endpoints are persisted only in browser storage for this standalone runtime.
177+
- LiveKit browser publishing must use the vendored SDK shipped in the repo, not a CDN copy.
178+
- OBS browser integration must stay a thin browser bridge; no server relay or backend media graph is introduced.
179+
- local recording must stay browser-local and use the same active program media session as OBS / LiveKit so record and source switching stay in sync
180+
- local recording must prefer real local file writing through the File System Access API when the browser exposes it, but must fall back to browser download instead of pretending save-to-disk is universally available
181+
- recording codec/container export must never advertise unsupported browser encoders as if they are guaranteed; the runtime must probe support and choose a real fallback profile
182+
- right-rail telemetry must never show fake packet-loss, jitter, ping, or upload metrics when the browser runtime does not actually own those measurements
183+
- remote room UI must not render fake guest personas; when no real remote guest transport exists, it may only show honest local-host state and persisted room identity
98184
- Browser acceptance verifies `Go Live` preview and source switching against deterministic synthetic cameras, not only against static DOM state.
185+
- Browser acceptance for LiveKit and OBS verifies real `getUserMedia` audio/video requests and runtime session state, not only button labels.
186+
187+
## Exception Notes
188+
189+
- `src/PrompterLive.Shared/wwwroot/media/go-live-media-compositor.js` temporarily exceeds the root `file_max_loc` limit because canvas compositing, shared device capture, and audio-bus graph ownership are tightly coupled around browser-only APIs. Scope: only the browser program graph. Removal plan: split video composition and audio graph helpers once the pipeline stabilizes and the recording/export profile surface is no longer moving.
190+
- `src/PrompterLive.Shared/wwwroot/media/go-live-output-support.js` temporarily exceeds the root `file_max_loc` limit because request normalization, codec probing, and local-save recording helpers must currently stay aligned with the browser runtime contract in one place. Scope: request normalization and browser recording/export helpers. Removal plan: split request normalization from recording/export helpers after the profile mapping rules settle.

src/PrompterLive.App/wwwroot/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ <h2 id="app-shell-error-title">Prompter.live hit a shell error</h2>
3737
</div>
3838

3939
<script src="_content/PrompterLive.Shared/media/browser-media.js"></script>
40+
<script src="_content/PrompterLive.Shared/media/go-live-media-compositor.js"></script>
41+
<script src="_content/PrompterLive.Shared/media/go-live-output-support.js"></script>
42+
<script src="_content/PrompterLive.Shared/vendor/livekit-client/v2.18.0/livekit-client.umd.js"></script>
43+
<script src="_content/PrompterLive.Shared/media/go-live-output.js"></script>
4044
<script src="_content/PrompterLive.Shared/editor/editor-source-panel.js"></script>
45+
<script src="_content/PrompterLive.Shared/teleprompter/teleprompter-reader.js"></script>
4146
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
4247
<script>
4348
const wasmDebugQueryKey = "wasm-debug";

src/PrompterLive.Core/AGENTS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ It owns TPS parsing, compilation, export, RSVP helpers, preview and workspace st
1616
- `Media/*`
1717
- `Streaming/*`
1818
- `Localization/*`
19-
- `Samples/*`
2019

2120
## Boundaries
2221

src/PrompterLive.Core/Library/Abstractions/ILibraryFolderRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace PrompterLive.Core.Abstractions;
44

55
public interface ILibraryFolderRepository
66
{
7-
Task InitializeAsync(IEnumerable<StoredLibraryFolder> seedFolders, CancellationToken cancellationToken = default);
7+
Task InitializeAsync(IEnumerable<StoredLibraryFolder> initialFolders, CancellationToken cancellationToken = default);
88

99
Task<IReadOnlyList<StoredLibraryFolder>> ListAsync(CancellationToken cancellationToken = default);
1010

src/PrompterLive.Core/Library/Abstractions/IScriptRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace PrompterLive.Core.Abstractions;
44

55
public interface IScriptRepository
66
{
7-
Task InitializeAsync(IEnumerable<StoredScriptDocument> seedDocuments, CancellationToken cancellationToken = default);
7+
Task InitializeAsync(IEnumerable<StoredScriptDocument> initialDocuments, CancellationToken cancellationToken = default);
88

99
Task<IReadOnlyList<StoredScriptSummary>> ListAsync(CancellationToken cancellationToken = default);
1010

src/PrompterLive.Core/PrompterLive.Core.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<ItemGroup>
4-
<EmbeddedResource Include="Samples/**/*.tps" />
5-
</ItemGroup>
6-
73
<ItemGroup>
84
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
95
</ItemGroup>

src/PrompterLive.Core/Samples/SampleLibraryFolderCatalog.cs

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/PrompterLive.Core/Samples/SampleScriptCatalog.cs

Lines changed: 0 additions & 84 deletions
This file was deleted.

0 commit comments

Comments
 (0)