Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8248906
Sync default frontend template and GHCR build
Jun 22, 2026
98d32a0
Disable GHCR image attestations
Jun 22, 2026
3f36a2e
Move vendor filter above groups
Jun 23, 2026
6475eb0
Allow public subscription plan listing
Jun 23, 2026
3d661da
Add claude sonnet 5 to homepage pricing
Jul 1, 2026
e85b052
Fix user subscription management action
Jul 3, 2026
35d3edf
Update homepage headline and price precision
Jul 6, 2026
b29b326
Truncate homepage price decimals
Jul 6, 2026
a47774c
Sync upstream updates while preserving custom UI
Jul 9, 2026
f3cee0b
Merge upstream frontend features
Jul 9, 2026
859dc87
Restore custom frontend theme
Jul 9, 2026
9818317
Merge pull request #1 from Aunt-Lee/codex/sync-upstream-20260709
Aunt-Lee Jul 10, 2026
6968044
Fix GHCR build missing GitHub icon dependency
Jul 10, 2026
3d8c7ae
Update pricing page filters and display defaults
Jul 10, 2026
0a093b7
Use backend pricing on homepage
Jul 13, 2026
eab0434
Convert homepage official prices to CNY
Jul 16, 2026
41338ed
Show base URLs on API keys page
Jul 16, 2026
8bc18c8
Fix currency display for homepage and subscriptions
Jul 23, 2026
5ddb60f
Remove homepage pricing notice
Jul 23, 2026
363b7a4
Split homepage plans by quota reset type
Jul 23, 2026
5007898
Fix homepage language switching
Jul 24, 2026
570e317
Default interface language to English
Jul 25, 2026
b8ac2d8
fix frontend currency labels
Jul 27, 2026
dedda2b
Improve frontend pricing and readability
Jul 29, 2026
ad846cf
Refine public pricing navigation and ordering
Jul 29, 2026
43a5848
feat(web): localize configured content and refine homepage
Aug 4, 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
56 changes: 56 additions & 0 deletions .github/workflows/ghcr-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Publish GHCR image

on:
push:
branches:
- main
workflow_dispatch:

jobs:
build:
name: Build and push image
runs-on: ubuntu-latest

permissions:
contents: read
packages: write

steps:
- name: Check out
uses: actions/checkout@v4

- name: Normalize GHCR repository
run: echo "GHCR_REPOSITORY=${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ env.GHCR_REPOSITORY }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=sha,prefix=sha-

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: false
sbom: false
2 changes: 1 addition & 1 deletion router/api-router.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func SetApiRouter(router *gin.Engine) {
//apiRouter.GET("/midjourney", controller.GetMidjourney)
apiRouter.GET("/home_page_content", controller.GetHomePageContent)
apiRouter.GET("/pricing", middleware.HeaderNavModuleAuth("pricing"), controller.GetPricing)
apiRouter.GET("/subscription/plans", controller.GetSubscriptionPlans)
perfMetricsRoute := apiRouter.Group("/perf-metrics")
perfMetricsRoute.Use(middleware.HeaderNavModulePublicOrUserAuth("pricing"))
{
Expand Down Expand Up @@ -151,7 +152,6 @@ func SetApiRouter(router *gin.Engine) {
subscriptionRoute := apiRouter.Group("/subscription")
subscriptionRoute.Use(middleware.UserAuth())
{
subscriptionRoute.GET("/plans", controller.GetSubscriptionPlans)
subscriptionRoute.GET("/self", controller.GetSubscriptionSelf)
subscriptionRoute.PUT("/self/preference", controller.UpdateSubscriptionPreference)
subscriptionRoute.POST("/balance/pay", middleware.CriticalRateLimit(), controller.SubscriptionRequestBalancePay)
Expand Down
89 changes: 1 addition & 88 deletions web/default/src/components/config-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import { useTheme } from '@/context/theme-provider'
import {
type ContentLayout,
THEME_PRESETS,
type ThemeFont,
type ThemePreset,
type ThemeRadius,
type ThemeScale,
Expand Down Expand Up @@ -107,7 +106,6 @@ export function ConfigDrawer() {
<div className={sideDrawerFormClassName()}>
<ThemeConfig />
<PresetConfig />
<FontConfig />
<RadiusConfig />
<ScaleConfig />
<SidebarConfig />
Expand Down Expand Up @@ -306,90 +304,6 @@ function PresetConfig() {
)
}

/**
* Font options shown in the theme drawer.
*
* Each option renders a live "Aa" preview in the font it represents.
* `Auto` deliberately leaves `fontFamily` undefined so the preview inherits
* the currently active body font — that way the user sees what `Auto` will
* actually look like for the active preset (Anthropic → serif glyphs,
* everything else → sans glyphs) without us having to duplicate the
* preset-default mapping in the UI.
*/
const FONT_OPTIONS: {
value: ThemeFont
label: string
// CSS font-family applied to the "Aa" preview. `undefined` = inherit
// from the current theme (used by the `default` option).
preview?: string
}[] = [
{ value: 'default', label: 'Auto', preview: undefined },
{ value: 'sans', label: 'Sans', preview: 'var(--font-sans)' },
{ value: 'serif', label: 'Serif', preview: 'var(--font-serif)' },
]

function FontConfig() {
const { t } = useTranslation()
const { defaults, customization, setFont } = useThemeCustomization()
return (
<div>
<SectionTitle
title={t('Font')}
showReset={customization.font !== defaults.font}
onReset={() => setFont(defaults.font)}
/>
<Radio
value={customization.font}
onValueChange={(v) => setFont(v as ThemeFont)}
className='grid w-full grid-cols-3 gap-4'
aria-label={t('Select body font')}
>
{FONT_OPTIONS.map((option) => (
<Item
key={option.value}
value={option.value}
className='group flex flex-col items-stretch outline-none'
aria-label={
option.value === 'default' ? t('System default') : option.label
}
>
<div
className={cn(
'ring-border relative h-12 rounded-md ring-[1px] transition',
'group-data-checked:ring-primary group-data-checked:shadow-md',
'group-focus-visible:ring-2',
'group-hover:ring-primary/60'
)}
>
<CircleCheck
className={cn(
'fill-primary absolute top-0 right-0 z-10 size-5 translate-x-1/2 -translate-y-1/2 stroke-white',
'group-data-unchecked:hidden'
)}
aria-hidden='true'
/>
<span
aria-hidden='true'
className='text-foreground absolute inset-0 flex items-center justify-center text-lg leading-none font-medium'
style={
option.preview
? { fontFamily: option.preview }
: // `font: inherit` defers to the active theme so the
// "Auto" tile previews what the resolved font will be.
{ font: 'inherit', fontSize: '1.125rem' }
}
>
Aa
</span>
</div>
<div className='mt-1.5 text-center text-xs'>{option.label}</div>
</Item>
))}
</Radio>
</div>
)
}

const RADIUS_OPTIONS: {
value: ThemeRadius
label: string
Expand Down Expand Up @@ -492,7 +406,6 @@ function ScaleConfig() {
{ value: 'sm', label: t('Compact'), rows: 4, rowGap: '3px' },
{ value: 'default', label: t('Default'), rows: 3, rowGap: '6px' },
{ value: 'lg', label: t('Comfortable'), rows: 2, rowGap: '10px' },
{ value: 'xl', label: t('Super Large'), rows: 1, rowGap: '14px' },
]
return (
<div>
Expand All @@ -504,7 +417,7 @@ function ScaleConfig() {
<Radio
value={customization.scale}
onValueChange={(v) => setScale(v as ThemeScale)}
className='grid w-full grid-cols-4 gap-3'
className='grid w-full grid-cols-3 gap-4'
aria-label={t('Select interface density')}
>
{scaleOptions.map((option) => (
Expand Down
73 changes: 73 additions & 0 deletions web/default/src/components/content-language-select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
Copyright (C) 2023-2026 QuantumNous

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

For commercial licensing, please contact support@quantumnous.com
*/
import { useTranslation } from 'react-i18next'

import { FormLabel } from '@/components/ui/form'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import {
INTERFACE_LANGUAGE_OPTIONS,
type InterfaceLanguageCode,
} from '@/i18n/languages'

export type EditableContentLocale = 'default' | InterfaceLanguageCode

interface ContentLanguageSelectProps {
value: EditableContentLocale
onValueChange: (value: EditableContentLocale) => void
}

export function ContentLanguageSelect({
value,
onValueChange,
}: ContentLanguageSelectProps) {
const { t } = useTranslation()

return (
<div className='space-y-2'>
<FormLabel>{t('Translation')}</FormLabel>
<Select
value={value}
onValueChange={(nextValue) =>
onValueChange(nextValue as EditableContentLocale)
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
<SelectItem value='default'>{t('Default')}</SelectItem>
{INTERFACE_LANGUAGE_OPTIONS.map((language) => (
<SelectItem key={language.code} value={language.code}>
{language.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function CompactContent<TData>({ row }: { row: Row<TData> }) {
return (
<div key={cell.id} className='min-w-0 flex-1 overflow-hidden'>
{label && (
<div className='text-muted-foreground mb-0.5 text-[10px] leading-none select-none'>
<div className='text-muted-foreground mb-0.5 text-xs leading-none select-none'>
{label}
</div>
)}
Expand Down Expand Up @@ -180,7 +180,7 @@ function FallbackContent<TData>({ row }: { row: Row<TData> }) {
key={cell.id}
className='flex items-start justify-between gap-2 overflow-hidden'
>
<span className='text-muted-foreground shrink-0 text-[10px] font-medium select-none'>
<span className='text-muted-foreground shrink-0 text-xs font-medium select-none'>
{label}
</span>
<div className='flex min-w-0 flex-1 items-center justify-end overflow-hidden text-xs [&_:is([data-slot=badge-cell],[data-slot=provider-badge],[data-slot=status-badge])]:ml-0'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function AuthenticatedLayout(props: AuthenticatedLayoutProps) {
<SearchProvider>
<SidebarProvider defaultOpen={defaultOpen} className='flex-col'>
<SkipToMain />
<AppHeader />
<AppHeader showSearch={false} />
<div className='flex min-h-0 w-full flex-1'>
<AppSidebar />
<SidebarInset
Expand Down
12 changes: 6 additions & 6 deletions web/default/src/components/layout/components/nav-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ import {
} from '@/components/ui/sidebar'

import { checkIsActive } from '../lib/url-utils'
import {
type NavCollapsible,
type NavChatPresets,
type NavLink,
type NavGroup as NavGroupProps,
import type {
NavCollapsible,
NavChatPresets,
NavLink,
NavGroup as NavGroupProps,
} from '../types'
import { ChatPresetsItem } from './chat-presets-item'

Expand All @@ -66,7 +66,7 @@ export function NavGroup({ title, items }: NavGroupProps) {

return (
<SidebarGroup className='px-2 py-1'>
<SidebarGroupLabel className='text-muted-foreground/70 px-2 text-[11px] font-medium tracking-wider uppercase'>
<SidebarGroupLabel className='text-muted-foreground/70 px-2 text-xs font-medium tracking-wider uppercase'>
{title}
</SidebarGroupLabel>
<SidebarMenu>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export function PublicHeader(props: PublicHeaderProps) {
/>
)}
</div>
<span className='text-sm font-semibold tracking-tight'>
<span className='text-base font-semibold tracking-tight'>
{loading ? <Skeleton className='h-4 w-16' /> : displaySiteName}
</span>
</Link>
Expand All @@ -229,7 +229,7 @@ export function PublicHeader(props: PublicHeaderProps) {
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-[13px] font-medium transition-colors duration-200',
'text-muted-foreground hover:text-foreground rounded-lg px-3 py-1.5 text-sm font-medium transition-colors duration-200',
link.disabled && 'pointer-events-none opacity-50'
)}
>
Expand All @@ -244,7 +244,7 @@ export function PublicHeader(props: PublicHeaderProps) {
disabled={link.disabled}
onClick={(event) => handleNavLinkClick(event, link)}
className={cn(
'rounded-lg px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
'rounded-lg px-3 py-1.5 text-sm font-medium transition-colors duration-200',
isActive
? 'text-foreground'
: 'text-muted-foreground hover:text-foreground',
Expand Down Expand Up @@ -287,7 +287,7 @@ export function PublicHeader(props: PublicHeaderProps) {
) : (
<Button
size='sm'
className='h-8 rounded-lg px-3.5 text-xs font-medium'
className='h-8 rounded-lg px-3.5 text-sm font-medium'
render={<Link to='/sign-in' />}
>
{t('Sign in')}
Expand Down
2 changes: 1 addition & 1 deletion web/default/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const buttonVariants = cva(
default:
'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
xs: "h-6 gap-1 rounded-md px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-7 gap-1 rounded-md px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
sm: "h-7 gap-1 rounded-md px-2.5 text-sm in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
icon: 'size-8',
'icon-xs':
Expand Down
Loading
Loading