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} +