From 585a4cedbfd80d96df13ae44341ec4a47156cd5b Mon Sep 17 00:00:00 2001
From: Danny White <3104761+dnywh@users.noreply.github.com>
Date: Wed, 25 Mar 2026 19:03:17 +1100
Subject: [PATCH] fix: keep recent items labels in sync after renames (#44155)
## What kind of change does this PR introduce?
Bug fix.
## What is the current behavior?
The `Recent items` list shown in the table editor empty-state can keep
showing an old label after a table or SQL snippet is renamed.
That UI is mounted from:
- `/project/[ref]/editor`
- `/project/[ref]/editor/new`
## What is the new behavior?
- Re-adding an existing recent item now refreshes its label and metadata
instead of only bumping the timestamp.
- Updating an open tab label now also updates the matching recent item,
so rename flows stay aligned with the recent-items list.
- The fix is applied in the shared tabs store, so table and SQL editor
tab state stay consistent even though the visible `Recent items` UI
currently appears in the table editor empty-state.
- Adds a small store-level regression test for both cases.
| Before | After |
| --- | --- |
|
|
|
---
apps/studio/state/tabs.test.ts | 62 ++++++++++++++++++++++++++++++++++
apps/studio/state/tabs.tsx | 21 ++++++++++--
2 files changed, 81 insertions(+), 2 deletions(-)
create mode 100644 apps/studio/state/tabs.test.ts
diff --git a/apps/studio/state/tabs.test.ts b/apps/studio/state/tabs.test.ts
new file mode 100644
index 00000000000..21915e58d46
--- /dev/null
+++ b/apps/studio/state/tabs.test.ts
@@ -0,0 +1,62 @@
+import { beforeEach, describe, expect, it } from 'vitest'
+
+import { createTabsState } from './tabs'
+import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants'
+
+describe('tabs recent items', () => {
+ beforeEach(() => {
+ localStorage.clear()
+ })
+
+ it('refreshes an existing recent item label when the tab is re-added', () => {
+ const store = createTabsState('default')
+
+ store.addRecentItem({
+ id: 'r-1',
+ type: ENTITY_TYPE.TABLE,
+ label: 'tasks',
+ metadata: {
+ schema: 'public',
+ name: 'tasks',
+ tableId: 1,
+ },
+ })
+
+ store.addRecentItem({
+ id: 'r-1',
+ type: ENTITY_TYPE.TABLE,
+ label: 'routines',
+ metadata: {
+ schema: 'public',
+ name: 'routines',
+ tableId: 1,
+ },
+ })
+
+ expect(store.recentItems).toHaveLength(1)
+ expect(store.recentItems[0].label).toBe('routines')
+ expect(store.recentItems[0].metadata?.name).toBe('routines')
+ })
+
+ it('keeps recent items aligned when an open tab label changes', () => {
+ const store = createTabsState('default')
+
+ store.addTab({
+ id: 'r-1',
+ type: ENTITY_TYPE.TABLE,
+ label: 'tasks',
+ metadata: {
+ schema: 'public',
+ name: 'tasks',
+ tableId: 1,
+ },
+ isPreview: false,
+ })
+
+ store.updateTab('r-1', { label: 'routines' })
+
+ expect(store.tabsMap['r-1'].label).toBe('routines')
+ expect(store.recentItems[0].label).toBe('routines')
+ expect(store.recentItems[0].metadata?.name).toBe('routines')
+ })
+})
diff --git a/apps/studio/state/tabs.tsx b/apps/studio/state/tabs.tsx
index 2ded8c04b08..d757d1c61ae 100644
--- a/apps/studio/state/tabs.tsx
+++ b/apps/studio/state/tabs.tsx
@@ -110,7 +110,21 @@ function getSavedTabs(ref: string) {
}
}
-function createTabsState(projectRef: string) {
+const getRecentItemLabel = (tab: Pick) =>
+ tab.label || tab.metadata?.name || 'Untitled'
+
+const syncRecentItemWithTab = (item: RecentItem, tab: Pick) => {
+ const nextLabel = getRecentItemLabel(tab)
+
+ item.label = nextLabel
+ item.metadata = {
+ ...item.metadata,
+ ...tab.metadata,
+ name: nextLabel,
+ }
+}
+
+export function createTabsState(projectRef: string) {
const recentItems = getSavedRecentItems(projectRef)
const { openTabs, activeTab, tabsMap, previewTabId } = getSavedTabs(projectRef)
@@ -125,6 +139,7 @@ function createTabsState(projectRef: string) {
if (existingItem) {
// If it exists, update its timestamp
existingItem.timestamp = Date.now()
+ syncRecentItemWithTab(existingItem, tab)
return // Exit the function
}
@@ -132,7 +147,7 @@ function createTabsState(projectRef: string) {
const recentItem: RecentItem = {
id: tab.id, // Set the ID
type: tab.type, // Set the type
- label: tab.label || 'Untitled', // Set the label or default to 'Untitled'
+ label: getRecentItemLabel(tab), // Set the label or default to 'Untitled'
timestamp: Date.now(), // Set the current timestamp
metadata: tab.metadata, // Set the metadata
}
@@ -211,6 +226,8 @@ function createTabsState(projectRef: string) {
if (!!store.tabsMap[id]) {
if ('label' in updates) {
store.tabsMap[id].label = updates.label
+ const recentItem = store.recentItems.find((item) => item.id === id)
+ if (recentItem) syncRecentItemWithTab(recentItem, store.tabsMap[id])
}
if ('scrollTop' in updates && store.tabsMap[id].metadata) {
store.tabsMap[id].metadata.scrollTop = updates.scrollTop