Skip to content

Refine web navigation chrome#59

Merged
TraderSamwise merged 2 commits into
masterfrom
chore/ui-cleanup
Jun 6, 2026
Merged

Refine web navigation chrome#59
TraderSamwise merged 2 commits into
masterfrom
chore/ui-cleanup

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • move project-scoped Inbox and Threads into the desktop sidebar views list
  • add global Inbox and Threads top-bar routes that flatten online projects
  • simplify narrow bottom navigation and fix Library refresh flicker

Verification

  • cd app && yarn typecheck
  • pre-push: yarn typecheck && yarn lint && yarn test

Summary by CodeRabbit

  • New Features

    • Global inbox aggregating notifications across all projects
    • Global threads view displaying messages from all projects
    • Added top navigation buttons for quick access to global notifications and threads
  • Improvements

    • Default starting tab now set to Project
    • Simplified mobile bottom tabs (Topology, Project, Library)
    • Sidebar now shows a primary navigation stack in drawer and desktop layouts
    • Auth menu shows a "Local" badge and adjusts sign-out visibility
  • Bug Fixes

    • Improved library refresh logic and loading/empty messaging

@vercel

vercel Bot commented Jun 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jun 6, 2026 3:09am

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Ready to act? Review this PR in Change Stack to turn feedback into patch suggestions you can inspect and refine.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54c9ee0a-0819-4c91-8c95-32386d95e5e7

📥 Commits

Reviewing files that changed from the base of the PR and between 76dc23c and bb207b3.

📒 Files selected for processing (7)
  • app/app/(main)/(tabs)/(threads)/threads.tsx
  • app/app/(main)/(tabs)/library/index.tsx
  • app/app/(main)/global-notifications.tsx
  • app/app/(main)/global-threads.tsx
  • app/components/ProjectSidebar.tsx
  • app/lib/view-location.ts
  • app/stores/ui.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/app/(main)/(tabs)/library/index.tsx
  • app/app/(main)/global-notifications.tsx
  • app/app/(main)/global-threads.tsx
  • app/components/ProjectSidebar.tsx

📝 Walkthrough

Walkthrough

This PR adds Global Notifications and Global Threads screens that aggregate data across online projects, registers them as root stack routes, refactors ProjectSidebar to a primary-nav mode with a SidebarMode atom, simplifies mobile tabs to three entries, adds TopBar route buttons, adjusts AuthMenu local-mode rendering, and fixes stale-closure behavior in LibraryScreen.

Changes

Global Notifications & Threads with Navigation Restructuring

Layer / File(s) Summary
Global Notifications Screen
app/app/(main)/global-notifications.tsx
New cross-project inbox aggregating notifications from online projects, filtering cleared items, aggregating per-project errors, sorting unread-first, and navigating to agent chats or project notifications.
Global Threads Screen
app/app/(main)/global-threads.tsx
New cross-project threads view fetching thread summaries in parallel, flattening and sorting by recent activity, aggregating per-project errors, and providing navigation to thread detail screens.
Root Layout and Tab Navigation
app/app/(main)/_layout.tsx, app/app/(main)/(tabs)/_layout.tsx
Registers global-notifications and global-threads as top-level routes and changes tabs initialRouteName from (dashboard) to project.
TopBar Route Navigation
app/components/TopBar.tsx
Adds TopBarRouteButton and replaces the previous bell control with route buttons for Global Threads and Global Inbox.
ProjectSidebar Primary Navigation
app/components/ProjectSidebar.tsx
Refactors sidebar to use a SidebarMode (dashboard
Mobile Tab Bar Simplification
app/components/MobileTabBar.tsx
Reduces bottom tabs to Topology, Project, Library; removes unread-count/badge logic and simplifies icon rendering.
AppShell Sidebar Integration
app/components/AppShell.tsx
Passes showPrimaryNav={false} to ProjectSidebar in the mobile drawer.
AuthMenu Local Mode Handling
app/components/AuthMenu.tsx
Removes early-return local badge UI, conditionally hides Sign Out in local mode, and switches trigger between Local badge and user initials.
Library Screen Stale Closure Fix
app/app/(main)/(tabs)/library/index.tsx
Uses refs for endpoint and getToken, adds a refresh sequence guard to ignore stale async results, short-circuits on null endpoint, and makes empty-list header conditional on loading.
Threads selected state
app/app/(main)/(tabs)/(threads)/threads.tsx
Reads optional global threadId param, computes selectedThreadId, and applies selected styling to the matching thread row.
View params & UI store
app/lib/view-location.ts, app/stores/ui.ts
Adds optional threadId to AimuxViewParams and updates mergeViewParams; introduces SidebarMode type and sidebarModeAtom.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • TraderSamwise/aimux#3: Related navigation work touching MobileTabBar.tsx and sidebar wiring that overlaps with this PR's tab and sidebar changes.
  • TraderSamwise/aimux#14: Edits ProjectSidebar.tsx styling and layout that may overlap with the primary-nav refactor here.
  • TraderSamwise/aimux#13: Prior AppShell/sidebar integration changes that intersect with this PR's showPrimaryNav prop updates.

Poem

🐰 I hop through projects, peeking at each thread and bell,
Inbox and threads now gather where the online projects dwell,
Tabs trimmed to three, the sidebar learned a new mode,
Refs chase stale closures down a safer, steadier road,
A rabbit claps — refresh, navigate, and onward we go!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Refine web navigation chrome' is vague and generic, using non-descriptive terminology that does not clearly convey the specific changes in the changeset. Consider a more specific title that captures the main changes, such as 'Add global notifications/threads and refactor sidebar navigation' or similar descriptive phrasing.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/ui-cleanup

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

app/app/(main)/(tabs)/(threads)/threads.tsx

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

app/app/(main)/(tabs)/library/index.tsx

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

app/app/(main)/global-notifications.tsx

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 4 others

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (2)
app/components/TopBar.tsx (1)

40-40: 💤 Low value

Consider using theme tokens instead of hardcoded colors.

The icon colors are hardcoded (#fafafa and #a1a1aa) while the rest of the component uses theme-based className props (e.g., text-foreground, text-muted-foreground). For consistency and maintainability, consider extracting these colors from the theme system or using NativeWind's color utilities.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/TopBar.tsx` at line 40, The Icon component in TopBar uses
hardcoded colors ("`#fafafa`" and "`#a1a1aa`"); change this to use the app theme
tokens or NativeWind utilities instead—e.g., derive the color from the theme
(useTheme or your theme object) or map the active boolean to theme class names
(like "text-foreground" / "text-muted-foreground") and pass that value to Icon
(or wrap Icon in a styled View) so colors come from the shared theme instead of
hardcoded hex values; update the Icon usage in TopBar to reference those theme
tokens or className-driven colors.
app/components/MobileTabBar.tsx (1)

60-60: 💤 Low value

Consider dynamic icon color for active state consistency.

The icon uses a hardcoded #a1a1aa (zinc-400) color that does not change based on active state, while the text below dynamically switches between text-foreground and text-muted-foreground. For better visual consistency, the icon color could also reflect the active state.

♻️ Proposed refactor for dynamic icon color
-            <Icon size={20} color="`#a1a1aa`" />
+            <Icon size={20} className={active ? "text-foreground" : "text-muted-foreground"} />

Note: This assumes lucide-react-native supports className for color. If not, use conditional color prop:

-            <Icon size={20} color="`#a1a1aa`" />
+            <Icon size={20} color={active ? "`#09090b`" : "`#a1a1aa`"} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/MobileTabBar.tsx` at line 60, Replace the hardcoded Icon color
with the same active-state logic used for the label so the icon reflects
selection; instead of <Icon size={20} color="`#a1a1aa`" />, derive the color from
the tab's active flag (e.g., isActive or active) and pass that value to the Icon
via its color prop (or className if lucide-react-native supports it), using the
same tokens/values that drive the label classes (text-foreground when active,
text-muted-foreground when inactive) so both icon and text stay visually
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/app/`(main)/(tabs)/library/index.tsx:
- Around line 79-104: The refresh function can race: a previous request for a
different endpoint may resolve after a later request and overwrite state; fix
refresh (the async function named refresh which calls listProjectLibrary) by
recording the currentEndpoint at start (already assigned to currentEndpoint) and
before committing results call setDocuments and setError only if
endpointRef.current === currentEndpoint (or otherwise track a monotonic
requestId/nonce stored in a ref and only apply results when it matches); ensure
this check is applied both in the success path (after setDocuments) and in the
catch path before calling setError so stale responses are ignored when
endpointKey/endpointRef changed.

In `@app/app/`(main)/global-notifications.tsx:
- Around line 158-165: The empty-state branch currently shows "All caught up"
whenever rows.length === 0 even if fetches failed; update the render condition
to distinguish a genuine empty inbox from a failure by introducing or using an
error flag (e.g., hasFetchError or fetchError) and change the check to show the
success empty state only when rows.length === 0 && !loading && !hasFetchError;
otherwise render the failure banner or an error-specific message. Locate the
conditional that renders the Card (the JSX using rows and loading) and adjust
the logic to consult the error flag (or set one where project fetches are
handled) so failed requests do not display the "All caught up" message.
- Around line 65-110: Make refresh last-write-wins by introducing a local
request id checked before committing results: in the refresh function generate
an incrementing id (e.g., refreshId / latestRefreshIdRef) when starting, capture
it in the async closure, and only call setRows and setErrors if the captured id
=== latestRefreshIdRef.current; also add a top-level try/catch around the whole
refresh body (wrapping the getToken call and Promise.allSettled) so refresh-wide
failures (like getToken throwing) push a descriptive error into setErrors
instead of silently falling through, and ensure setLoading(false) still runs in
finally. Update references to onlineProjectsRef, refresh, getToken, setRows,
setErrors, and onlineProjectKey accordingly.

In `@app/app/`(main)/global-threads.tsx:
- Around line 128-135: The current empty-state Card in global-threads.tsx shows
"No threads" even when all project fetches failed; update the render logic
around rows, errors and loading so that when errors.length > 0 && rows.length
=== 0 you render an error-only empty state (e.g., a Card with an error
title/message) instead of the success "No threads" message. Locate the JSX block
using rows, loading and the Card/Text elements and add a branch that checks
errors.length > 0 && rows.length === 0 first, rendering the error UI; keep the
existing loading check for the "Loading threads..." text and fall back to the
original "No threads" when there are no errors and rows is empty.
- Around line 52-94: The refresh function is vulnerable to races and loses
refresh-wide errors; make it last-write-wins by generating a unique requestId at
the start of refresh (e.g., const reqId = ++refreshIdRef.current), capture it in
the async flow and before committing any state (setRows, setErrors, setLoading)
verify reqId === refreshIdRef.current so only the latest refresh writes state,
and also catch errors from getToken (and other top-level failures) to push a
refresh-wide error message into setErrors instead of leaving the UI empty;
update the surrounding hooks to use a refreshIdRef (useRef<number>) and keep
onlineProjectsRef, getToken, setRows, setErrors, and setLoading checks in place,
and ensure the useEffect that calls refresh still depends on onlineProjectKey
and refresh.
- Around line 138-143: Update the Pressable onPress in global-threads.tsx so the
tapped row's thread id is passed to the threads view and/or a selected-thread
atom is set: change the router.navigate(buildViewHref("/threads", { project:
row.projectPath })) call inside the rows.map(…) Pressable to include the thread
id (e.g., threadId: row.thread.thread.id) in the route params and/or call the
app's selected-thread Jotai setter (e.g.,
setSelectedThreadAtom(row.thread.thread.id)) before navigating; ensure the
threads.tsx reader consumes that threadId or selected-thread atom to render the
tapped thread.

In `@app/components/ProjectSidebar.tsx`:
- Around line 610-621: The component initializes sidebarMode to "dashboard" for
routeTab but the usePrePaintEffect sets sidebarMode to "views" when routeTab is
"inbox" or "threads", causing a flash; update the initialization logic (where
sidebarMode is initially set) to include routeTab === "inbox" and routeTab ===
"threads" in the same condition used by usePrePaintEffect (or alternatively
change usePrePaintEffect to match the existing initializer), so that the initial
value of sidebarMode and the effect are consistent; specifically adjust the
initializer that reads routeTab and showPrimaryNav and the setSidebarMode usage
so both include "inbox" and "threads".
- Around line 560-564: ProjectSidebar currently uses local useState for
sidebarMode which conflicts with the repo guideline to manage client/UI state
via Jotai and also initializes differently than usePrePaintEffect causing a
mount flip; replace the local state with a Jotai-backed atom (or derive from
existing atoms in app/stores/ui) so sidebarMode and setSidebarMode use the atom
instead of useState, and change the initial/derived value logic to match the
pre-paint effect condition (treat routeTab "project" | "topology" | "library" |
"inbox" | "threads" as "views" when showPrimaryNav is true) to avoid the extra
mode flip on mount (update references to sidebarMode and setSidebarMode in
ProjectSidebar accordingly).

---

Nitpick comments:
In `@app/components/MobileTabBar.tsx`:
- Line 60: Replace the hardcoded Icon color with the same active-state logic
used for the label so the icon reflects selection; instead of <Icon size={20}
color="`#a1a1aa`" />, derive the color from the tab's active flag (e.g., isActive
or active) and pass that value to the Icon via its color prop (or className if
lucide-react-native supports it), using the same tokens/values that drive the
label classes (text-foreground when active, text-muted-foreground when inactive)
so both icon and text stay visually consistent.

In `@app/components/TopBar.tsx`:
- Line 40: The Icon component in TopBar uses hardcoded colors ("`#fafafa`" and
"`#a1a1aa`"); change this to use the app theme tokens or NativeWind utilities
instead—e.g., derive the color from the theme (useTheme or your theme object) or
map the active boolean to theme class names (like "text-foreground" /
"text-muted-foreground") and pass that value to Icon (or wrap Icon in a styled
View) so colors come from the shared theme instead of hardcoded hex values;
update the Icon usage in TopBar to reference those theme tokens or
className-driven colors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 664a9784-d17b-46e9-9236-6aa6f92a5afa

📥 Commits

Reviewing files that changed from the base of the PR and between c5dced4 and 76dc23c.

📒 Files selected for processing (10)
  • app/app/(main)/(tabs)/_layout.tsx
  • app/app/(main)/(tabs)/library/index.tsx
  • app/app/(main)/_layout.tsx
  • app/app/(main)/global-notifications.tsx
  • app/app/(main)/global-threads.tsx
  • app/components/AppShell.tsx
  • app/components/AuthMenu.tsx
  • app/components/MobileTabBar.tsx
  • app/components/ProjectSidebar.tsx
  • app/components/TopBar.tsx

Comment thread app/app/(main)/(tabs)/library/index.tsx
Comment thread app/app/(main)/global-notifications.tsx
Comment thread app/app/(main)/global-notifications.tsx Outdated
Comment thread app/app/(main)/global-threads.tsx
Comment thread app/app/(main)/global-threads.tsx Outdated
Comment thread app/app/(main)/global-threads.tsx
Comment thread app/components/ProjectSidebar.tsx Outdated
Comment thread app/components/ProjectSidebar.tsx Outdated
@TraderSamwise
TraderSamwise merged commit b4368f8 into master Jun 6, 2026
3 checks passed
@TraderSamwise
TraderSamwise deleted the chore/ui-cleanup branch June 6, 2026 03:12
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.

1 participant