mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
remove /quit-confirm slash command
This commit is contained in:
@@ -145,16 +145,6 @@ Slash commands provide meta-level control over the CLI itself.
|
|||||||
- **`nodesc`** or **`nodescriptions`**:
|
- **`nodesc`** or **`nodescriptions`**:
|
||||||
- **Description:** Hide tool descriptions, showing only the tool names.
|
- **Description:** Hide tool descriptions, showing only the tool names.
|
||||||
|
|
||||||
- **`/quit-confirm`**
|
|
||||||
- **Description:** Show a confirmation dialog before exiting Qwen Code, allowing you to choose how to handle your current session.
|
|
||||||
- **Usage:** `/quit-confirm`
|
|
||||||
- **Features:**
|
|
||||||
- **Quit immediately:** Exit without saving anything (equivalent to `/quit`)
|
|
||||||
- **Generate summary and quit:** Create a project summary using `/summary` before exiting
|
|
||||||
- **Save conversation and quit:** Save the current conversation with an auto-generated tag before exiting
|
|
||||||
- **Keyboard shortcut:** Press **Ctrl+C** twice to trigger the quit confirmation dialog
|
|
||||||
- **Note:** This command is automatically triggered when you press Ctrl+C once, providing a safety mechanism to prevent accidental exits.
|
|
||||||
|
|
||||||
- **`/quit`** (or **`/exit`**)
|
- **`/quit`** (or **`/exit`**)
|
||||||
- **Description:** Exit Qwen Code immediately without any confirmation dialog.
|
- **Description:** Exit Qwen Code immediately without any confirmation dialog.
|
||||||
|
|
||||||
|
|||||||
@@ -671,4 +671,4 @@ Note: When usage statistics are enabled, events are sent to an Alibaba Cloud RUM
|
|||||||
- **Category:** UI
|
- **Category:** UI
|
||||||
- **Requires Restart:** No
|
- **Requires Restart:** No
|
||||||
- **Example:** `"enableWelcomeBack": false`
|
- **Example:** `"enableWelcomeBack": false`
|
||||||
- **Details:** When enabled, Qwen Code will automatically detect if you're returning to a project with a previously generated project summary (`.qwen/PROJECT_SUMMARY.md`) and show a dialog allowing you to continue your previous conversation or start fresh. This feature integrates with the `/summary` command and quit confirmation dialog. See the [Welcome Back documentation](./welcome-back.md) for more details.
|
- **Details:** When enabled, Qwen Code will automatically detect if you're returning to a project with a previously generated project summary (`.qwen/PROJECT_SUMMARY.md`) and show a dialog allowing you to continue your previous conversation or start fresh. This feature integrates with the `/summary` command. See the [Welcome Back documentation](./welcome-back.md) for more details.
|
||||||
|
|||||||
@@ -81,14 +81,6 @@ The Welcome Back feature works seamlessly with the `/summary` command:
|
|||||||
2. **Automatic Detection:** Next time you start Qwen Code in this project, Welcome Back will detect the summary
|
2. **Automatic Detection:** Next time you start Qwen Code in this project, Welcome Back will detect the summary
|
||||||
3. **Resume Work:** Choose to continue and the summary will be loaded as context
|
3. **Resume Work:** Choose to continue and the summary will be loaded as context
|
||||||
|
|
||||||
### Quit Confirmation
|
|
||||||
|
|
||||||
When exiting with `/quit-confirm` and choosing "Generate summary and quit":
|
|
||||||
|
|
||||||
1. A project summary is automatically created
|
|
||||||
2. Next session will trigger the Welcome Back dialog
|
|
||||||
3. You can seamlessly continue your work
|
|
||||||
|
|
||||||
## File Structure
|
## File Structure
|
||||||
|
|
||||||
The Welcome Back feature creates and uses:
|
The Welcome Back feature creates and uses:
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ export default {
|
|||||||
'open full Qwen Code documentation in your browser',
|
'open full Qwen Code documentation in your browser',
|
||||||
'Configuration not available.': 'Configuration not available.',
|
'Configuration not available.': 'Configuration not available.',
|
||||||
'change the auth method': 'change the auth method',
|
'change the auth method': 'change the auth method',
|
||||||
'Show quit confirmation dialog': 'Show quit confirmation dialog',
|
|
||||||
'Copy the last result or code snippet to clipboard':
|
'Copy the last result or code snippet to clipboard':
|
||||||
'Copy the last result or code snippet to clipboard',
|
'Copy the last result or code snippet to clipboard',
|
||||||
|
|
||||||
@@ -690,18 +689,6 @@ export default {
|
|||||||
'A custom command wants to run the following shell commands:':
|
'A custom command wants to run the following shell commands:':
|
||||||
'A custom command wants to run the following shell commands:',
|
'A custom command wants to run the following shell commands:',
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Dialogs - Quit Confirmation
|
|
||||||
// ============================================================================
|
|
||||||
'What would you like to do before exiting?':
|
|
||||||
'What would you like to do before exiting?',
|
|
||||||
'Quit immediately (/quit)': 'Quit immediately (/quit)',
|
|
||||||
'Generate summary and quit (/summary)':
|
|
||||||
'Generate summary and quit (/summary)',
|
|
||||||
'Save conversation and quit (/chat save)':
|
|
||||||
'Save conversation and quit (/chat save)',
|
|
||||||
'Cancel (stay in application)': 'Cancel (stay in application)',
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Dialogs - Pro Quota
|
// Dialogs - Pro Quota
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ export default {
|
|||||||
'在浏览器中打开完整的 Qwen Code 文档',
|
'在浏览器中打开完整的 Qwen Code 文档',
|
||||||
'Configuration not available.': '配置不可用',
|
'Configuration not available.': '配置不可用',
|
||||||
'change the auth method': '更改认证方法',
|
'change the auth method': '更改认证方法',
|
||||||
'Show quit confirmation dialog': '显示退出确认对话框',
|
|
||||||
'Copy the last result or code snippet to clipboard':
|
'Copy the last result or code snippet to clipboard':
|
||||||
'将最后的结果或代码片段复制到剪贴板',
|
'将最后的结果或代码片段复制到剪贴板',
|
||||||
|
|
||||||
@@ -655,15 +654,6 @@ export default {
|
|||||||
'A custom command wants to run the following shell commands:':
|
'A custom command wants to run the following shell commands:':
|
||||||
'自定义命令想要运行以下 shell 命令:',
|
'自定义命令想要运行以下 shell 命令:',
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Dialogs - Quit Confirmation
|
|
||||||
// ============================================================================
|
|
||||||
'What would you like to do before exiting?': '退出前您想要做什么?',
|
|
||||||
'Quit immediately (/quit)': '立即退出 (/quit)',
|
|
||||||
'Generate summary and quit (/summary)': '生成摘要并退出 (/summary)',
|
|
||||||
'Save conversation and quit (/chat save)': '保存对话并退出 (/chat save)',
|
|
||||||
'Cancel (stay in application)': '取消(留在应用程序中)',
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Dialogs - Pro Quota
|
// Dialogs - Pro Quota
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ vi.mock('../ui/commands/modelCommand.js', () => ({
|
|||||||
}));
|
}));
|
||||||
vi.mock('../ui/commands/quitCommand.js', () => ({
|
vi.mock('../ui/commands/quitCommand.js', () => ({
|
||||||
quitCommand: {},
|
quitCommand: {},
|
||||||
quitConfirmCommand: {},
|
|
||||||
}));
|
}));
|
||||||
vi.mock('../ui/commands/statsCommand.js', () => ({ statsCommand: {} }));
|
vi.mock('../ui/commands/statsCommand.js', () => ({ statsCommand: {} }));
|
||||||
vi.mock('../ui/commands/themeCommand.js', () => ({ themeCommand: {} }));
|
vi.mock('../ui/commands/themeCommand.js', () => ({ themeCommand: {} }));
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { mcpCommand } from '../ui/commands/mcpCommand.js';
|
|||||||
import { memoryCommand } from '../ui/commands/memoryCommand.js';
|
import { memoryCommand } from '../ui/commands/memoryCommand.js';
|
||||||
import { modelCommand } from '../ui/commands/modelCommand.js';
|
import { modelCommand } from '../ui/commands/modelCommand.js';
|
||||||
import { permissionsCommand } from '../ui/commands/permissionsCommand.js';
|
import { permissionsCommand } from '../ui/commands/permissionsCommand.js';
|
||||||
import { quitCommand, quitConfirmCommand } from '../ui/commands/quitCommand.js';
|
import { quitCommand } from '../ui/commands/quitCommand.js';
|
||||||
import { restoreCommand } from '../ui/commands/restoreCommand.js';
|
import { restoreCommand } from '../ui/commands/restoreCommand.js';
|
||||||
import { settingsCommand } from '../ui/commands/settingsCommand.js';
|
import { settingsCommand } from '../ui/commands/settingsCommand.js';
|
||||||
import { statsCommand } from '../ui/commands/statsCommand.js';
|
import { statsCommand } from '../ui/commands/statsCommand.js';
|
||||||
@@ -77,7 +77,6 @@ export class BuiltinCommandLoader implements ICommandLoader {
|
|||||||
modelCommand,
|
modelCommand,
|
||||||
...(this.config?.getFolderTrust() ? [permissionsCommand] : []),
|
...(this.config?.getFolderTrust() ? [permissionsCommand] : []),
|
||||||
quitCommand,
|
quitCommand,
|
||||||
quitConfirmCommand,
|
|
||||||
restoreCommand(this.config),
|
restoreCommand(this.config),
|
||||||
statsCommand,
|
statsCommand,
|
||||||
summaryCommand,
|
summaryCommand,
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ import { useSessionStats } from './contexts/SessionContext.js';
|
|||||||
import { useGitBranchName } from './hooks/useGitBranchName.js';
|
import { useGitBranchName } from './hooks/useGitBranchName.js';
|
||||||
import { useExtensionUpdates } from './hooks/useExtensionUpdates.js';
|
import { useExtensionUpdates } from './hooks/useExtensionUpdates.js';
|
||||||
import { ShellFocusContext } from './contexts/ShellFocusContext.js';
|
import { ShellFocusContext } from './contexts/ShellFocusContext.js';
|
||||||
import { useQuitConfirmation } from './hooks/useQuitConfirmation.js';
|
|
||||||
import { t } from '../i18n/index.js';
|
import { t } from '../i18n/index.js';
|
||||||
import { useWelcomeBack } from './hooks/useWelcomeBack.js';
|
import { useWelcomeBack } from './hooks/useWelcomeBack.js';
|
||||||
import { useDialogClose } from './hooks/useDialogClose.js';
|
import { useDialogClose } from './hooks/useDialogClose.js';
|
||||||
@@ -446,8 +445,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
|
|
||||||
const { toggleVimEnabled } = useVimMode();
|
const { toggleVimEnabled } = useVimMode();
|
||||||
|
|
||||||
const { showQuitConfirmation } = useQuitConfirmation();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isSubagentCreateDialogOpen,
|
isSubagentCreateDialogOpen,
|
||||||
openSubagentCreateDialog,
|
openSubagentCreateDialog,
|
||||||
@@ -493,7 +490,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
addConfirmUpdateExtensionRequest,
|
addConfirmUpdateExtensionRequest,
|
||||||
openSubagentCreateDialog,
|
openSubagentCreateDialog,
|
||||||
openAgentsManagerDialog,
|
openAgentsManagerDialog,
|
||||||
_showQuitConfirmation: showQuitConfirmation,
|
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
openAuthDialog,
|
openAuthDialog,
|
||||||
@@ -507,7 +503,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
openPermissionsDialog,
|
openPermissionsDialog,
|
||||||
openApprovalModeDialog,
|
openApprovalModeDialog,
|
||||||
addConfirmUpdateExtensionRequest,
|
addConfirmUpdateExtensionRequest,
|
||||||
showQuitConfirmation,
|
|
||||||
openSubagentCreateDialog,
|
openSubagentCreateDialog,
|
||||||
openAgentsManagerDialog,
|
openAgentsManagerDialog,
|
||||||
],
|
],
|
||||||
@@ -520,7 +515,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
commandContext,
|
commandContext,
|
||||||
shellConfirmationRequest,
|
shellConfirmationRequest,
|
||||||
confirmationRequest,
|
confirmationRequest,
|
||||||
quitConfirmationRequest,
|
|
||||||
} = useSlashCommandProcessor(
|
} = useSlashCommandProcessor(
|
||||||
config,
|
config,
|
||||||
settings,
|
settings,
|
||||||
@@ -969,7 +963,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
isFolderTrustDialogOpen,
|
isFolderTrustDialogOpen,
|
||||||
showWelcomeBackDialog,
|
showWelcomeBackDialog,
|
||||||
handleWelcomeBackClose,
|
handleWelcomeBackClose,
|
||||||
quitConfirmationRequest,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleExit = useCallback(
|
const handleExit = useCallback(
|
||||||
@@ -983,25 +976,18 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
if (timerRef.current) {
|
if (timerRef.current) {
|
||||||
clearTimeout(timerRef.current);
|
clearTimeout(timerRef.current);
|
||||||
}
|
}
|
||||||
// Exit directly without showing confirmation dialog
|
// Exit directly
|
||||||
handleSlashCommand('/quit');
|
handleSlashCommand('/quit');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First press: Prioritize cleanup tasks
|
// First press: Prioritize cleanup tasks
|
||||||
|
|
||||||
// Special case: If quit-confirm dialog is open, Ctrl+C means "quit immediately"
|
|
||||||
if (quitConfirmationRequest) {
|
|
||||||
handleSlashCommand('/quit');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Close other dialogs (highest priority)
|
// 1. Close other dialogs (highest priority)
|
||||||
/**
|
/**
|
||||||
* For AuthDialog it is required to complete the authentication process,
|
* For AuthDialog it is required to complete the authentication process,
|
||||||
* otherwise user cannot proceed to the next step.
|
* otherwise user cannot proceed to the next step.
|
||||||
* So a quit on AuthDialog should go with normal two press quit
|
* So a quit on AuthDialog should go with normal two press quit.
|
||||||
* and without quit-confirm dialog.
|
|
||||||
*/
|
*/
|
||||||
if (isAuthDialogOpen) {
|
if (isAuthDialogOpen) {
|
||||||
setPressedOnce(true);
|
setPressedOnce(true);
|
||||||
@@ -1022,14 +1008,17 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
return; // Request cancelled, end processing
|
return; // Request cancelled, end processing
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Clear input buffer (if has content)
|
// 4. Clear input buffer (if has content)
|
||||||
if (buffer.text.length > 0) {
|
if (buffer.text.length > 0) {
|
||||||
buffer.setText('');
|
buffer.setText('');
|
||||||
return; // Input cleared, end processing
|
return; // Input cleared, end processing
|
||||||
}
|
}
|
||||||
|
|
||||||
// All cleanup tasks completed, show quit confirmation dialog
|
// All cleanup tasks completed, set flag for double-press to quit
|
||||||
handleSlashCommand('/quit-confirm');
|
setPressedOnce(true);
|
||||||
|
timerRef.current = setTimeout(() => {
|
||||||
|
setPressedOnce(false);
|
||||||
|
}, CTRL_EXIT_PROMPT_DURATION_MS);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
isAuthDialogOpen,
|
isAuthDialogOpen,
|
||||||
@@ -1037,7 +1026,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
closeAnyOpenDialog,
|
closeAnyOpenDialog,
|
||||||
streamingState,
|
streamingState,
|
||||||
cancelOngoingRequest,
|
cancelOngoingRequest,
|
||||||
quitConfirmationRequest,
|
|
||||||
buffer,
|
buffer,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -1054,8 +1042,8 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On first press: set flag, start timer, and call handleExit for cleanup/quit-confirm
|
// On first press: set flag, start timer, and call handleExit for cleanup
|
||||||
// On second press (within 500ms): handleExit sees flag and does fast quit
|
// On second press (within timeout): handleExit sees flag and does fast quit
|
||||||
if (!ctrlCPressedOnce) {
|
if (!ctrlCPressedOnce) {
|
||||||
setCtrlCPressedOnce(true);
|
setCtrlCPressedOnce(true);
|
||||||
ctrlCTimerRef.current = setTimeout(() => {
|
ctrlCTimerRef.current = setTimeout(() => {
|
||||||
@@ -1196,7 +1184,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
!!confirmationRequest ||
|
!!confirmationRequest ||
|
||||||
confirmUpdateExtensionRequests.length > 0 ||
|
confirmUpdateExtensionRequests.length > 0 ||
|
||||||
!!loopDetectionConfirmationRequest ||
|
!!loopDetectionConfirmationRequest ||
|
||||||
!!quitConfirmationRequest ||
|
|
||||||
isThemeDialogOpen ||
|
isThemeDialogOpen ||
|
||||||
isSettingsDialogOpen ||
|
isSettingsDialogOpen ||
|
||||||
isModelDialogOpen ||
|
isModelDialogOpen ||
|
||||||
@@ -1245,7 +1232,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
confirmationRequest,
|
confirmationRequest,
|
||||||
confirmUpdateExtensionRequests,
|
confirmUpdateExtensionRequests,
|
||||||
loopDetectionConfirmationRequest,
|
loopDetectionConfirmationRequest,
|
||||||
quitConfirmationRequest,
|
|
||||||
geminiMdFileCount,
|
geminiMdFileCount,
|
||||||
streamingState,
|
streamingState,
|
||||||
initError,
|
initError,
|
||||||
@@ -1337,7 +1323,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
confirmationRequest,
|
confirmationRequest,
|
||||||
confirmUpdateExtensionRequests,
|
confirmUpdateExtensionRequests,
|
||||||
loopDetectionConfirmationRequest,
|
loopDetectionConfirmationRequest,
|
||||||
quitConfirmationRequest,
|
|
||||||
geminiMdFileCount,
|
geminiMdFileCount,
|
||||||
streamingState,
|
streamingState,
|
||||||
initError,
|
initError,
|
||||||
|
|||||||
@@ -8,35 +8,6 @@ import { formatDuration } from '../utils/formatters.js';
|
|||||||
import { CommandKind, type SlashCommand } from './types.js';
|
import { CommandKind, type SlashCommand } from './types.js';
|
||||||
import { t } from '../../i18n/index.js';
|
import { t } from '../../i18n/index.js';
|
||||||
|
|
||||||
export const quitConfirmCommand: SlashCommand = {
|
|
||||||
name: 'quit-confirm',
|
|
||||||
get description() {
|
|
||||||
return t('Show quit confirmation dialog');
|
|
||||||
},
|
|
||||||
kind: CommandKind.BUILT_IN,
|
|
||||||
action: (context) => {
|
|
||||||
const now = Date.now();
|
|
||||||
const { sessionStartTime } = context.session.stats;
|
|
||||||
const wallDuration = now - sessionStartTime.getTime();
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'quit_confirmation',
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
type: 'user',
|
|
||||||
text: `/quit-confirm`,
|
|
||||||
id: now - 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'quit_confirmation',
|
|
||||||
duration: formatDuration(wallDuration),
|
|
||||||
id: now,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const quitCommand: SlashCommand = {
|
export const quitCommand: SlashCommand = {
|
||||||
name: 'quit',
|
name: 'quit',
|
||||||
altNames: ['exit'],
|
altNames: ['exit'],
|
||||||
|
|||||||
@@ -100,12 +100,6 @@ export interface QuitActionReturn {
|
|||||||
messages: HistoryItem[];
|
messages: HistoryItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The return type for a command action that requests quit confirmation. */
|
|
||||||
export interface QuitConfirmationActionReturn {
|
|
||||||
type: 'quit_confirmation';
|
|
||||||
messages: HistoryItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The return type for a command action that results in a simple message
|
* The return type for a command action that results in a simple message
|
||||||
* being displayed to the user.
|
* being displayed to the user.
|
||||||
@@ -182,7 +176,6 @@ export type SlashCommandActionReturn =
|
|||||||
| ToolActionReturn
|
| ToolActionReturn
|
||||||
| MessageActionReturn
|
| MessageActionReturn
|
||||||
| QuitActionReturn
|
| QuitActionReturn
|
||||||
| QuitConfirmationActionReturn
|
|
||||||
| OpenDialogActionReturn
|
| OpenDialogActionReturn
|
||||||
| LoadHistoryActionReturn
|
| LoadHistoryActionReturn
|
||||||
| SubmitPromptActionReturn
|
| SubmitPromptActionReturn
|
||||||
|
|||||||
@@ -36,10 +36,6 @@ import { WelcomeBackDialog } from './WelcomeBackDialog.js';
|
|||||||
import { ModelSwitchDialog } from './ModelSwitchDialog.js';
|
import { ModelSwitchDialog } from './ModelSwitchDialog.js';
|
||||||
import { AgentCreationWizard } from './subagents/create/AgentCreationWizard.js';
|
import { AgentCreationWizard } from './subagents/create/AgentCreationWizard.js';
|
||||||
import { AgentsManagerDialog } from './subagents/manage/AgentsManagerDialog.js';
|
import { AgentsManagerDialog } from './subagents/manage/AgentsManagerDialog.js';
|
||||||
import {
|
|
||||||
QuitConfirmationDialog,
|
|
||||||
QuitChoice,
|
|
||||||
} from './QuitConfirmationDialog.js';
|
|
||||||
|
|
||||||
interface DialogManagerProps {
|
interface DialogManagerProps {
|
||||||
addItem: UseHistoryManagerReturn['addItem'];
|
addItem: UseHistoryManagerReturn['addItem'];
|
||||||
@@ -127,24 +123,6 @@ export const DialogManager = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (uiState.quitConfirmationRequest) {
|
|
||||||
return (
|
|
||||||
<QuitConfirmationDialog
|
|
||||||
onSelect={(choice: QuitChoice) => {
|
|
||||||
if (choice === QuitChoice.CANCEL) {
|
|
||||||
uiState.quitConfirmationRequest?.onConfirm(false, 'cancel');
|
|
||||||
} else if (choice === QuitChoice.QUIT) {
|
|
||||||
uiState.quitConfirmationRequest?.onConfirm(true, 'quit');
|
|
||||||
} else if (choice === QuitChoice.SUMMARY_AND_QUIT) {
|
|
||||||
uiState.quitConfirmationRequest?.onConfirm(
|
|
||||||
true,
|
|
||||||
'summary_and_quit',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (uiState.confirmationRequest) {
|
if (uiState.confirmationRequest) {
|
||||||
return (
|
return (
|
||||||
<ConsentPrompt
|
<ConsentPrompt
|
||||||
|
|||||||
@@ -108,9 +108,6 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
|
|||||||
{itemForDisplay.type === 'quit' && (
|
{itemForDisplay.type === 'quit' && (
|
||||||
<SessionSummaryDisplay duration={itemForDisplay.duration} />
|
<SessionSummaryDisplay duration={itemForDisplay.duration} />
|
||||||
)}
|
)}
|
||||||
{itemForDisplay.type === 'quit_confirmation' && (
|
|
||||||
<SessionSummaryDisplay duration={itemForDisplay.duration} />
|
|
||||||
)}
|
|
||||||
{itemForDisplay.type === 'tool_group' && (
|
{itemForDisplay.type === 'tool_group' && (
|
||||||
<ToolGroupMessage
|
<ToolGroupMessage
|
||||||
toolCalls={itemForDisplay.tools}
|
toolCalls={itemForDisplay.tools}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import type {
|
|||||||
ShellConfirmationRequest,
|
ShellConfirmationRequest,
|
||||||
ConfirmationRequest,
|
ConfirmationRequest,
|
||||||
LoopDetectionConfirmationRequest,
|
LoopDetectionConfirmationRequest,
|
||||||
QuitConfirmationRequest,
|
|
||||||
HistoryItemWithoutId,
|
HistoryItemWithoutId,
|
||||||
StreamingState,
|
StreamingState,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
@@ -69,7 +68,6 @@ export interface UIState {
|
|||||||
confirmationRequest: ConfirmationRequest | null;
|
confirmationRequest: ConfirmationRequest | null;
|
||||||
confirmUpdateExtensionRequests: ConfirmationRequest[];
|
confirmUpdateExtensionRequests: ConfirmationRequest[];
|
||||||
loopDetectionConfirmationRequest: LoopDetectionConfirmationRequest | null;
|
loopDetectionConfirmationRequest: LoopDetectionConfirmationRequest | null;
|
||||||
quitConfirmationRequest: QuitConfirmationRequest | null;
|
|
||||||
geminiMdFileCount: number;
|
geminiMdFileCount: number;
|
||||||
streamingState: StreamingState;
|
streamingState: StreamingState;
|
||||||
initError: string | null;
|
initError: string | null;
|
||||||
|
|||||||
@@ -918,7 +918,6 @@ describe('useSlashCommandProcessor', () => {
|
|||||||
vi.fn(), // toggleVimEnabled
|
vi.fn(), // toggleVimEnabled
|
||||||
vi.fn(), // setIsProcessing
|
vi.fn(), // setIsProcessing
|
||||||
vi.fn(), // setGeminiMdFileCount
|
vi.fn(), // setGeminiMdFileCount
|
||||||
vi.fn(), // _showQuitConfirmation
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
IdeClient,
|
IdeClient,
|
||||||
} from '@qwen-code/qwen-code-core';
|
} from '@qwen-code/qwen-code-core';
|
||||||
import { useSessionStats } from '../contexts/SessionContext.js';
|
import { useSessionStats } from '../contexts/SessionContext.js';
|
||||||
import { formatDuration } from '../utils/formatters.js';
|
|
||||||
import type {
|
import type {
|
||||||
Message,
|
Message,
|
||||||
HistoryItemWithoutId,
|
HistoryItemWithoutId,
|
||||||
@@ -53,7 +52,6 @@ function serializeHistoryItemForRecording(
|
|||||||
|
|
||||||
const SLASH_COMMANDS_SKIP_RECORDING = new Set([
|
const SLASH_COMMANDS_SKIP_RECORDING = new Set([
|
||||||
'quit',
|
'quit',
|
||||||
'quit-confirm',
|
|
||||||
'exit',
|
'exit',
|
||||||
'clear',
|
'clear',
|
||||||
'reset',
|
'reset',
|
||||||
@@ -75,7 +73,6 @@ interface SlashCommandProcessorActions {
|
|||||||
addConfirmUpdateExtensionRequest: (request: ConfirmationRequest) => void;
|
addConfirmUpdateExtensionRequest: (request: ConfirmationRequest) => void;
|
||||||
openSubagentCreateDialog: () => void;
|
openSubagentCreateDialog: () => void;
|
||||||
openAgentsManagerDialog: () => void;
|
openAgentsManagerDialog: () => void;
|
||||||
_showQuitConfirmation: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,10 +112,6 @@ export const useSlashCommandProcessor = (
|
|||||||
prompt: React.ReactNode;
|
prompt: React.ReactNode;
|
||||||
onConfirm: (confirmed: boolean) => void;
|
onConfirm: (confirmed: boolean) => void;
|
||||||
}>(null);
|
}>(null);
|
||||||
const [quitConfirmationRequest, setQuitConfirmationRequest] =
|
|
||||||
useState<null | {
|
|
||||||
onConfirm: (shouldQuit: boolean, action?: string) => void;
|
|
||||||
}>(null);
|
|
||||||
|
|
||||||
const [sessionShellAllowlist, setSessionShellAllowlist] = useState(
|
const [sessionShellAllowlist, setSessionShellAllowlist] = useState(
|
||||||
new Set<string>(),
|
new Set<string>(),
|
||||||
@@ -174,11 +167,6 @@ export const useSlashCommandProcessor = (
|
|||||||
type: 'quit',
|
type: 'quit',
|
||||||
duration: message.duration,
|
duration: message.duration,
|
||||||
};
|
};
|
||||||
} else if (message.type === MessageType.QUIT_CONFIRMATION) {
|
|
||||||
historyItemContent = {
|
|
||||||
type: 'quit_confirmation',
|
|
||||||
duration: message.duration,
|
|
||||||
};
|
|
||||||
} else if (message.type === MessageType.COMPRESSION) {
|
} else if (message.type === MessageType.COMPRESSION) {
|
||||||
historyItemContent = {
|
historyItemContent = {
|
||||||
type: 'compression',
|
type: 'compression',
|
||||||
@@ -449,66 +437,6 @@ export const useSlashCommandProcessor = (
|
|||||||
});
|
});
|
||||||
return { type: 'handled' };
|
return { type: 'handled' };
|
||||||
}
|
}
|
||||||
case 'quit_confirmation':
|
|
||||||
// Show quit confirmation dialog instead of immediately quitting
|
|
||||||
setQuitConfirmationRequest({
|
|
||||||
onConfirm: (shouldQuit: boolean, action?: string) => {
|
|
||||||
setQuitConfirmationRequest(null);
|
|
||||||
if (!shouldQuit) {
|
|
||||||
// User cancelled the quit operation - do nothing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (shouldQuit) {
|
|
||||||
if (action === 'summary_and_quit') {
|
|
||||||
// Generate summary and then quit
|
|
||||||
handleSlashCommand('/summary')
|
|
||||||
.then(() => {
|
|
||||||
// Wait for user to see the summary result
|
|
||||||
setTimeout(() => {
|
|
||||||
handleSlashCommand('/quit');
|
|
||||||
}, 1200);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
// If summary fails, still quit but show error
|
|
||||||
addItemWithRecording(
|
|
||||||
{
|
|
||||||
type: 'error',
|
|
||||||
text: `Failed to generate summary before quit: ${
|
|
||||||
error instanceof Error
|
|
||||||
? error.message
|
|
||||||
: String(error)
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
Date.now(),
|
|
||||||
);
|
|
||||||
// Give user time to see the error message
|
|
||||||
setTimeout(() => {
|
|
||||||
handleSlashCommand('/quit');
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Just quit immediately - trigger the actual quit action
|
|
||||||
const now = Date.now();
|
|
||||||
const { sessionStartTime } = sessionStats;
|
|
||||||
const wallDuration = now - sessionStartTime.getTime();
|
|
||||||
|
|
||||||
actions.quit([
|
|
||||||
{
|
|
||||||
type: 'user',
|
|
||||||
text: `/quit`,
|
|
||||||
id: now - 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'quit',
|
|
||||||
duration: formatDuration(wallDuration),
|
|
||||||
id: now,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { type: 'handled' };
|
|
||||||
|
|
||||||
case 'quit':
|
case 'quit':
|
||||||
actions.quit(result.messages);
|
actions.quit(result.messages);
|
||||||
@@ -692,7 +620,6 @@ export const useSlashCommandProcessor = (
|
|||||||
setSessionShellAllowlist,
|
setSessionShellAllowlist,
|
||||||
setIsProcessing,
|
setIsProcessing,
|
||||||
setConfirmationRequest,
|
setConfirmationRequest,
|
||||||
sessionStats,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -703,6 +630,5 @@ export const useSlashCommandProcessor = (
|
|||||||
commandContext,
|
commandContext,
|
||||||
shellConfirmationRequest,
|
shellConfirmationRequest,
|
||||||
confirmationRequest,
|
confirmationRequest,
|
||||||
quitConfirmationRequest,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,11 +44,6 @@ export interface DialogCloseOptions {
|
|||||||
// Welcome back dialog
|
// Welcome back dialog
|
||||||
showWelcomeBackDialog: boolean;
|
showWelcomeBackDialog: boolean;
|
||||||
handleWelcomeBackClose: () => void;
|
handleWelcomeBackClose: () => void;
|
||||||
|
|
||||||
// Quit confirmation dialog
|
|
||||||
quitConfirmationRequest: {
|
|
||||||
onConfirm: (shouldQuit: boolean, action?: string) => void;
|
|
||||||
} | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,9 +91,6 @@ export function useDialogClose(options: DialogCloseOptions) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: quitConfirmationRequest is NOT handled here anymore
|
|
||||||
// It's handled specially in handleExit - ctrl+c in quit-confirm should exit immediately
|
|
||||||
|
|
||||||
// No dialog was open
|
// No dialog was open
|
||||||
return false;
|
return false;
|
||||||
}, [options]);
|
}, [options]);
|
||||||
|
|||||||
@@ -161,11 +161,6 @@ export type HistoryItemQuit = HistoryItemBase & {
|
|||||||
duration: string;
|
duration: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HistoryItemQuitConfirmation = HistoryItemBase & {
|
|
||||||
type: 'quit_confirmation';
|
|
||||||
duration: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HistoryItemToolGroup = HistoryItemBase & {
|
export type HistoryItemToolGroup = HistoryItemBase & {
|
||||||
type: 'tool_group';
|
type: 'tool_group';
|
||||||
tools: IndividualToolCallDisplay[];
|
tools: IndividualToolCallDisplay[];
|
||||||
@@ -256,7 +251,6 @@ export type HistoryItemWithoutId =
|
|||||||
| HistoryItemModelStats
|
| HistoryItemModelStats
|
||||||
| HistoryItemToolStats
|
| HistoryItemToolStats
|
||||||
| HistoryItemQuit
|
| HistoryItemQuit
|
||||||
| HistoryItemQuitConfirmation
|
|
||||||
| HistoryItemCompression
|
| HistoryItemCompression
|
||||||
| HistoryItemSummary
|
| HistoryItemSummary
|
||||||
| HistoryItemCompression
|
| HistoryItemCompression
|
||||||
@@ -278,7 +272,6 @@ export enum MessageType {
|
|||||||
MODEL_STATS = 'model_stats',
|
MODEL_STATS = 'model_stats',
|
||||||
TOOL_STATS = 'tool_stats',
|
TOOL_STATS = 'tool_stats',
|
||||||
QUIT = 'quit',
|
QUIT = 'quit',
|
||||||
QUIT_CONFIRMATION = 'quit_confirmation',
|
|
||||||
GEMINI = 'gemini',
|
GEMINI = 'gemini',
|
||||||
COMPRESSION = 'compression',
|
COMPRESSION = 'compression',
|
||||||
SUMMARY = 'summary',
|
SUMMARY = 'summary',
|
||||||
@@ -342,12 +335,6 @@ export type Message =
|
|||||||
duration: string;
|
duration: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
type: MessageType.QUIT_CONFIRMATION;
|
|
||||||
timestamp: Date;
|
|
||||||
duration: string;
|
|
||||||
content?: string;
|
|
||||||
}
|
|
||||||
| {
|
| {
|
||||||
type: MessageType.COMPRESSION;
|
type: MessageType.COMPRESSION;
|
||||||
compression: CompressionProps;
|
compression: CompressionProps;
|
||||||
@@ -404,7 +391,3 @@ export interface ConfirmationRequest {
|
|||||||
export interface LoopDetectionConfirmationRequest {
|
export interface LoopDetectionConfirmationRequest {
|
||||||
onComplete: (result: { userSelection: 'disable' | 'keep' }) => void;
|
onComplete: (result: { userSelection: 'disable' | 'keep' }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QuitConfirmationRequest {
|
|
||||||
onConfirm: (shouldQuit: boolean, action?: string) => void;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user