@teamix-evo/ui 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/components/dialog/index.tsx +10 -1
- package/src/components/sheet/index.tsx +10 -1
- package/src/components/tabs/index.tsx +1 -1
- package/src/examples/built-in-assets/stories.tsx +572 -0
- package/src/examples/evaluators/stories.tsx +502 -0
- package/src/hooks/use-radix-popper-guard.ts +51 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamix-evo/ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Source-injected UI components for Teamix Evo (shadcn-based, antd capabilities)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"vite-tsconfig-paths": "^6.1.1",
|
|
53
53
|
"zod": "^3",
|
|
54
54
|
"@teamix-evo/eslint-config": "0.2.3",
|
|
55
|
-
"@teamix-evo/
|
|
56
|
-
"@teamix-evo/
|
|
55
|
+
"@teamix-evo/registry": "0.12.0",
|
|
56
|
+
"@teamix-evo/tokens": "^0.7.3"
|
|
57
57
|
},
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public",
|
|
@@ -23,6 +23,7 @@ import * as React from 'react';
|
|
|
23
23
|
import { Dialog as DialogPrimitive } from 'radix-ui';
|
|
24
24
|
|
|
25
25
|
import { cn } from '@/lib/utils';
|
|
26
|
+
import { useRadixPopperGuard } from '@/hooks/use-radix-popper-guard';
|
|
26
27
|
import { XIcon } from 'lucide-react';
|
|
27
28
|
|
|
28
29
|
export interface DialogProps
|
|
@@ -95,6 +96,7 @@ function DialogContent({
|
|
|
95
96
|
children,
|
|
96
97
|
size = 'sm',
|
|
97
98
|
showCloseButton = true,
|
|
99
|
+
onPointerDownOutside,
|
|
98
100
|
...props
|
|
99
101
|
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
100
102
|
/** 内容区尺寸。 @default 'sm' */
|
|
@@ -102,12 +104,18 @@ function DialogContent({
|
|
|
102
104
|
/** 是否显示右上角关闭按钮。 @default true */
|
|
103
105
|
showCloseButton?: boolean;
|
|
104
106
|
}) {
|
|
107
|
+
const guard = useRadixPopperGuard();
|
|
108
|
+
|
|
105
109
|
return (
|
|
106
110
|
<DialogPortal>
|
|
107
111
|
<DialogOverlay />
|
|
108
112
|
<DialogPrimitive.Content
|
|
109
113
|
data-slot="dialog-content"
|
|
110
114
|
data-size={size}
|
|
115
|
+
onPointerDownOutside={(e) => {
|
|
116
|
+
guard.onPointerDownOutside(e);
|
|
117
|
+
onPointerDownOutside?.(e);
|
|
118
|
+
}}
|
|
111
119
|
className={cn(
|
|
112
120
|
// eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- mobile viewport needs calc(100% - 2rem) for safe inset; box-shadow drives via component-level token --dialog-shadow per ADR 0026.
|
|
113
121
|
'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-md bg-popover text-sm text-popover-foreground shadow-[var(--dialog-shadow,0px_6px_24px_0px_rgba(0,0,0,0.1))] duration-100 outline-none data-[size=sm]:sm:max-w-md data-[size=md]:sm:max-w-xl data-[size=lg]:sm:max-w-3xl data-[size=xl]:sm:max-w-6xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
@@ -199,7 +207,8 @@ function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
199
207
|
<div
|
|
200
208
|
data-slot="dialog-footer"
|
|
201
209
|
className={cn(
|
|
202
|
-
|
|
210
|
+
// eslint-disable-next-line teamix-evo/no-bare-border -- border-footer-separator 是 @theme 声明的语义 token,已加入 lint-core 白名单
|
|
211
|
+
'flex flex-col-reverse gap-2 border-t border-footer-separator px-5 py-4 sm:flex-row sm:justify-end',
|
|
203
212
|
className,
|
|
204
213
|
)}
|
|
205
214
|
{...props}
|
|
@@ -25,6 +25,7 @@ import * as React from 'react';
|
|
|
25
25
|
import { Dialog as SheetPrimitive } from 'radix-ui';
|
|
26
26
|
|
|
27
27
|
import { cn } from '@/lib/utils';
|
|
28
|
+
import { useRadixPopperGuard } from '@/hooks/use-radix-popper-guard';
|
|
28
29
|
import { XIcon } from 'lucide-react';
|
|
29
30
|
|
|
30
31
|
export interface SheetProps
|
|
@@ -104,6 +105,7 @@ function SheetContent({
|
|
|
104
105
|
side = 'right',
|
|
105
106
|
size = 'sm',
|
|
106
107
|
showCloseButton = true,
|
|
108
|
+
onPointerDownOutside,
|
|
107
109
|
...props
|
|
108
110
|
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
|
109
111
|
/** 弹出方向。 @default 'right' */
|
|
@@ -113,6 +115,8 @@ function SheetContent({
|
|
|
113
115
|
/** 是否显示右上角关闭按钮。 @default true */
|
|
114
116
|
showCloseButton?: boolean;
|
|
115
117
|
}) {
|
|
118
|
+
const guard = useRadixPopperGuard();
|
|
119
|
+
|
|
116
120
|
return (
|
|
117
121
|
<SheetPortal>
|
|
118
122
|
<SheetOverlay />
|
|
@@ -120,6 +124,10 @@ function SheetContent({
|
|
|
120
124
|
data-slot="sheet-content"
|
|
121
125
|
data-side={side}
|
|
122
126
|
data-size={size}
|
|
127
|
+
onPointerDownOutside={(e) => {
|
|
128
|
+
guard.onPointerDownOutside(e);
|
|
129
|
+
onPointerDownOutside?.(e);
|
|
130
|
+
}}
|
|
123
131
|
className={cn(
|
|
124
132
|
// 基础布局:fixed 定位 + flex 列容器 + token 化阴影(抽屉与背景的边界由阴影表达,不画 border)
|
|
125
133
|
// eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- box-shadow 走 component-level token --drawer-shadow,按 ADR 0026 在源码内显式声明。
|
|
@@ -237,7 +245,8 @@ function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
237
245
|
<div
|
|
238
246
|
data-slot="sheet-footer"
|
|
239
247
|
className={cn(
|
|
240
|
-
|
|
248
|
+
// eslint-disable-next-line teamix-evo/no-bare-border -- border-footer-separator 是 @theme 声明的语义 token,已加入 lint-core 白名单
|
|
249
|
+
'flex flex-col gap-2 border-t border-footer-separator px-4 py-2.5 sm:flex-row sm:justify-start',
|
|
241
250
|
className,
|
|
242
251
|
)}
|
|
243
252
|
{...props}
|
|
@@ -269,7 +269,7 @@ const tabsTriggerVariants = cva(
|
|
|
269
269
|
{
|
|
270
270
|
variants: {
|
|
271
271
|
variant: {
|
|
272
|
-
line: '-mb-px border-b-2 border-transparent text-muted-foreground data-[state=active]:border-
|
|
272
|
+
line: '-mb-px border-b-2 border-transparent text-muted-foreground data-[state=active]:border-tab-indicator data-[state=active]:text-foreground group-data-[orientation=vertical]/tabs:mb-0 group-data-[orientation=vertical]/tabs:-mr-px group-data-[orientation=vertical]/tabs:border-b-0 group-data-[orientation=vertical]/tabs:border-r-2 group-data-[orientation=vertical]/tabs:justify-start',
|
|
273
273
|
capsule:
|
|
274
274
|
'rounded-md text-muted-foreground data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',
|
|
275
275
|
// text 模式:trigger 间 ::after 竖线分隔(8px 高、1px 宽、垂直居中、border 色)
|
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import {
|
|
3
|
+
Sidebar,
|
|
4
|
+
SidebarContent,
|
|
5
|
+
SidebarFooter,
|
|
6
|
+
SidebarGroup,
|
|
7
|
+
SidebarGroupContent,
|
|
8
|
+
SidebarGroupLabel,
|
|
9
|
+
SidebarHeader,
|
|
10
|
+
SidebarMenu,
|
|
11
|
+
SidebarMenuButton,
|
|
12
|
+
SidebarMenuItem,
|
|
13
|
+
} from '@/components/sidebar';
|
|
14
|
+
import { PageShell } from '@/components/page-shell';
|
|
15
|
+
import { PageHeader, PageHeaderHeading } from '@/components/page-header';
|
|
16
|
+
import { Card, CardContent } from '@/components/card';
|
|
17
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/tabs';
|
|
18
|
+
import { Input } from '@/components/input';
|
|
19
|
+
import {
|
|
20
|
+
Select,
|
|
21
|
+
SelectContent,
|
|
22
|
+
SelectItem,
|
|
23
|
+
SelectTrigger,
|
|
24
|
+
SelectValue,
|
|
25
|
+
} from '@/components/select';
|
|
26
|
+
import { Avatar, AvatarFallback } from '@/components/avatar';
|
|
27
|
+
import { Badge } from '@/components/badge';
|
|
28
|
+
import { Tag } from '@/components/tag';
|
|
29
|
+
import { TooltipProvider } from '@/components/tooltip';
|
|
30
|
+
import {
|
|
31
|
+
Search,
|
|
32
|
+
Bot,
|
|
33
|
+
BookOpen,
|
|
34
|
+
Sparkles,
|
|
35
|
+
ShieldCheck,
|
|
36
|
+
FlaskConical,
|
|
37
|
+
BarChart3,
|
|
38
|
+
MoreVertical,
|
|
39
|
+
PanelLeftClose,
|
|
40
|
+
LayoutGrid,
|
|
41
|
+
Wrench,
|
|
42
|
+
Brain,
|
|
43
|
+
MessageSquare,
|
|
44
|
+
FileText,
|
|
45
|
+
ListChecks,
|
|
46
|
+
Activity,
|
|
47
|
+
ClipboardList,
|
|
48
|
+
ChevronDown,
|
|
49
|
+
} from 'lucide-react';
|
|
50
|
+
|
|
51
|
+
const meta: Meta = {
|
|
52
|
+
title: '页面示例 · Page Examples/内置资产 卡片列表',
|
|
53
|
+
tags: ['autodocs'],
|
|
54
|
+
parameters: {
|
|
55
|
+
layout: 'fullscreen',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export default meta;
|
|
59
|
+
|
|
60
|
+
type Story = StoryObj;
|
|
61
|
+
|
|
62
|
+
// ─── Mock Data ─────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
const builtInSkills = [
|
|
65
|
+
{
|
|
66
|
+
id: '1',
|
|
67
|
+
name: '网络搜索',
|
|
68
|
+
subName: 'web-search',
|
|
69
|
+
version: 'v1.0.0',
|
|
70
|
+
description: 'Web search and content extraction with Tavily and Exa ...',
|
|
71
|
+
category: 'AI能力',
|
|
72
|
+
categoryPalette: 'blue' as const,
|
|
73
|
+
security: 'safe' as const,
|
|
74
|
+
installStatus: 'installed' as const,
|
|
75
|
+
createdAt: '2026-04-16',
|
|
76
|
+
avatarColor: 'bg-primary',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: '2',
|
|
80
|
+
name: '曙光环境综合运维工具集',
|
|
81
|
+
subName: 'sgenv',
|
|
82
|
+
version: 'v1.0.0',
|
|
83
|
+
description: '统一CLI入口,覆盖环境代理查询、数据库信息、DockerID...',
|
|
84
|
+
category: '数据处理',
|
|
85
|
+
categoryPalette: 'green' as const,
|
|
86
|
+
security: 'safe' as const,
|
|
87
|
+
installStatus: 'installed' as const,
|
|
88
|
+
createdAt: '2026-04-16',
|
|
89
|
+
avatarColor: 'bg-success',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: '3',
|
|
93
|
+
name: '经验总结与改进',
|
|
94
|
+
subName: 'self-improvement',
|
|
95
|
+
version: 'v1.0.0',
|
|
96
|
+
description: 'Captures learnings, errors, and corrections to enable c...',
|
|
97
|
+
category: '自动化',
|
|
98
|
+
categoryPalette: 'orange' as const,
|
|
99
|
+
security: 'safe' as const,
|
|
100
|
+
installStatus: 'has-update' as const,
|
|
101
|
+
createdAt: '2026-04-16',
|
|
102
|
+
avatarColor: 'bg-data-4',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: '4',
|
|
106
|
+
name: '天气查询',
|
|
107
|
+
subName: 'weather',
|
|
108
|
+
version: 'v1.0.0',
|
|
109
|
+
description: 'Get current weather and forecasts (no API required).',
|
|
110
|
+
category: 'AI能力',
|
|
111
|
+
categoryPalette: 'blue' as const,
|
|
112
|
+
security: 'safe' as const,
|
|
113
|
+
installStatus: 'not-installed' as const,
|
|
114
|
+
createdAt: '2026-04-16',
|
|
115
|
+
avatarColor: 'bg-primary',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: '5',
|
|
119
|
+
name: 'A/B test方案制定',
|
|
120
|
+
subName: 'ab-test-setup',
|
|
121
|
+
version: 'v1.0.0',
|
|
122
|
+
description: 'When the user wants to plan, design, or implement an ...',
|
|
123
|
+
category: '数据处理',
|
|
124
|
+
categoryPalette: 'green' as const,
|
|
125
|
+
security: 'safe' as const,
|
|
126
|
+
installStatus: 'not-installed' as const,
|
|
127
|
+
createdAt: '2026-04-16',
|
|
128
|
+
avatarColor: 'bg-warning',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: '6',
|
|
132
|
+
name: '代码质控',
|
|
133
|
+
subName: 'code-quality-checker',
|
|
134
|
+
version: 'v1.0.0',
|
|
135
|
+
description: '代码质量检测技能,在代码更新完成后自动进行多维度质量...',
|
|
136
|
+
category: '数据处理',
|
|
137
|
+
categoryPalette: 'green' as const,
|
|
138
|
+
security: 'safe' as const,
|
|
139
|
+
installStatus: 'not-installed' as const,
|
|
140
|
+
createdAt: '2026-04-16',
|
|
141
|
+
avatarColor: 'bg-brand-3',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: '7',
|
|
145
|
+
name: '分析追踪',
|
|
146
|
+
subName: 'analytics-tracking',
|
|
147
|
+
version: 'v1.0.0',
|
|
148
|
+
description: 'When the user wants to set up, improve, or audit analyt...',
|
|
149
|
+
category: 'AI能力',
|
|
150
|
+
categoryPalette: 'blue' as const,
|
|
151
|
+
security: 'safe' as const,
|
|
152
|
+
installStatus: 'not-installed' as const,
|
|
153
|
+
createdAt: '2026-04-16',
|
|
154
|
+
avatarColor: 'bg-success',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: '8',
|
|
158
|
+
name: '头脑风暴',
|
|
159
|
+
subName: 'brainstorming',
|
|
160
|
+
version: 'v1.0.0',
|
|
161
|
+
description: 'You MUST use this before any creative work - creating ...',
|
|
162
|
+
category: 'AI能力',
|
|
163
|
+
categoryPalette: 'blue' as const,
|
|
164
|
+
security: 'safe' as const,
|
|
165
|
+
installStatus: 'not-installed' as const,
|
|
166
|
+
createdAt: '2026-04-16',
|
|
167
|
+
avatarColor: 'bg-warning',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: '9',
|
|
171
|
+
name: '深度研究',
|
|
172
|
+
subName: 'deep-research',
|
|
173
|
+
version: 'v1.0.0',
|
|
174
|
+
description: '综合多来源信息并引用的深度研究助手,适用于进行深...',
|
|
175
|
+
category: 'AI能力',
|
|
176
|
+
categoryPalette: 'blue' as const,
|
|
177
|
+
security: 'safe' as const,
|
|
178
|
+
installStatus: 'not-installed' as const,
|
|
179
|
+
createdAt: '2026-04-16',
|
|
180
|
+
avatarColor: 'bg-data-4',
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
// ─── Sidebar ───────────────────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* OP Logo — inline SVG(来源:_assets/OP_LOGO.svg)
|
|
188
|
+
* 外层 rect 使用 fill="currentColor" 适配主题,内层图案使用 fill="white"
|
|
189
|
+
*/
|
|
190
|
+
function OpLogo({ className }: { className?: string }) {
|
|
191
|
+
return (
|
|
192
|
+
<svg
|
|
193
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
194
|
+
fill="none"
|
|
195
|
+
viewBox="0 0 84 84"
|
|
196
|
+
className={className}
|
|
197
|
+
>
|
|
198
|
+
<defs>
|
|
199
|
+
<clipPath id="opai-logo-clip-built-in-assets">
|
|
200
|
+
<rect x="0" y="0" width="84" height="84" rx="18" />
|
|
201
|
+
</clipPath>
|
|
202
|
+
</defs>
|
|
203
|
+
<g clipPath="url(#opai-logo-clip-built-in-assets)">
|
|
204
|
+
<rect x="0" y="0" width="84" height="84" rx="18" fill="currentColor" />
|
|
205
|
+
<path
|
|
206
|
+
d="M56.628 43.209L48.407 45.869C46.698 46.422 45.142 44.688 45.876 43.048L60.648 10.047C61.316 8.554 62.798 7.594 64.434 7.594L70.258 7.594C71.893 7.594 73.376 8.554 74.044 10.047L88.816 43.048C89.55 44.688 87.994 46.422 86.284 45.869L78.063 43.209C77.784 43.119 77.556 42.914 77.436 42.646L69.239 24.334C68.507 22.698 66.185 22.698 65.453 24.334L57.255 42.646C57.136 42.914 56.908 43.119 56.628 43.209ZM54.737 51.511C53.792 51.195 53.792 49.859 54.737 49.543L63.03 46.774C63.3 46.684 63.521 46.487 63.641 46.229L66.407 40.304C66.779 39.506 67.914 39.506 68.286 40.304L71.052 46.229C71.172 46.487 71.393 46.684 71.663 46.774L79.956 49.543C80.901 49.859 80.901 51.195 79.956 51.511L71.663 54.28C71.393 54.37 71.172 54.567 71.052 54.825L68.286 60.75C67.914 61.548 66.779 61.548 66.407 60.75L63.641 54.825C63.521 54.567 63.3 54.37 63.03 54.28L54.737 51.511Z"
|
|
207
|
+
fillRule="evenodd"
|
|
208
|
+
fill="white"
|
|
209
|
+
transform="matrix(0.707 0.707 -0.707 0.707 18.752 -30.083)"
|
|
210
|
+
/>
|
|
211
|
+
</g>
|
|
212
|
+
</svg>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function OpentrekSidebar() {
|
|
217
|
+
return (
|
|
218
|
+
<Sidebar collapsible="none">
|
|
219
|
+
<SidebarHeader className="px-4 pt-4 pb-0">
|
|
220
|
+
<div className="flex items-center justify-between">
|
|
221
|
+
<div className="flex h-8 items-center gap-2">
|
|
222
|
+
<OpLogo className="size-6 shrink-0 text-foreground" />
|
|
223
|
+
<span className="text-base font-black leading-tight tracking-tight">
|
|
224
|
+
OPENTREK
|
|
225
|
+
{/* eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- demo badge 10px 无对应 token */}
|
|
226
|
+
<span className="ml-1 text-[10px] font-bold tracking-wider text-muted-foreground">
|
|
227
|
+
DEV
|
|
228
|
+
</span>
|
|
229
|
+
</span>
|
|
230
|
+
</div>
|
|
231
|
+
<button
|
|
232
|
+
type="button"
|
|
233
|
+
className="flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground"
|
|
234
|
+
>
|
|
235
|
+
<PanelLeftClose className="size-4" />
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
</SidebarHeader>
|
|
239
|
+
|
|
240
|
+
<SidebarContent className="pt-4">
|
|
241
|
+
{/* 空间选择器 */}
|
|
242
|
+
<SidebarGroup>
|
|
243
|
+
<SidebarGroupContent>
|
|
244
|
+
<SidebarMenu>
|
|
245
|
+
<SidebarMenuItem>
|
|
246
|
+
<SidebarMenuButton tooltip="产品研发空间">
|
|
247
|
+
<Bot className="size-4" />
|
|
248
|
+
<span>产品研发空间</span>
|
|
249
|
+
<ChevronDown className="ml-auto size-3.5 text-muted-foreground" />
|
|
250
|
+
</SidebarMenuButton>
|
|
251
|
+
</SidebarMenuItem>
|
|
252
|
+
</SidebarMenu>
|
|
253
|
+
</SidebarGroupContent>
|
|
254
|
+
</SidebarGroup>
|
|
255
|
+
|
|
256
|
+
{/* 概览 */}
|
|
257
|
+
<SidebarGroup>
|
|
258
|
+
<SidebarGroupContent>
|
|
259
|
+
<SidebarMenu>
|
|
260
|
+
<SidebarMenuItem>
|
|
261
|
+
<SidebarMenuButton tooltip="概览">
|
|
262
|
+
<LayoutGrid className="size-4" />
|
|
263
|
+
<span>概览</span>
|
|
264
|
+
</SidebarMenuButton>
|
|
265
|
+
</SidebarMenuItem>
|
|
266
|
+
</SidebarMenu>
|
|
267
|
+
</SidebarGroupContent>
|
|
268
|
+
</SidebarGroup>
|
|
269
|
+
|
|
270
|
+
{/* 开发 */}
|
|
271
|
+
<SidebarGroup>
|
|
272
|
+
<SidebarGroupLabel>开发</SidebarGroupLabel>
|
|
273
|
+
<SidebarGroupContent>
|
|
274
|
+
<SidebarMenu>
|
|
275
|
+
<SidebarMenuItem>
|
|
276
|
+
<SidebarMenuButton tooltip="智能体">
|
|
277
|
+
<Bot className="size-4" />
|
|
278
|
+
<span>智能体</span>
|
|
279
|
+
</SidebarMenuButton>
|
|
280
|
+
</SidebarMenuItem>
|
|
281
|
+
<SidebarMenuItem>
|
|
282
|
+
<SidebarMenuButton tooltip="工具箱">
|
|
283
|
+
<Wrench className="size-4" />
|
|
284
|
+
<span>工具箱</span>
|
|
285
|
+
</SidebarMenuButton>
|
|
286
|
+
</SidebarMenuItem>
|
|
287
|
+
</SidebarMenu>
|
|
288
|
+
</SidebarGroupContent>
|
|
289
|
+
</SidebarGroup>
|
|
290
|
+
|
|
291
|
+
{/* Skills Hub (独立项,当前激活) */}
|
|
292
|
+
<SidebarGroup>
|
|
293
|
+
<SidebarGroupContent>
|
|
294
|
+
<SidebarMenu>
|
|
295
|
+
<SidebarMenuItem>
|
|
296
|
+
<SidebarMenuButton isActive tooltip="Skills Hub">
|
|
297
|
+
<Sparkles className="size-4" />
|
|
298
|
+
<span>Skills Hub</span>
|
|
299
|
+
</SidebarMenuButton>
|
|
300
|
+
</SidebarMenuItem>
|
|
301
|
+
<SidebarMenuItem>
|
|
302
|
+
<SidebarMenuButton tooltip="记忆体">
|
|
303
|
+
<Brain className="size-4" />
|
|
304
|
+
<span>记忆体</span>
|
|
305
|
+
</SidebarMenuButton>
|
|
306
|
+
</SidebarMenuItem>
|
|
307
|
+
<SidebarMenuItem>
|
|
308
|
+
<SidebarMenuButton tooltip="知识库">
|
|
309
|
+
<BookOpen className="size-4" />
|
|
310
|
+
<span>知识库</span>
|
|
311
|
+
</SidebarMenuButton>
|
|
312
|
+
</SidebarMenuItem>
|
|
313
|
+
<SidebarMenuItem>
|
|
314
|
+
<SidebarMenuButton tooltip="提示词">
|
|
315
|
+
<MessageSquare className="size-4" />
|
|
316
|
+
<span>提示词</span>
|
|
317
|
+
</SidebarMenuButton>
|
|
318
|
+
</SidebarMenuItem>
|
|
319
|
+
</SidebarMenu>
|
|
320
|
+
</SidebarGroupContent>
|
|
321
|
+
</SidebarGroup>
|
|
322
|
+
|
|
323
|
+
{/* 评测 */}
|
|
324
|
+
<SidebarGroup>
|
|
325
|
+
<SidebarGroupLabel>评测</SidebarGroupLabel>
|
|
326
|
+
<SidebarGroupContent>
|
|
327
|
+
<SidebarMenu>
|
|
328
|
+
<SidebarMenuItem>
|
|
329
|
+
<SidebarMenuButton tooltip="评测集">
|
|
330
|
+
<FileText className="size-4" />
|
|
331
|
+
<span>评测集</span>
|
|
332
|
+
</SidebarMenuButton>
|
|
333
|
+
</SidebarMenuItem>
|
|
334
|
+
<SidebarMenuItem>
|
|
335
|
+
<SidebarMenuButton tooltip="评估器">
|
|
336
|
+
<FlaskConical className="size-4" />
|
|
337
|
+
<span>评估器</span>
|
|
338
|
+
</SidebarMenuButton>
|
|
339
|
+
</SidebarMenuItem>
|
|
340
|
+
<SidebarMenuItem>
|
|
341
|
+
<SidebarMenuButton tooltip="评测任务">
|
|
342
|
+
<ListChecks className="size-4" />
|
|
343
|
+
<span>评测任务</span>
|
|
344
|
+
</SidebarMenuButton>
|
|
345
|
+
</SidebarMenuItem>
|
|
346
|
+
</SidebarMenu>
|
|
347
|
+
</SidebarGroupContent>
|
|
348
|
+
</SidebarGroup>
|
|
349
|
+
|
|
350
|
+
{/* 观测 */}
|
|
351
|
+
<SidebarGroup>
|
|
352
|
+
<SidebarGroupLabel>观测</SidebarGroupLabel>
|
|
353
|
+
<SidebarGroupContent>
|
|
354
|
+
<SidebarMenu>
|
|
355
|
+
<SidebarMenuItem>
|
|
356
|
+
<SidebarMenuButton tooltip="指标监控">
|
|
357
|
+
<BarChart3 className="size-4" />
|
|
358
|
+
<span>指标监控</span>
|
|
359
|
+
</SidebarMenuButton>
|
|
360
|
+
</SidebarMenuItem>
|
|
361
|
+
<SidebarMenuItem>
|
|
362
|
+
<SidebarMenuButton tooltip="Trace追踪">
|
|
363
|
+
<Activity className="size-4" />
|
|
364
|
+
<span>Trace追踪</span>
|
|
365
|
+
</SidebarMenuButton>
|
|
366
|
+
</SidebarMenuItem>
|
|
367
|
+
<SidebarMenuItem>
|
|
368
|
+
<SidebarMenuButton tooltip="操作记录">
|
|
369
|
+
<ClipboardList className="size-4" />
|
|
370
|
+
<span>操作记录</span>
|
|
371
|
+
</SidebarMenuButton>
|
|
372
|
+
</SidebarMenuItem>
|
|
373
|
+
</SidebarMenu>
|
|
374
|
+
</SidebarGroupContent>
|
|
375
|
+
</SidebarGroup>
|
|
376
|
+
</SidebarContent>
|
|
377
|
+
|
|
378
|
+
<SidebarFooter>
|
|
379
|
+
<div className="flex items-center gap-2 px-3 py-3">
|
|
380
|
+
<div className="flex size-7 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold">
|
|
381
|
+
食
|
|
382
|
+
</div>
|
|
383
|
+
<span className="truncate text-sm font-medium">少食</span>
|
|
384
|
+
<button
|
|
385
|
+
type="button"
|
|
386
|
+
className="ml-auto flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
387
|
+
>
|
|
388
|
+
<MoreVertical className="size-4" />
|
|
389
|
+
</button>
|
|
390
|
+
</div>
|
|
391
|
+
</SidebarFooter>
|
|
392
|
+
</Sidebar>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ─── ItemCard (Labeled Fields 布局,匹配目标设计) ─────────────────────────
|
|
397
|
+
|
|
398
|
+
function ItemCard({ skill }: { skill: (typeof builtInSkills)[number] }) {
|
|
399
|
+
return (
|
|
400
|
+
<Card className="group/item relative cursor-pointer transition-shadow hover:shadow-md">
|
|
401
|
+
<CardContent className="flex flex-col gap-2.5">
|
|
402
|
+
{/* Title Row: Avatar + Name + SubName */}
|
|
403
|
+
<div className="flex items-center gap-3">
|
|
404
|
+
<Avatar className="size-10 shrink-0 rounded-lg">
|
|
405
|
+
<AvatarFallback
|
|
406
|
+
className={`rounded-lg text-base font-semibold text-white ${skill.avatarColor}`}
|
|
407
|
+
>
|
|
408
|
+
{skill.name.charAt(0)}
|
|
409
|
+
</AvatarFallback>
|
|
410
|
+
</Avatar>
|
|
411
|
+
<div className="min-w-0 flex-1">
|
|
412
|
+
<div className="truncate text-sm font-semibold text-foreground">
|
|
413
|
+
{skill.name}
|
|
414
|
+
</div>
|
|
415
|
+
<div className="truncate text-xs text-muted-foreground">
|
|
416
|
+
{skill.subName}
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
{/* 描述 */}
|
|
422
|
+
<div className="text-xs text-muted-foreground">
|
|
423
|
+
<span className="text-foreground/70">描述:</span>
|
|
424
|
+
<span className="line-clamp-1">{skill.description}</span>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
{/* 版本 */}
|
|
428
|
+
<div className="text-xs text-muted-foreground">
|
|
429
|
+
<span className="text-foreground/70">版本:</span>
|
|
430
|
+
{skill.version}
|
|
431
|
+
</div>
|
|
432
|
+
|
|
433
|
+
{/* 安全扫描 */}
|
|
434
|
+
<div className="flex items-center gap-1 text-xs">
|
|
435
|
+
<span className="text-foreground/70">安全扫描:</span>
|
|
436
|
+
{skill.security === 'safe' ? (
|
|
437
|
+
<Badge
|
|
438
|
+
variant="outline"
|
|
439
|
+
className="h-5 gap-0.5 px-1.5 text-success"
|
|
440
|
+
>
|
|
441
|
+
<ShieldCheck className="size-3" />
|
|
442
|
+
安全
|
|
443
|
+
</Badge>
|
|
444
|
+
) : (
|
|
445
|
+
<Badge
|
|
446
|
+
variant="outline"
|
|
447
|
+
className="h-5 gap-0.5 px-1.5 text-destructive"
|
|
448
|
+
>
|
|
449
|
+
<ShieldCheck className="size-3" />
|
|
450
|
+
有风险
|
|
451
|
+
</Badge>
|
|
452
|
+
)}
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{/* 安装状态 */}
|
|
456
|
+
<div className="flex items-center gap-1 text-xs">
|
|
457
|
+
<span className="text-foreground/70">安装状态:</span>
|
|
458
|
+
{skill.installStatus === 'installed' && (
|
|
459
|
+
<Tag variant="status" status="success">
|
|
460
|
+
已安装
|
|
461
|
+
</Tag>
|
|
462
|
+
)}
|
|
463
|
+
{skill.installStatus === 'not-installed' && (
|
|
464
|
+
<span className="text-muted-foreground">未安装</span>
|
|
465
|
+
)}
|
|
466
|
+
{skill.installStatus === 'has-update' && (
|
|
467
|
+
<Tag variant="status" status="warning">
|
|
468
|
+
有更新
|
|
469
|
+
</Tag>
|
|
470
|
+
)}
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
{/* 标签分类 */}
|
|
474
|
+
<div className="flex items-center gap-1 text-xs">
|
|
475
|
+
<span className="text-foreground/70">标签分类:</span>
|
|
476
|
+
<Tag palette={skill.categoryPalette}>{skill.category}</Tag>
|
|
477
|
+
</div>
|
|
478
|
+
|
|
479
|
+
{/* 创建时间 */}
|
|
480
|
+
<div className="text-xs text-muted-foreground">
|
|
481
|
+
<span>📅 创建时间:{skill.createdAt}</span>
|
|
482
|
+
</div>
|
|
483
|
+
</CardContent>
|
|
484
|
+
</Card>
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ─── Page Story ────────────────────────────────────────────────────────────
|
|
489
|
+
|
|
490
|
+
/** Skills Hub - OT-CL-1 Tab导航卡片列表(内置Skills Tab) */
|
|
491
|
+
export const Default: Story = {
|
|
492
|
+
render: () => (
|
|
493
|
+
<TooltipProvider>
|
|
494
|
+
<PageShell
|
|
495
|
+
sidebar={<OpentrekSidebar />}
|
|
496
|
+
background="muted"
|
|
497
|
+
// eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- Storybook 固定高度展示
|
|
498
|
+
className="h-[900px]"
|
|
499
|
+
>
|
|
500
|
+
<div className="flex-1 p-5">
|
|
501
|
+
<Card>
|
|
502
|
+
<CardContent className="flex flex-col gap-4">
|
|
503
|
+
{/* PageHeader */}
|
|
504
|
+
<PageHeader>
|
|
505
|
+
<PageHeaderHeading>
|
|
506
|
+
<span className="text-lg font-medium">Skills Hub</span>
|
|
507
|
+
</PageHeaderHeading>
|
|
508
|
+
</PageHeader>
|
|
509
|
+
|
|
510
|
+
{/* Tabs */}
|
|
511
|
+
<Tabs defaultValue="builtin" className="flex-col">
|
|
512
|
+
<TabsList variant="line">
|
|
513
|
+
<TabsTrigger value="workspace">空间Skills</TabsTrigger>
|
|
514
|
+
<TabsTrigger value="shared">共享Skills</TabsTrigger>
|
|
515
|
+
<TabsTrigger value="builtin">内置Skills</TabsTrigger>
|
|
516
|
+
</TabsList>
|
|
517
|
+
|
|
518
|
+
<TabsContent
|
|
519
|
+
value="builtin"
|
|
520
|
+
className="flex flex-col gap-4 pt-4"
|
|
521
|
+
>
|
|
522
|
+
{/* ActionToolbar */}
|
|
523
|
+
<div className="flex items-center gap-2">
|
|
524
|
+
<div className="relative">
|
|
525
|
+
<Input
|
|
526
|
+
placeholder="搜索Skills名称..."
|
|
527
|
+
className="w-56 pr-8"
|
|
528
|
+
/>
|
|
529
|
+
<Search className="absolute right-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
530
|
+
</div>
|
|
531
|
+
<div className="ml-auto">
|
|
532
|
+
<Select defaultValue="all">
|
|
533
|
+
<SelectTrigger className="w-24">
|
|
534
|
+
<SelectValue placeholder="全部" />
|
|
535
|
+
</SelectTrigger>
|
|
536
|
+
<SelectContent>
|
|
537
|
+
<SelectItem value="all">全部</SelectItem>
|
|
538
|
+
<SelectItem value="ai">AI能力</SelectItem>
|
|
539
|
+
<SelectItem value="data">数据处理</SelectItem>
|
|
540
|
+
<SelectItem value="auto">自动化</SelectItem>
|
|
541
|
+
</SelectContent>
|
|
542
|
+
</Select>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
|
|
546
|
+
{/* CardGrid - 3列 */}
|
|
547
|
+
<div className="grid grid-cols-3 gap-4">
|
|
548
|
+
{builtInSkills.map((skill) => (
|
|
549
|
+
<ItemCard key={skill.id} skill={skill} />
|
|
550
|
+
))}
|
|
551
|
+
</div>
|
|
552
|
+
</TabsContent>
|
|
553
|
+
|
|
554
|
+
<TabsContent value="workspace" className="pt-4">
|
|
555
|
+
<p className="text-sm text-muted-foreground">
|
|
556
|
+
空间Skills内容区(卡片列表)
|
|
557
|
+
</p>
|
|
558
|
+
</TabsContent>
|
|
559
|
+
|
|
560
|
+
<TabsContent value="shared" className="pt-4">
|
|
561
|
+
<p className="text-sm text-muted-foreground">
|
|
562
|
+
共享Skills内容区(卡片列表)
|
|
563
|
+
</p>
|
|
564
|
+
</TabsContent>
|
|
565
|
+
</Tabs>
|
|
566
|
+
</CardContent>
|
|
567
|
+
</Card>
|
|
568
|
+
</div>
|
|
569
|
+
</PageShell>
|
|
570
|
+
</TooltipProvider>
|
|
571
|
+
),
|
|
572
|
+
};
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import {
|
|
3
|
+
Sidebar,
|
|
4
|
+
SidebarContent,
|
|
5
|
+
SidebarFooter,
|
|
6
|
+
SidebarGroup,
|
|
7
|
+
SidebarGroupContent,
|
|
8
|
+
SidebarGroupLabel,
|
|
9
|
+
SidebarHeader,
|
|
10
|
+
SidebarMenu,
|
|
11
|
+
SidebarMenuButton,
|
|
12
|
+
SidebarMenuItem,
|
|
13
|
+
} from '@/components/sidebar';
|
|
14
|
+
import { PageShell } from '@/components/page-shell';
|
|
15
|
+
import { PageHeader, PageHeaderHeading } from '@/components/page-header';
|
|
16
|
+
import { Card, CardContent } from '@/components/card';
|
|
17
|
+
import { Button } from '@/components/button';
|
|
18
|
+
import { Input } from '@/components/input';
|
|
19
|
+
import { DataTable } from '@/components/data-table';
|
|
20
|
+
import type { DataTableColumn } from '@/components/data-table';
|
|
21
|
+
import { Tag } from '@/components/tag';
|
|
22
|
+
import { TooltipProvider } from '@/components/tooltip';
|
|
23
|
+
import {
|
|
24
|
+
Search,
|
|
25
|
+
Bot,
|
|
26
|
+
BookOpen,
|
|
27
|
+
Sparkles,
|
|
28
|
+
FlaskConical,
|
|
29
|
+
BarChart3,
|
|
30
|
+
MoreVertical,
|
|
31
|
+
PanelLeftClose,
|
|
32
|
+
LayoutGrid,
|
|
33
|
+
Wrench,
|
|
34
|
+
Brain,
|
|
35
|
+
MessageSquare,
|
|
36
|
+
FileText,
|
|
37
|
+
ListChecks,
|
|
38
|
+
Activity,
|
|
39
|
+
ClipboardList,
|
|
40
|
+
ChevronDown,
|
|
41
|
+
Plus,
|
|
42
|
+
} from 'lucide-react';
|
|
43
|
+
|
|
44
|
+
const meta: Meta = {
|
|
45
|
+
title: '页面示例 · Page Examples/评估器 标准列表',
|
|
46
|
+
tags: ['autodocs'],
|
|
47
|
+
parameters: {
|
|
48
|
+
layout: 'fullscreen',
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export default meta;
|
|
52
|
+
|
|
53
|
+
type Story = StoryObj;
|
|
54
|
+
|
|
55
|
+
// ─── Mock Data ─────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
interface Evaluator extends Record<string, unknown> {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
type: string;
|
|
61
|
+
metrics: string;
|
|
62
|
+
status: 'active' | 'inactive' | 'draft';
|
|
63
|
+
creator: string;
|
|
64
|
+
creatorUsername: string;
|
|
65
|
+
createdAt: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const evaluators: Evaluator[] = [
|
|
69
|
+
{
|
|
70
|
+
id: '1',
|
|
71
|
+
name: '通用问答质量评估',
|
|
72
|
+
type: '模型评估',
|
|
73
|
+
metrics: '准确率、流畅度、相关性',
|
|
74
|
+
status: 'active',
|
|
75
|
+
creator: '张明',
|
|
76
|
+
creatorUsername: 'zhangming',
|
|
77
|
+
createdAt: '2026-05-10 14:30:00',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: '2',
|
|
81
|
+
name: 'RAG 检索质量评估',
|
|
82
|
+
type: '检索评估',
|
|
83
|
+
metrics: '召回率、精确率、MRR',
|
|
84
|
+
status: 'active',
|
|
85
|
+
creator: '李华',
|
|
86
|
+
creatorUsername: 'lihua',
|
|
87
|
+
createdAt: '2026-05-08 10:15:00',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: '3',
|
|
91
|
+
name: '安全合规检测器',
|
|
92
|
+
type: '安全评估',
|
|
93
|
+
metrics: '违规率、敏感词命中',
|
|
94
|
+
status: 'active',
|
|
95
|
+
creator: '王磊',
|
|
96
|
+
creatorUsername: 'wanglei',
|
|
97
|
+
createdAt: '2026-05-06 16:45:00',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: '4',
|
|
101
|
+
name: '代码生成评估',
|
|
102
|
+
type: '模型评估',
|
|
103
|
+
metrics: 'Pass@1、编译通过率',
|
|
104
|
+
status: 'draft',
|
|
105
|
+
creator: '赵婷',
|
|
106
|
+
creatorUsername: 'zhaoting',
|
|
107
|
+
createdAt: '2026-05-04 09:20:00',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: '5',
|
|
111
|
+
name: '多轮对话一致性评估',
|
|
112
|
+
type: '对话评估',
|
|
113
|
+
metrics: '上下文一致性、话题连贯性',
|
|
114
|
+
status: 'active',
|
|
115
|
+
creator: '张明',
|
|
116
|
+
creatorUsername: 'zhangming',
|
|
117
|
+
createdAt: '2026-05-02 11:30:00',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: '6',
|
|
121
|
+
name: '摘要生成评估',
|
|
122
|
+
type: '模型评估',
|
|
123
|
+
metrics: 'ROUGE-L、信息覆盖率',
|
|
124
|
+
status: 'inactive',
|
|
125
|
+
creator: '李华',
|
|
126
|
+
creatorUsername: 'lihua',
|
|
127
|
+
createdAt: '2026-04-28 15:10:00',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: '7',
|
|
131
|
+
name: '意图识别评估',
|
|
132
|
+
type: '分类评估',
|
|
133
|
+
metrics: 'F1-Score、准确率',
|
|
134
|
+
status: 'active',
|
|
135
|
+
creator: '王磊',
|
|
136
|
+
creatorUsername: 'wanglei',
|
|
137
|
+
createdAt: '2026-04-25 08:50:00',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: '8',
|
|
141
|
+
name: '知识库问答评估',
|
|
142
|
+
type: '检索评估',
|
|
143
|
+
metrics: 'EM、F1、召回率',
|
|
144
|
+
status: 'active',
|
|
145
|
+
creator: '赵婷',
|
|
146
|
+
creatorUsername: 'zhaoting',
|
|
147
|
+
createdAt: '2026-04-22 13:40:00',
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
// ─── Table Columns ─────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
const columns: DataTableColumn<Evaluator>[] = [
|
|
154
|
+
{
|
|
155
|
+
key: 'name',
|
|
156
|
+
title: '评估器名称',
|
|
157
|
+
dataIndex: 'name',
|
|
158
|
+
width: 200,
|
|
159
|
+
sortable: true,
|
|
160
|
+
render: (value) => (
|
|
161
|
+
<span className="cursor-pointer font-medium text-primary">
|
|
162
|
+
{value as string}
|
|
163
|
+
</span>
|
|
164
|
+
),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
key: 'type',
|
|
168
|
+
title: '类型',
|
|
169
|
+
dataIndex: 'type',
|
|
170
|
+
width: 120,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
key: 'metrics',
|
|
174
|
+
title: '评测指标',
|
|
175
|
+
dataIndex: 'metrics',
|
|
176
|
+
width: 220,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
key: 'status',
|
|
180
|
+
title: '状态',
|
|
181
|
+
dataIndex: 'status',
|
|
182
|
+
width: 100,
|
|
183
|
+
render: (value) => {
|
|
184
|
+
const statusMap: Record<
|
|
185
|
+
string,
|
|
186
|
+
{ status: 'success' | 'default' | 'warning'; text: string }
|
|
187
|
+
> = {
|
|
188
|
+
active: { status: 'success', text: '已启用' },
|
|
189
|
+
inactive: { status: 'default', text: '已停用' },
|
|
190
|
+
draft: { status: 'warning', text: '草稿' },
|
|
191
|
+
};
|
|
192
|
+
const config = statusMap[value as string] ?? statusMap['active'];
|
|
193
|
+
return (
|
|
194
|
+
<Tag variant="status" status={config!.status}>
|
|
195
|
+
{config!.text}
|
|
196
|
+
</Tag>
|
|
197
|
+
);
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
key: 'creator',
|
|
202
|
+
title: '创建人',
|
|
203
|
+
dataIndex: 'creator',
|
|
204
|
+
width: 150,
|
|
205
|
+
render: (_value, record) => (
|
|
206
|
+
<span className="text-sm">
|
|
207
|
+
{record.creator}{' '}
|
|
208
|
+
<span className="text-muted-foreground">
|
|
209
|
+
({record.creatorUsername})
|
|
210
|
+
</span>
|
|
211
|
+
</span>
|
|
212
|
+
),
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
key: 'createdAt',
|
|
216
|
+
title: '创建时间',
|
|
217
|
+
dataIndex: 'createdAt',
|
|
218
|
+
width: 180,
|
|
219
|
+
sortable: true,
|
|
220
|
+
render: (value) => <span className="tabular-nums">{value as string}</span>,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
key: 'actions',
|
|
224
|
+
title: '操作',
|
|
225
|
+
width: 160,
|
|
226
|
+
align: 'right',
|
|
227
|
+
render: () => (
|
|
228
|
+
<div className="flex items-center justify-end gap-3">
|
|
229
|
+
<span className="cursor-pointer text-primary">详情</span>
|
|
230
|
+
<span className="cursor-pointer text-primary">编辑</span>
|
|
231
|
+
<span className="cursor-pointer text-destructive">删除</span>
|
|
232
|
+
</div>
|
|
233
|
+
),
|
|
234
|
+
},
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
// ─── OP Sidebar ────────────────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* OP Logo — inline SVG(来源:_assets/OP_LOGO.svg)
|
|
241
|
+
*/
|
|
242
|
+
function OpLogo({ className }: { className?: string }) {
|
|
243
|
+
return (
|
|
244
|
+
<svg
|
|
245
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
246
|
+
fill="none"
|
|
247
|
+
viewBox="0 0 84 84"
|
|
248
|
+
className={className}
|
|
249
|
+
>
|
|
250
|
+
<defs>
|
|
251
|
+
<clipPath id="opai-logo-clip-evaluators">
|
|
252
|
+
<rect x="0" y="0" width="84" height="84" rx="18" />
|
|
253
|
+
</clipPath>
|
|
254
|
+
</defs>
|
|
255
|
+
<g clipPath="url(#opai-logo-clip-evaluators)">
|
|
256
|
+
<rect x="0" y="0" width="84" height="84" rx="18" fill="currentColor" />
|
|
257
|
+
<path
|
|
258
|
+
d="M56.628 43.209L48.407 45.869C46.698 46.422 45.142 44.688 45.876 43.048L60.648 10.047C61.316 8.554 62.798 7.594 64.434 7.594L70.258 7.594C71.893 7.594 73.376 8.554 74.044 10.047L88.816 43.048C89.55 44.688 87.994 46.422 86.284 45.869L78.063 43.209C77.784 43.119 77.556 42.914 77.436 42.646L69.239 24.334C68.507 22.698 66.185 22.698 65.453 24.334L57.255 42.646C57.136 42.914 56.908 43.119 56.628 43.209ZM54.737 51.511C53.792 51.195 53.792 49.859 54.737 49.543L63.03 46.774C63.3 46.684 63.521 46.487 63.641 46.229L66.407 40.304C66.779 39.506 67.914 39.506 68.286 40.304L71.052 46.229C71.172 46.487 71.393 46.684 71.663 46.774L79.956 49.543C80.901 49.859 80.901 51.195 79.956 51.511L71.663 54.28C71.393 54.37 71.172 54.567 71.052 54.825L68.286 60.75C67.914 61.548 66.779 61.548 66.407 60.75L63.641 54.825C63.521 54.567 63.3 54.37 63.03 54.28L54.737 51.511Z"
|
|
259
|
+
fillRule="evenodd"
|
|
260
|
+
fill="white"
|
|
261
|
+
transform="matrix(0.707 0.707 -0.707 0.707 18.752 -30.083)"
|
|
262
|
+
/>
|
|
263
|
+
</g>
|
|
264
|
+
</svg>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function OpentrekSidebar() {
|
|
269
|
+
return (
|
|
270
|
+
<Sidebar collapsible="none">
|
|
271
|
+
<SidebarHeader className="px-4 pt-4 pb-0">
|
|
272
|
+
<div className="flex items-center justify-between">
|
|
273
|
+
<div className="flex h-8 items-center gap-2">
|
|
274
|
+
<OpLogo className="size-6 shrink-0 text-foreground" />
|
|
275
|
+
<span className="text-base font-black leading-tight tracking-tight">
|
|
276
|
+
OPENTREK
|
|
277
|
+
{/* eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- demo badge 10px 无对应 token */}
|
|
278
|
+
<span className="ml-1 text-[10px] font-bold tracking-wider text-muted-foreground">
|
|
279
|
+
DEV
|
|
280
|
+
</span>
|
|
281
|
+
</span>
|
|
282
|
+
</div>
|
|
283
|
+
<button
|
|
284
|
+
type="button"
|
|
285
|
+
className="flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground"
|
|
286
|
+
>
|
|
287
|
+
<PanelLeftClose className="size-4" />
|
|
288
|
+
</button>
|
|
289
|
+
</div>
|
|
290
|
+
</SidebarHeader>
|
|
291
|
+
|
|
292
|
+
<SidebarContent className="pt-4">
|
|
293
|
+
{/* 空间选择器 */}
|
|
294
|
+
<SidebarGroup>
|
|
295
|
+
<SidebarGroupContent>
|
|
296
|
+
<SidebarMenu>
|
|
297
|
+
<SidebarMenuItem>
|
|
298
|
+
<SidebarMenuButton tooltip="产品研发空间">
|
|
299
|
+
<Bot className="size-4" />
|
|
300
|
+
<span>产品研发空间</span>
|
|
301
|
+
<ChevronDown className="ml-auto size-3.5 text-muted-foreground" />
|
|
302
|
+
</SidebarMenuButton>
|
|
303
|
+
</SidebarMenuItem>
|
|
304
|
+
</SidebarMenu>
|
|
305
|
+
</SidebarGroupContent>
|
|
306
|
+
</SidebarGroup>
|
|
307
|
+
|
|
308
|
+
{/* 概览 */}
|
|
309
|
+
<SidebarGroup>
|
|
310
|
+
<SidebarGroupContent>
|
|
311
|
+
<SidebarMenu>
|
|
312
|
+
<SidebarMenuItem>
|
|
313
|
+
<SidebarMenuButton tooltip="概览">
|
|
314
|
+
<LayoutGrid className="size-4" />
|
|
315
|
+
<span>概览</span>
|
|
316
|
+
</SidebarMenuButton>
|
|
317
|
+
</SidebarMenuItem>
|
|
318
|
+
</SidebarMenu>
|
|
319
|
+
</SidebarGroupContent>
|
|
320
|
+
</SidebarGroup>
|
|
321
|
+
|
|
322
|
+
{/* 开发 */}
|
|
323
|
+
<SidebarGroup>
|
|
324
|
+
<SidebarGroupLabel>开发</SidebarGroupLabel>
|
|
325
|
+
<SidebarGroupContent>
|
|
326
|
+
<SidebarMenu>
|
|
327
|
+
<SidebarMenuItem>
|
|
328
|
+
<SidebarMenuButton tooltip="智能体">
|
|
329
|
+
<Bot className="size-4" />
|
|
330
|
+
<span>智能体</span>
|
|
331
|
+
</SidebarMenuButton>
|
|
332
|
+
</SidebarMenuItem>
|
|
333
|
+
<SidebarMenuItem>
|
|
334
|
+
<SidebarMenuButton tooltip="工具箱">
|
|
335
|
+
<Wrench className="size-4" />
|
|
336
|
+
<span>工具箱</span>
|
|
337
|
+
</SidebarMenuButton>
|
|
338
|
+
</SidebarMenuItem>
|
|
339
|
+
</SidebarMenu>
|
|
340
|
+
</SidebarGroupContent>
|
|
341
|
+
</SidebarGroup>
|
|
342
|
+
|
|
343
|
+
{/* Skills Hub / 记忆体 / 知识库 / 提示词 */}
|
|
344
|
+
<SidebarGroup>
|
|
345
|
+
<SidebarGroupContent>
|
|
346
|
+
<SidebarMenu>
|
|
347
|
+
<SidebarMenuItem>
|
|
348
|
+
<SidebarMenuButton tooltip="Skills Hub">
|
|
349
|
+
<Sparkles className="size-4" />
|
|
350
|
+
<span>Skills Hub</span>
|
|
351
|
+
</SidebarMenuButton>
|
|
352
|
+
</SidebarMenuItem>
|
|
353
|
+
<SidebarMenuItem>
|
|
354
|
+
<SidebarMenuButton tooltip="记忆体">
|
|
355
|
+
<Brain className="size-4" />
|
|
356
|
+
<span>记忆体</span>
|
|
357
|
+
</SidebarMenuButton>
|
|
358
|
+
</SidebarMenuItem>
|
|
359
|
+
<SidebarMenuItem>
|
|
360
|
+
<SidebarMenuButton tooltip="知识库">
|
|
361
|
+
<BookOpen className="size-4" />
|
|
362
|
+
<span>知识库</span>
|
|
363
|
+
</SidebarMenuButton>
|
|
364
|
+
</SidebarMenuItem>
|
|
365
|
+
<SidebarMenuItem>
|
|
366
|
+
<SidebarMenuButton tooltip="提示词">
|
|
367
|
+
<MessageSquare className="size-4" />
|
|
368
|
+
<span>提示词</span>
|
|
369
|
+
</SidebarMenuButton>
|
|
370
|
+
</SidebarMenuItem>
|
|
371
|
+
</SidebarMenu>
|
|
372
|
+
</SidebarGroupContent>
|
|
373
|
+
</SidebarGroup>
|
|
374
|
+
|
|
375
|
+
{/* 评测 */}
|
|
376
|
+
<SidebarGroup>
|
|
377
|
+
<SidebarGroupLabel>评测</SidebarGroupLabel>
|
|
378
|
+
<SidebarGroupContent>
|
|
379
|
+
<SidebarMenu>
|
|
380
|
+
<SidebarMenuItem>
|
|
381
|
+
<SidebarMenuButton tooltip="评测集">
|
|
382
|
+
<FileText className="size-4" />
|
|
383
|
+
<span>评测集</span>
|
|
384
|
+
</SidebarMenuButton>
|
|
385
|
+
</SidebarMenuItem>
|
|
386
|
+
<SidebarMenuItem>
|
|
387
|
+
<SidebarMenuButton isActive tooltip="评估器">
|
|
388
|
+
<FlaskConical className="size-4" />
|
|
389
|
+
<span>评估器</span>
|
|
390
|
+
</SidebarMenuButton>
|
|
391
|
+
</SidebarMenuItem>
|
|
392
|
+
<SidebarMenuItem>
|
|
393
|
+
<SidebarMenuButton tooltip="评测任务">
|
|
394
|
+
<ListChecks className="size-4" />
|
|
395
|
+
<span>评测任务</span>
|
|
396
|
+
</SidebarMenuButton>
|
|
397
|
+
</SidebarMenuItem>
|
|
398
|
+
</SidebarMenu>
|
|
399
|
+
</SidebarGroupContent>
|
|
400
|
+
</SidebarGroup>
|
|
401
|
+
|
|
402
|
+
{/* 观测 */}
|
|
403
|
+
<SidebarGroup>
|
|
404
|
+
<SidebarGroupLabel>观测</SidebarGroupLabel>
|
|
405
|
+
<SidebarGroupContent>
|
|
406
|
+
<SidebarMenu>
|
|
407
|
+
<SidebarMenuItem>
|
|
408
|
+
<SidebarMenuButton tooltip="指标监控">
|
|
409
|
+
<BarChart3 className="size-4" />
|
|
410
|
+
<span>指标监控</span>
|
|
411
|
+
</SidebarMenuButton>
|
|
412
|
+
</SidebarMenuItem>
|
|
413
|
+
<SidebarMenuItem>
|
|
414
|
+
<SidebarMenuButton tooltip="Trace追踪">
|
|
415
|
+
<Activity className="size-4" />
|
|
416
|
+
<span>Trace追踪</span>
|
|
417
|
+
</SidebarMenuButton>
|
|
418
|
+
</SidebarMenuItem>
|
|
419
|
+
<SidebarMenuItem>
|
|
420
|
+
<SidebarMenuButton tooltip="操作记录">
|
|
421
|
+
<ClipboardList className="size-4" />
|
|
422
|
+
<span>操作记录</span>
|
|
423
|
+
</SidebarMenuButton>
|
|
424
|
+
</SidebarMenuItem>
|
|
425
|
+
</SidebarMenu>
|
|
426
|
+
</SidebarGroupContent>
|
|
427
|
+
</SidebarGroup>
|
|
428
|
+
</SidebarContent>
|
|
429
|
+
|
|
430
|
+
<SidebarFooter>
|
|
431
|
+
<div className="flex items-center gap-2 px-3 py-3">
|
|
432
|
+
<div className="flex size-7 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold">
|
|
433
|
+
食
|
|
434
|
+
</div>
|
|
435
|
+
<span className="truncate text-sm font-medium">少食</span>
|
|
436
|
+
<button
|
|
437
|
+
type="button"
|
|
438
|
+
className="ml-auto flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
439
|
+
>
|
|
440
|
+
<MoreVertical className="size-4" />
|
|
441
|
+
</button>
|
|
442
|
+
</div>
|
|
443
|
+
</SidebarFooter>
|
|
444
|
+
</Sidebar>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ─── Page Story ────────────────────────────────────────────────────────────
|
|
449
|
+
|
|
450
|
+
/** 评估器 - OT-STD-2 简单标准列表 */
|
|
451
|
+
export const Default: Story = {
|
|
452
|
+
render: () => (
|
|
453
|
+
<TooltipProvider>
|
|
454
|
+
<PageShell
|
|
455
|
+
sidebar={<OpentrekSidebar />}
|
|
456
|
+
background="muted"
|
|
457
|
+
// eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- Storybook 固定高度展示
|
|
458
|
+
className="h-[900px]"
|
|
459
|
+
>
|
|
460
|
+
<div className="flex-1 p-5">
|
|
461
|
+
<Card>
|
|
462
|
+
<CardContent className="flex flex-col gap-4">
|
|
463
|
+
{/* PageHeader */}
|
|
464
|
+
<PageHeader>
|
|
465
|
+
<PageHeaderHeading>
|
|
466
|
+
<span className="text-lg font-medium">评估器</span>
|
|
467
|
+
</PageHeaderHeading>
|
|
468
|
+
</PageHeader>
|
|
469
|
+
|
|
470
|
+
{/* ActionToolbar: F 变体 (Primary + Search) */}
|
|
471
|
+
<div className="flex items-center gap-2">
|
|
472
|
+
<Button variant="default" color="primary">
|
|
473
|
+
<Plus className="size-4" />
|
|
474
|
+
新建评估器
|
|
475
|
+
</Button>
|
|
476
|
+
<div className="relative ml-5">
|
|
477
|
+
<Input
|
|
478
|
+
placeholder="搜索评估器名称..."
|
|
479
|
+
className="w-56 pr-8"
|
|
480
|
+
/>
|
|
481
|
+
<Search className="absolute right-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
482
|
+
</div>
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
{/* DataTable */}
|
|
486
|
+
<DataTable<Evaluator>
|
|
487
|
+
columns={columns}
|
|
488
|
+
dataSource={evaluators}
|
|
489
|
+
rowKey="id"
|
|
490
|
+
pagination={{
|
|
491
|
+
current: 1,
|
|
492
|
+
pageSize: 10,
|
|
493
|
+
total: 24,
|
|
494
|
+
}}
|
|
495
|
+
/>
|
|
496
|
+
</CardContent>
|
|
497
|
+
</Card>
|
|
498
|
+
</div>
|
|
499
|
+
</PageShell>
|
|
500
|
+
</TooltipProvider>
|
|
501
|
+
),
|
|
502
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 防止 Radix DismissableLayer 级联关闭 hook。
|
|
7
|
+
*
|
|
8
|
+
* 当弹窗 / 抽屉内嵌 Select / Popover / HoverCard 等 Radix Popper 浮层时,
|
|
9
|
+
* 点击 Popper 外部(但仍在 Dialog / Sheet 内)会被 DismissableLayer 误判为"外部点击",
|
|
10
|
+
* 从而连带关闭宿主弹窗。本 hook 通过 pointerdown 捕获阶段嗅探 `[data-radix-popper-content-wrapper]`
|
|
11
|
+
* 是否存在来决定是否阻止级联关闭。
|
|
12
|
+
*
|
|
13
|
+
* 用法:
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const { onPointerDownOutside } = useRadixPopperGuard();
|
|
16
|
+
* <DialogContent onPointerDownOutside={onPointerDownOutside} />
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* 逻辑:
|
|
20
|
+
* - pointerdown 捕获阶段:记录当前 DOM 是否存在 `[data-radix-popper-content-wrapper]`(即有 Popper 处于打开状态)
|
|
21
|
+
* - onPointerDownOutside 回调:若上一次捕获阶段标记为"有 Popper 打开",则 `e.preventDefault()` 阻止 DismissableLayer 触发 dismiss
|
|
22
|
+
* - 若无 Popper(正常遮罩点击),不阻止,DismissableLayer 原生 dismiss 机制正常触发
|
|
23
|
+
*/
|
|
24
|
+
export function useRadixPopperGuard() {
|
|
25
|
+
const popperOpenRef = React.useRef(false);
|
|
26
|
+
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
function handlePointerDown() {
|
|
29
|
+
popperOpenRef.current = !!document.querySelector(
|
|
30
|
+
'[data-radix-popper-content-wrapper]',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 在捕获阶段侦听,确保在 Radix DismissableLayer 处理之前执行
|
|
35
|
+
document.addEventListener('pointerdown', handlePointerDown, true);
|
|
36
|
+
return () => {
|
|
37
|
+
document.removeEventListener('pointerdown', handlePointerDown, true);
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const onPointerDownOutside = React.useCallback(
|
|
42
|
+
(e: { preventDefault: () => void }) => {
|
|
43
|
+
if (popperOpenRef.current) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
[],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return { onPointerDownOutside };
|
|
51
|
+
}
|