Skip to content

Commit a80d96c

Browse files
committed
fix(copilot): pin a restored table view so a stale views list cannot strand it
Reopening a chat hands the embedded table its saved view through `initialViewId`, which the table honours only while its views query already lists that id. A cached list from before the agent created the view resolves it to nothing, so adoption settles on the default and stamps itself closed — nothing revisits the id when the refetch lands, and the restored view is lost until the tab is reopened. Pin on mount as well as on later changes, so the handoff waits for the list that carries the view. When adoption already applied the same view the table consumes the pin without touching the URL, and a table opened with no saved view still pins nothing. This also makes a first mount agree with a tab switch, which already re-pins the saved view through the same path.
1 parent fdbc7a7 commit a80d96c

2 files changed

Lines changed: 23 additions & 5 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ describe('ResourceContent table view handoff', () => {
5151
})
5252
}
5353

54+
it('hands off a restored view the table is mounted with', () => {
55+
// The table can only honour `initialViewId` while its views query already
56+
// lists that id. Reopening a chat against a cached list from before the
57+
// agent's write would otherwise strand the restored view.
58+
render({ type: 'table', id: 'table-1', title: 'Invoices', viewId: 'view-restored' })
59+
60+
expect(useTableViewPinStore.getState().pins['table-1']?.viewId).toBe('view-restored')
61+
})
62+
63+
it('does not pin a table opened without a saved view', () => {
64+
render({ type: 'table', id: 'table-1', title: 'Invoices' })
65+
66+
expect(useTableViewPinStore.getState().pins['table-1']).toBeUndefined()
67+
})
68+
5469
it('hands off a saved view that arrives after the embedded table mounts', () => {
5570
const table: MothershipResource = {
5671
type: 'table',

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,7 @@ export const ResourceContent = memo(function ResourceContent({
179179
visible = true,
180180
onBrowserOverlayControllerChange,
181181
}: ResourceContentProps) {
182-
const observedTableViewRef = useRef(
183-
resource.type === 'table' ? { tableId: resource.id, viewId: resource.viewId } : null
184-
)
182+
const observedTableViewRef = useRef<{ tableId: string; viewId?: string } | null>(null)
185183

186184
useEffect(() => {
187185
const previous = observedTableViewRef.current
@@ -192,8 +190,13 @@ export const ResourceContent = memo(function ResourceContent({
192190
return
193191
}
194192
/**
195-
* `initialViewId` owns the first table adoption. If refreshed chat data
196-
* supplies it later, use the same one-shot handoff as live stream events.
193+
* Pinned on mount as well as on later changes. `initialViewId` alone is not
194+
* enough: the table honours it only while its views query already carries
195+
* that id, and a cached list from before the agent wrote the view resolves
196+
* it to nothing. Adoption then settles on the default and never revisits
197+
* the id, so the restored view is lost until the tab is reopened. The pin
198+
* waits for the refetch instead, and costs nothing when adoption already
199+
* applied the same view — the table consumes it without touching the URL.
197200
*/
198201
useTableViewPinStore.getState().pin(next.tableId, next.viewId)
199202
}, [resource.id, resource.type, resource.viewId])

0 commit comments

Comments
 (0)