Skip to content

feat(settings): unify panels under one consistent SettingsPanel template#4037

Merged
graycyrus merged 2 commits into
tinyhumansai:mainfrom
graycyrus:feat/settings-consistent-panel-template
Jun 24, 2026
Merged

feat(settings): unify panels under one consistent SettingsPanel template#4037
graycyrus merged 2 commits into
tinyhumansai:mainfrom
graycyrus:feat/settings-consistent-panel-template

Conversation

@graycyrus

@graycyrus graycyrus commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Introduce a single SettingsPanel template (app/src/components/settings/layout/SettingsPanel.tsx) that every Settings page renders through — it bakes in a consistent title (auto-derived from the route registry's titleKey), the back button, full-width body spacing (p-4 space-y-5), and the sibling sub-nav, so the header is identical everywhere: title + action → description → tabs → body.
  • Migrate all ~57 settings panels (plus WorkflowsTab and the embedded CostDashboardPanel) onto the template; strip per-panel z-10/contentClassName/inner padding wrappers and max-w-* width limiters so content is uniformly full-width.
  • Move the family sub-nav pill row from the modal layout into the panel header (via the template) so titles always sit above the pills and the content top no longer shifts between tabs.
  • Add an optional title prop to the shared PanelHeader/PanelScaffold/PanelPage primitives (backwards-compatible — generic panels stay title-less).
  • Deprecate the old SettingsHeader (kept only for the two non-settings page shells, Webhooks/Intelligence, that live outside the /settings route tree).

Problem

The Settings panels were visually inconsistent: drifting spacing (p-4 vs px-4 py-3, space-y-4 vs space-y-5), missing/mis-ordered titles, action buttons sometimes in the header and sometimes buried in the body, one panel hand-rolling a deprecated header, and per-panel max-w-* making content look narrower on some tabs. The cross-panel sub-nav rendered above the panel while in-panel tabs rendered inside it, producing the "title above tabs / tabs above title" mismatch. Switching tabs appeared to resize the modal.

Solution

One opinionated template removes the optional knobs that caused drift and derives the title from the existing route registry (so no new i18n keys). The desktop modal card is already fixed-size (h-[80vh] max-w-5xl); unifying the header structure + full-width content removes the remaining per-tab visual jump. A SettingsModalFrame test asserts the card's size classes are content-independent.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — new SettingsPanel.test.tsx (title derivation, override, action, tabs) and a content-independent-size test in SettingsModalFrame.test.tsx
  • Diff coverage ≥ 80% — change is largely a mechanical JSX migration covered by existing per-panel render tests plus the new template tests; CI diff-cover is the source of truth
  • Coverage matrix updated — N/A: behaviour-preserving UI refactor; no feature rows added/removed/renamed
  • All affected feature IDs from the matrix are listed under ## RelatedN/A: no feature rows affected
  • No new external network dependencies introduced
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: presentation-only refactor, no release-cut logic
  • Linked issue closed via Closes #NNNN/A: ad-hoc request, no tracking issue

Impact

  • Desktop/web only (Settings UI). Presentation-only refactor — no RPC, state, or behaviour changes. The page-tab usages of WorkflowsTab (Activity/Intelligence) are unchanged; only the settings automations route gets the panel chrome.

Related

  • Closes: N/A
  • Follow-up PR(s)/TODOs: migrate the two remaining non-settings page shells (Webhooks, Intelligence) off the deprecated SettingsHeader if/when they move under /settings.

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/settings-consistent-panel-template
  • Commit SHA: 4bf12c009de720d765115f64aa44b670b1829e03

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: vitest run src/components/settings src/components/intelligence/__tests__/WorkflowsTab.test.tsx — 97 files / 1167 tests pass
  • Rust fmt/check (if changed): N/A — no Rust changes
  • Tauri fmt/check (if changed): N/A — no Tauri changes

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: none (visual consistency only)
  • User-visible effect: every Settings panel now shows a consistent title + aligned action, description, then tabs; the modal no longer appears to resize between tabs

Parity Contract

  • Legacy behavior preserved: panel content, controls, and RPC unchanged; embedded sub-panels keep their headerless rendering
  • Guard/fallback/dispatch parity checks: WorkflowsTab page-tab usages unchanged (opt-in asSettingsPanel only on the settings route); deprecated SettingsHeader retained for non-settings pages

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: this
  • Resolution: N/A

Summary by CodeRabbit

  • New Features

    • Settings pages now use a more consistent panel layout, with titles, descriptions, and actions appearing in a unified header area.
    • Some settings screens now support an embedded mode, letting them render cleanly inside other views.
  • Bug Fixes

    • Improved layout consistency across dashboards and settings screens.
    • Removed redundant navigation chrome from many settings pages, reducing clutter and spacing issues.

Introduce a single SettingsPanel template that bakes in a consistent
title (derived from the route registry), back button, full-width body
spacing, and the sibling sub-nav, so every Settings page shares one
header structure. Migrate all panels (and WorkflowsTab) onto it, move
the sub-nav into the panel header, and add an optional title to the
PanelHeader/PanelScaffold/PanelPage primitives.
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dba4c2e1-ef99-4488-82ff-72168987ff29

📥 Commits

Reviewing files that changed from the base of the PR and between 4bf12c0 and 1291d15.

📒 Files selected for processing (6)
  • app/src/components/layout/PanelPage.tsx
  • app/src/components/settings/layout/SettingsPanel.tsx
  • app/src/components/settings/panels/AgentActivityPanel.tsx
  • app/src/components/settings/panels/AppearancePanel.tsx
  • app/src/components/settings/panels/ProfileEditorPage.tsx
  • app/src/components/settings/panels/TeamMembersPanel.tsx
💤 Files with no reviewable changes (2)
  • app/src/components/settings/panels/ProfileEditorPage.tsx
  • app/src/components/settings/panels/AgentActivityPanel.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/components/settings/layout/SettingsPanel.tsx
  • app/src/components/layout/PanelPage.tsx
  • app/src/components/settings/panels/AppearancePanel.tsx
  • app/src/components/settings/panels/TeamMembersPanel.tsx

📝 Walkthrough

Walkthrough

A new SettingsPanel component is introduced as the standard layout wrapper for all settings routes, with PanelHeader/PanelScaffold/PanelPage extended to support an optional title slot and headerExtra region. Sub-navigation chip rendering is moved from shell layouts into each panel's header via SettingsPanel. All ~50 settings panel components are then migrated from PanelPage + SettingsBackButton + useSettingsNavigation to the new SettingsPanel wrapper.

Changes

SettingsPanel layout rollout

Layer / File(s) Summary
PanelHeader/PanelScaffold/PanelPage title and headerExtra extension
app/src/components/layout/PanelHeader.tsx, app/src/components/layout/PanelScaffold.tsx, app/src/components/layout/PanelPage.tsx
PanelHeaderProps gains an optional title?: ReactNode rendered as an h2 in the control row. PanelScaffold forwards title and includes it in hasHeader/hasVisibleHeader logic. PanelPage gains optional title and headerExtra props forwarded to both scaffold and header paths.
SettingsSubNav className prop + shell layout rewiring
app/src/components/settings/layout/SettingsSubNav.tsx, app/src/components/settings/layout/SettingsLayout.tsx, app/src/components/settings/modal/SettingsModalLayout.tsx
SettingsSubNav gains an optional className prop so SettingsPanel can inject it with custom classes. SettingsLayout removes its pinned sub-nav region; SettingsModalLayout removes its direct SettingsSubNav render — both now delegate sub-nav rendering to each panel's header.
SettingsPanel component, SettingsHeader deprecation, and tests
app/src/components/settings/layout/SettingsPanel.tsx, app/src/components/settings/layout/SettingsPanel.test.tsx, app/src/components/settings/components/SettingsHeader.tsx, app/src/components/settings/modal/SettingsModalFrame.test.tsx
SettingsPanel derives a fallback title from the route registry, renders a back button from navigation state, injects SettingsSubNav via PanelPage's headerExtra, and branches between tabbed and single-body PanelPage modes. SettingsHeader is annotated as deprecated. Tests verify title derivation, explicit override, description/action/children rendering, tab interaction, and modal fixed sizing.
WorkflowsTab asSettingsPanel prop and route wiring
app/src/components/intelligence/WorkflowsTab.tsx, app/src/components/settings/settingsRouteElements.tsx
WorkflowsTabProps gains an optional asSettingsPanel boolean. When true, the component wraps its body in SettingsPanel with the extracted newWorkflowButton as action. The automations settings route passes asSettingsPanel to WorkflowsTab.
All settings panels migrated from PanelPage to SettingsPanel
app/src/components/settings/panels/*, app/src/components/dashboard/CostDashboardPanel.tsx
Every settings panel removes PanelPage, SettingsBackButton, and navigateBack/breadcrumbs from useSettingsNavigation, replacing them with SettingsPanel. Panels with an embedded prop retain a plain PanelPage for the embedded path, extracting shared UI into a body fragment.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tinyhumansai/openhuman#453: Added breadcrumbs support to SettingsHeader via useSettingsNavigation — the same chrome this PR deprecates and replaces with SettingsPanel.
  • tinyhumansai/openhuman#3611: Redirects legacy /workflows into /settings/automations, the same route this PR updates to render WorkflowsTab in the new asSettingsPanel mode.
  • tinyhumansai/openhuman#4002: Modifies settingsRouteElements.tsx and the /settings/automations route wiring, directly overlapping with this PR's WorkflowsTab/SettingsPanel rendering changes.

Suggested labels

feature, working

🐇 A new SettingsPanel arrived today,
No more back-buttons scattered away!
The sub-nav moved in, snug in each header,
Fifty panels migrated — oh, what a cheddar!
🥕 One wrapper to rule them all, hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% 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 Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: consolidating settings panels onto a shared SettingsPanel template.
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.


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

@graycyrus

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@graycyrus graycyrus marked this pull request as ready for review June 24, 2026 08:45
@graycyrus graycyrus requested a review from a team June 24, 2026 08:45

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4bf12c009d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

{/* Refreshing indicator - only when loading and has existing data */}
{isLoadingMembers && members.length > 0 && (
<InlineLoadingStatus label={t('team.refreshingMembers')} />
<SettingsPanel description={t('pages.settings.account.teamDesc')}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pass a Team members title

When this panel is reached via /settings/team/members or /settings/team/manage/:teamId/members, useSettingsNavigation() resolves the current route as team-members, but that id is not present in settingsRouteRegistry, so SettingsPanel falls back to t('nav.settings'). Because this call omits an explicit title, the new header shows a generic “Settings” title for the members page; pass title={t('team.members')} here (as the invites panel now does) or register the route id.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 1291d15 — passing title={t('team.members')} (the team-members route id isn't in the registry, so the fallback was showing "Settings"). Matches the invites panel.

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

Caution

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

⚠️ Outside diff range comments (5)
app/src/components/layout/PanelPage.tsx (1)

123-141: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Render headerExtra in the tabbed header path too.

The single-body branch forwards headerExtra, but the tabbed branch always renders only ChipTabs. That means any SettingsPanel using tabs loses the injected SettingsSubNav, which breaks the new “sub-nav lives in the header” contract for tabbed settings pages. Based on the SettingsPanel rollout contract in the PR summary and review stack context.

🤖 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/src/components/layout/PanelPage.tsx` around lines 123 - 141, The tabbed
header path in PanelPage is not rendering headerExtra, so SettingsPanel
sub-navigation gets dropped when tabs are present. Update the PanelPage header
render branch that uses PanelHeader and ChipTabs to also forward and render
headerExtra alongside the tabs, matching the single-body branch behavior and
preserving the header sub-nav contract for tabbed settings pages.
app/src/components/settings/panels/PersonalityPanel.tsx (1)

33-51: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep SettingsSubNav in tabbed SettingsPanel headers.

This tabbed SettingsPanel takes the upstream tabs && tabs.length > 0 branch, but that branch does not pass headerExtra={subNav}. Personality & Face will therefore lose the sibling sub-nav row despite the new layout contract. Add headerExtra={subNav} to the tabbed PanelPage branch in SettingsPanel.

🤖 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/src/components/settings/panels/PersonalityPanel.tsx` around lines 33 -
51, The tabbed SettingsPanel path is missing the sibling sub-nav row because the
`PanelPage` branch used when `tabs && tabs.length > 0` does not pass
`headerExtra={subNav}`. Update `SettingsPanel` so the tabbed branch also
forwards `subNav` through `headerExtra`, preserving the SettingsSubNav in the
Personality/Face header layout while keeping the existing tab rendering logic in
`PersonalityPanel` and related callers intact.
app/src/components/settings/panels/NotificationsTabbedPanel.tsx (1)

34-51: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tabbed notifications lose the settings sub-nav in the new wrapper.

This consumer now goes through SettingsPanel’s tabs branch, but the provided SettingsPanel implementation only injects the sibling sub-nav (headerExtra={subNav}) in the non-tab path. That means /settings/notifications no longer matches the new “title + sibling pills in the header” contract.

The fix likely belongs in app/src/components/settings/layout/SettingsPanel.tsx so the tabbed branch forwards the same header-extra region too.

🤖 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/src/components/settings/panels/NotificationsTabbedPanel.tsx` around lines
34 - 51, The tabbed notifications view is missing the sibling sub-nav because
the SettingsPanel tabs branch does not forward the header extra region. Update
SettingsPanel so the tabbed path in SettingsPanel still renders the same
header-extra/sub-nav area as the non-tab path, preserving the title + sibling
pills contract for NotificationsTabbedPanel and other tabbed consumers.
app/src/components/settings/panels/IntegrationsPanel.tsx (1)

43-61: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tabbed SettingsPanel currently drops the sibling sub-nav.

This route now renders through SettingsPanel’s tabbed branch, but the supplied app/src/components/settings/layout/SettingsPanel.tsx snippet only passes headerExtra={subNav} in the non-tabbed branch. The result is that /settings/integrations loses the sibling settings pills, which breaks the new “sub-nav in every panel header” contract.

🤖 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/src/components/settings/panels/IntegrationsPanel.tsx` around lines 43 -
61, The tabbed IntegrationsPanel path is not preserving the sibling settings
pills because SettingsPanel only forwards headerExtra via subNav in the
non-tabbed branch. Update SettingsPanel so the headerExtra/subNav content is
rendered for both the tabbed and non-tabbed layouts, and verify the
IntegrationsPanel route still shows the sibling pills alongside the tabs. Use
the SettingsPanel component and its tabbed rendering branch to locate the fix.
app/src/components/settings/panels/UsagePanel.tsx (1)

37-55: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Tabbed SettingsPanel drops the sibling sub-nav here.

This route now goes through the tabbed branch of SettingsPanel, but the supplied SettingsPanel implementation only injects SettingsSubNav in the non-tab branch. That means /settings/usage loses the sibling pill row the rollout is trying to standardize across settings pages.

🤖 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/src/components/settings/panels/UsagePanel.tsx` around lines 37 - 55, The
tabbed SettingsPanel usage in UsagePanel is bypassing the sibling sub-nav, so
the route no longer shows the SettingsSubNav pill row. Update the SettingsPanel
tabbed branch to also render the sibling sub-nav when available, or adjust the
UsagePanel composition so the sub-nav is preserved alongside the tabs; use
SettingsPanel, SettingsSubNav, and UsagePanel as the main anchors while making
the change.
🧹 Nitpick comments (3)
app/src/components/settings/layout/SettingsPanel.test.tsx (1)

66-85: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tab-switch test doesn’t verify the switch.

The test clicks the second tab but never updates value, so it only verifies onChange (controlled behavior), not content swap. Either rename the test or rerender with value="two" and assert bodies swap.

Proposed test adjustment
-  it('renders chip tabs and swaps to the active tab content', () => {
+  it('calls onChange and renders new active content after value update', () => {
     const onChange = vi.fn();
-    const { getByText, queryByText, getByTestId } = renderWithProviders(
+    const tabs = [
+      { id: 'one', label: 'One', content: <p>first body</p> },
+      { id: 'two', label: 'Two', content: <p>second body</p> },
+    ] as const;
+    const { getByText, queryByText, getByTestId, rerender } = renderWithProviders(
       <SettingsPanel
         value="one"
         onChange={onChange}
         tabsTestIdPrefix="demo-tab"
-        tabs={[
-          { id: 'one', label: 'One', content: <p>first body</p> },
-          { id: 'two', label: 'Two', content: <p>second body</p> },
-        ]}
+        tabs={tabs as any}
       />
     );
@@
     fireEvent.click(getByTestId('demo-tab-two'));
     expect(onChange).toHaveBeenCalledWith('two');
+    rerender(
+      <SettingsPanel
+        value="two"
+        onChange={onChange}
+        tabsTestIdPrefix="demo-tab"
+        tabs={tabs as any}
+      />
+    );
+    expect(getByText('second body')).toBeInTheDocument();
+    expect(queryByText('first body')).not.toBeInTheDocument();
   });
🤖 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/src/components/settings/layout/SettingsPanel.test.tsx` around lines 66 -
85, The SettingsPanel test only checks the controlled onChange callback after
clicking the second tab, so it does not verify the content actually switches.
Update the test in SettingsPanel.test.tsx to either reflect controlled behavior
in the name or rerender SettingsPanel with value set to "two" after the click,
then assert the active content changes from "first body" to "second body". Use
the existing SettingsPanel, renderWithProviders, and getByTestId setup to keep
the test focused on tab switching.
app/src/components/settings/panels/AnalysisViewsPanel.tsx (1)

49-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use SettingsPanel's tab slot here.

This keeps the chip row in body content instead of the standardized header region that SettingsPanel already provides. Passing views through tabs, value, and onChange would preserve the intended title → description → tabs → body order for settings pages.

🤖 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/src/components/settings/panels/AnalysisViewsPanel.tsx` around lines 49 -
65, The tab row is rendered inside the body of SettingsPanel instead of using
its built-in tab slot. Update AnalysisViewsPanel to pass the existing views data
through SettingsPanel’s tabs, value, and onChange props, and remove the inline
PillTabBar so the header keeps the standard title, description, tabs, body
order. Use the SettingsPanel and views symbols to locate the affected markup and
preserve the current tab switching behavior with activeView/setActiveView.
app/src/components/settings/panels/ApprovalHistoryPanel.tsx (1)

89-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the subtitle and refresh button into the panel header.

This migration still renders both inside the first section, so the page misses the standardized SettingsPanel description/action layout. Wire t('settings.approvalHistory.subtitle') through description and the refresh control through action.

🤖 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/src/components/settings/panels/ApprovalHistoryPanel.tsx` around lines 89
- 104, The ApprovalHistoryPanel still renders the subtitle and refresh button
inside the first SettingsSection instead of using the SettingsPanel header
layout. Update ApprovalHistoryPanel to pass
t('settings.approvalHistory.subtitle') into SettingsPanel’s description prop and
move the refresh Button into its action prop, keeping handleRefresh and
isLoading behavior unchanged.
🤖 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/src/components/layout/PanelScaffold.tsx`:
- Around line 73-84: The scaffold still mounts PanelHeader when only leading is
provided, which can leave an empty fixed header gap above the body on wide
viewports. Update PanelScaffold’s header gating logic so the header is only
rendered when there is visible header content, using the existing
hasVisibleHeader condition (or equivalent) rather than hasHeader, and keep the
border decision separate from whether the header mounts.

In `@app/src/components/settings/layout/SettingsPanel.tsx`:
- Around line 77-91: The tabbed render path in SettingsPanel omits headerExtra,
so the in-panel header layout is inconsistent with the non-tab branch. Update
the PanelPage<T> usage in SettingsPanel to pass headerExtra through in the tabs
&& tabs.length > 0 branch as well, keeping the header order consistent with
SettingsSubNav and the existing header contract.

In `@app/src/components/settings/panels/AgentActivityPanel.tsx`:
- Around line 109-113: The panel is rendering the same activity-level
description twice: once through SettingsPanel and again inside
AgentActivityPanel. Remove the extra <p> block in AgentActivityPanel so the
intro text is only provided via SettingsPanel, keeping the panel content from
duplicating the copy and spacing.

In `@app/src/components/settings/panels/AppearancePanel.tsx`:
- Around line 157-169: In AppearancePanel’s radio button styling, the base class
currently applies the selected dark-mode background to every option, so
unselected rows look active. Move the dark background class into the selected
branch of the className conditional for the option buttons rendered in this
picker, and apply the same fix to the other matching picker block referenced by
the review.

In `@app/src/components/settings/panels/ComposioTriagePanel.tsx`:
- Around line 93-118: The intro copy and the section description in
ComposioTriagePanel are built from multiple translation calls, which prevents
translators from controlling sentence order and placement of the env-var token.
Update the text to use single translation keys for each full sentence, then
inject OPENHUMAN_TRIGGER_TRIAGE_DISABLED using the existing repo pattern with
t(...).replace(...), keeping the logic localized around ComposioTriagePanel and
its SettingsPanel/SettingsSection content.

In `@app/src/components/settings/panels/CronJobsPanel.tsx`:
- Around line 188-230: The cron jobs panel is wrapping CoreJobList in an extra
SettingsSection, which duplicates the section chrome and heading. Update
CronJobsPanel so the cron jobs content uses only the existing CoreJobList
section, keeping the surrounding SettingsPanel/SettingsStatusLine and buttons
but removing the redundant outer SettingsSection around CoreJobList.

In `@app/src/components/settings/panels/NotificationsPanel.tsx`:
- Around line 122-127: The category switch aria-label in NotificationsPanel is
hardcoded English and bypasses localization. Update the SettingsSwitch
aria-label in NotificationsPanel to use the existing useT() translation hook
with a new key for the full “Toggle {category} notifications” label, and add
that key to en.ts plus all other locale files so screen readers announce the
control in the active language.

In `@app/src/components/settings/panels/ProfileEditorPage.tsx`:
- Around line 182-189: In ProfileEditorPage’s notFound branch, remove the extra
back button so the header-level back control from SettingsPanel remains the only
back affordance. Update the conditional render around the notFound message to
keep just the notice and drop the Button that calls backToList.

In `@app/src/components/settings/panels/SearchPanel.tsx`:
- Around line 461-464: The standalone SearchPanel is still rendering
SettingsPanel chrome even though it is used outside the settings route tree, so
the title and back behavior become route-dependent in the wrong place. Update
SearchPanel and its non-embedded caller in Skills.tsx so the SettingsPanel
wrapper is only used for the embedded/settings-route case, or move that chrome
to the host page instead. Use the SearchPanel component’s embedded/non-embedded
branch and the SettingsPanel wrapper as the main points to adjust.

In `@app/src/components/settings/panels/VoicePanel.tsx`:
- Around line 952-957: The fallback `'Test failed'` string in `VoicePanel`
bypasses `useT()` and should be localized like the rest of the panel copy.
Update the error-handling paths in the affected voice test flows (including the
`catch` blocks around the key test result setters) to use i18n keys from
`app/src/lib/i18n/I18nContext` instead of hardcoded English text. Add the new
translation key(s) to `en.ts` and every locale file so the `setKeyTestResult`
and related error messages stay locale-parity compliant.

---

Outside diff comments:
In `@app/src/components/layout/PanelPage.tsx`:
- Around line 123-141: The tabbed header path in PanelPage is not rendering
headerExtra, so SettingsPanel sub-navigation gets dropped when tabs are present.
Update the PanelPage header render branch that uses PanelHeader and ChipTabs to
also forward and render headerExtra alongside the tabs, matching the single-body
branch behavior and preserving the header sub-nav contract for tabbed settings
pages.

In `@app/src/components/settings/panels/IntegrationsPanel.tsx`:
- Around line 43-61: The tabbed IntegrationsPanel path is not preserving the
sibling settings pills because SettingsPanel only forwards headerExtra via
subNav in the non-tabbed branch. Update SettingsPanel so the headerExtra/subNav
content is rendered for both the tabbed and non-tabbed layouts, and verify the
IntegrationsPanel route still shows the sibling pills alongside the tabs. Use
the SettingsPanel component and its tabbed rendering branch to locate the fix.

In `@app/src/components/settings/panels/NotificationsTabbedPanel.tsx`:
- Around line 34-51: The tabbed notifications view is missing the sibling
sub-nav because the SettingsPanel tabs branch does not forward the header extra
region. Update SettingsPanel so the tabbed path in SettingsPanel still renders
the same header-extra/sub-nav area as the non-tab path, preserving the title +
sibling pills contract for NotificationsTabbedPanel and other tabbed consumers.

In `@app/src/components/settings/panels/PersonalityPanel.tsx`:
- Around line 33-51: The tabbed SettingsPanel path is missing the sibling
sub-nav row because the `PanelPage` branch used when `tabs && tabs.length > 0`
does not pass `headerExtra={subNav}`. Update `SettingsPanel` so the tabbed
branch also forwards `subNav` through `headerExtra`, preserving the
SettingsSubNav in the Personality/Face header layout while keeping the existing
tab rendering logic in `PersonalityPanel` and related callers intact.

In `@app/src/components/settings/panels/UsagePanel.tsx`:
- Around line 37-55: The tabbed SettingsPanel usage in UsagePanel is bypassing
the sibling sub-nav, so the route no longer shows the SettingsSubNav pill row.
Update the SettingsPanel tabbed branch to also render the sibling sub-nav when
available, or adjust the UsagePanel composition so the sub-nav is preserved
alongside the tabs; use SettingsPanel, SettingsSubNav, and UsagePanel as the
main anchors while making the change.

---

Nitpick comments:
In `@app/src/components/settings/layout/SettingsPanel.test.tsx`:
- Around line 66-85: The SettingsPanel test only checks the controlled onChange
callback after clicking the second tab, so it does not verify the content
actually switches. Update the test in SettingsPanel.test.tsx to either reflect
controlled behavior in the name or rerender SettingsPanel with value set to
"two" after the click, then assert the active content changes from "first body"
to "second body". Use the existing SettingsPanel, renderWithProviders, and
getByTestId setup to keep the test focused on tab switching.

In `@app/src/components/settings/panels/AnalysisViewsPanel.tsx`:
- Around line 49-65: The tab row is rendered inside the body of SettingsPanel
instead of using its built-in tab slot. Update AnalysisViewsPanel to pass the
existing views data through SettingsPanel’s tabs, value, and onChange props, and
remove the inline PillTabBar so the header keeps the standard title,
description, tabs, body order. Use the SettingsPanel and views symbols to locate
the affected markup and preserve the current tab switching behavior with
activeView/setActiveView.

In `@app/src/components/settings/panels/ApprovalHistoryPanel.tsx`:
- Around line 89-104: The ApprovalHistoryPanel still renders the subtitle and
refresh button inside the first SettingsSection instead of using the
SettingsPanel header layout. Update ApprovalHistoryPanel to pass
t('settings.approvalHistory.subtitle') into SettingsPanel’s description prop and
move the refresh Button into its action prop, keeping handleRefresh and
isLoading behavior unchanged.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8f551f51-a75e-4d89-9761-ae32f51d6c9f

📥 Commits

Reviewing files that changed from the base of the PR and between 4fff156 and 4bf12c0.

📒 Files selected for processing (77)
  • app/src/components/dashboard/CostDashboardPanel.tsx
  • app/src/components/intelligence/WorkflowsTab.tsx
  • app/src/components/layout/PanelHeader.tsx
  • app/src/components/layout/PanelPage.tsx
  • app/src/components/layout/PanelScaffold.tsx
  • app/src/components/settings/components/SettingsHeader.tsx
  • app/src/components/settings/layout/SettingsLayout.tsx
  • app/src/components/settings/layout/SettingsPanel.test.tsx
  • app/src/components/settings/layout/SettingsPanel.tsx
  • app/src/components/settings/layout/SettingsSubNav.tsx
  • app/src/components/settings/modal/SettingsModalFrame.test.tsx
  • app/src/components/settings/modal/SettingsModalLayout.tsx
  • app/src/components/settings/panels/AIPanel.tsx
  • app/src/components/settings/panels/AboutPanel.tsx
  • app/src/components/settings/panels/AccountPanel.tsx
  • app/src/components/settings/panels/AgentAccessPanel.tsx
  • app/src/components/settings/panels/AgentActivityPanel.tsx
  • app/src/components/settings/panels/AgentBoxPanel.tsx
  • app/src/components/settings/panels/AgentChatPanel.tsx
  • app/src/components/settings/panels/AgentEditorPage.tsx
  • app/src/components/settings/panels/AgentsPanel.tsx
  • app/src/components/settings/panels/AnalysisViewsPanel.tsx
  • app/src/components/settings/panels/AppearancePanel.tsx
  • app/src/components/settings/panels/ApprovalHistoryPanel.tsx
  • app/src/components/settings/panels/AutocompleteDebugPanel.tsx
  • app/src/components/settings/panels/AutocompletePanel.tsx
  • app/src/components/settings/panels/BillingPanel.tsx
  • app/src/components/settings/panels/CompanionPanel.tsx
  • app/src/components/settings/panels/ComposioPanel.tsx
  • app/src/components/settings/panels/ComposioTriagePanel.tsx
  • app/src/components/settings/panels/CronJobsPanel.tsx
  • app/src/components/settings/panels/DesktopAgentPanel.tsx
  • app/src/components/settings/panels/DevWorkflowPanel.tsx
  • app/src/components/settings/panels/DeveloperOptionsPanel.tsx
  • app/src/components/settings/panels/DevicesPanel.tsx
  • app/src/components/settings/panels/EmbeddingsPanel.tsx
  • app/src/components/settings/panels/EventLogPanel.tsx
  • app/src/components/settings/panels/IntegrationsPanel.tsx
  • app/src/components/settings/panels/LocalModelDebugPanel.tsx
  • app/src/components/settings/panels/MascotPanel.tsx
  • app/src/components/settings/panels/McpServerPanel.tsx
  • app/src/components/settings/panels/MeetingSettingsPanel.tsx
  • app/src/components/settings/panels/MemoryDataPanel.tsx
  • app/src/components/settings/panels/MemoryDebugPanel.tsx
  • app/src/components/settings/panels/MemorySyncPanel.tsx
  • app/src/components/settings/panels/MigrationPanel.tsx
  • app/src/components/settings/panels/ModelHealthPanel.tsx
  • app/src/components/settings/panels/NotificationRoutingPanel.tsx
  • app/src/components/settings/panels/NotificationsPanel.tsx
  • app/src/components/settings/panels/NotificationsTabbedPanel.tsx
  • app/src/components/settings/panels/PermissionsPanel.tsx
  • app/src/components/settings/panels/PersonaPanel.tsx
  • app/src/components/settings/panels/PersonalityPanel.tsx
  • app/src/components/settings/panels/PrivacyPanel.tsx
  • app/src/components/settings/panels/ProfileEditorPage.tsx
  • app/src/components/settings/panels/ProfilesPanel.tsx
  • app/src/components/settings/panels/RecoveryPhrasePanel.tsx
  • app/src/components/settings/panels/SandboxSettingsPanel.tsx
  • app/src/components/settings/panels/ScreenAwarenessDebugPanel.tsx
  • app/src/components/settings/panels/ScreenIntelligencePanel.tsx
  • app/src/components/settings/panels/SearchPanel.tsx
  • app/src/components/settings/panels/SecurityPanel.tsx
  • app/src/components/settings/panels/TaskSourcesPanel.tsx
  • app/src/components/settings/panels/TasksPanel.tsx
  • app/src/components/settings/panels/TeamInvitesPanel.tsx
  • app/src/components/settings/panels/TeamManagementPanel.tsx
  • app/src/components/settings/panels/TeamMembersPanel.tsx
  • app/src/components/settings/panels/TeamPanel.tsx
  • app/src/components/settings/panels/ToolPolicyDiagnosticsPanel.tsx
  • app/src/components/settings/panels/ToolsPanel.tsx
  • app/src/components/settings/panels/UsagePanel.tsx
  • app/src/components/settings/panels/VoiceDebugPanel.tsx
  • app/src/components/settings/panels/VoicePanel.tsx
  • app/src/components/settings/panels/WalletBalancesPanel.tsx
  • app/src/components/settings/panels/WebhooksDebugPanel.tsx
  • app/src/components/settings/panels/WorkflowRunnerPanel.tsx
  • app/src/components/settings/settingsRouteElements.tsx

Comment thread app/src/components/layout/PanelScaffold.tsx
Comment thread app/src/components/settings/layout/SettingsPanel.tsx
Comment thread app/src/components/settings/panels/AgentActivityPanel.tsx Outdated
Comment thread app/src/components/settings/panels/AppearancePanel.tsx
Comment thread app/src/components/settings/panels/ComposioTriagePanel.tsx
Comment thread app/src/components/settings/panels/CronJobsPanel.tsx
Comment thread app/src/components/settings/panels/NotificationsPanel.tsx
Comment thread app/src/components/settings/panels/ProfileEditorPage.tsx Outdated
Comment thread app/src/components/settings/panels/SearchPanel.tsx Outdated
Comment thread app/src/components/settings/panels/VoicePanel.tsx Outdated
…ate gaps

- Revert AIPanel/VoicePanel/EmbeddingsPanel/SearchPanel/ComposioPanel/
  MemoryDataPanel/MemoryDebugPanel/AnalysisViewsPanel: these render on the
  Connections/Brain pages (their /settings routes redirect), so they must
  not use the settings-route-derived SettingsPanel chrome.
- SettingsPanel: pass headerExtra (sub-nav) in the tabbed branch too, and
  forward headerExtra through PanelPage's tabbed header.
- TeamMembersPanel: pass explicit title (route id not in registry).
- AgentActivityPanel: drop description duplicated by the template.
- ProfileEditorPage: drop duplicate back button in the not-found state.
- AppearancePanel: stop tinting unselected rows in dark mode.
@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. working A PR that is being worked on by the team. labels Jun 24, 2026
@graycyrus graycyrus merged commit 1639480 into tinyhumansai:main Jun 24, 2026
27 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant