feat: sync deployed v1.0.0-rc.38-custom.42
Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
1 parent
aa6c3f50f4
commit
ceddfd3d6b
828 files changed
+122253
-13702
No files matched your search
@@ -21,11 +21,12 @@ import { LanguageSwitcher } from '@/components/language-switcher'
|
||||
import { NotificationPopover } from '@/components/notification-popover'
|
||||
import { ProfileDropdown } from '@/components/profile-dropdown'
|
||||
import { Search } from '@/components/search'
|
||||
import { SystemUpdateAction } from '@/features/system-update/system-update-action'
|
||||
import { useNotifications } from '@/hooks/use-notifications'
|
||||
import { useTopNavLinks } from '@/hooks/use-top-nav-links'
|
||||
|
||||
import { defaultTopNavLinks } from '../config/top-nav.config'
|
||||
import { type TopNavLink } from '../types'
|
||||
import type { TopNavLink } from '../types'
|
||||
import { Header } from './header'
|
||||
import { SystemBrand } from './system-brand'
|
||||
import { TopNav } from './top-nav'
|
||||
@@ -111,40 +112,43 @@ export function AppHeader({
|
||||
const notifications = useNotifications()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header>
|
||||
<Header>
|
||||
<div className='@container/system-brand flex min-w-0 flex-1 items-center gap-1'>
|
||||
<SystemBrand variant='inline' />
|
||||
<SystemUpdateAction presentation='version' />
|
||||
</div>
|
||||
|
||||
{leftContent ? (
|
||||
<div className='ms-2 flex items-center'>{leftContent}</div>
|
||||
) : null}
|
||||
{leftContent ? (
|
||||
<div className='ms-2 flex items-center'>{leftContent}</div>
|
||||
) : null}
|
||||
|
||||
{rightContent ?? (
|
||||
<div className='ms-auto flex items-center gap-1 sm:gap-2'>
|
||||
{showTopNav && (
|
||||
<div className='me-1 hidden lg:block'>
|
||||
<TopNav links={links} />
|
||||
</div>
|
||||
)}
|
||||
{showSearch && <Search />}
|
||||
{showNotifications && (
|
||||
<NotificationPopover
|
||||
open={notifications.popoverOpen}
|
||||
onOpenChange={notifications.setPopoverOpen}
|
||||
unreadCount={notifications.unreadCount}
|
||||
activeTab={notifications.activeTab}
|
||||
onTabChange={notifications.setActiveTab}
|
||||
notice={notifications.notice}
|
||||
announcements={notifications.announcements}
|
||||
loading={notifications.loading}
|
||||
/>
|
||||
)}
|
||||
<LanguageSwitcher />
|
||||
{showConfigDrawer && <ConfigDrawer />}
|
||||
{showProfileDropdown && <ProfileDropdown />}
|
||||
</div>
|
||||
)}
|
||||
</Header>
|
||||
</>
|
||||
{rightContent ?? (
|
||||
<div className='ms-auto flex shrink-0 items-center gap-1 sm:gap-2'>
|
||||
{showTopNav && (
|
||||
<div className='me-1 hidden lg:block'>
|
||||
<TopNav links={links} />
|
||||
</div>
|
||||
)}
|
||||
{showSearch && (
|
||||
<Search className='w-8 flex-none [&>span]:hidden sm:[&>span]:inline' />
|
||||
)}
|
||||
{showNotifications && (
|
||||
<NotificationPopover
|
||||
open={notifications.popoverOpen}
|
||||
onOpenChange={notifications.setPopoverOpen}
|
||||
unreadCount={notifications.unreadCount}
|
||||
activeTab={notifications.activeTab}
|
||||
onTabChange={notifications.setActiveTab}
|
||||
notice={notifications.notice}
|
||||
announcements={notifications.announcements}
|
||||
loading={notifications.loading}
|
||||
/>
|
||||
)}
|
||||
<LanguageSwitcher />
|
||||
{showConfigDrawer && <ConfigDrawer />}
|
||||
{showProfileDropdown && <ProfileDropdown />}
|
||||
</div>
|
||||
)}
|
||||
</Header>
|
||||
)
|
||||
}
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
resolveChatUrl,
|
||||
type ChatPreset,
|
||||
} from '@/features/chat/lib/chat-links'
|
||||
import { handleServerError } from '@/lib/handle-server-error'
|
||||
|
||||
import { normalizeHref } from '../lib/url-utils'
|
||||
import type { NavChatPresets } from '../types'
|
||||
@@ -194,7 +195,7 @@ export function ChatPresetsItem({ item }: { item: NavChatPresets }) {
|
||||
: t(
|
||||
'Unable to prepare chat link. Please ensure you have an enabled API key.'
|
||||
)
|
||||
toast.error(message)
|
||||
handleServerError(error, message)
|
||||
return
|
||||
} finally {
|
||||
loadingPresetIdRef.current = null
|
||||
|
||||
@@ -149,6 +149,22 @@ function ProjectAttribution(props: { currentYear: number; inline?: boolean }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ServiceSupportAttribution() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<span className='text-muted-foreground/60 text-xs'>
|
||||
<a
|
||||
href='https://www.crash.work/'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-foreground/70 hover:text-foreground font-medium transition-colors'
|
||||
>
|
||||
{t('Service support provided by Crash Workshop Cloud Computing')}
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function Footer(props: FooterProps) {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
@@ -238,6 +254,7 @@ export function Footer(props: FooterProps) {
|
||||
/>
|
||||
<div className='border-border/60 text-muted-foreground/45 flex w-full flex-wrap items-center justify-center gap-x-3 gap-y-1 border-t pt-4 text-xs sm:w-auto sm:justify-end sm:border-t-0 sm:border-l sm:pt-0 sm:pl-5'>
|
||||
<LegalLinks />
|
||||
<ServiceSupportAttribution />
|
||||
<ProjectAttribution currentYear={currentYear} inline />
|
||||
</div>
|
||||
</div>
|
||||
@@ -272,14 +289,14 @@ export function Footer(props: FooterProps) {
|
||||
{/* Links columns */}
|
||||
{isDemoSiteMode && (
|
||||
<div className='grid grid-cols-3 gap-8 md:gap-16'>
|
||||
{displayColumns.map((column, index) => (
|
||||
<div key={index}>
|
||||
{displayColumns.map((column) => (
|
||||
<div key={column.title}>
|
||||
<p className='text-muted-foreground/50 mb-3 text-xs font-medium tracking-wider uppercase'>
|
||||
{t(column.title)}
|
||||
</p>
|
||||
<ul className='space-y-2.5'>
|
||||
{column.links.map((link, linkIndex) => (
|
||||
<li key={linkIndex}>
|
||||
{column.links.map((link) => (
|
||||
<li key={`${link.text}:${link.href}`}>
|
||||
<FooterLinkItem link={link} />
|
||||
</li>
|
||||
))}
|
||||
@@ -301,6 +318,7 @@ export function Footer(props: FooterProps) {
|
||||
<LegalLinks leadingSeparator />
|
||||
</div>
|
||||
<ProjectAttribution currentYear={currentYear} />
|
||||
<ServiceSupportAttribution />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { Link, useNavigate, useRouterState } from '@tanstack/react-router'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState, type ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Dialog } from '@/components/dialog'
|
||||
@@ -27,6 +27,7 @@ import { ProfileDropdown } from '@/components/profile-dropdown'
|
||||
import { ThemeSwitch } from '@/components/theme-switch'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { SystemUpdateAction } from '@/features/system-update/system-update-action'
|
||||
import { useNotifications } from '@/hooks/use-notifications'
|
||||
import { useSystemConfig } from '@/hooks/use-system-config'
|
||||
import { useTopNavLinks } from '@/hooks/use-top-nav-links'
|
||||
@@ -98,6 +99,29 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
const displaySiteName = customSiteName || systemName
|
||||
const links = dynamicLinks.length > 0 ? dynamicLinks : navLinks
|
||||
|
||||
let logoContent: ReactNode = (
|
||||
<HeaderLogo
|
||||
src={systemLogo}
|
||||
loading={loading}
|
||||
logoLoaded={logoLoaded}
|
||||
className='size-full rounded-lg object-contain'
|
||||
/>
|
||||
)
|
||||
if (customLogo) logoContent = customLogo
|
||||
if (loading) logoContent = <Skeleton className='size-full rounded-lg' />
|
||||
|
||||
let authContent = (
|
||||
<Button
|
||||
size='sm'
|
||||
className='h-8 rounded-lg px-3.5 text-xs font-medium'
|
||||
render={<Link to='/sign-in' />}
|
||||
>
|
||||
{t('Sign in')}
|
||||
</Button>
|
||||
)
|
||||
if (isAuthenticated) authContent = <ProfileDropdown />
|
||||
if (loading) authContent = <Skeleton className='h-8 w-20 rounded-lg' />
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 20)
|
||||
onScroll()
|
||||
@@ -184,52 +208,52 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
>
|
||||
<nav
|
||||
className={cn(
|
||||
'flex items-center justify-between transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]',
|
||||
'flex items-center justify-between gap-2 transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]',
|
||||
scrolled
|
||||
? 'bg-background/60 ring-border/50 h-12 rounded-2xl pr-1.5 pl-4 shadow-[0_2px_16px_-6px_rgba(0,0,0,0.08),0_0_0_0.5px_rgba(0,0,0,0.02)] ring-[0.5px] backdrop-blur-2xl dark:shadow-[0_2px_16px_-6px_rgba(0,0,0,0.4)]'
|
||||
: 'h-16 px-2'
|
||||
)}
|
||||
>
|
||||
{/* Logo */}
|
||||
<Link
|
||||
to={homeUrl}
|
||||
className='group flex shrink-0 items-center gap-2.5'
|
||||
>
|
||||
<div className='flex size-7 shrink-0 items-center justify-center transition-all duration-300 group-hover:scale-105'>
|
||||
{loading ? (
|
||||
<Skeleton className='size-full rounded-lg' />
|
||||
) : customLogo ? (
|
||||
customLogo
|
||||
) : (
|
||||
<HeaderLogo
|
||||
src={systemLogo}
|
||||
loading={loading}
|
||||
logoLoaded={logoLoaded}
|
||||
className='size-full rounded-lg object-contain'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<span className='text-sm font-semibold tracking-tight'>
|
||||
{loading ? <Skeleton className='h-4 w-16' /> : displaySiteName}
|
||||
</span>
|
||||
</Link>
|
||||
<div className='@container/system-brand flex min-w-0 flex-1 items-center gap-1 lg:min-w-36'>
|
||||
<Link
|
||||
to={homeUrl}
|
||||
className='group flex min-w-0 items-center gap-2.5'
|
||||
>
|
||||
<div className='flex size-7 shrink-0 items-center justify-center transition-all duration-300 group-hover:scale-105'>
|
||||
{logoContent}
|
||||
</div>
|
||||
<span
|
||||
className='max-w-48 truncate text-sm font-semibold tracking-tight'
|
||||
title={displaySiteName}
|
||||
>
|
||||
{loading ? (
|
||||
<Skeleton className='h-4 w-16' />
|
||||
) : (
|
||||
displaySiteName
|
||||
)}
|
||||
</span>
|
||||
</Link>
|
||||
<SystemUpdateAction presentation='version' />
|
||||
</div>
|
||||
|
||||
{/* Desktop nav */}
|
||||
<div className='hidden items-center gap-0.5 sm:flex'>
|
||||
{links.map((link, i) => {
|
||||
<div className='hidden min-w-0 items-center gap-0.5 lg:flex'>
|
||||
{links.map((link) => {
|
||||
const isActive = pathname === link.href
|
||||
if (link.external) {
|
||||
return (
|
||||
<a
|
||||
key={i}
|
||||
key={`${link.title}:${link.href}`}
|
||||
href={link.href}
|
||||
title={t(link.title)}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
aria-disabled={link.disabled}
|
||||
tabIndex={link.disabled ? -1 : undefined}
|
||||
onClick={(event) => handleNavLinkClick(event, link)}
|
||||
className={cn(
|
||||
'text-muted-foreground hover:text-foreground rounded-lg px-3 py-1.5 text-sm font-medium transition-colors duration-200',
|
||||
'text-muted-foreground hover:text-foreground min-w-0 truncate rounded-lg px-3 py-1.5 text-sm font-medium transition-colors duration-200',
|
||||
link.disabled && 'pointer-events-none opacity-50'
|
||||
)}
|
||||
>
|
||||
@@ -239,12 +263,13 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
key={i}
|
||||
key={`${link.title}:${link.href}`}
|
||||
to={link.href}
|
||||
title={t(link.title)}
|
||||
disabled={link.disabled}
|
||||
onClick={(event) => handleNavLinkClick(event, link)}
|
||||
className={cn(
|
||||
'rounded-lg px-3 py-1.5 text-sm font-medium transition-colors duration-200',
|
||||
'min-w-0 truncate rounded-lg px-3 py-1.5 text-sm font-medium transition-colors duration-200',
|
||||
isActive
|
||||
? 'text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground',
|
||||
@@ -280,25 +305,13 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
{showAuthButtons && (
|
||||
<>
|
||||
<div className='bg-border/40 mx-1 h-4 w-px' />
|
||||
{loading ? (
|
||||
<Skeleton className='h-8 w-20 rounded-lg' />
|
||||
) : isAuthenticated ? (
|
||||
<ProfileDropdown />
|
||||
) : (
|
||||
<Button
|
||||
size='sm'
|
||||
className='h-8 rounded-lg px-3.5 text-xs font-medium'
|
||||
render={<Link to='/sign-in' />}
|
||||
>
|
||||
{t('Sign in')}
|
||||
</Button>
|
||||
)}
|
||||
{authContent}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Mobile: compact actions + hamburger */}
|
||||
<div className='flex items-center gap-2 sm:hidden'>
|
||||
<div className='flex shrink-0 items-center gap-2 lg:hidden'>
|
||||
{showThemeSwitch && <ThemeSwitch />}
|
||||
{showAuthButtons && !loading && isAuthenticated && (
|
||||
<ProfileDropdown />
|
||||
@@ -340,7 +353,7 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
{/* Mobile full-screen overlay */}
|
||||
<div
|
||||
className={cn(
|
||||
'bg-background/98 fixed inset-0 z-40 backdrop-blur-2xl transition-all duration-500 ease-[cubic-bezier(0.16,1,0.3,1)] sm:pointer-events-none sm:hidden',
|
||||
'bg-background/98 fixed inset-0 z-40 backdrop-blur-2xl transition-all duration-500 ease-[cubic-bezier(0.16,1,0.3,1)] lg:pointer-events-none lg:hidden',
|
||||
mobileOpen
|
||||
? 'pointer-events-auto opacity-100'
|
||||
: 'pointer-events-none opacity-0'
|
||||
@@ -364,7 +377,7 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
if (link.external) {
|
||||
return (
|
||||
<a
|
||||
key={i}
|
||||
key={`${link.title}:${link.href}`}
|
||||
href={link.href}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
@@ -380,7 +393,7 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
key={i}
|
||||
key={`${link.title}:${link.href}`}
|
||||
to={link.href}
|
||||
disabled={link.disabled}
|
||||
onClick={(event) => handleNavLinkClick(event, link, true)}
|
||||
|
||||
@@ -61,11 +61,11 @@ export function SystemBrand(props: SystemBrandProps) {
|
||||
to='/'
|
||||
aria-label={t('Go to home')}
|
||||
className={cn(
|
||||
'text-foreground inline-flex h-7 items-center gap-1.5 rounded-md px-1.5 text-sm font-medium transition-colors outline-none select-none',
|
||||
'text-foreground inline-flex h-7 min-w-0 items-center gap-1.5 rounded-md px-1.5 text-sm font-medium transition-colors outline-none select-none',
|
||||
'hover:bg-accent focus-visible:ring-ring/40 focus-visible:ring-2'
|
||||
)}
|
||||
>
|
||||
<div className='flex size-5 items-center justify-center overflow-hidden rounded-md'>
|
||||
<div className='flex size-5 shrink-0 items-center justify-center overflow-hidden rounded-md'>
|
||||
<img
|
||||
src={logo}
|
||||
alt={t('Logo')}
|
||||
|
||||
@@ -16,9 +16,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { type TFunction } from 'i18next'
|
||||
import type { TFunction } from 'i18next'
|
||||
import {
|
||||
Box,
|
||||
Route,
|
||||
CreditCard,
|
||||
Layout,
|
||||
Settings,
|
||||
@@ -32,6 +33,7 @@ import { getBillingSectionNavItems } from '@/features/system-settings/billing/se
|
||||
import { getContentSectionNavItems } from '@/features/system-settings/content/section-registry.tsx'
|
||||
import { getModelsSectionNavItems } from '@/features/system-settings/models/section-registry.tsx'
|
||||
import { getOperationsSectionNavItems } from '@/features/system-settings/operations/section-registry.tsx'
|
||||
import { getPolicySectionNavItems } from '@/features/system-settings/request-policies/section-registry'
|
||||
import { getSecuritySectionNavItems } from '@/features/system-settings/security/section-registry.tsx'
|
||||
import { getSiteSectionNavItems } from '@/features/system-settings/site/section-registry.tsx'
|
||||
|
||||
@@ -66,10 +68,15 @@ function getSystemSettingsNavGroups(t: TFunction): NavGroup[] {
|
||||
items: getBillingSectionNavItems(t),
|
||||
},
|
||||
{
|
||||
title: t('Models & Routing'),
|
||||
title: t('Models'),
|
||||
icon: Box,
|
||||
items: getModelsSectionNavItems(t),
|
||||
},
|
||||
{
|
||||
title: t('Request policies'),
|
||||
icon: Route,
|
||||
items: getPolicySectionNavItems(t),
|
||||
},
|
||||
{
|
||||
title: t('Security & Limits'),
|
||||
icon: ShieldAlert,
|
||||
|
||||
Reference in new issue
Block a user