Skip to content
Merged
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
53 changes: 27 additions & 26 deletions web/TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# HeroUI v3 Cleanup

Branch: `feature/heroui-v3-cleanup`
Tracker across PRs:
- PR #9 (merged) — Phase 0, 2, 3.
- PR <pending> (this branch) — Phase 4, 6.

Active branch: `feature/heroui-v3-success-warning-buttons`

Goal: bring `web/` into full compliance with HeroUI v3 component rules
(ref: https://heroui.com/react/llms.txt and per-component migration guides).
Expand Down Expand Up @@ -62,26 +66,24 @@ Mapping applied:
- [x] Run codemod (102 component files, ~325 prop edits + Chip in semi.js).
- [x] Verify zero `variant='solid|bordered|flat|light|faded|shadow'` and zero `radius=` left in `web/src/`.

### Phase 4 — Manual review of `color='success' / 'warning'`

v3 Button has no built-in success/warning variant. The codemod left
these in place; each needs a Tailwind override (`bg-success`, etc.) or
a swap to the closest semantic intent.

- [ ] `web/src/components/settings/personal/cards/CheckinCalendar.jsx` —
"补签 / Gift" success Button.
- [ ] `web/src/components/table/channels/modals/ModelTestModal.jsx` —
"测试中" warning indicator.
- [ ] `web/src/components/table/channels/modals/MultiKeyManageModal.jsx` —
warning badge.
- [ ] `web/src/components/table/tokens/index.jsx` — warning label.
- [ ] `web/src/components/table/users/UsersColumnDefs.jsx` — risk warning.
- [ ] `web/src/components/table/users/modals/UserSubscriptionsModal.jsx` —
warning.
- [ ] `web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx` —
warning.
- [ ] `web/src/pages/Setting/Performance/SettingsPerformance.jsx` —
"清理缓存" warning Button.
### Phase 4 — Manual review of `color='success' / 'warning'` ✅

v3 Button has no built-in success/warning variant. Centralised the four
tone classes in `web/src/components/common/ui/buttonTones.js`
(`successButtonClass`, `warningButtonClass`, `warningSoftButtonClass`,
`warningGhostButtonClass`) — each overrides `--button-bg / -hover /
-pressed / -fg` using the warning / success tokens `@heroui/styles`
already exposes via Tailwind v4. Pair with v3 `variant='primary'` for
solid tones, `variant='tertiary'` for ghost / icon-only.

- [x] `CheckinCalendar` "立即签到" → `variant='primary' + successButtonClass`.
- [x] `SettingsPerformance` "清理不活跃缓存" → `variant='primary' + warningButtonClass`.
- [x] `MultiKeyManageModal` "删除自动禁用密钥" → `variant='primary' + warningButtonClass`.
- [x] `SettingsChannelAffinity` "清空规则缓存" (icon-only X) → `variant='tertiary' + warningGhostButtonClass`.
- [x] `ModelTestModal` "前往设置" → `variant='tertiary' + warningGhostButtonClass`.
- [x] `UserSubscriptionsModal` "作废" → `variant='tertiary' + warningGhostButtonClass`.
- [x] `UsersColumnDefs` "提升" → `variant='tertiary' + warningGhostButtonClass`.
- [x] `tokens/index` "不再提醒" → `variant='tertiary' + warningGhostButtonClass`.

### Phase 5 — `startContent` / `endContent` → children

Expand All @@ -91,12 +93,11 @@ Mostly mechanical but needs human review for ordering and gap classes.

- [ ] Write codemod, run, manual sanity pass.

### Phase 6 — `classNames={...}` cleanup

Single occurrence remaining (`web/src/components/auth/AuthLayout.jsx`),
v3 uses plain `className`.
### Phase 6 — `classNames={...}` cleanup ✅

- [ ] Migrate.
- [x] `web/src/components/auth/AuthLayout.jsx` — replaced
`classNames={{ base, label }}` with root `className='items-start'`
+ `<span>` wrapper for the label tone.

### Phase 7 — Final verification

Expand Down
9 changes: 3 additions & 6 deletions web/src/components/auth/AuthLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,9 @@ export function AuthAgreement({
<Checkbox
isSelected={checked}
onValueChange={onChange}
classNames={{
base: 'items-start',
label: 'text-sm leading-6 text-muted',
}}
className='items-start'
>
<>
<span className='text-sm leading-6 text-muted'>
{t('我已阅读并同意')}
{hasUserAgreement ? (
<a
Expand All @@ -137,7 +134,7 @@ export function AuthAgreement({
{t('隐私政策')}
</a>
) : null}
</>
</span>
</Checkbox>
</div>
);
Expand Down
45 changes: 45 additions & 0 deletions web/src/components/common/ui/buttonTones.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright (C) 2025 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
*/

// HeroUI v3 dropped the `color` prop on Button — the v3 variants only cover
// primary / secondary / tertiary / outline / ghost / danger / danger-soft.
// For warning / success buttons we reuse v3's Button base (so size, padding,
// radius, focus / pressed transitions stay correct) and override the
// CSS variables Button reads (`--button-bg`, `--button-bg-hover`,
// `--button-bg-pressed`, `--button-fg`) using the warning / success tokens
// `@heroui/styles` already exposes via Tailwind v4.
//
// Pair these classes with `variant="primary"` (for solid) or
// `variant="tertiary"` (for soft / icon-only) so disabled / pending state
// styling stays consistent with the rest of the app.

export const warningButtonClass =
'[--button-bg:var(--color-warning)] [--button-bg-hover:var(--color-warning-hover)] [--button-bg-pressed:var(--color-warning-hover)] [--button-fg:var(--color-warning-foreground)]';

export const warningSoftButtonClass =
'[--button-bg:var(--color-warning-soft)] [--button-bg-hover:var(--color-warning-soft-hover)] [--button-bg-pressed:var(--color-warning-soft-hover)] [--button-fg:var(--color-warning-soft-foreground)]';

// Ghost-style warning: transparent base, warning-tinted hover, warning text.
// Use with `variant="tertiary"` for low-emphasis cautionary actions
// (e.g. "前往设置", "不再提醒", icon-only delete-cache buttons).
export const warningGhostButtonClass =
'text-warning [--button-bg-hover:var(--color-warning-soft)] [--button-bg-pressed:var(--color-warning-soft-hover)]';

export const successButtonClass =
'[--button-bg:var(--color-success)] [--button-bg-hover:var(--color-success-hover)] [--button-bg-pressed:var(--color-success-hover)] [--button-fg:var(--color-success-foreground)]';
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from 'lucide-react';
import Turnstile from 'react-turnstile';
import { API, showError, showSuccess, renderQuota } from '../../../../helpers';
import { successButtonClass } from '../../../common/ui/buttonTones';

const WEEKDAY_KEYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

Expand Down Expand Up @@ -369,7 +370,8 @@ const CheckinCalendar = ({ t, status, turnstileEnabled, turnstileSiteKey }) => {
</div>
</div>
<Button
color='success'
variant='primary'
className={successButtonClass}
startContent={<Gift size={14} />}
onPress={() => doCheckin()}
isPending={checkinLoading || !initialLoaded}
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/table/channels/modals/ModelTestModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from '@heroui/react';
import { Info, Search, Settings } from 'lucide-react';
import { copy, showError, showInfo, showSuccess } from '../../../../helpers';
import { warningGhostButtonClass } from '../../../common/ui/buttonTones';
import { MODEL_TABLE_PAGE_SIZE } from '../../../../constants';

const TAG_TONE = {
Expand Down Expand Up @@ -264,12 +265,11 @@ const ModelTestModal = ({
<Button
size='sm'
variant='tertiary'
color='warning'
startContent={<Settings size={12} />}
onPress={() =>
window.open('/console/setting?tab=ratio', '_blank')
}
className='w-fit'
className={`w-fit ${warningGhostButtonClass}`}
>
{t('前往设置')}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
timestamp2string,
} from '../../../../helpers';
import ConfirmDialog from '../../../common/ui/ConfirmDialog';
import { warningButtonClass } from '../../../common/ui/buttonTones';

// ----------------------------- helpers -----------------------------

Expand Down Expand Up @@ -509,7 +510,8 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
)}
<Button
size='sm'
color='warning'
variant='primary'
className={warningButtonClass}
isPending={operationLoading.delete_disabled}
onPress={() => setConfirmDeleteDisabled(true)}
>
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/table/tokens/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
showWarning,
} from '../../../helpers';
import CardPro from '../../common/ui/CardPro';
import { warningGhostButtonClass } from '../../common/ui/buttonTones';
import TokensTable from './TokensTable';
import TokensActions from './TokensActions';
import TokensFilters from './TokensFilters';
Expand Down Expand Up @@ -463,7 +464,7 @@ function FluentNoticePanel({
<Button
size='sm'
variant='tertiary'
color='warning'
className={warningGhostButtonClass}
onPress={onSuppress}
>
{t('不再提醒')}
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/table/users/UsersColumnDefs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { Button, Tooltip } from '@heroui/react';
import { Copy, MoreHorizontal } from 'lucide-react';
import { renderGroup, renderNumber, renderQuota, copy, showSuccess } from '../../../helpers';
import { warningGhostButtonClass } from '../../common/ui/buttonTones';

const TONE_CLASSES = {
blue:
Expand Down Expand Up @@ -370,9 +371,9 @@ const renderOperations = (
{t('编辑')}
</Button>
<Button
color='warning'
variant='tertiary'
size='sm'
className={warningGhostButtonClass}
onPress={() => showPromoteModal(record)}
>
{t('提升')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { convertUSDToCurrency } from '../../../../helpers/render';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
import CardTable from '../../../common/ui/CardTable';
import ConfirmDialog from '@/components/common/ui/ConfirmDialog';
import { warningGhostButtonClass } from '@/components/common/ui/buttonTones';

const PAGE_SIZE = 10;

Expand Down Expand Up @@ -317,8 +318,8 @@ const UserSubscriptionsModal = ({
<div className='flex flex-wrap items-center gap-1.5'>
<Button
size='sm'
color='warning'
variant='tertiary'
className={warningGhostButtonClass}
isDisabled={!isActive || isCancelled}
onPress={() =>
setPendingConfirm({
Expand Down
3 changes: 2 additions & 1 deletion web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
} from '../../../constants/channel-affinity-template.constants';
import ParamOverrideEditorModal from '../../../components/table/channels/modals/ParamOverrideEditorModal';
import ConfirmDialog from '../../../components/common/ui/ConfirmDialog';
import { warningGhostButtonClass } from '../../../components/common/ui/buttonTones';

// ----------------------------- helpers -----------------------------

Expand Down Expand Up @@ -879,7 +880,7 @@ export default function SettingsChannelAffinity(props) {
isIconOnly
size='sm'
variant='tertiary'
color='warning'
className={warningGhostButtonClass}
isDisabled={!record.include_rule_name}
aria-label={t('清空该规则缓存')}
onPress={() => setConfirmClearRule(record)}
Expand Down
4 changes: 3 additions & 1 deletion web/src/pages/Setting/Performance/SettingsPerformance.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
showWarning,
} from '../../../helpers';
import ConfirmDialog from '../../../components/common/ui/ConfirmDialog';
import { warningButtonClass } from '../../../components/common/ui/buttonTones';

// ----------------------------- helpers -----------------------------

Expand Down Expand Up @@ -617,7 +618,8 @@ export default function SettingsPerformance(props) {
{t('刷新统计')}
</Button>
<Button
color='warning'
variant='primary'
className={warningButtonClass}
onPress={() => setConfirmClearCache(true)}
>
{t('清理不活跃缓存')}
Expand Down