chore(web): 前端质量检查 - 规范模型编辑格式并消除回归测试缩进依赖
This commit is contained in:
@@ -7,12 +7,18 @@
|
||||
}
|
||||
.choice-browser-panels {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.35fr) minmax(0, .65fr);
|
||||
grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--workspace-border);
|
||||
border-radius: var(--r-md);
|
||||
}
|
||||
.choice-browser-list { height: 248px; overflow-y: auto; overscroll-behavior: contain; padding: 4px; scrollbar-gutter: stable; }
|
||||
.choice-browser-list {
|
||||
height: 248px;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding: 4px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.choice-browser-option {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 120px) 16px;
|
||||
@@ -23,26 +29,105 @@
|
||||
border-radius: var(--r-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
.choice-browser-option:hover { background: var(--surface-hover); }
|
||||
.choice-browser-option[data-selected="true"] { background: var(--workspace-accent-soft); box-shadow: inset 0 0 0 1px var(--workspace-border-strong); }
|
||||
.choice-browser-option:has(input:disabled) { opacity: .5; cursor: not-allowed; }
|
||||
.choice-browser-option:has(input:focus-visible) { outline: 2px solid var(--ring); outline-offset: -2px; }
|
||||
.choice-browser-option input { accent-color: var(--workspace-accent); width: 14px; height: 14px; }
|
||||
.choice-browser-main { display: grid; min-width: 0; gap: 2px; }
|
||||
.choice-browser-main strong { font-size: var(--fs-caption); font-weight: 600; overflow-wrap: anywhere; }
|
||||
.choice-browser-main > span, .choice-browser-vendor { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-label); color: var(--muted-foreground); }
|
||||
.choice-browser-main > span { font-family: monospace; }
|
||||
.choice-browser-detail { min-width: 0; max-height: 248px; overflow-y: auto; overscroll-behavior: contain; padding: 16px; border-left: 1px solid var(--workspace-border); }
|
||||
.choice-browser-detail > span { display: block; margin-bottom: 4px; color: var(--muted-foreground); font-size: var(--fs-label); }
|
||||
.choice-browser-detail > strong { font-size: var(--fs-caption); overflow-wrap: anywhere; }
|
||||
.choice-browser-detail dl { display: grid; gap: 8px; margin-top: 12px; font-size: var(--fs-caption); }
|
||||
.choice-browser-detail dl > div { display: grid; grid-template-columns: 4em minmax(0, 1fr); gap: 8px; }
|
||||
.choice-browser-detail dt, .choice-browser-detail p { color: var(--muted-foreground); }
|
||||
.choice-browser-detail dd, .choice-browser-detail p { margin: 0; overflow-wrap: anywhere; }
|
||||
.choice-browser-state { display: grid; min-height: 100px; place-items: center; color: var(--muted-foreground); font-size: var(--fs-caption); padding: 16px; }
|
||||
@container (max-width: 620px) {
|
||||
.choice-browser-panels { grid-template-columns: minmax(0, 1fr); }
|
||||
.choice-browser-detail { border-left: 0; border-top: 1px solid var(--workspace-border); }
|
||||
.choice-browser-option { grid-template-columns: minmax(0, 1fr) 16px; }
|
||||
.choice-browser-vendor { display: none; }
|
||||
.choice-browser-option:hover {
|
||||
background: var(--surface-hover);
|
||||
}
|
||||
.choice-browser-option[data-selected="true"] {
|
||||
background: var(--workspace-accent-soft);
|
||||
box-shadow: inset 0 0 0 1px var(--workspace-border-strong);
|
||||
}
|
||||
.choice-browser-option:has(input:disabled) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.choice-browser-option:has(input:focus-visible) {
|
||||
outline: 2px solid var(--ring);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.choice-browser-option input {
|
||||
accent-color: var(--workspace-accent);
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.choice-browser-main {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
}
|
||||
.choice-browser-main strong {
|
||||
font-size: var(--fs-caption);
|
||||
font-weight: 600;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.choice-browser-main > span,
|
||||
.choice-browser-vendor {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: var(--fs-label);
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
.choice-browser-main > span {
|
||||
font-family: monospace;
|
||||
}
|
||||
.choice-browser-detail {
|
||||
min-width: 0;
|
||||
max-height: 248px;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding: 16px;
|
||||
border-left: 1px solid var(--workspace-border);
|
||||
}
|
||||
.choice-browser-detail > span {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
color: var(--muted-foreground);
|
||||
font-size: var(--fs-label);
|
||||
}
|
||||
.choice-browser-detail > strong {
|
||||
font-size: var(--fs-caption);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.choice-browser-detail dl {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
font-size: var(--fs-caption);
|
||||
}
|
||||
.choice-browser-detail dl > div {
|
||||
display: grid;
|
||||
grid-template-columns: 4em minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
.choice-browser-detail dt,
|
||||
.choice-browser-detail p {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
.choice-browser-detail dd,
|
||||
.choice-browser-detail p {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.choice-browser-state {
|
||||
display: grid;
|
||||
min-height: 100px;
|
||||
place-items: center;
|
||||
color: var(--muted-foreground);
|
||||
font-size: var(--fs-caption);
|
||||
padding: 16px;
|
||||
}
|
||||
@container (max-width: 620px) {
|
||||
.choice-browser-panels {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
.choice-browser-detail {
|
||||
border-left: 0;
|
||||
border-top: 1px solid var(--workspace-border);
|
||||
}
|
||||
.choice-browser-option {
|
||||
grid-template-columns: minmax(0, 1fr) 16px;
|
||||
}
|
||||
.choice-browser-vendor {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,24 +42,12 @@ export function ChoiceBrowser({
|
||||
const [query, setQuery] = useState("");
|
||||
const selected = options.find((option) => option.value === value);
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
const visible = options.filter((option) =>
|
||||
[option.label, option.subtitle, option.vendor, option.searchText].join(" ").toLowerCase().includes(normalizedQuery),
|
||||
);
|
||||
const visible = options.filter((option) => [option.label, option.subtitle, option.vendor, option.searchText].join(" ").toLowerCase().includes(normalizedQuery));
|
||||
const unavailable = disabled || loading || Boolean(error);
|
||||
|
||||
return (
|
||||
<div className="choice-browser">
|
||||
<Input
|
||||
id={id}
|
||||
{...aria}
|
||||
allowClear
|
||||
aria-label={`搜索${label}`}
|
||||
disabled={unavailable}
|
||||
prefix={<Search className="size-3.5" aria-hidden="true" />}
|
||||
placeholder={placeholder}
|
||||
value={query}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
/>
|
||||
<Input id={id} {...aria} allowClear aria-label={`搜索${label}`} disabled={unavailable} prefix={<Search className="size-3.5" aria-hidden="true" />} placeholder={placeholder} value={query} onChange={(event) => setQuery(event.target.value)} />
|
||||
{loading || error || !options.length ? (
|
||||
<div className="choice-browser-state" role={error ? "alert" : "status"}>
|
||||
{loading ? "正在读取可选项…" : error || "暂无可用选项"}
|
||||
@@ -76,14 +64,7 @@ export function ChoiceBrowser({
|
||||
<span className="choice-browser-vendor" title={option.vendor}>
|
||||
{option.vendor}
|
||||
</span>
|
||||
<input
|
||||
type="radio"
|
||||
name={name}
|
||||
value={option.value}
|
||||
checked={option.value === value}
|
||||
disabled={unavailable || option.disabled}
|
||||
onChange={() => onChange?.(option.value)}
|
||||
/>
|
||||
<input type="radio" name={name} value={option.value} checked={option.value === value} disabled={unavailable || option.disabled} onChange={() => onChange?.(option.value)} />
|
||||
</label>
|
||||
))}
|
||||
{!visible.length && (
|
||||
|
||||
@@ -1,23 +1,103 @@
|
||||
.model-editor-modal .ant-modal-container { display: flex; flex-direction: column; height: min(880px, calc(100dvh - 48px)); }
|
||||
.model-editor-modal .ant-modal-header, .model-editor-modal .ant-modal-footer { padding: 18px 24px; flex-shrink: 0; }
|
||||
.model-editor-modal .ant-modal-footer { border-top: 1px solid var(--workspace-border); }
|
||||
.model-editor-modal .ant-modal-body { display: flex; flex-direction: column; overflow: hidden; }
|
||||
.model-editor-modal .ant-modal-body > .ant-form { display: flex; flex-direction: column; height: 100%; min-height: 0; }
|
||||
.model-editor-modal .ant-tabs { display: flex; flex: 1; min-height: 0; }
|
||||
.model-editor-modal .ant-tabs-nav { flex-shrink: 0; margin: 0; padding-inline: 24px; }
|
||||
.model-editor-modal .ant-tabs-body-holder { min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; }
|
||||
.model-editor-modal .ant-modal-body > .model-editor-panel { flex: 1; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; }
|
||||
.model-editor-panel { display: grid; gap: 16px; padding: 20px 24px; min-width: 0; }
|
||||
.model-editor-panel .ant-form-item { margin-bottom: 0; }
|
||||
.model-editor-connection-fields > .ant-form-item { grid-column: auto; min-width: 0; }
|
||||
.model-editor-subtitle { margin: 4px 0 0; color: var(--muted-foreground); font-size: var(--fs-caption); font-weight: 400; }
|
||||
.model-editor-section { display: grid; gap: 12px; min-width: 0; }
|
||||
.model-editor-section + .model-editor-section { border-top: 1px solid var(--workspace-border); padding-top: 16px; }
|
||||
.model-editor-section h2 { margin: 0; font-size: var(--fs-body); font-weight: 600; }
|
||||
.model-editor-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
||||
.model-editor-footer-actions { display: flex; gap: 8px; margin-left: auto; }
|
||||
@media (max-width: 600px) {
|
||||
.model-editor-modal .ant-modal-container { height: calc(100dvh - 24px); }
|
||||
.model-editor-panel { padding: 16px; }
|
||||
.model-editor-modal .ant-modal-header, .model-editor-modal .ant-modal-footer { padding: 16px; }
|
||||
.model-editor-modal .ant-modal-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: min(880px, calc(100dvh - 48px));
|
||||
}
|
||||
.model-editor-modal .ant-modal-header,
|
||||
.model-editor-modal .ant-modal-footer {
|
||||
padding: 18px 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.model-editor-modal .ant-modal-footer {
|
||||
border-top: 1px solid var(--workspace-border);
|
||||
}
|
||||
.model-editor-modal .ant-modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.model-editor-modal .ant-modal-body > .ant-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
.model-editor-modal .ant-tabs {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.model-editor-modal .ant-tabs-nav {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding-inline: 24px;
|
||||
}
|
||||
.model-editor-modal .ant-tabs-body-holder {
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.model-editor-modal .ant-modal-body > .model-editor-panel {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.model-editor-panel {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
padding: 20px 24px;
|
||||
min-width: 0;
|
||||
}
|
||||
.model-editor-panel .ant-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.model-editor-connection-fields > .ant-form-item {
|
||||
grid-column: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
.model-editor-subtitle {
|
||||
margin: 4px 0 0;
|
||||
color: var(--muted-foreground);
|
||||
font-size: var(--fs-caption);
|
||||
font-weight: 400;
|
||||
}
|
||||
.model-editor-section {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.model-editor-section + .model-editor-section {
|
||||
border-top: 1px solid var(--workspace-border);
|
||||
padding-top: 16px;
|
||||
}
|
||||
.model-editor-section h2 {
|
||||
margin: 0;
|
||||
font-size: var(--fs-body);
|
||||
font-weight: 600;
|
||||
}
|
||||
.model-editor-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.model-editor-footer-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.model-editor-modal .ant-modal-container {
|
||||
height: calc(100dvh - 24px);
|
||||
}
|
||||
.model-editor-panel {
|
||||
padding: 16px;
|
||||
}
|
||||
.model-editor-modal .ant-modal-header,
|
||||
.model-editor-modal .ant-modal-footer {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,7 @@ export function ModelEditorModal({
|
||||
admin?: boolean;
|
||||
children?: ReactNode | ((tabs: ReactNode) => ReactNode);
|
||||
}) {
|
||||
const tabs = items?.length ? (
|
||||
<Tabs
|
||||
activeKey={activeKey}
|
||||
onChange={onTabChange}
|
||||
animated={false}
|
||||
items={items.map((item) => ({ ...item, forceRender: true, children: <div className="model-editor-panel">{item.children}</div> }))}
|
||||
/>
|
||||
) : null;
|
||||
const tabs = items?.length ? <Tabs activeKey={activeKey} onChange={onTabChange} animated={false} items={items.map((item) => ({ ...item, forceRender: true, children: <div className="model-editor-panel">{item.children}</div> }))} /> : null;
|
||||
const content = typeof children === "function" ? children(tabs) : children || tabs;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { ChoiceBrowser } from "./choice-browser";
|
||||
import type { ModelProtocolDefinition, ProtocolCapability } from "@/lib/model-protocols";
|
||||
|
||||
export function ModelProtocolBrowser({ capability, protocols, ...props }: {
|
||||
export function ModelProtocolBrowser({
|
||||
capability,
|
||||
protocols,
|
||||
...props
|
||||
}: {
|
||||
capability?: ProtocolCapability;
|
||||
protocols: ModelProtocolDefinition[];
|
||||
value?: string;
|
||||
|
||||
Reference in New Issue
Block a user