- 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
- 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.
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
- Rename React components/pages/utilities that contain JSX to `.jsx` across `web/src`
- Update import paths and re-exports to match new `.jsx` extensions
- Fix Vite entry by switching `web/index.html` from `/src/index.js` to `/src/index.jsx`
- Verified remaining `.js` files are plain JS (hooks/helpers/constants) and do not require JSX
- No runtime behavior changes; extension and reference alignment only
Context: Resolves the Vite pre-transform error caused by the stale `/src/index.js` entry after migrating to `.jsx`.
- Add RightStatsCard and place it in RechargeCard header
- Shows current balance, historical spend, and request count
- Mobile: stacks under title; three metrics split equally (flex-1); vertical dividers hidden on small screens
- Remove extra margins; small card styling
- RechargeCard
- Replace redeem code Input icon with Semi UI IconGift
- Style “Payable amount” number in red and bold; keep same style in confirm modal
- Always render payment methods as Cards (remove Button variant) with adaptive grid
- Use brand color icons: SiAlipay (#1677FF), SiWechat (#07C160), SiStripe (#635BFF)
- Replace Stripe icon with SiStripe
- Integrate RightStatsCard props; adjust header to flex-col on mobile and flex-row on desktop
- Hide Banner close button when online top-up is disabled (closeIcon={null})
- InvitationCard
- Simplify to match RechargeCard’s minimalist slate style
- Use Card title for “Rewards” and place content directly in body
- Improve link input and copy button; use Badge dots for bullet points
- TopUp index
- Remove separate right-column stats card; pass userState and renderQuota to RechargeCard
- Cleanup
- Lint passes; no functional changes to APIs or business logic
- Truncate long labels via pure CSS and always show full text in a Tooltip
- Ensure the right-side Tag is never truncated and remains fully visible
- Simplify implementation: remove overflow detection and ResizeObserver
- Use minimal markup with sbg-button/sbg-inner/sbg-label to enable shrinking
- Add global rules to allow `.semi-button-content` to shrink and ellipsize
Files:
- web/src/components/common/ui/SelectableButtonGroup.jsx
- web/src/index.css
No API changes; visuals improved and code complexity reduced.
Frontend
- Models table (model management):
- Render billing types with the same segmented list component (renderLimitedItems) used by tags and endpoints
- Display quota_types as an array with capped items (maxDisplay: 3) and graceful fallback for unknown types
- Pricing view (unchanged by request):
- Revert to single-value quota_type rendering and sorter
- Keep ratio display logic based on quota_type only
Files
- web/src/components/table/models/ModelsColumnDefs.js
- web/src/components/table/model-pricing/view/table/PricingTableColumns.js
Notes
- This commit only adjusts the model management UI rendering; pricing views remain as-is
* Added full GNU Affero General Public License v3 header at the top of `JSONEditor.js`.
* Removed unused `IconCode` and `IconRefresh` imports to eliminate dead code.
* Set `closeIcon={null}` and applied `!rounded-md` class for `Banner`, improving visual consistency and preventing unintended dismissal.
* Normalized whitespace and line-breaks for better readability and lint compliance.
- Why:
- Eliminate `gorm.io/datatypes` for a single field and fix scan errors when drivers return JSON as string.
- Prevent JSONEditor manual mode from locking on invalid JSON and from appending stray characters after “Fill Template”.
- What:
- Backend (`model/prefill_group.go`):
- Replaced `datatypes.JSON` with `JSONValue` (based on `json.RawMessage`) for `PrefillGroup.Items`.
- Implemented `sql.Scanner` and `driver.Valuer` to accept both `[]byte` and `string`.
- Implemented `MarshalJSON`/`UnmarshalJSON` to preserve raw JSON in API without base64.
- Converted comments to Chinese.
- Frontend (`web/src/components/common/ui/JSONEditor.js`):
- Added `manualText` buffer for manual mode to avoid input being overridden by external value.
- Only propagate `onChange` when manual text is valid JSON; otherwise show error but do not block typing.
- Safe manual-mode rendering: derive rows from `manualText` and avoid calling `split` on non-strings.
- Improved mode toggle: populate `manualText` from visual data; validate before switching back to visual.
- Fixed “Fill Template” to sync `manualText`, `jsonData`, and `onChange` to avoid stray trailing characters.
- Impact:
- Resolves: “unsupported Scan, storing driver.Value type string into type *json.RawMessage”.
- Resolves: `value.split is not a function` in manual mode.
- Resolves: extra `s` appended after inserting template.
- API shape and DB column type remain the same (`gorm:"type:json"`); no `go.mod` changes.
- Lints pass for modified files.
Files changed:
- model/prefill_group.go
- web/src/components/common/ui/JSONEditor.js
- Why:
- Avoid introducing `gorm.io/datatypes` for a single field.
- Align with existing pattern (`ChannelInfo`, `Properties`) using `Scanner`/`Valuer`.
- Fix runtime error when drivers return JSON as string.
- What:
- Introduced `JSONValue` (based on `json.RawMessage`) implementing `sql.Scanner` and `driver.Valuer`, with `MarshalJSON`/`UnmarshalJSON` to preserve raw JSON in API.
- Updated `PrefillGroup.Items` to use `JSONValue` with `gorm:"type:json"`.
- Localized comments in `model/prefill_group.go` to Chinese.
- Impact:
- Resolves “unsupported Scan, storing driver.Value type string into type *json.RawMessage”.
- Works with MySQL/Postgres/SQLite whether JSON is returned as `[]byte` or `string`.
- API and DB schema remain unchanged; no `go.mod` changes; lints pass.
Files changed:
- model/prefill_group.go
- Why
- Needed to separate help text from action buttons in JSONEditor for better layout and UX.
- Models table should robustly render both new object-based endpoint mappings and legacy arrays.
- Columns should re-render when vendor map changes.
- Minor import cleanups for consistency.
- What
- JSONEditor.js
- Added optional prop extraFooter to render content below the extraText divider.
- Kept extraText rendered via Divider; extraFooter appears on the next line for clear separation.
- EditModelModal.jsx
- Moved endpoint group buttons from extraText into extraFooter to display under the helper text.
- Kept merge-logic: group items are merged into current endpoints JSON with key override semantics.
- Consolidated lucide-react imports into a single line.
- ModelsColumnDefs.js
- Made endpoint renderer resilient:
- Supports object-based JSON (keys as endpoint types) and legacy array format.
- Displays keys/items as tags and limits the number shown; uses stringToColor for visual consistency.
- Consolidated Semi UI imports into a single line.
- ModelsTable.jsx
- Fixed columns memoization dependency to include vendorMap, ensuring re-render when vendor data changes.
- Notes
- Backward-compatible: extraFooter is additive; existing JSONEditor usage remains unchanged.
- No API changes to backend.
- No linter errors introduced.
- Files touched
- web/src/components/common/ui/JSONEditor.js
- web/src/components/table/models/modals/EditModelModal.jsx
- web/src/components/table/models/ModelsColumnDefs.js
- web/src/components/table/models/ModelsTable.jsx
- Impact
- Clearer UI for endpoint editing (buttons now below helper text).
- Correct endpoints display for object-based mappings in models list.
- More reliable reactivity when vendor data updates.
Backend (Go)
- Include custom endpoints in each model’s SupportedEndpointTypes by parsing Model.Endpoints (JSON) and appending keys alongside native endpoint types.
- Build a global supportedEndpointMap map[string]EndpointInfo{path, method} by:
- Seeding with native defaults.
- Overriding/adding from models.endpoints (accepts string path → default POST, or {path, method}).
- Expose supported_endpoint at the top level of /api/pricing (vendors-like), removing per-model duplication.
- Fix default path for EndpointTypeOpenAIResponse to /v1/responses.
- Keep concurrency/caching for pricing retrieval intact.
Frontend (React)
- Fetch supported_endpoint in useModelPricingData and propagate to PricingPage → ModelDetailSideSheet → ModelEndpoints.
- ModelEndpoints
- Resolve path+method via endpointMap; replace {model} with actual model name.
- Fix mobile visibility; always show path and HTTP method.
- JSONEditor
- Wrap with Form.Slot to inherit form layout; simplify visual styles.
- Use Tabs for “Visual” / “Manual” modes.
- Unify editors: key-value editor now supports nested JSON:
- “+” to convert a primitive into an object and add nested fields.
- Add “Convert to value” for two‑way toggle back from object.
- Stable key rename without reordering rows; new rows append at bottom.
- Use Row/Col grid for clean alignment; region editor uses Form.Slot + grid.
- Editing flows
- EditModelModal / EditPrefillGroupModal use JSONEditor (editorType='object') for endpoint mappings.
- PrefillGroupManagement renders endpoint group items by JSON keys.
Data expectations / compatibility
- models.endpoints should be a JSON object mapping endpoint type → string path or {path, method}. Strings default to POST.
- No schema changes; existing TEXT field continues to store JSON.
QA
- /api/pricing now returns custom endpoint types and global supported_endpoint.
- UI shows both native and custom endpoints; paths/methods render on mobile; nested editing works and preserves order.
- **Backend Changes:**
- Refactor pricing API to return separate vendors array with ID-based model references
- Remove redundant vendor_name/vendor_icon fields from pricing records, use vendor_id only
- Add vendor_description to pricing response for frontend display
- Maintain 1-minute cache protection for pricing endpoint security
- **Frontend Data Flow:**
- Update useModelPricingData hook to build vendorsMap from API response
- Enhance model records with vendor info during data processing
- Pass vendorsMap through component hierarchy for consistent vendor data access
- **UI Component Replacements:**
- Replace PricingCategories with PricingVendors component for vendor-based filtering
- Replace PricingCategoryIntro with PricingVendorIntro in header section
- Remove all model category related components and logic
- **Header Improvements:**
- Implement vendor intro with real backend data (name, icon, description)
- Add text collapsible feature (2-line limit with expand/collapse functionality)
- Support carousel animation for "All Vendors" view with vendor icon rotation
- **Model Detail Modal Enhancements:**
- Update ModelHeader to use real vendor icons via getLobeHubIcon()
- Move tags from header to ModelBasicInfo content area to avoid SideSheet title width constraints
- Display only custom tags from backend with stringToColor() for consistent styling
- Use Space component with wrap property for proper tag layout
- **Table View Optimizations:**
- Integrate RenderUtils for description and tags columns
- Implement renderLimitedItems for tags (max 3 visible, +x popover for overflow)
- Use renderDescription for text truncation with tooltip support
- **Filter Logic Updates:**
- Vendor filter shows disabled options instead of hiding when no models match
- Include "Unknown Vendor" category for models without vendor information
- Remove all hardcoded vendor descriptions, use real backend data
- **Code Quality:**
- Fix import paths after component relocation
- Remove unused model category utilities and hardcoded mappings
- Ensure consistent vendor data usage across all pricing views
- Maintain backward compatibility with existing pricing calculation logic
This refactor provides a more scalable vendor-based architecture while eliminating
data redundancy and improving user experience with real-time backend data integration.
Overview
• Re-designed `MissingModelsModal` to align with `ModelTestModal` and deliver a cleaner, paginated experience.
• Improved mobile responsiveness for action buttons in `ModelsActions`.
Details
1. MissingModelsModal.jsx
• Switched from `List` to `Table` for a more structured view.
• Added search bar with live keyword filtering and clear icon.
• Implemented pagination via `MODEL_TABLE_PAGE_SIZE`; auto-resets on search.
• Dynamic rendering: when no data, show unified Empty state without column header.
• Enhanced header layout with total-count subtitle and modal corner rounding.
• Removed unused `Typography.Text` import.
2. ModelsActions.jsx
• Set “Delete Selected Models” and “Missing Models” buttons to `flex-1 md:flex-initial`, placing them on the same row as “Add Model” on small screens.
Result
The “Missing Models” workflow now offers quicker discovery, a familiar table interface, and full mobile friendliness—without altering API behavior.
This commit introduces a unified, maintainable solution for all model-pricing filter buttons and removes redundant code.
Key points
• Added `usePricingFilterCounts` hook
- Centralises filtering logic and returns:
- `quotaTypeModels`, `endpointTypeModels`, `dynamicCategoryCounts`, `groupCountModels`
- Keeps internal helpers private (removed public `modelsAfterCategory`).
• Updated components to consume the new hook
- `PricingSidebar.jsx`
- `FilterModalContent.jsx`
• Improved button UI/UX
- `SelectableButtonGroup.jsx` now respects `item.disabled` and auto-disables when `tagCount === 0`.
- `PricingGroups.jsx` counts models per group (after all other filters) and disables groups with zero matches.
- `PricingEndpointTypes.jsx` enumerates all endpoint types, computes filtered counts, and disables entries with zero matches.
• Removed obsolete / duplicate calculations and comments to keep components lean.
The result is consistent, real-time tag counts across all filter groups, automatic disabling of unavailable options, and a single source of truth for filter computations, making future extensions straightforward.
- Remove K/M switch from model price column header in pricing table
- Add "Display in K units" option to pricing display settings panel
- Update parameter passing for tokenUnit and setTokenUnit across components:
- PricingDisplaySettings: Add tokenUnit toggle functionality
- PricingSidebar: Pass tokenUnit props to display settings
- PricingFilterModal: Include tokenUnit in mobile filter modal
- Enhance resetPricingFilters utility to reset token unit to default 'M'
- Clean up PricingTableColumns by removing unused setTokenUnit parameter
- Add English translation for "按K显示单位" as "Display in K units"
This change improves UX by consolidating all display-related controls
in the filter settings panel, making the interface more organized and
the token unit setting more discoverable alongside other display options.
Affected components:
- PricingTableColumns.js
- PricingDisplaySettings.jsx
- PricingSidebar.jsx
- PricingFilterModal.jsx
- PricingTable.jsx
- utils.js (resetPricingFilters)
- en.json (translations)
- Increase skeleton card count from 6 to 10 for better visual coverage
- Extend minimum skeleton display duration from 500ms to 1000ms for smoother UX
- Add circle shape to all pricing tags for consistent rounded design
- Apply circle styling to billing type, popularity, endpoint, and context tags
This commit improves the visual consistency and user experience of the pricing
card view by standardizing tag appearance and optimizing skeleton loading timing.
Add comprehensive loading state support with skeleton animations for the SelectableButtonGroup component, improving user experience during data loading.
Key Changes:
- Add loading prop to SelectableButtonGroup with minimum 500ms display duration
- Implement skeleton buttons with proper Semi-UI Skeleton wrapper and active animation
- Use fixed skeleton count (6 items) to prevent visual jumping during load transitions
- Pass loading state through all pricing filter components hierarchy:
- PricingSidebar and PricingFilterModal as container components
- PricingDisplaySettings, PricingCategories, PricingGroups, PricingQuotaTypes as filter components
Technical Details:
- Reference CardTable.js implementation for consistent skeleton UI patterns
- Add useEffect hook for 500ms minimum loading duration control
- Support both checkbox and regular button skeleton modes
- Maintain responsive layout compatibility (mobile/desktop)
- Add proper JSDoc parameter documentation for loading prop
Fixes:
- Prevent skeleton count sudden changes that caused visual discontinuity
- Ensure proper skeleton animation with Semi-UI active parameter
- Maintain consistent loading experience across all filter components
- Add withCheckbox prop to SelectableButtonGroup component for checkbox-prefixed buttons
- Support both single value and array activeValue for multi-selection scenarios
- Refactor PricingDisplaySettings to use consistent SelectableButtonGroup styling
- Replace Switch components with checkbox-enabled SelectableButtonGroup
- Replace Select dropdown with SelectableButtonGroup for currency selection
- Maintain unified UI/UX across all pricing filter components
- Add proper JSDoc documentation for new withCheckbox functionality
This improves visual consistency and provides a more cohesive user experience
in the model pricing filter interface.
* **PricingDisplaySettings.jsx**
• Extracted display settings (recharge price, currency, ratio toggle) from PricingSidebar
• Maintains complete styling and functionality as standalone component
* **SelectableButtonGroup.jsx**
• Added isMobile detection with conditional Col spans
• Mobile: `span={12}` (2 buttons per row) for better touch experience
• Desktop: preserved responsive grid `xs={24} sm={24} md={24} lg={12} xl={8}`
* **PricingSidebar.jsx**
• Updated imports to use new PricingDisplaySettings component
• Simplified component structure while preserving reset logic
These changes enhance code modularity and provide optimized mobile UX for filter button groups across the pricing interface.
- Add left-right pagination layout for desktop (total info on left, controls on right)
- Keep mobile layout centered with pagination controls only
- Implement proper i18n support for pagination text using react-i18next
- Add pagination translations for Chinese and English
- Standardize t function usage across all table components to use xxxData.t pattern
- Update CardPro footer layout to support justify-between on desktop
- Use CSS variable --semi-color-text-2 for consistent text styling
- Disable built-in Pagination showTotal to avoid duplication
Components updated:
- CardPro: Enhanced footer layout with responsive design
- createCardProPagination: Added i18n support and custom total text
- All table components: Unified t function usage pattern
- i18n files: Added pagination-related translations
The pagination now displays "Showing X to Y of Z items" on desktop
and maintains existing centered layout on mobile devices.
- Create new ScrollableContainer component in @/components/common/ui
- Provides automatic scroll detection and fade indicator
- Supports customizable height, styling, and event callbacks
- Includes comprehensive PropTypes for type safety
- Optimized with useCallback for better performance
- Refactor Detail page to use ScrollableContainer
- Remove manual scroll detection functions (checkApiScrollable, checkCardScrollable)
- Remove scroll event handlers (handleApiScroll, handleCardScroll)
- Remove scroll-related refs and state variables
- Replace all card scroll containers with ScrollableContainer component
* API info card
* System announcements card
* FAQ card
* Uptime monitoring card (both single and multi-tab scenarios)
- Benefits:
- Improved code reusability and maintainability
- Reduced code duplication across components
- Consistent scroll behavior throughout the application
- Easier to maintain and extend scroll functionality
Breaking changes: None
Migration: Existing scroll behavior is preserved with no user-facing changes
Summary
1. CardTable
• Added collapsible “Details / Collapse” section on mobile cards using Semi-UI Button + Collapsible with chevron icons.
• Integrated i18n (`useTranslation`) for the toggle labels.
• Restored original variable-width skeleton placeholders (50 % / 60 % / 70 % …) for more natural loading states.
2. UsageLogsColumnDefs
• Wrapped each `Tag` inside a native `<span>` when used as Tooltip trigger, removing `findDOMNode` deprecation warnings in React StrictMode.
Impact
• Cleaner, shorter rows on small screens with optional expansion.
• Fully translated UI controls.
• No more console noise in development & CI caused by StrictMode warnings.
• Accept an array for `actionsArea`, enabling multiple action blocks in one card
• Automatically insert a `Divider` between consecutive action blocks
• Add a `Divider` between `actionsArea` and `searchArea` when both exist
• Standardize `Divider` spacing by removing custom `margin` props
• Update `PropTypes`: `actionsArea` now supports `arrayOf(node)`
These changes improve visual separation and usability for complex table cards (e.g., Channels), making the UI cleaner and more consistent.
Previously the component unmounted the Modal as soon as `showModelTestModal` became
false, preventing Semi UI from running its cleanup routine. This left `body`
stuck with `overflow: hidden`, disabling page scroll after the dialog closed.
Changes made
– Removed the early `return null` and always keep the Modal mounted; visibility
is now controlled solely via the `visible` prop.
– Introduced a `hasChannel` guard to safely skip data processing/rendering when
no channel is selected.
– Added defensive checks for table data, footer and title to avoid undefined
access when the Modal is hidden.
This fix ensures that closing the test-model dialog correctly restores the
page’s scroll behaviour on both desktop and mobile.
This commit addresses an issue where RPM and TPM statistics did not load automatically on mobile devices.
Key changes
• Replaced conditional rendering with persistent rendering of `actionsArea` and `searchArea` in `CardPro` and applied the `hidden` CSS class when the sections should be concealed.
• Ensures internal hooks (e.g. `useUsageLogsData`) always run, allowing stats to be fetched without requiring the user to tap “Show Actions”.
• Maintains existing desktop behaviour; only mobile handling is affected.
Files updated
• `web/src/components/common/ui/CardPro.js`
Result
Mobile users now see up-to-date RPM/TPM (and other statistics) immediately after page load, improving usability and consistency with the desktop experience.
• Imported Semi-UI `Empty` component.
• Detect when `dataSource` is empty on mobile card view:
– Renders supplied `empty` placeholder (`tableProps.empty`) or a default `<Empty description="No Data" />`.
– Suppresses the mobile `Pagination` component to avoid blank pages.
• Pagination now renders only when `dataSource.length > 0`, preserving UX parity with desktop tables.
1. Add `web/src/components/common/ui/CardTable.js`
• Renders Semi-UI `Table` on desktop; on mobile, transforms each row into a rounded `Card`.
• Supports all standard `Table` props, including `rowSelection`, `scroll`, `pagination`, etc.
• Adds mobile pagination via Semi-UI `Pagination`.
• Implements a 500 ms minimum, active Skeleton loader that mimics real column layout (including operation-button row).
2. Replace legacy `Table` with `CardTable`
• Updated all major data pages: Channels, MJ-Logs, Redemptions, Tokens, Task-Logs, Usage-Logs and Users.
• Removed unused `Table` imports; kept behaviour on desktop unchanged.
3. UI polish
• Right-aligned operation buttons and sensitive fields (e.g., token keys) inside mobile cards.
• Improved Skeleton placeholders to better reflect actual UI hierarchy and preserve the active animation.
These changes dramatically improve the mobile experience while retaining full functionality on larger screens.
Summary
-------
Introduce a reusable compact-mode toggle component and greatly improve the CardPro header for small screens. Removes duplicated code, adds i18n support, and refines overall responsiveness.
Details
-------
🎨 UI / Components
• Create `common/ui/CompactModeToggle.js`
– Provides a single source of truth for switching between “Compact list” and “Adaptive list”
– Automatically hides itself on mobile devices via `useIsMobile()`
• Refactor table modules to use the new component
– `Users`, `Tokens`, `Redemptions`, `Channels`, `TaskLogs`, `MjLogs`, `UsageLogs`
– Deletes legacy in-file toggle buttons & reduces repetition
📱 CardPro improvements
• Hide `actionsArea` and `searchArea` on mobile, showing a single “Show Actions / Hide Actions” toggle button
• Add i18n: texts are now pulled from injected `t()` function (`显示操作项` / `隐藏操作项` etc.)
• Extend PropTypes to accept the `t` prop; supply a safe fallback
• Minor cleanup: remove legacy DOM observers & flag CSS, simplify logic
🔧 Integration
• Pass the `t` translation function to every `CardPro` usage across table pages
• Remove temporary custom class hooks after logic simplification
Benefits
--------
✓ Consistent, DRY compact-mode handling across the entire dashboard
✓ Better mobile experience with decluttered headers
✓ Full translation support for newly added strings
✓ Easier future maintenance (single compact toggle, unified CardPro API)