feat(v0.6): kill a session directly from the Projects grid cards
The detail view could kill sessions, but the grid card's session rows were enter-only. Add a ✕ kill button to each card session row (stopPropagation so it doesn't also enter the session); makeSessionRow gains an optional onKill, makeProjectCard an optional onKillSession, and mountProjects wires it to DELETE /live-sessions/:id followed by a refresh. Frontend-only. projects-panel tests +1 (51): ✕ present only with the callback, click kills the right id without entering. Verified in-browser: a card with 2 sessions dropped to 1 after clicking ✕.
This commit is contained in:
@@ -174,6 +174,7 @@ function sessionDotClass(status: ClaudeStatus): string {
|
||||
function makeSessionRow(
|
||||
sess: { id: string; title?: string; status: ClaudeStatus; clientCount: number },
|
||||
onEnterSession: (id: string) => void,
|
||||
onKill?: (id: string) => void,
|
||||
): HTMLElement {
|
||||
const row = el('div', 'proj-session-row')
|
||||
row.setAttribute('role', 'button')
|
||||
@@ -185,6 +186,19 @@ function makeSessionRow(
|
||||
|
||||
row.append(dot, title, badge)
|
||||
|
||||
if (onKill) {
|
||||
const kill = el('button', 'proj-session-kill', '✕')
|
||||
kill.title = 'Kill this session'
|
||||
kill.setAttribute('aria-label', 'Kill session')
|
||||
// Don't let the kill button trigger the row's enter handler.
|
||||
kill.addEventListener('pointerdown', (e) => e.stopPropagation())
|
||||
kill.addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
onKill(sess.id)
|
||||
})
|
||||
row.append(kill)
|
||||
}
|
||||
|
||||
row.addEventListener('click', () => onEnterSession(sess.id))
|
||||
row.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
@@ -283,6 +297,7 @@ export function makeProjectCard(
|
||||
hooks: ProjectsHooks,
|
||||
onToggleFav: (path: string) => void,
|
||||
onOpenDetail?: (path: string) => void,
|
||||
onKillSession?: (id: string) => void,
|
||||
): HTMLElement {
|
||||
const card = el('div', 'proj-card')
|
||||
|
||||
@@ -339,7 +354,7 @@ export function makeProjectCard(
|
||||
if (runningSessions.length > 0) {
|
||||
const sessionsList = el('div', 'proj-sessions-list')
|
||||
for (const sess of runningSessions) {
|
||||
sessionsList.append(makeSessionRow(sess, hooks.onEnterSession))
|
||||
sessionsList.append(makeSessionRow(sess, hooks.onEnterSession, onKillSession))
|
||||
}
|
||||
card.append(sessionsList)
|
||||
}
|
||||
@@ -507,6 +522,10 @@ export function mountProjects(host: HTMLElement, hooks: ProjectsHooks): Projects
|
||||
void refresh()
|
||||
}
|
||||
|
||||
function killAndRefresh(id: string): void {
|
||||
void killSession(id).then(() => void refresh())
|
||||
}
|
||||
|
||||
function renderGrid(): void {
|
||||
const filtered = filterProjects(allProjects, searchInput.value)
|
||||
const sorted = sortProjects(filtered, favs)
|
||||
@@ -534,6 +553,7 @@ export function mountProjects(host: HTMLElement, hooks: ProjectsHooks): Projects
|
||||
renderGrid()
|
||||
},
|
||||
openDetail,
|
||||
killAndRefresh,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1066,6 +1066,26 @@ body {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Kill (✕) on a session row — close the session straight from the card. */
|
||||
.proj-session-kill {
|
||||
flex: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background: none;
|
||||
color: var(--text-faint);
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
transition: background 0.1s ease, color 0.1s ease;
|
||||
}
|
||||
.proj-session-kill:hover {
|
||||
background: var(--red, #e5484d);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Client-count badge (👁 N) */
|
||||
.proj-session-badge {
|
||||
font-size: 11px;
|
||||
|
||||
Reference in New Issue
Block a user