Skip to content

Track and restore latest URL#2190

Draft
myieye wants to merge 6 commits intodevelopfrom
claude/track-url-session-restore-9Wd2q
Draft

Track and restore latest URL#2190
myieye wants to merge 6 commits intodevelopfrom
claude/track-url-session-restore-9Wd2q

Conversation

@myieye
Copy link
Collaborator

@myieye myieye commented Mar 3, 2026

Resolves #2189

@github-actions github-actions bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Mar 3, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1aabf39f-4bc1-40f3-b48d-6617eed732ca

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements server-side JSON file-backed preferences storage and integrates URL persistence across the application. It adds a new JsonFilePreferencesService, introduces preference key enumeration, updates app startup to restore last URLs, creates reactive storage utilities for frontend persistence, and replaces localStorage-based preferences with service-backed storage.

Changes

Cohort / File(s) Summary
Backend Preferences Service Implementation
backend/FwLite/FwLiteShared/Services/JsonFilePreferencesService.cs, backend/FwLite/FwLiteShared/Services/PreferenceKey.cs
New JSON file-backed preferences service with thread-safe in-memory caching, atomic save operations, and corresponding PreferenceKey enum with AppLastUrl member.
Backend Integration & Service Registration
backend/FwLite/FwLiteMaui/App.xaml.cs, backend/FwLite/FwLiteWeb/FwLiteWebKernel.cs, backend/FwLite/FwLiteWeb/Program.cs, backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
Injects IPreferencesService into App constructor to restore last URL on startup; registers JsonFilePreferencesService in DI container; updates browser launch logic to use saved URL; exports PreferenceKey enum to TypeScript.
Frontend Storage & State Management Utilities
frontend/viewer/src/lib/utils/storage-prop.svelte.ts, frontend/viewer/src/lib/utils/app-storage.svelte.ts, frontend/viewer/src/lib/utils/project-storage.svelte.ts
Introduces StorageProp class for reactive async-persisted storage values; adds app-storage singleton for tracking last URL; refactors project-storage to use new project-scoped StorageProp pattern instead of localStorage.
Frontend Route Tracking & Service API
frontend/viewer/src/lib/services/service-provider.ts, frontend/viewer/src/AppRoutes.svelte
Changes tryUsePreferencesService to usePreferencesService (non-undefined return); adds runtime URL tracking via debounced pushState/replaceState/popstate handlers to persist current pathname+query+hash.
Generated TypeScript Types
frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/PreferenceKey.ts, frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/index.ts
Generates TypeScript enum PreferenceKey with AppLastUrl member; adds re-export in Services index.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Handles IPreferencesService wiring and TS type generation shared with MAUI/localStorage implementations (#2158)
  • Refactors project-scoped storage architecture to use reactive StorageProp pattern for task selection persistence (#2142)
  • Modifies frontend routing/URL initialization logic for persistent state restoration across sessions (#2018)

Suggested labels

🟨Medium

Suggested reviewers

  • rmunn
  • imnasnainaec

Poem

🐰 A rabbit's delight in persistent paths!
URLs remembered, no second lapse,
JSON files hold preferences dear,
Reactive storage keeps configs near,
Click, save, reload—same place reappears! 📍✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description 'Resolves #2189' is minimal but directly related to the changeset, which implements the feature requested in issue #2189 regarding JSON file preferences for Linux.
Linked Issues check ✅ Passed The PR fully meets issue #2189 objectives: introduces JsonFilePreferencesService backend implementation [App.xaml.cs, JsonFilePreferencesService.cs, FwLiteWebKernel.cs], enables server-side preference persistence with shared PreferenceKey enum [PreferenceKey.cs, ReinforcedFwLiteTypingConfig.cs], integrates URL restoration logic across backend and frontend [Program.cs, AppRoutes.svelte, app-storage.svelte.ts], and replaces localStorage with backend-backed approach [storage-prop.svelte.ts, project-storage.svelte.ts].
Out of Scope Changes check ✅ Passed All changes are within scope of issue #2189: JSON-file preferences backend for Linux, preference key infrastructure, URL persistence and restoration across restarts, and shared type definitions between C# and TypeScript.
Title check ✅ Passed The title 'Track and restore latest URL' accurately summarizes the main change: implementing URL tracking and restoration functionality across app restarts, which is the core objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/track-url-session-restore-9Wd2q

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

UI unit Tests

  1 files  ±0   50 suites  ±0   23s ⏱️ -2s
138 tests ±0  138 ✅ ±0  0 💤 ±0  0 ❌ ±0 
203 runs  ±0  203 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit ab65466. ± Comparison against base commit dc02e1c.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

C# Unit Tests

162 tests  ±0   162 ✅ ±0   19s ⏱️ -1s
 23 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit ab65466. ± Comparison against base commit dc02e1c.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
frontend/viewer/src/AppRoutes.svelte (1)

25-28: Avoid magic number for debounce delay.

200 should be a named constant so this behavior is easier to tune and reason about.

💡 Suggested tweak
+  const URL_SAVE_DEBOUNCE_MS = 200;
   onMount(() => {
     let timeout: ReturnType<typeof setTimeout>;
     const saveUrl = () => {
       clearTimeout(timeout);
       timeout = setTimeout(() => {
         const { pathname, search, hash } = document.location;
         void appStorage.lastUrl.set(pathname + search + hash);
-      }, 200);
+      }, URL_SAVE_DEBOUNCE_MS);
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/viewer/src/AppRoutes.svelte` around lines 25 - 28, Replace the magic
number 200 with a named constant (e.g., URL_SAVE_DEBOUNCE_MS) declared in module
scope of AppRoutes.svelte and use that constant when creating the timeout;
update the timeout logic that references the variable timeout and the call to
appStorage.lastUrl.set(pathname + search + hash) to use the new constant so the
debounce delay is easy to find and tune.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/FwLite/FwLiteShared/Services/JsonFilePreferencesService.cs`:
- Around line 76-90: Save() currently catches and logs all exceptions, causing
Set()/Remove() to appear successful even when disk writes fail; change Save() to
surface failures instead of swallowing them by either rethrowing the caught
exception after logging (throw;) or returning a failure result that callers must
check, and update Set() and Remove() to propagate that failure (i.e., throw on
Save failure or return/propagate a false result) so callers get an exception or
error when File.WriteAllText/File.Move fails; reference Save(), Set(), Remove(),
_logger and _filePath when making the change.

In `@frontend/viewer/src/lib/utils/storage-prop.svelte.ts`:
- Line 27: The constructor currently calls void this.load() and other spots
(lines ~48-54) fire-and-forget the async load which can leave this.loading true
and produce unhandled rejections if backend.get() rejects; update the code so
the load() promise is observed and errors are handled: either add a .catch(...)
to the void this.load() call(s) in the constructor and at the other
fire-and-forget sites to log/handle the error, and/or modify the load() method
(the function named load that calls backend.get()) to wrap its await in
try/catch and a finally that always sets this.loading = false; ensure references
to backend.get() remain and use the class method names (constructor, load) so
the loading flag cannot remain true and no unhandled rejection escapes.

---

Nitpick comments:
In `@frontend/viewer/src/AppRoutes.svelte`:
- Around line 25-28: Replace the magic number 200 with a named constant (e.g.,
URL_SAVE_DEBOUNCE_MS) declared in module scope of AppRoutes.svelte and use that
constant when creating the timeout; update the timeout logic that references the
variable timeout and the call to appStorage.lastUrl.set(pathname + search +
hash) to use the new constant so the debounce delay is easy to find and tune.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc02e1c and ab65466.

📒 Files selected for processing (13)
  • backend/FwLite/FwLiteMaui/App.xaml.cs
  • backend/FwLite/FwLiteShared/Services/JsonFilePreferencesService.cs
  • backend/FwLite/FwLiteShared/Services/PreferenceKey.cs
  • backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
  • backend/FwLite/FwLiteWeb/FwLiteWebKernel.cs
  • backend/FwLite/FwLiteWeb/Program.cs
  • frontend/viewer/src/AppRoutes.svelte
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/PreferenceKey.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/index.ts
  • frontend/viewer/src/lib/services/service-provider.ts
  • frontend/viewer/src/lib/utils/app-storage.svelte.ts
  • frontend/viewer/src/lib/utils/project-storage.svelte.ts
  • frontend/viewer/src/lib/utils/storage-prop.svelte.ts

Comment on lines +76 to +90
private void Save()
{
try
{
var directory = Path.GetDirectoryName(_filePath);
if (directory is not null) Directory.CreateDirectory(directory);
var json = JsonSerializer.Serialize(_cache, new JsonSerializerOptions { WriteIndented = true });
var tempPath = _filePath + ".tmp";
File.WriteAllText(tempPath, json);
File.Move(tempPath, _filePath, overwrite: true);
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to save preferences to {FilePath}", _filePath);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not swallow persistence failures in Save().

Set()/Remove() can appear successful even when disk writes fail, causing silent data loss for preference updates.

Proposed fix
 private void Save()
 {
     try
     {
         var directory = Path.GetDirectoryName(_filePath);
         if (directory is not null) Directory.CreateDirectory(directory);
         var json = JsonSerializer.Serialize(_cache, new JsonSerializerOptions { WriteIndented = true });
         var tempPath = _filePath + ".tmp";
         File.WriteAllText(tempPath, json);
         File.Move(tempPath, _filePath, overwrite: true);
     }
     catch (Exception e)
     {
         _logger.LogWarning(e, "Failed to save preferences to {FilePath}", _filePath);
+        throw;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void Save()
{
try
{
var directory = Path.GetDirectoryName(_filePath);
if (directory is not null) Directory.CreateDirectory(directory);
var json = JsonSerializer.Serialize(_cache, new JsonSerializerOptions { WriteIndented = true });
var tempPath = _filePath + ".tmp";
File.WriteAllText(tempPath, json);
File.Move(tempPath, _filePath, overwrite: true);
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to save preferences to {FilePath}", _filePath);
}
private void Save()
{
try
{
var directory = Path.GetDirectoryName(_filePath);
if (directory is not null) Directory.CreateDirectory(directory);
var json = JsonSerializer.Serialize(_cache, new JsonSerializerOptions { WriteIndented = true });
var tempPath = _filePath + ".tmp";
File.WriteAllText(tempPath, json);
File.Move(tempPath, _filePath, overwrite: true);
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to save preferences to {FilePath}", _filePath);
throw;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/FwLite/FwLiteShared/Services/JsonFilePreferencesService.cs` around
lines 76 - 90, Save() currently catches and logs all exceptions, causing
Set()/Remove() to appear successful even when disk writes fail; change Save() to
surface failures instead of swallowing them by either rethrowing the caught
exception after logging (throw;) or returning a failure result that callers must
check, and update Set() and Remove() to propagate that failure (i.e., throw on
Save failure or return/propagate a false result) so callers get an exception or
error when File.WriteAllText/File.Move fails; reference Save(), Set(), Remove(),
_logger and _filePath when making the change.

constructor(key: string, backend: IPreferencesService) {
this.#key = key;
this.#backend = backend;
void this.load();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle load() failures to prevent unhandled rejections and stuck loading state.

If backend.get() rejects, the constructor’s fire-and-forget call can leave loading true forever.

Proposed fix
   private async load(): Promise<void> {
-    const value = await this.#backend.get(this.#key);
-    if (!this.#hasBeenSet) {
-      this.#value = value ?? '';
-      this.#hasBeenSet = true;
-    }
+    try {
+      const value = await this.#backend.get(this.#key);
+      if (!this.#hasBeenSet) {
+        this.#value = value ?? '';
+      }
+    } catch (error) {
+      console.warn(`Failed to load storage key '${this.#key}'`, error);
+    } finally {
+      if (!this.#hasBeenSet) {
+        this.#hasBeenSet = true;
+      }
+    }
   }

Also applies to: 48-54

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/viewer/src/lib/utils/storage-prop.svelte.ts` at line 27, The
constructor currently calls void this.load() and other spots (lines ~48-54)
fire-and-forget the async load which can leave this.loading true and produce
unhandled rejections if backend.get() rejects; update the code so the load()
promise is observed and errors are handled: either add a .catch(...) to the void
this.load() call(s) in the constructor and at the other fire-and-forget sites to
log/handle the error, and/or modify the load() method (the function named load
that calls backend.get()) to wrap its await in try/catch and a finally that
always sets this.loading = false; ensure references to backend.get() remain and
use the class method names (constructor, load) so the loading flag cannot remain
true and no unhandled rejection escapes.

@argos-ci
Copy link

argos-ci bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Awaiting the start of a new Argos build…

@myieye myieye changed the title Use JSON file for user preferences on Linux Track and restore latest URL Mar 3, 2026
@myieye myieye changed the base branch from develop to use-json-file-for-preferences-on-linux March 3, 2026 16:49
@myieye myieye force-pushed the claude/track-url-session-restore-9Wd2q branch from ab65466 to 1545e20 Compare March 3, 2026 16:50
@myieye myieye marked this pull request as draft March 3, 2026 16:51
@myieye myieye force-pushed the use-json-file-for-preferences-on-linux branch from 8ba07d0 to fb7c92c Compare March 4, 2026 07:46
@myieye myieye force-pushed the claude/track-url-session-restore-9Wd2q branch from 1545e20 to 5f944e1 Compare March 4, 2026 08:00
Base automatically changed from use-json-file-for-preferences-on-linux to develop March 4, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use JSON file for user preferences on Linux

1 participant