Skip to content

Commit 5b32d2a

Browse files
[Manager] Persist/Restore Manager UI state (#4180)
1 parent 23ba7e6 commit 5b32d2a

File tree

6 files changed

+119
-41
lines changed

6 files changed

+119
-41
lines changed

src/components/dialog/content/manager/ManagerDialogContent.vue

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,14 @@
9393
import { whenever } from '@vueuse/core'
9494
import { merge } from 'lodash'
9595
import Button from 'primevue/button'
96-
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
96+
import {
97+
computed,
98+
onBeforeUnmount,
99+
onMounted,
100+
onUnmounted,
101+
ref,
102+
watch
103+
} from 'vue'
97104
import { useI18n } from 'vue-i18n'
98105
99106
import ContentDivider from '@/components/common/ContentDivider.vue'
@@ -106,6 +113,7 @@ import PackCard from '@/components/dialog/content/manager/packCard/PackCard.vue'
106113
import RegistrySearchBar from '@/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue'
107114
import GridSkeleton from '@/components/dialog/content/manager/skeleton/GridSkeleton.vue'
108115
import { useResponsiveCollapse } from '@/composables/element/useResponsiveCollapse'
116+
import { useManagerStatePersistence } from '@/composables/manager/useManagerStatePersistence'
109117
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks'
110118
import { usePackUpdateStatus } from '@/composables/nodePack/usePackUpdateStatus'
111119
import { useWorkflowPacks } from '@/composables/nodePack/useWorkflowPacks'
@@ -116,13 +124,15 @@ import type { TabItem } from '@/types/comfyManagerTypes'
116124
import { ManagerTab } from '@/types/comfyManagerTypes'
117125
import { components } from '@/types/comfyRegistryTypes'
118126
119-
const { initialTab = ManagerTab.All } = defineProps<{
120-
initialTab: ManagerTab
127+
const { initialTab } = defineProps<{
128+
initialTab?: ManagerTab
121129
}>()
122130
123131
const { t } = useI18n()
124132
const comfyManagerStore = useComfyManagerStore()
125133
const { getPackById } = useComfyRegistryStore()
134+
const persistedState = useManagerStatePersistence()
135+
const initialState = persistedState.loadStoredState()
126136
127137
const GRID_STYLE = {
128138
display: 'grid',
@@ -156,8 +166,10 @@ const tabs = ref<TabItem[]>([
156166
icon: 'pi-sync'
157167
}
158168
])
169+
170+
const initialTabId = initialTab ?? initialState.selectedTabId
159171
const selectedTab = ref<TabItem>(
160-
tabs.value.find((tab) => tab.id === initialTab) || tabs.value[0]
172+
tabs.value.find((tab) => tab.id === initialTabId) || tabs.value[0]
161173
)
162174
163175
const {
@@ -168,7 +180,11 @@ const {
168180
searchMode,
169181
sortField,
170182
suggestions
171-
} = useRegistrySearch()
183+
} = useRegistrySearch({
184+
initialSortField: initialState.sortField,
185+
initialSearchMode: initialState.searchMode,
186+
initialSearchQuery: initialState.searchQuery
187+
})
172188
pageNumber.value = 0
173189
const onApproachEnd = () => {
174190
pageNumber.value++
@@ -456,6 +472,15 @@ watch(searchQuery, () => {
456472
}
457473
})
458474
475+
onBeforeUnmount(() => {
476+
persistedState.persistState({
477+
selectedTabId: selectedTab.value?.id,
478+
searchQuery: searchQuery.value,
479+
searchMode: searchMode.value,
480+
sortField: sortField.value
481+
})
482+
})
483+
459484
onUnmounted(() => {
460485
getPackById.cancel()
461486
})
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {
2+
ManagerState,
3+
ManagerTab,
4+
SortableAlgoliaField
5+
} from '@/types/comfyManagerTypes'
6+
7+
const STORAGE_KEY = 'Comfy.Manager.UI.State'
8+
9+
export const useManagerStatePersistence = () => {
10+
/**
11+
* Load the UI state from localStorage.
12+
*/
13+
const loadStoredState = (): ManagerState => {
14+
try {
15+
const stored = localStorage.getItem(STORAGE_KEY)
16+
if (stored) {
17+
return JSON.parse(stored)
18+
}
19+
} catch (e) {
20+
console.error('Failed to load manager UI state:', e)
21+
}
22+
return {
23+
selectedTabId: ManagerTab.All,
24+
searchQuery: '',
25+
searchMode: 'packs',
26+
sortField: SortableAlgoliaField.Downloads
27+
}
28+
}
29+
30+
/**
31+
* Persist the UI state to localStorage.
32+
*/
33+
const persistState = (state: ManagerState) => {
34+
localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
35+
}
36+
37+
/**
38+
* Reset the UI state to the default values.
39+
*/
40+
const reset = () => {
41+
persistState({
42+
selectedTabId: ManagerTab.All,
43+
searchQuery: '',
44+
searchMode: 'packs',
45+
sortField: SortableAlgoliaField.Downloads
46+
})
47+
}
48+
49+
return {
50+
loadStoredState,
51+
persistState,
52+
reset
53+
}
54+
}

src/composables/useRegistrySearch.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { watchDebounced } from '@vueuse/core'
22
import type { Hit } from 'algoliasearch/dist/lite/browser'
33
import { memoize, orderBy } from 'lodash'
4-
import { computed, onUnmounted, ref, watch } from 'vue'
4+
import { computed, ref, watch } from 'vue'
55

66
import { useAlgoliaSearchService } from '@/services/algoliaSearchService'
77
import type {
@@ -14,7 +14,6 @@ import { SortableAlgoliaField } from '@/types/comfyManagerTypes'
1414
const SEARCH_DEBOUNCE_TIME = 320
1515
const DEFAULT_PAGE_SIZE = 64
1616
const DEFAULT_SORT_FIELD = SortableAlgoliaField.Downloads // Set in the index configuration
17-
const DEFAULT_MAX_CACHE_SIZE = 64
1817
const SORT_DIRECTIONS: Record<SortableAlgoliaField, 'asc' | 'desc'> = {
1918
[SortableAlgoliaField.Downloads]: 'desc',
2019
[SortableAlgoliaField.Created]: 'desc',
@@ -30,18 +29,25 @@ const isDateField = (field: SortableAlgoliaField): boolean =>
3029
/**
3130
* Composable for managing UI state of Comfy Node Registry search.
3231
*/
33-
export function useRegistrySearch(
34-
options: {
35-
maxCacheSize?: number
36-
} = {}
37-
) {
38-
const { maxCacheSize = DEFAULT_MAX_CACHE_SIZE } = options
32+
export function useRegistrySearch(options: {
33+
initialSortField?: SortableAlgoliaField
34+
initialSearchMode?: 'nodes' | 'packs'
35+
initialSearchQuery?: string
36+
initialPageNumber?: number
37+
}) {
38+
const {
39+
initialSortField = SortableAlgoliaField.Downloads,
40+
initialSearchMode = 'packs',
41+
initialSearchQuery = '',
42+
initialPageNumber = 0
43+
} = options
44+
3945
const isLoading = ref(false)
40-
const sortField = ref<SortableAlgoliaField>(SortableAlgoliaField.Downloads)
41-
const searchMode = ref<'nodes' | 'packs'>('packs')
46+
const sortField = ref<SortableAlgoliaField>(initialSortField)
47+
const searchMode = ref<'nodes' | 'packs'>(initialSearchMode)
4248
const pageSize = ref(DEFAULT_PAGE_SIZE)
43-
const pageNumber = ref(0)
44-
const searchQuery = ref('')
49+
const pageNumber = ref(initialPageNumber)
50+
const searchQuery = ref(initialSearchQuery)
4551
const results = ref<AlgoliaNodePack[]>([])
4652
const suggestions = ref<NodesIndexSuggestion[]>([])
4753

@@ -62,9 +68,7 @@ export function useRegistrySearch(
6268
)
6369

6470
const { searchPacksCached, toRegistryPack, clearSearchPacksCache } =
65-
useAlgoliaSearchService({
66-
maxCacheSize
67-
})
71+
useAlgoliaSearchService()
6872

6973
const algoliaToRegistry = memoize(
7074
toRegistryPack,
@@ -124,8 +128,6 @@ export function useRegistrySearch(
124128
immediate: true
125129
})
126130

127-
onUnmounted(clearSearchPacksCache)
128-
129131
return {
130132
isLoading,
131133
pageNumber,
@@ -135,6 +137,7 @@ export function useRegistrySearch(
135137
searchQuery,
136138
suggestions,
137139
searchResults: resultsAsRegistryPacks,
138-
nodeSearchResults: resultsAsNodes
140+
nodeSearchResults: resultsAsNodes,
141+
clearCache: clearSearchPacksCache
139142
}
140143
}

src/services/algoliaSearchService.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ const RETRIEVE_ATTRIBUTES: SearchAttribute[] = [
4040
]
4141

4242
interface AlgoliaSearchServiceOptions {
43-
/**
44-
* Maximum number of search results to store in the cache.
45-
* The cache is automatically cleared when the component is unmounted.
46-
* @default 64
47-
*/
48-
maxCacheSize?: number
4943
/**
5044
* Minimum number of characters for suggestions. An additional query
5145
* will be made to the suggestions/completions index for queries that
@@ -55,17 +49,15 @@ interface AlgoliaSearchServiceOptions {
5549
minCharsForSuggestions?: number
5650
}
5751

52+
const searchPacksCache = new QuickLRU<string, SearchPacksResult>({
53+
maxSize: DEFAULT_MAX_CACHE_SIZE
54+
})
55+
5856
export const useAlgoliaSearchService = (
5957
options: AlgoliaSearchServiceOptions = {}
6058
) => {
61-
const {
62-
maxCacheSize = DEFAULT_MAX_CACHE_SIZE,
63-
minCharsForSuggestions = DEFAULT_MIN_CHARS_FOR_SUGGESTIONS
64-
} = options
59+
const { minCharsForSuggestions = DEFAULT_MIN_CHARS_FOR_SUGGESTIONS } = options
6560
const searchClient = algoliasearch(__ALGOLIA_APP_ID__, __ALGOLIA_API_KEY__)
66-
const searchPacksCache = new QuickLRU<string, SearchPacksResult>({
67-
maxSize: maxCacheSize
68-
})
6961

7062
const toRegistryLatestVersion = (
7163
algoliaNode: AlgoliaNodePack

src/services/dialogService.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import TemplateWorkflowsDialogHeader from '@/components/templates/TemplateWorkfl
2121
import { t } from '@/i18n'
2222
import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema'
2323
import { type ShowDialogOptions, useDialogStore } from '@/stores/dialogStore'
24-
import { ManagerTab } from '@/types/comfyManagerTypes'
2524

2625
export type ConfirmationDialogType =
2726
| 'default'
@@ -129,9 +128,7 @@ export const useDialogService = () => {
129128
}
130129

131130
function showManagerDialog(
132-
props: InstanceType<typeof ManagerDialogContent>['$props'] = {
133-
initialTab: ManagerTab.All
134-
}
131+
props: InstanceType<typeof ManagerDialogContent>['$props'] = {}
135132
) {
136133
dialogStore.showDialog({
137134
key: 'global-manager',

src/types/comfyManagerTypes.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export enum SortableAlgoliaField {
3939
}
4040

4141
export interface TabItem {
42-
id: string
42+
id: ManagerTab
4343
label: string
4444
icon: string
4545
}
@@ -234,3 +234,10 @@ export interface InstallPackParams extends ManagerPackInfo {
234234
export interface UpdateAllPacksParams {
235235
mode?: ManagerDatabaseSource
236236
}
237+
238+
export interface ManagerState {
239+
selectedTabId: ManagerTab
240+
searchQuery: string
241+
searchMode: 'nodes' | 'packs'
242+
sortField: SortableAlgoliaField
243+
}

0 commit comments

Comments
 (0)