diff --git a/web/src/components/Dashboard.jsx b/web/src/components/Dashboard.jsx index cece2be..b6a7d4f 100644 --- a/web/src/components/Dashboard.jsx +++ b/web/src/components/Dashboard.jsx @@ -197,26 +197,34 @@ export default function Dashboard({ api, refreshRef }) { {/* Recent Commands */} -
+
Recent Commands + {recentUnique.length}
{topCmds.length > 0 && ( -
+
+ Most used {topCmds.map((c, i) => ( -
{ navigator.clipboard.writeText(c.cmd); setCopiedIdx(i); setTimeout(() => setCopiedIdx(-1), 1200); }}> - {copiedIdx === i ? '✓ Copié' : c.cmd} - {c.count}× +
copyCmd(c.cmd, `top-${i}`)} title={c.cmd}> + {copiedSet.has(`top-${i}`) ? '✓ Copié' : c.cmd} +
+
+
+ {c.count}×
))}
)}
- {recentCmds.length === 0 && No history} - {recentCmds.map((c, i) => ( -
- {c.shell} - {c.cmd.length > 45 ? c.cmd.slice(0, 42) + '...' : c.cmd} + {recentUnique.length === 0 && No history} + {recentUnique.map((c, i) => ( +
copyCmd(c.cmd, `list-${i}`)} title={c.cmd + ' · click to copy'}> +
+ {c.cmd.length > 38 ? c.cmd.slice(0, 35) + '...' : c.cmd} + {relativeTime(c.ts)} +
+ {copiedSet.has(`list-${i}`) ? '✓' : '⎘'}
))}
diff --git a/web/src/components/Shell.jsx b/web/src/components/Shell.jsx index 633c7a8..64235ca 100644 --- a/web/src/components/Shell.jsx +++ b/web/src/components/Shell.jsx @@ -400,7 +400,7 @@ export default function Shell({ api }) { const onResize = () => { const el = document.getElementById(`terminal-${tabId}`) - if (el && el.offsetParent !== null) { + if (el && el.style.visibility !== 'hidden' && el.style.position !== 'absolute') { fitAddon.fit() } } @@ -438,23 +438,25 @@ export default function Shell({ api }) { const tryInit = (attempt) => { if (cancelled || attempt > 20) return const shellCol = document.querySelector('.shell-terminal-col') - if (!shellCol || shellCol.offsetParent === null) { + if (!shellCol) { pending.push(setTimeout(() => tryInit(attempt + 1), 150)) return } const container = document.getElementById(`terminal-${tab.id}`) - if (!container || container.offsetHeight === 0) { + if (!container) { pending.push(setTimeout(() => tryInit(attempt + 1), 100)) return } if (!tabsRef.current[tab.id]) { initTerminal(tab.id, tab) } - requestAnimationFrame(() => { - if (cancelled) return - const entry = tabsRef.current[tab.id] - if (entry) entry.fitAddon.fit() - }) + if (activeTab === tab.id) { + requestAnimationFrame(() => { + if (cancelled) return + const entry = tabsRef.current[tab.id] + if (entry) entry.fitAddon.fit() + }) + } } tryInit(0) @@ -470,7 +472,7 @@ export default function Shell({ api }) { const entry = tabsRef.current[tab.id] if (entry) { const el = document.getElementById(`terminal-${tab.id}`) - if (el && el.offsetParent !== null) { + if (el && el.style.visibility !== 'hidden') { entry.fitAddon.fit() } } @@ -839,7 +841,10 @@ export default function Shell({ api }) { key={tab.id} id={`terminal-${tab.id}`} className="shell-xterm-instance" - style={{ display: activeTab === tab.id ? 'block' : 'none' }} + style={activeTab === tab.id + ? { visibility: 'visible', pointerEvents: 'auto' } + : { visibility: 'hidden', pointerEvents: 'none' } + } /> ))}
diff --git a/web/src/styles/global.css b/web/src/styles/global.css index a9eb05f..9c69072 100644 --- a/web/src/styles/global.css +++ b/web/src/styles/global.css @@ -691,34 +691,38 @@ input::placeholder { color: var(--text-disabled); } } /* Commands */ -.dash-cmd-list { display: flex; flex-direction: column; gap: 3px; max-height: 270px; overflow-y: auto; } +.dash-cmd-card .dash-cmd-list { max-height: 220px; } +.dash-cmd-list { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; } .dash-cmd-row { - display: flex; align-items: center; gap: 6px; - padding: 3px 0; overflow: hidden; -} -.dash-cmd-shell { - font-size: 9px; font-family: var(--font-mono); color: var(--text-disabled); - background: var(--bg-input); padding: 1px 4px; border-radius: 3px; - text-transform: uppercase; flex-shrink: 0; + display: flex; align-items: center; justify-content: space-between; gap: 8px; + padding: 5px 8px; border-radius: var(--radius-sm); + background: var(--bg-surface); cursor: pointer; + transition: background 0.12s; } +.dash-cmd-row:hover { background: var(--accent-bg); } +.dash-cmd-left { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; } .dash-cmd-text { - font-size: 11px; font-family: var(--font-mono); color: var(--text-secondary); + font-size: 11px; font-family: var(--font-mono); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - flex: 1; min-width: 0; } +.dash-cmd-time { font-size: 9px; color: var(--text-disabled); } +.dash-cmd-copy { font-size: 13px; color: var(--text-disabled); flex-shrink: 0; } +.dash-cmd-row:hover .dash-cmd-copy { color: var(--accent); } + +.dash-cmd-freq { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--border); } +.dash-cmd-freq-title { font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--text-disabled); letter-spacing: 0.05em; margin-bottom: 2px; } +.dash-cmd-freq-row { + display: flex; align-items: center; gap: 8px; cursor: pointer; + padding: 3px 4px; border-radius: var(--radius-sm); + transition: background 0.12s; +} +.dash-cmd-freq-row:hover { background: var(--accent-bg); } +.dash-cmd-freq-name { font-size: 12px; font-weight: 600; font-family: var(--font-mono); color: var(--text-primary); width: 100px; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.dash-cmd-freq-bar-wrap { flex: 1; height: 6px; background: var(--bg-input); border-radius: 3px; overflow: hidden; } +.dash-cmd-freq-bar { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.3s ease; } +.dash-cmd-freq-count { font-size: 10px; font-family: var(--font-mono); color: var(--accent); width: 28px; text-align: right; flex-shrink: 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-copied { border-color: var(--accent) !important; background: var(--accent-bg) !important; } -.dash-cmd-chip-copied .dash-cmd-chip-name { color: var(--accent); } -.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; }