Skip to content

Commit bedd76f

Browse files
committed
fxies
1 parent a60bb03 commit bedd76f

13 files changed

Lines changed: 140 additions & 56 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public static IServiceCollection AddPrompterOneShared(
8888
services.AddScoped<AppShellService>();
8989
services.AddSingleton(runtimeTelemetryOptions ?? RuntimeTelemetryOptions.Disabled);
9090
services.AddScoped<RuntimeTelemetryService>();
91+
services.AddScoped<BrowserConnectivityInterop>();
9192
services.AddScoped<BrowserConnectivityService>();
9293
services.AddScoped<GoLiveSessionService>();
9394
services.AddScoped<GoLiveOutputInterop>();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using Microsoft.JSInterop;
2+
3+
namespace PrompterOne.Shared.Services.Diagnostics;
4+
5+
public sealed class BrowserConnectivityInterop(IJSRuntime jsRuntime) : IDisposable, IAsyncDisposable
6+
{
7+
private readonly IJSRuntime _jsRuntime = jsRuntime;
8+
private Task<IJSObjectReference?>? _moduleTask;
9+
10+
public async ValueTask<bool?> GetOnlineStatusAsync(CancellationToken cancellationToken = default)
11+
{
12+
var module = await GetModuleAsync();
13+
if (module is null)
14+
{
15+
return null;
16+
}
17+
18+
try
19+
{
20+
return await module.InvokeAsync<bool>(
21+
BrowserConnectivityInteropMethodNames.GetOnlineStatus,
22+
cancellationToken);
23+
}
24+
catch (JSException)
25+
{
26+
return null;
27+
}
28+
}
29+
30+
public void Dispose()
31+
{
32+
}
33+
34+
public async ValueTask DisposeAsync()
35+
{
36+
if (_moduleTask is null)
37+
{
38+
return;
39+
}
40+
41+
var module = await _moduleTask;
42+
if (module is not null)
43+
{
44+
await module.DisposeAsync();
45+
}
46+
}
47+
48+
private Task<IJSObjectReference?> GetModuleAsync() =>
49+
_moduleTask ??= ImportModuleAsync();
50+
51+
private async Task<IJSObjectReference?> ImportModuleAsync()
52+
{
53+
try
54+
{
55+
return await _jsRuntime.InvokeAsync<IJSObjectReference>(
56+
BrowserConnectivityInteropMethodNames.JSImportMethodName,
57+
BrowserConnectivityInteropMethodNames.ModulePath);
58+
}
59+
catch (InvalidOperationException)
60+
{
61+
return null;
62+
}
63+
catch (JSException)
64+
{
65+
return null;
66+
}
67+
}
68+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace PrompterOne.Shared.Services.Diagnostics;
2+
3+
internal static class BrowserConnectivityInteropMethodNames
4+
{
5+
public const string JSImportMethodName = "import";
6+
public const string ModulePath = "./_content/PrompterOne.Shared/diagnostics/browser-connectivity.js";
7+
public const string GetOnlineStatus = "getOnlineStatus";
8+
}

src/PrompterOne.Shared/Diagnostics/Services/BrowserConnectivityService.cs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
using Microsoft.Extensions.Localization;
2-
using Microsoft.JSInterop;
32
using PrompterOne.Shared.Localization;
43

54
namespace PrompterOne.Shared.Services.Diagnostics;
65

76
public sealed class BrowserConnectivityService(
8-
IJSRuntime jsRuntime,
7+
BrowserConnectivityInterop connectivityInterop,
98
IStringLocalizer<SharedResource> localizer) : IDisposable, IAsyncDisposable
109
{
11-
private const string EvaluateMethodName = "eval";
12-
private const string OnlineExpression = "navigator.onLine";
1310
private const int OnlineAutoHideDelayMilliseconds = 2400;
1411
private const int PollIntervalMilliseconds = 1000;
1512

16-
private readonly IJSRuntime _jsRuntime = jsRuntime;
13+
private readonly BrowserConnectivityInterop _connectivityInterop = connectivityInterop;
1714
private readonly IStringLocalizer<SharedResource> _localizer = localizer;
1815
private readonly SemaphoreSlim _startGate = new(1, 1);
1916

@@ -104,29 +101,26 @@ private async Task MonitorAsync(CancellationToken cancellationToken)
104101
{
105102
break;
106103
}
107-
catch (JSException)
108-
{
109-
break;
110-
}
111104
}
112105
}
113106

114107
private async Task ProbeAsync(CancellationToken cancellationToken)
115108
{
116109
var previousOnlineState = _lastKnownOnline;
117-
var isOnline = await _jsRuntime.InvokeAsync<bool>(
118-
EvaluateMethodName,
119-
cancellationToken,
120-
OnlineExpression);
110+
var isOnline = await _connectivityInterop.GetOnlineStatusAsync(cancellationToken);
111+
if (!isOnline.HasValue)
112+
{
113+
return;
114+
}
121115

122-
if (previousOnlineState == isOnline)
116+
if (previousOnlineState == isOnline.Value)
123117
{
124118
return;
125119
}
126120

127-
_lastKnownOnline = isOnline;
121+
_lastKnownOnline = isOnline.Value;
128122

129-
if (!isOnline)
123+
if (!isOnline.Value)
130124
{
131125
CancelPendingHide();
132126
UpdateState(

src/PrompterOne.Shared/Editor/Pages/EditorPage.DocumentSplit.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public partial class EditorPage
1818

1919
private Task OnSplitRequestedAsync(TpsDocumentSplitMode mode)
2020
{
21+
var sourceDocumentTask = ResolveCurrentScriptDocumentAsync();
2122
var persistedText = BuildPersistedDocument(_sourceText);
2223
var splitDocuments = DocumentSplitService.Split(persistedText, mode);
2324
if (splitDocuments.Count < MinimumSplitDocumentCount)
@@ -36,7 +37,7 @@ private Task OnSplitRequestedAsync(TpsDocumentSplitMode mode)
3637
SplitDraftMessage,
3738
async () =>
3839
{
39-
var sourceDocument = await ResolveCurrentScriptDocumentAsync();
40+
var sourceDocument = await sourceDocumentTask;
4041
var baseDocumentName = ResolveBaseDocumentName(sourceDocument);
4142

4243
foreach (var splitDocument in splitDocuments)

src/PrompterOne.Shared/Library/Services/LibraryCardFactory.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using PrompterOne.Core.Models.CompiledScript;
55
using PrompterOne.Core.Models.Documents;
66
using PrompterOne.Core.Services;
7+
using PrompterOne.Core.Services.Editor;
78
using PrompterOne.Core.Services.Preview;
89
using PrompterOne.Shared.Components.Library;
910
using PrompterOne.Shared.Contracts;
@@ -148,15 +149,15 @@ private static LibraryCardMetrics CalculateMetrics(CompiledScript compiledScript
148149
}
149150

150151
private static string ResolveAuthor(IReadOnlyDictionary<string, string> metadata, IStringLocalizer<SharedResource> localizer) =>
151-
metadata.TryGetValue("author", out var author) && !string.IsNullOrWhiteSpace(author)
152+
metadata.TryGetValue(TpsFrontMatterDocumentService.MetadataKeys.Author, out var author) && !string.IsNullOrWhiteSpace(author)
152153
? author
153154
: Text(localizer, UiTextKey.LibraryDefaultAuthor);
154155

155156
private static string ResolveModeLabel(IReadOnlyDictionary<string, string> metadata, int averageWpm, IStringLocalizer<SharedResource> localizer) =>
156-
metadata.TryGetValue("profile", out var profile) && !string.IsNullOrWhiteSpace(profile)
157+
metadata.TryGetValue(TpsFrontMatterDocumentService.MetadataKeys.Profile, out var profile) && !string.IsNullOrWhiteSpace(profile)
157158
? profile.Trim().Trim('"')
158159
: averageWpm >= 250
159-
? "RSVP"
160+
? Text(localizer, UiTextKey.CommonRsvp)
160161
: Text(localizer, UiTextKey.LibraryDefaultModeActor);
161162

162163
private static string ResolveCoverClass(string? emotionKey) =>

src/PrompterOne.Shared/Settings/Components/SettingsAiSection.razor.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ namespace PrompterOne.Shared.Components.Settings;
1010
public partial class SettingsAiSection : ComponentBase
1111
{
1212
private const string ActiveCssClass = "active";
13-
private const string AnthropicLabel = "Anthropic";
1413
private const string ClaudeApiCardId = "ai-claude-api";
1514
private const string DisconnectedStatusClass = "set-dest-idle";
1615
private const string LocalhostAuthority = "localhost:11434";
1716
private const string LocalStatusClass = "set-dest-local";
18-
private const string OpenAiLabel = "OpenAI";
1917
private const string OllamaCardId = "ai-ollama";
2018
private const string OpenAiCardId = "ai-openai";
2119
private const string SubtitleSeparator = " · ";
@@ -84,11 +82,11 @@ private string BuildStatusLabel(OpenAiProviderSettings settings) =>
8482
private string BuildStatusLabel(OllamaAiProviderSettings settings) =>
8583
settings.IsConfigured() ? Text(UiTextKey.CommonSavedLocally) : Text(UiTextKey.CommonNotConfigured);
8684

87-
private static string BuildClaudeSubtitle(AnthropicAiProviderSettings settings) =>
88-
BuildCatalogSubtitle(AnthropicLabel, settings.Model, ClaudeModelOptions);
85+
private string BuildClaudeSubtitle(AnthropicAiProviderSettings settings) =>
86+
BuildCatalogSubtitle(Text(UiTextKey.SettingsAiClaudeTitle), settings.Model, ClaudeModelOptions);
8987

90-
private static string BuildOpenAiSubtitle(OpenAiProviderSettings settings) =>
91-
BuildCatalogSubtitle(OpenAiLabel, settings.Model, OpenAiModelOptions);
88+
private string BuildOpenAiSubtitle(OpenAiProviderSettings settings) =>
89+
BuildCatalogSubtitle(Text(UiTextKey.SettingsAiOpenAiTitle), settings.Model, OpenAiModelOptions);
9290

9391
private string BuildOllamaSubtitle(OllamaAiProviderSettings settings)
9492
{

src/PrompterOne.Shared/Settings/Pages/SettingsPage.Cameras.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.AspNetCore.Components;
22
using PrompterOne.Core.Models.Media;
33
using PrompterOne.Core.Models.Workspace;
4+
using PrompterOne.Shared.Localization;
45
using PrompterOne.Shared.Settings.Components;
56
using PrompterOne.Shared.Storage;
67

@@ -148,7 +149,7 @@ private string BuildCameraMeta(MediaDeviceInfo camera, bool isPrimary)
148149

149150
if (isPrimary)
150151
{
151-
parts.Add("Primary");
152+
parts.Add(Text(UiTextKey.SettingsCamerasPrimaryBadge));
152153
}
153154

154155
return string.Join(" · ", parts);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function getOnlineStatus() {
2+
if (typeof navigator !== "object" || navigator === null) {
3+
return true;
4+
}
5+
6+
return navigator.onLine === true;
7+
}

tests/PrompterOne.Testing/Editor/EditorSplitFeedbackTestData.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ namespace PrompterOne.Testing.Editor;
33
public static class EditorSplitFeedbackTestData
44
{
55
public const int MetadataRailAdditionalCount = 2;
6-
public const int PostAutosaveObservationDelayMs = 1_700;
76
public const string EpisodeOneCardId = "untitled-script-split-01-episode-1-how-to-think-about-systems";
87
public const string EpisodeOneTitle = "Episode 1 - How to Think About Systems";
98
public const string EpisodeThreeTitle = "Episode 3 - Event Sourcing and CQRS";

0 commit comments

Comments
 (0)