@@ -116,6 +116,7 @@ import { usePagedPrompts } from "@inspector/core/react/usePagedPrompts.js";
116116import { usePagedResources } from "@inspector/core/react/usePagedResources.js" ;
117117import { usePaginatedList } from "./hooks/usePaginatedList" ;
118118import { useLastPersistedSettings } from "./hooks/useLastPersistedSettings" ;
119+ import { usePaginatedListsOverride } from "./hooks/usePaginatedListsOverride" ;
119120import { useValueChange } from "./hooks/useValueChange" ;
120121import type { ListPaginationControlsProps } from "./components/elements/ListPaginationControls/ListPaginationControls" ;
121122import { useManagedResourceTemplates } from "@inspector/core/react/useManagedResourceTemplates.js" ;
@@ -1234,26 +1235,15 @@ function App() {
12341235 const activeServerEntry = servers . find ( ( s ) => s . id === activeServerId ) ;
12351236 const persistedPaginatedLists =
12361237 activeServerEntry ?. settings ?. paginatedLists ?? false ;
1237- const [ paginatedListsOverride , setPaginatedListsOverride ] = useState <
1238- boolean | null
1239- > ( null ) ;
1240- // Drop the optimistic override once the persisted value catches up (or the
1241- // active server changes), so the persisted setting is the resting source.
1242- //
1243- // Keyed on the entry object rather than on the boolean it carries: the
1244- // override can hold a *concrete* value (a rollback sets one instead of
1245- // clearing), and if a later successful read reports the value that override
1246- // replaced — an outside edit overtaking the write — the boolean and the
1247- // server id are both unchanged and the UI stays stuck on it. Every successful
1248- // read rebuilds the list, so the entry is the signal that supersedes it, and
1249- // it subsumes the other two: the value is read off this entry, and switching
1250- // servers selects a different one (#2089).
1251- //
1252- // Adjusted during render rather than in an effect, per this repo's rule
1253- // against resetting state from render data in `useEffect` — the entry is
1254- // referentially stable between reads, which is what `useValueChange` needs.
1255- useValueChange ( activeServerEntry , ( ) => setPaginatedListsOverride ( null ) ) ;
1256- const paginatedLists = paginatedListsOverride ?? persistedPaginatedLists ;
1238+ // The optimistic override, held per server and superseded by the first
1239+ // successful list read that rebuilds that server's entry. Per server rather
1240+ // than one app-wide slot cleared on every active-entry change: the previous
1241+ // shape dropped the override on a plain server switch, so an A → B → A round
1242+ // trip fell back to A's stale entry and showed a value neither disk nor the
1243+ // live client held (#2095).
1244+ const paginatedListsOverride = usePaginatedListsOverride ( servers ) ;
1245+ const paginatedLists =
1246+ paginatedListsOverride . valueFor ( activeServerId ) ?? persistedPaginatedLists ;
12571247 // The malformed-entry report is written by the aggregate walk's salvage. In
12581248 // paginated mode the tools/prompts/resources panels render the paged stores
12591249 // instead, which never write or clear it — so it would linger above a page it
@@ -3968,9 +3958,13 @@ function App() {
39683958 // gating reads it now, and a persisted PUT so it survives reconnects (#1721).
39693959 const onTogglePaginatedLists = useCallback (
39703960 ( value : boolean ) => {
3971- setPaginatedListsOverride ( value ) ;
39723961 const current = servers . find ( ( s ) => s . id === activeServerId ) ;
39733962 if ( ! current || activeServerId === undefined ) return ;
3963+ // Recorded against this server rather than app-wide, so it survives a
3964+ // switch away and back (#2095). Ordered after the id guard because the
3965+ // record is keyed by that id; with no active server there is nothing the
3966+ // toggle could have been flipped for.
3967+ paginatedListsOverride . record ( activeServerId , value ) ;
39743968 // Not `current.settings` directly: that entry only advances on a
39753969 // successful list read, so once one has failed it describes disk as it
39763970 // was *before* the writes made since. Build on the last write known to
@@ -4019,8 +4013,16 @@ function App() {
40194013 // already destroyed.
40204014 const settlePaginationWrite = ( ) => {
40214015 const settled = write . landed ( next ) ;
4022- if ( settled && activeServerIdRef . current === activeServerId ) {
4023- setPaginatedListsOverride ( next . paginatedLists ?? false ) ;
4016+ if ( ! settled ) return ;
4017+ // The override is keyed by server, so it is re-applied whatever is
4018+ // active now — it is this server's value and is only ever displayed
4019+ // while this server is the active one. The live client is not: it
4020+ // belongs to whichever server is connected (#2095).
4021+ paginatedListsOverride . record (
4022+ activeServerId ,
4023+ next . paginatedLists ?? false ,
4024+ ) ;
4025+ if ( activeServerIdRef . current === activeServerId ) {
40244026 applyLiveServerSettings ( next ) ;
40254027 }
40264028 } ;
@@ -4061,17 +4063,25 @@ function App() {
40614063 // `persistedPaginatedLists` — read from a `servers` entry that may be
40624064 // stale, showing the same wrong value from the other side (#2089).
40634065 //
4064- // Applied only while this write's server is still the active one: the
4065- // override is a single app-wide boolean and the live client belongs to
4066- // whichever server is connected now, so a rejection arriving after the
4067- // user switched would render this server's value on another one.
4068- // The client is taken from the ref for the same reason the success
4069- // path does: a reconnect to the same server passes the id check while
4070- // this continuation's captured instance is already destroyed.
4066+ // The override is recorded whatever is active by the time this
4067+ // rejection arrives: it is keyed by this write's server and is only
4068+ // ever displayed while that server is the active one, so a switch in
4069+ // between costs nothing and dropping it would leave the stale entry
4070+ // to answer for A the next time it comes back (#2095).
4071+ //
4072+ // The live client is the half that stays gated — it belongs to
4073+ // whichever server is connected now, so pushing this server's value
4074+ // into it after a switch would apply it to another one. It is taken
4075+ // from the ref for the same reason the success path does: a reconnect
4076+ // to the same server passes the id check while this continuation's
4077+ // captured instance is already destroyed.
40714078 const baseline =
40724079 lastPersistedSettings . resolve ( activeServerId ) ?? EMPTY_SETTINGS ;
4080+ paginatedListsOverride . record (
4081+ activeServerId ,
4082+ baseline . paginatedLists ?? false ,
4083+ ) ;
40734084 if ( activeServerIdRef . current === activeServerId ) {
4074- setPaginatedListsOverride ( baseline . paginatedLists ?? false ) ;
40754085 applyLiveServerSettings ( baseline ) ;
40764086 }
40774087 notifications . show ( {
@@ -4085,6 +4095,7 @@ function App() {
40854095 servers ,
40864096 activeServerId ,
40874097 lastPersistedSettings ,
4098+ paginatedListsOverride ,
40884099 applyLiveServerSettings ,
40894100 inspectorClient ,
40904101 updateServerSettings ,
@@ -4513,8 +4524,9 @@ function App() {
45134524 const write = lastPersistedSettings . begin ( id ) ;
45144525 const settleSettingsWrite = ( ) => {
45154526 const settled = write . landed ( value ) ;
4516- if ( settled && activeServerIdRef . current === id ) {
4517- setPaginatedListsOverride ( value . paginatedLists ?? false ) ;
4527+ if ( ! settled ) return ;
4528+ paginatedListsOverride . record ( id , value . paginatedLists ?? false ) ;
4529+ if ( activeServerIdRef . current === id ) {
45184530 applyLiveServerSettings ( value ) ;
45194531 }
45204532 } ;
@@ -4541,9 +4553,11 @@ function App() {
45414553 // write is the account of it and nothing else will re-apply it. Same
45424554 // reconciliation the toggle's own failure path does.
45434555 const baseline = lastPersistedSettings . resolve ( id ) ;
4544- if ( baseline && activeServerIdRef . current === id ) {
4545- setPaginatedListsOverride ( baseline . paginatedLists ?? false ) ;
4546- applyLiveServerSettings ( baseline ) ;
4556+ if ( baseline ) {
4557+ paginatedListsOverride . record ( id , baseline . paginatedLists ?? false ) ;
4558+ if ( activeServerIdRef . current === id ) {
4559+ applyLiveServerSettings ( baseline ) ;
4560+ }
45474561 }
45484562 throw err ;
45494563 }
0 commit comments