mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-01-25 02:06:20 +00:00
Compare commits
2 Commits
main
...
refactor/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c8414488f | ||
|
|
6327e35a14 |
@@ -298,7 +298,9 @@ export default {
|
||||
'How is Qwen doing this session? (optional)':
|
||||
'Wie macht sich Qwen in dieser Sitzung? (optional)',
|
||||
Bad: 'Schlecht',
|
||||
Fine: 'In Ordnung',
|
||||
Good: 'Gut',
|
||||
Dismiss: 'Ignorieren',
|
||||
'Not Sure Yet': 'Noch nicht sicher',
|
||||
'Any other key': 'Beliebige andere Taste',
|
||||
'Disable Loading Phrases': 'Ladesprüche deaktivieren',
|
||||
|
||||
@@ -315,7 +315,9 @@ export default {
|
||||
'How is Qwen doing this session? (optional)':
|
||||
'How is Qwen doing this session? (optional)',
|
||||
Bad: 'Bad',
|
||||
Fine: 'Fine',
|
||||
Good: 'Good',
|
||||
Dismiss: 'Dismiss',
|
||||
'Not Sure Yet': 'Not Sure Yet',
|
||||
'Any other key': 'Any other key',
|
||||
'Disable Loading Phrases': 'Disable Loading Phrases',
|
||||
|
||||
@@ -319,7 +319,9 @@ export default {
|
||||
'How is Qwen doing this session? (optional)':
|
||||
'Как дела у Qwen в этой сессии? (необязательно)',
|
||||
Bad: 'Плохо',
|
||||
Fine: 'Нормально',
|
||||
Good: 'Хорошо',
|
||||
Dismiss: 'Отклонить',
|
||||
'Not Sure Yet': 'Пока не уверен',
|
||||
'Any other key': 'Любая другая клавиша',
|
||||
'Disable Loading Phrases': 'Отключить фразы при загрузке',
|
||||
|
||||
@@ -305,7 +305,9 @@ export default {
|
||||
'Enable User Feedback': '启用用户反馈',
|
||||
'How is Qwen doing this session? (optional)': 'Qwen 这次表现如何?(可选)',
|
||||
Bad: '不满意',
|
||||
Fine: '还行',
|
||||
Good: '满意',
|
||||
Dismiss: '忽略',
|
||||
'Not Sure Yet': '暂不评价',
|
||||
'Any other key': '任意其他键',
|
||||
'Disable Loading Phrases': '禁用加载短语',
|
||||
|
||||
@@ -1326,6 +1326,7 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
isFeedbackDialogOpen,
|
||||
openFeedbackDialog,
|
||||
closeFeedbackDialog,
|
||||
temporaryCloseFeedbackDialog,
|
||||
submitFeedback,
|
||||
} = useFeedbackDialog({
|
||||
config,
|
||||
@@ -1571,6 +1572,7 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
// Feedback dialog
|
||||
openFeedbackDialog,
|
||||
closeFeedbackDialog,
|
||||
temporaryCloseFeedbackDialog,
|
||||
submitFeedback,
|
||||
}),
|
||||
[
|
||||
@@ -1611,6 +1613,7 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
// Feedback dialog
|
||||
openFeedbackDialog,
|
||||
closeFeedbackDialog,
|
||||
temporaryCloseFeedbackDialog,
|
||||
submitFeedback,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -5,19 +5,21 @@ import { useUIActions } from './contexts/UIActionsContext.js';
|
||||
import { useUIState } from './contexts/UIStateContext.js';
|
||||
import { useKeypress } from './hooks/useKeypress.js';
|
||||
|
||||
const FEEDBACK_OPTIONS = {
|
||||
export const FEEDBACK_OPTIONS = {
|
||||
GOOD: 1,
|
||||
BAD: 2,
|
||||
NOT_SURE: 3,
|
||||
FINE: 3,
|
||||
DISMISS: 0,
|
||||
} as const;
|
||||
|
||||
const FEEDBACK_OPTION_KEYS = {
|
||||
[FEEDBACK_OPTIONS.GOOD]: '1',
|
||||
[FEEDBACK_OPTIONS.BAD]: '2',
|
||||
[FEEDBACK_OPTIONS.NOT_SURE]: 'any',
|
||||
[FEEDBACK_OPTIONS.FINE]: '3',
|
||||
[FEEDBACK_OPTIONS.DISMISS]: '0',
|
||||
} as const;
|
||||
|
||||
export const FEEDBACK_DIALOG_KEYS = ['1', '2'] as const;
|
||||
export const FEEDBACK_DIALOG_KEYS = ['1', '2', '3', '0'] as const;
|
||||
|
||||
export const FeedbackDialog: React.FC = () => {
|
||||
const uiState = useUIState();
|
||||
@@ -25,15 +27,19 @@ export const FeedbackDialog: React.FC = () => {
|
||||
|
||||
useKeypress(
|
||||
(key) => {
|
||||
if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.GOOD]) {
|
||||
uiActions.submitFeedback(FEEDBACK_OPTIONS.GOOD);
|
||||
} else if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.BAD]) {
|
||||
// Handle keys 0-3: permanent close with feedback/dismiss
|
||||
if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.BAD]) {
|
||||
uiActions.submitFeedback(FEEDBACK_OPTIONS.BAD);
|
||||
} else if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.FINE]) {
|
||||
uiActions.submitFeedback(FEEDBACK_OPTIONS.FINE);
|
||||
} else if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.GOOD]) {
|
||||
uiActions.submitFeedback(FEEDBACK_OPTIONS.GOOD);
|
||||
} else if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.DISMISS]) {
|
||||
uiActions.submitFeedback(FEEDBACK_OPTIONS.DISMISS);
|
||||
} else {
|
||||
uiActions.submitFeedback(FEEDBACK_OPTIONS.NOT_SURE);
|
||||
// Handle other keys: temporary close
|
||||
uiActions.temporaryCloseFeedbackDialog();
|
||||
}
|
||||
|
||||
uiActions.closeFeedbackDialog();
|
||||
},
|
||||
{ isActive: uiState.isFeedbackDialogOpen },
|
||||
);
|
||||
@@ -53,8 +59,16 @@ export const FeedbackDialog: React.FC = () => {
|
||||
<Text color="cyan">{FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.BAD]}: </Text>
|
||||
<Text>{t('Bad')}</Text>
|
||||
<Text> </Text>
|
||||
<Text color="cyan">{t('Any other key')}: </Text>
|
||||
<Text>{t('Not Sure Yet')}</Text>
|
||||
<Text color="cyan">
|
||||
{FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.FINE]}:{' '}
|
||||
</Text>
|
||||
<Text>{t('Fine')}</Text>
|
||||
<Text> </Text>
|
||||
<Text color="cyan">
|
||||
{FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.DISMISS]}:{' '}
|
||||
</Text>
|
||||
<Text>{t('Dismiss')}</Text>
|
||||
<Text> </Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -36,6 +36,11 @@ vi.mock('../utils/clipboardUtils.js');
|
||||
vi.mock('../contexts/UIStateContext.js', () => ({
|
||||
useUIState: vi.fn(() => ({ isFeedbackDialogOpen: false })),
|
||||
}));
|
||||
vi.mock('../contexts/UIActionsContext.js', () => ({
|
||||
useUIActions: vi.fn(() => ({
|
||||
temporaryCloseFeedbackDialog: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
const mockSlashCommands: SlashCommand[] = [
|
||||
{
|
||||
|
||||
@@ -37,6 +37,7 @@ import * as path from 'node:path';
|
||||
import { SCREEN_READER_USER_PREFIX } from '../textConstants.js';
|
||||
import { useShellFocusState } from '../contexts/ShellFocusContext.js';
|
||||
import { useUIState } from '../contexts/UIStateContext.js';
|
||||
import { useUIActions } from '../contexts/UIActionsContext.js';
|
||||
import { FEEDBACK_DIALOG_KEYS } from '../FeedbackDialog.js';
|
||||
export interface InputPromptProps {
|
||||
buffer: TextBuffer;
|
||||
@@ -109,6 +110,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
}) => {
|
||||
const isShellFocused = useShellFocusState();
|
||||
const uiState = useUIState();
|
||||
const uiActions = useUIActions();
|
||||
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
|
||||
const [escPressCount, setEscPressCount] = useState(0);
|
||||
const [showEscapePrompt, setShowEscapePrompt] = useState(false);
|
||||
@@ -337,12 +339,16 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept feedback dialog option keys (1, 2) when dialog is open
|
||||
if (
|
||||
uiState.isFeedbackDialogOpen &&
|
||||
(FEEDBACK_DIALOG_KEYS as readonly string[]).includes(key.name)
|
||||
) {
|
||||
return;
|
||||
// Handle feedback dialog keyboard interactions when dialog is open
|
||||
if (uiState.isFeedbackDialogOpen) {
|
||||
// If it's one of the feedback option keys (1-4), let FeedbackDialog handle it
|
||||
if ((FEEDBACK_DIALOG_KEYS as readonly string[]).includes(key.name)) {
|
||||
return;
|
||||
} else {
|
||||
// For any other key, close feedback dialog temporarily and continue with normal processing
|
||||
uiActions.temporaryCloseFeedbackDialog();
|
||||
// Continue processing the key for normal input handling
|
||||
}
|
||||
}
|
||||
|
||||
// Reset ESC count and hide prompt on any non-ESC key
|
||||
@@ -712,6 +718,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
onToggleShortcuts,
|
||||
showShortcuts,
|
||||
uiState,
|
||||
uiActions,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ export interface UIActions {
|
||||
// Feedback dialog
|
||||
openFeedbackDialog: () => void;
|
||||
closeFeedbackDialog: () => void;
|
||||
temporaryCloseFeedbackDialog: () => void;
|
||||
submitFeedback: (rating: number) => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
USER_SETTINGS_PATH,
|
||||
} from '../../config/settings.js';
|
||||
import type { SessionStatsState } from '../contexts/SessionContext.js';
|
||||
import { FEEDBACK_OPTIONS } from '../FeedbackDialog.js';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
|
||||
const FEEDBACK_SHOW_PROBABILITY = 0.25; // 25% probability of showing feedback dialog
|
||||
@@ -96,37 +97,48 @@ export const useFeedbackDialog = ({
|
||||
}: UseFeedbackDialogProps) => {
|
||||
// Feedback dialog state
|
||||
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
|
||||
const [isFeedbackDismissedTemporarily, setIsFeedbackDismissedTemporarily] =
|
||||
useState(false);
|
||||
|
||||
const openFeedbackDialog = useCallback(() => {
|
||||
setIsFeedbackDialogOpen(true);
|
||||
|
||||
// Record the timestamp when feedback dialog is shown (fire and forget)
|
||||
settings.setValue(
|
||||
SettingScope.User,
|
||||
'ui.feedbackLastShownTimestamp',
|
||||
Date.now(),
|
||||
);
|
||||
}, [settings]);
|
||||
}, []);
|
||||
|
||||
const closeFeedbackDialog = useCallback(
|
||||
() => setIsFeedbackDialogOpen(false),
|
||||
[],
|
||||
);
|
||||
|
||||
const temporaryCloseFeedbackDialog = useCallback(() => {
|
||||
setIsFeedbackDialogOpen(false);
|
||||
setIsFeedbackDismissedTemporarily(true);
|
||||
}, []);
|
||||
|
||||
const submitFeedback = useCallback(
|
||||
(rating: number) => {
|
||||
// Create and log the feedback event
|
||||
const feedbackEvent = new UserFeedbackEvent(
|
||||
sessionStats.sessionId,
|
||||
rating as UserFeedbackRating,
|
||||
config.getModel(),
|
||||
config.getApprovalMode(),
|
||||
// Only create and log feedback event for ratings 1-3 (GOOD, BAD, FINE)
|
||||
// Rating 0 (DISMISS) should not trigger any telemetry
|
||||
if (rating >= FEEDBACK_OPTIONS.GOOD && rating <= FEEDBACK_OPTIONS.FINE) {
|
||||
const feedbackEvent = new UserFeedbackEvent(
|
||||
sessionStats.sessionId,
|
||||
rating as UserFeedbackRating,
|
||||
config.getModel(),
|
||||
config.getApprovalMode(),
|
||||
);
|
||||
|
||||
logUserFeedback(config, feedbackEvent);
|
||||
}
|
||||
|
||||
// Record the timestamp when feedback dialog is submitted
|
||||
settings.setValue(
|
||||
SettingScope.User,
|
||||
'ui.feedbackLastShownTimestamp',
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
logUserFeedback(config, feedbackEvent);
|
||||
closeFeedbackDialog();
|
||||
},
|
||||
[config, sessionStats, closeFeedbackDialog],
|
||||
[closeFeedbackDialog, sessionStats.sessionId, config, settings],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -140,13 +152,15 @@ export const useFeedbackDialog = ({
|
||||
// 5. Random chance (25% probability)
|
||||
// 6. Meets minimum requirements (tool calls > 10 OR user messages > 5)
|
||||
// 7. Fatigue mechanism allows showing (not shown recently across sessions)
|
||||
// 8. Not temporarily dismissed
|
||||
if (
|
||||
config.getAuthType() !== AuthType.QWEN_OAUTH ||
|
||||
!config.getUsageStatisticsEnabled() ||
|
||||
settings.merged.ui?.enableUserFeedback === false ||
|
||||
!lastMessageIsAIResponse(history) ||
|
||||
Math.random() > FEEDBACK_SHOW_PROBABILITY ||
|
||||
!meetsMinimumSessionRequirements(sessionStats)
|
||||
!meetsMinimumSessionRequirements(sessionStats) ||
|
||||
isFeedbackDismissedTemporarily
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -164,15 +178,27 @@ export const useFeedbackDialog = ({
|
||||
history,
|
||||
sessionStats,
|
||||
isFeedbackDialogOpen,
|
||||
isFeedbackDismissedTemporarily,
|
||||
openFeedbackDialog,
|
||||
settings.merged.ui?.enableUserFeedback,
|
||||
config,
|
||||
]);
|
||||
|
||||
// Reset temporary dismissal when a new AI response starts streaming
|
||||
useEffect(() => {
|
||||
if (
|
||||
streamingState === StreamingState.Responding &&
|
||||
isFeedbackDismissedTemporarily
|
||||
) {
|
||||
setIsFeedbackDismissedTemporarily(false);
|
||||
}
|
||||
}, [streamingState, isFeedbackDismissedTemporarily]);
|
||||
|
||||
return {
|
||||
isFeedbackDialogOpen,
|
||||
openFeedbackDialog,
|
||||
closeFeedbackDialog,
|
||||
temporaryCloseFeedbackDialog,
|
||||
submitFeedback,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user