Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ See [docs/chromatic-setup.md](docs/chromatic-setup.md) for more details on our C
#### Update PR Branches

We provide a GitHub Actions workflow to automatically update open PR branches with the latest changes from `main`. This is useful for:

- Keeping long-running PRs up-to-date
- Reducing merge conflicts
- Repository maintenance
Expand Down
1 change: 1 addition & 0 deletions bun.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "comfy-org-registry-web",
Expand Down
33 changes: 18 additions & 15 deletions components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import React from 'react'
import { FaDiscord, FaGithub } from 'react-icons/fa'
import logoBluePng from '@/src/assets/images/logo_blue.png'
import { useNextTranslation } from '@/src/hooks/i18n'
import { themeConfig } from '@/utils/themeConfig'
import { useFromUrlParam } from '../common/HOC/useFromUrl'
import LanguageSwitcher from '../common/LanguageSwitcher'
import ThemeSwitcher from '../common/ThemeSwitcher'
import ProfileDropdown from './ProfileDropdown'

interface HeaderProps {
Expand All @@ -32,9 +34,8 @@ const Header: React.FC<HeaderProps> = ({ isLoggedIn, title }) => {
return (
<Navbar
fluid
className="mx-auto p-8"
className={`mx-auto p-8 ${themeConfig.header.background}`}
style={{
backgroundColor: 'rgb(17 24 39)',
paddingLeft: 0,
paddingRight: 0,
}}
Expand All @@ -47,11 +48,15 @@ const Header: React.FC<HeaderProps> = ({ isLoggedIn, title }) => {
height={36}
className="w-6 h-6 mr-3 sm:w-9 sm:h-9 rounded-lg"
/>
<span className="self-center text-xl font-semibold text-white whitespace-nowrap">
<span
className={`self-center text-xl font-semibold whitespace-nowrap ${themeConfig.header.text}`}
>
{t('Comfy Registry')}
</span>
</Link>
<div className="flex items-center gap-2 bg-gray-900 md:order-2 h-10">
<div
className={`flex items-center gap-2 md:order-2 h-10 ${themeConfig.header.background}`}
>
{isLoggedIn ? (
<div className="h-10 flex items-center">
<ProfileDropdown />
Expand All @@ -60,22 +65,20 @@ const Header: React.FC<HeaderProps> = ({ isLoggedIn, title }) => {
<>
<Button
onClick={handleLogIn}
color="dark"
size="xs"
className="h-10"
className={`${themeConfig.button.login} border-0 h-10`}
>
<span className="text-white text-xs md:text-base">
{t('Login')}
</span>
<span className="text-xs md:text-base">{t('Login')}</span>
</Button>

<Button
onClick={handleSignUp}
color="blue"
size="xs"
className="h-10"
className={`${themeConfig.button.signup} border-0 h-10`}
>
<span className="text-xs md:text-base">{t('Signup')}</span>
<span className="text-xs md:text-base text-white">
{t('Signup')}
</span>
</Button>
</>
)}
Expand All @@ -85,11 +88,10 @@ const Header: React.FC<HeaderProps> = ({ isLoggedIn, title }) => {
? 'https://docs.comfy.org/zh-CN'
: 'https://docs.comfy.org/registry/overview'
}
color="blue"
size="xs"
className="h-10"
className={`${themeConfig.button.documentation} border-0 h-10`}
>
<span className="text-white text-xs md:text-base">
<span className="text-xs md:text-base text-white">
{t('Documentation')}
</span>
</Button>
Expand All @@ -104,6 +106,7 @@ const Header: React.FC<HeaderProps> = ({ isLoggedIn, title }) => {
/>
</div>

<ThemeSwitcher />
{/* place in the most-right to reduce ... when switching language */}
<div className="h-10 flex items-center">
<LanguageSwitcher />
Expand Down
13 changes: 13 additions & 0 deletions components/Header/ProfileDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HiChevronDown } from 'react-icons/hi'
import { useGetUser } from '@/src/api/generated'
import { useNextTranslation } from '@/src/hooks/i18n'
import { useFirebaseUser } from '@/src/hooks/useFirebaseUser'
import { themeConfig } from '@/utils/themeConfig'
import { useLogout } from '../AuthUI/Logout'

export default function ProfileDropdown() {
Expand All @@ -13,6 +14,17 @@ export default function ProfileDropdown() {
const [onSignOut, isSignoutLoading, error] = useLogout()
const { data: user } = useGetUser()

// Custom theme for dropdown to match our theme configuration
const customDropdownTheme = {
floating: {
base: `z-10 w-fit rounded divide-y divide-gray-100 shadow focus:outline-none ${themeConfig.dropdown.background} ${themeConfig.dropdown.border}`,
content: 'py-1 text-sm',
item: {
base: `flex items-center justify-start py-2 px-4 text-sm cursor-pointer w-full ${themeConfig.dropdown.item}`,
},
},
}

// // debug
// return <>{JSON.stringify(useFirebaseUser(), null, 2)}</>
const [firebaseUser] = useFirebaseUser()
Expand All @@ -22,6 +34,7 @@ export default function ProfileDropdown() {
<Dropdown
arrowIcon={false}
inline
theme={customDropdownTheme}
label={
<div className="flex items-center gap-2 h-10">
<Avatar
Expand Down
10 changes: 5 additions & 5 deletions components/Search/SearchHit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@ const Hit: React.FC<HitProps> = ({ hit }) => {
)?.filter((e) => (e.matchedWords as string[])?.length)
return (
<Link
className="flex flex-col bg-gray-800 rounded-lg h-full dark:border-gray-700 p-4 shadow-md"
className="flex flex-col bg-gray-100 dark:bg-gray-800 rounded-lg h-full border border-gray-300 dark:border-gray-700 p-4 shadow-md"
href={`/nodes/${hit.id}`}
rel="noopener noreferrer"
>
<div className="flex flex-col flex-grow">
<h6 className="mb-2 text-base font-bold tracking-tight text-white break-words">
<h6 className="mb-2 text-base font-bold tracking-tight text-black dark:text-white break-words">
<Snippet hit={hit} attribute="name" />
</h6>

{/* description */}
<Markdown
components={{
p: ({ children }) => (
<p className="mb-1 text-xs font-light text-white mt-2 overflow-hidden text-ellipsis line-clamp-2">
<p className="mb-1 text-xs font-light text-black dark:text-white mt-2 overflow-hidden text-ellipsis line-clamp-2">
{children}
</p>
),
Expand All @@ -64,7 +64,7 @@ const Hit: React.FC<HitProps> = ({ hit }) => {

{/* nodes */}
{hit.comfy_nodes?.length && (
<div className="mb-1 text-xs font-light text-white mt-2 flex-row flex gap-2 whitespace-nowrap overflow-hidden overflow-ellipsis">
<div className="mb-1 text-xs font-light text-black dark:text-white mt-2 flex-row flex gap-2 whitespace-nowrap overflow-hidden overflow-ellipsis">
<Tooltip
content={
<pre className="max-w-[20em] max-h-[8em] overflow-auto whitespace-pre-wrap break-all">
Expand Down Expand Up @@ -95,7 +95,7 @@ const Hit: React.FC<HitProps> = ({ hit }) => {
</div>
{/* meta info */}
<p
className="text-xs font-light text-nowrap mt-2 text-gray-400"
className="text-xs font-light text-nowrap mt-2 text-gray-600 dark:text-gray-400"
dir="ltr"
>
<PublisherId publisherId={hit.publisher_id} />
Expand Down
4 changes: 2 additions & 2 deletions components/common/GenericHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ const GenericHeader: React.FC<Props> = ({
}) => {
return (
<>
<h1 className="text-xl font-bold leading-tight tracking-tight text-white sm:text-4xl">
<h1 className="text-xl font-bold leading-tight tracking-tight text-black dark:text-white sm:text-4xl">
{title}
</h1>
<p className="pt-2 pb-4 font-light text-gray-500 text-lg dark:text-gray-400">
<p className="pt-2 pb-4 font-light text-gray-700 text-lg dark:text-gray-400">
{subTitle}
</p>
<Link href={buttonLink}>
Expand Down
92 changes: 92 additions & 0 deletions components/common/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Dropdown, DropdownItem } from 'flowbite-react'
import React from 'react'
import { HiDesktopComputer, HiMoon, HiSun } from 'react-icons/hi'
import { useNextTranslation } from '@/src/hooks/i18n'
import { Theme, useTheme } from '@/src/hooks/useTheme'

const ThemeIcon = ({
theme,
actualTheme,
}: {
theme: Theme
actualTheme: 'light' | 'dark'
}) => {
const icons = {
auto: <HiDesktopComputer className="w-4 h-4" />,
light: <HiSun className="w-4 h-4" />,
dark: <HiMoon className="w-4 h-4" />,
} satisfies Record<Theme, React.ReactNode>

if (theme in icons) {
return icons[theme]
}
throw new Error(`Unknown theme: ${theme}`)
}

export default function ThemeSwitcher() {
const { theme, actualTheme, setTheme } = useTheme()
const { t } = useNextTranslation()

const themeOptions: {
value: Theme
label: string
icon: React.ReactNode
}[] = [
{
value: 'auto',
label: t('Auto'),
icon: <HiDesktopComputer className="w-4 h-4" />,
},
{
value: 'light',
label: t('Light'),
icon: <HiSun className="w-4 h-4" />,
},
{
value: 'dark',
label: t('Dark'),
icon: <HiMoon className="w-4 h-4" />,
},
]

const currentOption = themeOptions.find((option) => option.value === theme)

// Handle double-click to toggle between light and dark themes
const handleDoubleClick = () => {
if (actualTheme === 'light') {
setTheme('dark')
} else {
setTheme('light')
}
}

return (
<Dropdown
label=""
renderTrigger={() => (
<button
type="button"
className="inline-flex items-center justify-center p-2 w-8 h-8 text-sm text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:bg-gray-800 dark:border-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
aria-label="Toggle theme"
onDoubleClick={handleDoubleClick}
>
<ThemeIcon theme={theme} actualTheme={actualTheme} />
</button>
)}
color="gray"
size="xs"
dismissOnClick
>
{themeOptions.map((option) => (
<DropdownItem
key={option.value}
onClick={() => setTheme(option.value)}
className={`flex items-center gap-2 ${theme === option.value ? 'font-bold' : ''}`}
>
{option.icon}
{option.label}
</DropdownItem>
))}
</Dropdown>
)
}
92 changes: 92 additions & 0 deletions components/common/UnifiedBreadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Breadcrumb } from 'flowbite-react'
import { useRouter } from 'next/router'
import React, { FC, SVGProps } from 'react'
import { HiHome } from 'react-icons/hi'
import { themeConfig } from '@/utils/themeConfig'

interface BreadcrumbItem {
label: string
href?: string
onClick?: (e: React.MouseEvent) => void
icon?: FC<SVGProps<SVGSVGElement>>
isActive?: boolean
}

interface UnifiedBreadcrumbProps {
items: BreadcrumbItem[]
className?: string
}

const UnifiedBreadcrumb: React.FC<UnifiedBreadcrumbProps> = ({
items,
className = '',
}) => {
const router = useRouter()

return (
<Breadcrumb className={`py-4 ${className}`}>
{items.map((item, index) => (
<Breadcrumb.Item
key={index}
href={item.href}
icon={item.icon}
onClick={item.onClick}
className={`${themeConfig.breadcrumb.link} ${
item.isActive ? 'text-blue-500 dark:text-blue-400' : ''
}`}
>
{item.label}
</Breadcrumb.Item>
))}
</Breadcrumb>
)
}

export { UnifiedBreadcrumb }
export default UnifiedBreadcrumb

export const createHomeBreadcrumb = (t: (key: string) => string) => ({
label: t('Home'),
href: '/',
icon: HiHome,
onClick: (e: React.MouseEvent) => {
e.preventDefault()
window.location.href = '/'
},
})

export const createNodesBreadcrumb = (t: (key: string) => string) => ({
label: t('Your Nodes'),
})

export const createAllNodesBreadcrumb = (t: (key: string) => string) => ({
label: t('All Nodes'),
})

export const createNodeDetailBreadcrumb = (nodeId: string) => ({
label: nodeId,
isActive: true,
})

export const createPublisherBreadcrumb = (publisherName: string) => ({
label: publisherName,
isActive: true,
})

export const createAdminDashboardBreadcrumb = (
t: (key: string) => string,
isCurrentPage = false
) => ({
label: t('Admin Dashboard'),
href: isCurrentPage ? undefined : '/admin',
onClick: isCurrentPage
? undefined
: (e: React.MouseEvent) => {
e.preventDefault()
window.location.href = '/admin'
},
})

export const createUnclaimedNodesBreadcrumb = (t: (key: string) => string) => ({
label: t('Unclaimed Nodes'),
})
Loading