Skip to content

Conversation

@marcosmarf27
Copy link

@marcosmarf27 marcosmarf27 commented Nov 21, 2025

🎯 Objective

Implement functionality that allows users to interrupt chat message generation in real-time through a Stop button.

✨ Changes Implemented

New Component

  • StopButton (src/components/buttons/StopButton.tsx): New button component with stop icon

Interruption Logic

  • Added messageAbortController signal to manage AbortController
  • Modified fetchResponseFromEventStream() to support interruption via AbortController
  • New stopMessage() function that safely aborts SSE requests
  • Automatic controller cleanup in closeResponse()

User Interface

  • Stop button replaces Send button during loading
  • Automatic transition based on isLoading state
  • Added isLoading and onStopMessage props to TextInput component

🔧 How It Works

  1. User sends message → Button changes from "Send" to "Stop"
  2. During response generation → User can click "Stop" at any time
  3. When clicking "Stop" → AbortController.abort() interrupts the SSE request
  4. After interruption → Interface returns to normal state, ready for new message

🧪 Testing

  • ✅ Build compiled successfully
  • ✅ Functionality tested locally
  • ✅ Integration with existing SSE streaming system
  • ✅ Proper resource cleanup after abort

📦 Modified Files

  • src/components/buttons/StopButton.tsx (new)
  • src/components/Bot.tsx
  • src/components/inputs/textInput/components/TextInput.tsx
  • dist/ (build files)

🎨 Preview

The Stop button appears automatically when a message is being generated:

  • Normal state: "Send" button visible
  • During generation: "Stop" button visible
  • After stop/completion: Returns to "Send" button

🔍 Technical Notes

  • Uses native JavaScript AbortController
  • Compatible with @microsoft/fetch-event-source library
  • Does not affect other existing features (TTS, uploads, etc)
  • Maintains existing project code patterns

📸 User Experience

Users now have full control over message generation, improving the chat experience by allowing them to:

  • Stop responses that are too long
  • Cancel incorrect or unwanted queries
  • Quickly restart with a new message

Implementada funcionalidade completa de stop para permitir que usuários
interrompam a geração de mensagens do chat em tempo real.

Mudanças:
- Criado novo componente StopButton para UI
- Adicionado AbortController para gerenciar interrupção de mensagens
- Integrado AbortController em fetchResponseFromEventStream()
- Botão Stop substituiu botão Send durante carregamento
- Função stopMessage() para abortar requisições de forma segura

O botão Stop aparece automaticamente quando uma mensagem está sendo
gerada e volta para o botão Send quando a geração é concluída.
Atualizar yarn.lock e adicionar package-lock.json após instalação
de dependências necessárias para build do projeto.
@gemini-code-assist
Copy link

Summary of Changes

Hello @marcosmarf27, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

Este pull request introduz uma funcionalidade crucial que permite aos usuários interromper a geração de mensagens em tempo real em um chat, através da adição de um botão 'Stop'. Essa melhoria visa proporcionar maior controle e uma experiência de usuário mais fluida, especialmente em cenários onde as respostas do chat podem ser longas ou indesejadas. A implementação utiliza o AbortController para gerenciar o cancelamento de requisições de streaming de eventos, garantindo uma interrupção eficiente e segura.

Highlights

  • Novo Componente StopButton: Adição de um botão dedicado para interromper a geração de mensagens, que substitui o botão 'Send' durante o carregamento.
  • Interrupção de Requisições SSE: Implementação do AbortController para permitir o cancelamento de requisições de streaming de eventos do servidor em tempo real.
  • Lógica de Controle de Interrupção: Adicionado um estado messageAbortController para gerenciar o AbortController, com uma nova função stopMessage para abortar requisições e limpeza automática do controller.
  • Integração com TextInput: O componente TextInput agora aceita as propriedades isLoading e onStopMessage para gerenciar o estado do botão e a ação de parada.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Este pull request adiciona uma funcionalidade útil para interromper a geração de mensagens. A implementação geral usando AbortController está correta, mas identifiquei alguns pontos para melhoria. O mais crítico é uma rejeição de promessa não tratada que ocorre ao abortar a requisição, o que pode levar a instabilidade. Também há algumas simplificações possíveis nos componentes e no gerenciamento de estado, como a remoção de props e estados não utilizados. Por fim, fiz sugestões para melhorar a clareza e a acessibilidade do novo componente StopButton.

const controller = messageAbortController();
if (controller) {
setIsMessageStopping(true);
controller.abort();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

A chamada controller.abort() fará com que a promessa retornada por fetchEventSource seja rejeitada com um AbortError. Como a chamada para fetchResponseFromEventStream em handleSubmit não possui um bloco .catch(), isso resultará em uma rejeição de promessa não tratada (unhandled promise rejection) no console.

Embora a limpeza síncrona em stopMessage funcione visualmente, é crucial tratar a rejeição para evitar erros e garantir um código mais robusto. A abordagem ideal seria adicionar um .catch() à chamada de fetchResponseFromEventStream e mover a lógica de limpeza para lá, tratando especificamente o AbortError.

Comment on lines 4 to 15
type StopButtonProps = {
buttonColor?: string;
isDisabled?: boolean;
isLoading?: boolean;
disableIcon?: boolean;
} & JSX.ButtonHTMLAttributes<HTMLButtonElement>;

export const StopButton = (props: StopButtonProps) => {
return (
<button
type="button"
disabled={props.isDisabled || props.isLoading}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

As propriedades isLoading e disableIcon em StopButtonProps não são utilizadas ou podem causar confusão. A propriedade isLoading na lógica disabled é contra-intuitiva para um botão de parada, que deve ser clicável durante o carregamento. A propriedade disableIcon não é utilizada.

Sugiro simplificar as props e a lógica disabled. Além disso, como o botão não renderiza texto, um aria-label seria bom para acessibilidade.

Suggested change
type StopButtonProps = {
buttonColor?: string;
isDisabled?: boolean;
isLoading?: boolean;
disableIcon?: boolean;
} & JSX.ButtonHTMLAttributes<HTMLButtonElement>;
export const StopButton = (props: StopButtonProps) => {
return (
<button
type="button"
disabled={props.isDisabled || props.isLoading}
type StopButtonProps = {
buttonColor?: string;
isDisabled?: boolean;
} & JSX.ButtonHTMLAttributes<HTMLButtonElement>;
export const StopButton = (props: StopButtonProps) => {
return (
<button
type="button"
aria-label="Interromper geração da mensagem"
disabled={props.isDisabled}


const [isChatFlowAvailableToStream, setIsChatFlowAvailableToStream] = createSignal(false);
const [chatId, setChatId] = createSignal('');
const [isMessageStopping, setIsMessageStopping] = createSignal(false);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

O estado isMessageStopping é definido aqui, mas seu valor não é utilizado em nenhum lugar do componente. Recomendo remover esta declaração para limpar o código.

const stopMessage = () => {
const controller = messageAbortController();
if (controller) {
setIsMessageStopping(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

A função setIsMessageStopping é chamada aqui, mas o estado isMessageStopping não é utilizado em nenhum outro lugar. Recomendo remover esta linha, assim como a declaração do estado.

Comment on lines 231 to 234
on:click={props.onStopMessage}
>
<span style={{ 'font-family': 'Poppins, sans-serif' }}>Stop</span>
</StopButton>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

O componente StopButton não renderiza seus filhos, então o <span>Stop</span> não será exibido. Para manter a consistência com o SendButton (que é apenas um ícone), sugiro remover o span e tornar o componente auto-fechado. A acessibilidade deve ser tratada no próprio componente StopButton com um aria-label.

            on:click={props.onStopMessage} />

@marcosmarf27 marcosmarf27 changed the title Adicionar botão Stop para interromper geração de mensagens feat: Add Stop button to interrupt message generation Nov 21, 2025
Improvements based on Gemini Code Assist review:

- Simplified StopButton props by removing unused isLoading and disableIcon
- Added aria-label for accessibility
- Removed unused isMessageStopping state
- Added proper error handling for AbortController rejection
- Removed non-rendered children span from StopButton usage
- Handle AbortError silently as it's an expected user action

These changes improve code maintainability, accessibility, and prevent
unhandled promise rejections in the console.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants