From 0831ee3865654eb493751d08f72c27527dbd7521 Mon Sep 17 00:00:00 2001 From: Clawdbot Date: Wed, 28 Jan 2026 16:12:32 +0100 Subject: [PATCH 1/9] feat(ui): Delete session button in Chat tab Adds a Delete button to the Chat tab with confirmation modal. Features: - Delete button next to Stop/New session (disabled during generation) - Hidden for main session (cannot delete main) - Confirmation modal with session name - Enter key to confirm, Escape to cancel - After deletion: switches to main session and refreshes chat - Archives transcript when deleting (same as Sessions tab) Changes: - ui/src/ui/views/chat.ts: Add delete props, modal overlay, delete button - ui/src/ui/app.ts: Add chatDeleteConfirm state - ui/src/ui/app-render.ts: Wire up delete handlers, hide for main, switch after delete - ui/src/styles/components.css: Styles for confirmation modal --- ui/src/styles/components.css | 46 +++++++++++++++++++++++++++++ ui/src/ui/app-render.ts | 22 ++++++++++++++ ui/src/ui/app.ts | 1 + ui/src/ui/views/chat.ts | 56 +++++++++++++++++++++++++++++++++++- 4 files changed, 124 insertions(+), 1 deletion(-) diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 27dfe62d1..8ce1bf51f 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -1484,3 +1484,49 @@ flex-wrap: wrap; gap: 8px; } + +/* Delete session confirmation modal */ +.chat-delete-overlay { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 100; + backdrop-filter: blur(2px); +} + +.chat-delete-card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 8px; + padding: 20px 24px; + max-width: 360px; + text-align: center; +} + +.chat-delete-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 8px; +} + +.chat-delete-sub { + font-size: 13px; + color: var(--muted); + margin-bottom: 16px; + word-break: break-word; +} + +.chat-delete-actions { + display: flex; + gap: 8px; + justify-content: center; +} + +.chat-delete-hint { + margin-top: 12px; + font-size: 11px; + color: var(--muted); +} diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index 422af6863..e950e93be 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -497,6 +497,28 @@ export function renderApp(state: AppViewState) { onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio), assistantName: state.assistantName, assistantAvatar: state.assistantAvatar, + // Delete session (hidden for main session) + showDeleteConfirm: state.chatDeleteConfirm, + onDeleteClick: + state.sessionKey === "main" || + parseAgentSessionKey(state.sessionKey)?.rest === "main" + ? undefined + : () => (state.chatDeleteConfirm = true), + onDeleteConfirm: async () => { + state.chatDeleteConfirm = false; + const { deleteSession } = await import("./controllers/sessions"); + await deleteSession(state as Parameters[0], state.sessionKey); + // Switch to main session after deletion + const mainKey = state.sessionsResult?.mainSessionKey ?? "main"; + state.sessionKey = mainKey; + state.applySettings({ + ...state.settings, + sessionKey: mainKey, + lastActiveSessionKey: mainKey, + }); + void loadChatHistory(state); + }, + onDeleteCancel: () => (state.chatDeleteConfirm = false), }) : nothing} diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 50ffcdf76..c4b9e737e 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -130,6 +130,7 @@ export class MoltbotApp extends LitElement { @state() chatThinkingLevel: string | null = null; @state() chatQueue: ChatQueueItem[] = []; @state() chatAttachments: ChatAttachment[] = []; + @state() chatDeleteConfirm = false; // Sidebar state for tool output viewing @state() sidebarOpen = false; @state() sidebarContent: string | null = null; diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts index f5fb6e80b..31beb8541 100644 --- a/ui/src/ui/views/chat.ts +++ b/ui/src/ui/views/chat.ts @@ -68,6 +68,11 @@ export type ChatProps = { onCloseSidebar?: () => void; onSplitRatioChange?: (ratio: number) => void; onChatScroll?: (event: Event) => void; + // Delete session + showDeleteConfirm?: boolean; + onDeleteClick?: () => void; + onDeleteConfirm?: () => void; + onDeleteCancel?: () => void; }; const COMPACTION_TOAST_DURATION_MS = 5000; @@ -240,7 +245,44 @@ export function renderChat(props: ChatProps) { `; return html` -
+
{ + // Escape to cancel delete confirmation + if (e.key === "Escape" && props.showDeleteConfirm && props.onDeleteCancel) { + e.preventDefault(); + props.onDeleteCancel(); + } + // Enter to confirm delete + if (e.key === "Enter" && props.showDeleteConfirm && props.onDeleteConfirm) { + e.preventDefault(); + props.onDeleteConfirm(); + } + }} + > + ${props.showDeleteConfirm + ? html` + + ` + : nothing} + ${props.disabledReason ? html`
${props.disabledReason}
` : nothing} @@ -361,6 +403,18 @@ export function renderChat(props: ChatProps) { > ${canAbort ? "Stop" : "New session"} + ${props.onDeleteClick + ? html` + + ` + : nothing} From 9d529f51b16f0866761011110a7f5925af60e271 Mon Sep 17 00:00:00 2001 From: Clawdbot Date: Wed, 28 Jan 2026 17:38:24 +0100 Subject: [PATCH 3/9] fix: responsive layout for chat action buttons on mobile - Add flex-wrap to prevent button overflow - Reduce button padding and font-size on small screens - Hide keyboard hints (Esc/Enter) on mobile --- ui/src/styles/components.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 8ce1bf51f..9eb7f2150 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -1369,6 +1369,22 @@ } } +@media (max-width: 480px) { + .chat-compose__actions { + flex-wrap: wrap; + gap: 6px; + } + + .chat-compose__actions .btn { + padding: 6px 10px; + font-size: 13px; + } + + .chat-compose__actions .btn-kbd { + display: none; + } +} + /* =========================================== QR Code =========================================== */ From 3ab9c58285539f6ec245b399c175b8db282543c7 Mon Sep 17 00:00:00 2001 From: Clawdbot Date: Wed, 28 Jan 2026 21:03:30 +0100 Subject: [PATCH 4/9] fix: use fixed positioning for delete confirmation overlay background --- patches/chat-delete-session.patch | 203 ++++++++++++++++++++++++++++++ ui/src/styles/components.css | 2 +- 2 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 patches/chat-delete-session.patch diff --git a/patches/chat-delete-session.patch b/patches/chat-delete-session.patch new file mode 100644 index 000000000..bb49fc47e --- /dev/null +++ b/patches/chat-delete-session.patch @@ -0,0 +1,203 @@ +diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css +index 27dfe62d1..9eb7f2150 100644 +--- a/ui/src/styles/components.css ++++ b/ui/src/styles/components.css +@@ -1369,6 +1369,22 @@ + } + } + ++@media (max-width: 480px) { ++ .chat-compose__actions { ++ flex-wrap: wrap; ++ gap: 6px; ++ } ++ ++ .chat-compose__actions .btn { ++ padding: 6px 10px; ++ font-size: 13px; ++ } ++ ++ .chat-compose__actions .btn-kbd { ++ display: none; ++ } ++} ++ + /* =========================================== + QR Code + =========================================== */ +@@ -1484,3 +1500,49 @@ + flex-wrap: wrap; + gap: 8px; + } ++ ++/* Delete session confirmation modal */ ++.chat-delete-overlay { ++ position: fixed; ++ inset: 0; ++ background: rgba(0, 0, 0, 0.6); ++ display: flex; ++ align-items: center; ++ justify-content: center; ++ z-index: 100; ++ backdrop-filter: blur(2px); ++} ++ ++.chat-delete-card { ++ background: var(--surface); ++ border: 1px solid var(--border); ++ border-radius: 8px; ++ padding: 20px 24px; ++ max-width: 360px; ++ text-align: center; ++} ++ ++.chat-delete-title { ++ font-size: 16px; ++ font-weight: 600; ++ margin-bottom: 8px; ++} ++ ++.chat-delete-sub { ++ font-size: 13px; ++ color: var(--muted); ++ margin-bottom: 16px; ++ word-break: break-word; ++} ++ ++.chat-delete-actions { ++ display: flex; ++ gap: 8px; ++ justify-content: center; ++} ++ ++.chat-delete-hint { ++ margin-top: 12px; ++ font-size: 11px; ++ color: var(--muted); ++} +diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts +index a088c33ff..6607688f9 100644 +--- a/ui/src/ui/app-render.ts ++++ b/ui/src/ui/app-render.ts +@@ -496,6 +496,27 @@ export function renderApp(state: AppViewState) { + onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio), + assistantName: state.assistantName, + assistantAvatar: state.assistantAvatar, ++ // Delete session (disabled for main session) ++ showDeleteConfirm: state.chatDeleteConfirm, ++ isMainSession: ++ state.sessionKey === "main" || ++ parseAgentSessionKey(state.sessionKey)?.rest === "main", ++ onDeleteClick: () => (state.chatDeleteConfirm = true), ++ onDeleteConfirm: async () => { ++ state.chatDeleteConfirm = false; ++ const { deleteSession } = await import("./controllers/sessions"); ++ await deleteSession(state as Parameters[0], state.sessionKey); ++ // Switch to main session after deletion ++ const mainKey = state.sessionsResult?.mainSessionKey ?? "main"; ++ state.sessionKey = mainKey; ++ state.applySettings({ ++ ...state.settings, ++ sessionKey: mainKey, ++ lastActiveSessionKey: mainKey, ++ }); ++ void loadChatHistory(state); ++ }, ++ onDeleteCancel: () => (state.chatDeleteConfirm = false), + }) + : nothing} + +diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts +index d23e543cd..d2c7464b9 100644 +--- a/ui/src/ui/app.ts ++++ b/ui/src/ui/app.ts +@@ -130,6 +130,7 @@ export class MoltbotApp extends LitElement { + @state() chatThinkingLevel: string | null = null; + @state() chatQueue: ChatQueueItem[] = []; + @state() chatAttachments: ChatAttachment[] = []; ++ @state() chatDeleteConfirm = false; + // Sidebar state for tool output viewing + @state() sidebarOpen = false; + @state() sidebarContent: string | null = null; +diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts +index f5fb6e80b..c7c8b38aa 100644 +--- a/ui/src/ui/views/chat.ts ++++ b/ui/src/ui/views/chat.ts +@@ -68,6 +68,12 @@ export type ChatProps = { + onCloseSidebar?: () => void; + onSplitRatioChange?: (ratio: number) => void; + onChatScroll?: (event: Event) => void; ++ // Delete session ++ showDeleteConfirm?: boolean; ++ isMainSession?: boolean; ++ onDeleteClick?: () => void; ++ onDeleteConfirm?: () => void; ++ onDeleteCancel?: () => void; + }; + + const COMPACTION_TOAST_DURATION_MS = 5000; +@@ -240,7 +246,44 @@ export function renderChat(props: ChatProps) { + `; + + return html` +-
++
{ ++ // Escape to cancel delete confirmation ++ if (e.key === "Escape" && props.showDeleteConfirm && props.onDeleteCancel) { ++ e.preventDefault(); ++ props.onDeleteCancel(); ++ } ++ // Enter to confirm delete ++ if (e.key === "Enter" && props.showDeleteConfirm && props.onDeleteConfirm) { ++ e.preventDefault(); ++ props.onDeleteConfirm(); ++ } ++ }} ++ > ++ ${props.showDeleteConfirm ++ ? html` ++ ++ ` ++ : nothing} ++ + ${props.disabledReason + ? html`
${props.disabledReason}
` + : nothing} +@@ -361,6 +404,18 @@ export function renderChat(props: ChatProps) { + > + ${canAbort ? "Stop" : "New session"} + ++ ${props.onDeleteClick ++ ? html` ++ ++ ` ++ : nothing} + -+ -+ -+
Press Enter to delete, Escape to cancel
-+ -+ -+ ` -+ : nothing} -+ - ${props.disabledReason - ? html`
${props.disabledReason}
` - : nothing} -@@ -361,6 +404,18 @@ export function renderChat(props: ChatProps) { - > - ${canAbort ? "Stop" : "New session"} - -+ ${props.onDeleteClick -+ ? html` -+ -+ ` -+ : nothing} - - - -
Press Enter to delete, Escape to cancel
- - - ` - : nothing} - +
${props.disabledReason ? html`
${props.disabledReason}
` : nothing} @@ -404,15 +364,19 @@ export function renderChat(props: ChatProps) { > ${canAbort ? "Stop" : "New session"} - ${props.onDeleteClick + ${props.onDelete ? html` ` : nothing} From 4015f35ecb0f7874cab432657d96c7fad036ca7a Mon Sep 17 00:00:00 2001 From: Clawdbot Date: Thu, 29 Jan 2026 17:18:23 +0100 Subject: [PATCH 9/9] fix: neutral delete button style, stack input above buttons on mobile --- ui/src/styles/components.css | 6 +++++- ui/src/ui/views/chat.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 7ccb1286b..3b90f491b 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -1370,14 +1370,18 @@ } @media (max-width: 480px) { + .chat-compose__row { + flex-direction: column; + } + .chat-compose__actions { gap: 4px; + justify-content: flex-end; } .chat-compose__actions .btn { padding: 4px 8px; font-size: 13px; - flex-shrink: 0; } .chat-compose__actions .btn-kbd { diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts index 148a855d8..d92ac2ce5 100644 --- a/ui/src/ui/views/chat.ts +++ b/ui/src/ui/views/chat.ts @@ -367,7 +367,7 @@ export function renderChat(props: ChatProps) { ${props.onDelete ? html`