mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
feat: add working directory deletion option to quit confirmation modal
Add agent name input with confirmation pattern to enable a "Quit & Delete Working Dirs" button. Widen modal, reduce button size/font, prevent wrapping. Guard against window.maestro timing issues in production.
This commit is contained in:
@@ -851,6 +851,12 @@ function MaestroConsoleInner() {
|
||||
window.maestro.app.confirmQuit();
|
||||
}, []);
|
||||
|
||||
const handleConfirmQuitAndDelete = useCallback(() => {
|
||||
setQuitConfirmModalOpen(false);
|
||||
// TODO: Implement working directory deletion before quit
|
||||
window.maestro.app.confirmQuit();
|
||||
}, []);
|
||||
|
||||
const handleCancelQuit = useCallback(() => {
|
||||
setQuitConfirmModalOpen(false);
|
||||
window.maestro.app.cancelQuit();
|
||||
@@ -4135,6 +4141,10 @@ function MaestroConsoleInner() {
|
||||
|
||||
// Quit confirmation handler - shows modal when trying to quit with busy agents
|
||||
useEffect(() => {
|
||||
// Guard against window.maestro not being defined yet (production timing)
|
||||
if (!window.maestro?.app?.onQuitConfirmationRequest) {
|
||||
return;
|
||||
}
|
||||
const unsubscribe = window.maestro.app.onQuitConfirmationRequest(() => {
|
||||
// Get all busy AI sessions (agents that are actively thinking)
|
||||
const busyAgents = sessions.filter(
|
||||
@@ -13518,6 +13528,7 @@ You are taking over this conversation. Based on the context above, provide a bri
|
||||
onCloseConfirmModal={handleCloseConfirmModal}
|
||||
quitConfirmModalOpen={quitConfirmModalOpen}
|
||||
onConfirmQuit={handleConfirmQuit}
|
||||
onConfirmQuitAndDelete={handleConfirmQuitAndDelete}
|
||||
onCancelQuit={handleCancelQuit}
|
||||
// AppSessionModals props
|
||||
newInstanceModalOpen={newInstanceModalOpen}
|
||||
|
||||
@@ -291,6 +291,7 @@ export interface AppConfirmModalsProps {
|
||||
// Quit Confirm Modal
|
||||
quitConfirmModalOpen: boolean;
|
||||
onConfirmQuit: () => void;
|
||||
onConfirmQuitAndDelete: () => void;
|
||||
onCancelQuit: () => void;
|
||||
}
|
||||
|
||||
@@ -312,6 +313,7 @@ export function AppConfirmModals({
|
||||
// Quit Confirm Modal
|
||||
quitConfirmModalOpen,
|
||||
onConfirmQuit,
|
||||
onConfirmQuitAndDelete,
|
||||
onCancelQuit,
|
||||
}: AppConfirmModalsProps) {
|
||||
// Compute busy agents for QuitConfirmModal
|
||||
@@ -338,6 +340,7 @@ export function AppConfirmModals({
|
||||
busyAgentCount={busyAgents.length}
|
||||
busyAgentNames={busyAgents.map((s) => s.name)}
|
||||
onConfirmQuit={onConfirmQuit}
|
||||
onConfirmQuitAndDelete={onConfirmQuitAndDelete}
|
||||
onCancel={onCancelQuit}
|
||||
/>
|
||||
)}
|
||||
@@ -1742,6 +1745,7 @@ export interface AppModalsProps {
|
||||
onCloseConfirmModal: () => void;
|
||||
quitConfirmModalOpen: boolean;
|
||||
onConfirmQuit: () => void;
|
||||
onConfirmQuitAndDelete: () => void;
|
||||
onCancelQuit: () => void;
|
||||
|
||||
// --- AppSessionModals props ---
|
||||
@@ -2097,6 +2101,7 @@ export function AppModals(props: AppModalsProps) {
|
||||
onCloseConfirmModal,
|
||||
quitConfirmModalOpen,
|
||||
onConfirmQuit,
|
||||
onConfirmQuitAndDelete,
|
||||
onCancelQuit,
|
||||
// Session modals
|
||||
newInstanceModalOpen,
|
||||
@@ -2369,6 +2374,7 @@ export function AppModals(props: AppModalsProps) {
|
||||
onCloseConfirmModal={onCloseConfirmModal}
|
||||
quitConfirmModalOpen={quitConfirmModalOpen}
|
||||
onConfirmQuit={onConfirmQuit}
|
||||
onConfirmQuitAndDelete={onConfirmQuitAndDelete}
|
||||
onCancelQuit={onCancelQuit}
|
||||
/>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Focus defaults to Cancel to prevent accidental data loss.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import type { Theme } from '../types';
|
||||
import { useLayerStack } from '../contexts/LayerStackContext';
|
||||
@@ -20,6 +20,8 @@ interface QuitConfirmModalProps {
|
||||
busyAgentNames: string[];
|
||||
/** Callback when user confirms quit */
|
||||
onConfirmQuit: () => void;
|
||||
/** Callback when user confirms quit and requests working directory deletion */
|
||||
onConfirmQuitAndDelete: () => void;
|
||||
/** Callback when user cancels (stays in app) */
|
||||
onCancel: () => void;
|
||||
}
|
||||
@@ -35,6 +37,7 @@ export function QuitConfirmModal({
|
||||
busyAgentCount,
|
||||
busyAgentNames,
|
||||
onConfirmQuit,
|
||||
onConfirmQuitAndDelete,
|
||||
onCancel,
|
||||
}: QuitConfirmModalProps): JSX.Element {
|
||||
const { registerLayer, unregisterLayer, updateLayerHandler } = useLayerStack();
|
||||
@@ -42,6 +45,12 @@ export function QuitConfirmModal({
|
||||
const cancelButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const onCancelRef = useRef(onCancel);
|
||||
onCancelRef.current = onCancel;
|
||||
const [confirmName, setConfirmName] = useState('');
|
||||
|
||||
// Check if typed name matches any busy agent name (case-insensitive)
|
||||
const deleteEnabled = busyAgentNames.some(
|
||||
(name) => name.toLowerCase() === confirmName.trim().toLowerCase()
|
||||
);
|
||||
|
||||
// Focus Cancel button on mount (safer default action)
|
||||
useEffect(() => {
|
||||
@@ -98,7 +107,7 @@ export function QuitConfirmModal({
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<div
|
||||
className="w-[450px] border rounded-xl shadow-2xl overflow-hidden"
|
||||
className="w-[520px] border rounded-xl shadow-2xl overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: theme.colors.bgSidebar,
|
||||
borderColor: theme.colors.border,
|
||||
@@ -170,11 +179,47 @@ export function QuitConfirmModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Agent name confirmation input for working directory deletion */}
|
||||
<div className="mt-4">
|
||||
<label
|
||||
className="block text-xs mb-1.5"
|
||||
style={{ color: theme.colors.textDim }}
|
||||
htmlFor="quit-confirm-agent-name"
|
||||
>
|
||||
Enter agent name below to enable working directory deletion
|
||||
</label>
|
||||
<input
|
||||
id="quit-confirm-agent-name"
|
||||
type="text"
|
||||
value={confirmName}
|
||||
onChange={(e) => setConfirmName(e.target.value)}
|
||||
placeholder=""
|
||||
className="w-full px-3 py-1.5 rounded-lg border text-xs outline-none focus:ring-1"
|
||||
style={{
|
||||
backgroundColor: theme.colors.bgMain,
|
||||
borderColor: theme.colors.border,
|
||||
color: theme.colors.textMain,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="mt-6 flex justify-end gap-3">
|
||||
<div className="mt-5 flex items-center justify-end gap-2 flex-nowrap">
|
||||
<button
|
||||
onClick={onConfirmQuitAndDelete}
|
||||
disabled={!deleteEnabled}
|
||||
className="px-3 py-1.5 rounded-lg text-xs font-medium transition-colors whitespace-nowrap"
|
||||
style={{
|
||||
backgroundColor: deleteEnabled ? theme.colors.error : `${theme.colors.error}40`,
|
||||
color: deleteEnabled ? '#ffffff' : `#ffffff80`,
|
||||
cursor: deleteEnabled ? 'pointer' : 'not-allowed',
|
||||
}}
|
||||
>
|
||||
Quit & Delete Working Dirs
|
||||
</button>
|
||||
<button
|
||||
onClick={onConfirmQuit}
|
||||
className="px-4 py-2 rounded-lg text-sm font-medium transition-colors hover:opacity-90"
|
||||
className="px-3 py-1.5 rounded-lg text-xs font-medium transition-colors hover:opacity-90 whitespace-nowrap"
|
||||
style={{
|
||||
backgroundColor: theme.colors.error,
|
||||
color: '#ffffff',
|
||||
@@ -185,7 +230,7 @@ export function QuitConfirmModal({
|
||||
<button
|
||||
ref={cancelButtonRef}
|
||||
onClick={onCancel}
|
||||
className="px-4 py-2 rounded-lg text-sm font-medium outline-none focus:ring-2 focus:ring-offset-1 transition-colors"
|
||||
className="px-3 py-1.5 rounded-lg text-xs font-medium outline-none focus:ring-2 focus:ring-offset-1 transition-colors whitespace-nowrap"
|
||||
style={{
|
||||
backgroundColor: theme.colors.accent,
|
||||
color: theme.colors.accentForeground,
|
||||
|
||||
@@ -1815,6 +1815,10 @@ export function useSettings(): UseSettingsReturn {
|
||||
// Reload settings when system resumes from sleep/suspend
|
||||
// This ensures settings like maxOutputLines aren't reset to defaults
|
||||
useEffect(() => {
|
||||
// Guard against window.maestro not being defined yet (production timing)
|
||||
if (!window.maestro?.app?.onSystemResume) {
|
||||
return;
|
||||
}
|
||||
const cleanup = window.maestro.app.onSystemResume(() => {
|
||||
console.log('[Settings] System resumed from sleep, reloading settings');
|
||||
loadSettings();
|
||||
|
||||
Reference in New Issue
Block a user