Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,24 @@ Always check `@gpustack/core-ui` first. Frequently reused:
- **Form fields**: `BaseSelect`, `Input` (labeled).
- **Text overflow**: `AutoTooltip`.
- **Icons**: `IconFont`.
- **Status display** (success/failed/processing/warning): `StatusTag`.
- **Tags & status** (4 variants): see the section below.
- **Permission-gated visibility**: `Access` / `useAccess`.
- **Request hooks**: `useRequest` / `useQueryData` / `useQueryDataList`.
- **Table data fetching**: `useTableFetch`.
- **Submit guard** (prevent double-submit): `useSubmitLock`.
- **Tabbed forms**: `ScrollSpyTabs`.

# Tags & status indicators

Four core-ui components cover tag/status display in tables and lists. Pick by **what the value means**, not by how it looks — don't reach for a generic antd `Tag`:

- **`StatusTag`** — semantic status with a **dynamic message/detail** (tooltip, download, extra content). Use when a row's status carries variable text, e.g. a failed job with an error message. Colors come from `StatusColorMap` (error/warning/transitioning/success/inactive).
- **`StatusDot`** — colored dot + short label, **no message**. Use for a plain status/type cell where the value is a fixed enum (e.g. an event-type or log column). Same `StatusColorMap` palette; `inactive` dot is quaternary. If the status needs dynamic text, use `StatusTag` instead.
- **`ThemeTag`** — a **standalone category label** (independent content, e.g. a permission scope or a model name). Default neutral; wraps antd `Tag`.
- **`TextAttribute`** — a small neutral pill that is a **subordinate annotation following a primary text** (e.g. `key-name [custom]`), not a standalone tag. Manages its own leading margin. Two variants: `filled` (default) and `outlined`. Ref the name column in `src/pages/api-keys/hooks/use-keys-columns.tsx`.

Rule of thumb: semantic + dynamic text → `StatusTag`; semantic + fixed enum → `StatusDot`; independent category → `ThemeTag`; annotation of nearby text → `TextAttribute`.

# Dynamic add-item form fields

When building a form, select the add-item component from the **shape of the field's data** (its schema). Match the schema, don't hand-roll a list UI:
Expand Down
3 changes: 2 additions & 1 deletion src/pages/_components/model-tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const ModelTag: React.FC<ModelTagProps> = ({ categoryKey, size }) => {
opacity: 1,
paddingInline: 8,
borderRadius: 12,
transform: 'scale(0.9)'
transform: 'scale(0.9)',
backgroundColor: 'transparent'
}}
color={config.color}
>
Expand Down
38 changes: 9 additions & 29 deletions src/pages/api-keys/hooks/use-keys-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
AutoTooltip,
DropdownButtons,
IconFont,
icons
icons,
TextAttribute,
ThemeTag
} from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { MenuProps, Tag, Tooltip } from 'antd';
import { MenuProps, Tooltip } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import dayjs from 'dayjs';
import { useMemo } from 'react';
Expand Down Expand Up @@ -139,23 +141,14 @@ const useModelsColumns = ({
key: 'name',
sorter: tableSorter(1),
render: (text: string, record: ListItem) => (
<span className="flex items-center">
<span className="flex items-center gap-8">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the documentation in CLAUDE.md, TextAttribute manages its own leading margin. Adding gap-8 to the parent span will result in double spacing when record.is_custom is true. Remove gap-8 from the parent container to let TextAttribute handle its own spacing.

Suggested change
<span className="flex items-center gap-8">
<span className="flex items-center">

<AutoTooltip ghost style={{ maxWidth: 400 }} title={text}>
<span className="text-primary">{text}</span>
</AutoTooltip>
{record.is_custom && (
<Tag
style={{
marginLeft: 8,
borderRadius: 12,
color: 'var(--ant-color-text-tertiary)',
borderColor: 'var(--ant-color-split)',
backgroundColor: 'transparent'
}}
variant="outlined"
>
<TextAttribute>
{intl.formatMessage({ id: 'playground.params.custom' })}
</Tag>
</TextAttribute>
)}
</span>
)
Expand Down Expand Up @@ -203,26 +196,13 @@ const useModelsColumns = ({
)}
{(record.scope?.includes('inference') ||
record.scope?.includes('*')) && (
<div
style={{
border: '1px solid var(--ant-color-split)',
color: 'var(--ant-color-text-tertiary)',
backgroundColor: 'var(--ant-color-fill-quaternary)',
borderRadius: 4,
fontSize: 13,
paddingInline: 8,
flexGrow: 0,
maxWidth: '100%',
width: 'max-content',
display: 'flex'
}}
>
<ThemeTag>
<AutoTooltip ghost>
{record.allowed_model_names?.length
? record.allowed_model_names.join(', ')
: intl.formatMessage({ id: 'apikeys.models.all' })}
</AutoTooltip>
</div>
</ThemeTag>
)}
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,13 @@ interface ColumnsHookProps {
const getKindLabel = (record: ListItem) => {
if (record.spec?.nfs)
return (
<ThemeTag
style={{ width: 'fit-content' }}
color="cyan"
icon={<FolderOutlined />}
>
<ThemeTag color="cyan" icon={<FolderOutlined />}>
{StorageTypeKindLabelMap.nfs}
</ThemeTag>
);
if (record.spec?.s3)
return (
<ThemeTag
style={{ width: 'fit-content' }}
color="green"
icon={<IconFont type="icon-database" />}
>
<ThemeTag color="green" icon={<IconFont type="icon-database" />}>
{StorageTypeKindLabelMap.s3}
</ThemeTag>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/llmodels/hooks/use-models-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ const useModelsColumns = ({
dataIndex: 'created_at',
key: 'created_at',
sorter: tableSorter(5),
span: spans.createTime,
width: 180,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Changing the span to a fixed width of 180 breaks the 24-unit grid layout of SealTable (where column spans must sum to 24). It also leaves createTime as dead code in SPANS_DEFAULT and SPANS_WITH_PLUGIN. Revert this back to span: spans.createTime to maintain grid alignment.

Suggested change
width: 180,
span: spans.createTime,

render: (text: number) => (
<AutoTooltip ghost>
{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}
Expand Down
22 changes: 14 additions & 8 deletions src/pages/usage/events-tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
* tab (date range + "filter by user" for managers); the resource-type /
* event-type selects ride in the bar's ``extra`` slot.
*/
import { SimpleSelect } from '@gpustack/core-ui';
import { type StatusType } from '@/config/types';
import { SimpleSelect, StatusDot } from '@gpustack/core-ui';
import { useAccess, useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { Input, Select, Table, Tag } from 'antd';
import { Input, Select, Table } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react';
Expand All @@ -23,11 +24,11 @@ import useQueryResourceEvents from './services/use-query-resource-events';
// Only these four are ever emitted (see resource_event_logger): create/delete
// + the metering-window pair. updated/attached/detached exist as enum values
// but are intentionally not recorded, so they're not offered as filters.
const EVENT_COLOR: Record<string, string> = {
created: 'green',
deleted: 'red',
phase_to_metered: 'blue',
phase_left_metered: 'orange'
const EVENT_STATUS: Record<string, StatusType> = {
created: 'success',
deleted: 'error',
phase_to_metered: 'transitioning',
phase_left_metered: 'warning'
};

// Humanize a failure phase enum for display, e.g. "SSHPublicKeyCreateFailed" →
Expand Down Expand Up @@ -186,7 +187,12 @@ const ResourceEvents: React.FC = () => {
dataIndex: 'event_type',
key: 'event_type',
render: (v: string) => (
<Tag color={EVENT_COLOR[v] ?? 'default'}>{EVENT_LABEL[v] ?? v}</Tag>
<StatusDot
statusValue={{
status: EVENT_STATUS[v] ?? 'inactive',
text: EVENT_LABEL[v] ?? v
}}
/>
),
width: 180
},
Expand Down
Loading