Skip to content

feat: log viewer, expandable work log errors, and custom accent presets#18

Merged
aaditagrawal merged 8 commits intomainfrom
feat/log-viewer
Mar 16, 2026
Merged

feat: log viewer, expandable work log errors, and custom accent presets#18
aaditagrawal merged 8 commits intomainfrom
feat/log-viewer

Conversation

@aaditagrawal
Copy link
Copy Markdown
Owner

@aaditagrawal aaditagrawal commented Mar 16, 2026

Summary

This PR adds three user-facing features to improve debugging, error visibility, and personalization in T3 Code.

1. Log Viewer in Settings

A new Logs section in Settings that works in both the web app and desktop (Electron) modes:

  • Log directory path displayed with selectable text so users can easily copy it
  • "Show in File Manager" button (desktop only) opens the log directory in the system file manager
  • "View in App" button loads log files directly in-app with:
    • A file selector dropdown to switch between log files (e.g. server.log, desktop-main.log)
    • A Refresh button to reload the current file
    • Syntax highlighting for the Effect logger key=value format:
      • Timestamps dimmed (text-zinc-500)
      • Log levels color-coded: Info (blue), Warning (amber), Error (red), Debug (dim), Fatal (bold red)
      • Fiber IDs in subtle violet
      • Message keys dimmed, message values in normal foreground
    • Auto-scroll to bottom on file load

Architecture:

  • Server-side WS API endpoints (logs.getDir, logs.list, logs.read) with path traversal protection
  • Desktop IPC channels (desktop:log-dir, desktop:log-list, desktop:log-read, desktop:log-open-dir) for Electron
  • DesktopBridge and NativeApi contract updates
  • The Logs section is always visible in settings (not gated behind desktop bridge), since the WS API works everywhere

2. Expandable Work Log Entries

Work log entries in the chat timeline (error messages, tool outputs, etc.) that were previously truncated with CSS truncate and only visible via hover tooltip are now click-to-expand:

  • Clicking a truncated entry toggles between single-line ellipsis and full wrapped text
  • Expandable rows show a cursor-pointer visual cue
  • Icon alignment uses items-start so the icon stays at the top when text wraps
  • This is especially useful for long error messages like "Provider turn interrupt failed - Error: Provider validation failed in ProviderService.interruptTurn: Cannot recover thread '36649b16-5fab-45...'" which are now fully readable

3. Custom Accent Color Presets

Users can now save custom accent colors as reusable presets:

  • A "Save as Preset" button appears in the custom color picker row (right-aligned alongside Reset)
  • Clicking it reveals a themed inline text input (not a browser window.prompt) where users type a name and hit Save or press Enter
  • Press Escape or click away to cancel
  • Saved presets appear alongside the built-in presets (Blue, Emerald, Amber, Rose, Violet) with the same pill button style
  • Each custom preset shows an x button on hover to delete it
  • Unlimited presets, persisted in localStorage via customAccentPresets in AppSettingsSchema
  • The Save button is hidden when the current color already matches an existing preset (built-in or custom)

4. Upstream Sync

Merged upstream pingdotgg/t3code change e6d9a271 (fix GitHub bug report issue template for screenshots).

Files Changed

File Changes
packages/contracts/src/ipc.ts DesktopBridge: getLogDir, listLogFiles, readLogFile, openLogDir. NativeApi: logs.getDir/list/read
packages/contracts/src/ws.ts WS methods: logs.getDir, logs.list, logs.read with request body schemas
apps/desktop/src/main.ts IPC handlers for log dir, list, read, and open
apps/desktop/src/preload.ts Bridge exposure for all 4 log IPC channels
apps/server/src/wsServer.ts Route handlers for logs.getDir, logs.list, logs.read with path traversal guard
apps/web/src/wsNativeApi.ts logs.getDir/list/read WS transport calls
apps/web/src/routes/_chat.settings.tsx Logs section UI, syntax highlighter, custom accent preset UI with inline naming
apps/web/src/appSettings.ts customAccentPresets array in settings schema
apps/web/src/components/chat/MessagesTimeline.tsx Click-to-expand SimpleWorkEntryRow

Test plan

  • bun typecheck — 7/7 packages pass
  • bun run fmt:check — clean
  • Manual: Open Settings > Logs, verify log directory path shows
  • Manual: Click "View in App", verify file selector and syntax-highlighted content
  • Manual: Desktop: click "Show in File Manager", verify system file manager opens
  • Manual: Trigger a provider error, verify the work log entry is clickable and expands to full text
  • Manual: Pick a custom accent color, click "Save as Preset", type a name, verify it appears as a pill
  • Manual: Hover a custom preset pill, click x to delete, verify it's removed
  • Manual: Verify saved presets persist across page reloads

Summary by CodeRabbit

  • New Features

    • Integrated log viewer in Settings: list, open, and read log files with syntax-highlighted display and refresh controls; option to open the log directory in the file manager.
    • Save and manage custom accent color presets for easy theme switching.
    • Enhanced bug report UI: renamed logs field and added optional attachments upload (images, videos, logs, text, JSON, ZIP).
  • Documentation

    • Minor wording correction in maintainability notes.

UtkarshUsername and others added 7 commits March 15, 2026 22:31
- Add server-side WS API for logs (logs.getDir, logs.list, logs.read)
  so log viewing works in both web and desktop modes
- Settings Logs section: show log directory path, "Show in File Manager"
  (desktop only), and in-app log viewer with file selector
- Desktop IPC: getLogDir, listLogFiles, readLogFile, openLogDir channels
- Work log entries are now click-to-expand when truncated, showing the
  full error/detail text instead of requiring hover
Colorize key=value log tokens: timestamps (dim), levels (blue/amber/red
by severity), fiber IDs (violet), and message content (foreground).
Uses inline regex parsing — no external dependency.
- Add customAccentPresets array to app settings (persisted in localStorage)
- Custom presets appear alongside built-in presets with an x to remove
- "Save as Preset" button in the custom color row prompts for a name
- Move Reset and Save as Preset to the right side of the color picker row
Use a themed inline text input + Save button instead of window.prompt()
for naming custom accent color presets. Escape or blur cancels.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

Adds log-access features across desktop, server, and web: new IPC channels and preload APIs for desktop, WebSocket methods and handlers on server, a web-native API and Settings log viewer with highlighting, plus minor docs and settings schema updates and an updated bug report template.

Changes

Cohort / File(s) Summary
Issue template & docs
.github/ISSUE_TEMPLATE/bug_report.yml, AGENTS.md
Changed bug report template label and added optional attachments upload; minor grammar correction in AGENTS.md.
Desktop IPC & preload
apps/desktop/src/main.ts, apps/desktop/src/preload.ts
Added IPC channels/handlers: desktop:log-dir, desktop:log-list, desktop:log-read, desktop:log-open-dir; exposed DesktopBridge methods getLogDir, listLogFiles, readLogFile, openLogDir. Includes path-traversal validation and idempotent handler registration.
Server WebSocket handlers
apps/server/src/wsServer.ts
Added WS methods handling: logsGetDir, logsList, logsRead — return log dir, list .log files, and read file contents with error mapping to RouteRequestError.
Contracts / schemas
packages/contracts/src/ipc.ts, packages/contracts/src/ws.ts
Extended public IPC and WS contracts: added DesktopBridge methods and NativeApi logs namespace; added WS_METHODS entries and request body schemas for log operations.
Web native API & settings schema
apps/web/src/wsNativeApi.ts, apps/web/src/appSettings.ts
Added logs namespace to WsNativeApi (getDir, list, read). Extended AppSettingsSchema with customAccentPresets field.
Web UI: Settings log viewer & chat tweaks
apps/web/src/routes/_chat.settings.tsx, apps/web/src/components/chat/MessagesTimeline.tsx
Implemented a Settings log viewer: fetch dir, list files, read file, inline highlighted viewer, open-in-app/file-manager actions; MessagesTimeline rows made expandable for previews.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Web UI (Settings)
    participant Api as WsNativeApi
    participant Transport as WS Transport
    participant Server as App Server
    participant FS as File System

    UI->>Api: logs.list()
    Api->>Transport: request logsList
    Transport->>Server: logsList request
    Server->>FS: read log directory
    FS-->>Server: list of .log files
    Server-->>Transport: files list
    Transport-->>Api: files list
    Api-->>UI: files list
Loading
sequenceDiagram
    participant Renderer as Desktop Renderer
    participant Preload as Preload Bridge
    participant Main as Main Process
    participant FS2 as File System / Shell

    Renderer->>Preload: invoke listLogFiles()
    Preload->>Main: ipc invoke desktop:log-list
    Main->>FS2: read LOG_DIR, filter *.log
    FS2-->>Main: file list
    Main-->>Preload: file list
    Preload-->>Renderer: file list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

size:XL

Poem

🐰 I hopped through folders, sniffed each log and string,
IPC and WS now help the messages sing,
Presets and previews make the UI gleam,
A bunny cheers for every saved log dream! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main features added: log viewer, expandable work log errors, and custom accent presets, all clearly present in the changeset.
Description check ✅ Passed The description comprehensively covers all required sections: What Changed (detailed feature descriptions), Why (user benefits for debugging/visibility/personalization), and includes a files changed table. UI changes are well-documented with before/after behavior descriptions.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/log-viewer
📝 Coding Plan
  • Generate coding plan for human review comments

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

@github-actions github-actions bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 effective changed lines (test files excluded in mixed PRs). labels Mar 16, 2026
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (5)
apps/web/src/appSettings.ts (1)

106-115: Consider adding length constraints for consistency.

Other string fields in this schema use isMaxLength checks (e.g., accentColor has isMaxLength(16), paths have isMaxLength(4096)). The label and value fields in customAccentPresets lack these constraints.

💡 Optional: Add length constraints
   customAccentPresets: Schema.Array(
     Schema.Struct({
-      label: Schema.String,
-      value: Schema.String,
+      label: Schema.String.check(Schema.isMaxLength(64)),
+      value: Schema.String.check(Schema.isMaxLength(16)),
     }),
   ).pipe(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/appSettings.ts` around lines 106 - 115, customAccentPresets
schema entries for label and value currently use plain Schema.String; add length
constraints using Schema.isMaxLength to match the rest of the file (e.g., wrap
Schema.String for both label and value with Schema.isMaxLength(16) or another
appropriate limit) so the Schema.Struct in customAccentPresets enforces max
lengths consistently; update the Schema.Struct that defines label and value to
use the constrained string schema.
apps/web/src/components/chat/MessagesTimeline.tsx (1)

754-757: Consider adding keyboard accessibility for the clickable container.

The clickable div lacks keyboard navigation support. Users relying on keyboard navigation cannot toggle expansion.

♿ Add keyboard accessibility
     <div
-      className={cn("rounded-lg px-1 py-1", isExpandable && "cursor-pointer")}
+      className={cn("rounded-lg px-1 py-1", isExpandable && "cursor-pointer")}
+      role={isExpandable ? "button" : undefined}
+      tabIndex={isExpandable ? 0 : undefined}
       onClick={isExpandable ? () => setExpanded((prev) => !prev) : undefined}
+      onKeyDown={
+        isExpandable
+          ? (e) => {
+              if (e.key === "Enter" || e.key === " ") {
+                e.preventDefault();
+                setExpanded((prev) => !prev);
+              }
+            }
+          : undefined
+      }
     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/chat/MessagesTimeline.tsx` around lines 754 - 757,
The clickable container in MessagesTimeline lacks keyboard accessibility; update
the div that uses isExpandable and setExpanded to behave like a button by adding
tabIndex={0}, role="button", and aria-expanded={expanded} (or aria-expanded
based on setExpanded state), and implement an onKeyDown handler that toggles
expansion when Enter or Space is pressed (calling the same toggle logic used in
onClick) while preventing default for Space to avoid page scroll; ensure these
changes reference the existing isExpandable, setExpanded, and expanded state
variables so keyboard users can toggle the container.
apps/web/src/routes/_chat.settings.tsx (3)

1358-1382: Consider extracting the log file read logic to reduce duplication.

The file reading and scroll-to-bottom logic is duplicated between the onValueChange handler (lines 1331-1344) and the Refresh button onClick (lines 1362-1378). Extracting this to a shared helper would improve maintainability.

💡 Optional: Extract shared logic
const loadLogFile = useCallback(async (filename: string) => {
  setIsLoadingLogs(true);
  try {
    const api = ensureNativeApi();
    const result = await api.logs.read(filename);
    setLogContent(result.content);
    requestAnimationFrame(() => {
      if (logViewerRef.current) {
        logViewerRef.current.scrollTop = logViewerRef.current.scrollHeight;
      }
    });
  } catch {
    setLogContent("Failed to read log file.");
  } finally {
    setIsLoadingLogs(false);
  }
}, []);

Then use loadLogFile(value) in both handlers.

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

In `@apps/web/src/routes/_chat.settings.tsx` around lines 1358 - 1382, Extract the
duplicated log-reading and scroll-to-bottom logic used in the onValueChange
handler and the Refresh Button onClick into a shared async helper (e.g.,
loadLogFile) and call that from both places; the helper should accept the
filename, call ensureNativeApi() and api.logs.read(filename),
setIsLoadingLogs(true)/false around the call, setLogContent(result.content) or
the failure message on error, and perform the requestAnimationFrame scroll logic
using logViewerRef.current to scroll to scrollHeight so both handlers reuse the
same code.

670-676: Consider validating for duplicate preset names before saving.

The form submits the new preset without checking if a preset with the same name and value already exists in customAccentPresets. While the "Save as Preset" button is hidden when the color matches an existing preset, this relies on color matching only—a user could potentially create presets with duplicate names.

💡 Optional: Add duplicate name validation
 onSubmit={(e) => {
   e.preventDefault();
   const name = presetNameInput.trim();
   if (!name) return;
+  const isDuplicate = settings.customAccentPresets.some(
+    (p) => p.label.toLowerCase() === name.toLowerCase()
+  );
+  if (isDuplicate) return;
   updateSettings({
     customAccentPresets: [
       ...settings.customAccentPresets,
       { label: name, value: accentColor },
     ],
   });
   setPresetNameInput(null);
 }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/_chat.settings.tsx` around lines 670 - 676, Before
calling updateSettings to append the new preset, check customAccentPresets for
an existing preset with the same label (and optionally same value) and prevent
adding duplicates; if a matching preset is found, do not call updateSettings and
instead handle validation (e.g., show a validation error or clear the input) via
setPresetNameInput or existing state. Update the logic around updateSettings,
customAccentPresets and setPresetNameInput to perform this duplicate-name (and
optional name+value) check and early-return/notify instead of blindly pushing
the new preset.

600-614: Consider using a separate clickable element for better accessibility.

The delete control uses <span role="button"> nested inside a <button>. While role="button" provides some accessibility, it lacks keyboard focusability and doesn't receive keyboard events automatically. However, since nesting a <button> inside a <button> is invalid HTML, the current approach is a reasonable compromise for this hover-reveal pattern.

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

In `@apps/web/src/routes/_chat.settings.tsx` around lines 600 - 614, Replace the
non-focusable span "button" with a real focusable control: make the delete
control a standalone <button type="button"> (with the same aria-label, className
and onClick behavior that calls updateSettings and stops propagation) rather
than using role="button" on a span; ensure it is not nested inside another
<button> (refactor the parent markup if needed) and keep keyboard activation
semantics (Enter/Space) by using the native button element; reference
updateSettings, settings.customAccentPresets, preset.value and preset.label to
locate and preserve the remove logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/appSettings.ts`:
- Around line 106-115: customAccentPresets schema entries for label and value
currently use plain Schema.String; add length constraints using
Schema.isMaxLength to match the rest of the file (e.g., wrap Schema.String for
both label and value with Schema.isMaxLength(16) or another appropriate limit)
so the Schema.Struct in customAccentPresets enforces max lengths consistently;
update the Schema.Struct that defines label and value to use the constrained
string schema.

In `@apps/web/src/components/chat/MessagesTimeline.tsx`:
- Around line 754-757: The clickable container in MessagesTimeline lacks
keyboard accessibility; update the div that uses isExpandable and setExpanded to
behave like a button by adding tabIndex={0}, role="button", and
aria-expanded={expanded} (or aria-expanded based on setExpanded state), and
implement an onKeyDown handler that toggles expansion when Enter or Space is
pressed (calling the same toggle logic used in onClick) while preventing default
for Space to avoid page scroll; ensure these changes reference the existing
isExpandable, setExpanded, and expanded state variables so keyboard users can
toggle the container.

In `@apps/web/src/routes/_chat.settings.tsx`:
- Around line 1358-1382: Extract the duplicated log-reading and scroll-to-bottom
logic used in the onValueChange handler and the Refresh Button onClick into a
shared async helper (e.g., loadLogFile) and call that from both places; the
helper should accept the filename, call ensureNativeApi() and
api.logs.read(filename), setIsLoadingLogs(true)/false around the call,
setLogContent(result.content) or the failure message on error, and perform the
requestAnimationFrame scroll logic using logViewerRef.current to scroll to
scrollHeight so both handlers reuse the same code.
- Around line 670-676: Before calling updateSettings to append the new preset,
check customAccentPresets for an existing preset with the same label (and
optionally same value) and prevent adding duplicates; if a matching preset is
found, do not call updateSettings and instead handle validation (e.g., show a
validation error or clear the input) via setPresetNameInput or existing state.
Update the logic around updateSettings, customAccentPresets and
setPresetNameInput to perform this duplicate-name (and optional name+value)
check and early-return/notify instead of blindly pushing the new preset.
- Around line 600-614: Replace the non-focusable span "button" with a real
focusable control: make the delete control a standalone <button type="button">
(with the same aria-label, className and onClick behavior that calls
updateSettings and stops propagation) rather than using role="button" on a span;
ensure it is not nested inside another <button> (refactor the parent markup if
needed) and keep keyboard activation semantics (Enter/Space) by using the native
button element; reference updateSettings, settings.customAccentPresets,
preset.value and preset.label to locate and preserve the remove logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5608475b-df68-485b-9d45-08a5247ef6e2

📥 Commits

Reviewing files that changed from the base of the PR and between 97fdfd4 and e3afdcf.

📒 Files selected for processing (11)
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • AGENTS.md
  • apps/desktop/src/main.ts
  • apps/desktop/src/preload.ts
  • apps/server/src/wsServer.ts
  • apps/web/src/appSettings.ts
  • apps/web/src/components/chat/MessagesTimeline.tsx
  • apps/web/src/routes/_chat.settings.tsx
  • apps/web/src/wsNativeApi.ts
  • packages/contracts/src/ipc.ts
  • packages/contracts/src/ws.ts

- Add maxLength constraints to customAccentPresets schema (64 for label, 16 for value)
- Add keyboard accessibility to expandable work log entries (role, tabIndex, Enter/Space, aria-expanded)
- Extract loadLogFile helper to deduplicate log reading + scroll logic
- Add duplicate preset name validation (case-insensitive)
- Replace nested span[role=button] with proper button elements for preset delete
Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (1)
apps/web/src/routes/_chat.settings.tsx (1)

706-718: Consider adding maxLength to enforce schema constraint.

The PR summary mentions maxLength constraints were added to the schema (label: 64 characters). Adding maxLength={64} to the input provides immediate feedback and prevents users from typing beyond the limit.

✨ Proposed enhancement
                        <Input
                          ref={presetNameRef}
                          className="h-7 w-32 py-0 text-xs leading-7"
                          placeholder="Preset name"
                          value={presetNameInput}
+                         maxLength={64}
                          onChange={(e) => setPresetNameInput(e.target.value)}
                          onKeyDown={(e) => {
                            if (e.key === "Escape") setPresetNameInput(null);
                          }}
                          onBlur={() => {
                            if (!presetNameInput.trim()) setPresetNameInput(null);
                          }}
                        />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/_chat.settings.tsx` around lines 706 - 718, The Input for
preset names (the component using ref={presetNameRef}, value={presetNameInput},
onChange={(e) => setPresetNameInput(e.target.value)}) should enforce the schema
max length: add maxLength={64} to the Input props and ensure onChange trims or
blocks extra characters (e.g., only call setPresetNameInput with
e.target.value.slice(0,64)) so the UI prevents typing beyond the 64-char schema
constraint and stays in sync with onBlur/onKeyDown logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/routes/_chat.settings.tsx`:
- Around line 342-348: The current useEffect calls ensureNativeApi()
synchronously which can throw when the native API is unavailable; wrap the
ensureNativeApi() call in a try-catch inside the useEffect (around the call to
ensureNativeApi and subsequent api.logs.getDir() promise) so any synchronous
exception is caught and ignored or handled, and only call api.logs.getDir() when
ensureNativeApi() succeeds; update the effect that references ensureNativeApi(),
api.logs.getDir(), and setLogDir accordingly.

---

Nitpick comments:
In `@apps/web/src/routes/_chat.settings.tsx`:
- Around line 706-718: The Input for preset names (the component using
ref={presetNameRef}, value={presetNameInput}, onChange={(e) =>
setPresetNameInput(e.target.value)}) should enforce the schema max length: add
maxLength={64} to the Input props and ensure onChange trims or blocks extra
characters (e.g., only call setPresetNameInput with e.target.value.slice(0,64))
so the UI prevents typing beyond the 64-char schema constraint and stays in sync
with onBlur/onKeyDown logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b6929142-f7d6-4d78-886e-cda852de2b72

📥 Commits

Reviewing files that changed from the base of the PR and between e3afdcf and 2f8f9bc.

📒 Files selected for processing (3)
  • apps/web/src/appSettings.ts
  • apps/web/src/components/chat/MessagesTimeline.tsx
  • apps/web/src/routes/_chat.settings.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/appSettings.ts

Comment on lines +342 to +348
useEffect(() => {
const api = ensureNativeApi();
void api.logs
.getDir()
.then((result) => setLogDir(result.dir))
.catch(() => {});
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Uncaught exception when native API is unavailable.

ensureNativeApi() throws synchronously if the native API is not found. Since the call is outside the .catch() chain, the exception won't be caught when running in web-only mode.

🛡️ Proposed fix to wrap in try-catch
   useEffect(() => {
+    try {
       const api = ensureNativeApi();
       void api.logs
         .getDir()
         .then((result) => setLogDir(result.dir))
         .catch(() => {});
+    } catch {
+      // Native API not available (web-only mode)
+    }
   }, []);
📝 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
useEffect(() => {
const api = ensureNativeApi();
void api.logs
.getDir()
.then((result) => setLogDir(result.dir))
.catch(() => {});
}, []);
useEffect(() => {
try {
const api = ensureNativeApi();
void api.logs
.getDir()
.then((result) => setLogDir(result.dir))
.catch(() => {});
} catch {
// Native API not available (web-only mode)
}
}, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/_chat.settings.tsx` around lines 342 - 348, The current
useEffect calls ensureNativeApi() synchronously which can throw when the native
API is unavailable; wrap the ensureNativeApi() call in a try-catch inside the
useEffect (around the call to ensureNativeApi and subsequent api.logs.getDir()
promise) so any synchronous exception is caught and ignored or handled, and only
call api.logs.getDir() when ensureNativeApi() succeeds; update the effect that
references ensureNativeApi(), api.logs.getDir(), and setLogDir accordingly.

@aaditagrawal aaditagrawal merged commit 72876f4 into main Mar 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants