@@ -264,17 +264,35 @@ export function Table({
264264 * panel's Columns section edits it and the active view persists it. */
265265 const [ hiddenColumns , setHiddenColumns ] = useState < string [ ] > ( [ ] )
266266
267- const [ { sort : sortColumn , dir : sortDirection , view : activeViewId } , setTableParams ] =
267+ const [ { sort : sortColumn , dir : sortDirection , view : urlActiveViewId } , setTableParams ] =
268268 useQueryStates ( tableDetailParsers , tableDetailUrlKeys )
269269
270- /** A chat View resource owns its initial selection. After seeding, normal
271- * selector and URL behavior take over so users can switch Views as usual. */
272- const seededPropViewRef = useRef < string | undefined > ( undefined )
270+ /**
271+ * An embedded View must own the first render synchronously. Seeding only the
272+ * host URL in an effect races the view resolver: it can adopt Default view
273+ * before the URL write lands and clear the requested chat resource.
274+ *
275+ * Keep a local selection only for View-resource embeds; page Tables and plain
276+ * Table resources continue to use the URL as their sole source of truth.
277+ */
278+ const [ embeddedActiveViewId , setEmbeddedActiveViewId ] = useState < string | null > (
279+ ( ) => propViewId ?? null
280+ )
281+ const activeViewId = propViewId ? embeddedActiveViewId : urlActiveViewId
282+ const syncedPropViewRef = useRef < string | undefined > ( undefined )
273283 useEffect ( ( ) => {
274- if ( ! propViewId || seededPropViewRef . current === propViewId ) return
275- seededPropViewRef . current = propViewId
284+ if ( ! propViewId || syncedPropViewRef . current === propViewId ) return
285+ syncedPropViewRef . current = propViewId
286+ setEmbeddedActiveViewId ( propViewId )
276287 void setTableParams ( { view : propViewId } )
277288 } , [ propViewId , setTableParams ] )
289+ const setActiveViewId = useCallback (
290+ ( viewId : string ) => {
291+ if ( propViewId ) setEmbeddedActiveViewId ( viewId )
292+ void setTableParams ( { view : viewId } )
293+ } ,
294+ [ propViewId , setTableParams ]
295+ )
278296
279297 // Read-only mirrors for the resolve effect: it must know whether the user has
280298 // already applied a filter / hidden columns without re-running when they change.
@@ -552,7 +570,7 @@ export function Table({
552570 const keep = inheritedParams ? { ...localWork ( ) , sort : false } : localWork ( )
553571 if ( defaultView ) {
554572 seededViewIdRef . current = defaultView . id
555- setTableParams ( { view : defaultView . id } )
573+ setActiveViewId ( defaultView . id )
556574 applyViewConfig ( defaultView . config , keep )
557575 resolvePendingLayout ( true )
558576 return
@@ -561,7 +579,10 @@ export function Table({
561579 // would clear a deep-linked `?sort=` on mount. Inherited params are the
562580 // exception: nothing about them refers to this table, so they're cleared.
563581 seededViewIdRef . current = null
564- if ( inheritedParams ) setTableParams ( { view : ALL_VIEW_PARAM , sort : null , dir : null } )
582+ if ( inheritedParams ) {
583+ setActiveViewId ( ALL_VIEW_PARAM )
584+ setTableParams ( { sort : null , dir : null } )
585+ }
565586 resolvePendingLayout ( false )
566587 return
567588 }
@@ -581,7 +602,7 @@ export function Table({
581602 // Nothing to apply, but the URL still names a view that no longer exists.
582603 // Rewrite it so a stale bookmark can't be copied on, and so the param
583604 // matches the All the UI is already showing.
584- setTableParams ( { view : ALL_VIEW_PARAM } )
605+ setActiveViewId ( ALL_VIEW_PARAM )
585606 }
586607 return
587608 }
@@ -599,7 +620,7 @@ export function Table({
599620 if ( activeViewId !== null && activeViewId !== ALL_VIEW_PARAM && ! activeView ) {
600621 if ( pendingCreatedViewIdRef . current === activeViewId ) return
601622 seededViewIdRef . current = null
602- setTableParams ( { view : ALL_VIEW_PARAM } )
623+ setActiveViewId ( ALL_VIEW_PARAM )
603624 applyViewConfig ( null )
604625 return
605626 }
@@ -623,7 +644,7 @@ export function Table({
623644 embedded ,
624645 sortColumn ,
625646 applyViewConfig ,
626- setTableParams ,
647+ setActiveViewId ,
627648 resolvePendingLayout ,
628649 ] )
629650
@@ -707,9 +728,9 @@ export function Table({
707728
708729 const handleSelectView = useCallback (
709730 ( viewId : string | null ) => {
710- setTableParams ( { view : viewId ?? ALL_VIEW_PARAM } )
731+ setActiveViewId ( viewId ?? ALL_VIEW_PARAM )
711732 } ,
712- [ setTableParams ]
733+ [ setActiveViewId ]
713734 )
714735
715736 const handleRenameView = useCallback ( ( viewId : string ) => {
@@ -816,7 +837,7 @@ export function Table({
816837 // seeded — it can't tell a just-created view from a dead id otherwise.
817838 seededViewIdRef . current = view . id
818839 pendingCreatedViewIdRef . current = view . id
819- setTableParams ( { view : view . id } )
840+ setActiveViewId ( view . id )
820841 // Which means the blank config must be applied here; nuqs batches this
821842 // sort write with the `view` write above into one URL update.
822843 if ( blank ) applyViewConfig ( view . config )
@@ -830,12 +851,12 @@ export function Table({
830851 ( viewId : string ) => {
831852 deleteViewMutation . mutate ( viewId , {
832853 onSuccess : ( ) => {
833- if ( viewId === activeViewId ) setTableParams ( { view : ALL_VIEW_PARAM } )
854+ if ( viewId === activeViewId ) setActiveViewId ( ALL_VIEW_PARAM )
834855 } ,
835856 onError : ( error ) => toast . error ( getErrorMessage ( error , 'Failed to delete view' ) ) ,
836857 } )
837858 } ,
838- [ activeViewId , setTableParams ]
859+ [ activeViewId , setActiveViewId ]
839860 )
840861
841862 const runColumnMutation = useRunColumn ( { workspaceId, tableId } )
0 commit comments