diff --git a/src/NoteBookmark.Api.Tests/Domain/SettingsTests.cs b/src/NoteBookmark.Api.Tests/Domain/SettingsTests.cs
index 5bffb79..b547c11 100644
--- a/src/NoteBookmark.Api.Tests/Domain/SettingsTests.cs
+++ b/src/NoteBookmark.Api.Tests/Domain/SettingsTests.cs
@@ -194,7 +194,8 @@ public void Settings_WithFullData_PreservesAllValues()
SearchPrompt = "Find {topic}",
AiApiKey = "test-key",
AiBaseUrl = "https://api.openai.com",
- AiModelName = "gpt-4"
+ AiModelName = "gpt-4",
+ FontSize = "large"
};
// Assert
@@ -207,5 +208,22 @@ public void Settings_WithFullData_PreservesAllValues()
settings.AiApiKey.Should().Be("test-key");
settings.AiBaseUrl.Should().Be("https://api.openai.com");
settings.AiModelName.Should().Be("gpt-4");
+ settings.FontSize.Should().Be("large");
+ }
+
+ [Theory]
+ [InlineData("small")]
+ [InlineData("medium")]
+ [InlineData("large")]
+ public void FontSize_CanBeSetAndRetrieved(string fontSize)
+ {
+ var settings = new Settings
+ {
+ PartitionKey = "setting",
+ RowKey = "setting",
+ FontSize = fontSize
+ };
+
+ settings.FontSize.Should().Be(fontSize);
}
}
diff --git a/src/NoteBookmark.Domain/Settings.cs b/src/NoteBookmark.Domain/Settings.cs
index f37d026..7c029fd 100644
--- a/src/NoteBookmark.Domain/Settings.cs
+++ b/src/NoteBookmark.Domain/Settings.cs
@@ -44,6 +44,9 @@ public class Settings: ITableEntity
[DataMember(Name="ai_model_name")]
public string? AiModelName { get; set; }
+
+ [DataMember(Name="font_size")]
+ public string? FontSize { get; set; }
public required string PartitionKey { get ; set; }
public required string RowKey { get ; set; }
diff --git a/src/NoteBookmark.MauiApp.Tests/FontSizeHelperTests.cs b/src/NoteBookmark.MauiApp.Tests/FontSizeHelperTests.cs
new file mode 100644
index 0000000..9540ec8
--- /dev/null
+++ b/src/NoteBookmark.MauiApp.Tests/FontSizeHelperTests.cs
@@ -0,0 +1,25 @@
+using FluentAssertions;
+using NoteBookmark.SharedUI;
+using Xunit;
+
+namespace NoteBookmark.MauiApp.Tests;
+
+public class FontSizeHelperTests
+{
+ [Theory]
+ [InlineData("small", "0.875rem")]
+ [InlineData("SMALL", "0.875rem")]
+ [InlineData("medium", "1rem")]
+ [InlineData("MEDIUM", "1rem")]
+ [InlineData("large", "1.25rem")]
+ [InlineData("LARGE", "1.25rem")]
+ [InlineData("18px", "18px")]
+ [InlineData(null, "1rem")]
+ [InlineData("", "1rem")]
+ [InlineData(" ", "1rem")]
+ public void ToCssValue_MapsFontSizeCorrectly(string? input, string expectedCssValue)
+ {
+ var result = FontSizeHelper.ToCssValue(input);
+ result.Should().Be(expectedCssValue);
+ }
+}
diff --git a/src/NoteBookmark.MauiApp.Tests/LocalDataServiceTests.cs b/src/NoteBookmark.MauiApp.Tests/LocalDataServiceTests.cs
index d1e5b2d..c3311d0 100644
--- a/src/NoteBookmark.MauiApp.Tests/LocalDataServiceTests.cs
+++ b/src/NoteBookmark.MauiApp.Tests/LocalDataServiceTests.cs
@@ -144,4 +144,21 @@ public async Task DeletePost_ShouldSoftDeleteAndFlagPendingSync()
var pending = await _sut.GetPendingSyncPostsAsync();
pending.Should().ContainSingle(p => p.Id == "post1", "because it needs to sync the deletion");
}
+
+ [Fact]
+ public async Task SaveSettings_ShouldStoreAndRetrieveFontSize()
+ {
+ var settings = new Settings
+ {
+ PartitionKey = "setting",
+ RowKey = "setting",
+ FontSize = "large"
+ };
+
+ await _sut.SaveSettingsAsync(settings);
+
+ var retrieved = await _sut.GetSettingsAsync();
+ retrieved.Should().NotBeNull();
+ retrieved!.FontSize.Should().Be("large");
+ }
}
diff --git a/src/NoteBookmark.MauiApp/Components/Layout/MainLayout.razor b/src/NoteBookmark.MauiApp/Components/Layout/MainLayout.razor
index 6a5435b..2d62a87 100644
--- a/src/NoteBookmark.MauiApp/Components/Layout/MainLayout.razor
+++ b/src/NoteBookmark.MauiApp/Components/Layout/MainLayout.razor
@@ -3,10 +3,13 @@
@using Microsoft.Maui.ApplicationModel
@using Microsoft.FluentUI.AspNetCore.Components
@using NoteBookmark.MauiApp.Data
+@using NoteBookmark.SharedUI
@inject IConnectivity Connectivity
@inject NavigationManager Navigation
@inject ISyncService SyncService
@inject IToastService ToastService
+@inject IDataService DataService
+@inject IJSRuntime JSRuntime
@implements IDisposable
@@ -69,6 +72,24 @@
}
}
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (firstRender)
+ {
+ try
+ {
+ var settings = await DataService.GetSettings();
+ var cssSize = FontSizeHelper.ToCssValue(settings?.FontSize);
+ await JSRuntime.InvokeVoidAsync("document.documentElement.style.setProperty", "--app-font-size", cssSize);
+ await JSRuntime.InvokeVoidAsync("document.body.style.setProperty", "--app-font-size", cssSize);
+ }
+ catch
+ {
+ // Fallback for headless environments or prerendering
+ }
+ }
+ }
+
private void OnSyncConflictDetected(object? sender, SyncConflictEventArgs e)
{
InvokeAsync(() =>
diff --git a/src/NoteBookmark.MauiApp/Data/LocalModels.cs b/src/NoteBookmark.MauiApp/Data/LocalModels.cs
index 14ca4b3..681dd48 100644
--- a/src/NoteBookmark.MauiApp/Data/LocalModels.cs
+++ b/src/NoteBookmark.MauiApp/Data/LocalModels.cs
@@ -179,6 +179,7 @@ public class LocalSettings
public string? AiApiKey { get; set; }
public string? AiBaseUrl { get; set; }
public string? AiModelName { get; set; }
+ public string? FontSize { get; set; }
public bool IsPendingSync { get; set; }
public Settings ToDomain() => new Settings
@@ -193,7 +194,8 @@ public class LocalSettings
SearchPrompt = SearchPrompt,
AiApiKey = AiApiKey,
AiBaseUrl = AiBaseUrl,
- AiModelName = AiModelName
+ AiModelName = AiModelName,
+ FontSize = FontSize
};
public static LocalSettings FromDomain(Settings s, bool isPendingSync = false) => new LocalSettings
@@ -209,6 +211,7 @@ public class LocalSettings
AiApiKey = s.AiApiKey,
AiBaseUrl = s.AiBaseUrl,
AiModelName = s.AiModelName,
+ FontSize = s.FontSize,
IsPendingSync = isPendingSync
};
}
diff --git a/src/NoteBookmark.MauiApp/wwwroot/app.css b/src/NoteBookmark.MauiApp/wwwroot/app.css
index 77e3472..49ca487 100644
--- a/src/NoteBookmark.MauiApp/wwwroot/app.css
+++ b/src/NoteBookmark.MauiApp/wwwroot/app.css
@@ -1,5 +1,11 @@
+:root {
+ --app-font-size: 1rem;
+}
+
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: var(--app-font-size, 1rem);
+ --type-ramp-base-font-size: var(--app-font-size, 1rem);
}
a, .btn-link {
diff --git a/src/NoteBookmark.SharedUI/Components/Pages/Settings.razor b/src/NoteBookmark.SharedUI/Components/Pages/Settings.razor
index eb66dcd..5b65bab 100644
--- a/src/NoteBookmark.SharedUI/Components/Pages/Settings.razor
+++ b/src/NoteBookmark.SharedUI/Components/Pages/Settings.razor
@@ -5,6 +5,7 @@
@using NoteBookmark.Domain
@inject IDataService client
@inject NavigationManager Navigation
+@inject IJSRuntime JSRuntime
+
+
+
+
@@ -103,9 +112,22 @@
private Domain.Settings? settings;
+ private record FontSizeOption(string Text, string Value);
+
+ private static readonly List fontSizeOptions = new()
+ {
+ new("Small", "small"),
+ new("Medium", "medium"),
+ new("Large", "large")
+ };
+
protected override async Task OnInitializedAsync()
{
settings = await client.GetSettings();
+ if (settings != null && string.IsNullOrWhiteSpace(settings.FontSize))
+ {
+ settings.FontSize = FontSizeHelper.DefaultFontSize;
+ }
}
private async Task SaveSettings()
@@ -113,6 +135,16 @@
if (settings != null)
{
await client.SaveSettings(settings);
+ try
+ {
+ var cssSize = FontSizeHelper.ToCssValue(settings.FontSize);
+ await JSRuntime.InvokeVoidAsync("document.documentElement.style.setProperty", "--app-font-size", cssSize);
+ await JSRuntime.InvokeVoidAsync("document.body.style.setProperty", "--app-font-size", cssSize);
+ }
+ catch
+ {
+ // Prerender / non-JS environment safe
+ }
Navigation.NavigateTo("/");
}
}
diff --git a/src/NoteBookmark.SharedUI/FontSizeHelper.cs b/src/NoteBookmark.SharedUI/FontSizeHelper.cs
new file mode 100644
index 0000000..9138b7a
--- /dev/null
+++ b/src/NoteBookmark.SharedUI/FontSizeHelper.cs
@@ -0,0 +1,15 @@
+namespace NoteBookmark.SharedUI;
+
+public static class FontSizeHelper
+{
+ public const string DefaultFontSize = "medium";
+
+ public static string ToCssValue(string? fontSize) => fontSize?.ToLowerInvariant() switch
+ {
+ "small" => "0.875rem",
+ "large" => "1.25rem",
+ "medium" => "1rem",
+ _ when !string.IsNullOrWhiteSpace(fontSize) => fontSize,
+ _ => "1rem"
+ };
+}