feat(ui): redesign recent commands display and fix terminal visibility
All checks were successful
Beta Release / beta (push) Successful in 44s
All checks were successful
Beta Release / beta (push) Successful in 44s
- Dashboard: add frequency bars for top commands, click-to-copy, time display - Shell: switch from display:none to visibility:hidden for terminal containers - CSS: restyle command list with improved hover states and copy indicators 💘 Generated with Crush Assisted-by: MiniMax-M2.7 via Crush <crush@charm.land>
This commit is contained in:
@@ -197,26 +197,34 @@ export default function Dashboard({ api, refreshRef }) {
|
||||
</div>
|
||||
|
||||
{/* Recent Commands */}
|
||||
<div className="dash-card">
|
||||
<div className="dash-card dash-cmd-card">
|
||||
<div className="dash-card-head">
|
||||
<span className="dash-label">Recent Commands</span>
|
||||
<span className="dash-count">{recentUnique.length}</span>
|
||||
</div>
|
||||
{topCmds.length > 0 && (
|
||||
<div className="dash-cmd-top">
|
||||
<div className="dash-cmd-freq">
|
||||
<span className="dash-cmd-freq-title">Most used</span>
|
||||
{topCmds.map((c, i) => (
|
||||
<div key={i} className={'dash-cmd-chip' + (copiedIdx === i ? ' dash-cmd-chip-copied' : '')} onClick={() => { navigator.clipboard.writeText(c.cmd); setCopiedIdx(i); setTimeout(() => setCopiedIdx(-1), 1200); }}>
|
||||
<span className="dash-cmd-chip-name">{copiedIdx === i ? '✓ Copié' : c.cmd}</span>
|
||||
<span className="dash-cmd-chip-count">{c.count}×</span>
|
||||
<div key={i} className="dash-cmd-freq-row" onClick={() => copyCmd(c.cmd, `top-${i}`)} title={c.cmd}>
|
||||
<span className="dash-cmd-freq-name">{copiedSet.has(`top-${i}`) ? '✓ Copié' : c.cmd}</span>
|
||||
<div className="dash-cmd-freq-bar-wrap">
|
||||
<div className="dash-cmd-freq-bar" style={{ width: `${(c.count / maxCount) * 100}%` }} />
|
||||
</div>
|
||||
<span className="dash-cmd-freq-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) => (
|
||||
<div key={i} className="dash-cmd-row" title={c.cmd}>
|
||||
<span className="dash-cmd-shell">{c.shell}</span>
|
||||
<span className="dash-cmd-text">{c.cmd.length > 45 ? c.cmd.slice(0, 42) + '...' : c.cmd}</span>
|
||||
{recentUnique.length === 0 && <span className="dash-empty">No history</span>}
|
||||
{recentUnique.map((c, i) => (
|
||||
<div key={i} className="dash-cmd-row" onClick={() => copyCmd(c.cmd, `list-${i}`)} title={c.cmd + ' · click to copy'}>
|
||||
<div className="dash-cmd-left">
|
||||
<span className="dash-cmd-text">{c.cmd.length > 38 ? c.cmd.slice(0, 35) + '...' : c.cmd}</span>
|
||||
<span className="dash-cmd-time">{relativeTime(c.ts)}</span>
|
||||
</div>
|
||||
<span className="dash-cmd-copy">{copiedSet.has(`list-${i}`) ? '✓' : '⎘'}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -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' }
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user