Compare commits
17 Commits
v0.3.3-bet
...
v0.3.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce0337e5fb | ||
|
|
328e9e6457 | ||
|
|
c81ebb4e46 | ||
|
|
b0865bc598 | ||
|
|
0d8e1b1e1a | ||
|
|
485e085bb0 | ||
|
|
61da8039bc | ||
|
|
65df15498b | ||
|
|
b6147ddb12 | ||
|
|
275a9a4cc7 | ||
|
|
e92a2f00f5 | ||
|
|
1f12b8a4fb | ||
|
|
9188231a05 | ||
|
|
28e5113733 | ||
|
|
51a599fc83 | ||
|
|
d8384cad00 | ||
|
|
5b4a70e690 |
172
CHANGELOG.md
172
CHANGELOG.md
@@ -4,6 +4,178 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
## v0.3.3
|
||||
|
||||
### Changes since v0.3.2
|
||||
|
||||
- feat(dashboard): single-view grid with live CPU/RAM/Net graphs, API quota, processes, and sudo indicator (328e9e6)
|
||||
- feat(dashboard): add quota monitoring, process list, and command history (c81ebb4)
|
||||
- refactor(chat): deduplicate streaming code, add multi-conv, and XSS protection (b0865bc)
|
||||
- fix(studio): improve chat context, thinking tags, streaming, and tool results (0d8e1b1)
|
||||
- feat: add Cobra CLI, LSP/MCP registries, workflow engine, and enriched dashboard (485e085)
|
||||
- feat(agent): refactor AI chat with streaming, agent registry, and tool execution (61da803)
|
||||
- feat(onboarding): add minimax api key step and AI-powered editor scan (65df154)
|
||||
- fix(onboarding): require fields before advancing steps (b6147dd)
|
||||
- fix: register missing /api/config/reset and /api/starship/apply-theme routes (275a9a4)
|
||||
- fix(config): per-provider form state to avoid field cross-talk (e92a2f0)
|
||||
- fix(onboarding): auto-save on done step, keyboard nav, error feedback (1f12b8a)
|
||||
- feat(config): add system panel with reset and starship theme, add onboarding wizard (9188231)
|
||||
|
||||
### Downloads
|
||||
|
||||
| Platform | File |
|
||||
|----------|------|
|
||||
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-linux-amd64.tar.gz) |
|
||||
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-linux-arm64.tar.gz) |
|
||||
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-darwin-amd64.tar.gz) |
|
||||
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-darwin-arm64.tar.gz) |
|
||||
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-windows-amd64.zip) |
|
||||
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-windows-arm64.zip) |
|
||||
|
||||
The binary includes both CLI and Desktop modes.
|
||||
Run `muyue` for TUI, `muyue desktop` for web UI.
|
||||
|
||||
### Install
|
||||
|
||||
**Linux (x86_64)**
|
||||
```bash
|
||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-linux-amd64.tar.gz | tar xz
|
||||
chmod +x muyue-linux-amd64
|
||||
sudo mv muyue-linux-amd64 /usr/local/bin/muyue
|
||||
```
|
||||
|
||||
**macOS (Apple Silicon)**
|
||||
```bash
|
||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-darwin-arm64.tar.gz | tar xz
|
||||
chmod +x muyue-darwin-arm64
|
||||
sudo mv muyue-darwin-arm64 /usr/local/bin/muyue
|
||||
```
|
||||
|
||||
**Windows (x86_64)**
|
||||
```powershell
|
||||
Invoke-WebRequest -Uri "https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.3/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
||||
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
||||
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
||||
```
|
||||
|
||||
|
||||
## v0.3.2
|
||||
|
||||
### Changes since v0.3.1
|
||||
|
||||
- chore: update CHANGELOG for v0.3.2-beta.1 (51a599f)
|
||||
- fix: correct version from 3.2 to 0.3.2 (83d7a57)
|
||||
- chore: bump version to 3.2 (0fe82f6)
|
||||
- refactor(config): remove Terminal sub-tab from Configuration page (3b6cc38)
|
||||
- fix(terminal): init payload never sent due to ws.onopen being overwritten (93a22d4)
|
||||
- fix(terminal): improve shell resolution with better error handling and ws proxy support (e0e1e73)
|
||||
- feat(studio): parse AI thinking and tool launch messages in terminal panel (0496ca7)
|
||||
- fix(studio): forward AI thinking chunks to frontend instead of dropping them (b407ab8)
|
||||
- feat(studio): add tool execution and hide AI thinking tags (12df184)
|
||||
- fix(terminal): ignore invalid shell config from race condition (8af6d25)
|
||||
- feat(shell): restore AI assistant panel (4fd599a)
|
||||
- fix(terminal): restore terminal input and cursor visibility (bcba593)
|
||||
- refactor(api): split monolithic handlers.go into focused modules (04b0fff)
|
||||
|
||||
### Downloads
|
||||
|
||||
| Platform | File |
|
||||
|----------|------|
|
||||
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-linux-amd64.tar.gz) |
|
||||
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-linux-arm64.tar.gz) |
|
||||
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-darwin-amd64.tar.gz) |
|
||||
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-darwin-arm64.tar.gz) |
|
||||
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-windows-amd64.zip) |
|
||||
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-windows-arm64.zip) |
|
||||
|
||||
The binary includes both CLI and Desktop modes.
|
||||
Run `muyue` for TUI, `muyue desktop` for web UI.
|
||||
|
||||
### Install
|
||||
|
||||
**Linux (x86_64)**
|
||||
```bash
|
||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-linux-amd64.tar.gz | tar xz
|
||||
chmod +x muyue-linux-amd64
|
||||
sudo mv muyue-linux-amd64 /usr/local/bin/muyue
|
||||
```
|
||||
|
||||
**macOS (Apple Silicon)**
|
||||
```bash
|
||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-darwin-arm64.tar.gz | tar xz
|
||||
chmod +x muyue-darwin-arm64
|
||||
sudo mv muyue-darwin-arm64 /usr/local/bin/muyue
|
||||
```
|
||||
|
||||
**Windows (x86_64)**
|
||||
```powershell
|
||||
Invoke-WebRequest -Uri "https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
||||
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
||||
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
||||
```
|
||||
|
||||
|
||||
## v0.3.2-beta.1 (Beta)
|
||||
|
||||
### Commits since v0.3.1
|
||||
|
||||
- fix: correct version from 3.2 to 0.3.2 (83d7a57)
|
||||
|
||||
> This is a **beta** release. Use at your own risk.
|
||||
|
||||
## v0.3.1
|
||||
|
||||
### Changes since v0.3.0
|
||||
|
||||
- refactor(config): remove Terminal sub-tab from Configuration page (95bd824)
|
||||
- fix(terminal): init payload never sent due to ws.onopen being overwritten (252f178)
|
||||
- fix(terminal): improve shell resolution with better error handling and ws proxy support (7dcf505)
|
||||
- feat(studio): parse AI thinking and tool launch messages in terminal panel (8fb93fa)
|
||||
- fix(studio): forward AI thinking chunks to frontend instead of dropping them (5ec373c)
|
||||
- feat(studio): add tool execution and hide AI thinking tags (1eb5a6d)
|
||||
- fix(terminal): ignore invalid shell config from race condition (cd5ebe0)
|
||||
- feat(shell): restore AI assistant panel (2004c15)
|
||||
- fix(terminal): restore terminal input and cursor visibility (9306152)
|
||||
- refactor(api): split monolithic handlers.go into focused modules (e15a034)
|
||||
|
||||
### Downloads
|
||||
|
||||
| Platform | File |
|
||||
|----------|------|
|
||||
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-linux-amd64.tar.gz) |
|
||||
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-linux-arm64.tar.gz) |
|
||||
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-darwin-amd64.tar.gz) |
|
||||
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-darwin-arm64.tar.gz) |
|
||||
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-windows-amd64.zip) |
|
||||
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-windows-arm64.zip) |
|
||||
|
||||
The binary includes both CLI and Desktop modes.
|
||||
Run `muyue` for TUI, `muyue desktop` for web UI.
|
||||
|
||||
### Install
|
||||
|
||||
**Linux (x86_64)**
|
||||
```bash
|
||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-linux-amd64.tar.gz | tar xz
|
||||
chmod +x muyue-linux-amd64
|
||||
sudo mv muyue-linux-amd64 /usr/local/bin/muyue
|
||||
```
|
||||
|
||||
**macOS (Apple Silicon)**
|
||||
```bash
|
||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-darwin-arm64.tar.gz | tar xz
|
||||
chmod +x muyue-darwin-arm64
|
||||
sudo mv muyue-darwin-arm64 /usr/local/bin/muyue
|
||||
```
|
||||
|
||||
**Windows (x86_64)**
|
||||
```powershell
|
||||
Invoke-WebRequest -Uri "https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
||||
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
||||
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
||||
```
|
||||
|
||||
|
||||
## v0.3.0
|
||||
|
||||
### Changes since v0.2.1
|
||||
|
||||
@@ -3,32 +3,6 @@ import { useI18n } from '../i18n'
|
||||
|
||||
const MAX_POINTS = 30
|
||||
|
||||
function BgGraph({ data, max, color }) {
|
||||
if (!data || data.length < 2) return null
|
||||
const m = max || Math.max(...data, 1)
|
||||
const w = 120
|
||||
const h = 60
|
||||
const points = data.map((v, i) => {
|
||||
const x = (i / (data.length - 1)) * w
|
||||
const y = h - (v / m) * h
|
||||
return `${x},${y}`
|
||||
})
|
||||
const area = `${points.join(' ')} ${w},${h} 0,${h}`
|
||||
const line = points.join(' ')
|
||||
return (
|
||||
<svg viewBox={`0 0 ${w} ${h}`} className="dash-bg-graph" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<linearGradient id={`g-${color.replace('#','')}`} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity="0.25" />
|
||||
<stop offset="100%" stopColor={color} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<polygon fill={`url(#g-${color.replace('#','')})`} points={area} />
|
||||
<polyline fill="none" stroke={color} strokeWidth="1.5" points={line} vectorEffect="non-scaling-stroke" opacity="0.6" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function MiniGraph({ data, max, color, label, unit }) {
|
||||
if (!data || data.length < 2) return <div className="dash-graph-empty">collecting...</div>
|
||||
const m = max || Math.max(...data, 1)
|
||||
@@ -47,13 +21,6 @@ function MiniGraph({ data, max, color, label, unit }) {
|
||||
<span className="dash-graph-value" style={{ color }}>{last.toFixed(1)}{unit}</span>
|
||||
</div>
|
||||
<svg viewBox={`0 0 ${w} ${h}`} className="dash-graph-svg" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<linearGradient id={`fg-${color.replace('#','').replace('var(','').replace(')','')}`} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity="0.3" />
|
||||
<stop offset="100%" stopColor={color} stopOpacity="0.02" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<polygon fill={`url(#fg-${color.replace('#','').replace('var(','').replace(')','')})`} points={`${points} ${w},${h} 0,${h}`} />
|
||||
<polyline fill="none" stroke={color} strokeWidth="1.5" points={points} vectorEffect="non-scaling-stroke" />
|
||||
</svg>
|
||||
</div>
|
||||
@@ -62,6 +29,7 @@ function MiniGraph({ data, max, color, label, unit }) {
|
||||
|
||||
export default function Dashboard({ api, refreshRef }) {
|
||||
const { t } = useI18n()
|
||||
const [dashboardStatus, setDashboardStatus] = useState(null)
|
||||
const [quota, setQuota] = useState(null)
|
||||
const [recentCmds, setRecentCmds] = useState([])
|
||||
const [processes, setProcesses] = useState([])
|
||||
@@ -70,16 +38,17 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
const memRef = useRef([])
|
||||
const netRxRef = useRef([])
|
||||
const netTxRef = useRef([])
|
||||
const procCountRef = useRef([])
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
const [quotaData, cmdData, procData, metricsData] = await Promise.all([
|
||||
const [dashData, quotaData, cmdData, procData, metricsData] = await Promise.all([
|
||||
api.getDashboardStatus().catch(() => null),
|
||||
api.getProvidersQuota().catch(() => null),
|
||||
api.getRecentCommands().catch(() => ({ commands: [] })),
|
||||
api.getRunningProcesses().catch(() => ({ processes: [] })),
|
||||
api.getSystemMetrics().catch(() => null),
|
||||
])
|
||||
setDashboardStatus(dashData)
|
||||
setQuota(quotaData?.providers || [])
|
||||
setRecentCmds(cmdData.commands || [])
|
||||
setProcesses(procData.processes || [])
|
||||
@@ -90,7 +59,6 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
netRxRef.current = [...netRxRef.current, metricsData.net_rx_kbs].slice(-MAX_POINTS)
|
||||
netTxRef.current = [...netTxRef.current, metricsData.net_tx_kbs].slice(-MAX_POINTS)
|
||||
}
|
||||
procCountRef.current = [...procCountRef.current, procData.processes?.length || 0].slice(-MAX_POINTS)
|
||||
} catch (err) {
|
||||
console.error('Dashboard load error:', err)
|
||||
}
|
||||
@@ -105,52 +73,37 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
|
||||
const minimax = (quota || []).find(p => p.name === 'minimax')
|
||||
const zai = (quota || []).find(p => p.name === 'zai')
|
||||
const totalQuotaUsed = minimax?.data?.models?.reduce((s, m) => s + (m.used || 0), 0) || 0
|
||||
const totalQuotaMax = minimax?.data?.models?.reduce((s, m) => s + (m.total || 0), 0) || 1
|
||||
|
||||
return (
|
||||
<div className="dash-grid">
|
||||
{/* CPU */}
|
||||
<div className="dash-card dash-card-graph">
|
||||
<BgGraph data={cpuRef.current} max={100} color="#06b6d4" />
|
||||
<div className="dash-card-content">
|
||||
{/* CPU / RAM / Network Graphs */}
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">CPU</span>
|
||||
<span className="dash-count">{metrics ? metrics.cpu_percent.toFixed(0) : '—'}%</span>
|
||||
</div>
|
||||
<MiniGraph data={cpuRef.current} max={100} color="var(--accent)" label="CPU" unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* RAM */}
|
||||
<div className="dash-card dash-card-graph">
|
||||
<BgGraph data={memRef.current} max={100} color="#a78bfa" />
|
||||
<div className="dash-card-content">
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">RAM</span>
|
||||
<span className="dash-count">{metrics ? `${metrics.mem_used_mb.toFixed(0)}/${metrics.mem_total_mb.toFixed(0)}` : '—'}</span>
|
||||
<span className="dash-count">{metrics ? `${metrics.mem_used_mb.toFixed(0)}/${metrics.mem_total_mb.toFixed(0)} MB` : '—'}</span>
|
||||
</div>
|
||||
<MiniGraph data={memRef.current} max={100} color="#a78bfa" label="RAM" unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Network */}
|
||||
<div className="dash-card dash-card-graph">
|
||||
<BgGraph data={netRxRef.current} max={null} color="#34d399" />
|
||||
<div className="dash-card-content">
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">Network</span>
|
||||
<span className="dash-count">{metrics ? `↓${metrics.net_rx_kbs.toFixed(0)} ↑${metrics.net_tx_kbs.toFixed(0)}` : '—'}</span>
|
||||
<span className="dash-count">{metrics ? `↓${metrics.net_rx_kbs.toFixed(0)} ↑${metrics.net_tx_kbs.toFixed(0)} KB/s` : '—'}</span>
|
||||
</div>
|
||||
<MiniGraph data={netRxRef.current} max={null} color="#34d399" label="RX" unit=" KB/s" />
|
||||
<MiniGraph data={netTxRef.current} max={null} color="#f59e0b" label="TX" unit=" KB/s" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* API Quota */}
|
||||
<div className="dash-card dash-card-graph">
|
||||
<BgGraph data={totalQuotaMax > 0 ? [totalQuotaUsed / totalQuotaMax * 100, ...(cpuRef.current.length > 0 ? [] : [0])] : []} max={100} color="#f472b6" />
|
||||
<div className="dash-card-content">
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">API Quota</span>
|
||||
</div>
|
||||
@@ -179,19 +132,16 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
{!minimax && !zai && <span className="dash-quota-val" style={{ color: 'var(--text-tertiary)' }}>No providers</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Running Processes */}
|
||||
<div className="dash-card dash-card-graph">
|
||||
<BgGraph data={procCountRef.current} max={null} color="#fb923c" />
|
||||
<div className="dash-card-content">
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">Processes</span>
|
||||
<span className="dash-label">Running Processes</span>
|
||||
<span className="dash-count">{processes.length}</span>
|
||||
</div>
|
||||
<div className="dash-proc-list">
|
||||
{processes.length === 0 && <span className="dash-empty">No relevant processes</span>}
|
||||
{processes.slice(0, 6).map((p, i) => (
|
||||
{processes.slice(0, 8).map((p, i) => (
|
||||
<div key={i} className="dash-proc-row">
|
||||
<span className="dash-proc-name">{p.name}</span>
|
||||
<span className="dash-proc-res">cpu {p.cpu}% · mem {p.mem}%</span>
|
||||
@@ -199,7 +149,6 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Commands */}
|
||||
<div className="dash-card">
|
||||
@@ -216,6 +165,38 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div className="dash-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">Services</span>
|
||||
</div>
|
||||
{dashboardStatus ? (
|
||||
<div className="dash-services">
|
||||
<div className="dash-svc-row">
|
||||
<span className="dash-svc-name">MCP</span>
|
||||
<span className="dash-svc-val">{dashboardStatus.mcp?.healthy || 0}/{dashboardStatus.mcp?.total || 0} healthy</span>
|
||||
</div>
|
||||
<div className="dash-svc-row">
|
||||
<span className="dash-svc-name">LSP</span>
|
||||
<span className="dash-svc-val">{dashboardStatus.lsp?.installed || 0}/{dashboardStatus.lsp?.total || 0} installed</span>
|
||||
</div>
|
||||
<div className="dash-svc-row">
|
||||
<span className="dash-svc-name">Skills</span>
|
||||
<span className="dash-svc-val">{dashboardStatus.skills?.total || 0} deployed</span>
|
||||
</div>
|
||||
{(dashboardStatus.skills?.issues || []).length > 0 && (
|
||||
<div className="dash-svc-issues">
|
||||
{(dashboardStatus.skills.issues || []).slice(0, 3).map((issue, i) => (
|
||||
<div key={i} className="dash-svc-issue">⚠ {issue}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<span className="dash-empty">Loading...</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -541,22 +541,11 @@ input::placeholder { color: var(--text-disabled); }
|
||||
overflow: hidden;
|
||||
}
|
||||
.dash-card {
|
||||
position: relative;
|
||||
background: var(--bg-card); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg); padding: 14px 16px;
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.dash-card-graph { padding: 0; }
|
||||
.dash-bg-graph {
|
||||
position: absolute; inset: 0; width: 100%; height: 100%;
|
||||
opacity: 0.35; pointer-events: none;
|
||||
}
|
||||
.dash-card-content {
|
||||
position: relative; z-index: 1;
|
||||
padding: 14px 16px;
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
}
|
||||
.dash-span-2 { grid-column: span 2; }
|
||||
.dash-card-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user