Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2767f883fc | ||
|
|
e98651da2b |
@@ -1,143 +0,0 @@
|
|||||||
---
|
|
||||||
name: create-crud-page
|
|
||||||
description: Scaffold a CRUD list/table page module in the gpustack-ui monorepo. Use when creating a new page module, building a list/table page, adding a create/edit drawer, or setting up the components/config/forms/hooks/services structure for a feature.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Create a CRUD Table Page
|
|
||||||
|
|
||||||
## Inputs (do this first)
|
|
||||||
|
|
||||||
- The argument passed to this skill is the **module name** (e.g. `/create-crud-page api-keys` → module `api-keys`). If no name was given, ask for it.
|
|
||||||
- **Always ask the user for the API documentation before generating any code**, even if a module name was provided:
|
|
||||||
|
|
||||||
> Where is the API documentation for this module? (OpenAPI/Swagger URL, schema file path, or an interface description)
|
|
||||||
|
|
||||||
- Wait for the answer, then read/fetch it. Derive `config/types.ts` (`FormData`, `ListItem`), the `services` request hooks, and form fields from that schema. Do not guess field names or endpoints — if the doc is missing details, ask.
|
|
||||||
|
|
||||||
- **Also ask which form layout to scaffold:**
|
|
||||||
|
|
||||||
> Should the form use tabs? (1) a plain form without tabs, or (2) a tabbed form
|
|
||||||
|
|
||||||
Choose the form structure in section 3 accordingly. Default to **no tabs** unless the user picks tabs or the schema clearly has many grouped sections.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Before anything: **reuse common `components`, `hooks`, and `utils` from `@gpustack/core-ui` whenever possible.**
|
|
||||||
|
|
||||||
Reference implementation for sections below: `src/pages/model-routes`.
|
|
||||||
|
|
||||||
## Module structure
|
|
||||||
|
|
||||||
Create the module under `src/pages/{module}`:
|
|
||||||
|
|
||||||
```text
|
|
||||||
{module}
|
|
||||||
├── components
|
|
||||||
├── config
|
|
||||||
├── forms
|
|
||||||
├── hooks
|
|
||||||
├── index.tsx
|
|
||||||
└── services
|
|
||||||
```
|
|
||||||
|
|
||||||
## 1. components
|
|
||||||
|
|
||||||
Module-specific components.
|
|
||||||
|
|
||||||
- The create/edit form component is named `add-xxx-modal.tsx` (repo convention — keep the `-modal` suffix even though it is built with `FormDrawer`).
|
|
||||||
- Use `FormDrawer` from `@gpustack/core-ui`.
|
|
||||||
- If a table cell's render logic/structure is complex, extract it into `xxx-cell.tsx`.
|
|
||||||
|
|
||||||
## 2. config
|
|
||||||
|
|
||||||
```text
|
|
||||||
config
|
|
||||||
├── index.ts # static configs & constants
|
|
||||||
└── types.ts # TypeScript types
|
|
||||||
```
|
|
||||||
|
|
||||||
Naming: form types → `FormData`; table list item types → `ListItem`.
|
|
||||||
|
|
||||||
## 3. forms
|
|
||||||
|
|
||||||
Main form component goes in `forms/index.tsx`.
|
|
||||||
|
|
||||||
- **Complex interactions** (Form.Item split across components): create a dedicated Form Context and wrap with `FormContext.Provider`.
|
|
||||||
- **Tab-based forms**: use `ScrollSpyTabs` from `@gpustack/core-ui`, wrapping the `Form` or `FormContext.Provider`. Do not use tabs unless necessary.
|
|
||||||
- **Required-field validation**: use `getRuleMessage` for standard `input`/`select`.
|
|
||||||
- For cascading selectors and async race protection, follow the **form-patterns** skill.
|
|
||||||
|
|
||||||
## 4. hooks
|
|
||||||
|
|
||||||
- Table columns → `use-xxx-columns.tsx`.
|
|
||||||
- Open/close hooks for `add-xxx-modal.tsx` → `use-create-xxx.ts`.
|
|
||||||
|
|
||||||
## 5. index.tsx (list page entry)
|
|
||||||
|
|
||||||
- **Data fetching**: `useTableFetch` from `@gpustack/core-ui`.
|
|
||||||
- **Data display**:
|
|
||||||
- Standard table → Ant Design `Table`. Ref: `src/pages/users/index.tsx`.
|
|
||||||
- Expandable/collapsible rows → `Table` from `@gpustack/core-ui`. Ref: `src/pages/model-routes/index.tsx`.
|
|
||||||
- Card-style lists → use `InfiniteScrollerProvider`. Ref: `src/pages/backends/index.tsx`.
|
|
||||||
|
|
||||||
## 6. services
|
|
||||||
|
|
||||||
`request` is injected via a provider — do **not** create a centralized `apis` directory like in `gpustack-ui`. Define request hooks directly in `services`.
|
|
||||||
|
|
||||||
- Use `useRequest` from `@gpustack/core-ui`, or `useQueryData` (same underlying method).
|
|
||||||
- Ref: `src/pages/gpu-service/storage-types/services/use-create-storage-type.ts`.
|
|
||||||
|
|
||||||
## 7. Empty data
|
|
||||||
|
|
||||||
- Page table lists → `NoResult`.
|
|
||||||
- Simple (non-page) tables → `Empty` with `image={Empty.PRESENTED_IMAGE_SIMPLE}`.
|
|
||||||
|
|
||||||
## Common UI conventions
|
|
||||||
|
|
||||||
- **Drawer/Modal open/close**: use `useBodyScroll` from `@gpustack/core-ui`. Ref: `src/pages/model-routes/hooks/use-create-route.ts`.
|
|
||||||
- **Status display** (success/failed/processing/warning): use `StatusTag`, never `Tag` from `antd` directly. See **Status display** below. Ref: `src/pages/llmodels/components/table-list.tsx`.
|
|
||||||
- **Permission-gated visibility**: use `Access` / `useAccess`. Ref: `src/pages/access/index.tsx`.
|
|
||||||
- **Styles**: avoid `styled-components` for complex/large styling. Prefer `createStyles` for component-scoped dynamic styles, CSS Modules (`xxx.module.less`) for static structured styles.
|
|
||||||
|
|
||||||
## Status display
|
|
||||||
|
|
||||||
Use `StatusTag` from `@gpustack/core-ui` for status display. Do not use `Tag` from `antd` directly.
|
|
||||||
|
|
||||||
1. Define the mapping from business status values to UI status values in the module's `config/index.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { StatusMaps } from '@/config';
|
|
||||||
import { StatusType } from '@/config/types';
|
|
||||||
|
|
||||||
export const XxxStatusValueMap = {
|
|
||||||
Running: 'running',
|
|
||||||
Pending: 'pending',
|
|
||||||
Failed: 'failed'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const XxxStatusLabelMap: Record<string, string> = {
|
|
||||||
[XxxStatusValueMap.Running]: 'Running',
|
|
||||||
[XxxStatusValueMap.Pending]: 'Pending',
|
|
||||||
[XxxStatusValueMap.Failed]: 'Failed'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const status: Record<string, StatusType> = {
|
|
||||||
[XxxStatusValueMap.Running]: StatusMaps.success,
|
|
||||||
[XxxStatusValueMap.Pending]: StatusMaps.transitioning,
|
|
||||||
[XxxStatusValueMap.Failed]: StatusMaps.error
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
2. In table columns, pass only the UI status value and display text required by `StatusTag`:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<StatusTag
|
|
||||||
statusValue={{
|
|
||||||
status: status[value],
|
|
||||||
text: XxxStatusLabelMap[value] || value,
|
|
||||||
message: record.state_message
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
`statusValue.status` must be a value mapped from `StatusMaps` (`success`, `transitioning`, `warning`, `error`, `inactive`). Do not pass business status values such as `running` or `pending` directly.
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
---
|
|
||||||
name: form-patterns
|
|
||||||
description: Patterns for forms with cascading/dependent selections in the gpustack-ui monorepo. Use when building a form where picking one field derives another (pick A → auto-pick B → write form), handling async option loading on modal open, or protecting against stale async results.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Form Patterns
|
|
||||||
|
|
||||||
Theme: keep cascading selections (pick A → derive B → write form) on a single, predictable path.
|
|
||||||
|
|
||||||
## Accessing the form: `form` vs `form.current`
|
|
||||||
|
|
||||||
This is **not** absolute — it depends on the call site:
|
|
||||||
|
|
||||||
- **Inside the form component** (`forms/index.tsx`), or anywhere holding a `Form.useForm()` instance → call it directly: `form.setFieldsValue(...)`.
|
|
||||||
- **In the outer Drawer/Modal wrapper** that opens the form and holds it via `ref={form}` (`const form = useRef(null)`), driven by an `open` prop → go through the ref: `form.current?.setFieldsValue(...)`.
|
|
||||||
|
|
||||||
The reference template below is written for the **Drawer-wrapper scenario** (it reacts to `open` and owns the shared `selection` state), so it uses `form.current?` throughout. If you lift this logic into the form body with a `useForm()` instance, drop the `.current`.
|
|
||||||
|
|
||||||
## 1. No fallback for derived selection
|
|
||||||
|
|
||||||
When "pick A then auto-pick B", match by rule and return `undefined` if no match — let the form field stay empty. Do **not** silently fall back to `list[0]`; a fallback hides data issues and fakes a valid selection.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const findB = (key, list) =>
|
|
||||||
key ? list.find((x) => x.key === key) : undefined;
|
|
||||||
```
|
|
||||||
|
|
||||||
For form fields, clear with `undefined`, not `''`. In Ant Design `undefined` restores the placeholder; `''` is treated as a real value.
|
|
||||||
|
|
||||||
## 2. Async race protection
|
|
||||||
|
|
||||||
For fetches triggered by a lifecycle entry (e.g. modal open), tag each invocation with a session ref. Discard stale results if the session rotated (modal closed and re-opened) before the response arrives.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const sessionRef = useRef(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) {
|
|
||||||
sessionRef.current += 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const session = ++sessionRef.current;
|
|
||||||
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
|
|
||||||
if (sessionRef.current !== session) return;
|
|
||||||
applySelection(as.items[0], findB(as.items[0].key, bs.items));
|
|
||||||
});
|
|
||||||
}, [open]);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Reference template
|
|
||||||
|
|
||||||
Two cascading selectors backed by a single shared state, with a single atomic write (state + form together):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type Selection = { a?: string; b?: number };
|
|
||||||
|
|
||||||
const [selection, setSelection] = useState<Selection>({});
|
|
||||||
const sessionRef = useRef(0);
|
|
||||||
const form = useRef<any>(null); // wrapper holds the form via <Form ref={form} /> — see "Accessing the form" above
|
|
||||||
|
|
||||||
const findB = (key, list) =>
|
|
||||||
key ? list.find((x) => x.key === key) : undefined;
|
|
||||||
|
|
||||||
// Single atomic write: state + form together.
|
|
||||||
const applySelection = (a, b) => {
|
|
||||||
setSelection({ a: a.name, b: b?.id });
|
|
||||||
form.current?.setFieldsValue({
|
|
||||||
field: b?.field,
|
|
||||||
spec: { ...currentSpec, ...b?.spec }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Trigger 1: modal opened
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) {
|
|
||||||
sessionRef.current++;
|
|
||||||
setSelection({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const session = ++sessionRef.current;
|
|
||||||
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
|
|
||||||
if (sessionRef.current !== session) return;
|
|
||||||
const first = as.items[0];
|
|
||||||
applySelection(first, findB(first.key, bs.items));
|
|
||||||
});
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
// Trigger 2: user picks A
|
|
||||||
const handleAChange = (a) => {
|
|
||||||
applySelection(a, findB(a.key, listB));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Trigger 3: user picks B
|
|
||||||
const handleBChange = (b) => {
|
|
||||||
setSelection((prev) => ({ ...prev, b: b.id }));
|
|
||||||
form.current?.setFieldsValue({ ...b.fields });
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Related
|
|
||||||
|
|
||||||
- Module/file structure for forms lives in the **create-crud-page** skill (section 3).
|
|
||||||
- Required-field validation: use `getRuleMessage`.
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: {}
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
@@ -118,20 +117,3 @@ jobs:
|
|||||||
remote_path: releases/${{ steps.version.outputs.version }}.tar.gz
|
remote_path: releases/${{ steps.version.outputs.version }}.tar.gz
|
||||||
accelerate: true
|
accelerate: true
|
||||||
clean: false
|
clean: false
|
||||||
|
|
||||||
trigger-backend:
|
|
||||||
needs: build-publish
|
|
||||||
if: (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) || github.event_name == 'workflow_dispatch'
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Dispatch backend build
|
|
||||||
uses: peter-evans/repository-dispatch@v3
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.DISPATCH_PAT }}
|
|
||||||
repository: gpustack/gpustack
|
|
||||||
event-type: ui-built
|
|
||||||
client-payload: |
|
|
||||||
{
|
|
||||||
"ref": "${{ github.ref }}",
|
|
||||||
"sha": "${{ github.sha }}"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,6 +13,6 @@
|
|||||||
.swc
|
.swc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
.claude/settings.local.json
|
.claude
|
||||||
/dist.zip
|
/dist.zip
|
||||||
.cache
|
.cache
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
# React State and Request Patterns
|
||||||
|
|
||||||
|
These guidelines define preferred patterns for request handling, state updates, and side-effect management in React applications.
|
||||||
|
|
||||||
|
The primary goal is to keep data flow explicit, predictable, maintainable, and performant while avoiding unnecessary rerenders and effect-driven logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Avoid Effect-Driven Requests
|
||||||
|
|
||||||
|
Do not use request functions themselves as dependencies in `useEffect`.
|
||||||
|
|
||||||
|
Avoid patterns like:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [fetchData]);
|
||||||
|
```
|
||||||
|
|
||||||
|
Requests should be triggered explicitly by user actions or lifecycle entry points.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Form Requests Should Be Action-Driven
|
||||||
|
|
||||||
|
For form-related requests (such as loading `Select` options):
|
||||||
|
|
||||||
|
- Fetch data when the form is opened for the first time.
|
||||||
|
- If later requests depend on user interactions, trigger them directly inside the interaction handler.
|
||||||
|
- Do not rely on `useEffect` dependency changes to trigger requests.
|
||||||
|
|
||||||
|
Recommended:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const handleOnChange = (value) => {
|
||||||
|
fetchData(value);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Avoid:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData(value);
|
||||||
|
}, [value]);
|
||||||
|
```
|
||||||
|
|
||||||
|
The action itself should control the request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Update Related States Together
|
||||||
|
|
||||||
|
If a single action updates multiple related states:
|
||||||
|
|
||||||
|
- Do not synchronize them through `useEffect`
|
||||||
|
- Do not derive them indirectly through `useMemo`
|
||||||
|
|
||||||
|
Instead, update all related states directly inside the action handler.
|
||||||
|
|
||||||
|
Recommended:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const handleOnChange = (value) => {
|
||||||
|
setState1(...);
|
||||||
|
setState2(...);
|
||||||
|
buildState(...);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Avoid implicit state synchronization chains.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Group Strongly Related State
|
||||||
|
|
||||||
|
If multiple states are always updated together:
|
||||||
|
|
||||||
|
- Do not split them into multiple `useState` calls.
|
||||||
|
- Prefer a single state object.
|
||||||
|
|
||||||
|
Recommended:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const [state, setState] = useState({
|
||||||
|
state1: ...,
|
||||||
|
state2: ...,
|
||||||
|
state3: ...,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This reduces unnecessary rerenders and keeps state transitions predictable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Prefer Explicit State Flow
|
||||||
|
|
||||||
|
Avoid chaining business logic through multiple `useEffect` hooks.
|
||||||
|
|
||||||
|
Keep:
|
||||||
|
|
||||||
|
- request execution
|
||||||
|
- state updates
|
||||||
|
- derived calculations
|
||||||
|
|
||||||
|
close to the triggering action whenever possible.
|
||||||
|
|
||||||
|
Prefer:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const handleAction = () => {
|
||||||
|
fetchData();
|
||||||
|
setTableData(...);
|
||||||
|
setSelectedRow(...);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Over:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
useEffect(() => {
|
||||||
|
buildTable();
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateSelection();
|
||||||
|
}, [tableData]);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Avoid Premature Memoization
|
||||||
|
|
||||||
|
Do not use `useMemo` or `useCallback` unless there is a confirmed rendering or computation bottleneck.
|
||||||
|
|
||||||
|
Overusing memoization:
|
||||||
|
|
||||||
|
- increases complexity
|
||||||
|
- makes state flow harder to understand
|
||||||
|
- may introduce stale dependency issues
|
||||||
|
|
||||||
|
Prefer simple and explicit logic first.
|
||||||
|
|
||||||
|
Optimize only when necessary.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Keep Request Logic Predictable
|
||||||
|
|
||||||
|
A user interaction should clearly show:
|
||||||
|
|
||||||
|
- what request is triggered
|
||||||
|
- which states are updated
|
||||||
|
- how the UI changes
|
||||||
|
|
||||||
|
Avoid indirect update chains caused by dependency-driven effects.
|
||||||
|
|
||||||
|
The code should make the request and update flow easy to trace.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Prefer Action-Driven Architecture
|
||||||
|
|
||||||
|
Prefer:
|
||||||
|
|
||||||
|
- action-driven updates
|
||||||
|
- explicit handlers
|
||||||
|
- localized state transitions
|
||||||
|
|
||||||
|
Over:
|
||||||
|
|
||||||
|
- effect-driven synchronization
|
||||||
|
- cross-hook implicit updates
|
||||||
|
- reactive chains between states
|
||||||
|
|
||||||
|
The triggering action should remain the primary source of truth for UI updates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Form
|
||||||
|
|
||||||
|
Form-specific patterns that build on the rules above. The theme: keep cascading selections (pick A → derive B → write form) on a single, predictable path.
|
||||||
|
|
||||||
|
## 1. No Fallback for Derived Selection
|
||||||
|
|
||||||
|
When "pick A then auto-pick B", match by rule and return `undefined` if no match — let the corresponding form field stay empty.
|
||||||
|
|
||||||
|
Do not silently fall back to `list[0]` or another default. A fallback hides data issues and tricks the user into thinking they have a valid selection.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const findB = (key, list) =>
|
||||||
|
key ? list.find((x) => x.key === key) : undefined;
|
||||||
|
```
|
||||||
|
|
||||||
|
For form fields, prefer clearing with `undefined` over `''`. With Ant Design, `undefined` restores the placeholder; `''` is treated as a real value.
|
||||||
|
|
||||||
|
## 2. Async Race Protection
|
||||||
|
|
||||||
|
For fetches triggered by a lifecycle entry (e.g., modal open), tag each invocation with a session ref. Discard stale results if the session has rotated (the modal was closed and re-opened) by the time the response arrives.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const sessionRef = useRef(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) {
|
||||||
|
sessionRef.current += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const session = ++sessionRef.current;
|
||||||
|
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
|
||||||
|
if (sessionRef.current !== session) return;
|
||||||
|
applySelection(as.items[0], findB(as.items[0].key, bs.items));
|
||||||
|
});
|
||||||
|
}, [open]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Reference Template
|
||||||
|
|
||||||
|
A typical form with two cascading selectors backed by a single shared state:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Selection = { a?: string; b?: number };
|
||||||
|
|
||||||
|
const [selection, setSelection] = useState<Selection>({});
|
||||||
|
const sessionRef = useRef(0);
|
||||||
|
|
||||||
|
const findB = (key, list) =>
|
||||||
|
key ? list.find((x) => x.key === key) : undefined;
|
||||||
|
|
||||||
|
// Single atomic write: state + form together.
|
||||||
|
const applySelection = (a, b) => {
|
||||||
|
setSelection({ a: a.name, b: b?.id });
|
||||||
|
form.current?.setFieldsValue({
|
||||||
|
field: b?.field,
|
||||||
|
spec: { ...currentSpec, ...b?.spec }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trigger 1: modal opened
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) {
|
||||||
|
sessionRef.current++;
|
||||||
|
setSelection({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const session = ++sessionRef.current;
|
||||||
|
Promise.all([fetchA(), fetchB()]).then(([as, bs]) => {
|
||||||
|
if (sessionRef.current !== session) return;
|
||||||
|
const first = as.items[0];
|
||||||
|
applySelection(first, findB(first.key, bs.items));
|
||||||
|
});
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
// Trigger 2: user picks A
|
||||||
|
const handleAChange = (a) => {
|
||||||
|
applySelection(a, findB(a.key, listB));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trigger 3: user picks B
|
||||||
|
const handleBChange = (b) => {
|
||||||
|
setSelection((prev) => ({ ...prev, b: b.id }));
|
||||||
|
form.current?.setFieldsValue({ ...b.fields });
|
||||||
|
};
|
||||||
|
```
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# Agent Instructions
|
|
||||||
|
|
||||||
This project keeps a single source of truth for agent/contributor conventions in [`CLAUDE.md`](./CLAUDE.md). **Read [`CLAUDE.md`](./CLAUDE.md) and follow it.**
|
|
||||||
|
|
||||||
@CLAUDE.md
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
# Repo
|
|
||||||
|
|
||||||
This is the **open source UI** (`gpustack-ui`). Common `components`, `hooks`, and `utils` are published as `@gpustack/core-ui` and consumed throughout `src`.
|
|
||||||
|
|
||||||
**Always prioritize reusing common `components`, `hooks`, and `utils` from `@gpustack/core-ui`.**
|
|
||||||
|
|
||||||
Task-specific conventions live in skills: use **create-crud-page** when building a page module, **form-patterns** when building cascading/dependent forms.
|
|
||||||
|
|
||||||
# React State and Request Patterns
|
|
||||||
|
|
||||||
Keep data flow explicit, predictable, and performant. The triggering **action** is the source of truth for UI updates — not effect-driven synchronization.
|
|
||||||
|
|
||||||
## 1. Avoid effect-driven requests
|
|
||||||
|
|
||||||
Do not use request functions as `useEffect` dependencies. Trigger requests explicitly from user actions or lifecycle entry points.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Avoid
|
|
||||||
useEffect(() => {
|
|
||||||
fetchData();
|
|
||||||
}, [fetchData]);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. Form requests should be action-driven
|
|
||||||
|
|
||||||
- Fetch form data (e.g. `Select` options) when the form first opens.
|
|
||||||
- If later requests depend on interactions, trigger them inside the interaction handler.
|
|
||||||
- Do not rely on `useEffect` dependency changes.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Recommended
|
|
||||||
const handleOnChange = (value) => {
|
|
||||||
fetchData(value);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Update related states together
|
|
||||||
|
|
||||||
When one action updates multiple related states, update them all directly in the handler. Do not sync via `useEffect` or derive indirectly via `useMemo`.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const handleOnChange = (value) => {
|
|
||||||
setState1(...);
|
|
||||||
setState2(...);
|
|
||||||
buildState(...);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. Group strongly related state
|
|
||||||
|
|
||||||
If multiple states always update together, use a single state object instead of multiple `useState` calls — fewer rerenders, more predictable transitions.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const [state, setState] = useState({ state1: ..., state2: ..., state3: ... });
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Prefer explicit state flow
|
|
||||||
|
|
||||||
Keep request execution, state updates, and derived calculations close to the triggering action. Avoid chaining business logic through multiple `useEffect` hooks.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Prefer
|
|
||||||
const handleAction = () => {
|
|
||||||
fetchData();
|
|
||||||
setTableData(...);
|
|
||||||
setSelectedRow(...);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. Avoid premature memoization
|
|
||||||
|
|
||||||
Do not use `useMemo` / `useCallback` unless there is a confirmed bottleneck. Overuse adds complexity, obscures state flow, and risks stale dependencies. Optimize only when necessary.
|
|
||||||
|
|
||||||
## 7. Keep request logic predictable
|
|
||||||
|
|
||||||
A user interaction should clearly show: what request fires, which states update, how the UI changes. Avoid indirect update chains from dependency-driven effects.
|
|
||||||
|
|
||||||
## 8. Prefer action-driven architecture
|
|
||||||
|
|
||||||
Prefer action-driven updates, explicit handlers, and localized state transitions over effect-driven synchronization, cross-hook implicit updates, and reactive chains between states.
|
|
||||||
|
|
||||||
# Styles
|
|
||||||
|
|
||||||
**Future direction (apply to all new code):** avoid `styled-components`. Prefer:
|
|
||||||
|
|
||||||
1. `createStyles` for component-scoped dynamic styles
|
|
||||||
2. CSS Modules (`xxx.module.less`) for structured static styles
|
|
||||||
|
|
||||||
Existing `styled-components` usage is legacy tech debt — do not migrate it wholesale, but do not add new `styled-components` either. Theme tokens (`var(--ant-color-*)`) work in all three approaches.
|
|
||||||
|
|
||||||
## Layout
|
|
||||||
|
|
||||||
Compose layout with Ant components, not hand-written `display: flex`.
|
|
||||||
|
|
||||||
- **1D flex** (row/column with `gap`, `align`, `justify`) → `Flex`. Do not write raw `display: flex` in new code.
|
|
||||||
- **Inline sequence** of a few elements with uniform spacing → `Space`.
|
|
||||||
- **Page/grid columns** → `Row` / `Col`.
|
|
||||||
|
|
||||||
Drive spacing with the theme scale (`Flex`/`Space` `gap`, or `var(--ant-*)` spacing tokens), not scattered `px` literals.
|
|
||||||
|
|
||||||
# Naming conventions
|
|
||||||
|
|
||||||
A page module lives under `src/pages/{module}` with this sub-structure: `components/`, `config/`, `forms/`, `hooks/`, `services/`, `index.tsx`. File naming:
|
|
||||||
|
|
||||||
- **Create/edit modal**: `add-{feature}-modal.tsx` (keep the `-modal` suffix even when built with `FormDrawer`).
|
|
||||||
- **Table columns hook**: `use-{feature}-columns.tsx`.
|
|
||||||
- **Open/close & request hooks**: `use-{verb}-{noun}.ts` (e.g. `use-create-user.ts`, `use-query-user-list.ts`).
|
|
||||||
- **Complex table cell**: extract into `{feature}-cell.tsx`.
|
|
||||||
|
|
||||||
# Config & types
|
|
||||||
|
|
||||||
- `config/types.ts` — TypeScript types. Form shape → `FormData`; table/list row → `ListItem`.
|
|
||||||
- `config/index.ts` — static constants, enums, and value/label maps (e.g. `XxxStatusValueMap`, `XxxStatusLabelMap`). Keep constants out of `types.ts`.
|
|
||||||
- **`Select` options that need i18n**: set `label` to the message key and add `locale: true` on the option — the field translates it at render. Omit `locale` for options whose label is already final text. Ref `src/pages/benchmark/config/index.ts`.
|
|
||||||
|
|
||||||
# Common components
|
|
||||||
|
|
||||||
Always check `@gpustack/core-ui` first. Frequently reused:
|
|
||||||
|
|
||||||
- **Drawer/Modal open/close**: `useBodyScroll`.
|
|
||||||
- **Form drawer / footer**: `FormDrawer`, `ModalFooter`.
|
|
||||||
- **Delete confirmation**: `DeleteModal`.
|
|
||||||
- **Search + bulk actions bar**: `FilterBar`.
|
|
||||||
- **Form fields**: `BaseSelect`, `Input` (labeled).
|
|
||||||
- **Text overflow**: `AutoTooltip`.
|
|
||||||
- **Icons**: `IconFont`.
|
|
||||||
- **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:
|
|
||||||
|
|
||||||
- **Plain object** (key→value map) → `LabelSelector`.
|
|
||||||
- **String array** → `ListInput`. Ref `src/pages/llmodels/forms/backend-parameters-list.tsx`.
|
|
||||||
- **Object array** → `MetadataList` with a custom item renderer per entry. Ref `src/pages/llmodels/forms/model-lora-list.tsx`.
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
## Create form table list
|
||||||
|
|
||||||
|
## Create a form
|
||||||
|
|
||||||
|
## StatusTag
|
||||||
|
|
||||||
|
Use `StatusTag` from `@gpustack/core-ui` for status display. Do not use `Tag` from `antd` directly.
|
||||||
|
|
||||||
|
1. Define the mapping from business status values to UI status values in the module's `config/index.ts`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { StatusMaps } from '@/config';
|
||||||
|
import { StatusType } from '@/config/types';
|
||||||
|
|
||||||
|
export const XxxStatusValueMap = {
|
||||||
|
Running: 'running',
|
||||||
|
Pending: 'pending',
|
||||||
|
Failed: 'failed'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const XxxStatusLabelMap: Record<string, string> = {
|
||||||
|
[XxxStatusValueMap.Running]: 'Running',
|
||||||
|
[XxxStatusValueMap.Pending]: 'Pending',
|
||||||
|
[XxxStatusValueMap.Failed]: 'Failed'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const status: Record<string, StatusType> = {
|
||||||
|
[XxxStatusValueMap.Running]: StatusMaps.success,
|
||||||
|
[XxxStatusValueMap.Pending]: StatusMaps.transitioning,
|
||||||
|
[XxxStatusValueMap.Failed]: StatusMaps.error
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
2. In table columns, pass only the UI status value and display text required by `StatusTag`:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<StatusTag
|
||||||
|
statusValue={{
|
||||||
|
status: status[value],
|
||||||
|
text: XxxStatusLabelMap[value] || value,
|
||||||
|
message: record.state_message
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
`statusValue.status` must be a value mapped from `StatusMaps`, such as `success`, `transitioning`, `warning`, `error`, or `inactive`. Do not pass business status values such as `running` or `pending` directly.
|
||||||
@@ -100,15 +100,6 @@ const baseRoutes = [
|
|||||||
path: '/models',
|
path: '/models',
|
||||||
redirect: '/models/deployments'
|
redirect: '/models/deployments'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'userModels',
|
|
||||||
path: '/models/user-models',
|
|
||||||
key: 'userModels',
|
|
||||||
icon: 'icon-models',
|
|
||||||
selectedIcon: 'icon-models-filled',
|
|
||||||
defaultIcon: 'icon-models',
|
|
||||||
component: './llmodels/user-models'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'modelCatalog',
|
name: 'modelCatalog',
|
||||||
path: '/models/catalog',
|
path: '/models/catalog',
|
||||||
@@ -119,6 +110,16 @@ const baseRoutes = [
|
|||||||
access: 'canSeeOrgAdmin',
|
access: 'canSeeOrgAdmin',
|
||||||
component: './llmodels/catalog'
|
component: './llmodels/catalog'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'userModels',
|
||||||
|
path: '/models/user-models',
|
||||||
|
key: 'userModels',
|
||||||
|
icon: 'icon-models',
|
||||||
|
selectedIcon: 'icon-models-filled',
|
||||||
|
defaultIcon: 'icon-models',
|
||||||
|
access: 'canSeeUser',
|
||||||
|
component: './llmodels/user-models'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'deployment',
|
name: 'deployment',
|
||||||
path: '/models/deployments',
|
path: '/models/deployments',
|
||||||
@@ -273,7 +274,7 @@ const baseRoutes = [
|
|||||||
selectedIcon: 'icon-cluster2-filled',
|
selectedIcon: 'icon-cluster2-filled',
|
||||||
defaultIcon: 'icon-cluster2-outline',
|
defaultIcon: 'icon-cluster2-outline',
|
||||||
component: './cluster-management/clusters',
|
component: './cluster-management/clusters',
|
||||||
subMenu: ['/resources/clusters/create']
|
subMenu: ['/resources/clusters/detail', '/resources/clusters/create']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'workers',
|
name: 'workers',
|
||||||
@@ -301,6 +302,16 @@ const baseRoutes = [
|
|||||||
selectedIcon: 'icon-credential-filled',
|
selectedIcon: 'icon-credential-filled',
|
||||||
defaultIcon: 'icon-credential-outline',
|
defaultIcon: 'icon-credential-outline',
|
||||||
component: './cluster-management/credentials'
|
component: './cluster-management/credentials'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'clusterDetail',
|
||||||
|
path: '/resources/clusters/detail',
|
||||||
|
key: 'clusterDetail',
|
||||||
|
icon: 'icon-cluster2-outline',
|
||||||
|
selectedIcon: 'icon-cluster2-filled',
|
||||||
|
defaultIcon: 'icon-cluster2-outline',
|
||||||
|
hideInMenu: true,
|
||||||
|
component: './cluster-management/cluster-detail'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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.39",
|
"@gpustack/core-ui": "^1.0.27",
|
||||||
"@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",
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
"culori": "^4.0.2",
|
"culori": "^4.0.2",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"dompurify": "^3.2.6",
|
"dompurify": "^3.2.6",
|
||||||
|
"driver.js": "^1.3.1",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"has-ansi": "^5.0.1",
|
"has-ansi": "^5.0.1",
|
||||||
|
|||||||
@@ -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.39
|
specifier: ^1.0.27
|
||||||
version: 1.0.39(czdvzceysqw7iv6pct2ucnb23e)
|
version: 1.0.27(czdvzceysqw7iv6pct2ucnb23e)
|
||||||
'@huggingface/gguf':
|
'@huggingface/gguf':
|
||||||
specifier: ^0.1.7
|
specifier: ^0.1.7
|
||||||
version: 0.1.18
|
version: 0.1.18
|
||||||
@@ -49,7 +49,7 @@ importers:
|
|||||||
version: 4.17.24
|
version: 4.17.24
|
||||||
'@umijs/max':
|
'@umijs/max':
|
||||||
specifier: ^4.6.15
|
specifier: ^4.6.15
|
||||||
version: 4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lightningcss@1.22.1)(prettier@3.8.3)(rc-field-form@2.7.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)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
version: 4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lightningcss@1.22.1)(prettier@3.8.3)(rc-field-form@2.7.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)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@xterm/addon-fit':
|
'@xterm/addon-fit':
|
||||||
specifier: ^0.10.0
|
specifier: ^0.10.0
|
||||||
version: 0.10.0(@xterm/xterm@5.5.0)
|
version: 0.10.0(@xterm/xterm@5.5.0)
|
||||||
@@ -89,6 +89,9 @@ importers:
|
|||||||
dompurify:
|
dompurify:
|
||||||
specifier: ^3.2.6
|
specifier: ^3.2.6
|
||||||
version: 3.4.2
|
version: 3.4.2
|
||||||
|
driver.js:
|
||||||
|
specifier: ^1.3.1
|
||||||
|
version: 1.4.0
|
||||||
echarts:
|
echarts:
|
||||||
specifier: ^5.5.1
|
specifier: ^5.5.1
|
||||||
version: 5.6.0
|
version: 5.6.0
|
||||||
@@ -106,7 +109,7 @@ importers:
|
|||||||
version: 3.3.0
|
version: 3.3.0
|
||||||
jotai:
|
jotai:
|
||||||
specifier: ^2.8.4
|
specifier: ^2.8.4
|
||||||
version: 2.20.0(@babel/core@7.23.6)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1)
|
version: 2.20.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1)
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
@@ -202,7 +205,7 @@ importers:
|
|||||||
version: 6.4.1(css-to-react-native@3.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 6.4.1(css-to-react-native@3.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
umi-presets-pro:
|
umi-presets-pro:
|
||||||
specifier: ^2.0.3
|
specifier: ^2.0.3
|
||||||
version: 2.0.3(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(chokidar@3.6.0)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(encoding@0.1.13)(rc-field-form@2.7.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)(typescript@5.9.3)(umi@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))
|
version: 2.0.3(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(chokidar@3.6.0)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(encoding@0.1.13)(rc-field-form@2.7.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)(typescript@5.9.3)(umi@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))
|
||||||
wavesurfer.js:
|
wavesurfer.js:
|
||||||
specifier: ^7.8.8
|
specifier: ^7.8.8
|
||||||
version: 7.12.6
|
version: 7.12.6
|
||||||
@@ -230,10 +233,10 @@ importers:
|
|||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
'@umijs/plugins':
|
'@umijs/plugins':
|
||||||
specifier: ^4.4.11
|
specifier: ^4.4.11
|
||||||
version: 4.6.51(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
version: 4.6.51(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
||||||
babel-plugin-named-asset-import:
|
babel-plugin-named-asset-import:
|
||||||
specifier: ^0.3.8
|
specifier: ^0.3.8
|
||||||
version: 0.3.8(@babel/core@7.23.6)
|
version: 0.3.8(@babel/core@7.29.0)
|
||||||
case-sensitive-paths-webpack-plugin:
|
case-sensitive-paths-webpack-plugin:
|
||||||
specifier: ^2.4.0
|
specifier: ^2.4.0
|
||||||
version: 2.4.0
|
version: 2.4.0
|
||||||
@@ -1481,24 +1484,24 @@ 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.39':
|
'@gpustack/core-ui@1.0.27':
|
||||||
resolution: {integrity: sha512-6NS0TDkBFHK6RipfvLwvDnLZlAoa/yFbHV+xeERAnUb+4t0mDyIQFqvYAeDgrUW/JIZ6WScrm31FDZe2sKiuaw==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.39.tgz}
|
resolution: {integrity: sha512-m3ue0EHFKULla0mpnpxZwn9LVaGKS+HnuzQYSBECQa4vaP8MEEQsR7oFBtG8bhWUWYA9VzP6k7fjozDkP6TymA==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.27.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@ant-design/icons': ^6.1.0
|
'@ant-design/icons': '>=6.0.0'
|
||||||
'@ant-design/pro-components': 3.1.0-0
|
'@ant-design/pro-components': 3.1.0-0
|
||||||
'@monaco-editor/react': ^4.6.0
|
'@monaco-editor/react': ^4.6.0
|
||||||
ahooks: ^3.8.5
|
ahooks: '>=3.0.0'
|
||||||
antd: ^6.3.3
|
antd: '>=6.0.0'
|
||||||
antd-style: ^3.6.2
|
antd-style: '>=3.0.0'
|
||||||
axios: ^1.8.2
|
axios: '>=1.8.0'
|
||||||
echarts: ^5.5.1
|
echarts: '>=5.0.0'
|
||||||
file-saver: ^2.0.5
|
file-saver: ^2.0.5
|
||||||
monaco-editor: ^0.30.1
|
monaco-editor: ^0.30.1
|
||||||
monaco-yaml: ^4.0.0
|
monaco-yaml: ^4.0.0
|
||||||
overlayscrollbars-react: ^0.5.6
|
overlayscrollbars-react: ^0.5.6
|
||||||
react: ^18.2.0
|
react: '>=18.0.0'
|
||||||
react-dom: ^18.2.0
|
react-dom: '>=18.0.0'
|
||||||
styled-components: ^6.1.15
|
styled-components: '>=6.0.0'
|
||||||
|
|
||||||
'@hono/node-server@1.19.14':
|
'@hono/node-server@1.19.14':
|
||||||
resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==, tarball: https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz}
|
resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==, tarball: https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz}
|
||||||
@@ -4317,6 +4320,9 @@ packages:
|
|||||||
dot-case@3.0.4:
|
dot-case@3.0.4:
|
||||||
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==, tarball: https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz}
|
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==, tarball: https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz}
|
||||||
|
|
||||||
|
driver.js@1.4.0:
|
||||||
|
resolution: {integrity: sha512-Gm64jm6PmcU+si21sQhBrTAM1JvUrR0QhNmjkprNLxohOBzul9+pNHXgQaT9lW84gwg9GMLB3NZGuGolsz5uew==, tarball: https://registry.npmjs.org/driver.js/-/driver.js-1.4.0.tgz}
|
||||||
|
|
||||||
duck@0.1.12:
|
duck@0.1.12:
|
||||||
resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==, tarball: https://registry.npmjs.org/duck/-/duck-0.1.12.tgz}
|
resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==, tarball: https://registry.npmjs.org/duck/-/duck-0.1.12.tgz}
|
||||||
|
|
||||||
@@ -9251,14 +9257,14 @@ snapshots:
|
|||||||
'@radix-ui/popper': 0.0.10
|
'@radix-ui/popper': 0.0.10
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
'@alita/plugins@3.5.5(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
'@alita/plugins@3.5.5(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@alita/babel-transform-jsx-class': 0.0.2
|
'@alita/babel-transform-jsx-class': 0.0.2
|
||||||
'@alita/inspx': 0.0.2(react@18.3.1)
|
'@alita/inspx': 0.0.2(react@18.3.1)
|
||||||
'@alita/request': 3.1.2
|
'@alita/request': 3.1.2
|
||||||
'@alita/types': 3.1.2
|
'@alita/types': 3.1.2
|
||||||
'@umijs/bundler-utils': 4.4.11
|
'@umijs/bundler-utils': 4.4.11
|
||||||
'@umijs/plugins': 4.4.11(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
'@umijs/plugins': 4.4.11(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
||||||
'@umijs/utils': 4.4.11
|
'@umijs/utils': 4.4.11
|
||||||
ahooks: 3.9.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
ahooks: 3.9.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
antd-mobile-alita: 2.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
antd-mobile-alita: 2.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@@ -10128,90 +10134,90 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.29.0
|
'@babel/types': 7.29.0
|
||||||
|
|
||||||
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.6)':
|
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
|
|
||||||
'@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.6)':
|
'@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.23.6)
|
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
|
||||||
'@babel/helper-plugin-utils': 7.28.6
|
'@babel/helper-plugin-utils': 7.28.6
|
||||||
'@babel/helper-simple-access': 7.27.1
|
'@babel/helper-simple-access': 7.27.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -10802,7 +10808,7 @@ snapshots:
|
|||||||
|
|
||||||
'@formatjs/intl-utils@2.3.0': {}
|
'@formatjs/intl-utils@2.3.0': {}
|
||||||
|
|
||||||
'@gpustack/core-ui@1.0.39(czdvzceysqw7iv6pct2ucnb23e)':
|
'@gpustack/core-ui@1.0.27(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)
|
||||||
@@ -12275,11 +12281,11 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@umijs/bundler-utoopack@4.6.51(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))':
|
'@umijs/bundler-utoopack@4.6.51(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@umijs/bundler-utils': 4.6.51
|
'@umijs/bundler-utils': 4.6.51
|
||||||
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@utoo/pack': 1.4.3(less-loader@11.1.0(less@4.1.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(less@4.1.3)(postcss@8.5.14)(resolve-url-loader@5.0.0)(sass-loader@13.2.0(sass@1.54.0)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))
|
'@utoo/pack': 1.4.3(less-loader@11.1.0(less@4.1.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(less@4.1.3)(postcss@8.5.14)(resolve-url-loader@5.0.0)(sass-loader@13.2.0(sass@1.54.0)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))
|
||||||
compression: 1.8.1
|
compression: 1.8.1
|
||||||
connect-history-api-fallback: 2.0.0
|
connect-history-api-fallback: 2.0.0
|
||||||
cors: 2.8.6
|
cors: 2.8.6
|
||||||
@@ -12547,14 +12553,14 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
'@umijs/max@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lightningcss@1.22.1)(prettier@3.8.3)(rc-field-form@2.7.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)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))':
|
'@umijs/max@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(lightningcss@1.22.1)(prettier@3.8.3)(rc-field-form@2.7.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)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@umijs/lint': 4.6.51(eslint@8.35.0)(stylelint@14.8.2)(typescript@5.9.3)
|
'@umijs/lint': 4.6.51(eslint@8.35.0)(stylelint@14.8.2)(typescript@5.9.3)
|
||||||
'@umijs/plugins': 4.6.51(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(antd@4.24.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
'@umijs/plugins': 4.6.51(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(antd@4.24.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
||||||
antd: 4.24.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
antd: 4.24.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
eslint: 8.35.0
|
eslint: 8.35.0
|
||||||
stylelint: 14.8.2
|
stylelint: 14.8.2
|
||||||
umi: 4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@8.35.0)(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
umi: 4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@8.35.0)(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- '@rspack/core'
|
- '@rspack/core'
|
||||||
@@ -12636,7 +12642,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tsx: 3.12.2
|
tsx: 3.12.2
|
||||||
|
|
||||||
'@umijs/plugins@4.4.11(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
'@umijs/plugins@4.4.11(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ahooksjs/use-request': 2.8.15(react@18.3.1)
|
'@ahooksjs/use-request': 2.8.15(react@18.3.1)
|
||||||
'@ant-design/antd-theme-variable': 1.0.0
|
'@ant-design/antd-theme-variable': 1.0.0
|
||||||
@@ -12651,7 +12657,7 @@ snapshots:
|
|||||||
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.20)
|
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.20)
|
||||||
axios: 0.27.2
|
axios: 0.27.2
|
||||||
babel-plugin-import: 1.13.8
|
babel-plugin-import: 1.13.8
|
||||||
babel-plugin-styled-components: 2.1.4(@babel/core@7.23.6)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
babel-plugin-styled-components: 2.1.4(@babel/core@7.29.0)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
dayjs: 1.11.20
|
dayjs: 1.11.20
|
||||||
dva-core: 2.0.4(redux@4.2.1)
|
dva-core: 2.0.4(redux@4.2.1)
|
||||||
dva-immer: 1.0.2(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
dva-immer: 1.0.2(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
@@ -12681,7 +12687,7 @@ snapshots:
|
|||||||
- react-native
|
- react-native
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@umijs/plugins@4.6.51(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(antd@4.24.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
'@umijs/plugins@4.6.51(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(antd@4.24.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ahooksjs/use-request': 2.8.15(react@18.3.1)
|
'@ahooksjs/use-request': 2.8.15(react@18.3.1)
|
||||||
'@ant-design/antd-theme-variable': 1.0.0
|
'@ant-design/antd-theme-variable': 1.0.0
|
||||||
@@ -12696,7 +12702,7 @@ snapshots:
|
|||||||
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.20)
|
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.20)
|
||||||
axios: 0.27.2
|
axios: 0.27.2
|
||||||
babel-plugin-import: 1.13.8
|
babel-plugin-import: 1.13.8
|
||||||
babel-plugin-styled-components: 2.1.4(@babel/core@7.23.6)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
babel-plugin-styled-components: 2.1.4(@babel/core@7.29.0)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
dayjs: 1.11.20
|
dayjs: 1.11.20
|
||||||
dva-core: 2.0.4(redux@4.2.1)
|
dva-core: 2.0.4(redux@4.2.1)
|
||||||
dva-immer: 1.0.2(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
dva-immer: 1.0.2(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
@@ -12726,7 +12732,7 @@ snapshots:
|
|||||||
- react-native
|
- react-native
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@umijs/plugins@4.6.51(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
'@umijs/plugins@4.6.51(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ahooksjs/use-request': 2.8.15(react@18.3.1)
|
'@ahooksjs/use-request': 2.8.15(react@18.3.1)
|
||||||
'@ant-design/antd-theme-variable': 1.0.0
|
'@ant-design/antd-theme-variable': 1.0.0
|
||||||
@@ -12741,7 +12747,7 @@ snapshots:
|
|||||||
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.20)
|
antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.20)
|
||||||
axios: 0.27.2
|
axios: 0.27.2
|
||||||
babel-plugin-import: 1.13.8
|
babel-plugin-import: 1.13.8
|
||||||
babel-plugin-styled-components: 2.1.4(@babel/core@7.23.6)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
babel-plugin-styled-components: 2.1.4(@babel/core@7.29.0)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
dayjs: 1.11.20
|
dayjs: 1.11.20
|
||||||
dva-core: 2.0.4(redux@4.2.1)
|
dva-core: 2.0.4(redux@4.2.1)
|
||||||
dva-immer: 1.0.2(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
dva-immer: 1.0.2(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
@@ -12771,7 +12777,7 @@ snapshots:
|
|||||||
- react-native
|
- react-native
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@umijs/preset-umi@4.6.51(@types/node@25.6.2)(@types/react@18.3.28)(lightningcss@1.22.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))':
|
'@umijs/preset-umi@4.6.51(@types/node@25.6.2)(@types/react@18.3.28)(lightningcss@1.22.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@iconify/utils': 2.1.1
|
'@iconify/utils': 2.1.1
|
||||||
'@stagewise/toolbar': 0.6.2
|
'@stagewise/toolbar': 0.6.2
|
||||||
@@ -12781,7 +12787,7 @@ snapshots:
|
|||||||
'@umijs/bundler-esbuild': 4.6.51
|
'@umijs/bundler-esbuild': 4.6.51
|
||||||
'@umijs/bundler-mako': 0.11.10(postcss@8.5.14)(sass@1.54.0)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/bundler-mako': 0.11.10(postcss@8.5.14)(sass@1.54.0)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/bundler-utils': 4.6.51
|
'@umijs/bundler-utils': 4.6.51
|
||||||
'@umijs/bundler-utoopack': 4.6.51(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/bundler-utoopack': 4.6.51(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/bundler-vite': 4.6.51(@types/node@25.6.2)(lightningcss@1.22.1)(postcss@8.5.14)(rollup@3.30.0)(sass@1.54.0)(terser@5.48.0)
|
'@umijs/bundler-vite': 4.6.51(@types/node@25.6.2)(lightningcss@1.22.1)(postcss@8.5.14)(rollup@3.30.0)(sass@1.54.0)(terser@5.48.0)
|
||||||
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/core': 4.6.51
|
'@umijs/core': 4.6.51
|
||||||
@@ -12864,13 +12870,13 @@ snapshots:
|
|||||||
react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
react-router-dom: 6.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
react-router-dom: 6.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
|
||||||
'@umijs/request-record@1.1.4(umi@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))':
|
'@umijs/request-record@1.1.4(umi@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar: 3.6.0
|
chokidar: 3.6.0
|
||||||
express: 4.22.1
|
express: 4.22.1
|
||||||
lodash: 4.18.1
|
lodash: 4.18.1
|
||||||
prettier: 2.8.8
|
prettier: 2.8.8
|
||||||
umi: 4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
umi: 4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -12886,13 +12892,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@umijs/test@4.6.51(@babel/core@7.23.6)':
|
'@umijs/test@4.6.51(@babel/core@7.29.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.6)
|
'@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.29.0)
|
||||||
'@jest/types': 27.5.1
|
'@jest/types': 27.5.1
|
||||||
'@umijs/bundler-utils': 4.6.51
|
'@umijs/bundler-utils': 4.6.51
|
||||||
'@umijs/utils': 4.6.51
|
'@umijs/utils': 4.6.51
|
||||||
babel-jest: 29.7.0(@babel/core@7.23.6)
|
babel-jest: 29.7.0(@babel/core@7.29.0)
|
||||||
esbuild: 0.21.4
|
esbuild: 0.21.4
|
||||||
identity-obj-proxy: 3.0.0
|
identity-obj-proxy: 3.0.0
|
||||||
isomorphic-unfetch: 4.0.2
|
isomorphic-unfetch: 4.0.2
|
||||||
@@ -13012,7 +13018,7 @@ snapshots:
|
|||||||
'@utoo/pack-win32-x64-msvc@1.4.3':
|
'@utoo/pack-win32-x64-msvc@1.4.3':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@utoo/pack@1.4.3(less-loader@11.1.0(less@4.1.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(less@4.1.3)(postcss@8.5.14)(resolve-url-loader@5.0.0)(sass-loader@13.2.0(sass@1.54.0)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))':
|
'@utoo/pack@1.4.3(less-loader@11.1.0(less@4.1.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(less@4.1.3)(postcss@8.5.14)(resolve-url-loader@5.0.0)(sass-loader@13.2.0(sass@1.54.0)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.22.5
|
'@babel/code-frame': 7.22.5
|
||||||
'@hono/node-server': 1.19.14(hono@4.12.18)
|
'@hono/node-server': 1.19.14(hono@4.12.18)
|
||||||
@@ -13033,7 +13039,7 @@ snapshots:
|
|||||||
sass-loader: 13.2.0(sass@1.54.0)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
sass-loader: 13.2.0(sass@1.54.0)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
semver: 7.8.0
|
semver: 7.8.0
|
||||||
send: 0.17.1
|
send: 0.17.1
|
||||||
styled-jsx: 5.1.7(@babel/core@7.23.6)(react@18.3.1)
|
styled-jsx: 5.1.7(@babel/core@7.29.0)(react@18.3.1)
|
||||||
ws: 8.20.0
|
ws: 8.20.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@utoo/pack-darwin-arm64': 1.4.3
|
'@utoo/pack-darwin-arm64': 1.4.3
|
||||||
@@ -13549,13 +13555,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
|
|
||||||
babel-jest@29.7.0(@babel/core@7.23.6):
|
babel-jest@29.7.0(@babel/core@7.29.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@jest/transform': 29.7.0
|
'@jest/transform': 29.7.0
|
||||||
'@types/babel__core': 7.20.5
|
'@types/babel__core': 7.20.5
|
||||||
babel-plugin-istanbul: 6.1.1
|
babel-plugin-istanbul: 6.1.1
|
||||||
babel-preset-jest: 29.6.3(@babel/core@7.23.6)
|
babel-preset-jest: 29.6.3(@babel/core@7.29.0)
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
slash: 3.0.0
|
slash: 3.0.0
|
||||||
@@ -13595,9 +13601,9 @@ snapshots:
|
|||||||
cosmiconfig: 7.1.0
|
cosmiconfig: 7.1.0
|
||||||
resolve: 1.22.12
|
resolve: 1.22.12
|
||||||
|
|
||||||
babel-plugin-named-asset-import@0.3.8(@babel/core@7.23.6):
|
babel-plugin-named-asset-import@0.3.8(@babel/core@7.29.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
|
|
||||||
babel-plugin-react-compiler@0.0.0-experimental-c23de8d-20240515:
|
babel-plugin-react-compiler@0.0.0-experimental-c23de8d-20240515:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -13609,11 +13615,11 @@ snapshots:
|
|||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
zod-validation-error: 2.1.0(zod@3.25.76)
|
zod-validation-error: 2.1.0(zod@3.25.76)
|
||||||
|
|
||||||
babel-plugin-styled-components@2.1.4(@babel/core@7.23.6)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)):
|
babel-plugin-styled-components@2.1.4(@babel/core@7.29.0)(styled-components@6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-annotate-as-pure': 7.27.3
|
'@babel/helper-annotate-as-pure': 7.27.3
|
||||||
'@babel/helper-module-imports': 7.28.6
|
'@babel/helper-module-imports': 7.28.6
|
||||||
'@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.23.6)
|
'@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
|
||||||
lodash: 4.18.1
|
lodash: 4.18.1
|
||||||
picomatch: 2.3.2
|
picomatch: 2.3.2
|
||||||
styled-components: 6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
styled-components: 6.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@@ -13621,30 +13627,30 @@ snapshots:
|
|||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
babel-preset-current-node-syntax@1.2.0(@babel/core@7.23.6):
|
babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.6)
|
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.6)
|
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.6)
|
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.6)
|
'@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.23.6)
|
'@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.6)
|
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.6)
|
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.6)
|
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.6)
|
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.6)
|
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.6)
|
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.6)
|
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.6)
|
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.6)
|
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0)
|
||||||
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.6)
|
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0)
|
||||||
|
|
||||||
babel-preset-jest@29.6.3(@babel/core@7.23.6):
|
babel-preset-jest@29.6.3(@babel/core@7.29.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
babel-plugin-jest-hoist: 29.6.3
|
babel-plugin-jest-hoist: 29.6.3
|
||||||
babel-preset-current-node-syntax: 1.2.0(@babel/core@7.23.6)
|
babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0)
|
||||||
|
|
||||||
babel-runtime-jsx-plus@0.1.5: {}
|
babel-runtime-jsx-plus@0.1.5: {}
|
||||||
|
|
||||||
@@ -14548,6 +14554,8 @@ snapshots:
|
|||||||
no-case: 3.0.4
|
no-case: 3.0.4
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
driver.js@1.4.0: {}
|
||||||
|
|
||||||
duck@0.1.12:
|
duck@0.1.12:
|
||||||
dependencies:
|
dependencies:
|
||||||
underscore: 1.13.8
|
underscore: 1.13.8
|
||||||
@@ -16402,9 +16410,9 @@ snapshots:
|
|||||||
|
|
||||||
jiti@2.7.0: {}
|
jiti@2.7.0: {}
|
||||||
|
|
||||||
jotai@2.20.0(@babel/core@7.23.6)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1):
|
jotai@2.20.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
'@babel/template': 7.28.6
|
'@babel/template': 7.28.6
|
||||||
'@types/react': 18.3.28
|
'@types/react': 18.3.28
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
@@ -19797,12 +19805,12 @@ snapshots:
|
|||||||
css-to-react-native: 3.2.0
|
css-to-react-native: 3.2.0
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1):
|
styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
client-only: 0.0.1
|
client-only: 0.0.1
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@babel/core': 7.23.6
|
'@babel/core': 7.29.0
|
||||||
|
|
||||||
stylelint-config-recommended@7.0.0(stylelint@14.8.2):
|
stylelint-config-recommended@7.0.0(stylelint@14.8.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -20156,12 +20164,12 @@ snapshots:
|
|||||||
|
|
||||||
ua-parser-js@0.7.41: {}
|
ua-parser-js@0.7.41: {}
|
||||||
|
|
||||||
umi-presets-pro@2.0.3(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(chokidar@3.6.0)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(encoding@0.1.13)(rc-field-form@2.7.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)(typescript@5.9.3)(umi@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))):
|
umi-presets-pro@2.0.3(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(chokidar@3.6.0)(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(encoding@0.1.13)(rc-field-form@2.7.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)(typescript@5.9.3)(umi@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@alita/plugins': 3.5.5(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
'@alita/plugins': 3.5.5(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
||||||
'@umijs/max-plugin-openapi': 2.0.3(chokidar@3.6.0)(encoding@0.1.13)(typescript@5.9.3)
|
'@umijs/max-plugin-openapi': 2.0.3(chokidar@3.6.0)(encoding@0.1.13)(typescript@5.9.3)
|
||||||
'@umijs/plugins': 4.6.51(@babel/core@7.23.6)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
'@umijs/plugins': 4.6.51(@babel/core@7.29.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(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))(dva@2.5.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.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)
|
||||||
'@umijs/request-record': 1.1.4(umi@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))
|
'@umijs/request-record': 1.1.4(umi@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)))
|
||||||
swagger-ui-dist: 4.19.1
|
swagger-ui-dist: 4.19.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
@@ -20185,17 +20193,17 @@ snapshots:
|
|||||||
isomorphic-fetch: 2.2.1
|
isomorphic-fetch: 2.2.1
|
||||||
qs: 6.15.1
|
qs: 6.15.1
|
||||||
|
|
||||||
umi@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@8.35.0)(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)):
|
umi@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@8.35.0)(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.23.6
|
||||||
'@umijs/bundler-utils': 4.6.51
|
'@umijs/bundler-utils': 4.6.51
|
||||||
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/core': 4.6.51
|
'@umijs/core': 4.6.51
|
||||||
'@umijs/lint': 4.6.51(eslint@8.35.0)(stylelint@14.8.2)(typescript@5.9.3)
|
'@umijs/lint': 4.6.51(eslint@8.35.0)(stylelint@14.8.2)(typescript@5.9.3)
|
||||||
'@umijs/preset-umi': 4.6.51(@types/node@25.6.2)(@types/react@18.3.28)(lightningcss@1.22.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/preset-umi': 4.6.51(@types/node@25.6.2)(@types/react@18.3.28)(lightningcss@1.22.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/renderer-react': 4.6.51(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@umijs/renderer-react': 4.6.51(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@umijs/server': 4.6.51
|
'@umijs/server': 4.6.51
|
||||||
'@umijs/test': 4.6.51(@babel/core@7.23.6)
|
'@umijs/test': 4.6.51(@babel/core@7.29.0)
|
||||||
'@umijs/utils': 4.6.51
|
'@umijs/utils': 4.6.51
|
||||||
prettier-plugin-organize-imports: 3.2.4(prettier@3.8.3)(typescript@5.9.3)
|
prettier-plugin-organize-imports: 3.2.4(prettier@3.8.3)(typescript@5.9.3)
|
||||||
prettier-plugin-packagejson: 2.4.3(prettier@3.8.3)
|
prettier-plugin-packagejson: 2.4.3(prettier@3.8.3)
|
||||||
@@ -20239,17 +20247,17 @@ snapshots:
|
|||||||
- webpack-hot-middleware
|
- webpack-hot-middleware
|
||||||
- webpack-plugin-serve
|
- webpack-plugin-serve
|
||||||
|
|
||||||
umi@4.6.51(@babel/core@7.23.6)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)):
|
umi@4.6.51(@babel/core@7.29.0)(@types/node@25.6.2)(@types/react@18.3.28)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.22.1)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(stylelint@14.8.2)(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.23.6
|
||||||
'@umijs/bundler-utils': 4.6.51
|
'@umijs/bundler-utils': 4.6.51
|
||||||
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/bundler-webpack': 4.6.51(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/core': 4.6.51
|
'@umijs/core': 4.6.51
|
||||||
'@umijs/lint': 4.6.51(eslint@9.39.4(jiti@2.7.0))(stylelint@14.8.2)(typescript@5.9.3)
|
'@umijs/lint': 4.6.51(eslint@9.39.4(jiti@2.7.0))(stylelint@14.8.2)(typescript@5.9.3)
|
||||||
'@umijs/preset-umi': 4.6.51(@types/node@25.6.2)(@types/react@18.3.28)(lightningcss@1.22.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.23.6)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
'@umijs/preset-umi': 4.6.51(@types/node@25.6.2)(@types/react@18.3.28)(lightningcss@1.22.1)(rollup@3.30.0)(sass@1.54.0)(styled-jsx@5.1.7(@babel/core@7.29.0)(react@18.3.1))(terser@5.48.0)(type-fest@0.20.2)(typescript@5.9.3)(webpack@5.106.2(lightningcss@1.22.1)(postcss@8.5.14))
|
||||||
'@umijs/renderer-react': 4.6.51(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@umijs/renderer-react': 4.6.51(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@umijs/server': 4.6.51
|
'@umijs/server': 4.6.51
|
||||||
'@umijs/test': 4.6.51(@babel/core@7.23.6)
|
'@umijs/test': 4.6.51(@babel/core@7.29.0)
|
||||||
'@umijs/utils': 4.6.51
|
'@umijs/utils': 4.6.51
|
||||||
prettier-plugin-organize-imports: 3.2.4(prettier@3.8.3)(typescript@5.9.3)
|
prettier-plugin-organize-imports: 3.2.4(prettier@3.8.3)(typescript@5.9.3)
|
||||||
prettier-plugin-packagejson: 2.4.3(prettier@3.8.3)
|
prettier-plugin-packagejson: 2.4.3(prettier@3.8.3)
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
|||||||
import { setAtomStorage } from '@/atoms/utils';
|
import { setAtomStorage } from '@/atoms/utils';
|
||||||
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||||
import { COLOR_PRIMARY } from '@/config/theme/constants';
|
import { COLOR_PRIMARY } from '@/config/theme/constants';
|
||||||
|
import { queryClusterList } from '@/pages/cluster-management/apis';
|
||||||
|
import { ProviderValueMap } from '@/pages/cluster-management/config';
|
||||||
|
import { queryResourceEvents } from '@/pages/usage/apis/resource';
|
||||||
import { getGPUStackPlugin } from '@/plugins';
|
import { getGPUStackPlugin } from '@/plugins';
|
||||||
import { enterprisePluginReady } from '@/plugins/enterprise-ready';
|
import { enterprisePluginReady } from '@/plugins/enterprise-ready';
|
||||||
import { GPUStackPluginManager } from '@/plugins/manager';
|
import { GPUStackPluginManager } from '@/plugins/manager';
|
||||||
@@ -14,10 +17,6 @@ import {
|
|||||||
} from '@/services/profile/apis';
|
} from '@/services/profile/apis';
|
||||||
import { fetchSystemConfig } from '@/services/system/query-system-config';
|
import { fetchSystemConfig } from '@/services/system/query-system-config';
|
||||||
import { isOnline } from '@/utils';
|
import { isOnline } from '@/utils';
|
||||||
import {
|
|
||||||
markInitialStateProbed,
|
|
||||||
probeAccessFlags
|
|
||||||
} from '@/utils/access-probes';
|
|
||||||
import { installTenantFetch } from '@/utils/install-fetch';
|
import { installTenantFetch } from '@/utils/install-fetch';
|
||||||
import {
|
import {
|
||||||
IS_FIRST_LOGIN,
|
IS_FIRST_LOGIN,
|
||||||
@@ -42,6 +41,80 @@ const checkDefaultPage = async (userInfo: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Probes the caller's cluster list once so access predicates can gate
|
||||||
|
// GPU Service (Kubernetes-only). Cheap (one list request) and never
|
||||||
|
// blocks login — any failure just falls back to `undefined`, which
|
||||||
|
// the predicate treats as "unknown / don't restrict beyond role".
|
||||||
|
// The result is also mirrored into sessionStorage so access extensions
|
||||||
|
// that run without the initialState argument can read it (e.g. to
|
||||||
|
// override the admin shortcut in scopes where the menu shouldn't
|
||||||
|
// show even for admins).
|
||||||
|
const HAS_K8S_CLUSTER_KEY = 'hasKubernetesCluster';
|
||||||
|
const probeHasKubernetesCluster = async (): Promise<boolean | undefined> => {
|
||||||
|
try {
|
||||||
|
const res = await queryClusterList(
|
||||||
|
{ page: -1 },
|
||||||
|
{
|
||||||
|
skipErrorHandler: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const value = (res?.items ?? []).some(
|
||||||
|
(c) => c?.provider === ProviderValueMap.Kubernetes
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem(HAS_K8S_CLUSTER_KEY, JSON.stringify(value));
|
||||||
|
} catch {
|
||||||
|
// sessionStorage may be unavailable (Safari private mode); the
|
||||||
|
// access predicate already handles a missing value as "unknown".
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('probeHasKubernetesCluster error', error);
|
||||||
|
try {
|
||||||
|
window.sessionStorage.removeItem(HAS_K8S_CLUSTER_KEY);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Probes whether the caller has ANY resource-usage events (GPU/CPU instance or
|
||||||
|
// storage lifecycle). Used alongside the cluster probe so a user who has run
|
||||||
|
// GPU instances still sees GPU Service / the full Usage page even if they
|
||||||
|
// currently have no Kubernetes cluster. Mirrored into sessionStorage for the
|
||||||
|
// access extensions; any failure → undefined ("unknown — don't restrict").
|
||||||
|
const HAS_RESOURCE_EVENTS_KEY = 'hasResourceEvents';
|
||||||
|
const probeHasResourceEvents = async (): Promise<boolean | undefined> => {
|
||||||
|
try {
|
||||||
|
// No date range = "ever"; scope is clamped to the caller server-side.
|
||||||
|
const res = await queryResourceEvents(
|
||||||
|
{ perPage: 1 },
|
||||||
|
{
|
||||||
|
skipErrorHandler: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const value = (res?.pagination?.total ?? 0) > 0;
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem(
|
||||||
|
HAS_RESOURCE_EVENTS_KEY,
|
||||||
|
JSON.stringify(value)
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// sessionStorage may be unavailable; predicate treats missing as unknown.
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('probeHasResourceEvents error', error);
|
||||||
|
try {
|
||||||
|
window.sessionStorage.removeItem(HAS_RESOURCE_EVENTS_KEY);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// runtime configuration
|
// runtime configuration
|
||||||
export async function getInitialState(): Promise<{
|
export async function getInitialState(): Promise<{
|
||||||
fetchUserInfo: () => Promise<Global.UserInfo>;
|
fetchUserInfo: () => Promise<Global.UserInfo>;
|
||||||
@@ -162,24 +235,19 @@ export async function getInitialState(): Promise<{
|
|||||||
getAppVersionInfo();
|
getAppVersionInfo();
|
||||||
|
|
||||||
if (![DEFAULT_ENTER_PAGE.login].includes(location.pathname)) {
|
if (![DEFAULT_ENTER_PAGE.login].includes(location.pathname)) {
|
||||||
const [userInfo, accessFlags] = await Promise.all([
|
const [userInfo, hasKubernetesCluster, hasResourceEvents] =
|
||||||
fetchUserInfo(),
|
await Promise.all([
|
||||||
probeAccessFlags()
|
fetchUserInfo(),
|
||||||
]);
|
probeHasKubernetesCluster(),
|
||||||
// Record that the probes ran for an authenticated user this page load
|
probeHasResourceEvents()
|
||||||
// (the refresh path) so the layout doesn't re-probe. A failed
|
]);
|
||||||
// fetch (empty user — e.g. unauthenticated deep link that bounces to
|
|
||||||
// login) is NOT marked: the user will log in via SPA afterwards and
|
|
||||||
// the layout becomes responsible for probing.
|
|
||||||
if (userInfo?.username) {
|
|
||||||
markInitialStateProbed();
|
|
||||||
}
|
|
||||||
checkDefaultPage(userInfo);
|
checkDefaultPage(userInfo);
|
||||||
return {
|
return {
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
currentUser: userInfo,
|
currentUser: userInfo,
|
||||||
pluginData,
|
pluginData,
|
||||||
...accessFlags
|
hasKubernetesCluster,
|
||||||
|
hasResourceEvents
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -1,24 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
||||||
<defs>
|
|
||||||
<!-- 顶面填充(亮面,高透明度) -->
|
|
||||||
<linearGradient id="cube-top-grad" x1="12" y1="2" x2="12" y2="12" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#f759ab" stop-opacity="0.18"/>
|
|
||||||
<stop offset="100%" stop-color="#c41d7f" stop-opacity="0.1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<!-- 左侧面填充(暗面,低透明度) -->
|
|
||||||
<linearGradient id="cube-left-grad" x1="2" y1="7" x2="12" y2="17" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#c41d7f" stop-opacity="0.12"/>
|
|
||||||
<stop offset="100%" stop-color="#c41d7f" stop-opacity="0.04"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="cube-stroke-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#f759ab"/>
|
|
||||||
<stop offset="100%" stop-color="#c41d7f"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<!-- 顶面填充 -->
|
|
||||||
<polygon points="12,2 21.5,6.7 12,11.5 2.5,6.7" fill="url(#cube-top-grad)" />
|
|
||||||
<!-- 左侧面填充 -->
|
|
||||||
<polygon points="2.5,6.7 12,11.5 12,21.3 2.5,16.5" fill="url(#cube-left-grad)" />
|
|
||||||
<!-- 立方体全纯线外骨架(细化为圆角衔接) -->
|
|
||||||
<path d="M12 2L2.5 6.7M12 2l9.5 4.7M21.5 6.7L12 11.5M2.5 6.7L12 11.5M2.5 6.7v9.8l9.5 4.8M21.5 6.7v9.8l-9.5 4.8M12 11.5v9.8" stroke="url(#cube-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,22 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
||||||
<defs>
|
|
||||||
<!-- 1. 定义专属微通透渐变填充 -->
|
|
||||||
<linearGradient id="img-fill-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#d46b08" stop-opacity="0.12"/>
|
|
||||||
<stop offset="100%" stop-color="#d46b08" stop-opacity="0.04"/>
|
|
||||||
</linearGradient>
|
|
||||||
<!-- 2. 定义边框高精度渐变(亮橙到深橙,拉开层次) -->
|
|
||||||
<linearGradient id="img-stroke-grad" x1="4" y1="4" x2="20" y2="20" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#fa8c16"/>
|
|
||||||
<stop offset="100%" stop-color="#d46b08"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<!-- 3. 精装底色充填层 -->
|
|
||||||
<rect x="3" y="3" width="18" height="18" rx="4" fill="url(#img-fill-grad)" />
|
|
||||||
<!-- 4. 高级柔和微圆角边框层 -->
|
|
||||||
<rect x="3" y="3" width="18" height="18" rx="4" stroke="url(#img-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<!-- 内部几何现代山脉线条 -->
|
|
||||||
<path d="M3 16l4-4a2 2 0 0 1 2.8 0l5.2 5.2M13 15l2.5-2.5a2 2 0 0 1 2.8 0l2.7 2.7" stroke="url(#img-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<!-- 标志性通透小钻石 -->
|
|
||||||
<rect x="14" y="6" width="4" height="4" rx="1.5" transform="rotate(45 16 8)" fill="#fa8c16" fill-opacity="0.3" stroke="url(#img-stroke-grad)" stroke-width="1"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 68 KiB |
@@ -1,17 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="chat-fill-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#389e0d" stop-opacity="0.1"/>
|
|
||||||
<stop offset="100%" stop-color="#389e0d" stop-opacity="0.02"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="chat-stroke-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#73d13d"/>
|
|
||||||
<stop offset="100%" stop-color="#389e0d"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<!-- 主现代对话框体(全部改为圆润R角,精装填充) -->
|
|
||||||
<path d="M18 4H6a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h7.5l3.5 3.5a1 1 0 0 0 1.5-.5V17a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3z" fill="url(#chat-fill-grad)" stroke="url(#chat-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<!-- 内部通透对话线条(细化为圆角代码采样块感) -->
|
|
||||||
<rect x="7" y="8" width="8" height="1.5" rx="0.75" fill="#73d13d" fill-opacity="0.3"/>
|
|
||||||
<rect x="7" y="11.5" width="10" height="1.5" rx="0.75" fill="#73d13d" fill-opacity="0.2"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 640 B |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,19 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="rank-fill-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#08979c" stop-opacity="0.1"/>
|
|
||||||
<stop offset="100%" stop-color="#08979c" stop-opacity="0.01"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="rank-stroke-grad" x1="0" y1="0" x2="24" y2="24" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#36cfc9"/>
|
|
||||||
<stop offset="100%" stop-color="#08979c"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<!-- 数据权重条(全部改为高级圆角) -->
|
|
||||||
<rect x="11" y="4" width="10" height="2.5" rx="1.25" fill="#36cfc9" fill-opacity="0.1" stroke="url(#rank-stroke-grad)" stroke-width="1.5"/>
|
|
||||||
<rect x="11" y="9" width="7.5" height="2.5" rx="1.25" fill="#36cfc9" fill-opacity="0.05" stroke="url(#rank-stroke-grad)" stroke-width="1.5"/>
|
|
||||||
<rect x="11" y="14" width="5" height="2.5" rx="1.25" stroke="url(#rank-stroke-grad)" stroke-width="1.5"/>
|
|
||||||
<!-- 基准线与立体双向指引箭头(优化为圆角) -->
|
|
||||||
<path d="M3 17l3 3 3-3M6 4v16" stroke="url(#rank-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M4.5 5.5L6 4l1.5 1.5" stroke="url(#rank-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 801 B |
@@ -1,18 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="stt-fill-grad" x1="12" y1="3" x2="12" y2="14" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#40a9ff" stop-opacity="0.2"/>
|
|
||||||
<stop offset="100%" stop-color="#1677ff" stop-opacity="0.05"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="stt-stroke-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#40a9ff"/>
|
|
||||||
<stop offset="100%" stop-color="#1677ff"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<!-- 麦克风核心体:精装通透填充 -->
|
|
||||||
<rect x="8.5" y="3" width="7" height="11" rx="3.5" fill="url(#stt-fill-grad)" stroke="url(#stt-stroke-grad)" stroke-width="1.8" />
|
|
||||||
<!-- 内部音膜立体结构线(细化为点状) -->
|
|
||||||
<line x1="10" y1="8" x2="14" y2="8" stroke="url(#stt-stroke-grad)" stroke-width="1" stroke-dasharray="1 2"/>
|
|
||||||
<!-- 悬挂外托架与底座(全部改为高级圆角) -->
|
|
||||||
<path d="M5 10a7 7 0 0 0 14 0M12 17v4M8 21h8" stroke="url(#stt-stroke-grad)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,23 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
||||||
<defs>
|
|
||||||
<!-- 1. 核心高通透深蓝渐变充填 -->
|
|
||||||
<linearGradient id="tts-v2-fill" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#2f54eb" stop-opacity="0.15"/>
|
|
||||||
<stop offset="100%" stop-color="#1d39c4" stop-opacity="0.03"/>
|
|
||||||
</linearGradient>
|
|
||||||
<!-- 2. 精准调校的专属蓝色渐变边框 -->
|
|
||||||
<linearGradient id="tts-v2-stroke" x1="4" y1="4" x2="20" y2="20" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0%" stop-color="#2f54eb"/>
|
|
||||||
<stop offset="100%" stop-color="#1d39c4"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- 左侧:低频辅助声波(大间距,带现代圆角) -->
|
|
||||||
<rect x="4" y="8" width="2.2" height="8" rx="1.1" stroke="url(#tts-v2-stroke)" stroke-width="1.8" stroke-linecap="round"/>
|
|
||||||
|
|
||||||
<!-- 中央:核心高频声波主体(拉大宽度,注入通透水晶质感) -->
|
|
||||||
<rect x="10.4" y="2" width="3.2" height="20" rx="1.6" fill="url(#tts-v2-fill)" stroke="url(#tts-v2-stroke)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
|
|
||||||
<!-- 右侧:高频衰减声波(保持几何对称与呼吸感) -->
|
|
||||||
<rect x="17.8" y="5" width="2.2" height="14" rx="1.1" stroke="url(#tts-v2-stroke)" stroke-width="1.8" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -9,15 +9,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes tableEmptyFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-table {
|
.scroll-table {
|
||||||
.ant-table {
|
.ant-table {
|
||||||
.ant-table-container {
|
.ant-table-container {
|
||||||
@@ -27,27 +18,5 @@
|
|||||||
scrollbar-color: var(--color-scrollbar-thumb) transparent;
|
scrollbar-color: var(--color-scrollbar-thumb) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve a stable block for the empty/loading state so the first-load
|
|
||||||
// spinner and the empty result occupy the same height as eventual data —
|
|
||||||
// this removes the layout jump when entering the page. Scoped to
|
|
||||||
// `.ant-table-content` so it only targets x-scroll tables (whose empty
|
|
||||||
// row lives here) and leaves fixed-height `scroll.y` tables untouched.
|
|
||||||
// Height must match the `minHeight` passed to <NoResult> in
|
|
||||||
// use-no-resource-result.
|
|
||||||
.ant-table-content {
|
|
||||||
.ant-table-placeholder {
|
|
||||||
> .ant-table-cell {
|
|
||||||
height: calc(100vh - 300px);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoResult renders nothing while loading and mounts an <Empty> only
|
|
||||||
// once the request settles, so this fires exactly when the empty
|
|
||||||
// state appears — a seamless fade-in instead of a hard pop.
|
|
||||||
.ant-empty {
|
|
||||||
animation: tableEmptyFadeIn 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { GPUStackVersionAtom } from '@/atoms/user';
|
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||||
|
import { getAtomStorage } from '@/atoms/utils';
|
||||||
import VersionInfo, { modalConfig } from '@/components/version-info';
|
import VersionInfo, { modalConfig } from '@/components/version-info';
|
||||||
import externalLinks from '@/constants/external-links';
|
import externalLinks from '@/constants/external-links';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Divider, Modal, Typography } from 'antd';
|
import { Button, Divider, Modal, Typography } from 'antd';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const CompanyWrapper = styled.div`
|
const CompanyWrapper = styled.div`
|
||||||
@@ -35,7 +35,6 @@ const Footer: React.FC = () => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [modal, contextHolder] = Modal.useModal();
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
const version = useAtomValue(GPUStackVersionAtom);
|
|
||||||
|
|
||||||
const showVersion = () => {
|
const showVersion = () => {
|
||||||
modal.info({
|
modal.info({
|
||||||
@@ -74,7 +73,7 @@ const Footer: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Divider orientation="vertical" />
|
<Divider orientation="vertical" />
|
||||||
<Button type="link" size="small" onClick={showVersion}>
|
<Button type="link" size="small" onClick={showVersion}>
|
||||||
{version?.version}
|
{getAtomStorage(GPUStackVersionAtom)?.version}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// Wrapper around core-ui's FullMarkdown that co-locates the KaTeX stylesheet.
|
|
||||||
//
|
|
||||||
// core-ui deliberately does NOT bundle katex.min.css (importing it there
|
|
||||||
// base64-inlines ~1.4MB of fonts into the shared, render-blocking index.css).
|
|
||||||
// Importing it here keeps the KaTeX CSS in the route chunk that actually
|
|
||||||
// renders math, so it loads lazily and never blocks first paint.
|
|
||||||
//
|
|
||||||
// Always import FullMarkdown from this module, not from '@gpustack/core-ui/markdown'.
|
|
||||||
import { FullMarkdown } from '@gpustack/core-ui/markdown';
|
|
||||||
import 'katex/dist/katex.min.css';
|
|
||||||
|
|
||||||
export default FullMarkdown;
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import Logo from '@/assets/images/gpustack-logo.png';
|
import Logo from '@/assets/images/gpustack-logo.png';
|
||||||
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||||
import externalLinks from '@/constants/external-links';
|
import externalLinks from '@/constants/external-links';
|
||||||
import { useLogo } from '@/hooks/use-logo';
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -19,7 +18,7 @@ const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
|||||||
isProd,
|
isProd,
|
||||||
isDev
|
isDev
|
||||||
} = gpuStackVersionAtom;
|
} = gpuStackVersionAtom;
|
||||||
const { sidebarLogo } = useLogo();
|
|
||||||
// user info
|
// user info
|
||||||
const { is_admin } = userDataAtom || {};
|
const { is_admin } = userDataAtom || {};
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="version-box">
|
<div className="version-box">
|
||||||
<div className="img">
|
<div className="img">
|
||||||
<img src={sidebarLogo || Logo} alt="logo" />
|
<img src={Logo} alt="logo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ver">
|
<div className="ver">
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ export default {
|
|||||||
rowSelectedBg: 'transparent',
|
rowSelectedBg: 'transparent',
|
||||||
headerSortActiveBg: 'transparent',
|
headerSortActiveBg: 'transparent',
|
||||||
headerSortHoverBg: 'transparent',
|
headerSortHoverBg: 'transparent',
|
||||||
bodySortBg: 'transparent',
|
|
||||||
headerBg: 'none'
|
headerBg: 'none'
|
||||||
},
|
},
|
||||||
Button: {
|
Button: {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export default {
|
|||||||
rowSelectedBg: 'transparent',
|
rowSelectedBg: 'transparent',
|
||||||
headerSortActiveBg: 'transparent',
|
headerSortActiveBg: 'transparent',
|
||||||
headerSortHoverBg: 'transparent',
|
headerSortHoverBg: 'transparent',
|
||||||
bodySortBg: 'transparent',
|
|
||||||
headerSplitColor: '#e8e8e8',
|
headerSplitColor: '#e8e8e8',
|
||||||
headerBg: 'none'
|
headerBg: 'none'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ html {
|
|||||||
// ======== container ============
|
// ======== container ============
|
||||||
--color-border-container: #ededed;
|
--color-border-container: #ededed;
|
||||||
--color-text-table-header: #71717a;
|
--color-text-table-header: #71717a;
|
||||||
--seal-table-row-min-height: 68px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme='realDark'] {
|
html[data-theme='realDark'] {
|
||||||
@@ -639,15 +638,10 @@ body {
|
|||||||
inset: 53px auto auto 0 !important;
|
inset: 53px auto auto 0 !important;
|
||||||
border-radius: 0 0 var(--border-radius-base) var(--border-radius-base);
|
border-radius: 0 0 var(--border-radius-base) var(--border-radius-base);
|
||||||
border-top: none;
|
border-top: none;
|
||||||
.ant-cascader-menus {
|
max-height: 240px;
|
||||||
align-items: stretch;
|
min-height: 100px;
|
||||||
}
|
|
||||||
|
|
||||||
&.gpu-selector {
|
&.gpu-selector {
|
||||||
.ant-cascader-menu {
|
|
||||||
max-height: 400px;
|
|
||||||
height: unset;
|
|
||||||
}
|
|
||||||
.ant-cascader-menu:last-child {
|
.ant-cascader-menu:last-child {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
|
|
||||||
@@ -852,6 +846,3 @@ body {
|
|||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
padding-block: 8px;
|
padding-block: 8px;
|
||||||
}
|
}
|
||||||
.ant-select-multiple .ant-select-content {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export default function useActions<T>(actions: Global.ActionItem<T>[], ctx: T) {
|
||||||
|
return actions
|
||||||
|
.filter((action) => {
|
||||||
|
return action.visible ? action.visible(ctx) : true;
|
||||||
|
})
|
||||||
|
.map((action) => ({
|
||||||
|
...action,
|
||||||
|
disabled: action.disabled?.(ctx)
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { message } from 'antd';
|
||||||
|
|
||||||
|
type MessageType = 'input' | 'select';
|
||||||
|
|
||||||
|
const useAppUtils = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type Array<'input' | 'select'>
|
||||||
|
* @param name
|
||||||
|
* @param locale boolean
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getRuleMessage = (
|
||||||
|
type: MessageType | MessageType[],
|
||||||
|
name: string,
|
||||||
|
locale = true
|
||||||
|
) => {
|
||||||
|
const nameStr = locale ? intl.formatMessage({ id: name }) : name;
|
||||||
|
// transform type to array
|
||||||
|
const typeList = Array.isArray(type) ? type : [type];
|
||||||
|
|
||||||
|
if (typeList.includes('select') && typeList.includes('input')) {
|
||||||
|
return intl.formatMessage(
|
||||||
|
{ id: 'common.form.rule.selectInput' },
|
||||||
|
{ name: nameStr }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeList.includes('input')) {
|
||||||
|
return intl.formatMessage(
|
||||||
|
{ id: 'common.form.rule.input' },
|
||||||
|
{ name: nameStr }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return intl.formatMessage(
|
||||||
|
{ id: 'common.form.rule.select' },
|
||||||
|
{ name: nameStr }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showSuccess = (msg?: string) => {
|
||||||
|
messageApi.success(
|
||||||
|
msg || intl.formatMessage({ id: 'common.message.success' })
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getRuleMessage,
|
||||||
|
showSuccess
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAppUtils;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// broadcast channel hook
|
||||||
|
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export const useBroadcast = () => {
|
||||||
|
const broadcastChannel = useRef<BroadcastChannel | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
broadcastChannel.current = new BroadcastChannel('broadcast_channel');
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
broadcastChannel.current?.close();
|
||||||
|
console.log('broadcast channel closed');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { broadcastChannel };
|
||||||
|
};
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
export default function useContainerScroll(
|
||||||
|
container: any,
|
||||||
|
options?: { toBottom?: boolean }
|
||||||
|
) {
|
||||||
|
const isWheeled = useRef(false);
|
||||||
|
const scroller = useRef(container);
|
||||||
|
const optionsRef = useRef(options);
|
||||||
|
const toBottomFlag = useRef(options?.toBottom);
|
||||||
|
const timerRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const debunceResetWheeled = _.debounce(() => {
|
||||||
|
isWheeled.current = false;
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const handleContentWheel = (e: any) => {
|
||||||
|
isWheeled.current = true;
|
||||||
|
debunceResetWheeled.cancel?.();
|
||||||
|
debunceResetWheeled();
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollerRun = () => {
|
||||||
|
const scrollerContainer = scroller.current?.current || {};
|
||||||
|
const { scrollHeight, clientHeight, scrollTop } = scrollerContainer;
|
||||||
|
if (
|
||||||
|
optionsRef.current?.toBottom &&
|
||||||
|
toBottomFlag.current &&
|
||||||
|
scrollHeight > clientHeight + scrollTop
|
||||||
|
) {
|
||||||
|
scroller.current.current.scrollTop = scrollHeight;
|
||||||
|
// toBottomFlag.current = false;
|
||||||
|
isWheeled.current = false;
|
||||||
|
} else if (
|
||||||
|
!isWheeled.current &&
|
||||||
|
scrollHeight > clientHeight + scrollTop &&
|
||||||
|
scroller.current?.current
|
||||||
|
) {
|
||||||
|
scroller.current.current.scrollTop += 10;
|
||||||
|
window.requestAnimationFrame(scrollerRun);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateScrollerPosition = () => {
|
||||||
|
if (!isWheeled.current) {
|
||||||
|
window.requestAnimationFrame(scrollerRun);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleContentWheel,
|
||||||
|
updateScrollerPosition,
|
||||||
|
scroller
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,132 +1,54 @@
|
|||||||
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
|
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
|
||||||
import { clampChroma, formatHex, modeOklch, modeRgb, useMode } from 'culori/fn';
|
import { converter, modeHsl, modeRgb, useMode } from 'culori/fn';
|
||||||
import useUserSettings from './use-user-settings';
|
|
||||||
|
|
||||||
// Linear-style minimal/neutral palette: a TIGHT blue -> violet band in OKLCH
|
const toHsl = converter('hsl');
|
||||||
// (perceptually uniform), kept moderate-chroma so fills read as clean and gentle
|
|
||||||
// rather than candy-colored. We deliberately stay narrow and DON'T fan out to
|
const DEFAULT_BRAND_HUE = 211; // brand color
|
||||||
// green/magenta — in this aesthetic series are separated by LIGHTNESS, not by
|
const COOL_HUE_START = 180;
|
||||||
// spreading across the hue wheel. Every series (including the first) is generated
|
const COOL_HUE_END = 280;
|
||||||
// from this one ramp, so the whole palette stays in a single cohesive color
|
|
||||||
// family — no special high-saturation brand color that clashes with the rest.
|
|
||||||
const COOL_HUE_START = 250; // blue
|
|
||||||
const COOL_HUE_END = 315; // violet (stops short of magenta/pink, stays neutral-cool)
|
|
||||||
const COOL_HUE_RANGE = COOL_HUE_END - COOL_HUE_START;
|
const COOL_HUE_RANGE = COOL_HUE_END - COOL_HUE_START;
|
||||||
|
const GOLDEN_RATIO_CONJUGATE = 0.618033988749895;
|
||||||
|
|
||||||
/**
|
function colorStringToHue(input?: string): number | undefined {
|
||||||
* Vivid, distinct cool accents — for places that need a handful of "primary"
|
if (!input) return undefined;
|
||||||
* colors, one per card/section (e.g. the summary trend cards), NOT a stacked
|
const color = toHsl(input);
|
||||||
* multi-series palette. Every color is anchor-quality (bright + saturated) and
|
if (!color || typeof color.h !== 'number') return undefined;
|
||||||
* spread evenly across the blue→violet band, so the set reads as several equally
|
return color.h;
|
||||||
* strong primaries rather than one bold + several washed-out fills.
|
|
||||||
*/
|
|
||||||
export function useCoolAccents() {
|
|
||||||
useMode(modeRgb);
|
|
||||||
useMode(modeOklch);
|
|
||||||
|
|
||||||
const { isDarkTheme } = useUserSettings();
|
|
||||||
|
|
||||||
return useMemoizedFn((count: number): string[] => {
|
|
||||||
if (count <= 0) return [];
|
|
||||||
|
|
||||||
const l = isDarkTheme ? 0.62 : 0.64;
|
|
||||||
const c = isDarkTheme ? 0.16 : 0.2;
|
|
||||||
|
|
||||||
const out: string[] = [];
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const t = count <= 1 ? 0 : i / (count - 1);
|
|
||||||
const hue = COOL_HUE_START + t * COOL_HUE_RANGE;
|
|
||||||
out.push(
|
|
||||||
formatHex(clampChroma({ mode: 'oklch', l, c, h: hue }, 'oklch'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useCoolColors() {
|
export default function useCoolColors() {
|
||||||
|
// const brandHue = useMemo(
|
||||||
|
// () => colorStringToHue(userSettings.colorPrimary) ?? DEFAULT_BRAND_HUE,
|
||||||
|
// [userSettings.colorPrimary]
|
||||||
|
// );
|
||||||
useMode(modeRgb);
|
useMode(modeRgb);
|
||||||
useMode(modeOklch);
|
useMode(modeHsl);
|
||||||
|
|
||||||
const { isDarkTheme } = useUserSettings();
|
const brandHue = DEFAULT_BRAND_HUE;
|
||||||
|
|
||||||
return useMemoizedFn((count: number): string[] => {
|
return useMemoizedFn((count: number): string[] => {
|
||||||
if (count <= 0) return [];
|
if (count <= 0) return [];
|
||||||
|
|
||||||
// Moderate chroma: clean and crisp, but gentle (not neon). Too low reads as
|
const colors: string[] = [];
|
||||||
// muddy/dirty; too high reads as harsh. Dark mode a touch lower so fills
|
|
||||||
// stay calm against the dark canvas.
|
|
||||||
const baseChroma = isDarkTheme ? 0.085 : 0.12;
|
|
||||||
|
|
||||||
// First series is the "primary" anchor: SAME blue family (the bluest end of
|
for (let i = 0; i < count; i++) {
|
||||||
// the ramp, nearest the brand hue) but clearly brighter and more saturated —
|
let hue: number;
|
||||||
// a vivid, clean brand-blue that reads as the base color. The rest of the
|
|
||||||
// palette stays low-chroma, so the anchor pops as the primary while the
|
|
||||||
// family still feels cohesive.
|
|
||||||
const colors: string[] = [
|
|
||||||
formatHex(
|
|
||||||
clampChroma(
|
|
||||||
{
|
|
||||||
mode: 'oklch',
|
|
||||||
l: isDarkTheme ? 0.62 : 0.66,
|
|
||||||
c: isDarkTheme ? 0.16 : 0.2,
|
|
||||||
h: COOL_HUE_START
|
|
||||||
},
|
|
||||||
'oklch'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
const rest = count - 1;
|
if (i === 0) {
|
||||||
if (rest <= 0) return colors;
|
hue = brandHue - 5;
|
||||||
|
} else {
|
||||||
|
const offset = (i * GOLDEN_RATIO_CONJUGATE) % 1;
|
||||||
|
hue = COOL_HUE_START + offset * COOL_HUE_RANGE;
|
||||||
|
|
||||||
// Separation is driven by LIGHTNESS, not hue. The hue band is narrow, so as
|
if (Math.abs(hue - brandHue) < 5) {
|
||||||
// the count grows we add lightness TIERS — each tier reuses the same narrow
|
hue = (hue + 10) % COOL_HUE_END;
|
||||||
// hue ramp at a distinct lightness level, multiplying how many separable
|
}
|
||||||
// colors fit while keeping the whole palette in one cohesive family.
|
}
|
||||||
const tiers = rest <= 6 ? 1 : rest <= 12 ? 2 : 3;
|
|
||||||
const steps = Math.ceil(rest / tiers);
|
|
||||||
|
|
||||||
// Distinct lightness levels per tier count (index 0 → 2 tiers, 1 → 3 tiers).
|
const s = i === 0 ? 100 : 75 + (i % 3) * 5;
|
||||||
// Lighter, airier levels for a fresh/crisp feel; kept in the upper-mid range
|
const l = i === 0 ? 50 : 55 + (i % 2) * 5;
|
||||||
// so fills stay clean and legible.
|
|
||||||
const lightTiers = isDarkTheme
|
|
||||||
? [
|
|
||||||
[0.68, 0.5],
|
|
||||||
[0.72, 0.6, 0.48]
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
[0.82, 0.64],
|
|
||||||
[0.84, 0.72, 0.6]
|
|
||||||
];
|
|
||||||
// Single-tier light/dark zig-zag for the common small-count case.
|
|
||||||
const [lightLo, lightHi] = isDarkTheme ? [0.5, 0.68] : [0.64, 0.82];
|
|
||||||
|
|
||||||
for (let i = 0; i < rest; i++) {
|
colors.push(`hsl(${Math.round(hue)}, ${s}%, ${l}%)`);
|
||||||
// Cycle the tier on every step so consecutive series always differ in
|
|
||||||
// lightness — exactly where stacked bars are hardest to tell apart.
|
|
||||||
const tier = i % tiers;
|
|
||||||
const step = Math.floor(i / tiers);
|
|
||||||
|
|
||||||
// Even hue ramp across the narrow band. Each tier is offset by a fraction
|
|
||||||
// of a step so same-step colors in different tiers don't share a hue.
|
|
||||||
const t = steps <= 1 ? 0.5 : (step + tier / tiers) / steps;
|
|
||||||
const hue = COOL_HUE_START + t * COOL_HUE_RANGE;
|
|
||||||
|
|
||||||
// 1 tier (≤6 series): light/dark zig-zag. 2-3 tiers: the tier's level.
|
|
||||||
const lightness =
|
|
||||||
tiers === 1 ? (i % 2 ? lightLo : lightHi) : lightTiers[tiers - 2][tier];
|
|
||||||
|
|
||||||
// Clamp chroma into the sRGB gamut so values don't get distorted by a raw
|
|
||||||
// channel clip when serialized to hex.
|
|
||||||
colors.push(
|
|
||||||
formatHex(
|
|
||||||
clampChroma(
|
|
||||||
{ mode: 'oklch', l: lightness, c: baseChroma, h: hue },
|
|
||||||
'oklch'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return colors;
|
return colors;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
const useCopyToClipboard = () => {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
const copyToClipboard = useCallback(async (text: string) => {
|
||||||
|
try {
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopied(false);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setCopied(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { copied, copyToClipboard };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCopyToClipboard;
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { HandlerOptions } from '@/hooks/use-chunk-fetch';
|
||||||
|
import { useDownloadStream } from '@gpustack/core-ui';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Progress, notification } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const renderMessage = (title: string) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 280,
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createFileName = (name: string) => {
|
||||||
|
const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss');
|
||||||
|
const fileName = `${name}_${timestamp}.txt`;
|
||||||
|
return fileName;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDownloadLogs = () => {
|
||||||
|
const { downloadStream } = useDownloadStream();
|
||||||
|
const intl = useIntl();
|
||||||
|
const [api, contextHolder] = notification.useNotification({
|
||||||
|
stack: { threshold: 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
const downloadNotification = (
|
||||||
|
data: HandlerOptions & {
|
||||||
|
filename: string;
|
||||||
|
duration?: number;
|
||||||
|
chunkRequestRef: any;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
api.open({
|
||||||
|
duration: data.duration,
|
||||||
|
message: renderMessage(data.filename),
|
||||||
|
key: data.filename,
|
||||||
|
closeIcon: (
|
||||||
|
<span>{intl.formatMessage({ id: 'common.button.cancel' })}</span>
|
||||||
|
),
|
||||||
|
description: <Progress percent={data.percent} size="small"></Progress>,
|
||||||
|
onClose() {
|
||||||
|
data.chunkRequestRef?.current?.abort();
|
||||||
|
notification.destroy?.(data.filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownloadLog = async (params: { url: string; name: string }) => {
|
||||||
|
downloadStream({
|
||||||
|
url: params.url,
|
||||||
|
filename: createFileName(params.name),
|
||||||
|
downloadNotification
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDownloadLog: handleDownloadLog,
|
||||||
|
contextHolder
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDownloadLogs;
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import useSetChunkFetch, { HandlerOptions } from '@/hooks/use-chunk-fetch';
|
||||||
|
import { message } from 'antd';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export default function useDownloadStream() {
|
||||||
|
const chunkRequestRef = useRef<any>(null);
|
||||||
|
const logParseWorker = useRef<any>(null);
|
||||||
|
const clearScreen = useRef(false);
|
||||||
|
const filename = useRef('log');
|
||||||
|
const downloadNotificationRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const { setChunkFetch } = useSetChunkFetch();
|
||||||
|
|
||||||
|
const downloadFile = (content: string) => {
|
||||||
|
const blob = new Blob([content], { type: 'text/plain' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename.current;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateContent = (data: string, options?: HandlerOptions) => {
|
||||||
|
const { isComplete, percent } = options || {};
|
||||||
|
|
||||||
|
logParseWorker.current?.postMessage({
|
||||||
|
inputStr: data,
|
||||||
|
reset: clearScreen.current,
|
||||||
|
isComplete: isComplete,
|
||||||
|
percent: percent,
|
||||||
|
chunked: false
|
||||||
|
});
|
||||||
|
clearScreen.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleError = (error: any) => {
|
||||||
|
const errorMsg = error?.message || error;
|
||||||
|
const msg =
|
||||||
|
typeof errorMsg === 'string' ? errorMsg : JSON.stringify(errorMsg);
|
||||||
|
message.error(msg);
|
||||||
|
downloadNotificationRef.current?.({
|
||||||
|
duration: 1,
|
||||||
|
percent: 0,
|
||||||
|
filename: filename.current
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadStream = async (props: {
|
||||||
|
data?: any;
|
||||||
|
url: string;
|
||||||
|
params?: any;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
method?: string;
|
||||||
|
headers?: any;
|
||||||
|
filename?: string;
|
||||||
|
downloadNotification?: (data: any) => void;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
clearScreen.current = true;
|
||||||
|
filename.current = props.filename || 'log';
|
||||||
|
downloadNotificationRef.current = props.downloadNotification;
|
||||||
|
const { params, url } = props;
|
||||||
|
|
||||||
|
chunkRequestRef.current?.current?.abort?.();
|
||||||
|
|
||||||
|
chunkRequestRef.current = setChunkFetch({
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
watch: false,
|
||||||
|
contentType: 'text',
|
||||||
|
errorHandler: handleError,
|
||||||
|
handler: updateContent
|
||||||
|
});
|
||||||
|
downloadNotificationRef.current?.({
|
||||||
|
filename: filename.current,
|
||||||
|
duration: null,
|
||||||
|
chunkRequestRef: chunkRequestRef.current
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
downloadNotificationRef.current?.({
|
||||||
|
duration: 1,
|
||||||
|
percent: 0,
|
||||||
|
filename: filename.current
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
logParseWorker.current?.terminate?.();
|
||||||
|
|
||||||
|
logParseWorker.current = new Worker(
|
||||||
|
// @ts-ignore
|
||||||
|
new URL('@/components/logs-viewer/parse-worker.ts', import.meta.url),
|
||||||
|
{
|
||||||
|
type: 'module'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
logParseWorker.current.onmessage = (event: any) => {
|
||||||
|
const { result, isComplete, percent } = event.data;
|
||||||
|
|
||||||
|
const isAborted = chunkRequestRef.current?.current?.signal?.aborted;
|
||||||
|
if (!isComplete && !isAborted) {
|
||||||
|
downloadNotificationRef.current?.({
|
||||||
|
percent: percent,
|
||||||
|
duration: null,
|
||||||
|
filename: filename.current,
|
||||||
|
chunkRequestRef: chunkRequestRef.current
|
||||||
|
});
|
||||||
|
} else if (isComplete && !isAborted) {
|
||||||
|
downloadNotificationRef.current?.({
|
||||||
|
duration: 1,
|
||||||
|
percent: 100,
|
||||||
|
filename: filename.current
|
||||||
|
});
|
||||||
|
downloadFile(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (logParseWorker.current) {
|
||||||
|
logParseWorker.current.terminate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
downloadStream
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { driver, type Config } from 'driver.js';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export const useDriver = (config?: Config & { id: string }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const driverRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const handleDoNotShowAgain = () => {};
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
driverRef.current = driver({
|
||||||
|
overlayOpacity: 0.2,
|
||||||
|
animate: false,
|
||||||
|
...config
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const start = () => {
|
||||||
|
if (!driverRef.current) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
driverRef.current.drive();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
driverRef.current?.destroy();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { start, initDriver: init, driver: driverRef.current };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDriver;
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import HotKeys from '@/config/hotkeys';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import { throttle } from 'lodash';
|
||||||
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ css, token }) => ({
|
||||||
|
hintOverlay: css`
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: var(--color-esc-hint-bg);
|
||||||
|
color: ${token.colorTextLightSolid};
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 2000;
|
||||||
|
font-size: 14px;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: fadeInOut 2s ease-in-out;
|
||||||
|
@keyframes fadeInOut {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}));
|
||||||
|
|
||||||
|
export function useEscHint(options?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
message?: string;
|
||||||
|
throttleDelay?: number;
|
||||||
|
}) {
|
||||||
|
const { enabled = true, message, throttleDelay = 3000 } = options || {};
|
||||||
|
const intl = useIntl();
|
||||||
|
const { styles } = useStyles();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const timeoutRef = useRef<any>(null);
|
||||||
|
const isHintActiveRef = useRef(false);
|
||||||
|
|
||||||
|
const showHintThrottled = useMemo(
|
||||||
|
() =>
|
||||||
|
throttle(
|
||||||
|
() => {
|
||||||
|
if (isHintActiveRef.current) return;
|
||||||
|
|
||||||
|
isHintActiveRef.current = true;
|
||||||
|
|
||||||
|
setVisible(true);
|
||||||
|
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutRef.current = setTimeout(() => {
|
||||||
|
setVisible(false);
|
||||||
|
isHintActiveRef.current = false;
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
throttleDelay,
|
||||||
|
{
|
||||||
|
leading: true,
|
||||||
|
trailing: false
|
||||||
|
}
|
||||||
|
),
|
||||||
|
[throttleDelay]
|
||||||
|
);
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
HotKeys.ESC,
|
||||||
|
() => {
|
||||||
|
if (!enabled) return;
|
||||||
|
showHintThrottled();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: enabled
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
}
|
||||||
|
showHintThrottled.cancel();
|
||||||
|
};
|
||||||
|
}, [showHintThrottled]);
|
||||||
|
|
||||||
|
const EscHint = visible ? (
|
||||||
|
<div className={styles.hintOverlay}>
|
||||||
|
{message || intl.formatMessage({ id: 'common.tips.escape.disable' })}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return { EscHint };
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import qs from 'query-string';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export const createEventSourceURL = (url: string) => {
|
||||||
|
const { host, protocol } = window.location;
|
||||||
|
return `${protocol}://${host}${url}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
0: connecting
|
||||||
|
1: connect successfully
|
||||||
|
2: closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function useEventSource() {
|
||||||
|
const eventSourceRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const createEventSourceConnection = (query: {
|
||||||
|
url: string;
|
||||||
|
params: any;
|
||||||
|
onmessage?: (data: any) => void;
|
||||||
|
}) => {
|
||||||
|
eventSourceRef.current?.close?.();
|
||||||
|
|
||||||
|
const { url, params, onmessage = () => {} } = query;
|
||||||
|
|
||||||
|
const sseurl = createEventSourceURL(url);
|
||||||
|
|
||||||
|
eventSourceRef.current = new EventSource(
|
||||||
|
`${url}?${qs.stringify({
|
||||||
|
...params
|
||||||
|
})}`,
|
||||||
|
{
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
eventSourceRef.current.onmessage = (res: any) => {
|
||||||
|
try {
|
||||||
|
console.log('event source message: ', { res, resData: res });
|
||||||
|
const data = JSON.parse(res.data);
|
||||||
|
onmessage(data);
|
||||||
|
} catch (error) {
|
||||||
|
// error
|
||||||
|
console.log('event source error: ', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
eventSourceRef.current.onclose = () => {
|
||||||
|
console.log('event source closed...');
|
||||||
|
};
|
||||||
|
|
||||||
|
eventSourceRef.current.onopen = () => {
|
||||||
|
console.log('event source connected...');
|
||||||
|
};
|
||||||
|
|
||||||
|
eventSourceRef.current.onerror = (error: any) => {
|
||||||
|
console.log('event source error: ', error);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
eventSourceRef.current?.close?.();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventSourceRef: eventSourceRef,
|
||||||
|
createEventSourceConnection
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { throttle } from 'lodash';
|
||||||
|
import {
|
||||||
|
UseOverlayScrollbarsParams,
|
||||||
|
useOverlayScrollbars
|
||||||
|
} from 'overlayscrollbars-react';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import useUserSettings from './use-user-settings';
|
||||||
|
|
||||||
|
type OverflowBehavior =
|
||||||
|
| 'hidden'
|
||||||
|
| 'scroll'
|
||||||
|
| 'visible'
|
||||||
|
| 'visible-hidden'
|
||||||
|
| 'visible-scroll';
|
||||||
|
export interface OverlayScrollerOptions {
|
||||||
|
oppositeTheme?: boolean;
|
||||||
|
overflow?: {
|
||||||
|
x?: OverflowBehavior;
|
||||||
|
y?: OverflowBehavior;
|
||||||
|
};
|
||||||
|
scrollbars?: {
|
||||||
|
theme?: 'os-theme-light' | 'os-theme-dark';
|
||||||
|
autoHide?: 'never' | 'scroll' | 'leave' | 'move';
|
||||||
|
autoHideDelay?: number;
|
||||||
|
clickScroll?: boolean | 'instant';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const overlaySollerOptions: UseOverlayScrollbarsParams = {
|
||||||
|
options: {
|
||||||
|
update: {
|
||||||
|
debounce: 0
|
||||||
|
},
|
||||||
|
overflow: {
|
||||||
|
x: 'hidden'
|
||||||
|
},
|
||||||
|
scrollbars: {
|
||||||
|
theme: 'os-theme-light',
|
||||||
|
autoHide: 'scroll',
|
||||||
|
autoHideDelay: 600,
|
||||||
|
clickScroll: 'instant'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defer: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const RESETSCROLLDELAY = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param options.theme: if set theme, it will fix the theme
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useOverlayScroller(data?: {
|
||||||
|
options?: OverlayScrollerOptions;
|
||||||
|
events?: any;
|
||||||
|
defer?: boolean;
|
||||||
|
}) {
|
||||||
|
const { userSettings } = useUserSettings();
|
||||||
|
const { options, events, defer = true } = data || {};
|
||||||
|
const { scrollbars, overflow, oppositeTheme } = options || {};
|
||||||
|
const scrollEventElement = React.useRef<any>(null);
|
||||||
|
const instanceRef = React.useRef<any>(null);
|
||||||
|
const initialized = React.useRef(false);
|
||||||
|
const scrollElementRef = React.useRef<any>(null);
|
||||||
|
const stopUpdatePosition = React.useRef(false);
|
||||||
|
const timerRef = React.useRef<any>(null);
|
||||||
|
const [initialize, instance] = useOverlayScrollbars({
|
||||||
|
options: {
|
||||||
|
update: {
|
||||||
|
debounce: 0
|
||||||
|
},
|
||||||
|
overflow: {
|
||||||
|
x: 'hidden',
|
||||||
|
...overflow
|
||||||
|
},
|
||||||
|
scrollbars: {
|
||||||
|
autoHide: 'scroll',
|
||||||
|
autoHideDelay: 600,
|
||||||
|
clickScroll: 'instant',
|
||||||
|
...scrollbars,
|
||||||
|
theme:
|
||||||
|
scrollbars?.theme ||
|
||||||
|
(userSettings.theme === 'light' || !userSettings.theme
|
||||||
|
? 'os-theme-dark'
|
||||||
|
: 'os-theme-light')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
...events
|
||||||
|
},
|
||||||
|
defer: defer
|
||||||
|
});
|
||||||
|
|
||||||
|
instanceRef.current = instance?.();
|
||||||
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
|
||||||
|
const handleOnScroll = () => {
|
||||||
|
const scrollTop = scrollEventElement.current?.scrollTop;
|
||||||
|
const scrollHeight = scrollEventElement.current?.scrollHeight;
|
||||||
|
const clientHeight = scrollEventElement.current?.clientHeight;
|
||||||
|
|
||||||
|
const isBottom = scrollTop + clientHeight + 20 >= scrollHeight;
|
||||||
|
|
||||||
|
if (isBottom) {
|
||||||
|
stopUpdatePosition.current = false;
|
||||||
|
} else {
|
||||||
|
stopUpdatePosition.current = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const throttledScroll = useMemoizedFn(
|
||||||
|
throttle(() => {
|
||||||
|
scrollEventElement.current?.scrollTo?.({
|
||||||
|
top: scrollEventElement.current?.scrollHeight,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
instanceRef.current?.update?.();
|
||||||
|
}, 100)
|
||||||
|
);
|
||||||
|
|
||||||
|
const scrollauto = useMemoizedFn(() => {
|
||||||
|
scrollEventElement.current?.scrollTo?.({
|
||||||
|
top: scrollEventElement.current.scrollHeight,
|
||||||
|
behavior: 'auto'
|
||||||
|
});
|
||||||
|
instanceRef.current?.update?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
// scroll to bottom
|
||||||
|
const throttledUpdateScrollerPosition = useMemoizedFn((delay?: number) => {
|
||||||
|
if (stopUpdatePosition.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (delay === 0) {
|
||||||
|
scrollauto();
|
||||||
|
} else {
|
||||||
|
throttledScroll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// scroll to top
|
||||||
|
const updateScrollerPositionToTop = useMemoizedFn(() => {
|
||||||
|
scrollEventElement.current?.scrollTo?.({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'auto'
|
||||||
|
});
|
||||||
|
instanceRef.current?.update?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
const generateInstance = () => {
|
||||||
|
instanceRef.current = instance?.();
|
||||||
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWheelCallback = useMemoizedFn((e: any) => {
|
||||||
|
handleOnScroll();
|
||||||
|
if (timerRef.current) {
|
||||||
|
clearTimeout(timerRef.current);
|
||||||
|
}
|
||||||
|
timerRef.current = setTimeout(() => {
|
||||||
|
stopUpdatePosition.current = false;
|
||||||
|
}, RESETSCROLLDELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
// add wheel event
|
||||||
|
const handleWheelEvent = () => {
|
||||||
|
scrollElementRef.current?.addEventListener?.('wheel', handleWheelCallback, {
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove wheel event
|
||||||
|
const removeWheelEvent = () => {
|
||||||
|
scrollElementRef.current?.removeEventListener?.(
|
||||||
|
'wheel',
|
||||||
|
handleWheelCallback,
|
||||||
|
{ passive: true }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createInstance = useMemoizedFn((el: any) => {
|
||||||
|
if (instanceRef.current) {
|
||||||
|
return instanceRef.current;
|
||||||
|
}
|
||||||
|
if (el) {
|
||||||
|
initialize(el);
|
||||||
|
scrollElementRef.current = el;
|
||||||
|
initialized.current = true;
|
||||||
|
instanceRef.current = instance?.();
|
||||||
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
handleWheelEvent();
|
||||||
|
}
|
||||||
|
return instanceRef.current;
|
||||||
|
});
|
||||||
|
|
||||||
|
const destroyInstance = () => {
|
||||||
|
instanceRef.current?.destroy?.();
|
||||||
|
removeWheelEvent();
|
||||||
|
instanceRef.current = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollToTarget = (target: any, offset = 100) => {
|
||||||
|
if (!target) return;
|
||||||
|
if (!instanceRef.current || !scrollEventElement.current) {
|
||||||
|
instanceRef.current = instance?.();
|
||||||
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewport = instanceRef.current?.elements().viewport;
|
||||||
|
const containerRect = viewport.getBoundingClientRect();
|
||||||
|
const targetRect = target.getBoundingClientRect();
|
||||||
|
|
||||||
|
const scrollerState = instanceRef.current?.state();
|
||||||
|
|
||||||
|
const currentScroll = scrollerState.current?.overflowAmount?.y;
|
||||||
|
|
||||||
|
// const currentScroll = instanceRef.current?.scroll().position.y;
|
||||||
|
const targetPos = targetRect.top - containerRect.top + currentScroll;
|
||||||
|
|
||||||
|
scrollEventElement.current.scroll({
|
||||||
|
y: targetPos - offset,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
instanceRef.current?.update?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScrollElementScrollableHeight = () => {
|
||||||
|
if (!instanceRef.current || !scrollEventElement.current) {
|
||||||
|
instanceRef.current = instance?.();
|
||||||
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollOffsetElement = instanceRef.current?.elements().viewport;
|
||||||
|
|
||||||
|
return {
|
||||||
|
scrollTop: scrollOffsetElement?.scrollTop,
|
||||||
|
scrollHeight:
|
||||||
|
scrollOffsetElement?.scrollHeight - scrollOffsetElement?.clientHeight
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScrollElement = () => {
|
||||||
|
if (!instanceRef.current || !scrollEventElement.current) {
|
||||||
|
instanceRef.current = instance?.();
|
||||||
|
scrollEventElement.current =
|
||||||
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
}
|
||||||
|
return scrollEventElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
instanceRef.current?.destroy?.();
|
||||||
|
removeWheelEvent();
|
||||||
|
};
|
||||||
|
}, [instance]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialize: createInstance,
|
||||||
|
instance: instanceRef,
|
||||||
|
scrollEventElement: scrollEventElement,
|
||||||
|
initialized: initialized.current,
|
||||||
|
getScrollElementScrollableHeight,
|
||||||
|
getScrollElement,
|
||||||
|
generateInstance,
|
||||||
|
destroyInstance: destroyInstance,
|
||||||
|
updateScrollerPosition: throttledUpdateScrollerPosition,
|
||||||
|
updateScrollerPositionToTop: updateScrollerPositionToTop,
|
||||||
|
scrollToBottom: scrollauto,
|
||||||
|
scrollToTop: updateScrollerPositionToTop,
|
||||||
|
scrollToTarget
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -40,11 +40,6 @@ export default function useWatchList<T = Record<string, any>>(API: string) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const cancelWatch = useMemoizedFn(() => {
|
|
||||||
chunkRequestRef.current?.current?.cancel?.();
|
|
||||||
listRequestTokenRef.current?.cancel?.();
|
|
||||||
});
|
|
||||||
|
|
||||||
const queryAllDataList = async (
|
const queryAllDataList = async (
|
||||||
params: Global.SearchParams,
|
params: Global.SearchParams,
|
||||||
options?: any
|
options?: any
|
||||||
@@ -83,15 +78,14 @@ export default function useWatchList<T = Record<string, any>>(API: string) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
createWatchChunkRequest();
|
createWatchChunkRequest();
|
||||||
return () => {
|
return () => {
|
||||||
cancelWatch();
|
chunkRequestRef.current?.cancel?.();
|
||||||
|
listRequestTokenRef.current?.cancel?.();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
watchDataList,
|
watchDataList,
|
||||||
setWatchDataList,
|
setWatchDataList,
|
||||||
startWatch: createWatchChunkRequest,
|
|
||||||
cancelWatch,
|
|
||||||
deleteItemFromCache: handleDeleteItemFromCache
|
deleteItemFromCache: handleDeleteItemFromCache
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import VersionInfo, { modalConfig } from '@/components/version-info';
|
|||||||
import externalLinks from '@/constants/external-links';
|
import externalLinks from '@/constants/external-links';
|
||||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||||
import { logout } from '@/pages/login/apis';
|
import { logout } from '@/pages/login/apis';
|
||||||
import { getGPUStackPlugin } from '@/plugins';
|
|
||||||
import { useModel } from '@@/plugin-model';
|
import { useModel } from '@@/plugin-model';
|
||||||
import {
|
import {
|
||||||
DiscordOutlined,
|
DiscordOutlined,
|
||||||
@@ -99,7 +98,6 @@ const CustomItem = styled.div`
|
|||||||
|
|
||||||
export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||||
const { isDarkTheme } = props;
|
const { isDarkTheme } = props;
|
||||||
const plugin = getGPUStackPlugin();
|
|
||||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
const [modal, contextHolder] = Modal.useModal();
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
const [version] = useAtom(GPUStackVersionAtom);
|
const [version] = useAtom(GPUStackVersionAtom);
|
||||||
@@ -288,17 +286,15 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
|||||||
</NewLabel>
|
</NewLabel>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!plugin && (
|
<DropdownActions menu={{ ...helpMenu }} popupRender={helpPopupRender}>
|
||||||
<DropdownActions menu={{ ...helpMenu }} popupRender={helpPopupRender}>
|
<IconWrapper>
|
||||||
<IconWrapper>
|
<IconFont
|
||||||
<IconFont
|
type="icon-help"
|
||||||
type="icon-help"
|
className="font-size-20"
|
||||||
className="font-size-20"
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
/>
|
||||||
/>
|
</IconWrapper>
|
||||||
</IconWrapper>
|
</DropdownActions>
|
||||||
</DropdownActions>
|
|
||||||
)}
|
|
||||||
<PluginExtraField name="GlobalSettings" />
|
<PluginExtraField name="GlobalSettings" />
|
||||||
<DropdownActions menu={{ ...userMenu }} popupRender={userPopupRender}>
|
<DropdownActions menu={{ ...userMenu }} popupRender={userPopupRender}>
|
||||||
<IconWrapper>
|
<IconWrapper>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import useUserSettings from '@/hooks/use-user-settings';
|
|||||||
import useUserSettingsStorage from '@/hooks/use-user-settings-storage';
|
import useUserSettingsStorage from '@/hooks/use-user-settings-storage';
|
||||||
import useAddResource from '@/pages/dashboard/hooks/use-add-resource';
|
import useAddResource from '@/pages/dashboard/hooks/use-add-resource';
|
||||||
import { logout } from '@/pages/login/apis';
|
import { logout } from '@/pages/login/apis';
|
||||||
import { didInitialStateProbe, probeAccessFlags } from '@/utils/access-probes';
|
|
||||||
import {
|
import {
|
||||||
readColumnSettings,
|
readColumnSettings,
|
||||||
readState,
|
readState,
|
||||||
@@ -42,7 +41,7 @@ import {
|
|||||||
import { Button, ConfigProvider, Modal, theme } from 'antd';
|
import { Button, ConfigProvider, Modal, theme } from 'antd';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import 'overlayscrollbars/overlayscrollbars.css';
|
import 'overlayscrollbars/overlayscrollbars.css';
|
||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import { PageContainerInner } from '../pages/_components/page-box';
|
import { PageContainerInner } from '../pages/_components/page-box';
|
||||||
import Exception from './Exception';
|
import Exception from './Exception';
|
||||||
import './Layout.css';
|
import './Layout.css';
|
||||||
@@ -52,6 +51,21 @@ import { ExtraContent } from './extraRender';
|
|||||||
import { patchRoutes } from './runtime';
|
import { patchRoutes } from './runtime';
|
||||||
import SiderMenu from './sider-menu';
|
import SiderMenu from './sider-menu';
|
||||||
|
|
||||||
|
// Pages that use the page container in the page
|
||||||
|
const NO_CONTAINER_PAGES = [
|
||||||
|
'chat',
|
||||||
|
'rerank',
|
||||||
|
'embedding',
|
||||||
|
'speech',
|
||||||
|
'image',
|
||||||
|
'text2images',
|
||||||
|
'clusterDetail',
|
||||||
|
'clusterCreate',
|
||||||
|
'benchmarkDetail',
|
||||||
|
'deployment',
|
||||||
|
'video'
|
||||||
|
];
|
||||||
|
|
||||||
const CHECK_RESOURCE_PATH = [
|
const CHECK_RESOURCE_PATH = [
|
||||||
'/resources/workers',
|
'/resources/workers',
|
||||||
'/resources/clusters/list',
|
'/resources/clusters/list',
|
||||||
@@ -141,50 +155,6 @@ export default (props: any) => {
|
|||||||
|
|
||||||
const { initialState, loading, setInitialState } = initialInfo;
|
const { initialState, loading, setInitialState } = initialInfo;
|
||||||
const access = useAccess();
|
const access = useAccess();
|
||||||
const probedForUserRef = useRef<string | null>(null);
|
|
||||||
|
|
||||||
// Backfill the access probes (cluster / resource-events) once we're in
|
|
||||||
// the authenticated shell. `getInitialState` runs only once at app
|
|
||||||
// boot and can't probe on the login page (no session => 401), so after
|
|
||||||
// a first login the flags arrive here as `undefined` — which the
|
|
||||||
// access predicate treats as "don't restrict", flashing GPU Service /
|
|
||||||
// the full Usage page on until a manual refresh. This layout mounts
|
|
||||||
// only post-auth (login is `layout:false`) and on every entry, so it's
|
|
||||||
// the reliable place to resolve them.
|
|
||||||
//
|
|
||||||
// The effect is keyed on the user IDENTITY, not on the flag values —
|
|
||||||
// gating on the flags is what made this fragile (on a refresh
|
|
||||||
// `getInitialState` commits `currentUser` and the flags in the same
|
|
||||||
// update, so a flag-gated effect sees them already-known and never
|
|
||||||
// fires). Whether to actually hit the network is decided by the
|
|
||||||
// module-scoped `didInitialStateProbe()` marker, which is true only
|
|
||||||
// when `getInitialState` already probed for an authenticated user this
|
|
||||||
// page load (the refresh path) — so we skip the duplicate request there
|
|
||||||
// but still probe on the SPA-login path. Keying on identity also
|
|
||||||
// re-probes correctly if the signed-in user changes.
|
|
||||||
const currentUser = initialState?.currentUser;
|
|
||||||
const username = currentUser?.username;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!username || !setInitialState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (probedForUserRef.current === username) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
probedForUserRef.current = username;
|
|
||||||
// The refresh path already resolved the flags inside `getInitialState`
|
|
||||||
// for this user — don't issue a duplicate probe.
|
|
||||||
if (didInitialStateProbe()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
probeAccessFlags().then((accessFlags) => {
|
|
||||||
setInitialState((prev: any) => ({
|
|
||||||
...prev,
|
|
||||||
...accessFlags
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}, [username, setInitialState]);
|
|
||||||
|
|
||||||
const userConfig = {
|
const userConfig = {
|
||||||
title: '',
|
title: '',
|
||||||
@@ -254,6 +224,11 @@ export default (props: any) => {
|
|||||||
[location.pathname]
|
[location.pathname]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isNoContainerPage = useMemo(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
return NO_CONTAINER_PAGES.includes(matchedRoute?.name as string);
|
||||||
|
}, [matchedRoute]);
|
||||||
|
|
||||||
const collapsed = useMemo(() => {
|
const collapsed = useMemo(() => {
|
||||||
return userSettings.collapsed || false;
|
return userSettings.collapsed || false;
|
||||||
}, [userSettings.collapsed]);
|
}, [userSettings.collapsed]);
|
||||||
@@ -449,11 +424,15 @@ export default (props: any) => {
|
|||||||
unAccessible={runtimeConfig?.unAccessible}
|
unAccessible={runtimeConfig?.unAccessible}
|
||||||
noAccessible={runtimeConfig?.noAccessible}
|
noAccessible={runtimeConfig?.noAccessible}
|
||||||
>
|
>
|
||||||
<PageContainerInner>
|
{isNoContainerPage ? (
|
||||||
<div>
|
<Outlet />
|
||||||
<Outlet />
|
) : (
|
||||||
</div>
|
<PageContainerInner>
|
||||||
</PageContainerInner>
|
<div>
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</PageContainerInner>
|
||||||
|
)}
|
||||||
</Exception>
|
</Exception>
|
||||||
</div>
|
</div>
|
||||||
{NoResourceModal}
|
{NoResourceModal}
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ export default {
|
|||||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
'On the Kubernetes cluster that needs to be registered, run the following command to create the Kubernetes resources and register the cluster.',
|
'On the Kubernetes cluster that needs to be registered, run the following command to create the Kubernetes resources and register the cluster.',
|
||||||
'clusters.create.addCommand.k8s.version.warning':
|
|
||||||
'The minimum supported Kubernetes version is 1.23. To use the GPU Service feature, the minimum supported Kubernetes version is 1.27.',
|
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
'On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.',
|
'On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.',
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
|
|||||||
@@ -198,8 +198,6 @@ export default {
|
|||||||
'common.table.user': 'User',
|
'common.table.user': 'User',
|
||||||
'common.settings.instructions': 'Instructions',
|
'common.settings.instructions': 'Instructions',
|
||||||
'common.settings.language': 'Language',
|
'common.settings.language': 'Language',
|
||||||
'common.settings.language.tips':
|
|
||||||
'Set the display language for the interface.',
|
|
||||||
'common.delete.confirm':
|
'common.delete.confirm':
|
||||||
'Are you sure you want to delete the selected {type}?',
|
'Are you sure you want to delete the selected {type}?',
|
||||||
'common.delete.single.confirm':
|
'common.delete.single.confirm':
|
||||||
@@ -253,11 +251,6 @@ export default {
|
|||||||
'common.appearance.tips': 'Default follows system preference.',
|
'common.appearance.tips': 'Default follows system preference.',
|
||||||
'common.button.forgotpassword': 'Forgot password?',
|
'common.button.forgotpassword': 'Forgot password?',
|
||||||
'common.appearance.theme': 'Theme',
|
'common.appearance.theme': 'Theme',
|
||||||
'common.appearance.description':
|
|
||||||
'Customize how the interface looks on your device.',
|
|
||||||
'common.security': 'Security',
|
|
||||||
'common.security.description':
|
|
||||||
'Manage the password used to sign in to your account.',
|
|
||||||
'common.page.wentwrong': 'Something went wrong.',
|
'common.page.wentwrong': 'Something went wrong.',
|
||||||
'common.page.refresh.tips':
|
'common.page.refresh.tips':
|
||||||
'The page may need to be updated. Try refreshing it!',
|
'The page may need to be updated. Try refreshing it!',
|
||||||
@@ -267,10 +260,6 @@ export default {
|
|||||||
'common.select.count': '{count} selected',
|
'common.select.count': '{count} selected',
|
||||||
'common.login.auth': 'Authenticating...',
|
'common.login.auth': 'Authenticating...',
|
||||||
'common.login.auth.failed': 'Authentication failed',
|
'common.login.auth.failed': 'Authentication failed',
|
||||||
'common.login.error.source_conflict':
|
|
||||||
'An account with this username already exists from a different authentication source. Please contact an administrator to link or convert it.',
|
|
||||||
'common.login.error.auth_failed':
|
|
||||||
'Authentication with the identity provider failed. Please try again or contact your administrator.',
|
|
||||||
'common.login.password': 'Log in with Password',
|
'common.login.password': 'Log in with Password',
|
||||||
'common.login.username.holder': 'Please enter username',
|
'common.login.username.holder': 'Please enter username',
|
||||||
'common.login.password.holder': 'Please enter password',
|
'common.login.password.holder': 'Please enter password',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ export default {
|
|||||||
'gpuservice.template': 'GPU Instance Template',
|
'gpuservice.template': 'GPU Instance Template',
|
||||||
'gpuservice.template.add': 'Add Instance Template',
|
'gpuservice.template.add': 'Add Instance Template',
|
||||||
'gpuservice.template.edit': 'Edit Instance Template',
|
'gpuservice.template.edit': 'Edit Instance Template',
|
||||||
'gpuservice.template.clone': 'Clone Instance Template',
|
|
||||||
'gpuservice.template.filter.name': 'Filter by name',
|
'gpuservice.template.filter.name': 'Filter by name',
|
||||||
'gpuservice.template.filter.vendor': 'Filter by vendor',
|
'gpuservice.template.filter.vendor': 'Filter by vendor',
|
||||||
'gpuservice.template.image': 'Image',
|
'gpuservice.template.image': 'Image',
|
||||||
@@ -82,8 +81,6 @@ export default {
|
|||||||
'gpuservice.publicKey': 'SSH Public Key',
|
'gpuservice.publicKey': 'SSH Public Key',
|
||||||
'gpuservice.publicKey.add': 'Add SSH Public Key',
|
'gpuservice.publicKey.add': 'Add SSH Public Key',
|
||||||
'gpuservice.publicKey.edit': 'Edit SSH Public Key',
|
'gpuservice.publicKey.edit': 'Edit SSH Public Key',
|
||||||
'gpuservice.publicKey.delete.tips':
|
|
||||||
'Deleting an SSH Public Key will not revoke access for existing attached Instances. To remove access, edit those Instances separately.',
|
|
||||||
'gpuservice.publicKey.filter.name': 'Search by name',
|
'gpuservice.publicKey.filter.name': 'Search by name',
|
||||||
'gpuservice.publicKey.label': 'SSH Public Key',
|
'gpuservice.publicKey.label': 'SSH Public Key',
|
||||||
'gpuservice.instance.ssh.enable': 'Enable SSH Access',
|
'gpuservice.instance.ssh.enable': 'Enable SSH Access',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default {
|
|||||||
'menu.models.modelCatalog': 'Catalog',
|
'menu.models.modelCatalog': 'Catalog',
|
||||||
'menu.models.catalog': 'Model Catalog',
|
'menu.models.catalog': 'Model Catalog',
|
||||||
'menu.models.deployment': 'Deployments',
|
'menu.models.deployment': 'Deployments',
|
||||||
'menu.models.userModels': 'Models',
|
'menu.models.userModels': 'My Models',
|
||||||
'menu.models.benchmark': 'Benchmarks',
|
'menu.models.benchmark': 'Benchmarks',
|
||||||
'menu.models.benchmarkDetail': 'Benchmark Details',
|
'menu.models.benchmarkDetail': 'Benchmark Details',
|
||||||
'menu.models.providers': 'Providers',
|
'menu.models.providers': 'Providers',
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ export default {
|
|||||||
'users.form.active.description': 'Enable or disable this user account',
|
'users.form.active.description': 'Enable or disable this user account',
|
||||||
'users.form.fullname': 'Full Name',
|
'users.form.fullname': 'Full Name',
|
||||||
'users.form.source': 'Source',
|
'users.form.source': 'Source',
|
||||||
'users.form.source.local': 'Local',
|
|
||||||
'users.form.source.tip.switchToLocal':
|
|
||||||
'Switching to Local requires a new password. The user will sign in via the standard login form.',
|
|
||||||
'users.form.source.tip.switchToExternal':
|
|
||||||
"Switching to an external source clears the user's local password. They will sign in via the configured identity provider.",
|
|
||||||
'users.table.user': 'users',
|
'users.table.user': 'users',
|
||||||
'users.form.admin': 'Admin',
|
'users.form.admin': 'Admin',
|
||||||
'users.form.user': 'User',
|
'users.form.user': 'User',
|
||||||
@@ -33,8 +28,6 @@ export default {
|
|||||||
'users.password.modify.title': 'Modify Password',
|
'users.password.modify.title': 'Modify Password',
|
||||||
'users.password.modify.description':
|
'users.password.modify.description':
|
||||||
"For your account's security, please change your initial password.",
|
"For your account's security, please change your initial password.",
|
||||||
'users.password.modify.tips':
|
|
||||||
'Regularly updating your password helps keep your account secure.',
|
|
||||||
'users.password.confirm': 'Confirm New Password',
|
'users.password.confirm': 'Confirm New Password',
|
||||||
'users.password.confirm.empty': 'Please confirm the new password.',
|
'users.password.confirm.empty': 'Please confirm the new password.',
|
||||||
'users.password.confirm.error': 'The two passwords entered do not match.',
|
'users.password.confirm.error': 'The two passwords entered do not match.',
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ export default {
|
|||||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
'登録する Kubernetes クラスターで以下のコマンドを実行し、Kubernetes リソースを作成してクラスターを登録します。',
|
'登録する Kubernetes クラスターで以下のコマンドを実行し、Kubernetes リソースを作成してクラスターを登録します。',
|
||||||
'clusters.create.addCommand.k8s.version.warning':
|
|
||||||
'サポートされる Kubernetes の最小バージョンは 1.23 です。GPU Service 機能を使用する場合、サポートされる Kubernetes の最小バージョンは 1.27 です。',
|
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
'Use the following command to check if the environment is ready.',
|
'Use the following command to check if the environment is ready.',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
|
|||||||
@@ -199,7 +199,6 @@ export default {
|
|||||||
'common.table.user': 'ユーザー',
|
'common.table.user': 'ユーザー',
|
||||||
'common.settings.instructions': '手順',
|
'common.settings.instructions': '手順',
|
||||||
'common.settings.language': '言語',
|
'common.settings.language': '言語',
|
||||||
'common.settings.language.tips': 'インターフェースの表示言語を設定します。',
|
|
||||||
'common.delete.confirm': '選択した {type} を削除してもよろしいですか?',
|
'common.delete.confirm': '選択した {type} を削除してもよろしいですか?',
|
||||||
'common.delete.single.confirm':
|
'common.delete.single.confirm':
|
||||||
'<span style="font-size: 13px;font-weight: 700">{name}</span> を削除してもよろしいですか?',
|
'<span style="font-size: 13px;font-weight: 700">{name}</span> を削除してもよろしいですか?',
|
||||||
@@ -252,11 +251,6 @@ export default {
|
|||||||
'common.appearance.tips': 'Default follows system preference.',
|
'common.appearance.tips': 'Default follows system preference.',
|
||||||
'common.button.forgotpassword': 'Forgot password?',
|
'common.button.forgotpassword': 'Forgot password?',
|
||||||
'common.appearance.theme': 'Theme',
|
'common.appearance.theme': 'Theme',
|
||||||
'common.appearance.description':
|
|
||||||
'デバイス上でのインターフェースの表示をカスタマイズします。',
|
|
||||||
'common.security': 'セキュリティ',
|
|
||||||
'common.security.description':
|
|
||||||
'アカウントへのログインに使用するパスワードを管理します。',
|
|
||||||
'common.page.wentwrong': 'Something went wrong.',
|
'common.page.wentwrong': 'Something went wrong.',
|
||||||
'common.page.refresh.tips':
|
'common.page.refresh.tips':
|
||||||
'The page may need to be updated. Try refreshing it!',
|
'The page may need to be updated. Try refreshing it!',
|
||||||
@@ -266,10 +260,6 @@ export default {
|
|||||||
'common.select.count': '{count} selected',
|
'common.select.count': '{count} selected',
|
||||||
'common.login.auth': 'Authenticating...',
|
'common.login.auth': 'Authenticating...',
|
||||||
'common.login.auth.failed': 'Authentication failed',
|
'common.login.auth.failed': 'Authentication failed',
|
||||||
'common.login.error.source_conflict':
|
|
||||||
'このユーザー名のアカウントは別の認証ソースで既に存在します。管理者にリンクまたは変換を依頼してください。',
|
|
||||||
'common.login.error.auth_failed':
|
|
||||||
'ID プロバイダーでの認証に失敗しました。再試行するか、管理者にお問い合わせください。',
|
|
||||||
'common.login.password': 'Log in with Password',
|
'common.login.password': 'Log in with Password',
|
||||||
'common.login.username.holder': 'Please enter username',
|
'common.login.username.holder': 'Please enter username',
|
||||||
'common.login.password.holder': 'Please enter password',
|
'common.login.password.holder': 'Please enter password',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ export default {
|
|||||||
'gpuservice.template': 'GPU インスタンステンプレート',
|
'gpuservice.template': 'GPU インスタンステンプレート',
|
||||||
'gpuservice.template.add': 'インスタンステンプレートを追加',
|
'gpuservice.template.add': 'インスタンステンプレートを追加',
|
||||||
'gpuservice.template.edit': 'インスタンステンプレートを編集',
|
'gpuservice.template.edit': 'インスタンステンプレートを編集',
|
||||||
'gpuservice.template.clone': 'インスタンステンプレートを複製',
|
|
||||||
'gpuservice.template.filter.name': '名前でフィルター',
|
'gpuservice.template.filter.name': '名前でフィルター',
|
||||||
'gpuservice.template.filter.vendor': 'ベンダーでフィルター',
|
'gpuservice.template.filter.vendor': 'ベンダーでフィルター',
|
||||||
'gpuservice.template.image': 'コンテナイメージ',
|
'gpuservice.template.image': 'コンテナイメージ',
|
||||||
@@ -81,8 +80,6 @@ export default {
|
|||||||
'gpuservice.publicKey': 'SSH 公開鍵',
|
'gpuservice.publicKey': 'SSH 公開鍵',
|
||||||
'gpuservice.publicKey.add': 'SSH 公開鍵を追加',
|
'gpuservice.publicKey.add': 'SSH 公開鍵を追加',
|
||||||
'gpuservice.publicKey.edit': 'SSH 公開鍵を編集',
|
'gpuservice.publicKey.edit': 'SSH 公開鍵を編集',
|
||||||
'gpuservice.publicKey.delete.tips':
|
|
||||||
'SSH 公開鍵を削除しても、既にアタッチされているインスタンスのアクセス権は取り消されません。アクセス権を削除するには、対象のインスタンスを個別に編集してください。',
|
|
||||||
'gpuservice.publicKey.filter.name': '名前で検索',
|
'gpuservice.publicKey.filter.name': '名前で検索',
|
||||||
'gpuservice.publicKey.label': 'SSH 公開鍵',
|
'gpuservice.publicKey.label': 'SSH 公開鍵',
|
||||||
'gpuservice.instance.ssh.enable': 'SSH アクセスを有効化',
|
'gpuservice.instance.ssh.enable': 'SSH アクセスを有効化',
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default {
|
|||||||
'menu.accessControl.organizations': 'Organizations',
|
'menu.accessControl.organizations': 'Organizations',
|
||||||
'menu.resources.clusters': 'Clusters',
|
'menu.resources.clusters': 'Clusters',
|
||||||
'menu.resources.credentials': 'Cloud Credentials',
|
'menu.resources.credentials': 'Cloud Credentials',
|
||||||
'menu.models.userModels': 'Models',
|
'menu.models.userModels': 'My Models',
|
||||||
'menu.resources.clusterDetail': 'Cluster Detail',
|
'menu.resources.clusterDetail': 'Cluster Detail',
|
||||||
'menu.resources.clusterCreate': 'Create Cluster',
|
'menu.resources.clusterCreate': 'Create Cluster',
|
||||||
'menu.models.backendsList': 'Inference Backends',
|
'menu.models.backendsList': 'Inference Backends',
|
||||||
@@ -62,11 +62,11 @@ export default {
|
|||||||
// 6. 'menu.accessControl.apikeys': 'API Keys',
|
// 6. 'menu.accessControl.apikeys': 'API Keys',
|
||||||
// 7. 'menu.accessControl.users': 'Users',
|
// 7. 'menu.accessControl.users': 'Users',
|
||||||
// 8. 'menu.clusterManagement': 'Cluster Management',
|
// 8. 'menu.clusterManagement': 'Cluster Management',
|
||||||
// 9. 'menu.resources.clusters': 'Clusters',
|
// 9. 'menu.clusterManagement.clusters': 'Clusters',
|
||||||
// 10. 'menu.resources.credentials': 'Cloud Credentials',
|
// 10. 'menu.clusterManagement.credentials': 'Cloud Credentials',
|
||||||
// 11. 'menu.models.userModels': 'Models'
|
// 11. 'menu.models.userModels': 'My Models'
|
||||||
// 12. 'menu.resources.clusterDetail': 'Cluster Detail',
|
// 12. 'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||||
// 13. 'menu.resources.clusterCreate': 'Create Cluster',
|
// 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||||
// 14. 'menu.models.backendsList': 'Inference Backends',
|
// 14. 'menu.models.backendsList': 'Inference Backends',
|
||||||
// 15. 'menu.models.benchmark': 'Benchmarks',
|
// 15. 'menu.models.benchmark': 'Benchmarks',
|
||||||
// 15. 'menu.models.provider': 'Provider',
|
// 15. 'menu.models.provider': 'Provider',
|
||||||
|
|||||||
@@ -13,11 +13,6 @@ export default {
|
|||||||
'このユーザーアカウントを有効または無効にする',
|
'このユーザーアカウントを有効または無効にする',
|
||||||
'users.form.fullname': 'フルネーム',
|
'users.form.fullname': 'フルネーム',
|
||||||
'users.form.source': 'ソース',
|
'users.form.source': 'ソース',
|
||||||
'users.form.source.local': 'ローカル',
|
|
||||||
'users.form.source.tip.switchToLocal':
|
|
||||||
'ローカルに切り替えるには新しいパスワードが必要です。以後、ユーザーは標準のログインフォームからサインインします。',
|
|
||||||
'users.form.source.tip.switchToExternal':
|
|
||||||
'外部ソースに切り替えるとユーザーのローカルパスワードが削除され、設定済みの ID プロバイダーからサインインするようになります。',
|
|
||||||
'users.table.user': 'ユーザー',
|
'users.table.user': 'ユーザー',
|
||||||
'users.form.admin': '管理者',
|
'users.form.admin': '管理者',
|
||||||
'users.form.user': '一般ユーザー',
|
'users.form.user': '一般ユーザー',
|
||||||
@@ -34,8 +29,6 @@ export default {
|
|||||||
'users.password.modify.title': 'パスワードを変更',
|
'users.password.modify.title': 'パスワードを変更',
|
||||||
'users.password.modify.description':
|
'users.password.modify.description':
|
||||||
'アカウントのセキュリティのため、初期パスワードを変更してください。',
|
'アカウントのセキュリティのため、初期パスワードを変更してください。',
|
||||||
'users.password.modify.tips':
|
|
||||||
'パスワードを定期的に更新すると、アカウントの安全を保てます。',
|
|
||||||
'users.password.confirm': '新しいパスワードを確認',
|
'users.password.confirm': '新しいパスワードを確認',
|
||||||
'users.password.confirm.empty': '新しいパスワードを確認してください。',
|
'users.password.confirm.empty': '新しいパスワードを確認してください。',
|
||||||
'users.password.confirm.error': '入力された2つのパスワードが一致しません。',
|
'users.password.confirm.error': '入力された2つのパスワードが一致しません。',
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ export default {
|
|||||||
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
'На Kubernetes-кластере, который необходимо зарегистрировать, выполните следующую команду, чтобы создать ресурсы Kubernetes и зарегистрировать этот кластер.',
|
'На Kubernetes-кластере, который необходимо зарегистрировать, выполните следующую команду, чтобы создать ресурсы Kubernetes и зарегистрировать этот кластер.',
|
||||||
'clusters.create.addCommand.k8s.version.warning':
|
|
||||||
'Минимальная поддерживаемая версия Kubernetes — 1.23. Для использования функции GPU Service минимальная поддерживаемая версия Kubernetes — 1.27.',
|
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
'Используйте следующую команду для проверки готовности окружения',
|
'Используйте следующую команду для проверки готовности окружения',
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
|
|||||||
@@ -197,7 +197,6 @@ export default {
|
|||||||
'common.table.user': 'Пользователь',
|
'common.table.user': 'Пользователь',
|
||||||
'common.settings.instructions': 'Инструкции',
|
'common.settings.instructions': 'Инструкции',
|
||||||
'common.settings.language': 'Язык',
|
'common.settings.language': 'Язык',
|
||||||
'common.settings.language.tips': 'Задайте язык отображения интерфейса.',
|
|
||||||
'common.delete.confirm': 'Вы уверены, что хотите удалить выбранный {type}?',
|
'common.delete.confirm': 'Вы уверены, что хотите удалить выбранный {type}?',
|
||||||
'common.delete.single.confirm':
|
'common.delete.single.confirm':
|
||||||
'Вы уверены, что хотите удалить <span style="font-size: 13px;font-weight: 700">{name}</span>?',
|
'Вы уверены, что хотите удалить <span style="font-size: 13px;font-weight: 700">{name}</span>?',
|
||||||
@@ -251,11 +250,6 @@ export default {
|
|||||||
'common.appearance.tips': 'По умолчанию соответствует системным настройкам.',
|
'common.appearance.tips': 'По умолчанию соответствует системным настройкам.',
|
||||||
'common.button.forgotpassword': 'Забыли пароль?',
|
'common.button.forgotpassword': 'Забыли пароль?',
|
||||||
'common.appearance.theme': 'Тема',
|
'common.appearance.theme': 'Тема',
|
||||||
'common.appearance.description':
|
|
||||||
'Настройте внешний вид интерфейса на вашем устройстве.',
|
|
||||||
'common.security': 'Безопасность',
|
|
||||||
'common.security.description':
|
|
||||||
'Управляйте паролем для входа в учётную запись.',
|
|
||||||
'common.page.wentwrong': 'Что-то пошло не так.',
|
'common.page.wentwrong': 'Что-то пошло не так.',
|
||||||
'common.page.refresh.tips':
|
'common.page.refresh.tips':
|
||||||
'Страница может нуждаться в обновлении. Попробуйте обновить её!',
|
'Страница может нуждаться в обновлении. Попробуйте обновить её!',
|
||||||
@@ -265,10 +259,6 @@ export default {
|
|||||||
'common.select.count': '{count} Выбрано',
|
'common.select.count': '{count} Выбрано',
|
||||||
'common.login.auth': 'Аутентификация...',
|
'common.login.auth': 'Аутентификация...',
|
||||||
'common.login.auth.failed': 'Ошибка аутентификации',
|
'common.login.auth.failed': 'Ошибка аутентификации',
|
||||||
'common.login.error.source_conflict':
|
|
||||||
'Учётная запись с таким именем уже существует, но с другим источником аутентификации. Обратитесь к администратору для связывания или преобразования.',
|
|
||||||
'common.login.error.auth_failed':
|
|
||||||
'Не удалось пройти аутентификацию через провайдера идентификации. Попробуйте ещё раз или обратитесь к администратору.',
|
|
||||||
'common.login.password': 'Войти с паролем',
|
'common.login.password': 'Войти с паролем',
|
||||||
'common.login.username.holder': 'Введите имя пользователя',
|
'common.login.username.holder': 'Введите имя пользователя',
|
||||||
'common.login.password.holder': 'Введите пароль',
|
'common.login.password.holder': 'Введите пароль',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ export default {
|
|||||||
'gpuservice.template': 'Шаблон экземпляра GPU',
|
'gpuservice.template': 'Шаблон экземпляра GPU',
|
||||||
'gpuservice.template.add': 'Добавить шаблон экземпляра',
|
'gpuservice.template.add': 'Добавить шаблон экземпляра',
|
||||||
'gpuservice.template.edit': 'Редактировать шаблон экземпляра',
|
'gpuservice.template.edit': 'Редактировать шаблон экземпляра',
|
||||||
'gpuservice.template.clone': 'Клонировать шаблон экземпляра',
|
|
||||||
'gpuservice.template.filter.name': 'Фильтр по имени',
|
'gpuservice.template.filter.name': 'Фильтр по имени',
|
||||||
'gpuservice.template.filter.vendor': 'Фильтр по производителю',
|
'gpuservice.template.filter.vendor': 'Фильтр по производителю',
|
||||||
'gpuservice.template.image': 'Образ',
|
'gpuservice.template.image': 'Образ',
|
||||||
@@ -85,8 +84,6 @@ export default {
|
|||||||
'gpuservice.publicKey': 'Открытый ключ SSH',
|
'gpuservice.publicKey': 'Открытый ключ SSH',
|
||||||
'gpuservice.publicKey.add': 'Добавить открытый ключ SSH',
|
'gpuservice.publicKey.add': 'Добавить открытый ключ SSH',
|
||||||
'gpuservice.publicKey.edit': 'Изменить открытый ключ SSH',
|
'gpuservice.publicKey.edit': 'Изменить открытый ключ SSH',
|
||||||
'gpuservice.publicKey.delete.tips':
|
|
||||||
'Удаление открытого ключа SSH не отзывает доступ для уже подключённых экземпляров. Чтобы удалить доступ, отредактируйте эти экземпляры отдельно.',
|
|
||||||
'gpuservice.publicKey.filter.name': 'Поиск по имени',
|
'gpuservice.publicKey.filter.name': 'Поиск по имени',
|
||||||
'gpuservice.publicKey.label': 'Открытый ключ SSH',
|
'gpuservice.publicKey.label': 'Открытый ключ SSH',
|
||||||
'gpuservice.instance.ssh.enable': 'Включить SSH-доступ',
|
'gpuservice.instance.ssh.enable': 'Включить SSH-доступ',
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default {
|
|||||||
'menu.accessControl.organizations': 'Организации',
|
'menu.accessControl.organizations': 'Организации',
|
||||||
'menu.resources.clusters': 'Кластеры',
|
'menu.resources.clusters': 'Кластеры',
|
||||||
'menu.resources.credentials': 'Облачные аккаунты',
|
'menu.resources.credentials': 'Облачные аккаунты',
|
||||||
'menu.models.userModels': 'Модели',
|
'menu.models.userModels': 'Мои модели',
|
||||||
'menu.resources.clusterDetail': 'Детали кластера',
|
'menu.resources.clusterDetail': 'Детали кластера',
|
||||||
'menu.resources.clusterCreate': 'Создать кластер',
|
'menu.resources.clusterCreate': 'Создать кластер',
|
||||||
'menu.models.backendsList': 'Бэкенды запуска',
|
'menu.models.backendsList': 'Бэкенды запуска',
|
||||||
|
|||||||
@@ -13,11 +13,6 @@ export default {
|
|||||||
'Включить или отключить эту учетную запись пользователя',
|
'Включить или отключить эту учетную запись пользователя',
|
||||||
'users.form.fullname': 'Полное имя',
|
'users.form.fullname': 'Полное имя',
|
||||||
'users.form.source': 'Источник',
|
'users.form.source': 'Источник',
|
||||||
'users.form.source.local': 'Локальный',
|
|
||||||
'users.form.source.tip.switchToLocal':
|
|
||||||
'Переключение на «Локальный» требует ввода нового пароля. После этого пользователь будет входить через стандартную форму входа.',
|
|
||||||
'users.form.source.tip.switchToExternal':
|
|
||||||
'Переключение на внешний источник удаляет локальный пароль пользователя. После этого вход будет выполняться через настроенного провайдера идентификации.',
|
|
||||||
'users.table.user': 'пользователи',
|
'users.table.user': 'пользователи',
|
||||||
'users.form.admin': 'Администратор',
|
'users.form.admin': 'Администратор',
|
||||||
'users.form.user': 'Пользователь',
|
'users.form.user': 'Пользователь',
|
||||||
@@ -34,8 +29,6 @@ export default {
|
|||||||
'users.password.modify.title': 'Смена пароля',
|
'users.password.modify.title': 'Смена пароля',
|
||||||
'users.password.modify.description':
|
'users.password.modify.description':
|
||||||
'В целях безопасности измените первоначальный пароль.',
|
'В целях безопасности измените первоначальный пароль.',
|
||||||
'users.password.modify.tips':
|
|
||||||
'Регулярное обновление пароля помогает защитить вашу учётную запись.',
|
|
||||||
'users.password.confirm': 'Подтвердите новый пароль',
|
'users.password.confirm': 'Подтвердите новый пароль',
|
||||||
'users.password.confirm.empty': 'Подтвердите новый пароль',
|
'users.password.confirm.empty': 'Подтвердите новый пароль',
|
||||||
'users.password.confirm.error': 'Пароли не совпадают',
|
'users.password.confirm.error': 'Пароли не совпадают',
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ export default {
|
|||||||
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
'Kaydedilmesi gereken Kubernetes kümesinde, Kubernetes kaynaklarını oluşturmak ve kümeyi kaydetmek için aşağıdaki komutu çalıştırın.',
|
'Kaydedilmesi gereken Kubernetes kümesinde, Kubernetes kaynaklarını oluşturmak ve kümeyi kaydetmek için aşağıdaki komutu çalıştırın.',
|
||||||
'clusters.create.addCommand.k8s.version.warning':
|
|
||||||
'Desteklenen minimum Kubernetes sürümü 1.23’tür. GPU Service özelliğini kullanmak için desteklenen minimum Kubernetes sürümü 1.27’dir.',
|
|
||||||
'clusters.create.register.tips':
|
'clusters.create.register.tips':
|
||||||
'Eklenmesi gereken Kubernetes kümesinde, düğümlerini kümeye katılması için aşağıdaki komutu çalıştırın.',
|
'Eklenmesi gereken Kubernetes kümesinde, düğümlerini kümeye katılması için aşağıdaki komutu çalıştırın.',
|
||||||
'cluster.create.checkEnv.tips':
|
'cluster.create.checkEnv.tips':
|
||||||
|
|||||||