Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
58550a5
refactor: replace marketplace context with nuqs + jotai + tanstack query
hyoban Jan 7, 2026
64909f1
no test change for now
hyoban Jan 7, 2026
e529368
tab
hyoban Jan 7, 2026
3b4e6bd
Merge branch 'main' into 1-7-marketplace-state
hyoban Jan 7, 2026
712c2c1
reative version
hyoban Jan 7, 2026
75fddd8
Merge branch 'main' into 1-7-marketplace-state
hyoban Jan 8, 2026
38d5f22
update
hyoban Jan 8, 2026
afec4f7
update
hyoban Jan 8, 2026
060c480
update
hyoban Jan 8, 2026
8feb878
update
hyoban Jan 8, 2026
10a4d7b
update
hyoban Jan 8, 2026
98aff7e
update
hyoban Jan 8, 2026
a7402ac
Merge branch 'main' into 1-7-marketplace-state
hyoban Jan 8, 2026
2ba0adb
update
hyoban Jan 8, 2026
8acd17d
update
hyoban Jan 8, 2026
ee8f6c2
update
hyoban Jan 8, 2026
21c647b
update
hyoban Jan 8, 2026
5614fc8
update
hyoban Jan 8, 2026
5ab08b1
search mode
hyoban Jan 8, 2026
0ded496
search mode default
hyoban Jan 8, 2026
7b05cc1
better search mode logic
hyoban Jan 8, 2026
38507d8
prefetch
hyoban Jan 8, 2026
8549f51
prefetch for all category
hyoban Jan 8, 2026
385d3fc
share params
hyoban Jan 8, 2026
817ed91
no prefetch for app
hyoban Jan 8, 2026
f47ff73
type
hyoban Jan 8, 2026
e55640f
rename
hyoban Jan 8, 2026
237ee02
rename
hyoban Jan 8, 2026
9a80174
update
hyoban Jan 8, 2026
1b7d182
update
hyoban Jan 8, 2026
e509a9b
update
hyoban Jan 8, 2026
bd73c33
update
hyoban Jan 8, 2026
a63d985
note
hyoban Jan 8, 2026
e04a45b
parseAsStringEnum
hyoban Jan 8, 2026
df183b7
upda
hyoban Jan 8, 2026
908d649
value of
hyoban Jan 8, 2026
ff97344
update
hyoban Jan 8, 2026
16b7ae9
deprecated
hyoban Jan 8, 2026
d91306d
Merge branch 'main' into 1-7-marketplace-state
hyoban Jan 8, 2026
200b028
update
hyoban Jan 8, 2026
3d2950e
update
hyoban Jan 8, 2026
0f46207
update
hyoban Jan 8, 2026
147d6e4
update
hyoban Jan 8, 2026
effdcf9
update
hyoban Jan 8, 2026
2f0cafb
update
hyoban Jan 8, 2026
d04a8dd
update
hyoban Jan 8, 2026
3d356d9
update
hyoban Jan 8, 2026
15cdafc
update
hyoban Jan 8, 2026
0115251
update
hyoban Jan 8, 2026
cfb7e8f
update
hyoban Jan 8, 2026
fe24859
update
hyoban Jan 8, 2026
369b982
update
hyoban Jan 8, 2026
b8955c6
deprecated only
hyoban Jan 8, 2026
53af2ff
todo
hyoban Jan 9, 2026
18853eb
fix: remove obsolete context-based tests and fix type errors
hyoban Jan 9, 2026
e500dc7
Merge branch 'main' into 1-7-marketplace-state
hyoban Jan 9, 2026
77c531b
no preserve in app
hyoban Jan 9, 2026
b95129c
refactor
hyoban Jan 9, 2026
12e496d
fix test
hyoban Jan 9, 2026
c2cb75f
rename
hyoban Jan 9, 2026
3592093
bring back debounce
hyoban Jan 9, 2026
aecee0c
debounce text only
hyoban Jan 9, 2026
a85c811
fix load next page
hyoban Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions web/app/(commonLayout)/plugins/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Marketplace from '@/app/components/plugins/marketplace'
import PluginPage from '@/app/components/plugins/plugin-page'
import PluginsPanel from '@/app/components/plugins/plugin-page/plugins-panel'

const PluginList = async () => {
const PluginList = () => {
return (
<PluginPage
plugins={<PluginsPanel />}
marketplace={<Marketplace pluginTypeSwitchClassName="top-[60px]" showSearchParams={false} />}
marketplace={<Marketplace pluginTypeSwitchClassName="top-[60px]" />}
/>
)
}
Expand Down
81 changes: 81 additions & 0 deletions web/app/components/plugins/marketplace/atoms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { ActivePluginType } from './constants'
import type { PluginsSort, SearchParamsFromCollection } from './types'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useQueryState } from 'nuqs'
import { useCallback } from 'react'
import { DEFAULT_SORT, PLUGIN_CATEGORY_WITH_COLLECTIONS } from './constants'
import { marketplaceSearchParamsParsers } from './search-params'

const marketplaceSortAtom = atom<PluginsSort>(DEFAULT_SORT)
export function useMarketplaceSort() {
return useAtom(marketplaceSortAtom)
}
export function useMarketplaceSortValue() {
return useAtomValue(marketplaceSortAtom)
}
export function useSetMarketplaceSort() {
return useSetAtom(marketplaceSortAtom)
}

/**
* Preserve the state for marketplace
*/
export const preserveSearchStateInQueryAtom = atom<boolean>(false)

const searchPluginTextAtom = atom<string>('')
const activePluginTypeAtom = atom<ActivePluginType>('all')
const filterPluginTagsAtom = atom<string[]>([])

export function useSearchPluginText() {
const preserveSearchStateInQuery = useAtomValue(preserveSearchStateInQueryAtom)
const queryState = useQueryState('q', marketplaceSearchParamsParsers.q)
const atomState = useAtom(searchPluginTextAtom)
return preserveSearchStateInQuery ? queryState : atomState
}
export function useActivePluginType() {
const preserveSearchStateInQuery = useAtomValue(preserveSearchStateInQueryAtom)
const queryState = useQueryState('category', marketplaceSearchParamsParsers.category)
const atomState = useAtom(activePluginTypeAtom)
return preserveSearchStateInQuery ? queryState : atomState
}
export function useFilterPluginTags() {
const preserveSearchStateInQuery = useAtomValue(preserveSearchStateInQueryAtom)
const queryState = useQueryState('tags', marketplaceSearchParamsParsers.tags)
const atomState = useAtom(filterPluginTagsAtom)
return preserveSearchStateInQuery ? queryState : atomState
}

/**
* Not all categories have collections, so we need to
* force the search mode for those categories.
*/
export const searchModeAtom = atom<true | null>(null)

export function useMarketplaceSearchMode() {
const [searchPluginText] = useSearchPluginText()
const [filterPluginTags] = useFilterPluginTags()
const [activePluginType] = useActivePluginType()

const searchMode = useAtomValue(searchModeAtom)
const isSearchMode = !!searchPluginText
|| filterPluginTags.length > 0
|| (searchMode ?? (!PLUGIN_CATEGORY_WITH_COLLECTIONS.has(activePluginType)))
return isSearchMode
}

export function useMarketplaceMoreClick() {
const [,setQ] = useSearchPluginText()
const setSort = useSetAtom(marketplaceSortAtom)
const setSearchMode = useSetAtom(searchModeAtom)

return useCallback((searchParams?: SearchParamsFromCollection) => {
if (!searchParams)
return
setQ(searchParams?.query || '')
setSort({
sortBy: searchParams?.sort_by || DEFAULT_SORT.sortBy,
sortOrder: searchParams?.sort_order || DEFAULT_SORT.sortOrder,
})
setSearchMode(true)
}, [setQ, setSort, setSearchMode])
}
24 changes: 24 additions & 0 deletions web/app/components/plugins/marketplace/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
import { PluginCategoryEnum } from '../types'

export const DEFAULT_SORT = {
sortBy: 'install_count',
sortOrder: 'DESC',
}

export const SCROLL_BOTTOM_THRESHOLD = 100

export const PLUGIN_TYPE_SEARCH_MAP = {
all: 'all',
model: PluginCategoryEnum.model,
tool: PluginCategoryEnum.tool,
agent: PluginCategoryEnum.agent,
extension: PluginCategoryEnum.extension,
datasource: PluginCategoryEnum.datasource,
trigger: PluginCategoryEnum.trigger,
bundle: 'bundle',
} as const

type ValueOf<T> = T[keyof T]

export type ActivePluginType = ValueOf<typeof PLUGIN_TYPE_SEARCH_MAP>

export const PLUGIN_CATEGORY_WITH_COLLECTIONS = new Set<ActivePluginType>(
[
PLUGIN_TYPE_SEARCH_MAP.all,
PLUGIN_TYPE_SEARCH_MAP.tool,
],
)
Loading
Loading