refactor: unify speed test panel UI with project design system

UI improvements:
- Update modal border radius from rounded-lg to rounded-xl
- Unify header padding from px-6 py-4 to p-6
- Change speed test button color to blue theme (bg-blue-500) for consistency
- Update footer background from bg-gray-50 to bg-gray-100
- Style "Done" button as primary action button with blue theme
- Adjust footer button spacing and hover states

Simplify endpoint display:
- Remove endpoint labels (e.g., "Current Address", "Custom 1")
- Display only URL for cleaner interface
- Clean up all label-related logic:
  * Remove label field from EndpointCandidate interface
  * Remove label generation in buildInitialEntries function
  * Remove label handling in useEffect merge logic
  * Remove label generation in handleAddEndpoint
  * Remove label parameters from claudeSpeedTestEndpoints
  * Remove label parameters from codexSpeedTestEndpoints
This commit is contained in:
Jason
2025-10-06 21:14:54 +08:00
parent 1c9a9af11c
commit b4b176580e
2 changed files with 23 additions and 70 deletions

View File

@@ -1123,21 +1123,21 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
const claudeSpeedTestEndpoints = useMemo<EndpointCandidate[]>(() => {
if (isCodex) return [];
const map = new Map<string, EndpointCandidate>();
const add = (url?: string, label?: string) => {
const add = (url?: string) => {
if (!url) return;
const sanitized = url.trim().replace(/\/+$/, "");
if (!sanitized || map.has(sanitized)) return;
map.set(sanitized, { url: sanitized, label });
map.set(sanitized, { url: sanitized });
};
if (baseUrl) {
add(baseUrl, "当前地址");
add(baseUrl);
}
if (initialData && typeof initialData.settingsConfig === "object") {
const envUrl = (initialData.settingsConfig as any)?.env?.ANTHROPIC_BASE_URL;
if (typeof envUrl === "string") {
add(envUrl, envUrl === baseUrl ? "当前地址" : "历史地址");
add(envUrl);
}
}
@@ -1149,7 +1149,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
const preset = providerPresets[selectedPreset];
const presetEnv = (preset.settingsConfig as any)?.env?.ANTHROPIC_BASE_URL;
if (typeof presetEnv === "string") {
add(presetEnv, presetEnv === baseUrl ? "当前地址" : "预设地址");
add(presetEnv);
}
}
@@ -1159,15 +1159,15 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
const codexSpeedTestEndpoints = useMemo<EndpointCandidate[]>(() => {
if (!isCodex) return [];
const map = new Map<string, EndpointCandidate>();
const add = (url?: string, label?: string) => {
const add = (url?: string) => {
if (!url) return;
const sanitized = url.trim().replace(/\/+$/, "");
if (!sanitized || map.has(sanitized)) return;
map.set(sanitized, { url: sanitized, label });
map.set(sanitized, { url: sanitized });
};
if (codexBaseUrl) {
add(codexBaseUrl, "当前地址");
add(codexBaseUrl);
}
const initialCodexConfig =
@@ -1176,7 +1176,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
: "";
const existing = extractCodexBaseUrl(initialCodexConfig);
if (existing) {
add(existing, existing === codexBaseUrl ? "当前地址" : "历史地址");
add(existing);
}
if (
@@ -1187,7 +1187,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
const presetConfig = codexProviderPresets[selectedCodexPreset]?.config;
const presetBase = extractCodexBaseUrl(presetConfig);
if (presetBase) {
add(presetBase, presetBase === codexBaseUrl ? "当前地址" : "预设地址");
add(presetBase);
}
}

View File

@@ -7,7 +7,6 @@ import type { AppType } from "../../lib/tauri-api";
export interface EndpointCandidate {
id?: string;
url: string;
label?: string;
isCustom?: boolean;
}
@@ -40,25 +39,11 @@ const buildInitialEntries = (
const addCandidate = (candidate: EndpointCandidate) => {
const sanitized = candidate.url ? normalizeEndpointUrl(candidate.url) : "";
if (!sanitized) return;
if (map.has(sanitized)) {
const existing = map.get(sanitized)!;
if (candidate.label && candidate.label !== existing.label) {
map.set(sanitized, { ...existing, label: candidate.label });
}
return;
}
const index = map.size;
const label =
candidate.label ??
(candidate.isCustom
? `自定义 ${index + 1}`
: index === 0
? "默认地址"
: `候选 ${index + 1}`);
if (map.has(sanitized)) return;
map.set(sanitized, {
id: candidate.id ?? randomId(),
url: sanitized,
label,
isCustom: candidate.isCustom ?? false,
latency: null,
status: undefined,
@@ -70,7 +55,7 @@ const buildInitialEntries = (
const selectedUrl = normalizeEndpointUrl(selected);
if (selectedUrl && !map.has(selectedUrl)) {
addCandidate({ url: selectedUrl, label: "当前地址", isCustom: true });
addCandidate({ url: selectedUrl, isCustom: true });
}
return Array.from(map.values());
@@ -112,25 +97,11 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
: "";
if (!sanitized) return;
const existing = map.get(sanitized);
if (existing) {
if (candidate.label && candidate.label !== existing.label) {
map.set(sanitized, { ...existing, label: candidate.label });
changed = true;
}
return;
}
const index = map.size;
const label =
candidate.label ??
(candidate.isCustom
? `自定义 ${index + 1}`
: index === 0
? "默认地址"
: `候选 ${index + 1}`);
if (existing) return;
map.set(sanitized, {
id: candidate.id ?? randomId(),
url: sanitized,
label,
isCustom: candidate.isCustom ?? false,
latency: null,
status: undefined,
@@ -141,19 +112,8 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
initialEndpoints.forEach(mergeCandidate);
if (normalizedSelected) {
const existing = map.get(normalizedSelected);
if (existing) {
if (existing.label !== "当前地址") {
map.set(normalizedSelected, {
...existing,
label: existing.isCustom ? existing.label : "当前地址",
});
changed = true;
}
} else {
mergeCandidate({ url: normalizedSelected, label: "当前地址", isCustom: true });
}
if (normalizedSelected && !map.has(normalizedSelected)) {
mergeCandidate({ url: normalizedSelected, isCustom: true });
}
if (!changed) {
@@ -204,13 +164,11 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
setAddError("该地址已存在");
return prev;
}
const customCount = prev.filter((entry) => entry.isCustom).length;
return [
...prev,
{
id: randomId(),
url: sanitized,
label: `自定义 ${customCount + 1}`,
isCustom: true,
latency: null,
status: undefined,
@@ -341,9 +299,9 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
/>
{/* Modal */}
<div className="relative bg-white dark:bg-gray-900 rounded-lg shadow-lg w-full max-w-2xl mx-4 max-h-[80vh] overflow-hidden flex flex-col">
<div className="relative bg-white dark:bg-gray-900 rounded-xl shadow-lg w-full max-w-2xl mx-4 max-h-[80vh] overflow-hidden flex flex-col">
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-800">
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-800">
<h3 className="text-base font-medium text-gray-900 dark:text-gray-100">
</h3>
@@ -378,7 +336,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
type="button"
onClick={runSpeedTest}
disabled={isTesting || !hasEndpoints}
className="flex h-7 items-center gap-1.5 rounded-md bg-gray-900 px-2.5 text-xs font-medium text-white transition hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-40 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200"
className="flex h-7 items-center gap-1.5 rounded-md bg-blue-500 px-2.5 text-xs font-medium text-white transition hover:bg-blue-600 disabled:cursor-not-allowed disabled:opacity-40 dark:bg-blue-600 dark:hover:bg-blue-700"
>
{isTesting ? (
<>
@@ -456,12 +414,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
{/* 内容 */}
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
{entry.label}
</span>
</div>
<div className="mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400">
<div className="truncate text-sm text-gray-900 dark:text-gray-100">
{entry.url}
</div>
</div>
@@ -516,11 +469,11 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
</div>
{/* Footer */}
<div className="flex items-center justify-end px-6 py-4 border-t border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-800">
<div className="flex items-center justify-end gap-3 p-6 border-t border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-800">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm font-medium text-gray-900 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors"
className="px-4 py-2 bg-blue-500 dark:bg-blue-600 text-white rounded-lg hover:bg-blue-600 dark:hover:bg-blue-700 transition-colors text-sm font-medium"
>
</button>