chat: redesign chat input stop/send buttons

- stop button replaces send button in footer instead of overlay
- floating queue button above stop when input has content
- buttons same size, stop icon larger for emphasis
This commit is contained in:
Bohdan Triapitsyn
2026-02-01 22:43:46 +02:00
parent b441d27e99
commit d846f039e6
3 changed files with 141 additions and 57 deletions
+24 -13
View File
@@ -66,6 +66,8 @@ import { useSessionStore } from '@/stores/useSessionStore';
import { useConfigStore } from '@/stores/useConfigStore';
import { useContextStore } from '@/stores/contextStore';
import { useUIStore } from '@/stores/useUIStore';
import { useMessageQueueStore } from '@/stores/messageQueueStore';
import { useCurrentSessionActivity } from '@/hooks/useSessionActivity';
import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore';
import { opencodeClient } from '@/lib/opencode/client';
import { useDirectoryShowHidden } from '@/lib/directoryShowHidden';
@@ -358,6 +360,9 @@ export const FilesView: React.FC = () => {
const setMainTabGuard = useUIStore((state) => state.setMainTabGuard);
const inputBarOffset = useUIStore((state) => state.inputBarOffset);
const isKeyboardOpen = useUIStore((state) => state.isKeyboardOpen);
const queueModeEnabled = useMessageQueueStore((state) => state.queueModeEnabled);
const addToQueue = useMessageQueueStore((state) => state.addToQueue);
const { phase: sessionPhase } = useCurrentSessionActivity();
// Global mouseup to end drag selection
@@ -467,19 +472,25 @@ export const FilesView: React.FC = () => {
setLineSelection(null);
setActiveMainTab('chat');
void sendMessage(
message,
effectiveProviderId,
effectiveModelId,
sessionAgent,
undefined,
undefined,
undefined,
effectiveVariant
).catch((e) => {
console.error('Failed to send comment', e);
});
}, [lineSelection, commentText, selectedFile, fileContent, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, extractSelectedCode, sendMessage, setActiveMainTab, getSessionAgentSelection, getAgentModelForSession, getAgentModelVariantForSession]);
// Check if should queue instead of send
const canQueue = sessionPhase !== 'idle';
if (queueModeEnabled && canQueue) {
addToQueue(currentSessionId, { content: message });
} else {
void sendMessage(
message,
effectiveProviderId,
effectiveModelId,
sessionAgent,
undefined,
undefined,
undefined,
effectiveVariant
).catch((e) => {
console.error('Failed to send comment', e);
});
}
}, [lineSelection, commentText, selectedFile, fileContent, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, extractSelectedCode, sendMessage, setActiveMainTab, getSessionAgentSelection, getAgentModelForSession, getAgentModelVariantForSession, queueModeEnabled, sessionPhase, addToQueue]);
const mapDirectoryEntries = React.useCallback((dirPath: string, entries: Array<{ name: string; path: string; isDirectory: boolean }>): FileNode[] => {
const nodes = entries
@@ -15,6 +15,8 @@ import { useSessionStore } from '@/stores/useSessionStore';
import { useConfigStore } from '@/stores/useConfigStore';
import { useContextStore } from '@/stores/contextStore';
import { useUIStore } from '@/stores/useUIStore';
import { useMessageQueueStore } from '@/stores/messageQueueStore';
import { useCurrentSessionActivity } from '@/hooks/useSessionActivity';
import { useDeviceInfo } from '@/lib/device';
import { cn, getModifierLabel } from '@/lib/utils';
@@ -188,6 +190,9 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
const getSessionAgentSelection = useContextStore(state => state.getSessionAgentSelection);
const getAgentModelForSession = useContextStore(state => state.getAgentModelForSession);
const getAgentModelVariantForSession = useContextStore(state => state.getAgentModelVariantForSession);
const queueModeEnabled = useMessageQueueStore(state => state.queueModeEnabled);
const addToQueue = useMessageQueueStore(state => state.addToQueue);
const { phase: sessionPhase } = useCurrentSessionActivity();
// Update main content metrics on resize
useEffect(() => {
@@ -303,19 +308,25 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
setSelection(null);
setActiveMainTab('chat');
void sendMessage(
message,
effectiveProviderId,
effectiveModelId,
sessionAgent,
undefined,
undefined,
undefined,
effectiveVariant
).catch((e) => {
console.error('Failed to send comment', e);
});
}, [selection, commentText, original, modified, fileName, language, sendMessage, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, setActiveMainTab, getSessionAgentSelection, getAgentModelForSession, getAgentModelVariantForSession]);
// Check if should queue instead of send
const canQueue = sessionPhase !== 'idle';
if (queueModeEnabled && canQueue) {
addToQueue(currentSessionId, { content: message });
} else {
void sendMessage(
message,
effectiveProviderId,
effectiveModelId,
sessionAgent,
undefined,
undefined,
undefined,
effectiveVariant
).catch((e) => {
console.error('Failed to send comment', e);
});
}
}, [selection, commentText, original, modified, fileName, language, sendMessage, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, setActiveMainTab, getSessionAgentSelection, getAgentModelForSession, getAgentModelVariantForSession, queueModeEnabled, sessionPhase, addToQueue]);
ensurePierreThemeRegistered(lightTheme);
ensurePierreThemeRegistered(darkTheme);