diff --git a/CLAUDE.md b/CLAUDE.md
index 7a825aa..33aacfb 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -20,7 +20,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- **Zustand** with persistence middleware manages tabs, key search, type filters, selections, and search history
- **React Query** handles server state (key lists, values, metadata)
-- **Persistence:** Optional `RedisBrowserStorage` interface for localStorage/custom storage. Schema is at version 7 with automated migrations from earlier versions.
+- **Persistence:** Optional `RedisBrowserStorage` interface for localStorage/custom storage. Schema is versioned (see `version` in `src/store.tsx`) with automated migrations from earlier versions.
### Key Source Locations
diff --git a/src/components/databrowser/components/tab.tsx b/src/components/databrowser/components/tab.tsx
index 9ec5504..4dcb740 100644
--- a/src/components/databrowser/components/tab.tsx
+++ b/src/components/databrowser/components/tab.tsx
@@ -1,10 +1,11 @@
import type { TabId } from "@/store"
-import { useDatabrowserStore } from "@/store"
+import { useDatabrowserRootRef, useDatabrowserStore } from "@/store"
import { useTab } from "@/tab-provider"
import {
IconArrowsMinimize,
IconCopyPlus,
IconPin,
+ IconRestore,
IconSearch,
IconSquareX,
IconX,
@@ -34,12 +35,27 @@ export const Tab = ({ id, isList }: { id: TabId; isList?: boolean }) => {
duplicateTab,
closeOtherTabs,
closeAllButPinned,
+ closedTabs,
+ reopenClosedTab,
} = useDatabrowserStore()
+ const rootRef = useDatabrowserRootRef()
const hasPinnedTabs = tabs.some(([, data]) => data.pinned)
const { ref, isOverflow } = useOverflow()
+ const handleReopenClosedTab = () => {
+ const reopenedId = reopenClosedTab()
+ if (!reopenedId) return
+
+ setTimeout(() => {
+ const tab = rootRef?.current?.querySelector(`#tab-${reopenedId}`)
+ if (!tab) return
+
+ tab.scrollIntoView({ behavior: "smooth" })
+ }, 20)
+ }
+
const label = isValuesSearchSelected ? valuesSearch.index : search.key || selectedKey
const iconNode = isValuesSearchSelected ? (
@@ -125,6 +141,15 @@ export const Tab = ({ id, isList }: { id: TabId; isList?: boolean }) => {
Close All But Pinned
+
+
+
+ Reopen Closed Tab
+
)
diff --git a/src/store.tsx b/src/store.tsx
index 2d050e6..21b6a44 100644
--- a/src/store.tsx
+++ b/src/store.tsx
@@ -45,7 +45,7 @@ export const DatabrowserProvider = ({
setItem: (_name, value) => storage.set(JSON.stringify(value)),
removeItem: () => {},
},
- version: 8,
+ version: 9,
migrate: (originalState, version) => {
const state = originalState as DatabrowserStore
@@ -93,6 +93,11 @@ export const DatabrowserProvider = ({
state.aiDataSharingConsent = state.aiDataSharingConsent ?? false
}
+ if (version <= 8) {
+ // Add recently closed tabs stack
+ state.closedTabs = state.closedTabs ?? []
+ }
+
return state
},
})
@@ -159,6 +164,10 @@ type DatabrowserStore = {
selectedTab: TabId | undefined
tabs: [TabId, TabData][]
+ // Recently closed tabs, grouped by close action so that one reopen
+ // restores everything a "Close Other Tabs" etc. closed at once
+ closedTabs: TabData[][]
+
addTab: () => TabId
removeTab: (id: TabId) => void
forceRemoveTab: (id: TabId) => void
@@ -170,6 +179,7 @@ type DatabrowserStore = {
duplicateTab: (id: TabId) => TabId | undefined
closeOtherTabs: (id: TabId) => void
closeAllButPinned: () => void
+ reopenClosedTab: () => TabId | undefined
// Tab actions
getSelectedKeys: (tabId: TabId) => string[]
@@ -196,321 +206,370 @@ type DatabrowserStore = {
export type DatabrowserStoreObject = UseBoundStore>
-const storeCreator: StateCreator = (set, get) => ({
- selectedTab: undefined,
- tabs: [],
-
- addTab: () => {
- const id = crypto.randomUUID() as TabId
-
- const newTabData: TabData = {
- id,
- selectedKeys: [],
- search: { key: "", type: undefined },
- valuesSearch: { index: "", queries: {}, queryBuilderMode: "ui" },
- isValuesSearchSelected: false,
- pinned: false,
- }
-
- set((old) => ({
- tabs: [...old.tabs, [id, newTabData]],
- selectedTab: id,
- }))
-
- return id
- },
-
- reorderTabs: (oldIndex, newIndex) => {
- set((old) => {
- // Don't allow reordering pinned tabs
- const [, oldTabData] = old.tabs[oldIndex]
- const [, newTabData] = old.tabs[newIndex]
- if (oldTabData.pinned || newTabData.pinned) return old
-
- const newTabs = [...old.tabs]
- const [movedTab] = newTabs.splice(oldIndex, 1)
- newTabs.splice(newIndex, 0, movedTab)
- return { ...old, tabs: newTabs }
- })
- },
-
- removeTab: (id) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
- if (tabIndex === -1) return old
-
- // Don't remove pinned tabs via X button
- const [, tabData] = old.tabs[tabIndex]
- if (tabData.pinned) return old
-
- const newTabs = [...old.tabs]
- newTabs.splice(tabIndex, 1)
-
- // If we're removing the selected tab, select the tab to the left
- let selectedTab = old.selectedTab
- if (selectedTab === id) {
- const [newId] = newTabs[tabIndex - 1] ?? newTabs[tabIndex]
-
- selectedTab = newTabs.length > 0 ? newId : undefined
+const MAX_CLOSED_TAB_GROUPS = 10
+
+const storeCreator: StateCreator = (set, get) => {
+ const pushClosedTabs = (tabs: TabData[]) =>
+ tabs.length > 0 ? [...get().closedTabs, tabs].slice(-MAX_CLOSED_TAB_GROUPS) : get().closedTabs
+
+ return {
+ selectedTab: undefined,
+ tabs: [],
+ closedTabs: [],
+
+ addTab: () => {
+ const id = crypto.randomUUID() as TabId
+
+ const newTabData: TabData = {
+ id,
+ selectedKeys: [],
+ search: { key: "", type: undefined },
+ valuesSearch: { index: "", queries: {}, queryBuilderMode: "ui" },
+ isValuesSearchSelected: false,
+ pinned: false,
}
- return { tabs: newTabs, selectedTab }
- })
- },
+ set((old) => ({
+ tabs: [...old.tabs, [id, newTabData]],
+ selectedTab: id,
+ }))
+
+ return id
+ },
+
+ reorderTabs: (oldIndex, newIndex) => {
+ set((old) => {
+ // Don't allow reordering pinned tabs
+ const [, oldTabData] = old.tabs[oldIndex]
+ const [, newTabData] = old.tabs[newIndex]
+ if (oldTabData.pinned || newTabData.pinned) return old
+
+ const newTabs = [...old.tabs]
+ const [movedTab] = newTabs.splice(oldIndex, 1)
+ newTabs.splice(newIndex, 0, movedTab)
+ return { ...old, tabs: newTabs }
+ })
+ },
- forceRemoveTab: (id) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
- if (tabIndex === -1) return old
+ removeTab: (id) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
+ if (tabIndex === -1) return old
- const newTabs = [...old.tabs]
- newTabs.splice(tabIndex, 1)
+ // Don't remove pinned tabs via X button
+ const [, tabData] = old.tabs[tabIndex]
+ if (tabData.pinned) return old
- // If we're removing the selected tab, select the tab to the left
- let selectedTab = old.selectedTab
- if (selectedTab === id) {
- const [newId] = newTabs[tabIndex - 1] ?? newTabs[tabIndex]
+ const newTabs = [...old.tabs]
+ newTabs.splice(tabIndex, 1)
- selectedTab = newTabs.length > 0 ? newId : undefined
- }
+ // If we're removing the selected tab, select the tab to the left
+ let selectedTab = old.selectedTab
+ if (selectedTab === id) {
+ const [newId] = newTabs[tabIndex - 1] ?? newTabs[tabIndex] ?? []
- return { tabs: newTabs, selectedTab }
- })
- },
-
- togglePinTab: (id) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [tabId, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [tabId, { ...tabData, pinned: !tabData.pinned }]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- duplicateTab: (id) => {
- let newId: TabId | undefined
- set((old) => {
- const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newId = crypto.randomUUID() as TabId
- const duplicated: [TabId, TabData] = [newId, { ...tabData, id: newId }]
-
- // Insert right after the original tab
- newTabs.splice(tabIndex + 1, 0, duplicated)
-
- return { ...old, tabs: newTabs, selectedTab: newId }
- })
- return newId
- },
-
- closeOtherTabs: (id) => {
- set((old) => {
- const exists = old.tabs.some(([tabId]) => tabId === id)
- if (!exists) return old
-
- const newTabs: [TabId, TabData][] = old.tabs.filter(([tabId]) => tabId === id)
- return { ...old, tabs: newTabs, selectedTab: id }
- })
- },
-
- closeAllButPinned: () => {
- set((old) => {
- const newTabs = old.tabs.filter(([, data]) => data.pinned)
- const newSelected = newTabs.length > 0 ? newTabs[0][0] : undefined
- return { ...old, tabs: newTabs, selectedTab: newSelected }
- })
- },
-
- selectTab: (id) => {
- set({ selectedTab: id })
- },
-
- getSelectedKeys: (tabId) => {
- return get().tabs.find(([id]) => id === tabId)?.[1]?.selectedKeys ?? []
- },
-
- setSelectedKey: (tabId, key) => {
- get().setSelectedKeys(tabId, key ? [key] : [])
- },
-
- setSelectedKeys: (tabId, keys) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [tabId, { ...tabData, selectedKeys: keys, selectedListItem: undefined }]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setSelectedListItem: (tabId, item) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [tabId, { ...tabData, selectedListItem: item }]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setSearch: (tabId, search) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [tabId, { ...tabData, search }]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setSearchKey: (tabId, key) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [
- tabId,
- {
- ...tabData,
- search: { ...tabData.search, key },
- },
- ]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setSearchType: (tabId, type) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [
- tabId,
- {
- ...tabData,
- search: { ...tabData.search, type },
- },
- ]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setValuesSearch: (tabId, valuesSearch) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [tabId, { ...tabData, valuesSearch }]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setValuesSearchIndex: (tabId, index) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [
- tabId,
- {
- ...tabData,
- valuesSearch: { ...tabData.valuesSearch, index },
- },
- ]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setValuesSearchQuery: (tabId, query) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- const currentIndex = tabData.valuesSearch.index
- newTabs[tabIndex] = [
- tabId,
- {
- ...tabData,
- valuesSearch: {
- ...tabData.valuesSearch,
- queries: { ...tabData.valuesSearch.queries, [currentIndex]: query },
+ selectedTab = newTabs.length > 0 ? newId : undefined
+ }
+
+ return {
+ tabs: newTabs,
+ selectedTab,
+ closedTabs: pushClosedTabs([tabData]),
+ }
+ })
+ },
+
+ forceRemoveTab: (id) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
+ if (tabIndex === -1) return old
+
+ const [, tabData] = old.tabs[tabIndex]
+
+ const newTabs = [...old.tabs]
+ newTabs.splice(tabIndex, 1)
+
+ // If we're removing the selected tab, select the tab to the left
+ let selectedTab = old.selectedTab
+ if (selectedTab === id) {
+ const [newId] = newTabs[tabIndex - 1] ?? newTabs[tabIndex] ?? []
+
+ selectedTab = newTabs.length > 0 ? newId : undefined
+ }
+
+ return {
+ tabs: newTabs,
+ selectedTab,
+ closedTabs: pushClosedTabs([tabData]),
+ }
+ })
+ },
+
+ togglePinTab: (id) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [tabId, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [tabId, { ...tabData, pinned: !tabData.pinned }]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ duplicateTab: (id) => {
+ let newId: TabId | undefined
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([tabId]) => tabId === id)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newId = crypto.randomUUID() as TabId
+ const duplicated: [TabId, TabData] = [newId, { ...tabData, id: newId }]
+
+ // Insert right after the original tab
+ newTabs.splice(tabIndex + 1, 0, duplicated)
+
+ return { ...old, tabs: newTabs, selectedTab: newId }
+ })
+ return newId
+ },
+
+ closeOtherTabs: (id) => {
+ set((old) => {
+ const exists = old.tabs.some(([tabId]) => tabId === id)
+ if (!exists) return old
+
+ const closed = old.tabs.filter(([tabId]) => tabId !== id).map(([, data]) => data)
+
+ const newTabs: [TabId, TabData][] = old.tabs.filter(([tabId]) => tabId === id)
+ return {
+ ...old,
+ tabs: newTabs,
+ selectedTab: id,
+ closedTabs: pushClosedTabs(closed),
+ }
+ })
+ },
+
+ closeAllButPinned: () => {
+ set((old) => {
+ const closed = old.tabs.filter(([, data]) => !data.pinned).map(([, data]) => data)
+
+ const newTabs = old.tabs.filter(([, data]) => data.pinned)
+ const newSelected = newTabs.length > 0 ? newTabs[0][0] : undefined
+ return {
+ ...old,
+ tabs: newTabs,
+ selectedTab: newSelected,
+ closedTabs: pushClosedTabs(closed),
+ }
+ })
+ },
+
+ reopenClosedTab: () => {
+ let reopenedId: TabId | undefined
+ set((old) => {
+ const group = old.closedTabs.at(-1)
+ if (!group) return old
+
+ reopenedId = group.at(-1)?.id
+ return {
+ ...old,
+ tabs: [...old.tabs, ...group.map((tab) => [tab.id, tab] as [TabId, TabData])],
+ selectedTab: reopenedId,
+ closedTabs: old.closedTabs.slice(0, -1),
+ }
+ })
+ return reopenedId
+ },
+
+ selectTab: (id) => {
+ set({ selectedTab: id })
+ },
+
+ getSelectedKeys: (tabId) => {
+ return get().tabs.find(([id]) => id === tabId)?.[1]?.selectedKeys ?? []
+ },
+
+ setSelectedKey: (tabId, key) => {
+ get().setSelectedKeys(tabId, key ? [key] : [])
+ },
+
+ setSelectedKeys: (tabId, keys) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [tabId, { ...tabData, selectedKeys: keys, selectedListItem: undefined }]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setSelectedListItem: (tabId, item) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [tabId, { ...tabData, selectedListItem: item }]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setSearch: (tabId, search) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [tabId, { ...tabData, search }]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setSearchKey: (tabId, key) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [
+ tabId,
+ {
+ ...tabData,
+ search: { ...tabData.search, key },
},
- },
- ]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setIsValuesSearchSelected: (tabId, isSelected) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [tabId, { ...tabData, isValuesSearchSelected: isSelected }]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- setQueryBuilderMode: (tabId, mode) => {
- set((old) => {
- const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
- if (tabIndex === -1) return old
-
- const newTabs = [...old.tabs]
- const [, tabData] = newTabs[tabIndex]
- newTabs[tabIndex] = [
- tabId,
- {
- ...tabData,
- valuesSearch: { ...tabData.valuesSearch, queryBuilderMode: mode },
- },
- ]
-
- return { ...old, tabs: newTabs }
- })
- },
-
- searchHistory: [],
- addSearchHistory: (key) => {
- set((old) => ({ ...old, searchHistory: [key, ...old.searchHistory] }))
- },
-
- aiDataSharingConsent: false,
- setAiDataSharingConsent: (consent) => {
- set({ aiDataSharingConsent: consent })
- },
-})
+ ]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setSearchType: (tabId, type) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [
+ tabId,
+ {
+ ...tabData,
+ search: { ...tabData.search, type },
+ },
+ ]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setValuesSearch: (tabId, valuesSearch) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [tabId, { ...tabData, valuesSearch }]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setValuesSearchIndex: (tabId, index) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [
+ tabId,
+ {
+ ...tabData,
+ valuesSearch: { ...tabData.valuesSearch, index },
+ },
+ ]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setValuesSearchQuery: (tabId, query) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ const currentIndex = tabData.valuesSearch.index
+ newTabs[tabIndex] = [
+ tabId,
+ {
+ ...tabData,
+ valuesSearch: {
+ ...tabData.valuesSearch,
+ queries: { ...tabData.valuesSearch.queries, [currentIndex]: query },
+ },
+ },
+ ]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setIsValuesSearchSelected: (tabId, isSelected) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [tabId, { ...tabData, isValuesSearchSelected: isSelected }]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ setQueryBuilderMode: (tabId, mode) => {
+ set((old) => {
+ const tabIndex = old.tabs.findIndex(([id]) => id === tabId)
+ if (tabIndex === -1) return old
+
+ const newTabs = [...old.tabs]
+ const [, tabData] = newTabs[tabIndex]
+ newTabs[tabIndex] = [
+ tabId,
+ {
+ ...tabData,
+ valuesSearch: { ...tabData.valuesSearch, queryBuilderMode: mode },
+ },
+ ]
+
+ return { ...old, tabs: newTabs }
+ })
+ },
+
+ searchHistory: [],
+ addSearchHistory: (key) => {
+ set((old) => ({ ...old, searchHistory: [key, ...old.searchHistory] }))
+ },
+
+ aiDataSharingConsent: false,
+ setAiDataSharingConsent: (consent) => {
+ set({ aiDataSharingConsent: consent })
+ },
+ }
+}