fix(dashboard): scope chat sidebar model card to selected profile (#46665)

* fix(dashboard): scope chat sidebar model card to selected profile

The PTY already honors ?profile= on profile switch, but the JSON-RPC
sidecar created sessions against the dashboard launch profile. Pass the
management profile through session.create and reconnect on switch.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(dashboard): sync active profile with management scope

Align the sidebar switcher with the sticky active profile on load and
when "Set as active" is clicked, so Chat and management pages match
what the Profiles page shows as active.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(dashboard): auto-reconnect chat sidebar on profile switch

Bump the sidecar connection version when profile or PTY channel changes,
matching the manual Reconnect path so gateway and events sockets come
back without clicking the error banner.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(dashboard): prevent model selector chevron overlapping label

Use inline flex layout instead of Button suffix, which is absolutely
positioned and overlapped truncated model names at px-0.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Austin Pickett 2026-06-15 12:50:19 -04:00 committed by GitHub
parent 0bbff1fc7e
commit 0bbf325a8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 94 additions and 29 deletions

View file

@ -26,10 +26,12 @@ import { ProfileContext } from "@/contexts/profile-context";
* truth, the effect below re-asserts `?profile=` onto the new location
* after each navigation, so the scope survives nav and stays deep-linkable.
*
* This exists because "Set as active" on the Profiles page only flips the
* sticky active_profile file (future CLI/gateway runs) it cannot retarget
* the running dashboard. The switcher is the dashboard's own, visible,
* write-target selector.
* This exists because "Set as active" on the Profiles page historically only
* flipped the sticky active_profile file (future CLI/gateway runs). The
* switcher is the dashboard's write-target selector for Chat and management
* pages. We now sync the switcher when the sticky active profile differs from
* the dashboard process on load, and ProfilesPage updates the switcher when
* you click "Set as active".
*/
export function ProfileProvider({ children }: { children: ReactNode }) {
const [searchParams, setSearchParams] = useSearchParams();
@ -77,14 +79,34 @@ export function ProfileProvider({ children }: { children: ReactNode }) {
}, [pathname, profile]);
useEffect(() => {
api
.getProfiles()
.then((res) => setProfiles(res.profiles.map((p) => p.name)))
.catch(() => {});
api
.getActiveProfile()
.then((info) => setCurrentProfile(info.current || "default"))
let cancelled = false;
const urlProfile = searchParams.get("profile");
Promise.all([api.getProfiles(), api.getActiveProfile()])
.then(([profilesRes, info]) => {
if (cancelled) return;
setProfiles(profilesRes.profiles.map((p) => p.name));
const current = info.current || "default";
const active = info.active || "default";
setCurrentProfile(current);
// Deep links (?profile=) win. Otherwise align the switcher with the
// sticky active profile so Chat and management pages match what the
// Profiles page shows as "active" (machine dashboard runs as
// `current`, usually default).
if (urlProfile === null && active !== current) {
setManagementProfile(active);
setProfileState(active);
}
})
.catch(() => {});
return () => {
cancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const setProfile = useCallback(