Skip to content

Support TCP servers in native app discover page - #149

Merged
matheusfillipe merged 3 commits into
mainfrom
feat/update-discover-page-schema
Mar 9, 2026
Merged

matheusfillipe merged 3 commits into
mainfrom
feat/update-discover-page-schema

Conversation

@matheusfillipe

@matheusfillipe matheusfillipe commented Feb 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Platform-aware server discovery and selection (browser vs. desktop) with improved prefill of server address and port when adding a server.
  • Improvements

    • Better handling of WSS vs IRCS choices, including an option that locks the connection type when a server supports only one.
    • Search and filtering refined to respect platform capabilities and preserve name/description matching.

@coderabbitai

coderabbitai Bot commented Feb 24, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a6fdcf8b-aae1-4676-9e64-7f391bf701e1

📥 Commits

Reviewing files that changed from the base of the PR and between 4c8ee33 and f413c0e.

📒 Files selected for processing (4)
  • src/components/ui/AddServerModal.tsx
  • src/components/ui/HomeScreen.tsx
  • src/store/types.ts
  • tests/components/HomeScreen.test.ts

📝 Walkthrough

Walkthrough

Adds Tauri runtime detection and extends server discovery to support separate wss and ircs URIs. HomeScreen filtering and server-click handling now choose protocol and compute host/port based on platform; AddServerModal and store types accept WebSocket locking and useWebSocket flags. Tests added for visibility and prefill logic.

Changes

Cohort / File(s) Summary
HomeScreen (UI behaviour & model)
src/components/ui/HomeScreen.tsx
Replaced server+port model with DiscoverServer (wss?, ircs?, obsidian?). Added platform-aware filtering and handleServerClick that picks URI (ircs vs wss), parses host/port, and sets useWebSocket / lockWebSocket for prefilling AddServerModal.
Tauri detection utility
src/lib/tauri.ts
New global Window augmentation and exported isTauri() helper to detect Tauri runtime via window.__TAURI__.
AddServerModal (prefill UX & lock)
src/components/ui/AddServerModal.tsx
Simplified host/port composition to wss:// or ircs:// based on useWebSocket; added lockWebSocket support (disables toggling, updates labels/tooltips); improved host input display when connection info is locked.
Store types
src/store/types.ts
Added optional lockWebSocket?: boolean to ConnectionDetails in UI/store types.
Tests: HomeScreen logic
tests/components/HomeScreen.test.ts
New comprehensive tests: DiscoverServer type, isVisible and buildPrefill helpers, and test cases for browser vs Tauri visibility, prefill values, port parsing, defaults, and display/stripping behavior.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant HomeScreen
  participant PlatformDetector
  participant AddServerModal
  participant Store

  User->>HomeScreen: click server entry
  HomeScreen->>PlatformDetector: isTauri()
  PlatformDetector-->>HomeScreen: true/false
  HomeScreen->>HomeScreen: select protocol (ircs vs wss), parse host/port, decide useWebSocket & lockWebSocket
  HomeScreen->>AddServerModal: open(prefill: host, port, useWebSocket, lockWebSocket, title)
  AddServerModal->>Store: dispatch save/update (if user confirms)
  Store-->>AddServerModal: ack
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • fix/wss for all #148 — Modifies protocol selection and AddServerModal parsing/selection logic; closely related to wss/ircs handling changes.

Poem

🐰 Hop-hop, I sniff the runtime bright,

WSS or IRCS, I pick what's right.
Tauri whispers where to go,
Ports and hosts all in a row —
I nudge the modal, soft and light.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly summarizes the main change: adding support for TCP servers (via ircs and wss URIs) in the discover page of a native app, which aligns with the key modifications to the server data model and connection handling logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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/update-discover-page-schema

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
Copy Markdown

Pages Preview
Preview URL: https://feat-update-discover-page-sc.obsidianirc.pages.dev

Automated deployment preview for the PR in the Cloudflare Pages.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/ui/HomeScreen.tsx (1)

42-55: ⚠️ Potential issue | 🟠 Major

new URL() calls are unguarded and an absent URI leaves the modal in a broken state.

Two problems in handleServerClick:

  1. Unhandled TypeError from invalid URLs. Both new URL(uri).hostname (line 47) and the new URL(uri) inside uriPort (line 40) throw if uri is not a well-formed URL. The uri ? guard only catches falsy values, not malformed strings sourced from the remote JSON. One bad entry in the server list will crash the click handler silently.

  2. Empty host with disabled field. When the relevant URI field is absent (e.g., a server only has wss but no ircs, and the user is running in Tauri), uri is undefined, so host becomes "". Combined with disableServerConnectionInfo: true, the modal opens with a locked, empty host — the server is unconnectable and the user has no way to fix it.

🐛 Proposed fix
  const handleServerClick = (server: { name: string; description: string; wss?: string; ircs?: string }) => {
    const uri = isTauri() ? server.ircs : server.wss;
    const defaultPort = isTauri() ? "6697" : "443";

+   if (!uri) {
+     // This environment (browser vs native) has no supported URI for this server.
+     toggleAddServerModal(true, {
+       name: server.name,
+       host: "",
+       port: defaultPort,
+       nickname: "",
+       ui: { title: server.name },
+     });
+     return;
+   }
+
+   let hostname: string;
+   let port: string;
+   try {
+     const parsed = new URL(uri);
+     hostname = parsed.hostname;
+     port = parsed.port || defaultPort;
+   } catch {
+     console.error(`Invalid server URI: ${uri}`);
+     return;
+   }
+
    toggleAddServerModal(true, {
      name: server.name,
-     host: uri ? new URL(uri).hostname : "",
-     port: uri ? uriPort(uri, defaultPort) : defaultPort,
+     host: hostname,
+     port,
      nickname: "",
      ui: {
        disableServerConnectionInfo: true,
        title: server.name,
      },
    });
  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/HomeScreen.tsx` around lines 42 - 55, The click handler
handleServerClick currently calls new URL(uri) (and uriPort which also
constructs a URL) without guarding against malformed strings, causing a
TypeError and/or producing an empty, locked host when uri is undefined; wrap URL
parsing in a try/catch and validate uri before using new URL, falling back to a
safe host value (e.g., server.host || server.name || "") and a sane port default
when parsing fails, and ensure toggleAddServerModal is called with
disableServerConnectionInfo: false if the host is empty so the user can edit
connection info; also update uriPort (the function invoked for port extraction)
to return the default port on invalid/malformed uri instead of throwing.
🧹 Nitpick comments (1)
src/components/ui/HomeScreen.tsx (1)

39-40: Move uriPort outside the component.

uriPort is a pure function with no closure over component state or props. Defining it inside the component body recreates it on every render.

♻️ Proposed refactor
+const uriPort = (uri: string, defaultPort: string) =>
+  new URL(uri).port || defaultPort;
+
 const DiscoverGrid = () => {
   ...
-  const uriPort = (uri: string, defaultPort: string) =>
-    new URL(uri).port || defaultPort;
-
   const handleServerClick = ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/HomeScreen.tsx` around lines 39 - 40, The helper function
uriPort is defined inside the HomeScreen component causing it to be recreated
every render; move the const uriPort = (uri: string, defaultPort: string) => new
URL(uri).port || defaultPort; declaration to module/top-level scope (above the
HomeScreen component) so it is instantiated once, keep the same
signature/TypeScript types, and update any local references to call the
top-level uriPort; this keeps behavior identical while avoiding per-render
allocations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/ui/HomeScreen.tsx`:
- Line 42: Change the parameter type of handleServerClick to match the concrete
servers item shape instead of Record<string,string>: use the exact object type
(e.g. { name: string; description: string; wss?: string; ircs?: string }) or a
named alias/interface used for the servers state so optional properties wss and
ircs are allowed; update the function signature for handleServerClick
accordingly to remove the strict-mode mismatch with the servers array type.

---

Outside diff comments:
In `@src/components/ui/HomeScreen.tsx`:
- Around line 42-55: The click handler handleServerClick currently calls new
URL(uri) (and uriPort which also constructs a URL) without guarding against
malformed strings, causing a TypeError and/or producing an empty, locked host
when uri is undefined; wrap URL parsing in a try/catch and validate uri before
using new URL, falling back to a safe host value (e.g., server.host ||
server.name || "") and a sane port default when parsing fails, and ensure
toggleAddServerModal is called with disableServerConnectionInfo: false if the
host is empty so the user can edit connection info; also update uriPort (the
function invoked for port extraction) to return the default port on
invalid/malformed uri instead of throwing.

---

Nitpick comments:
In `@src/components/ui/HomeScreen.tsx`:
- Around line 39-40: The helper function uriPort is defined inside the
HomeScreen component causing it to be recreated every render; move the const
uriPort = (uri: string, defaultPort: string) => new URL(uri).port ||
defaultPort; declaration to module/top-level scope (above the HomeScreen
component) so it is instantiated once, keep the same signature/TypeScript types,
and update any local references to call the top-level uriPort; this keeps
behavior identical while avoiding per-render allocations.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb8f7ee and 78e0951.

📒 Files selected for processing (7)
  • src/App.tsx
  • src/components/layout/AppLayout.tsx
  • src/components/layout/ChatArea.tsx
  • src/components/ui/AddServerModal.tsx
  • src/components/ui/HomeScreen.tsx
  • src/hooks/useKeyboardResize.ts
  • src/lib/tauri.ts

Comment thread src/components/ui/HomeScreen.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/ui/HomeScreen.tsx (1)

43-43: ⚠️ Potential issue | 🟡 Minor

Reuse the server item type here instead of Record<string, string>.

The state on Line 12 stores items with optional wss/ircs, so Record<string, string> overstates what handleServerClick actually receives and masks the nullability this branch depends on. A shared ServerEntry type would keep the state and handler in sync.

#!/bin/bash
# Verify the current server item shape and the handler signature side by side.
# Expected: the handler parameter should reuse the same server-entry type as the state array.
rg -n -C2 'useState<|wss\?: string|ircs\?: string|handleServerClick|Record<string, string>' src/components/ui/HomeScreen.tsx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/HomeScreen.tsx` at line 43, Define or reuse a ServerEntry
type that matches the state shape (with optional wss?: string and ircs?: string)
and update both the state generic and the handleServerClick parameter to use
ServerEntry instead of Record<string, string>; specifically, declare/interface
ServerEntry { /* ... wss?: string; ircs?: string; other fields... */ } then
change useState<...> to use ServerEntry[] and change const handleServerClick =
(server: Record<string, string>) => to const handleServerClick = (server:
ServerEntry) => so the handler's nullability assumptions match the actual stored
items.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/ui/HomeScreen.tsx`:
- Around line 43-52: The handleServerClick handler opens a locked AddServer
modal even when platform-specific URI (server.ircs or server.wss) is missing;
update handleServerClick to first compute uri = isTauri() ? server.ircs :
server.wss and if uri is falsy call toggleAddServerModal with
ui.disableServerConnectionInfo set to false (and host/port left empty or default
editable values) so the user can enter connection details, otherwise continue to
parse new URL(uri).hostname and uriPort as now; reference handleServerClick,
toggleAddServerModal, server.ircs/server.wss, uriPort, and isTauri when making
the change.

---

Duplicate comments:
In `@src/components/ui/HomeScreen.tsx`:
- Line 43: Define or reuse a ServerEntry type that matches the state shape (with
optional wss?: string and ircs?: string) and update both the state generic and
the handleServerClick parameter to use ServerEntry instead of Record<string,
string>; specifically, declare/interface ServerEntry { /* ... wss?: string;
ircs?: string; other fields... */ } then change useState<...> to use
ServerEntry[] and change const handleServerClick = (server: Record<string,
string>) => to const handleServerClick = (server: ServerEntry) => so the
handler's nullability assumptions match the actual stored items.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 030387c1-d5f7-4e9d-8755-5e4fb273d69c

📥 Commits

Reviewing files that changed from the base of the PR and between 78e0951 and 4c8ee33.

📒 Files selected for processing (1)
  • src/components/ui/HomeScreen.tsx

Comment thread src/components/ui/HomeScreen.tsx Outdated
Comment on lines 43 to 52
const handleServerClick = (server: Record<string, string>) => {
const uri = isTauri() ? server.ircs : server.wss;
const defaultPort = isTauri() ? "6697" : "443";
toggleAddServerModal(true, {
name: server.name,
host: server.server || "", // Use empty string if server is undefined
port: server.port || "443", // Default to 443 if port is undefined
host: uri ? new URL(uri).hostname : "",
port: uri ? uriPort(uri, defaultPort) : defaultPort,
nickname: "", // Generate a default nickname
ui: {
disableServerConnectionInfo: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't open a locked server config when the current transport URI is missing.

wss/ircs are optional in the server shape, but this branch still opens the modal with host: "" and disableServerConnectionInfo: true when the platform-specific URI is absent. That leaves the user stuck with an invalid, non-editable connection.

Suggested fix
 const handleServerClick = (server: Record<string, string>) => {
   const uri = isTauri() ? server.ircs : server.wss;
+  if (!uri) {
+    return;
+  }
   const defaultPort = isTauri() ? "6697" : "443";
+  const parsed = new URL(uri);
   toggleAddServerModal(true, {
     name: server.name,
-    host: uri ? new URL(uri).hostname : "",
-    port: uri ? uriPort(uri, defaultPort) : defaultPort,
+    host: parsed.hostname,
+    port: parsed.port || defaultPort,
     nickname: "", // Generate a default nickname
     ui: {
       disableServerConnectionInfo: true,
       title: server.name,
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/HomeScreen.tsx` around lines 43 - 52, The handleServerClick
handler opens a locked AddServer modal even when platform-specific URI
(server.ircs or server.wss) is missing; update handleServerClick to first
compute uri = isTauri() ? server.ircs : server.wss and if uri is falsy call
toggleAddServerModal with ui.disableServerConnectionInfo set to false (and
host/port left empty or default editable values) so the user can enter
connection details, otherwise continue to parse new URL(uri).hostname and
uriPort as now; reference handleServerClick, toggleAddServerModal,
server.ircs/server.wss, uriPort, and isTauri when making the change.

@matheusfillipe
matheusfillipe merged commit 978e632 into main Mar 9, 2026
3 of 4 checks passed
@matheusfillipe
matheusfillipe deleted the feat/update-discover-page-schema branch March 9, 2026 08:58
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