Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion packages/core/src/browser-tabs/browserTabsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,25 @@ interface BrowserTabsState {
setSnapshot: (snapshot: TabsSnapshot) => void;
}

// True when two snapshots carry the same value. Reference check first (the
// common no-op), then a structural compare. Stringify can only yield a false
// *mismatch* (e.g. key-order drift), never a false match — so it can bail on a
// redundant write but never skip a real change, which would strand a stale
// mirror.
function snapshotsEqual(a: TabsSnapshot, b: TabsSnapshot): boolean {
return a === b || JSON.stringify(a) === JSON.stringify(b);
}

export const browserTabsStore = createStore<BrowserTabsState>((set) => ({
snapshot: { windows: [], tabs: [] },
setSnapshot: (snapshot) => set({ snapshot }),
// Skip redundant writes: every tab mutation's onSuccess re-applies the
// authoritative snapshot even when an optimistic write already set an equal
// value. Without this guard that echo re-renders every subscriber and re-runs
// the navigation effect (which depends on this snapshot) for no change.
setSnapshot: (snapshot) =>
set((prev) =>
snapshotsEqual(prev.snapshot, snapshot) ? prev : { snapshot },
),
}));

export const getTabsSnapshot = () => browserTabsStore.getState().snapshot;
1 change: 1 addition & 0 deletions packages/shared/src/analytics-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ export type ChannelsSurface =
export type ChannelActionType =
| "enter_space"
| "leave_space"
| "toggle_channels"
| "leave_feedback"
| "nav_click"
| "open_channel"
Expand Down
7 changes: 7 additions & 0 deletions packages/shared/src/browser-tabs-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export const browserTabSchema = z.object({
* blank). Pairs with `channelId`: the two together identify a channel tab.
*/
channelSection: z.string().nullable().default(null),
/**
* Top-level app page this tab shows (`inbox` / `agents` / `skills` /
* `mcp-servers` / `command-center` / `home`). Null for a canvas / task /
* channel / blank tab. These pages have no channel, task, or dashboard id, so
* this is what lets them be a real tab target (label + restore-on-refocus).
*/
appView: z.string().nullable().default(null),
/** Gap-spaced ordering key within a window. Reindexed on collision. */
position: z.number(),
/**
Expand Down
Loading
Loading