Compare commits

..
Author SHA1 Message Date
hibigandgithub-actions[bot] 9c6e1839fe chore: bump @gpustack/core-ui to v1.0.39 2026-07-17 08:54:14 +00:00
8 changed files with 63 additions and 47 deletions
+1 -12
View File
@@ -124,24 +124,13 @@ Always check `@gpustack/core-ui` first. Frequently reused:
- **Form fields**: `BaseSelect`, `Input` (labeled). - **Form fields**: `BaseSelect`, `Input` (labeled).
- **Text overflow**: `AutoTooltip`. - **Text overflow**: `AutoTooltip`.
- **Icons**: `IconFont`. - **Icons**: `IconFont`.
- **Tags & status** (4 variants): see the section below. - **Status display** (success/failed/processing/warning): `StatusTag`.
- **Permission-gated visibility**: `Access` / `useAccess`. - **Permission-gated visibility**: `Access` / `useAccess`.
- **Request hooks**: `useRequest` / `useQueryData` / `useQueryDataList`. - **Request hooks**: `useRequest` / `useQueryData` / `useQueryDataList`.
- **Table data fetching**: `useTableFetch`. - **Table data fetching**: `useTableFetch`.
- **Submit guard** (prevent double-submit): `useSubmitLock`. - **Submit guard** (prevent double-submit): `useSubmitLock`.
- **Tabbed forms**: `ScrollSpyTabs`. - **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 # 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: 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:
+1 -1
View File
@@ -17,7 +17,7 @@
"@ant-design/pro-components": "3.1.0-0", "@ant-design/pro-components": "3.1.0-0",
"@antv/g6": "^5.0.51", "@antv/g6": "^5.0.51",
"@braintree/sanitize-url": "^7.1.1", "@braintree/sanitize-url": "^7.1.1",
"@gpustack/core-ui": "^1.0.40", "@gpustack/core-ui": "^1.0.39",
"@huggingface/gguf": "^0.1.7", "@huggingface/gguf": "^0.1.7",
"@huggingface/hub": "^0.15.1", "@huggingface/hub": "^0.15.1",
"@huggingface/tasks": "^0.11.6", "@huggingface/tasks": "^0.11.6",
+12 -6
View File
@@ -24,8 +24,8 @@ importers:
specifier: ^7.1.1 specifier: ^7.1.1
version: 7.1.2 version: 7.1.2
'@gpustack/core-ui': '@gpustack/core-ui':
specifier: ^1.0.40 specifier: ^1.0.39
version: 1.0.40(czdvzceysqw7iv6pct2ucnb23e) version: 1.0.39(czdvzceysqw7iv6pct2ucnb23e)
'@huggingface/gguf': '@huggingface/gguf':
specifier: ^0.1.7 specifier: ^0.1.7
version: 0.1.18 version: 0.1.18
@@ -281,7 +281,7 @@ importers:
version: 9.1.7 version: 9.1.7
less-loader: less-loader:
specifier: ^12.2.0 specifier: ^12.2.0
version: 12.3.2(less@4.6.4)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)) version: 12.3.2(less@4.1.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
lint-staged: lint-staged:
specifier: ^15.2.2 specifier: ^15.2.2
version: 15.5.2 version: 15.5.2
@@ -1481,8 +1481,8 @@ packages:
resolution: {integrity: sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==, tarball: https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz} resolution: {integrity: sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==, tarball: https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz}
deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package
'@gpustack/core-ui@1.0.40': '@gpustack/core-ui@1.0.39':
resolution: {integrity: sha512-Ahd5GcKOulaU32gX//jIZJOWEgr2W1s6jS8UtRTsxZ53UPzBoKkfUnwyWkB2WBUSjvk4lntxvoC4kDefKXbd9w==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.40.tgz} resolution: {integrity: sha512-6NS0TDkBFHK6RipfvLwvDnLZlAoa/yFbHV+xeERAnUb+4t0mDyIQFqvYAeDgrUW/JIZ6WScrm31FDZe2sKiuaw==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.39.tgz}
peerDependencies: peerDependencies:
'@ant-design/icons': ^6.1.0 '@ant-design/icons': ^6.1.0
'@ant-design/pro-components': 3.1.0-0 '@ant-design/pro-components': 3.1.0-0
@@ -10802,7 +10802,7 @@ snapshots:
'@formatjs/intl-utils@2.3.0': {} '@formatjs/intl-utils@2.3.0': {}
'@gpustack/core-ui@1.0.40(czdvzceysqw7iv6pct2ucnb23e)': '@gpustack/core-ui@1.0.39(czdvzceysqw7iv6pct2ucnb23e)':
dependencies: dependencies:
'@ant-design/icons': 6.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ant-design/icons': 6.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-components': 3.1.0-0(antd@6.3.7(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ant-design/pro-components': 3.1.0-0(antd@6.3.7(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -16496,6 +16496,12 @@ snapshots:
less: 4.1.3 less: 4.1.3
webpack: 5.106.2(lightningcss@1.22.1)(postcss@8.5.14) webpack: 5.106.2(lightningcss@1.22.1)(postcss@8.5.14)
less-loader@12.3.2(less@4.1.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)):
dependencies:
less: 4.1.3
optionalDependencies:
webpack: 5.106.2(lightningcss@1.22.1)(postcss@8.5.14)
less-loader@12.3.2(less@4.6.4)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)): less-loader@12.3.2(less@4.6.4)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)):
dependencies: dependencies:
less: 4.6.4 less: 4.6.4
+1 -2
View File
@@ -60,8 +60,7 @@ const ModelTag: React.FC<ModelTagProps> = ({ categoryKey, size }) => {
opacity: 1, opacity: 1,
paddingInline: 8, paddingInline: 8,
borderRadius: 12, borderRadius: 12,
transform: 'scale(0.9)', transform: 'scale(0.9)'
backgroundColor: 'transparent'
}} }}
color={config.color} color={config.color}
> >
+29 -9
View File
@@ -6,12 +6,10 @@ import {
AutoTooltip, AutoTooltip,
DropdownButtons, DropdownButtons,
IconFont, IconFont,
icons, icons
TextAttribute,
ThemeTag
} from '@gpustack/core-ui'; } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { MenuProps, Tooltip } from 'antd'; import { MenuProps, Tag, Tooltip } from 'antd';
import { ColumnsType } from 'antd/lib/table'; import { ColumnsType } from 'antd/lib/table';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useMemo } from 'react'; import { useMemo } from 'react';
@@ -141,14 +139,23 @@ const useModelsColumns = ({
key: 'name', key: 'name',
sorter: tableSorter(1), sorter: tableSorter(1),
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex items-center gap-8"> <span className="flex items-center">
<AutoTooltip ghost style={{ maxWidth: 400 }} title={text}> <AutoTooltip ghost style={{ maxWidth: 400 }} title={text}>
<span className="text-primary">{text}</span> <span className="text-primary">{text}</span>
</AutoTooltip> </AutoTooltip>
{record.is_custom && ( {record.is_custom && (
<TextAttribute> <Tag
style={{
marginLeft: 8,
borderRadius: 12,
color: 'var(--ant-color-text-tertiary)',
borderColor: 'var(--ant-color-split)',
backgroundColor: 'transparent'
}}
variant="outlined"
>
{intl.formatMessage({ id: 'playground.params.custom' })} {intl.formatMessage({ id: 'playground.params.custom' })}
</TextAttribute> </Tag>
)} )}
</span> </span>
) )
@@ -196,13 +203,26 @@ const useModelsColumns = ({
)} )}
{(record.scope?.includes('inference') || {(record.scope?.includes('inference') ||
record.scope?.includes('*')) && ( record.scope?.includes('*')) && (
<ThemeTag> <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'
}}
>
<AutoTooltip ghost> <AutoTooltip ghost>
{record.allowed_model_names?.length {record.allowed_model_names?.length
? record.allowed_model_names.join(', ') ? record.allowed_model_names.join(', ')
: intl.formatMessage({ id: 'apikeys.models.all' })} : intl.formatMessage({ id: 'apikeys.models.all' })}
</AutoTooltip> </AutoTooltip>
</ThemeTag> </div>
)} )}
</div> </div>
) )
@@ -28,13 +28,21 @@ interface ColumnsHookProps {
const getKindLabel = (record: ListItem) => { const getKindLabel = (record: ListItem) => {
if (record.spec?.nfs) if (record.spec?.nfs)
return ( return (
<ThemeTag color="cyan" icon={<FolderOutlined />}> <ThemeTag
style={{ width: 'fit-content' }}
color="cyan"
icon={<FolderOutlined />}
>
{StorageTypeKindLabelMap.nfs} {StorageTypeKindLabelMap.nfs}
</ThemeTag> </ThemeTag>
); );
if (record.spec?.s3) if (record.spec?.s3)
return ( return (
<ThemeTag color="green" icon={<IconFont type="icon-database" />}> <ThemeTag
style={{ width: 'fit-content' }}
color="green"
icon={<IconFont type="icon-database" />}
>
{StorageTypeKindLabelMap.s3} {StorageTypeKindLabelMap.s3}
</ThemeTag> </ThemeTag>
); );
@@ -268,7 +268,7 @@ const useModelsColumns = ({
dataIndex: 'created_at', dataIndex: 'created_at',
key: 'created_at', key: 'created_at',
sorter: tableSorter(5), sorter: tableSorter(5),
width: 180, span: spans.createTime,
render: (text: number) => ( render: (text: number) => (
<AutoTooltip ghost> <AutoTooltip ghost>
{dayjs(text).format('YYYY-MM-DD HH:mm:ss')} {dayjs(text).format('YYYY-MM-DD HH:mm:ss')}
+8 -14
View File
@@ -8,11 +8,10 @@
* tab (date range + "filter by user" for managers); the resource-type / * tab (date range + "filter by user" for managers); the resource-type /
* event-type selects ride in the bar's ``extra`` slot. * event-type selects ride in the bar's ``extra`` slot.
*/ */
import { type StatusType } from '@/config/types'; import { SimpleSelect } from '@gpustack/core-ui';
import { SimpleSelect, StatusDot } from '@gpustack/core-ui';
import { useAccess, useIntl } from '@umijs/max'; import { useAccess, useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { Input, Select, Table } from 'antd'; import { Input, Select, Table, Tag } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
@@ -24,11 +23,11 @@ import useQueryResourceEvents from './services/use-query-resource-events';
// Only these four are ever emitted (see resource_event_logger): create/delete // Only these four are ever emitted (see resource_event_logger): create/delete
// + the metering-window pair. updated/attached/detached exist as enum values // + the metering-window pair. updated/attached/detached exist as enum values
// but are intentionally not recorded, so they're not offered as filters. // but are intentionally not recorded, so they're not offered as filters.
const EVENT_STATUS: Record<string, StatusType> = { const EVENT_COLOR: Record<string, string> = {
created: 'success', created: 'green',
deleted: 'error', deleted: 'red',
phase_to_metered: 'transitioning', phase_to_metered: 'blue',
phase_left_metered: 'warning' phase_left_metered: 'orange'
}; };
// Humanize a failure phase enum for display, e.g. "SSHPublicKeyCreateFailed" → // Humanize a failure phase enum for display, e.g. "SSHPublicKeyCreateFailed" →
@@ -187,12 +186,7 @@ const ResourceEvents: React.FC = () => {
dataIndex: 'event_type', dataIndex: 'event_type',
key: 'event_type', key: 'event_type',
render: (v: string) => ( render: (v: string) => (
<StatusDot <Tag color={EVENT_COLOR[v] ?? 'default'}>{EVENT_LABEL[v] ?? v}</Tag>
statusValue={{
status: EVENT_STATUS[v] ?? 'inactive',
text: EVENT_LABEL[v] ?? v
}}
/>
), ),
width: 180 width: 180
}, },