Skip to content

Commit fc39b1b

Browse files
committed
records in teleprompter
1 parent f612506 commit fc39b1b

43 files changed

Lines changed: 1444 additions & 37 deletions

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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ Rule format:
8484
- Teleprompter reader text alignment must expose explicit left, center, and right modes, default to left alignment, and keep the left-aligned mode optically centered by offsetting the text mass away from a visibly left-heavy block.
8585
- Learn RSVP focus layout is a hard stability contract: the ORP focus letter, focus row, and next-phrase region must keep fixed screen positions and fixed reserved heights while words and phrase text change; next-phrase wrapping must clamp inside its reserved area instead of moving the focus word vertically.
8686
- Teleprompter `Read Width` must map honestly to the visible reading lane: at `100%` it must not keep extra internal container padding or shrink-to-content gutters that make the text block visibly narrower than the width guides.
87+
- Teleprompter reader media controls must keep background media and recording capture as separate concepts: background camera or video-file playback is stage scenery, while recording explicitly captures microphone-only or camera-plus-microphone from user-selected devices that default to Settings selections.
88+
- Teleprompter reader recording must show a live, unobtrusive audio-level indicator while armed or recording so the user can confirm microphone input is present before and during rehearsal capture.
89+
- Browser media settings must expose supported audio/video capture options used by the app and LiveKit path, including device selection and audio processing controls such as echo cancellation, noise suppression, automatic gain control, voice isolation when available, channel count, sample rate, and sample size where the browser/runtime supports them.
8790
- The TPS editor migration to Monaco must be complete: syntax coloring, IntelliSense/autocomplete, hover or inline tooltip help, decorations, and TPS authoring feedback must be Monaco-native instead of split across legacy overlay or hidden-textarea behavior.
8891
- TPS authoring completeness must be checked against the upstream `managedcode/TPS` README, not only the currently shipped editor menus, so new editor support stays aligned with the full spec for emotions, delivery, pauses, speed, pronunciation, and related cues.
8992
- Editor TPS command surfaces must expose the full currently supported TPS authoring set consistently across top toolbar menus, floating-toolbar menus, and Monaco assistance; do not ship partial or differently grouped command taxonomies between those surfaces.

src/PrompterOne.Core/Workspace/Models/StudioSettings.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ public sealed record MicrophoneStudioSettings(
3636
string? DefaultMicrophoneId = null,
3737
int InputLevelPercent = 65,
3838
bool NoiseSuppression = true,
39-
bool EchoCancellation = true);
39+
bool EchoCancellation = true,
40+
bool AutoGainControl = true,
41+
bool VoiceIsolation = false,
42+
int? ChannelCount = null,
43+
int? SampleRate = null,
44+
int? SampleSize = null);
4045

4146
public sealed record StreamStudioSettings(
4247
ProgramCaptureProfile? ProgramCapture = null,

src/PrompterOne.Shared/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
- Dropdown item content across `PrompterOne.Shared` must align from the left edge as one readable cluster; do not push tags, shortcuts, or meta copy to a fake right column inside menu rows.
5959
- Editor Cards view must behave like a real card board: script cards should support drag-and-drop reordering, expose clear drag handles, and persist the resulting source order instead of relying only on up/down buttons.
6060
- Editor Cards view must be theme-aware across light and dark themes; do not hardcode white card/page backgrounds or dark-only text that bypasses shared app color tokens.
61+
- Editor header export actions must use one clear `Export` entry point with format choices inside it; do not render separate top-level buttons for each export format because they read as duplicated actions and crowd the shell chrome.
6162
- Tooltip surfaces across the app must feel intentional and premium: compact, aligned, clearly separated from the background, and positioned so they do not clip, overlap, or awkwardly fight the control that owns them.
6263
- Editor header toolbar tooltips must reveal more slowly than dropdown intent so quick menu interactions open the dropdown without competing hover tooltip paint.
6364
- Repeated menus, dropdowns, tooltips, badges, icon rows, image wrappers, and similar visual chrome must be standardized as reusable Blazor components with owning styles; routed pages and catalog files must compose those components instead of embedding bespoke markup or inline visual logic.

src/PrompterOne.Shared/AppShell/Components/MainLayoutHeader.razor

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -193,27 +193,50 @@
193193

194194
@if (ShowSaveFileAction)
195195
{
196-
<button type="button"
197-
class="btn-ghost app-header-native-export"
198-
@onclick="HandleSaveFileClickAsync"
199-
data-test="@UiTestIds.Header.EditorSaveFile">
200-
<UiIcon Kind="UiIconKind.ExportArrow" Size="15" />
201-
<span class="app-header-action-label">@SaveFileLabel</span>
202-
</button>
203-
<button type="button"
204-
class="btn-ghost app-header-format-export"
205-
@onclick="HandleExportMarkdownClickAsync"
206-
data-test="@UiTestIds.Header.EditorExportMarkdown">
207-
<UiIcon Kind="UiIconKind.DocumentLines" Size="15" />
208-
<span class="app-header-action-label">@ExportMarkdownLabel</span>
209-
</button>
210-
<button type="button"
211-
class="btn-ghost app-header-format-export"
212-
@onclick="HandleExportPlainTextClickAsync"
213-
data-test="@UiTestIds.Header.EditorExportPlainText">
214-
<UiIcon Kind="UiIconKind.TextLines" Size="15" />
215-
<span class="app-header-action-label">@ExportPlainTextLabel</span>
216-
</button>
196+
<div class="app-header-export"
197+
data-test="@UiTestIds.Header.EditorExportMenu">
198+
<button type="button"
199+
class="btn-ghost app-header-export-trigger"
200+
aria-haspopup="menu"
201+
aria-expanded="@(_isExportMenuOpen ? EnabledStateValue : DisabledStateValue)"
202+
@onclick="ToggleExportMenu"
203+
data-test="@UiTestIds.Header.EditorSaveFile">
204+
<UiIcon Kind="UiIconKind.ExportArrow" Size="15" />
205+
<span class="app-header-action-label">@SaveFileLabel</span>
206+
<UiIcon Kind="UiIconKind.ChevronDown" Size="13" StrokeWidth="2.2m" />
207+
</button>
208+
209+
@if (_isExportMenuOpen)
210+
{
211+
<div class="app-header-export-menu"
212+
role="menu">
213+
<button type="button"
214+
class="app-header-export-option"
215+
role="menuitem"
216+
@onclick="HandleSaveFileClickAsync"
217+
data-test="@UiTestIds.Header.EditorExportNative">
218+
<UiIcon Kind="UiIconKind.ExportArrow" Size="15" />
219+
<span>@ExportNativeLabel</span>
220+
</button>
221+
<button type="button"
222+
class="app-header-export-option"
223+
role="menuitem"
224+
@onclick="HandleExportMarkdownClickAsync"
225+
data-test="@UiTestIds.Header.EditorExportMarkdown">
226+
<UiIcon Kind="UiIconKind.DocumentLines" Size="15" />
227+
<span>@ExportMarkdownLabel</span>
228+
</button>
229+
<button type="button"
230+
class="app-header-export-option"
231+
role="menuitem"
232+
@onclick="HandleExportPlainTextClickAsync"
233+
data-test="@UiTestIds.Header.EditorExportPlainText">
234+
<UiIcon Kind="UiIconKind.TextLines" Size="15" />
235+
<span>@ExportPlainTextLabel</span>
236+
</button>
237+
</div>
238+
}
239+
</div>
217240
}
218241

219242
@if (ShowLearnAction)
@@ -251,6 +274,7 @@
251274
private const string DisabledStateValue = "false";
252275
private const string EnabledStateValue = "true";
253276
private const decimal HomeIconStrokeWidth = 1.9m;
277+
private bool _isExportMenuOpen;
254278

255279
[Parameter, EditorRequired] public string CssClass { get; set; } = string.Empty;
256280
[Parameter] public bool IsReaderMuted { get; set; }
@@ -292,6 +316,7 @@
292316
[Parameter, EditorRequired] public string ReadLabel { get; set; } = string.Empty;
293317
[Parameter, EditorRequired] public string RestartTourLabel { get; set; } = string.Empty;
294318
[Parameter, EditorRequired] public string ExportMarkdownLabel { get; set; } = string.Empty;
319+
[Parameter, EditorRequired] public string ExportNativeLabel { get; set; } = string.Empty;
295320
[Parameter, EditorRequired] public string ExportPlainTextLabel { get; set; } = string.Empty;
296321
[Parameter, EditorRequired] public string SaveFileLabel { get; set; } = string.Empty;
297322
[Parameter, EditorRequired] public string SearchPlaceholder { get; set; } = string.Empty;
@@ -333,9 +358,23 @@
333358

334359
private Task HandleOpenReadClickAsync() => OnOpenReadClick.InvokeAsync();
335360

336-
private Task HandleExportMarkdownClickAsync() => OnExportMarkdownClick.InvokeAsync();
361+
private void ToggleExportMenu() => _isExportMenuOpen = !_isExportMenuOpen;
362+
363+
private async Task HandleExportMarkdownClickAsync()
364+
{
365+
_isExportMenuOpen = false;
366+
await OnExportMarkdownClick.InvokeAsync();
367+
}
337368

338-
private Task HandleExportPlainTextClickAsync() => OnExportPlainTextClick.InvokeAsync();
369+
private async Task HandleExportPlainTextClickAsync()
370+
{
371+
_isExportMenuOpen = false;
372+
await OnExportPlainTextClick.InvokeAsync();
373+
}
339374

340-
private Task HandleSaveFileClickAsync() => OnSaveFileClick.InvokeAsync();
375+
private async Task HandleSaveFileClickAsync()
376+
{
377+
_isExportMenuOpen = false;
378+
await OnSaveFileClick.InvokeAsync();
379+
}
341380
}

src/PrompterOne.Shared/AppShell/Components/MainLayoutHeader.razor.css

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,59 @@
44
flex: 0 0 auto;
55
}
66

7+
.app-header-export {
8+
position: relative;
9+
display: inline-flex;
10+
flex: 0 0 auto;
11+
}
12+
13+
.app-header-export-trigger {
14+
min-width: 112px;
15+
justify-content: center;
16+
}
17+
18+
.app-header-export-menu {
19+
position: absolute;
20+
top: calc(100% + 8px);
21+
right: 0;
22+
z-index: 20;
23+
display: grid;
24+
gap: 3px;
25+
min-width: 176px;
26+
padding: 6px;
27+
border: 1px solid color-mix(in srgb, var(--gold-20) 72%, var(--border-subtle));
28+
border-radius: 12px;
29+
background: color-mix(in srgb, var(--bg-1) 96%, var(--gold-04));
30+
box-shadow:
31+
0 18px 44px rgba(0, 0, 0, .34),
32+
0 0 0 1px rgba(255, 255, 255, .03);
33+
}
34+
35+
.app-header-export-option {
36+
display: flex;
37+
align-items: center;
38+
gap: 9px;
39+
width: 100%;
40+
min-height: 34px;
41+
padding: 7px 9px;
42+
border: 0;
43+
border-radius: 8px;
44+
color: var(--text-2);
45+
background: transparent;
46+
font: inherit;
47+
font-size: 13px;
48+
font-weight: 600;
49+
text-align: left;
50+
cursor: pointer;
51+
}
52+
53+
.app-header-export-option:hover,
54+
.app-header-export-option:focus-visible {
55+
color: var(--text-1);
56+
background: var(--gold-09);
57+
outline: none;
58+
}
59+
760
.app-header-title {
861
max-width: 100%;
962
min-width: 0;

src/PrompterOne.Shared/AppShell/Layout/MainLayout.razor

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@
5050
ImportPickerVersion="@_importScriptPickerVersion"
5151
ReadLabel="@Text(UiTextKey.HeaderRead)"
5252
RestartTourLabel="@Text(UiTextKey.OnboardingRestartTour)"
53-
ExportMarkdownLabel="@Text(UiTextKey.HeaderExportMarkdown)"
54-
ExportPlainTextLabel="@Text(UiTextKey.HeaderExportPlainText)"
53+
ExportMarkdownLabel="@Text(UiTextKey.SettingsFilesExportFormatMarkdown)"
54+
ExportNativeLabel="@Text(UiTextKey.SettingsFilesExportFormatTpsNative)"
55+
ExportPlainTextLabel="@Text(UiTextKey.SettingsFilesExportFormatPlainText)"
5556
SaveFileLabel="@Text(UiTextKey.HeaderSaveFile)"
5657
SearchPlaceholder="@Text(UiTextKey.HeaderSearchPlaceholder)"
5758
SearchText="@ShellState.SearchText"

src/PrompterOne.Shared/AppShell/Services/PrompterOneServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public static IServiceCollection AddPrompterOneShared(
111111
services.AddScoped<LearnRsvpLayoutInterop>();
112112
services.AddScoped<LearnPrepNotesStore>();
113113
services.AddScoped<MicrophoneLevelInterop>();
114+
services.AddScoped<ReaderRecordingInterop>();
114115
services.AddScoped<TeleprompterReaderInterop>();
115116
services.AddScoped<KineticReaderInterop>();
116117
services.AddScoped<AppBootstrapper>();

src/PrompterOne.Shared/Contracts/UiTestIds.Header.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ public static class Header
2121
public const string ImportProgressStep = "header-import-progress-step";
2222
public const string EditorLearn = "header-editor-learn";
2323
public const string EditorRead = "header-editor-read";
24+
public const string EditorExportMenu = "header-editor-export-menu";
2425
public const string EditorExportMarkdown = "header-editor-export-markdown";
26+
public const string EditorExportNative = "header-editor-export-native";
2527
public const string EditorExportPlainText = "header-editor-export-plain-text";
2628
public const string EditorSaveFile = "header-editor-save-file";
2729
public const string GoLive = "header-go-live";

src/PrompterOne.Shared/Contracts/UiTestIds.Settings.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,22 @@ public static class Settings
5555
public const string CloudSyncOnStartup = "settings-cloud-sync-on-startup";
5656
public const string DefaultCamera = "settings-default-camera";
5757
public const string EchoCancellation = "settings-echo-cancellation";
58+
public const string AutoGainControl = "settings-auto-gain-control";
5859
public const string FileAutoSave = "settings-file-autosave";
5960
public const string FileBackupCopies = "settings-file-backup-copies";
6061
public const string FilesPanel = "settings-files-panel";
6162
public const string FilesRecordingsCard = "settings-files-recordings-card";
6263
public const string FilesScriptsCard = "settings-files-scripts-card";
6364
public const string MicLevel = "settings-mic-level";
65+
public const string MicChannelCount = "settings-mic-channel-count";
6466
public const string MicLevelValue = "settings-mic-level-value";
6567
public const string MicPreviewCard = "settings-mic-preview-card";
6668
public const string MicPreviewEmpty = "settings-mic-preview-empty";
6769
public const string MicPreviewLabel = "settings-mic-preview-label";
6870
public const string MicPreviewMeter = "settings-mic-preview-meter";
6971
public const string MicPreviewValue = "settings-mic-preview-value";
72+
public const string MicSampleRate = "settings-mic-sample-rate";
73+
public const string MicSampleSize = "settings-mic-sample-size";
7074
public const string MicsPanel = "settings-mics-panel";
7175
public const string Main = "settings-main";
7276
public const string NavAbout = "settings-nav-about";
@@ -83,6 +87,7 @@ public static class Settings
8387
public const string NavShortcuts = "settings-nav-shortcuts";
8488
public const string NavStreaming = "settings-nav-streaming";
8589
public const string NoiseSuppression = "settings-noise-suppression";
90+
public const string VoiceIsolation = "settings-voice-isolation";
8691
public const string Page = "settings-page";
8792
public const string Layout = "settings-layout";
8893
public const string PrimaryMic = "settings-primary-mic";

src/PrompterOne.Shared/Contracts/UiTestIds.Teleprompter.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ public static class Teleprompter
4949
public const string ProgressFill = "teleprompter-progress-fill";
5050
public const string ProgressLabel = "teleprompter-progress-label";
5151
public const string ProgressSegments = "teleprompter-progress-segments";
52+
public const string RecordingCameraSelect = "teleprompter-recording-camera-select";
53+
public const string RecordingMicrophoneSelect = "teleprompter-recording-microphone-select";
54+
public const string RecordingModeSelect = "teleprompter-recording-mode-select";
55+
public const string RecordingPanel = "teleprompter-recording-panel";
56+
public const string RecordingStatus = "teleprompter-recording-status";
57+
public const string RecordingToggle = "teleprompter-recording-toggle";
58+
public const string RecordingLevel = "teleprompter-recording-level";
5259
public const string SpeedDown = "teleprompter-speed-down";
5360
public const string SpeedCueDisplayMode = "teleprompter-speed-cue-display-mode";
5461
public const string SpeedCueDisplayMultiplier = "teleprompter-speed-cue-display-multiplier";

0 commit comments

Comments
 (0)