Spaces:
Running
Running
github-actions[bot] commited on
Commit ·
58e5ff2
1
Parent(s): a3cd373
deploy: 8e9f55e — 更新 ModelCard.jsx
Browse files
frontend/src/components/ModelCard.jsx
CHANGED
|
@@ -42,7 +42,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 42 |
<div className={`card flex flex-col animate-slide-in transition-all duration-200 ${
|
| 43 |
model.enabled ? "" : "opacity-50"
|
| 44 |
}`}>
|
| 45 |
-
{/* ── Card header ── */}
|
| 46 |
<div className="p-4 flex items-start justify-between gap-3">
|
| 47 |
<div className="min-w-0 flex-1">
|
| 48 |
<div className="flex items-center gap-2 flex-wrap">
|
|
@@ -82,7 +81,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 82 |
|
| 83 |
<hr className="divider" />
|
| 84 |
|
| 85 |
-
{/* ── Endpoint info ── */}
|
| 86 |
<div className="p-4 space-y-2">
|
| 87 |
<InfoRow
|
| 88 |
icon={Globe}
|
|
@@ -112,12 +110,11 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 112 |
<InfoRow
|
| 113 |
icon={Key}
|
| 114 |
label="API Key"
|
| 115 |
-
value={model.apiKey ? "Configured
|
| 116 |
className={model.apiKey ? "text-accent-green" : "text-text-muted"}
|
| 117 |
/>
|
| 118 |
</div>
|
| 119 |
|
| 120 |
-
{/* ── Expandable section ── */}
|
| 121 |
<div className="border-t border-white/5">
|
| 122 |
<button
|
| 123 |
onClick={() => setExpanded(e => !e)}
|
|
@@ -130,7 +127,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 130 |
|
| 131 |
{expanded && (
|
| 132 |
<div className="px-4 pb-4 space-y-3 animate-slide-in">
|
| 133 |
-
{/* Curl example */}
|
| 134 |
<div>
|
| 135 |
<div className="flex items-center justify-between mb-1.5">
|
| 136 |
<span className="text-xs text-text-muted font-mono">curl</span>
|
|
@@ -146,7 +142,6 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 146 |
</div>
|
| 147 |
</div>
|
| 148 |
|
| 149 |
-
{/* Test button */}
|
| 150 |
<div>
|
| 151 |
<button
|
| 152 |
onClick={runTest}
|
|
@@ -154,7 +149,7 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 154 |
className="btn-secondary w-full justify-center text-xs"
|
| 155 |
>
|
| 156 |
{testing ? (
|
| 157 |
-
<><RefreshCwIcon className="animate-spin" size={12} /> Testing
|
| 158 |
) : "Run connectivity test"}
|
| 159 |
</button>
|
| 160 |
|
|
@@ -165,8 +160,8 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 165 |
: "bg-accent-red/5 border-accent-red/20 text-accent-red"
|
| 166 |
}`}>
|
| 167 |
{testResult.success
|
| 168 |
-
?
|
| 169 |
-
:
|
| 170 |
}
|
| 171 |
</div>
|
| 172 |
)}
|
|
@@ -178,6 +173,29 @@ export default function ModelCard({ model, onEdit, onDelete, onToggle }) {
|
|
| 178 |
);
|
| 179 |
}
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
function InfoRow({ icon: Icon, label, value, onCopy, copied, mono, accent, className }) {
|
| 182 |
return (
|
| 183 |
<div className="flex items-center gap-2 group">
|
|
@@ -203,7 +221,6 @@ function InfoRow({ icon: Icon, label, value, onCopy, copied, mono, accent, class
|
|
| 203 |
);
|
| 204 |
}
|
| 205 |
|
| 206 |
-
// Inline icon to avoid import issues
|
| 207 |
function RefreshCwIcon({ size, className }) {
|
| 208 |
return (
|
| 209 |
<svg width={size} height={size} viewBox="0 0 24 24" fill="none"
|
|
@@ -214,4 +231,4 @@ function RefreshCwIcon({ size, className }) {
|
|
| 214 |
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
| 215 |
</svg>
|
| 216 |
);
|
| 217 |
-
}
|
|
|
|
| 42 |
<div className={`card flex flex-col animate-slide-in transition-all duration-200 ${
|
| 43 |
model.enabled ? "" : "opacity-50"
|
| 44 |
}`}>
|
|
|
|
| 45 |
<div className="p-4 flex items-start justify-between gap-3">
|
| 46 |
<div className="min-w-0 flex-1">
|
| 47 |
<div className="flex items-center gap-2 flex-wrap">
|
|
|
|
| 81 |
|
| 82 |
<hr className="divider" />
|
| 83 |
|
|
|
|
| 84 |
<div className="p-4 space-y-2">
|
| 85 |
<InfoRow
|
| 86 |
icon={Globe}
|
|
|
|
| 110 |
<InfoRow
|
| 111 |
icon={Key}
|
| 112 |
label="API Key"
|
| 113 |
+
value={model.apiKey ? "Configured v" : "Not required / None"}
|
| 114 |
className={model.apiKey ? "text-accent-green" : "text-text-muted"}
|
| 115 |
/>
|
| 116 |
</div>
|
| 117 |
|
|
|
|
| 118 |
<div className="border-t border-white/5">
|
| 119 |
<button
|
| 120 |
onClick={() => setExpanded(e => !e)}
|
|
|
|
| 127 |
|
| 128 |
{expanded && (
|
| 129 |
<div className="px-4 pb-4 space-y-3 animate-slide-in">
|
|
|
|
| 130 |
<div>
|
| 131 |
<div className="flex items-center justify-between mb-1.5">
|
| 132 |
<span className="text-xs text-text-muted font-mono">curl</span>
|
|
|
|
| 142 |
</div>
|
| 143 |
</div>
|
| 144 |
|
|
|
|
| 145 |
<div>
|
| 146 |
<button
|
| 147 |
onClick={runTest}
|
|
|
|
| 149 |
className="btn-secondary w-full justify-center text-xs"
|
| 150 |
>
|
| 151 |
{testing ? (
|
| 152 |
+
<><RefreshCwIcon className="animate-spin" size={12} /> Testing...</>
|
| 153 |
) : "Run connectivity test"}
|
| 154 |
</button>
|
| 155 |
|
|
|
|
| 160 |
: "bg-accent-red/5 border-accent-red/20 text-accent-red"
|
| 161 |
}`}>
|
| 162 |
{testResult.success
|
| 163 |
+
? "OK -- " + testResult.latencyMs + "ms"
|
| 164 |
+
: <ErrorDisplay error={testResult.error} />
|
| 165 |
}
|
| 166 |
</div>
|
| 167 |
)}
|
|
|
|
| 173 |
);
|
| 174 |
}
|
| 175 |
|
| 176 |
+
function ErrorDisplay({ error }) {
|
| 177 |
+
const [showFull, setShowFull] = useState(false);
|
| 178 |
+
const text = typeof error === "string" ? error : JSON.stringify(error, null, 2);
|
| 179 |
+
const LIMIT = 200;
|
| 180 |
+
const isLong = text.length > LIMIT;
|
| 181 |
+
|
| 182 |
+
return (
|
| 183 |
+
<div>
|
| 184 |
+
<span className="whitespace-pre-wrap break-all">
|
| 185 |
+
{showFull ? text : text.slice(0, LIMIT)}
|
| 186 |
+
</span>
|
| 187 |
+
{isLong && (
|
| 188 |
+
<button
|
| 189 |
+
onClick={() => setShowFull(v => !v)}
|
| 190 |
+
className="ml-1 underline text-accent-red/70 hover:text-accent-red"
|
| 191 |
+
>
|
| 192 |
+
{showFull ? "show less" : "...show full error"}
|
| 193 |
+
</button>
|
| 194 |
+
)}
|
| 195 |
+
</div>
|
| 196 |
+
);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
function InfoRow({ icon: Icon, label, value, onCopy, copied, mono, accent, className }) {
|
| 200 |
return (
|
| 201 |
<div className="flex items-center gap-2 group">
|
|
|
|
| 221 |
);
|
| 222 |
}
|
| 223 |
|
|
|
|
| 224 |
function RefreshCwIcon({ size, className }) {
|
| 225 |
return (
|
| 226 |
<svg width={size} height={size} viewBox="0 0 24 24" fill="none"
|
|
|
|
| 231 |
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
| 232 |
</svg>
|
| 233 |
);
|
| 234 |
+
}
|