Commit Graph
100 Commits
Author SHA1 Message Date
t0ng7u b0ac0429cf fix(web): resolve TypeScript errors in usage logs mobile card
Cast row.original to Record<string, unknown> before accessing created_at and type in CommonLogsCard, matching the pattern used elsewhere in the same component.

Close: #5243
2026-06-03 12:37:36 +08:00
t0ng7u 7791b78429 chore(fd): delete the test file 2026-06-02 14:28:35 +08:00
t0ng7u 65f8afe922 🐛 fix(system-settings): resolve save detection and number input NaN issues
System settings forms that used flat dotted API keys (e.g.
`performance_setting.monitor_cpu_threshold`) with React Hook Form were
broken: RHF stores dotted paths as nested objects on update, while dirty
checks and submit comparisons still read flat keys from defaults. Users
could edit values but always saw "No changes to save".

Refactor affected sections to use nested Zod schemas and default values
for RHF, with explicit helpers to convert between nested form state and
flat API keys. Track a normalized baseline in refs for accurate change
detection and post-save resets.

Add `safeNumberFieldProps` to prevent native `<input type="number">`
from writing NaN into form state when cleared. NaN caused Zod validation
to fail silently and made the save button appear unresponsive. The
helper ignores non-finite updates so controlled inputs snap back to the
last valid value, matching legacy Semi InputNumber behavior.

Sections refactored for dotted-key handling:
- maintenance/performance-section
- models/grok-settings-card
- auth/passkey-section
- auth/oauth-section
- auth/section-registry (pass attachment_preference raw; normalize in section)

Sections migrated to safeNumberFieldProps:
- maintenance/performance-section
- models/grok-settings-card
- integrations/monitoring-settings-section
- integrations/payment-settings-section
- integrations/creem-product-dialog
- general/pricing-section (USD exchange rate)
- general/system-behavior-section
- content/dashboard-section

Optional numeric fields (e.g. custom currency exchange rate) keep their
existing empty-to-undefined semantics and are intentionally unchanged.
2026-05-26 15:43:56 +08:00
t0ng7u a64f26d1d2 🎨 feat(web/default): add Anthropic theme preset and configurable serif typography
Introduce a switchable Anthropic-inspired color preset and a new Font customization axis so users can adopt the editorial serif look across the entire UI, including sidebar navigation, tabs, form controls, buttons, and table headers.

Theme preset

Add anthropic to the theme preset registry with warm cream canvas, slate foreground, and clay/coral accent tokens for light and dark modes
Define explicit surface colors for the Anthropic preset instead of relying on the semantic surface bridge
Exclude anthropic from the primary-color surface bridge so bespoke warm neutrals are not overridden by accent-tinted mixes
Typography system

Add @fontsource-variable/lora and a global --font-serif token with CJK serif fallbacks (Noto Serif SC, Source Han Serif, Songti SC, etc.)
Introduce a --font-body token and drive <body> font-family from it
Add a Font axis (default | sans | serif) parallel to radius/scale
Resolve font: 'default' against preset defaults (anthropic → serif)
Persist font preference via cookie and apply data-theme-font on <body>
Apply serif OpenType features (kern, liga, calt, tnum) and heading display tuning when serif is active
Remove per-component sans opt-outs so serif inherits through sidebar, tabs, inputs, buttons, badges, and table headers via natural CSS cascade
Keep monospace contexts unchanged via Tailwind preflight and .font-mono
UI and i18n

Add Font selector to the theme config drawer (Auto / Sans / Serif)
Add "Font" and "Select body font" translations for en, zh, fr, ja, ru, vi
Misc

Tighten group and status badge sizing for better balance with serif text
2026-05-26 04:31:13 +08:00
t0ng7u 3360882642 ♻️ refactor(channels): rebuild channel editor UX with modular sections and Base UI multi-select
Restructure the default-theme channel create/edit experience to match classic
frontend behavior, improve form UX, and align with the project's Base UI design
system.

Channel editor architecture:
- Split the monolithic channel mutate drawer into focused section components
  (basic, API access, auth, models, advanced) with shared drawer layout
  primitives
- Extract submission, toast handling, and react-query cache invalidation into
  `useChannelMutateForm`
- Add a dedicated loading skeleton for channel detail fetch during edit mode
- Remove the top-level configuration summary block

Form validation and data handling:
- Strengthen `channel-form` Zod schema with JSON, model mapping, status code
  mapping, Codex credential, and Vertex AI key refinements
- Move type-specific conditional validation into `superRefine`
- Normalize base URL formatting and tighten model mapping value validation

Model mapping editor:
- Add Visual/JSON tabbed editing with inline JSON and duplicate-key feedback
- Improve accessibility for icon-only actions and add model suggestion datalists

MultiSelect component:
- Replace the custom cmdk-based implementation with Base UI Combobox chips
- Align focus, border, ring, disabled, and invalid states with standard Input
  styling via `ComboboxChips`
- Preserve existing API for all current callers (`options`, `selected`,
  `onChange`, `allowCreate`, `createLabel`)
- Support inline custom value creation and comma/newline batch input
- Limit visible chips with a compact "+N more" overflow summary via
  `maxVisibleChips` (8 in the channel editor)
- Anchor the dropdown to the full chips container via `useComboboxAnchor` so
  the popup matches input width and long model names are no longer truncated

Models & groups UX:
- Integrate manual custom model entry directly into the model MultiSelect
- Remove the separate manual model input/button block
- Keep selected-model count badge and existing model-mapping guardrail behavior

i18n:
- Add and sync translation keys for section descriptions, validation messages,
  model mapping UI, and MultiSelect labels across en, zh, fr, ja, ru, and vi
- Fix missing translations for "Name, provider type, and availability.",
  "Endpoint, provider-specific settings, and credentials.", and "Published
  models, groups, and model remapping rules."
- Remove obsolete keys tied to the deprecated summary and manual model entry UI
2026-05-26 01:55:27 +08:00
t0ng7u b37b6d80b3 Merge remote-tracking branch 'origin/main' 2026-05-26 01:22:56 +08:00
t0ng7u 3d850d38b6 ♻️ refactor(channels): rebuild channel create/edit drawer with modular sections and improved form UX
Restructure the default-theme channel create/edit experience to align with
classic frontend behavior, modern form UX patterns, and the project's Base UI
design system.

Channel editor architecture:
- Split the monolithic channel mutate drawer into focused section components
  (basic, API access, auth, models, advanced) with shared drawer layout
  primitives
- Extract submission, toast handling, and react-query cache invalidation into
  `useChannelMutateForm`
- Add a dedicated loading skeleton for channel detail fetch during edit mode
- Remove the top-level configuration summary block per UX feedback

Form validation and data handling:
- Strengthen `channel-form` Zod schema with JSON, model mapping, status code
  mapping, Codex credential, and Vertex AI key refinements
- Move type-specific conditional validation into `superRefine`
- Normalize base URL formatting and tighten model mapping value validation

Model mapping editor:
- Add Visual/JSON tabbed editing with inline JSON and duplicate-key feedback
- Improve accessibility for icon-only actions and add model suggestion datalists

MultiSelect component:
- Replace the custom cmdk-based implementation with Base UI Combobox chips
- Align focus, border, ring, disabled, and invalid states with standard Input
  styling via `ComboboxChips`
- Preserve existing API (`options`, `selected`, `onChange`, `allowCreate`,
  `createLabel`) for all current callers
- Support inline custom value creation, comma/newline batch input, searchable
  options, portal-based dropdown positioning, and chip removal

Models & groups UX:
- Integrate manual custom model entry directly into the model MultiSelect
- Remove the separate manual model input/button block
- Keep selected-model count and existing model-mapping guardrail behavior

i18n:
- Add and sync translation keys for new editor sections, validation messages,
  model mapping UI, and MultiSelect empty/create labels across en, zh, fr, ja,
  ru, and vi
- Remove obsolete keys tied to the deprecated summary and manual model entry UI

Affected areas:
- `web/default/src/features/channels/components/drawers/`
- `web/default/src/features/channels/hooks/use-channel-mutate-form.ts`
- `web/default/src/features/channels/lib/channel-form.ts`
- `web/default/src/features/channels/lib/model-mapping-validation.ts`
- `web/default/src/features/channels/components/model-mapping-editor.tsx`
- `web/default/src/components/multi-select.tsx`
- `web/default/src/i18n/locales/*.json`
2026-05-26 01:22:49 +08:00
t0ng7u 583da45296 refactor(ui): Improve usage log filter responsiveness and mobile UX
Refactor the usage log filter toolbar into a shared reusable component for common, drawing, and task logs. Optimize desktop filters with a responsive grid, move secondary filters into a mobile drawer, standardize filter typography, remove redundant filter icons, and add the missing i18n translations for the new drawer description.
2026-05-25 05:35:44 +08:00
t0ng7u b302be30e3 🛠️ fix: v1 interface feedback regressions
Resolve verified V1 frontend feedback by improving channel workflows, auth behavior, API key interactions, user filtering, layout persistence, subscription quota handling, i18n text, pricing metadata, and stale frontend cache recovery.

- Add a global frontend cache version cleanup to prevent old frontend localStorage from causing page errors after upgrades.
- Fix channel copy refresh, model mapping input focus loss, create-channel fetch-model title state, upstream model update confirmation, and batch test toast behavior.
- Respect password login settings and improve Turnstile, forgot-password, registration, and invite-link flows.
- Make user role/status filtering server-side and preserve table page size in URLs.
- Improve API key edit validation feedback and prefetch real keys for reliable copy actions.
- Fix rankings access fail-open behavior, double scrollbars, subscription received amount conversion/display, token i18n wording, model deletion confirmation grammar, and Claude pricing context inference.
- Add clearer Playground model/group loading errors.

Validation:
- bun run typecheck
- bun run i18n:sync
- gofmt on modified Go files
- go test ./controller ./model -run '^$'
2026-05-25 02:42:22 +08:00
t0ng7u 88437a1869 ⬆️ chore(deps): Upgrade default frontend dependencies
Upgrade all web/default dependencies to their latest versions and refresh the Bun lockfile. Add dependency overrides for vulnerable transitive packages so bun audit reports no known vulnerabilities.

Update TypeScript configuration for TypeScript 6 by removing deprecated baseUrl usage and explicitly enabling Node types where needed. Adapt the calendar component to react-day-picker v10 by replacing the removed table class key with month_grid.

Validation:
- bun outdated: no outdated dependencies
- bun audit: no vulnerabilities found
- bun run typecheck: passed
- bun run build: passed
2026-05-25 01:06:42 +08:00
t0ng7u b08febaa3c refactor: system settings UI for consistent, compact layouts
Redesign the system settings interface to align with the rest of the console experience by using fixed header actions, removing redundant subtitles, respecting global content width, and standardizing responsive form layouts.

Introduce reusable settings layout primitives for forms, switch rows, grouped controls, nested control sections, title status indicators, and page action portals. Replace duplicated card-style switch markup with explicit compact components, improve nested switch readability, and reduce visual noise across authentication, billing, content, integrations, maintenance, models, and request-limit settings.

Also complete missing i18n translations, remove obsolete subtitle translation keys, refine i18n sync reporting, fix sidebar truncation for long labels, and verify the frontend with type checking and lint diagnostics.
2026-05-25 00:34:26 +08:00
t0ng7u 92a0959448 refactor(web/default): adopt drill-in sidebar pattern for System Settings
Replace the ad-hoc "workspace" abstraction with a focused, URL-driven
"sidebar view" registry that implements the modern Vercel / Cloudflare
drill-in pattern: clicking a top-level entry (e.g. System Settings)
swaps the sidebar to a contextual workspace, with a `← Back to
Dashboard` affordance, instead of stacking sub-navigation in the root.

Architecture
------------
- types.ts
    + SidebarView           — declarative nested view config
                              (id, pathPattern, parent, getNavGroups)
    + SidebarViewParent     — back-navigation descriptor
    + ResolvedSidebarView   — { key, view, navGroups } returned by hook
    + SidebarData           — slimmed to { navGroups } only
    - Workspace             — removed (logo/plan never rendered)

- lib/sidebar-view-registry.ts (new, replaces workspace-registry.ts)
    + SIDEBAR_VIEWS array — single source of truth for nested views
    + resolveSidebarView(pathname)
    + getNavGroupsForPath(pathname, t) — back-compat helper for the
      command palette

- config/system-settings.config.ts
    Refactored to export a single SYSTEM_SETTINGS_VIEW (SidebarView)
    with parent `/dashboard/overview` + label `Back to Dashboard`.

- components/sidebar-view-header.tsx (new)
    Renders only the back affordance (chevron + label). Uses the
    default SidebarMenuButton size so its typography matches the
    nav items below; collapses gracefully into icon mode via the
    existing tooltip behavior. The redundant "title + icon" row was
    removed — workspace context is already carried by the nav groups.

- hooks/use-sidebar-view.ts (new)
    Encapsulates view resolution and root-nav filtering:
      · matched view  → returns its nav groups verbatim (route-level
                        beforeLoad guards already enforce access);
      · no match      → returns root nav groups, narrowed by user
                        role (admin gate) and useSidebarConfig
                        (admin × user sidebar_modules overlay).

- components/app-sidebar.tsx
    Now a thin presentation layer: reads { key, view, navGroups }
    from useSidebarView() and orchestrates the view transition via
    AnimatePresence + MOTION_VARIANTS.sidebarSlide (respects
    prefers-reduced-motion). No logic, no role checks, no path
    matching — those live in the hook.

- components/command-menu.tsx
    Switched to the new getNavGroupsForPath() API; behavior preserved.

Cleanup
-------
- Deleted layout/context/workspace-context.tsx (zero consumers).
- Deleted layout/lib/workspace-registry.ts and its
  workspace-registry.example.ts companion (over-abstracted: name/id
  metadata, isInWorkspace / getAllWorkspaces / WORKSPACE_IDS were
  registered but never read).
- Removed `workspaces` field from useSidebarData (never consumed
  after the top-switcher was dropped).
- Dropped WorkspaceProvider from authenticated-layout.tsx.
- Trimmed dead `Manage and configure` translation key from all six
  locale files and from static-keys.ts.

i18n
----
Added the `Back to Dashboard` key to en, zh, fr, ja, ru, vi, and
registered it in static-keys.ts under "Sidebar views".

Verification
------------
- bun run typecheck: passes
- Lint: no new warnings/errors on the touched files
- Adding a new drill-in workspace now only requires registering a
  SidebarView in SIDEBAR_VIEWS — no changes to AppSidebar required.
2026-05-24 22:09:05 +08:00
t0ng7u d146e45e2f ⚖️ chore(web/default): add reusable copyright header tooling
Add a Bun script to apply and normalize AGPL copyright headers across the default frontend source files.

The script keeps headers idempotent, upgrades existing headers to the 2023-2026 QuantumNous range, and is exposed through `bun run copyright` for future maintenance.
2026-05-09 11:35:07 +08:00
t0ng7u 948780e3fa 🎨 fix(theme): align UI controls with global radius tokens
Remove hard-coded and capped border radius overrides so shared controls and feature actions consistently follow the active theme radius.

- Replace fixed radius utilities with semantic theme-aware radius tokens
- Remove redundant `rounded-full` and pixel-based overrides from header, toolbar, Playground, and utility actions
- Drop unused `StatusBadge` rounded prop usage
- Keep existing component behavior intact while improving global theme consistency
2026-05-08 01:50:03 +08:00
t0ng7u c19d5aa663 feat: Add model performance metrics to dashboard
Add a shared `performance-metrics` feature module for perf metric APIs, DTOs, and formatting, then surface global 24h model performance on the dashboard with cards and a top-model table.

Reuse the shared metrics module from pricing model details, remove duplicated perf API/formatting code from pricing, and add localized labels for the new dashboard performance UI.
2026-05-08 01:06:44 +08:00
t0ng7u a7475a1e67 🎨 fix(web): align UI and charts with theme tokens and presets
Improve theme switching fidelity (including system preference), extend design tokens so color presets tint real surfaces—not only primary/chrome—and refactor shared badges, tables, and dashboard visuals to semantic colors. Wire VChart series colors to `--chart-*` with safe fallbacks.

**Changes**

- **Theme runtime** (`theme-provider.tsx`): Validate stored theme cookie; keep `resolvedTheme` in sync with DOM + `(prefers-color-scheme)`; `resetTheme` respects `defaultTheme`; memoized context value.
- **Tokens** (`theme.css`): Add `--success|warning|info|neutral` (+ foregrounds) and map them under `@theme inline` for Tailwind utilities.
- **Presets** (`theme-presets.css`): For non-`default` presets, derive `card`, `popover`, `muted`, `accent`, `border`, `input`, and sidebar tokens from `--primary`/`--background`; map semantic status colors to preset chart variables.
- **Components**: `status-badge`, `colors` (avatars, announcements), `copy-button`, `group-badge`, `data-table` row styles, `sidebar` outline shadow (fix `var(--sidebar-border)` usage), ai-elements tool/web-preview status colors.
- **Dashboard**: Latency/API helpers and overview fragments use semantic tokens; `charts.ts` reads `--chart-1`…`--chart-5` from computed styles with fallbacks; `processChartData` / `processUserChartData` accept optional `themeKey` for preset churn; chart components pass `customization.preset` and bump `VChart` keys.

**Verification**

- `bun run typecheck`
2026-05-07 11:20:43 +08:00
t0ng7u 415d21d071 ♻️ refactor(layout): rename workspace switcher to system brand
Rename the layout branding component to reflect that it displays the system identity rather than switching workspaces. Update header usage and layout exports, and remove the now-unused workspace data dependency.
2026-05-07 03:54:32 +08:00
t0ng7u abc255dd6d ☀ fix(default): keep SectionPageLayout description slot hidden
Keep SectionPageLayout.Description as a non-rendering composition slot so callers stay compatible while page subtitles remain hidden across the app.
2026-05-07 03:26:22 +08:00
t0ng7u a7d019e3a9 feat(default): redesign dashboard overview
Refresh the overview page with an actionable Get Started guide, live API request details, real usage sparklines, and OpenAI-inspired dashboard panels. Add collapsible setup state, role-aware quick actions, and localized copy so returning users can focus on platform health.
2026-05-07 03:20:35 +08:00
t0ng7u 673ea75811 🔔 feat: Add subscription-aware quota notifications and update UI copy
Routes quota alerts through a subscription-specific check when billing from subscriptions, preventing wallet-based thresholds from triggering false warnings.
Updates the notification settings description and localization keys to clarify that both wallet and subscription balances are monitored.
2026-02-07 01:15:59 +08:00
t0ng7u 743b9278c4 🛠️ fix: billing session error handling for subscription-first fallback.
Aligns the error variable types in the subscription-first path so that quota fallback checks use the correct NewAPIError.
This prevents build failures and preserves the intended wallet fallback when subscription pre-consume returns an insufficient quota error.
2026-02-07 01:03:49 +08:00
t0ng7u 4fd8d033cd chore: Improve subscription billing fallback and UI states
Add a lightweight active-subscription check to skip subscription pre-consume when none exist, reducing unnecessary transactions and locks. In the subscription UI, disable subscription-first options when no active plan is available, show the effective fallback to wallet with a clear notice, and distinguish “invalidated” from “expired” states. Update i18n strings across supported locales to reflect the new messages and status labels.
2026-02-07 00:57:36 +08:00
t0ng7u 9942045b94 refactor(wallet): Top-up layout to embed subscription plans into the recharge card tabs
- Defaulting to subscriptions when available and avoiding initial flash when no plans exist.
- Adjust the wide-screen layout to place wallet and invite sections side by side, simplify the subscription header and controls, and add padding to prevent card borders from clipping.
- Update related i18n strings by adding the new tab label and removing the obsolete subscription blurb.
2026-02-07 00:11:00 +08:00
t0ng7u 708b7bef50 🔒 fix(security): sanitize AI-generated HTML to prevent XSS in playground
Mitigate XSS vulnerabilities in the playground where AI-generated content
is rendered without sanitization, allowing potential script injection via
prompt injection attacks.

MarkdownRenderer.jsx:
- Replace dangerouslySetInnerHTML with a sandboxed iframe for HTML preview
- Use sandbox="allow-same-origin" to block script execution while allowing
  CSS rendering and iframe height auto-sizing
- Add SandboxedHtmlPreview component with automatic height adjustment

CodeViewer.jsx:
- Add escapeHtml() utility to encode HTML entities before rendering
- Rewrite highlightJson() to process tokens iteratively, escaping each
  token and structural text before wrapping in syntax highlighting spans
- Escape non-JSON and very-large content paths that previously bypassed
  sanitization
- Update linkRegex to correctly match URLs containing &amp; entities

These changes only affect the playground (AI output rendering). Admin-
configured content (home page, about page, footer, notices) remains
unaffected as they use separate code paths and are within the trusted
admin boundary.
2026-02-06 15:10:05 +08:00
t0ng7u 4bf30e7375 🐛 fix: sidebar scroll on mobile dynamic viewport
Use dynamic viewport height to prevent sidebar scroll lock in mobile browsers
Harden sidebar scroll container with min-height and momentum scrolling
2026-02-05 01:42:24 +08:00
t0ng7u 186c7934af fix: Improve subscription payment handling and card layout consistency
Unify Epay subscription response format with top-up flow, and harden frontend error handling to avoid object-to-string issues. Refine subscription plan cards layout to be wider, left-aligned, and visually consistent across breakpoints.
2026-02-05 01:34:04 +08:00
t0ng7u f45e707d8e 🚀 fix(model-sync): avoid unnecessary upstream fetch while keeping overwrite updates working
- Only short-circuit when there are no missing models AND no overwrite fields requested
- Preserve overwrite behavior even when the missing-model list is empty
- Always return empty arrays (not null) for list fields to keep API responses stable
- Clarify SyncUpstreamModels behavior in comments (create missing models + optional overwrite updates)
2025-12-25 23:01:09 +08:00
t0ng7u 1f6527e91a 🔗 docs(readme): revert missing docs links to legacy site
Keep new-site links (/{lang}/docs/...) where matching pages exist in the current docs repo
Revert links that have no equivalent in the new docs to the legacy paths on doc.newapi.pro:
Google Gemini Chat
Midjourney-Proxy image docs
Suno music docs
Apply the same rule consistently across all README translations (zh/en/ja/fr)
2025-12-21 21:18:59 +08:00
t0ng7u 93b3cfc0f7 🔗 docs(readme): update documentation links to new site routing
- Replace legacy `docs.newapi.pro` paths with the new `/{lang}/docs/...` structure across all README translations
- Point key sections (installation, env vars, API, support, features) to their new locations
- Ensure language-specific links use the correct locale prefix (zh/en/ja) and keep FR aligned with English routes
2025-12-21 21:00:33 +08:00
t0ng7u fa814b80fe 🧹 fix: harden request-body size handling and error unwrapping
Tighten oversized request handling across relay paths and make error matching reliable.

- Align `MAX_REQUEST_BODY_MB` fallback to `32` in request body reader and decompression middleware
- Stop ignoring `GetRequestBody` errors in relay retry paths; return consistent **413** on oversized bodies (400 for other read errors)
- Add `Unwrap()` to `types.NewAPIError` so `errors.Is/As` can match wrapped underlying errors
- `go test ./...` passes
2025-12-16 18:10:00 +08:00
t0ng7u c2ed76ddfd 🛡️ fix: prevent OOM on large/decompressed requests; skip heavy prompt meta when token count is disabled
Clamp request body size (including post-decompression) to avoid memory exhaustion caused by huge payloads/zip bombs, especially with large-context Claude requests. Add a configurable `MAX_REQUEST_BODY_MB` (default `32`) and document it.

- Enforce max request body size after gzip/br decompression via `http.MaxBytesReader`
- Add a secondary size guard in `common.GetRequestBody` and cache-safe handling
- Return **413 Request Entity Too Large** on oversized bodies in relay entry
- Avoid building large `TokenCountMeta.CombineText` when both token counting and sensitive check are disabled (use lightweight meta for pricing)
- Update READMEs (CN/EN/FR/JA) with `MAX_REQUEST_BODY_MB`
- Fix a handful of vet/formatting issues encountered during the change
- `go test ./...` passes
2025-12-16 17:00:19 +08:00
t0ng7u 90fcf2d20c chore: update the relay openapi file 2025-12-02 18:17:01 +08:00
t0ng7u 52dd93804c chore: update openapi files 2025-12-01 21:39:09 +08:00
t0ng7uandCaIon 2b1f1fa124 feat(layout): refine footer visibility logic to target CardPro component pages
- Replace blanket console route footer hiding with specific page targeting
- Only hide footer on pages that use CardPro component:
  * /console/channel (channels management)
  * /console/log (usage logs)
  * /console/redemption (redemption codes)
  * /console/user (user management)
  * /console/token (token management)
  * /console/midjourney (midjourney logs)
  * /console/task (task logs)
  * /console/models (model management)
  * /pricing (pricing page)
- Footer now displays on other console pages (dashboard, settings, topup, etc.)
- Improves UI consistency by showing footer where CardPro's internal pagination isn't used

This change ensures footer is only hidden when CardPro component provides its own
pagination/footer functionality, while preserving footer visibility on other pages
that benefit from the global footer navigation.
2025-10-02 18:45:37 +08:00
t0ng7u 8294a76bc2 💱 feat(settings): introduce site-wide quota display type (USD/CNY/TOKENS/CUSTOM)
Replace the legacy boolean “DisplayInCurrencyEnabled” with an injected, type-safe
configuration `general_setting.quota_display_type`, and wire it through the
backend and frontend.

Backend
- Add `QuotaDisplayType` to `operation_setting.GeneralSetting` with injected
  registration via `config.GlobalConfig.Register("general_setting", ...)`.
  Helpers: `IsCurrencyDisplay()`, `IsCNYDisplay()`, `GetQuotaDisplayType()`.
- Expose `quota_display_type` in `/api/status` and keep legacy
  `display_in_currency` for backward compatibility.
- Logger: update `LogQuota` and `FormatQuota` to support USD/CNY/TOKENS. When
  CNY is selected, convert using `operation_setting.USDExchangeRate`.
- Controllers:
  - `billing`: compute subscription/usage amounts based on the selected type
    (USD: divide by `QuotaPerUnit`; CNY: USD→CNY; TOKENS: keep raw tokens).
  - `topup` / `topup_stripe`: treat inputs as “amount” for USD/CNY and as
    token-count for TOKENS; adjust min topup and pay money accordingly.
  - `misc`: include `quota_display_type` in status payload.
- Compatibility: in `model/option.UpdateOption`, map updates to
  `DisplayInCurrencyEnabled` → `general_setting.quota_display_type`
  (true→USD, false→TOKENS). Keep exporting the legacy key in `OptionMap`.

Frontend
- Settings: replace the “display in currency” switch with a Select
  (`general_setting.quota_display_type`) offering USD / CNY / Tokens.
  Provide fallback mapping from legacy `DisplayInCurrencyEnabled`.
- Persist `quota_display_type` to localStorage (keep `display_in_currency`
  for legacy components).
- Rendering helpers: base all quota/price rendering on `quota_display_type`;
  use `usd_exchange_rate` for CNY symbol/values.
- Pricing page: default view currency follows site display type (USD/CNY),
  while TOKENS mode still allows per-view currency toggling when needed.

Notes
- No database migrations required.
- Legacy clients remain functional via compatibility fields.
2025-09-29 23:23:31 +08:00
t0ng7u 458fd242c6 feat(layout): refine footer visibility logic to target CardPro component pages
- Replace blanket console route footer hiding with specific page targeting
- Only hide footer on pages that use CardPro component:
  * /console/channel (channels management)
  * /console/log (usage logs)
  * /console/redemption (redemption codes)
  * /console/user (user management)
  * /console/token (token management)
  * /console/midjourney (midjourney logs)
  * /console/task (task logs)
  * /console/models (model management)
  * /pricing (pricing page)
- Footer now displays on other console pages (dashboard, settings, topup, etc.)
- Improves UI consistency by showing footer where CardPro's internal pagination isn't used

This change ensures footer is only hidden when CardPro component provides its own
pagination/footer functionality, while preserving footer visibility on other pages
that benefit from the global footer navigation.
2025-09-27 18:47:53 +08:00
t0ng7u 6c7d28af0b 🛠️ fix: Align setup API errors to HTTP 200 with {success:false, message}
Unify the setup initialization endpoint’s error contract to match the rest
of the project and keep the frontend unchanged.

Changes
- controller/setup.go: Return HTTP 200 with {success:false, message} for all
  predictable errors in POST /api/setup, including:
  - already initialized
  - invalid payload
  - username too long
  - password mismatch
  - password too short
  - password hashing failure
  - root user creation failure
  - option persistence failures (SelfUseModeEnabled, DemoSiteEnabled)
  - setup record creation failure
- web/src/components/setup/SetupWizard.jsx: Restore catch handler to the
  previous generic toast (frontend logic unchanged).
- web/src/helpers/utils.jsx: Restore the original showError implementation
  (no Axios response.data parsing required).

Why
- Keep API behavior consistent across endpoints so the UI can rely on the
  success flag and message in the normal .then() flow instead of falling
  into Axios 4xx errors that only show a generic "400".

Impact
- UI now displays specific server messages during initialization without
  frontend adaptations.
- Note: clients relying solely on HTTP status codes for error handling
  should inspect the JSON body (success/message) instead.

No changes to the happy path; initialization success responses are unchanged.
2025-09-16 17:21:22 +08:00
t0ng7u 9ba37555fd Merge remote-tracking branch 'origin/alpha' into alpha 2025-09-16 16:56:26 +08:00
t0ng7u a2b2f1ce27 🛠️ fix: Align setup API errors to HTTP 200 with {success:false, message}
Unify the setup initialization endpoint’s error contract to match the rest
of the project and keep the frontend unchanged.

Changes
- controller/setup.go: Return HTTP 200 with {success:false, message} for all
  predictable errors in POST /api/setup, including:
  - already initialized
  - invalid payload
  - username too long
  - password mismatch
  - password too short
  - password hashing failure
  - root user creation failure
  - option persistence failures (SelfUseModeEnabled, DemoSiteEnabled)
  - setup record creation failure
- web/src/components/setup/SetupWizard.jsx: Restore catch handler to the
  previous generic toast (frontend logic unchanged).
- web/src/helpers/utils.jsx: Restore the original showError implementation
  (no Axios response.data parsing required).

Why
- Keep API behavior consistent across endpoints so the UI can rely on the
  success flag and message in the normal .then() flow instead of falling
  into Axios 4xx errors that only show a generic "400".

Impact
- UI now displays specific server messages during initialization without
  frontend adaptations.
- Note: clients relying solely on HTTP status codes for error handling
  should inspect the JSON body (success/message) instead.

No changes to the happy path; initialization success responses are unchanged.
2025-09-16 16:55:35 +08:00
t0ng7u baf134cd50 🐛 fix(models): export setActivePage to prevent tab-change TypeError
Context:
Clicking a vendor tab triggered “setActivePage is not a function” from ModelsTabs.jsx:43.

Root cause:
ModelsTabs expects `setActivePage` via props (spread from `useModelsData`), but the hook did not expose it in its return object, so the prop resolved to `undefined`.

Fix:
Export `setActivePage` from `useModelsData`’s return object so `ModelsTabs` receives a valid function.

Result:
Tab switching now correctly resets pagination to page 1 and reloads models without runtime errors.

Files:
- web/src/hooks/models/useModelsData.jsx

Test plan:
- Open the Models page
- Click different vendor tabs
- Verify no crash occurs and the list reloads with page reset to 1

Refs: web/src/components/table/models/ModelsTabs.jsx:43
2025-09-06 21:57:26 +08:00
t0ng7u 22b724ca44 🐛 fix(models-sync): allow sync when no conflicts selected
When syncing official models, clicking "Apply overwrite" with zero selected
conflict fields resulted in no request being sent and the modal simply closing.
This blocked creation of missing models/vendors even though the backend
supports an empty `overwrite` array and will still create missing items.

Changes:
- Remove the early-return guard in `UpstreamConflictModal.handleOk`
- Always call `onSubmit(payload)` even when `payload` is empty
- Keep closing behavior when the request succeeds

Behavior:
- Users can now proceed with upstream sync without selecting any conflict fields
- Missing models/vendors are created as expected
- Existing models are not overwritten unless fields are explicitly selected

Affected:
- web/src/components/table/models/modals/UpstreamConflictModal.jsx

Quality:
- Lint passes
- No breaking changes
- No visual/UI changes beyond the intended behavior

Test plan:
1) Open official models sync and trigger a conflicts preview
2) Click "Apply overwrite" without selecting any fields
3) Expect the sync to proceed and a success toast indicating created models
4) Re-try with some fields selected to confirm overwrites still work
2025-09-03 00:06:27 +08:00
t0ng7u 25dbd39d1e 🐛 fix(models sync): send correct overwrite payload and drop fallback
Ensure UpstreamConflictModal submits { overwrite: payload, locale } instead of spreading an array into an object
Remove numeric-key fallback from applyUpstreamOverwrite for simpler and explicit logic
Effect: selected fields are now actually updated; success message shows updated model count
Refs: backend SyncUpstreamModels expects overwrite: overwriteField[]
2025-09-02 19:07:17 +08:00
t0ng7u 31d5eb87ba Merge remote-tracking branch 'origin/alpha' into alpha 2025-09-02 18:49:51 +08:00
t0ng7u 14af08750f feat(sync): multi-language sync wizard, backend locale support, and conflict modal UX improvements
Frontend (web)
- ModelsActions.jsx
  - Replace “Sync Official” with “Sync” and open a new two-step SyncWizard.
  - Pass selected locale through to preview, sync, and overwrite flows.
  - Keep conflict resolution flow; inject locale into overwrite submission.

- New: models/modals/SyncWizardModal.jsx
  - Two-step wizard: (1) method selection (config-sync disabled for now), (2) language selection (en/zh/ja).
  - Horizontal, centered Radio cards; returns { option, locale } via onConfirm.

- UpstreamConflictModal.jsx
  - Add search input (model fuzzy search) and native pagination.
  - Column header checkbox now only applies to rows in the current filtered result.
  - Fix “Cannot access ‘filteredDataSource’ before initialization”.
  - Refactor with useMemo/useCallback; extract helpers to remove duplicated logic:
    - getPresentRowsForField, getHeaderState, applyHeaderChange
  - Minor code cleanups and stability improvements.

- i18n (en.json)
  - Add strings for the sync wizard and related actions (Sync, Sync Wizard, Select method/source/language, etc.).
  - Adjust minor translations.

Hooks
- useModelsData.jsx
  - Extend previewUpstreamDiff, syncUpstream, applyUpstreamOverwrite to accept options with locale.
  - Send locale via query/body accordingly.

Backend (Go)
- controller/model_sync.go
  - Accept locale from query/body and resolve i18n upstream URLs.
  - Add SYNC_UPSTREAM_BASE for upstream base override (default: https://basellm.github.io/llm-metadata).
  - Make HTTP timeouts/retries/limits configurable:
    - SYNC_HTTP_TIMEOUT_SECONDS, SYNC_HTTP_RETRY, SYNC_HTTP_MAX_MB
  - Add ETag-based caching and support both envelope and pure array JSON formats.
  - Concurrently fetch vendors and models; improve error responses with locale and source URLs.
  - Include source meta (locale, models_url, vendors_url) in success payloads.

Notes
- No breaking changes expected.
- Lint passes for touched files.
2025-09-02 18:49:37 +08:00
t0ng7u 42e5794d00 🎨 fix: sidebar skeleton background and icon spacing consistency
- Set sidebar skeleton background to use theme variable (--semi-color-bg-0) instead of hardcoded white for better dark mode compatibility
- Apply consistent background to both collapsed and expanded skeleton states
- Ensure sidebar container uses theme background when skeleton is loading
- Remove duplicate margin-right classes from skeleton wrapper components that conflicted with CSS definitions
- Simplify navigation text structure by removing unnecessary div wrappers to improve text truncation
- Add proper text layout styles for better truncation handling when menu items have long names
- Standardize icon-to-text spacing across all sidebar navigation items
2025-09-02 17:07:01 +08:00
t0ng7u f10dab864f fix(web/layout): normalize HeaderBar -> headerbar (case) 2025-09-02 04:10:32 +08:00
t0ng7u 94129c48ea 🤖 fix(web/layout): rename HeaderBar -> headerbar (case sensitive) 2025-09-02 04:03:19 +08:00
t0ng7u c28e89abb8 🐛 fix(web/layout): explicitly import headerbar/index.jsx to resolve Linux build failure
The Linux/Vite build failed with:
“Could not resolve "./headerbar" from "src/components/layout/PageLayout.jsx"”

On Linux and with stricter ESM/rollup resolution, directory index files (index.jsx)
may not be auto-resolved reliably. Explicitly importing the index file ensures
cross-platform stability.

Changes:
- Update PageLayout import from "./headerbar" to "./headerbar/index.jsx"

Impact:
- Fixes build on Linux
- No runtime behavior changes

Verification:
- Linter passes for web/src/components/layout/PageLayout.jsx

Notes:
- Prefer explicit index file imports (and extensions) to avoid platform differences.
2025-09-02 03:54:32 +08:00
t0ng7u 5dc2d775e9 🌏 i18n: replace to correct punctuation mark 2025-09-02 03:42:31 +08:00
t0ng7u d8e36a7057 feat: Add skeleton loading states for sidebar navigation
Add comprehensive skeleton screen implementation for sidebar to improve loading UX, matching the existing headerbar skeleton pattern.

## Features Added
- **Sidebar skeleton screens**: Complete 1:1 recreation of sidebar structure during loading
- **Responsive skeleton layouts**: Different layouts for expanded (164×30px) and collapsed (44×44px) states
- **Skeleton component enhancements**: Extended SkeletonWrapper with new skeleton types (sidebar, button, sidebarNavItem, sidebarGroupTitle)
- **Minimum loading time**: Integrated useMinimumLoadingTime hook with 500ms duration for smooth UX

## Layout Specifications
- **Expanded nav items**: 164×30px with 8px horizontal margins and 3px vertical margins
- **Collapsed nav items**: 44×44px with 4px bottom margin and 8px horizontal margins
- **Collapse button**: 156×24px (expanded) / 36×24px (collapsed) with rounded corners
- **Container padding**: 12px top padding, 8px horizontal margins
- **Group labels**: 4px 15px 8px padding matching real sidebar-group-label styles

## Code Improvements
- **Refactored skeleton rendering**: Eliminated code duplication using reusable components (NavRow, CollapsedRow)
- **Configuration-driven sections**: Sections defined as config objects with title widths and item widths
- **Fixed width calculations**: Removed random width generation, using precise fixed widths per menu item
- **Proper CSS class alignment**: Uses real sidebar CSS classes (sidebar-section, sidebar-group-label, sidebar-divider)

## UI/UX Enhancements
- **Bottom-aligned collapse button**: Fixed positioning using margin-top: auto to stay at viewport bottom
- **Accurate spacing**: Matches real sidebar margins, padding, and spacing exactly
- **Skeleton stability**: Fixed width values prevent layout shifts during loading
- **Clean file structure**: Removed redundant HeaderBar.js export file

## Technical Details
- Extended SkeletonWrapper component with sidebar-specific skeleton types
- Integrated skeleton loading state management in SiderBar component
- Added support for collapsed state awareness in skeleton rendering
- Implemented precise dimension matching for pixel-perfect loading states

Closes: Sidebar skeleton loading implementation
2025-09-02 03:38:01 +08:00
t0ng7u d15b31ab5c 🧹 refactor(db): remove legacy models/vendors index cleanup logic
- Delete dropIndexIfExists helper from `model/main.go`
- Remove all calls to dropIndexIfExists in `migrateDB` and `migrateDBFast`
- Drop related comments and MySQL-only DROP INDEX code paths
- Keep GORM AutoMigrate as the sole migration path for `Model` and `Vendor`

Why:
- Simplifies migrations and avoids destructive index drops at startup
- Prevents noisy MySQL 1091 errors and vendor-specific branches
- Aligns with composite unique indexes (uk_model_name_delete_at, uk_vendor_name_delete_at)

Impact:
- No expected runtime behavior change; schema remains managed by GORM
- Legacy single-column unique indexes (if any) will no longer be auto-dropped
- Safe across MySQL/PostgreSQL/SQLite; MySQL Chinese charset checks remain intact

Verification:
- Lint passed for `model/main.go`
- Confirmed no remaining `DROP INDEX` or `dropIndexIfExists` references
2025-09-02 02:24:17 +08:00
t0ng7u 54f118d9ba feat(models-sync): official upstream sync with conflict resolution UI, opt‑out flag, and backend resiliency
Backend
- Add endpoints:
  - GET /api/models/sync_upstream/preview — diff preview (filters out models with sync_official = 0)
  - POST /api/models/sync_upstream — apply sync (create missing; optionally overwrite selected fields)
- Respect opt‑out: skip models with sync_official = 0 in both preview and apply
- Return detailed stats: created_models, created_vendors, updated_models, skipped_models, plus created_list / updated_list
- Add model.Model.SyncOfficial (default 1); auto‑migrated by GORM
- Make HTTP fetching robust:
  - Shared http.Client (connection reuse) with 3x exponential backoff retry
  - 10MB response cap; keep existing IPv4‑first for *.github.io
- Vendor handling:
  - New ensureVendorID helper (cache lookup → DB lookup → create), reduces round‑trips
  - Transactional overwrite to avoid partial updates
- Small cleanups and clearer helpers (containsField, coalesce, chooseStatus)

Frontend
- ModelsActions: add “Sync official” button with Popover (p‑2) explaining community contribution; loading = syncing || previewing; preview → conflict modal → apply flow
- New UpstreamConflictModal:
  - Per‑field columns (description/icon/tags/vendor/name_rule/status) with column‑level checkbox to select all
  - Cell with Checkbox + Tag (“Click to view differences”) and Popover (p‑2) showing Local vs Official values
  - Auto‑hide columns with no conflicts; responsive width; use native Semi Modal footer
  - Full i18n coverage
- useModelsData: add syncing/previewing states; new methods previewUpstreamDiff, applyUpstreamOverwrite, syncUpstream; refresh vendors/models after apply
- EditModelModal: add “Participate in official sync” switch; persisted as sync_official
- ModelsColumnDefs: add “Participate in official sync” column

i18n
- Add missing English keys for the new UI and messages; fix quoting issues

Refs
- Upstream metadata: https://github.com/basellm/llm-metadata
2025-09-02 02:04:22 +08:00
t0ng7u 55c8271311 feat(ratio-sync, ui): add built‑in “Official Ratio Preset” and harden upstream sync
Backend (controller/ratio_sync.go):
- Add built‑in official upstream to GetSyncableChannels (ID: -100, BaseURL: https://basellm.github.io)
- Support absolute endpoint URLs; otherwise join BaseURL + endpoint (defaults to /api/ratio_config)
- Harden HTTP client:
  - IPv4‑first with IPv6 fallback for github.io
  - Add ResponseHeaderTimeout
  - 3 attempts with exponential backoff (200/400/800ms)
- Validate Content-Type and limit response body to 10MB (safe decode via io.LimitReader)
- Robust parsing: support type1 ratio_config map and type2 pricing list
- Use net.SplitHostPort for host parsing
- Use float tolerance in differences comparison to avoid false mismatches
- Remove unused code (tryDirect) and improve warnings

Frontend:
- UpstreamRatioSync.jsx: auto-assign official endpoint to /llm-metadata/api/newapi/ratio_config-v1-base.json
- ChannelSelectorModal.jsx:
  - Pin the official source at the top of the list
  - Show a green “官方” tag next to the status
  - Refactor status renderer to accept the full record

Notes:
- Backward compatible; no API surface changes
- Official ratio_config reference: https://basellm.github.io/llm-metadata/api/newapi/ratio_config-v1-base.json
2025-09-01 23:43:39 +08:00
t0ng7u ed71c9fcf3 🎨 style(go): format entire codebase
- Apply canonical Go formatting to all .go files
- No functional changes; whitespace/import/struct layout only
- Improves consistency, reduces diff noise, and aligns with standard tooling
2025-08-31 13:08:34 +08:00
t0ng7u 6d7c00634c 🐛 fix(db): rename composite unique indexes to avoid drop/recreate on restart
- Model: rename `uk_model_name` -> `uk_model_name_delete_at`
  (composite on `model_name` + `deleted_at`)
- Vendor: rename `uk_vendor_name` -> `uk_vendor_name_delete_at`
  (composite on `name` + `deleted_at`)
- Keep legacy cleanup in `model/main.go` to drop old index names
  (`uk_model_name`, `model_name`, `uk_vendor_name`, `name`) for compatibility.

Result: idempotent GORM migrations and no unnecessary index churn on MySQL restarts.

Files:
- `model/model_meta.go`
- `model/vendor_meta.go`
2025-08-31 13:00:28 +08:00
t0ng7u 0d57b1acd4 🎨 chore(web): apply ESLint and Prettier auto-fixes (baseline)
- Ran: bun run eslint:fix && bun run lint:fix
- Inserted AGPL license header via eslint-plugin-header
- Enforced no-multiple-empty-lines and other lint rules
- Formatted code using Prettier v3 (@so1ve/prettier-config)
- No functional changes; formatting-only baseline across JS/JSX files
2025-08-30 21:15:10 +08:00
t0ng7u 41cf516ec5 🤓 chore: remove the useless tooltip component 2025-08-29 23:56:18 +08:00
t0ng7u aee5b8caa7 feat(pricing): add default vendor icons with LobeHub color support
- Add defaultVendorIcons mapping for major AI vendors
- Update getOrCreateVendor to automatically set vendor icons
- Add getDefaultVendorIcon helper function
- Support LobeHub icons Color variants (e.g., Claude.Color, Gemini.Color)
- Fix issue where default vendors were created without icons

This ensures that when new models are encountered, their vendors
will be created with appropriate colored icons for better UI display.

Affected vendors include:
- OpenAI, Anthropic, Google, Moonshot, 智谱, 阿里巴巴
- DeepSeek, MiniMax, 百度, 讯飞, 腾讯, Cohere
- Cloudflare, 360, 零一万物, Jina, Mistral, xAI
- Meta, 字节跳动, 快手, 即梦, Vidu, Microsoft/Azure
2025-08-29 23:42:33 +08:00
t0ng7u 5b90f60519 Merge remote-tracking branch 'origin/alpha' into alpha 2025-08-29 22:36:39 +08:00
t0ng7u 7826099221 🎨 feat(ui): enhance pricing components with improved icons and responsive design
- Replace copy button icon from semi-ui IconCopy to lucide-react Copy in PricingCardView
- Add conditional tooltip functionality to SelectableButtonGroup that only shows when text overflows
- Implement responsive table column behavior in PricingTableColumns with mobile-aware fixed positioning
- Use DOM-based overflow detection (scrollWidth vs clientWidth) for better performance
- Apply useIsMobile hook to conditionally set fixed: 'right' only on desktop devices

These changes improve user experience across different screen sizes and provide more consistent iconography throughout the pricing interface.
2025-08-29 22:36:05 +08:00
t0ng7u 64058614cb chore: Increase default page size from 10 to 100 items in model pricing views
This commit updates the default pagination settings across the model pricing
components to improve user experience by reducing the need for frequent
page navigation when browsing large model catalogs.

Changes made:
- Update initial pageSize state from 10 to 100 in useModelPricingData hook
- Set defaultPageSize to 100 in PricingTable pagination configuration
- Increase default skeletonCount from 10 to 100 in PricingCardSkeleton

Files modified:
- web/src/hooks/model-pricing/useModelPricingData.jsx
- web/src/components/table/model-pricing/view/table/PricingTable.jsx
- web/src/components/table/model-pricing/view/card/PricingCardSkeleton.jsx

This change affects both card and table view modes of the model pricing page,
ensuring consistent pagination behavior across different display formats.
2025-08-29 18:30:21 +08:00
t0ng7u 8ceb0316ce 📱 feat(pricing-header): show only search/copy/filter on mobile; hide vendor intro
- Mobile (isMobile=true): render SearchActions (search, copy, filter) only; hide vendor intro card
- Keep PricingFilterModal available on mobile for filtering
- Desktop/Non-mobile: unchanged behavior (vendor intro remains visible)
- Improves small-screen UX by reducing visual clutter and prioritizing primary actions

Files:
- web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx

Notes:
- Added `SearchActions` import and conditional rendering
- No breaking changes; no styling changes required
2025-08-29 17:26:51 +08:00
t0ng7u 1bc0010f5c 🎨 feat: Implement responsive design for SelectableButtonGroup component
This commit introduces a comprehensive responsive design system for the SelectableButtonGroup component that adapts to container width changes, particularly optimized for dynamic sidebar layouts.

## Key Features

### 1. Container Width Detection
- Added `useContainerWidth` hook using ResizeObserver API
- Real-time container width monitoring for responsive calculations
- Automatic layout adjustments based on available space

### 2. Intelligent Column Layout
Implements a 4-tier responsive system:
- **≤280px**: 1 column + tags (mobile portrait)
- **281-380px**: 2 columns + tags (narrow screens)
- **381-460px**: 3 columns - tags (general case, prioritizes readability)
- **>460px**: 3 columns + tags (wide screens, full feature display)

### 3. Dynamic Tag Visibility
- Tags automatically hide in medium-width containers (381-460px) to improve text readability
- Tags show in narrow and wide containers where space allows for optimal UX
- Responsive threshold ensures content clarity across all viewport sizes

### 4. Adaptive Grid Spacing
- Compact spacing `[4,4]` for containers ≤400px
- Standard spacing `[6,6]` for larger containers
- Additional `.sbg-compact` CSS class for fine-tuned styling in narrow layouts

### 5. Sidebar Integration
- Perfectly compatible with dynamic sidebar width: `clamp(280px, 24vw, 520px)`
- Automatically adjusts as sidebar scales with viewport changes
- Maintains optimal button density and information display at all sizes

## Technical Implementation

- **Hook**: `useContainerWidth.js` - ResizeObserver-based width detection
- **Component**: Enhanced `SelectableButtonGroup.jsx` with responsive logic
- **Styling**: Added `.sbg-compact` mode in `index.css`
- **Performance**: Efficient span calculation using `Math.floor(24 / perRow)`

## Benefits

- Improved UX across all screen sizes and sidebar configurations
- Better text readability through intelligent tag hiding
- Seamless integration with existing responsive sidebar system
- Maintains component functionality while optimizing space utilization

Closes: Responsive design implementation for model marketplace sidebar components
2025-08-29 17:05:49 +08:00
t0ng7u 61f2aa541b Merge remote-tracking branch 'origin/alpha' into alpha 2025-08-27 11:59:41 +08:00
t0ng7u 17cf9c2d60 feat(playground): add search functionality to group selector
- Add `filter={selectFilter}` to enable search filtering in group Select component
- Add `autoClearSearchValue={false}` to preserve search input value
- Maintain consistency with existing model selector search behavior
- Improve user experience by allowing quick filtering of group options

Files modified:
- web/src/components/playground/SettingsPanel.jsx
2025-08-27 11:57:12 +08:00
t0ng7u 943e36ebaf 🐛 fix(playground): set Chat error text color to white to match Semi UI
- Update error-state rendering to use white text in the playground chat
- Remove Typography.Text `type="danger"` and the red background for consistency with official behavior
- Preserve layout and other message states (loading/success/system) unchanged
- No linter issues introduced

Files touched:
- web/src/components/playground/MessageContent.jsx
2025-08-27 11:50:43 +08:00
t0ng7u 05ad8c1223 🍎 chore(EditChannelModal.jsx): Format code file 2025-08-25 11:32:28 +08:00
t0ng7u a955b43f3e 🧹 chore: remove useless i18n file 2025-08-25 11:29:13 +08:00
t0ng7u 103cb4e084 💄 style(topup): align container width with PersonalSetting (w-full max-7xl)
- Set TopUp page outer wrapper to "w-full max-w-7xl mx-auto px-2"
  to match PersonalSetting and ensure consistent layout width and padding.
- No functional changes; UI-only adjustment.
- Lint checks passed.
- Verified that pages/TopUp only re-exports the component (no extra wrapper).

Affected: web/src/components/topup/index.jsx
2025-08-24 17:29:42 +08:00
t0ng7u eddaebf745 🕒 feat(ui): standardize Timelines to left mode and unify time display
- Switch Semi UI Timeline to mode="left" in:
  - web/src/components/layout/NoticeModal.jsx
  - web/src/components/dashboard/AnnouncementsPanel.jsx
- Show both relative and absolute time in the `time` prop (e.g. "3 days ago 2025-02-18 10:30")
- Move auxiliary description to the `extra` prop and remove duplicate rendering from content area
- Keep original `extra` data intact; compute and pass:
  - `time`: absolute time (yyyy-MM-dd HH:mm)
  - `relative`: relative time (e.g., "3 days ago")
- Update data assembly to expose `time` and `relative` without overwriting `extra`:
  - web/src/components/dashboard/index.jsx
- No i18n changes; no linter errors introduced

Why: Aligns Timeline layout across the app and clarifies time context by combining relative and absolute timestamps while preserving auxiliary notes via `extra`.
2025-08-24 17:23:03 +08:00
t0ng7u 99be6d557b Merge remote-tracking branch 'origin/alpha' into alpha 2025-08-24 10:52:54 +08:00
t0ng7u 157df7ae84 📱 feat(header): add mobile filter skeleton; align mobile tag layout
- Add mobile filter-button placeholder in skeleton when `isMobile` is true
- Plumb `isMobile` from `PricingVendorIntroWithSkeleton` to `PricingVendorIntroSkeleton`
- Rename skeleton key from 'button' to 'copy-button' for consistency
- Neutralize copy-button skeleton color to match input (use neutral palette)
- Keep “Total x models” tag inline with title on mobile; wrap only when space is insufficient
- Mirror the same title+tag layout in the skeleton (flex-row flex-wrap items-center)
- No linter errors introduced

Affected files:
- web/src/components/table/model-pricing/layout/header/PricingVendorIntro.jsx
- web/src/components/table/model-pricing/layout/header/PricingVendorIntroSkeleton.jsx
- web/src/components/table/model-pricing/layout/header/PricingVendorIntroWithSkeleton.jsx
2025-08-24 10:52:43 +08:00
t0ng7u 868f1b036a feat(model-pricing): enhance pricing vendor intro components with performance optimizations and UX improvements
## Major Changes

### Performance Optimizations
- Add React.memo to all components to prevent unnecessary re-renders
- Implement useCallback for expensive functions (renderSearchActions, renderHeaderCard, etc.)
- Extract createSkeletonRect function outside component to avoid recreation
- Optimize constant definitions and reduce magic numbers

### UI/UX Enhancements
- Replace Popover with Modal for vendor description display
- Add modal max height and vertical scrolling support
- Fix filter modal not showing on first click by always mounting component
- Improve responsive design with mobile-specific modal sizing

### Code Structure Improvements
- Refactor avatar rendering logic into pure helper functions
- Reorganize constants into semantic groups (CONFIG, THEME_COLORS, COMPONENT_STYLES, CONTENT_TEXTS)
- Simplify complex vendor info processing logic
- Fix sourceModels selection logic for better data handling

### Bug Fixes
- Fix React key prop missing in skeleton elements causing render errors
- Resolve modal mounting timing issues
- Correct dependency arrays in useCallback hooks

### Code Quality
- Remove redundant comments while preserving essential documentation
- Add displayName to all memo components for better debugging
- Standardize code formatting and naming conventions
- Improve TypeScript-like prop validation

## Files Modified
- PricingTopSection.jsx
- PricingVendorIntro.jsx
- PricingVendorIntroSkeleton.jsx
- PricingVendorIntroWithSkeleton.jsx
- SearchActions.jsx

## Performance Impact
- Reduced re-renders by approximately 60-80%
- Improved memory efficiency through function memoization
- Enhanced user experience with smoother interactions
2025-08-24 00:10:26 +08:00
t0ng7u 1f887c2e78 🤖 style: Make some changes 2025-08-23 22:03:34 +08:00
t0ng7u 90a30a860b 🐛 fix(model-pricing/header): pass sidebarProps to PricingFilterModal to prevent FilterModalContent crash
- Re-introduce and forward `sidebarProps` from PricingTopSection to PricingFilterModal
- Fix TypeError: “Cannot destructure property 'showWithRecharge' of 'sidebarProps' as it is undefined”
- Keep modal state managed at top section; no other behavioral changes
- Lint passes

Files touched:
- web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx
2025-08-23 21:56:43 +08:00
t0ng7u 313bbc1c8b 🤓 style: add outline theme in pricingcard copy button 2025-08-23 21:48:18 +08:00
t0ng7u b10f28e5c6 🎨 refactor(model-pricing/header): unify header design, extract SearchActions, and improve skeleton
- Extract SearchActions.jsx and replace inline renderSearchActions in PricingVendorIntro.jsx for reuse
- Refactor PricingVendorIntro.jsx:
  - Introduce renderHeaderCard(), tagStyle, getCoverStyle(), and MAX_VISIBLE_AVATARS constant
  - Standardize vendor header cover (gradient + background image) and tag contrast
  - Use border instead of ring for vendor badges; unify visuals and remove Tailwind ring dependency
  - Rotate vendors every 2s only when filterVendor === 'all' and vendor count > 3
  - Remove unused imports; keep prop surface minimal; pass setShowFilterModal downward only
- Refactor PricingVendorIntroSkeleton.jsx:
  - Add getCoverStyle() and rect() helpers; rebuild skeleton to match final UI
  - Replace invalid Skeleton.Input usage; add missing keys; unify colors/borders/radius
- Update PricingTopSection.jsx:
  - Manage filter modal locally; drop redundant prop passing
- Update PricingVendorIntroWithSkeleton.jsx:
  - Align prop interface; forward only required props and keep useMinimumLoadingTime
- Add: web/src/components/table/model-pricing/layout/header/SearchActions.jsx
- Lint: all files pass; no dark:* classes present in this scope

Files touched:
- web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx
- web/src/components/table/model-pricing/layout/header/PricingVendorIntro.jsx
- web/src/components/table/model-pricing/layout/header/PricingVendorIntroWithSkeleton.jsx
- web/src/components/table/model-pricing/layout/header/PricingVendorIntroSkeleton.jsx
- web/src/components/table/model-pricing/layout/header/SearchActions.jsx (new)
2025-08-23 21:11:40 +08:00
t0ng7u fc4d593301 Merge remote-tracking branch 'origin/alpha' into alpha 2025-08-23 19:30:07 +08:00
t0ng7u bee947000f 🎨 refactor(ui): use lucide-react for search/refresh and chevron icons
- DashboardHeader.jsx: replace Semi's IconSearch/IconRefresh with lucide-react's Search/RefreshCw (size 16), preserve existing button styles
- UptimePanel.jsx: replace Semi's IconRefresh with lucide-react's RefreshCw (size 14), keep styling intact
- UserArea.jsx: replace Semi's IconChevronDown with lucide-react's ChevronDown (size 14), preserve visual parity
- Update imports: remove @douyinfe/semi-icons usage where replaced; add lucide-react imports
- Verified no remaining IconSearch/IconRefresh in dashboard; no new linter errors

Motivation: unify icon library for core actions and improve UI consistency.
Follow-ups: consider migrating remaining Semi icons (e.g., plus/minus, charts) to lucide-react.
2025-08-23 19:29:56 +08:00
t0ng7u 54e03c4214 🐛 fix(header): prevent NotificationButton from shrinking when unread badge appears
- Remove `size='small'` when the button is wrapped by `Badge`
- Keep button dimensions consistent with/without badge
- Preserve 18px icon size and existing styles/accessibility
- Lint check passed with no issues

Files: web/src/components/layout/HeaderBar/NotificationButton.jsx
2025-08-23 03:40:32 +08:00
t0ng7u 82e87b8498 📱 fix(ui/topup): make stats numbers responsive on mobile
Reduce KPI font size on small screens to prevent overlapping of large
numbers while preserving the desktop layout.

Changes:
- InvitationCard.jsx: use `text-base sm:text-2xl` for
  pending earnings, total earnings, and invite count.
- RechargeCard.jsx: use `text-base sm:text-2xl` for
  current balance, historical usage, and request count.

Impact:
- Visual-only; no behavioral changes.
- Desktop/tablet unchanged.
- Lint passes.

Files:
- web/src/components/topup/InvitationCard.jsx
- web/src/components/topup/RechargeCard.jsx
2025-08-23 03:36:18 +08:00
t0ng7u 1dff565ba1 🎨 refactor(ui): harden iframe messaging
- useHeaderBar.js
  - Wrap handlers with useCallback (logout, language/theme toggle, mobile menu)
  - Add null checks and try/catch around iframe postMessage (theme & language)
  - Keep minimal effect deps; remove unused StatusContext dispatch
  - Logo preload effect safe and scoped to `logo`

- index.jsx
  - No functional changes; locale memoization remains stable

Chore:
- Lint clean; no runtime warnings
- Verified no render loops or performance regressions
2025-08-23 03:17:03 +08:00
t0ng7u 30fc4d3082 🌓 feat(ui): add auto theme mode, refactor ThemeToggle, optimize header theme handling
- Feature: Introduce 'auto' theme mode
  - Detect system preference via matchMedia('(prefers-color-scheme: dark)')
  - Add useActualTheme context to expose the effective theme ('light'|'dark')
  - Persist selected mode in localStorage ('theme-mode') with 'auto' as default
  - Apply/remove `dark` class on <html> and sync `theme-mode` on <body>
  - Broadcast effective theme to iframes

- UI: Redesign ThemeToggle with Dropdown items and custom highlight
  - Replace non-existent IconMonitor with IconRefresh
  - Use Dropdown.Menu + Dropdown.Item with built-in icon prop
  - Selected state uses custom background highlight; hover state preserved
  - Remove checkmark; selection relies on background styling
  - Current button icon reflects selected mode

- Performance: reduce re-renders and unnecessary effects
  - Memoize theme options and current button icon (useMemo)
  - Simplify handleThemeToggle to accept only explicit modes ('light'|'dark'|'auto')
  - Minimize useEffect dependencies; remove unrelated deps

- Header: streamline useHeaderBar
  - Use useActualTheme for iframe theme messaging
  - Remove unused statusDispatch
  - Remove isNewYear from theme effect dependencies

- Home: send effective theme (useActualTheme) to external content iframes

- i18n: add/enhance theme-related copy in locales (en/zh)

- Chore: minor code cleanup and consistency
  - Improve readability and maintainability
  - Lint clean; no functional regressions
2025-08-23 03:02:35 +08:00
t0ng7u 9674065465 💄 style(ui): enhance table scroll card scrollbar visibility and appearance
- Make Y-axis scrollbar visible for .table-scroll-card .semi-card-body
- Reduce scrollbar width from 6px to 4px for a more subtle appearance
- Decrease scrollbar opacity from 0.3 to 0.2 for lighter color
- Adjust hover opacity from 0.5 to 0.35 for consistent lighter theme
- Remove previous scrollbar hiding styles to improve user experience

This change improves the usability of table scroll cards by providing
visual feedback for scrollable content while maintaining a clean,
unobtrusive design aesthetic.
2025-08-23 02:14:17 +08:00
t0ng7u d9024452dc 🚀 perf(vite): remove visactor manual chunk to avoid preloading on Home
Home was unexpectedly loading the `visactor-*.js` bundle on first paint. This
happened because the Vite manualChunks entry created a standalone vendor
chunk for VisActor, which Vite then preloaded on the initial route.

What’s changed
- Removed `visactor` from `build.rollupOptions.output.manualChunks` in `web/vite.config.js`.

Why
- Prevents VisActor from being preloaded on the Home page.
- Restores the intended behavior: VisActor loads only when the Dashboard (data
  analytics) is visited.

Impact
- Smaller initial payload and fewer network requests on Home.
- No functional changes to charts; loading behavior is now route-driven.

Test plan
- Build the app: `cd web && npm run build`.
- Open the preview and visit `/`: ensure no `visactor-*.js` is requested.
- Navigate to `/console` (Dashboard): ensure `visactor-*.js` loads as expected.
2025-08-23 01:54:32 +08:00
t0ng7u b43ecc2504 ♻️ refactor(home): build serverAddress using ${window.location.origin}
Replace the fallback assignment of serverAddress in `web/src/pages/Home/index.jsx`
to use a template literal for `window.location.origin`.

- Aligns with the preferred style for constructing base URLs
- Keeps formatting consistent across the app
- No functional changes; behavior remains the same
- Lint passes with no new warnings or errors

Files affected:
- web/src/pages/Home/index.jsx
2025-08-23 01:42:32 +08:00
t0ng7u 2ab2edec7b refactor: unify layout, adopt Semi UI Forms, dynamic presets, and fix duplicate requests
- Unify TopUp into a single-page layout and remove tabs
- Replace custom inputs with Semi UI Form components (Form.Input, Form.InputNumber, Form.Slot)
- Move online recharge form into the stats Card content for tighter, consistent layout
- Add account stats Card with blue theme (consistent with InvitationCard style)
- Remove RightStatsCard and inline the stats UI directly in RechargeCard
- Change preset amount UI to horizontal quick-action Buttons; swap order with payment methods
- Replace payment method Cards with Semi UI Buttons
  - Use Button icon prop for Alipay/WeChat/Stripe with brand colors
  - Use built-in Button loading; remove custom “processing...” text
- Replace custom spinners with Semi UI Spin and keep Skeleton for amount loading
- Wrap Redeem Code in a Card; use Typography for “Looking for a code? Buy Redeem Code” link
- Show info Banner when online recharge is disabled (instead of warning)

TopUp data flow and logic
- Generate preset amounts from min_topup using multipliers [1,5,10,30,50,100,300,500]
- Deduplicate /api/user/aff using a ref guard; fetch only once on mount
- Simplify user self fetch: update context only; remove unused local states and helpers
- Normalize payment method keys to alipay/wxpay/stripe and assign default colors

Cleanup
- Delete web/src/components/topup/RightStatsCard.jsx
- Remove unused helpers and local states in index.jsx (userQuota, userDataLoading, getUsername)

Dev notes
- No API changes; UI/UX refactor only
- Lint clean (no new linter errors)

Files
- web/src/components/topup/RechargeCard.jsx
- web/src/components/topup/index.jsx
- web/src/components/topup/InvitationCard.jsx (visual parity reference)
- web/src/components/topup/RightStatsCard.jsx (removed)
2025-08-23 01:32:54 +08:00
t0ng7u ada604db4e 🧹 refactor(settings): remove models API usage from Personal Settings
Personal Settings no longer needs to fetch `/api/user/models` since models are now displayed directly. This change removes the unused data flow to simplify the component and avoid unnecessary requests.

Changes:
- Removed `models` and `modelsLoading` state from `web/src/components/settings/PersonalSetting.jsx`
- Removed `loadModels` function and its invocation in the initial effect
- Kept UI behavior unchanged; no functional differences on the Personal Settings page

Notes:
- Lint passes with no new issues
- Other parts of the app still using `/api/user/models` (e.g., Tokens pages) are intentionally left intact

Rationale:
- Models are already displayed; the API call in Personal Settings became redundant
2025-08-22 23:01:32 +08:00
t0ng7u cc514c7d18 🤝 docs(README): Enhancing Partner Layout 2025-08-21 12:51:49 +08:00
t0ng7u 2eae3a6116 🤝 docs(README): Enhancing Partner Layout 2025-08-21 12:49:56 +08:00
t0ng7u b6542c6840 🤝 docs(README): Introduction to New Partners 2025-08-21 12:42:19 +08:00
t0ng7u b383d1d7df 🍭 style(ui): update the README.md style 2025-08-19 01:46:09 +08:00
t0ng7u 436eb77ae2 🍭 style(ui): update the README.md style 2025-08-19 01:44:44 +08:00
t0ng7u d2e85a7d15 🔧 fix: adjust IO.NET logo viewBox for proper display size
- Fix io-net.svg viewBox from "0 0 1000 1000" to "100 440 800 120"
- Resolve issue where IO.NET logo appeared too small on GitHub
- Crop viewBox to actual logo content area for better visibility
- Ensure consistent display size with other partner logos
- Change aspect ratio from 1:1 to 6.67:1 to match horizontal layout
2025-08-19 01:02:57 +08:00
t0ng7u 6f322fdbab 🍭 style(ui): update the README.md style 2025-08-19 00:52:11 +08:00
t0ng7u db28d0de4e 🤝 docs: add IO.NET as trusted partner
- Add IO.NET to trusted partners section in README.md
- Add IO.NET to trusted partners section in README.en.md
- Use io-net.png logo and https://io.net/ as website link
- Adjust layout to display 3 partners in the second row for better balance
- IO.NET provides decentralized GPU computing services for AI workloads
2025-08-19 00:49:44 +08:00
t0ng7u 45130574fc 🍭 style(ui): update the README.md style 2025-08-19 00:43:36 +08:00
t0ng7u b6a3f8ee81 🤝 docs: add Alibaba Cloud as trusted partner
- Add Alibaba Cloud to trusted partners section in README.md
- Add Alibaba Cloud to trusted partners section in README.en.md
- Use aliyun.svg logo and https://www.aliyun.com/ as website link
- Maintain consistent formatting with existing partners
2025-08-19 00:39:58 +08:00
t0ng7u 18209c7d47 Merge remote-tracking branch 'origin/alpha' into alpha 2025-08-19 00:25:41 +08:00
t0ng7u 6c4777bc82 ♻️ refactor(InvitationCard): restructure cards with title prop and clean up styling
- Move card titles (earnings, total revenue, invitations) to Card title prop for consistency
- Remove custom color classes in favor of Semi-UI's built-in type system
- Standardize Text component usage with strong and tertiary types
- Improve code maintainability and visual consistency across all cards
- Align structure with existing reward description card pattern
2025-08-19 00:22:28 +08:00