mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-03 04:43:01 +00:00
feat(web): add text size theme axis and font license attribution
- New data-theme-text axis (sm/default/lg/xl/2xl) that overrides only the --text-* type ramp, composing with density and presets; config drawer section with per-tier "Aa" previews, cookie persistence, i18n for all seven locales - Add one-line font licensing note under the font picker linking Public Sans, Lora, and JetBrains Mono sources and the SIL OFL 1.1 license text
This commit is contained in:
+113
@@ -61,6 +61,7 @@ import {
|
||||
type ThemePreset,
|
||||
type ThemeRadius,
|
||||
type ThemeScale,
|
||||
type ThemeTextSize,
|
||||
} from '@/lib/theme-customization'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
@@ -122,6 +123,7 @@ export function ConfigDrawer({
|
||||
<FontConfig />
|
||||
<RadiusConfig />
|
||||
<ScaleConfig />
|
||||
<TextSizeConfig />
|
||||
<BadgeSizeConfig />
|
||||
{showLayoutControls && (
|
||||
<>
|
||||
@@ -436,10 +438,52 @@ function FontConfig() {
|
||||
</Item>
|
||||
))}
|
||||
</Radio>
|
||||
<FontLicenseNote />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* One-line licensing attribution for the bundled webfonts. All three faces
|
||||
* ship under the SIL Open Font License 1.1, which permits commercial use;
|
||||
* each name links to its official source and the license text.
|
||||
*/
|
||||
function FontLicenseNote() {
|
||||
const { t } = useTranslation()
|
||||
const fontSources: { name: string; href: string }[] = [
|
||||
{ name: 'Public Sans', href: 'https://github.com/uswds/public-sans' },
|
||||
{ name: 'Lora', href: 'https://fonts.google.com/specimen/Lora' },
|
||||
{ name: 'JetBrains Mono', href: 'https://www.jetbrains.com/lp/mono/' },
|
||||
]
|
||||
return (
|
||||
<p className='text-subtle-foreground mt-2 text-xs leading-relaxed'>
|
||||
{fontSources.map((font, index) => (
|
||||
<span key={font.name}>
|
||||
{index > 0 && ' · '}
|
||||
<a
|
||||
href={font.href}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className='hover:text-foreground hover:underline'
|
||||
>
|
||||
{font.name}
|
||||
</a>
|
||||
</span>
|
||||
))}
|
||||
{' — '}
|
||||
<a
|
||||
href='https://openfontlicense.org'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className='hover:text-foreground hover:underline'
|
||||
>
|
||||
SIL OFL 1.1
|
||||
</a>
|
||||
{`, ${t('free for commercial use')}`}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
const RADIUS_OPTIONS: {
|
||||
value: ThemeRadius
|
||||
label: string
|
||||
@@ -593,6 +637,75 @@ function ScaleConfig() {
|
||||
)
|
||||
}
|
||||
|
||||
function TextSizeConfig() {
|
||||
const { t } = useTranslation()
|
||||
const { defaults, customization, setTextSize } = useThemeCustomization()
|
||||
// Preview font sizes mirror each tier's `--text-base` so the tiles show
|
||||
// the actual relative difference between tiers.
|
||||
const textSizeOptions: {
|
||||
value: ThemeTextSize
|
||||
label: string
|
||||
previewSize: string
|
||||
}[] = [
|
||||
{ value: 'sm', label: t('Small'), previewSize: '0.88rem' },
|
||||
{ value: 'default', label: t('Default'), previewSize: '1rem' },
|
||||
{ value: 'lg', label: t('Large'), previewSize: '1.075rem' },
|
||||
{ value: 'xl', label: t('Extra Large'), previewSize: '1.125rem' },
|
||||
{ value: '2xl', label: t('Super Large'), previewSize: '1.21rem' },
|
||||
]
|
||||
return (
|
||||
<div>
|
||||
<SectionTitle
|
||||
title={t('Text size')}
|
||||
showReset={customization.textSize !== defaults.textSize}
|
||||
onReset={() => setTextSize(defaults.textSize)}
|
||||
/>
|
||||
<Radio
|
||||
value={customization.textSize}
|
||||
onValueChange={(v) => setTextSize(v as ThemeTextSize)}
|
||||
className='grid w-full grid-cols-5 gap-2'
|
||||
aria-label={t('Select text size')}
|
||||
>
|
||||
{textSizeOptions.map((option) => (
|
||||
<Item
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
className='group flex flex-col items-stretch outline-none'
|
||||
aria-label={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 leading-none font-medium'
|
||||
style={{ fontSize: option.previewSize }}
|
||||
>
|
||||
Aa
|
||||
</span>
|
||||
</div>
|
||||
<div className='mt-1.5 truncate text-center text-xs'>
|
||||
{option.label}
|
||||
</div>
|
||||
</Item>
|
||||
))}
|
||||
</Radio>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock pill rendered inside the badge-size preview tiles. Each option shows
|
||||
* the pill at the proportions that size will actually produce.
|
||||
|
||||
+49
-2
@@ -37,12 +37,14 @@ import {
|
||||
THEME_PRESET_VALUES,
|
||||
THEME_RADIUS_VALUES,
|
||||
THEME_SCALE_VALUES,
|
||||
THEME_TEXT_SIZE_VALUES,
|
||||
type ThemeBadgeSize,
|
||||
type ThemeCustomization,
|
||||
type ThemeFont,
|
||||
type ThemePreset,
|
||||
type ThemeRadius,
|
||||
type ThemeScale,
|
||||
type ThemeTextSize,
|
||||
} from '@/lib/theme-customization'
|
||||
|
||||
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365 // 1 year
|
||||
@@ -74,6 +76,7 @@ type ThemeCustomizationContextType = {
|
||||
setFont: (font: ThemeFont) => void
|
||||
setRadius: (radius: ThemeRadius) => void
|
||||
setScale: (scale: ThemeScale) => void
|
||||
setTextSize: (textSize: ThemeTextSize) => void
|
||||
setBadgeSize: (badgeSize: ThemeBadgeSize) => void
|
||||
setContentLayout: (contentLayout: ContentLayout) => void
|
||||
resetCustomization: () => void
|
||||
@@ -90,6 +93,7 @@ const FALLBACK_CONTEXT: ThemeCustomizationContextType = {
|
||||
setFont: () => {},
|
||||
setRadius: () => {},
|
||||
setScale: () => {},
|
||||
setTextSize: () => {},
|
||||
setBadgeSize: () => {},
|
||||
setContentLayout: () => {},
|
||||
resetCustomization: () => {},
|
||||
@@ -129,6 +133,13 @@ export function ThemeCustomizationProvider(props: {
|
||||
DEFAULT_THEME_CUSTOMIZATION.scale
|
||||
)
|
||||
)
|
||||
const [textSize, _setTextSize] = useState<ThemeTextSize>(() =>
|
||||
readCookie<ThemeTextSize>(
|
||||
THEME_COOKIE_KEYS.textSize,
|
||||
THEME_TEXT_SIZE_VALUES,
|
||||
DEFAULT_THEME_CUSTOMIZATION.textSize
|
||||
)
|
||||
)
|
||||
const [badgeSize, _setBadgeSize] = useState<ThemeBadgeSize>(() =>
|
||||
readCookie<ThemeBadgeSize>(
|
||||
THEME_COOKIE_KEYS.badgeSize,
|
||||
@@ -179,6 +190,13 @@ export function ThemeCustomizationProvider(props: {
|
||||
)
|
||||
}, [scale])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
applyAttribute(
|
||||
'data-theme-text',
|
||||
textSize === DEFAULT_THEME_CUSTOMIZATION.textSize ? null : textSize
|
||||
)
|
||||
}, [textSize])
|
||||
|
||||
// Unlike the other axes, the *site default* for badge size is `lg`, which
|
||||
// is a styled value — so the attribute is keyed on the literal unstyled
|
||||
// `default` tier rather than DEFAULT_THEME_CUSTOMIZATION (removing the
|
||||
@@ -230,6 +248,15 @@ export function ThemeCustomizationProvider(props: {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setTextSize = useCallback((value: ThemeTextSize) => {
|
||||
_setTextSize(value)
|
||||
if (value === DEFAULT_THEME_CUSTOMIZATION.textSize) {
|
||||
removeCookie(THEME_COOKIE_KEYS.textSize)
|
||||
} else {
|
||||
setCookie(THEME_COOKIE_KEYS.textSize, value, COOKIE_MAX_AGE)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setBadgeSize = useCallback((value: ThemeBadgeSize) => {
|
||||
_setBadgeSize(value)
|
||||
if (value === DEFAULT_THEME_CUSTOMIZATION.badgeSize) {
|
||||
@@ -253,18 +280,36 @@ export function ThemeCustomizationProvider(props: {
|
||||
setFont(DEFAULT_THEME_CUSTOMIZATION.font)
|
||||
setRadius(DEFAULT_THEME_CUSTOMIZATION.radius)
|
||||
setScale(DEFAULT_THEME_CUSTOMIZATION.scale)
|
||||
setTextSize(DEFAULT_THEME_CUSTOMIZATION.textSize)
|
||||
setBadgeSize(DEFAULT_THEME_CUSTOMIZATION.badgeSize)
|
||||
setContentLayout(DEFAULT_THEME_CUSTOMIZATION.contentLayout)
|
||||
}, [setPreset, setFont, setRadius, setScale, setBadgeSize, setContentLayout])
|
||||
}, [
|
||||
setPreset,
|
||||
setFont,
|
||||
setRadius,
|
||||
setScale,
|
||||
setTextSize,
|
||||
setBadgeSize,
|
||||
setContentLayout,
|
||||
])
|
||||
|
||||
const value = useMemo<ThemeCustomizationContextType>(
|
||||
() => ({
|
||||
defaults: DEFAULT_THEME_CUSTOMIZATION,
|
||||
customization: { preset, font, radius, scale, badgeSize, contentLayout },
|
||||
customization: {
|
||||
preset,
|
||||
font,
|
||||
radius,
|
||||
scale,
|
||||
textSize,
|
||||
badgeSize,
|
||||
contentLayout,
|
||||
},
|
||||
setPreset,
|
||||
setFont,
|
||||
setRadius,
|
||||
setScale,
|
||||
setTextSize,
|
||||
setBadgeSize,
|
||||
setContentLayout,
|
||||
resetCustomization,
|
||||
@@ -274,12 +319,14 @@ export function ThemeCustomizationProvider(props: {
|
||||
font,
|
||||
radius,
|
||||
scale,
|
||||
textSize,
|
||||
badgeSize,
|
||||
contentLayout,
|
||||
setPreset,
|
||||
setFont,
|
||||
setRadius,
|
||||
setScale,
|
||||
setTextSize,
|
||||
setBadgeSize,
|
||||
setContentLayout,
|
||||
resetCustomization,
|
||||
|
||||
Vendored
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "External operations mode",
|
||||
"External Speed Test": "External Speed Test",
|
||||
"Extra": "Extra",
|
||||
"Extra Large": "Extra Large",
|
||||
"Extra Notes (Optional)": "Extra Notes (Optional)",
|
||||
"Extra visible": "Extra visible",
|
||||
"Extra visible to {{group}}": "Extra visible to {{group}}",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "Forward requests directly to upstream providers without any post-processing.",
|
||||
"Frames per second": "Frames per second",
|
||||
"Free": "Free",
|
||||
"free for commercial use": "free for commercial use",
|
||||
"Free: {{free}} / Total: {{total}}": "Free: {{free}} / Total: {{total}}",
|
||||
"Frequency Penalty": "Frequency Penalty",
|
||||
"Friendly name to identify this channel": "Friendly name to identify this channel",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "Language preference saved",
|
||||
"Language Preferences": "Language Preferences",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "Language preferences sync across your signed-in devices and affect API error messages.",
|
||||
"Large": "Large",
|
||||
"Last 24h usage": "Last 24h usage",
|
||||
"Last 30 days uptime": "Last 30 days uptime",
|
||||
"Last check time": "Last check time",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "Select sync channels to compare prices",
|
||||
"Select sync channels to compare ratios": "Select sync channels to compare ratios",
|
||||
"Select Sync Source": "Select Sync Source",
|
||||
"Select text size": "Select text size",
|
||||
"Select the API endpoint region": "Select the API endpoint region",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.",
|
||||
"Select theme preference": "Select theme preference",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "Slug can only contain letters, numbers, hyphens, and underscores",
|
||||
"Slug is required": "Slug is required",
|
||||
"Slug must be less than 100 characters": "Slug must be less than 100 characters",
|
||||
"Small": "Small",
|
||||
"Smallest USD amount users can recharge (Epay)": "Smallest USD amount users can recharge (Epay)",
|
||||
"SMTP Email": "SMTP Email",
|
||||
"SMTP encryption": "SMTP encryption",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "Text Input",
|
||||
"Text or array of texts to embed": "Text or array of texts to embed",
|
||||
"Text Output": "Text Output",
|
||||
"Text size": "Text size",
|
||||
"Text to Video": "Text to Video",
|
||||
"Text tokens": "Text tokens",
|
||||
"The admin configured three groups and one special ratio rule:": "The admin configured three groups and one special ratio rule:",
|
||||
|
||||
Vendored
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "Mode opérations externes",
|
||||
"External Speed Test": "Test de vitesse externe",
|
||||
"Extra": "Supplémentaire",
|
||||
"Extra Large": "Extra grand",
|
||||
"Extra Notes (Optional)": "Notes supplémentaires (facultatif)",
|
||||
"Extra visible": "Visible en plus",
|
||||
"Extra visible to {{group}}": "Visible en plus pour {{group}}",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "Transférer les requêtes directement aux fournisseurs amont sans aucun post-traitement.",
|
||||
"Frames per second": "Images par seconde",
|
||||
"Free": "Libre",
|
||||
"free for commercial use": "usage commercial autorisé",
|
||||
"Free: {{free}} / Total: {{total}}": "Disponible : {{free}} / Total : {{total}}",
|
||||
"Frequency Penalty": "Pénalité de fréquence",
|
||||
"Friendly name to identify this channel": "Nom convivial pour identifier ce canal",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "Préférence de langue enregistrée",
|
||||
"Language Preferences": "Préférences de langue",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "Les préférences de langue se synchronisent sur vos appareils connectés et affectent les messages d'erreur de l'API.",
|
||||
"Large": "Grand",
|
||||
"Last 24h usage": "Utilisation 24h",
|
||||
"Last 30 days uptime": "Disponibilité 30 derniers jours",
|
||||
"Last check time": "Dernière vérification",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "Sélectionner les canaux de synchronisation pour comparer les prix",
|
||||
"Select sync channels to compare ratios": "Sélectionner les canaux de synchronisation pour comparer les ratios",
|
||||
"Select Sync Source": "Sélectionner la source de synchronisation",
|
||||
"Select text size": "Sélectionner la taille du texte",
|
||||
"Select the API endpoint region": "Sélectionner la région du point de terminaison API",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "Sélectionnez les champs que vous souhaitez écraser avec les données en amont. Les champs non sélectionnés conservent leurs valeurs locales.",
|
||||
"Select theme preference": "Sélectionner la préférence de thème",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "Le slug ne peut contenir que des lettres, des chiffres, des tirets et des underscores",
|
||||
"Slug is required": "Le slug est requis",
|
||||
"Slug must be less than 100 characters": "Le slug doit contenir moins de 100 caractères",
|
||||
"Small": "Petit",
|
||||
"Smallest USD amount users can recharge (Epay)": "Montant minimum en USD que les utilisateurs peuvent recharger (Epay)",
|
||||
"SMTP Email": "E-mail SMTP",
|
||||
"SMTP encryption": "Chiffrement SMTP",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "Entrée texte",
|
||||
"Text or array of texts to embed": "Texte ou tableau de textes à vectoriser",
|
||||
"Text Output": "Sortie texte",
|
||||
"Text size": "Taille du texte",
|
||||
"Text to Video": "Texte vers vidéo",
|
||||
"Text tokens": "Jetons texte",
|
||||
"The admin configured three groups and one special ratio rule:": "L’administrateur a configuré trois groupes et une règle de taux spécial :",
|
||||
|
||||
Vendored
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "外部運用モード",
|
||||
"External Speed Test": "外部スピードテスト",
|
||||
"Extra": "追加",
|
||||
"Extra Large": "特大",
|
||||
"Extra Notes (Optional)": "追加のメモ (オプション)",
|
||||
"Extra visible": "追加表示",
|
||||
"Extra visible to {{group}}": "{{group}} に追加表示",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "ポストプロセスなしで、リクエストをアップストリームプロバイダーに直接転送します。",
|
||||
"Frames per second": "フレームレート",
|
||||
"Free": "空き",
|
||||
"free for commercial use": "商用利用可",
|
||||
"Free: {{free}} / Total: {{total}}": "空き容量: {{free}} / 合計: {{total}}",
|
||||
"Frequency Penalty": "頻度ペナルティ",
|
||||
"Friendly name to identify this channel": "このチャネルを識別するための表示名",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "言語設定を保存しました",
|
||||
"Language Preferences": "言語設定",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "言語設定はログイン中のすべてのデバイスで同期され、API のエラーメッセージ言語にも反映されます。",
|
||||
"Large": "大",
|
||||
"Last 24h usage": "直近24時間の使用量",
|
||||
"Last 30 days uptime": "直近 30 日の稼働率",
|
||||
"Last check time": "最終チェック時刻",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "価格比較のために同期チャネルを選択してください",
|
||||
"Select sync channels to compare ratios": "比率を比較するために同期チャネルを選択",
|
||||
"Select Sync Source": "同期元を選択",
|
||||
"Select text size": "文字サイズを選択",
|
||||
"Select the API endpoint region": "APIエンドポイントのリージョンを選択",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "アップストリームデータで上書きしたいフィールドを選択してください。選択されていないフィールドはローカル値を保持します。",
|
||||
"Select theme preference": "テーマの好みを選択",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "スラッグには英数字、ハイフン、アンダースコアのみ使用できます",
|
||||
"Slug is required": "スラッグは必須です",
|
||||
"Slug must be less than 100 characters": "スラッグは100文字以内にしてください",
|
||||
"Small": "小",
|
||||
"Smallest USD amount users can recharge (Epay)": "ユーザーがチャージできる最小USD金額 (Epay)",
|
||||
"SMTP Email": "SMTPメール",
|
||||
"SMTP encryption": "SMTP 暗号化方式",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "テキスト入力",
|
||||
"Text or array of texts to embed": "ベクトル化するテキストまたは配列",
|
||||
"Text Output": "テキスト出力",
|
||||
"Text size": "文字サイズ",
|
||||
"Text to Video": "テキストから動画",
|
||||
"Text tokens": "テキストトークン",
|
||||
"The admin configured three groups and one special ratio rule:": "管理者は3つのグループと1つの特別倍率ルールを設定しました:",
|
||||
|
||||
Vendored
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "Режим внешних операций",
|
||||
"External Speed Test": "Внешний тест скорости",
|
||||
"Extra": "Дополнительно",
|
||||
"Extra Large": "Очень крупный",
|
||||
"Extra Notes (Optional)": "Дополнительные примечания (необязательно)",
|
||||
"Extra visible": "Дополнительно видимая",
|
||||
"Extra visible to {{group}}": "Дополнительно видима для {{group}}",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "Перенаправлять запросы напрямую upstream-провайдерам без какой-либо постобработки.",
|
||||
"Frames per second": "Кадров в секунду",
|
||||
"Free": "Свободно",
|
||||
"free for commercial use": "коммерческое использование разрешено",
|
||||
"Free: {{free}} / Total: {{total}}": "Свободно: {{free}} / Всего: {{total}}",
|
||||
"Frequency Penalty": "Штраф за частоту",
|
||||
"Friendly name to identify this channel": "Дружественное имя для идентификации этого канала",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "Языковая настройка сохранена",
|
||||
"Language Preferences": "Языковые настройки",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "Языковые настройки синхронизируются на всех ваших устройствах после входа и влияют на язык сообщений об ошибках API.",
|
||||
"Large": "Крупный",
|
||||
"Last 24h usage": "Расход за 24ч",
|
||||
"Last 30 days uptime": "Доступность за 30 дней",
|
||||
"Last check time": "Время последней проверки",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "Выберите каналы синхронизации для сравнения цен",
|
||||
"Select sync channels to compare ratios": "Выбрать каналы синхронизации для сравнения соотношений",
|
||||
"Select Sync Source": "Выбрать источник синхронизации",
|
||||
"Select text size": "Выберите размер текста",
|
||||
"Select the API endpoint region": "Выбрать регион конечной точки API",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "Выберите поля, которые вы хотите перезаписать данными из вышестоящего источника. Невыбранные поля сохранят свои локальные значения.",
|
||||
"Select theme preference": "Выбрать предпочтение темы",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "Slug может содержать только буквы, цифры, дефисы и подчёркивания",
|
||||
"Slug is required": "Slug обязателен",
|
||||
"Slug must be less than 100 characters": "Slug должен содержать менее 100 символов",
|
||||
"Small": "Мелкий",
|
||||
"Smallest USD amount users can recharge (Epay)": "Минимальная сумма в USD, которую пользователи могут пополнить (Epay)",
|
||||
"SMTP Email": "Электронная почта SMTP",
|
||||
"SMTP encryption": "Шифрование SMTP",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "Текстовый вход",
|
||||
"Text or array of texts to embed": "Текст или массив текстов для векторизации",
|
||||
"Text Output": "Текстовый выход",
|
||||
"Text size": "Размер текста",
|
||||
"Text to Video": "Текст в видео",
|
||||
"Text tokens": "Текстовые токены",
|
||||
"The admin configured three groups and one special ratio rule:": "Администратор настроил три группы и одно правило особого коэффициента:",
|
||||
|
||||
Vendored
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "Chế độ vận hành bên ngoài",
|
||||
"External Speed Test": "Kiểm tra tốc độ bên ngoài",
|
||||
"Extra": "Thêm",
|
||||
"Extra Large": "Cực lớn",
|
||||
"Extra Notes (Optional)": "Ghi chú bổ sung (Tùy chọn)",
|
||||
"Extra visible": "Hiển thị thêm",
|
||||
"Extra visible to {{group}}": "Hiển thị thêm cho {{group}}",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "Chuyển tiếp các yêu cầu trực tiếp đến các nhà cung cấp ngược dòng mà không cần xử lý hậu kỳ nào.",
|
||||
"Frames per second": "Khung hình / giây",
|
||||
"Free": "Trống",
|
||||
"free for commercial use": "được phép dùng thương mại",
|
||||
"Free: {{free}} / Total: {{total}}": "Còn trống: {{free}} / Tổng: {{total}}",
|
||||
"Frequency Penalty": "Phạt tần suất",
|
||||
"Friendly name to identify this channel": "Tên thân thiện để nhận dạng kênh này",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "Đã lưu tùy chọn ngôn ngữ",
|
||||
"Language Preferences": "Tùy chọn ngôn ngữ",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "Tùy chọn ngôn ngữ sẽ đồng bộ trên các thiết bị đã đăng nhập và ảnh hưởng đến ngôn ngữ thông báo lỗi API.",
|
||||
"Large": "Lớn",
|
||||
"Last 24h usage": "Sử dụng 24h qua",
|
||||
"Last 30 days uptime": "Uptime 30 ngày qua",
|
||||
"Last check time": "Thời gian kiểm tra gần nhất",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "Chọn kênh đồng bộ để so sánh giá",
|
||||
"Select sync channels to compare ratios": "Chọn kênh đồng bộ để so sánh tỷ lệ",
|
||||
"Select Sync Source": "Chọn Nguồn Đồng Bộ",
|
||||
"Select text size": "Chọn cỡ chữ",
|
||||
"Select the API endpoint region": "Chọn khu vực điểm cuối API",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "Chọn các trường bạn muốn ghi đè bằng dữ liệu thượng nguồn. Các trường không được chọn sẽ giữ nguyên giá trị cục bộ của chúng.",
|
||||
"Select theme preference": "Chọn chủ đề ưu tiên",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "Slug chỉ có thể chứa chữ cái, số, dấu gạch ngang và dấu gạch dưới",
|
||||
"Slug is required": "Slug là bắt buộc",
|
||||
"Slug must be less than 100 characters": "Slug phải ít hơn 100 ký tự",
|
||||
"Small": "Nhỏ",
|
||||
"Smallest USD amount users can recharge (Epay)": "Số tiền USD tối thiểu người dùng có thể nạp (Epay)",
|
||||
"SMTP Email": "Email SMTP",
|
||||
"SMTP encryption": "Mã hóa SMTP",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "Đầu vào văn bản",
|
||||
"Text or array of texts to embed": "Văn bản hoặc mảng văn bản cần vector hoá",
|
||||
"Text Output": "Đầu ra văn bản",
|
||||
"Text size": "Cỡ chữ",
|
||||
"Text to Video": "Văn bản sang video",
|
||||
"Text tokens": "Token văn bản",
|
||||
"The admin configured three groups and one special ratio rule:": "Quản trị viên đã cấu hình ba nhóm và một quy tắc hệ số đặc biệt:",
|
||||
|
||||
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "對外運營模式",
|
||||
"External Speed Test": "外部速度測試",
|
||||
"Extra": "額外",
|
||||
"Extra Large": "特大",
|
||||
"Extra Notes (Optional)": "額外備註(可選)",
|
||||
"Extra visible": "額外可見",
|
||||
"Extra visible to {{group}}": "對 {{group}} 額外可見",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "將請求直接轉發給上游供應商,不進行任何後處理。",
|
||||
"Frames per second": "幀率",
|
||||
"Free": "可用",
|
||||
"free for commercial use": "允許商用",
|
||||
"Free: {{free}} / Total: {{total}}": "可用空間: {{free}} / 總空間: {{total}}",
|
||||
"Frequency Penalty": "頻率懲罰",
|
||||
"Friendly name to identify this channel": "用於識別此渠道的友好名稱",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "語言偏好已儲存",
|
||||
"Language Preferences": "語言偏好",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "語言偏好會同步到您登入的所有設備,並影響 API 錯誤訊息語言。",
|
||||
"Large": "大",
|
||||
"Last 24h usage": "近 24 小時消耗",
|
||||
"Last 30 days uptime": "近 30 天可用率",
|
||||
"Last check time": "上次檢測時間",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "選擇同步渠道以對比價格",
|
||||
"Select sync channels to compare ratios": "選擇同步渠道以比較比率",
|
||||
"Select Sync Source": "選擇同步源",
|
||||
"Select text size": "選擇文字大小",
|
||||
"Select the API endpoint region": "選擇 API 終端節點區域",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "選擇要使用上游數據覆蓋的欄位。未選擇的欄位將保留其本地值。",
|
||||
"Select theme preference": "選擇主題偏好",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "Slug 只能包含字母、數字、連字符和底線",
|
||||
"Slug is required": "Slug 不能為空",
|
||||
"Slug must be less than 100 characters": "Slug 不能超過 100 個字元",
|
||||
"Small": "小",
|
||||
"Smallest USD amount users can recharge (Epay)": "用戶可以儲值的最小美元金額 (Epay)",
|
||||
"SMTP Email": "SMTP 電郵",
|
||||
"SMTP encryption": "SMTP 加密方式",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "文字輸入",
|
||||
"Text or array of texts to embed": "需要向量化的文字或文字陣列",
|
||||
"Text Output": "文字輸出",
|
||||
"Text size": "文字大小",
|
||||
"Text to Video": "文生影片",
|
||||
"Text tokens": "文字 Token",
|
||||
"The admin configured three groups and one special ratio rule:": "管理員設定了三個分組和一條特殊倍率規則:",
|
||||
|
||||
Vendored
+6
@@ -1776,6 +1776,7 @@
|
||||
"External operations mode": "对外运营模式",
|
||||
"External Speed Test": "外部速度测试",
|
||||
"Extra": "额外",
|
||||
"Extra Large": "特大",
|
||||
"Extra Notes (Optional)": "额外备注(可选)",
|
||||
"Extra visible": "额外可见",
|
||||
"Extra visible to {{group}}": "对 {{group}} 额外可见",
|
||||
@@ -2056,6 +2057,7 @@
|
||||
"Forward requests directly to upstream providers without any post-processing.": "将请求直接转发给上游提供商,不进行任何后处理。",
|
||||
"Frames per second": "帧率",
|
||||
"Free": "可用",
|
||||
"free for commercial use": "允许商用",
|
||||
"Free: {{free}} / Total: {{total}}": "可用空间: {{free}} / 总空间: {{total}}",
|
||||
"Frequency Penalty": "频率惩罚",
|
||||
"Friendly name to identify this channel": "用于识别此渠道的友好名称",
|
||||
@@ -2417,6 +2419,7 @@
|
||||
"Language preference saved": "语言偏好已保存",
|
||||
"Language Preferences": "语言偏好",
|
||||
"Language preferences sync across your signed-in devices and affect API error messages.": "语言偏好会同步到您登录的所有设备,并影响 API 错误消息语言。",
|
||||
"Large": "大",
|
||||
"Last 24h usage": "近 24 小时消耗",
|
||||
"Last 30 days uptime": "近 30 天可用率",
|
||||
"Last check time": "上次检测时间",
|
||||
@@ -4081,6 +4084,7 @@
|
||||
"Select sync channels to compare prices": "选择同步渠道以对比价格",
|
||||
"Select sync channels to compare ratios": "选择同步渠道以比较比率",
|
||||
"Select Sync Source": "选择同步源",
|
||||
"Select text size": "选择文字大小",
|
||||
"Select the API endpoint region": "选择 API 终端节点区域",
|
||||
"Select the fields you want to overwrite with upstream data. Unselected fields keep their local values.": "选择要使用上游数据覆盖的字段。未选择的字段将保留其本地值。",
|
||||
"Select theme preference": "选择主题偏好",
|
||||
@@ -4201,6 +4205,7 @@
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores": "Slug 只能包含字母、数字、连字符和下划线",
|
||||
"Slug is required": "Slug 不能为空",
|
||||
"Slug must be less than 100 characters": "Slug 不能超过 100 个字符",
|
||||
"Small": "小",
|
||||
"Smallest USD amount users can recharge (Epay)": "用户可以充值的最小美元金额 (Epay)",
|
||||
"SMTP Email": "SMTP 邮箱",
|
||||
"SMTP encryption": "SMTP 加密方式",
|
||||
@@ -4445,6 +4450,7 @@
|
||||
"Text Input": "文字输入",
|
||||
"Text or array of texts to embed": "需要向量化的文本或文本数组",
|
||||
"Text Output": "文字输出",
|
||||
"Text size": "文字大小",
|
||||
"Text to Video": "文生视频",
|
||||
"Text tokens": "文本 Token",
|
||||
"The admin configured three groups and one special ratio rule:": "管理员配置了三个分组和一条特殊倍率规则:",
|
||||
|
||||
+18
@@ -83,6 +83,13 @@ export type ThemePreset = (typeof THEME_PRESETS)[number]['value']
|
||||
export type ThemeRadius = 'default' | 'none' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
export type ThemeScale = 'default' | 'sm' | 'lg' | 'xl'
|
||||
|
||||
/**
|
||||
* Text size axis. Overrides only the `--text-*` type ramp, independently of
|
||||
* the density scale (which adjusts spacing and text together). `default`
|
||||
* follows the active preset/density ramp; explicit tiers win over both.
|
||||
*/
|
||||
export type ThemeTextSize = 'default' | 'sm' | 'lg' | 'xl' | '2xl'
|
||||
|
||||
/**
|
||||
* Badge size axis. Controls how "chubby" badges/pills (Badge, StatusBadge)
|
||||
* read, independently of the global density scale. `default` is the compact
|
||||
@@ -119,6 +126,7 @@ export type ThemeCustomization = {
|
||||
font: ThemeFont
|
||||
radius: ThemeRadius
|
||||
scale: ThemeScale
|
||||
textSize: ThemeTextSize
|
||||
badgeSize: ThemeBadgeSize
|
||||
contentLayout: ContentLayout
|
||||
}
|
||||
@@ -128,6 +136,7 @@ export const DEFAULT_THEME_CUSTOMIZATION: ThemeCustomization = {
|
||||
font: 'default',
|
||||
radius: 'default',
|
||||
scale: 'default',
|
||||
textSize: 'default',
|
||||
badgeSize: 'lg',
|
||||
contentLayout: 'full',
|
||||
}
|
||||
@@ -159,6 +168,14 @@ export const THEME_SCALE_VALUES: ReadonlySet<ThemeScale> = new Set([
|
||||
'xl',
|
||||
])
|
||||
|
||||
export const THEME_TEXT_SIZE_VALUES: ReadonlySet<ThemeTextSize> = new Set([
|
||||
'default',
|
||||
'sm',
|
||||
'lg',
|
||||
'xl',
|
||||
'2xl',
|
||||
])
|
||||
|
||||
export const THEME_BADGE_SIZE_VALUES: ReadonlySet<ThemeBadgeSize> = new Set([
|
||||
'default',
|
||||
'lg',
|
||||
@@ -175,6 +192,7 @@ export const THEME_COOKIE_KEYS = {
|
||||
font: 'theme_font',
|
||||
radius: 'theme_radius',
|
||||
scale: 'theme_scale',
|
||||
textSize: 'theme_text_size',
|
||||
badgeSize: 'theme_badge_size',
|
||||
contentLayout: 'theme_content_layout',
|
||||
} as const
|
||||
|
||||
+48
-2
@@ -728,6 +728,50 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
--spacing: 0.3rem;
|
||||
}
|
||||
|
||||
/* ── Text size ────────────────────────────────────────────────────────── */
|
||||
/* Dedicated type-ramp axis (`data-theme-text` on <body>). Unlike the density
|
||||
* scale above — which moves text and spacing together — this only overrides
|
||||
* the `--text-*` ramp, so it composes with any density/preset choice. It is
|
||||
* placed AFTER the preset and density blocks on purpose: an explicit text
|
||||
* size wins over the ramps those ship (e.g. simple-large, scale sm/lg/xl).
|
||||
* `default` removes the attribute and defers to the preset/density ramp. */
|
||||
[data-theme-text='sm'] {
|
||||
--text-xs: 0.7rem;
|
||||
--text-sm: 0.78rem;
|
||||
--text-base: 0.88rem;
|
||||
--text-lg: 1rem;
|
||||
--text-xl: 1.13rem;
|
||||
--text-2xl: 1.38rem;
|
||||
--text-3xl: 1.7rem;
|
||||
}
|
||||
[data-theme-text='lg'] {
|
||||
--text-xs: 0.84rem;
|
||||
--text-sm: 0.95rem;
|
||||
--text-base: 1.075rem;
|
||||
--text-lg: 1.2rem;
|
||||
--text-xl: 1.35rem;
|
||||
--text-2xl: 1.65rem;
|
||||
--text-3xl: 2rem;
|
||||
}
|
||||
[data-theme-text='xl'] {
|
||||
--text-xs: 0.9rem;
|
||||
--text-sm: 1rem;
|
||||
--text-base: 1.125rem;
|
||||
--text-lg: 1.25rem;
|
||||
--text-xl: 1.45rem;
|
||||
--text-2xl: 1.75rem;
|
||||
--text-3xl: 2.15rem;
|
||||
}
|
||||
[data-theme-text='2xl'] {
|
||||
--text-xs: 0.96rem;
|
||||
--text-sm: 1.08rem;
|
||||
--text-base: 1.21rem;
|
||||
--text-lg: 1.35rem;
|
||||
--text-xl: 1.55rem;
|
||||
--text-2xl: 1.9rem;
|
||||
--text-3xl: 2.3rem;
|
||||
}
|
||||
|
||||
/* ── Badge size ───────────────────────────────────────────────────────── */
|
||||
/* Independent axis for how "chubby" badges/pills read (`data-theme-badge`
|
||||
* on <body>, driven by ThemeCustomizationProvider). The redesign moved
|
||||
@@ -747,7 +791,8 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
border-radius: calc(infinity * 1px);
|
||||
}
|
||||
[data-theme-badge='lg'] [data-slot='badge'],
|
||||
[data-theme-badge='lg'] [data-slot='status-badge']:not([data-appearance='plain']) {
|
||||
[data-theme-badge='lg']
|
||||
[data-slot='status-badge']:not([data-appearance='plain']) {
|
||||
height: calc(var(--spacing) * 6);
|
||||
gap: calc(var(--spacing) * 1.5);
|
||||
padding-inline: calc(var(--spacing) * 2);
|
||||
@@ -758,7 +803,8 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
border-radius: calc(infinity * 1px);
|
||||
}
|
||||
[data-theme-badge='xl'] [data-slot='badge'],
|
||||
[data-theme-badge='xl'] [data-slot='status-badge']:not([data-appearance='plain']) {
|
||||
[data-theme-badge='xl']
|
||||
[data-slot='status-badge']:not([data-appearance='plain']) {
|
||||
height: calc(var(--spacing) * 7);
|
||||
gap: calc(var(--spacing) * 1.5);
|
||||
padding-inline: calc(var(--spacing) * 2.5);
|
||||
|
||||
Reference in New Issue
Block a user