Compare commits
2 Commits
v0.3.3-bet
...
v0.3.3-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2827acfe96 | ||
|
|
afb6e77c7f |
@@ -331,6 +331,11 @@ function getFieldLabel(key, t) {
|
||||
return key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
|
||||
}
|
||||
|
||||
const PROVIDER_MODELS = {
|
||||
minimax: ['MiniMax-M2.7', 'MiniMax-M1', 'abab6.5s-chat', 'abab6.5-chat', 'abab5.5-chat'],
|
||||
zai: ['glm', 'glm-4', 'glm-4-plus', 'glm-4-flash', 'glm-3-turbo'],
|
||||
}
|
||||
|
||||
function PanelProviders({ providers, editProvider, providerForm, setProviderForm, setEditProvider, openProviderEdit, handleSaveProvider, api, loadData, t }) {
|
||||
const [validating, setValidating] = useState(null)
|
||||
const [validationStatus, setValidationStatus] = useState(null)
|
||||
@@ -352,17 +357,29 @@ function PanelProviders({ providers, editProvider, providerForm, setProviderForm
|
||||
setValidating(null)
|
||||
}
|
||||
|
||||
const handleSelectModel = (providerName, model) => {
|
||||
setProviderForm(prev => ({
|
||||
...prev,
|
||||
[providerName]: { ...(prev[providerName] || {}), model },
|
||||
}))
|
||||
setEditProvider(providerName)
|
||||
}
|
||||
|
||||
const displayed = providers.filter(p => p.name === 'minimax' || p.name === 'zai')
|
||||
|
||||
return (
|
||||
<div className="config-providers-list">
|
||||
<div className="provider-setup-hint">{t('config.setupDescription')}</div>
|
||||
{providers.map((p, i) => {
|
||||
{displayed.map((p, i) => {
|
||||
const isEditing = editProvider === p.name
|
||||
const isValidationTarget = validationStatus?.provider === p.name
|
||||
const models = PROVIDER_MODELS[p.name] || []
|
||||
const selectedModel = providerForm[p.name]?.model || p.model
|
||||
|
||||
return (
|
||||
<div key={i} className="config-card provider-card-v2">
|
||||
<div className="provider-card-top">
|
||||
<div className="provider-card-identity">
|
||||
<span className="provider-card-name">{p.name}</span>
|
||||
<span className="provider-card-name">{p.name.toUpperCase()}</span>
|
||||
{p.active && <span className="badge ok" style={{ marginLeft: 6 }}>active</span>}
|
||||
{isValidationTarget && validationStatus?.valid && <span className="badge ok">{t('config.keyValid')}</span>}
|
||||
{isValidationTarget && !validationStatus?.valid && <span className="badge error">{validationStatus?.error}</span>}
|
||||
@@ -376,7 +393,7 @@ function PanelProviders({ providers, editProvider, providerForm, setProviderForm
|
||||
<input
|
||||
className="config-form-input"
|
||||
type="password"
|
||||
placeholder={t('config.tokenPlaceholder')}
|
||||
placeholder={p.apiKey ? '••••••••' : t('config.tokenPlaceholder')}
|
||||
value={isEditing ? (providerForm[p.name]?.api_key || '') : ''}
|
||||
onChange={e => {
|
||||
if (!isEditing) openProviderEdit(p)
|
||||
@@ -391,19 +408,31 @@ function PanelProviders({ providers, editProvider, providerForm, setProviderForm
|
||||
<button
|
||||
className="sm primary"
|
||||
disabled={validating === p.name || !providerForm[p.name]?.api_key}
|
||||
onClick={() => handleValidate(p.name, providerForm[p.name]?.api_key, providerForm[p.name]?.model, providerForm[p.name]?.base_url)}
|
||||
onClick={() => handleValidate(p.name, providerForm[p.name]?.api_key, selectedModel, providerForm[p.name]?.base_url)}
|
||||
>
|
||||
{validating === p.name ? t('config.validating') : t('config.validateKey')}
|
||||
</button>
|
||||
{isValidationTarget && validationStatus?.valid && (
|
||||
{isEditing && (
|
||||
<button className="sm" onClick={() => handleSaveProvider(p.name)}>{t('config.save')}</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="provider-card-meta" style={{ marginTop: 8 }}>
|
||||
{p.active && <span className="badge ok" style={{ marginRight: 6 }}>active</span>}
|
||||
{p.model && p.model !== p.name && <span className="mono">{p.model}</span>}
|
||||
</div>
|
||||
{models.length > 0 && (
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<span className="config-form-label">Modèle</span>
|
||||
<div className="chip-row" style={{ marginTop: 4 }}>
|
||||
{models.map(m => (
|
||||
<div
|
||||
key={m}
|
||||
className={`chip ${selectedModel === m ? 'active' : ''}`}
|
||||
onClick={() => handleSelectModel(p.name, m)}
|
||||
>
|
||||
{m}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -92,6 +92,21 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
const minimax = (quota || []).find(p => p.name === 'minimax')
|
||||
const zai = (quota || []).find(p => p.name === 'zai')
|
||||
|
||||
const EXCLUDE_CMDS = ['ls', 'cd', 'pwd', 'clear', 'exit', 'history']
|
||||
|
||||
const topCmds = (() => {
|
||||
const counts = {}
|
||||
for (const c of recentCmds) {
|
||||
const base = c.cmd.split(/\s+/)[0]
|
||||
if (EXCLUDE_CMDS.includes(base) || !base) continue
|
||||
counts[base] = (counts[base] || 0) + 1
|
||||
}
|
||||
return Object.entries(counts)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 5)
|
||||
.map(([cmd, count]) => ({ cmd, count }))
|
||||
})()
|
||||
|
||||
return (
|
||||
<div className="dash-grid">
|
||||
{/* CPU */}
|
||||
@@ -175,6 +190,16 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">Recent Commands</span>
|
||||
</div>
|
||||
{topCmds.length > 0 && (
|
||||
<div className="dash-cmd-top">
|
||||
{topCmds.map((c, i) => (
|
||||
<div key={i} className="dash-cmd-chip" onClick={() => navigator.clipboard.writeText(c.cmd)} title="Copier">
|
||||
<span className="dash-cmd-chip-name">{c.cmd}</span>
|
||||
<span className="dash-cmd-chip-count">{c.count}×</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="dash-cmd-list">
|
||||
{recentCmds.length === 0 && <span className="dash-empty">No history</span>}
|
||||
{recentCmds.map((c, i) => (
|
||||
|
||||
@@ -686,6 +686,17 @@ input::placeholder { color: var(--text-disabled); }
|
||||
flex: 1; min-width: 0;
|
||||
}
|
||||
|
||||
.dash-cmd-top { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
|
||||
.dash-cmd-chip {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 6px 12px; border-radius: var(--radius);
|
||||
background: var(--bg-surface); border: 1px solid var(--border);
|
||||
cursor: pointer; transition: all 0.15s;
|
||||
}
|
||||
.dash-cmd-chip:hover { border-color: var(--accent-dim); background: var(--accent-bg); }
|
||||
.dash-cmd-chip-name { font-size: 13px; font-weight: 700; font-family: var(--font-mono); color: var(--text-primary); }
|
||||
.dash-cmd-chip-count { font-size: 10px; font-family: var(--font-mono); color: var(--accent); }
|
||||
|
||||
/* Services */
|
||||
.dash-services { display: flex; flex-direction: column; gap: 6px; }
|
||||
.dash-svc-row {
|
||||
|
||||
Reference in New Issue
Block a user