Describe the bug
With "Save the application state?" enabled, Query Tool tabs are normally restored after a page refresh or application restart. If File → Reset Layout is used while Query Tool tabs are open, those tabs survive the reset itself, but they are lost on the next refresh. Only the default tabs (Dashboard, Properties, SQL, Statistics, Dependencies, Dependents) come back. The saved query text is left orphaned in the application_state table with nothing referencing it.
To Reproduce
Steps to reproduce the behavior:
In Preferences → Miscellaneous → User Interface, make sure "Save the application state?" is enabled. Layout can be Workspace or Classic; both reproduce.
Connect to any server and open a Query Tool. Type SELECT 1; so the tab has content.
Open a second Query Tool on the same database.
Control check: refresh the page (or reload in Desktop). Both Query Tool tabs are restored with their text. This confirms application-state restore is working.
Go to File → Reset Layout.
Observe that the default tabs return to their positions and both Query Tool tabs are still open with their content.
Refresh the page again.
Both Query Tool tabs are gone; only the default tabs are present.
Splitting or rearranging tabs before the reset is not required. One Query Tool tab, one reset and one refresh is enough.
Expected behavior
After step 7 the Query Tool tabs should be restored with their content, exactly as they were in step 4. Reset Layout should only reset panel positions; it should not affect which tabs are restored on the next load.
Error message
None. No error is shown in the UI or the browser console; the tabs are silently dropped.
Screenshots
[Attach: tab bar after step 6 showing default tabs plus the two Query Tool tabs, and tab bar after step 7 showing only the default tabs.]
Desktop (please complete the following information):
OS: macOS [your version]
pgAdmin version: 9.18 development (current master), also reproduces on 9.12
Mode: [Desktop or Server]
Browser (if running in server mode): [e.g. Chrome]
Package type: [e.g. Source / macOS DMG / Container]
Additional context
Root cause is in web/pgadmin/static/js/helpers/Layout/index.jsx, LayoutDocker.resetLayout(). Restore works by serialising each tool tab's metaData (tool URL, form params, tab params) into the saved layout via the saveTab callback, and loadTab rebuilds the tab from that metaData on the next load. A non-default tab without metaData is dropped by loadTab.
resetLayout() collects the non-default tabs, loads the default layout, then re-opens each one with:
this.openTab({
id: t.id, content: t.content, ...t.internal
}, this.resetToTabPanel, 'middle');
This passes the id, the live content and the internal display fields, but not t.metaData. The re-opened tab still works because the existing iframe content is reused, which is why it survives the reset. The this.saveLayout() call at the end of resetLayout() then writes those tabs as bare ids, and on the next load loadTab has nothing to rebuild them from.
Passing metaData: t.metaData through in that openTab call fixes it. LayoutDocker.getPanel() already spreads unknown attributes onto the tab, so the metadata is preserved once supplied.
This re-open code dates from the 2023 React migration and predates the Save Application State feature, so the two have never worked together. It is not related to the recent Object Explorer collapse change (#10254), which only added an event emission at the end of the same function.
Describe the bug
With "Save the application state?" enabled, Query Tool tabs are normally restored after a page refresh or application restart. If File → Reset Layout is used while Query Tool tabs are open, those tabs survive the reset itself, but they are lost on the next refresh. Only the default tabs (Dashboard, Properties, SQL, Statistics, Dependencies, Dependents) come back. The saved query text is left orphaned in the application_state table with nothing referencing it.
To Reproduce
Steps to reproduce the behavior:
In Preferences → Miscellaneous → User Interface, make sure "Save the application state?" is enabled. Layout can be Workspace or Classic; both reproduce.
Connect to any server and open a Query Tool. Type SELECT 1; so the tab has content.
Open a second Query Tool on the same database.
Control check: refresh the page (or reload in Desktop). Both Query Tool tabs are restored with their text. This confirms application-state restore is working.
Go to File → Reset Layout.
Observe that the default tabs return to their positions and both Query Tool tabs are still open with their content.
Refresh the page again.
Both Query Tool tabs are gone; only the default tabs are present.
Splitting or rearranging tabs before the reset is not required. One Query Tool tab, one reset and one refresh is enough.
Expected behavior
After step 7 the Query Tool tabs should be restored with their content, exactly as they were in step 4. Reset Layout should only reset panel positions; it should not affect which tabs are restored on the next load.
Error message
None. No error is shown in the UI or the browser console; the tabs are silently dropped.
Screenshots
[Attach: tab bar after step 6 showing default tabs plus the two Query Tool tabs, and tab bar after step 7 showing only the default tabs.]
Desktop (please complete the following information):
OS: macOS [your version]
pgAdmin version: 9.18 development (current master), also reproduces on 9.12
Mode: [Desktop or Server]
Browser (if running in server mode): [e.g. Chrome]
Package type: [e.g. Source / macOS DMG / Container]
Additional context
Root cause is in web/pgadmin/static/js/helpers/Layout/index.jsx, LayoutDocker.resetLayout(). Restore works by serialising each tool tab's metaData (tool URL, form params, tab params) into the saved layout via the saveTab callback, and loadTab rebuilds the tab from that metaData on the next load. A non-default tab without metaData is dropped by loadTab.
resetLayout() collects the non-default tabs, loads the default layout, then re-opens each one with:
this.openTab({
id: t.id, content: t.content, ...t.internal
}, this.resetToTabPanel, 'middle');
This passes the id, the live content and the internal display fields, but not t.metaData. The re-opened tab still works because the existing iframe content is reused, which is why it survives the reset. The this.saveLayout() call at the end of resetLayout() then writes those tabs as bare ids, and on the next load loadTab has nothing to rebuild them from.
Passing metaData: t.metaData through in that openTab call fixes it. LayoutDocker.getPanel() already spreads unknown attributes onto the tab, so the metadata is preserved once supplied.
This re-open code dates from the 2023 React migration and predates the Save Application State feature, so the two have never worked together. It is not related to the recent Object Explorer collapse change (#10254), which only added an event emission at the end of the same function.