Skip to content

Make settings runtime state honest#6

Merged
KSemenenko merged 1 commit intomainfrom
codex/settings-runtime-honesty
Apr 2, 2026
Merged

Make settings runtime state honest#6
KSemenenko merged 1 commit intomainfrom
codex/settings-runtime-honesty

Conversation

@KSemenenko
Copy link
Copy Markdown
Member

Summary

  • replace fake Settings defaults for cameras, cloud storage, file storage, microphones, and AI providers with browser-real persisted state
  • extract reusable Blazor settings components for AI, files, cameras, and microphones so the page is no longer a monolith
  • tighten settings tests around honest runtime state and persisted browser behavior

Validation

  • dotnet format /Users/ksemenenko/Developer/PrompterOne/PrompterOne.slnx
  • dotnet build /Users/ksemenenko/Developer/PrompterOne/PrompterOne.slnx -warnaserror
  • dotnet test /Users/ksemenenko/Developer/PrompterOne/tests/PrompterOne.App.Tests/PrompterOne.App.Tests.csproj --filter Settings
  • dotnet test /Users/ksemenenko/Developer/PrompterOne/tests/PrompterOne.App.UITests/PrompterOne.App.UITests.csproj --filter "FullyQualifiedName~Settings|FullyQualifiedName~TeleprompterSettingsFlow|FullyQualifiedName~MediaRuntimeIntegrationTests"
  • dotnet test /Users/ksemenenko/Developer/PrompterOne/PrompterOne.slnx
  • dotnet test /Users/ksemenenko/Developer/PrompterOne/PrompterOne.slnx --collect:"XPlat Code Coverage"

@KSemenenko KSemenenko marked this pull request as ready for review April 2, 2026 10:21
Copilot AI review requested due to automatic review settings April 2, 2026 10:21
@KSemenenko KSemenenko merged commit 3c075e3 into main Apr 2, 2026
1 check passed
@KSemenenko KSemenenko deleted the codex/settings-runtime-honesty branch April 2, 2026 10:23
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Settings experience reflect actual persisted browser state (instead of “fake” desktop defaults), splits the Settings page into reusable sections, and updates test coverage to validate the new runtime/persistence behavior.

Changes:

  • Reworked Settings UI into dedicated components/partials for Files, Cameras, Microphones, and AI provider drafts.
  • Introduced persisted stores/models for AI provider drafts and browser file-storage preferences; included them in cloud snapshot import/export.
  • Updated unit/UI tests and CSS to align with the new “honest runtime state” behavior and styling.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/PrompterOne.App.UITests/Teleprompter/TeleprompterSettingsFlowTests.cs Updates UI expectations for AI provider cards (open state + save button).
tests/PrompterOne.App.UITests/Support/BrowserTestConstants.cs Adds new UI-test constants and regex helpers used by updated flows.
tests/PrompterOne.App.UITests/Settings/SettingsCloudStorageFlowTests.cs Tightens cloud settings flow to start from persisted toggle state.
tests/PrompterOne.App.Tests/Support/TestSupport.cs Registers newly introduced settings stores for test DI.
tests/PrompterOne.App.Tests/Settings/SettingsInteractionTests.cs Adds/updates tests validating browser-local labels and AI draft persistence.
src/PrompterOne.Shared/wwwroot/design/modules/settings/20-reference.css Styling updates for “idle/local” statuses and inactive device cards.
src/PrompterOne.Shared/Storage/PrompterStorageDefaults.cs Introduces browser-container display prefix and adds recordings/exports paths.
src/PrompterOne.Shared/Storage/Cloud/CloudStorageTransferService.cs Includes AI + file-storage settings in settings bundle export/import.
src/PrompterOne.Shared/Storage/Cloud/CloudStorageModels.cs Makes cloud prefs booleans “honest” (no forced defaults) and expands settings bundle.
src/PrompterOne.Shared/Settings/Services/BrowserFileStorageStore.cs New store for file-storage settings + browser-local view state.
src/PrompterOne.Shared/Settings/Services/AiProviderSettingsStore.cs New store for persisted AI provider draft settings.
src/PrompterOne.Shared/Settings/Pages/SettingsPage.razor Replaces monolithic sections with extracted settings components.
src/PrompterOne.Shared/Settings/Pages/SettingsPage.Preferences.cs Removes legacy fake file/AI prefs and centralizes toggle CSS helper.
src/PrompterOne.Shared/Settings/Pages/SettingsPage.Navigation.cs Minor nav constant refactor.
src/PrompterOne.Shared/Settings/Pages/SettingsPage.Microphones.cs New partial: microphone behaviors extracted from the monolith.
src/PrompterOne.Shared/Settings/Pages/SettingsPage.MediaState.cs New partial: media/device load/normalization logic extracted.
src/PrompterOne.Shared/Settings/Pages/SettingsPage.Cameras.cs New partial: camera behaviors extracted from the monolith.
src/PrompterOne.Shared/Settings/Models/SettingsPagePreferences.cs Removes fake desktop defaults and AI selection from settings-page prefs.
src/PrompterOne.Shared/Settings/Models/BrowserFileStorageSettings.cs New persisted model + view-state records for file-storage section.
src/PrompterOne.Shared/Settings/Models/AiProviderSettings.cs New persisted model for AI provider drafts (Claude/OpenAI/Ollama).
src/PrompterOne.Shared/Settings/Components/SettingsMicrophonesSection.razor New extracted microphones section UI component.
src/PrompterOne.Shared/Settings/Components/SettingsMicrophoneLevelCard.razor Makes mic meter/test IDs configurable and respects enabled/active states.
src/PrompterOne.Shared/Settings/Components/SettingsFilesSection.razor.cs New code-behind for loading/saving browser file-storage settings/view state.
src/PrompterOne.Shared/Settings/Components/SettingsFilesSection.razor Updates Files section to show browser-real storage labels and persisted toggles.
src/PrompterOne.Shared/Settings/Components/SettingsCloudSection.razor.cs Adds explicit “idle” status class when disconnected.
src/PrompterOne.Shared/Settings/Components/SettingsCloudSection.razor Clarifies copy to reflect optional snapshot targets + local credential storage.
src/PrompterOne.Shared/Settings/Components/SettingsCamerasSection.razor New extracted cameras section UI component.
src/PrompterOne.Shared/Settings/Components/SettingsAiSection.razor.cs New AI provider persistence logic (save/clear + local-only messaging).
src/PrompterOne.Shared/Settings/Components/SettingsAiSection.razor Replaces “test connection” UI with local draft save/clear flow.
src/PrompterOne.Shared/Settings/Components/SettingsAiProviderCard.razor New reusable card wrapper for AI provider UI actions and messaging.
src/PrompterOne.Shared/Contracts/UiTestIds.cs Adds test IDs for AI save/clear/message and GO LIVE metrics.
src/PrompterOne.Shared/Contracts/UiDomIds.cs Adds per-device microphone monitor DOM IDs for multiple meters.
src/PrompterOne.Shared/AppShell/Services/PrompterOneServiceCollectionExtensions.cs Registers new settings stores in the shared DI setup.
src/PrompterOne.Core/Workspace/Models/StudioSettings.cs Changes default camera mirror setting to false.
src/PrompterOne.Core/Media/Models/MediaSceneState.cs Changes default scene transform horizontal mirror to false.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +72 to +76
private static async Task<bool> HasOnClassAsync(ILocator locator)
{
var classes = await locator.GetAttributeAsync("class");
return (classes ?? string.Empty).Contains("on", StringComparison.Ordinal);
}
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.

HasOnClassAsync uses string.Contains("on") which can produce false positives if any other CSS class contains the substring on (e.g., "button", "connection"). Since this helper controls toggle state assertions, it should parse the class list (split on spaces like before) or use a word-boundary regex (e.g., \bon\b) to reliably detect the on class.

Copilot uses AI. Check for mistakes.
Comment on lines +240 to +241
await _aiProviderSettingsStore.SaveAsync(bundle.AiProviderSettings, cancellationToken);
await _browserFileStorageStore.SaveSettingsAsync(bundle.FileStorageSettings, cancellationToken);
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.

RestoreSettingsAsync saves bundle.AiProviderSettings / bundle.FileStorageSettings without null-coalescing, but both AiProviderSettingsStore.SaveAsync and BrowserFileStorageStore.SaveSettingsAsync throw on null. If an imported snapshot contains explicit null values for these fields, import will fail. Coalesce/normalize before saving (e.g., default + .Normalize() for AI settings; BrowserFileStorageSettings.Default for file storage).

Suggested change
await _aiProviderSettingsStore.SaveAsync(bundle.AiProviderSettings, cancellationToken);
await _browserFileStorageStore.SaveSettingsAsync(bundle.FileStorageSettings, cancellationToken);
// Ensure AI provider settings are never null when saved
var aiSettings = (bundle.AiProviderSettings ?? new AiProviderSettings()).Normalize();
// Ensure file storage settings are never null when saved
var fileStorageSettings = bundle.FileStorageSettings ?? BrowserFileStorageSettings.Default;
await _aiProviderSettingsStore.SaveAsync(aiSettings, cancellationToken);
await _browserFileStorageStore.SaveSettingsAsync(fileStorageSettings, cancellationToken);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants