Skip to content

Conversation

@PBK-B
Copy link
Contributor

@PBK-B PBK-B commented Nov 7, 2025

[ChatPromptSubmit] emits provides event externally so that external events can be consumed

πŸ”— Linked issue

The problem is explained in the description.

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

Reproduction: https://codesandbox.io/p/devbox/sweet-dirac-7hpdy2?file=%2Fapp%2Fapp.tsx

Due to the event bubble, when UChatPrompt nests UChatPromptSubmit, the onStop and onReload of UChatPromptSubmit do not consume the event, which will bubble to the onSubmit event of UChatPrompt, causing some side effects (onSubmit event is called multiple times as shown in the figure below).

ζˆͺ屏2025-11-07 15 18 03

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 7, 2025

npm i https://pkg.pr.new/@nuxt/ui@5400

commit: b82ad4e

@benjamincanac
Copy link
Member

I'm having a hard-time understand your issue here, do you have the issue in a real environment when using @ai-sdk/vue?

Also, I tried your changes locally but adding this event still logs handleSubmit when pressing the stop button πŸ€”

@PBK-B
Copy link
Contributor Author

PBK-B commented Nov 17, 2025

I'm having a hard-time understand your issue here, do you have the issue in a real environment when using @ai-sdk/vue?

Also, I tried your changes locally but adding this event still logs handleSubmit when pressing the stop button πŸ€”

@benjamincanac This has nothing to do with @ai-sdk/vue. In my submission, I did not directly consume the click event (to avoid destructive updates). Instead, I chose to send the obtained event: MouseEvent through stop and reload emits in ChatPromptSubmit, so that developers can decide whether they need to call event.preventDefault to prevent the browser from triggering the submit event of the form.

To put it simply, onClick implemented in ChatPromptSubmit will call stop and reload based on the message status but will not send the event instance. Developers cannot implement operations such as preventing bubbling and consuming default events in onStop and onReload implemented by themselves.

The final effect is that users will be able to call event.preventDefault in handleStop and handleReload. This will prevent handleSubmit from being triggered. You can try the following sample code.

import { defineComponent, ref } from "vue";
import { type ChatPromptSubmitProps } from "@nuxt/ui/components/ChatPromptSubmit.vue";
import { UChatPrompt, UChatPromptSubmit } from "#components";

let timer: any;
export default defineComponent({
  setup() {
    const input = ref("value");
    const status = ref<ChatPromptSubmitProps["status"]>("ready");
    const handleSubmit = (e: Event) => {
      e.preventDefault();
      console.log("handleSubmit");
      if (timer) clearTimeout(timer);
      status.value = "streaming";
      timer = setTimeout(() => {
        status.value = "error";
      }, 3600);
    };
    const handleStop = (event) => {
      console.log("handleStop");
      status.value = "ready";
      event.preventDefault(); // this is the key place
    };
    const handleReload = (event) => {
      console.log("handleReload");
      status.value = "ready";
      event.preventDefault(); // this is the key place
    };

    return () => {
      return (
        <div>
          <UChatPrompt
            modelValue={input.value}
            onSubmit={handleSubmit}
            style={{ height: "5rem", paddingLeft: "0" }}
          >
            {{
              footer: () => (
                <div class="chat_prompt_buttons">
                  <div style={{ flex: 1 }}></div>
                  <UChatPromptSubmit
                    status={status.value} // simulation state
                    onStop={handleStop}
                    onReload={handleReload}
                  />
                </div>
              ),
            }}
          </UChatPrompt>
        </div>
      );
    };
  },
});

@PBK-B
Copy link
Contributor Author

PBK-B commented Nov 20, 2025

@benjamincanac is there anything I can do?

@benjamincanac benjamincanac changed the title fix(ChatPromptSubmit): emits provides event externally so that external events can be consumed fix(ChatPromptSubmit): proxy event to stop and reload emits Nov 21, 2025
@benjamincanac benjamincanac merged commit 736a547 into nuxt:v4 Nov 21, 2025
16 checks passed
@rodriciru
Copy link
Contributor

I almos add a new issue here about this! -> nuxt/nuxt#33738

@rodriciru
Copy link
Contributor

OK, now @stop.prevent="stopChat" stops the prompt sending, but not @stop.stop="stopChat" (before this PR it will throw an undefined error) (I'm not using @vercel/ai-sdk as supposed, I'm creating messages manually)
But I find if I do this, the stop event effectively stops the resending of the prompt (even before this PR exists):

 function stopChat() {
    controller.value.abort()
    controller.value = new AbortController()
    setTimeout(() => { // <-- THIS!!!
      chatStatus.value = 'ready'
    }, 0)
    toast.add({ title: 'Consulta detenida', icon: 'i-lucide-stop-circle' })
  }

Other values of chatStatus.value different from ready will not resend the prompt. It only happens when the value is ready

@rodriciru
Copy link
Contributor

To summarize:

  1. Nuxt UI 4.2.0

    • stop.prevent OR stop.stop β†’ Undefined error
    • chatStatus.value different from ready will stop the prompt resending
    • chatStatus.value = "ready" inside a setTimeout even with value 0, will stop the prompt resending
  2. Nuxt UI 4.2.1

    • stop.prevent OR stop.stop β†’ No error, but only stop.prevent will actually stop the prompt resending, no need for setTimeout hack
    • Only stop event without Event Modifiers need the manually event.preventDefault() OR the setTimeout hack

@PBK-B
Copy link
Contributor Author

PBK-B commented Nov 24, 2025

To summarize:

  1. Nuxt UI 4.2.0

    • stop.prevent OR stop.stop β†’ Undefined error
    • chatStatus.value different from ready will stop the prompt resending
    • chatStatus.value = "ready" inside a setTimeout even with value 0, will stop the prompt resending
  2. Nuxt UI 4.2.1

    • stop.prevent OR stop.stop β†’ No error, but only stop.prevent will actually stop the prompt resending, no need for setTimeout hack
    • Only stop event without Event Modifiers need the manually event.preventDefault() OR the setTimeout hack

@rodriciru Yes, you are right. I seem to have to think about the syntax of stop.prevent OR stop.stop. I would be very grateful if you could help me fix it and submit the PR.

@rodriciru
Copy link
Contributor

To summarize:

  1. Nuxt UI 4.2.0

    • stop.prevent OR stop.stop β†’ Undefined error
    • chatStatus.value different from ready will stop the prompt resending
    • chatStatus.value = "ready" inside a setTimeout even with value 0, will stop the prompt resending
  2. Nuxt UI 4.2.1

    • stop.prevent OR stop.stop β†’ No error, but only stop.prevent will actually stop the prompt resending, no need for setTimeout hack
    • Only stop event without Event Modifiers need the manually event.preventDefault() OR the setTimeout hack

@rodriciru Yes, you are right. I seem to have to think about the syntax of stop.prevent OR stop.stop. I would be very grateful if you could help me fix it and submit the PR.

Fix what? stop.prevent OR stop.stop now are doing his thing without errors, but only stop.prevent has the desired effect (stop the bubbling up) in this case. I have no idea of vue or nuxt internals

@PBK-B
Copy link
Contributor Author

PBK-B commented Nov 26, 2025

@rodriciru my understanding is that when you use the @reload.stop syntax, the event triggering needs to call e.stopPropagation() by default without the need to call it manually in handleReload to prevent the event from bubbling?

 <UChatPromptSubmit
    :status="status"
    @stop="handleStop"
    @reload.stop="handleReload"
 />

I tested that the above code does not meet expectations in Nuxt UI 4.2.1. This may be related to vue. https://github.com/vuejs/core/blob/25ebe3a42cd80ac0256355c2740a0258cdd7419d/packages/runtime-dom/src/directives/vOn.ts#L52-L53 Maybe you can help investigate the reason?

@rodriciru
Copy link
Contributor

rodriciru commented Nov 29, 2025 via email

@PBK-B PBK-B deleted the bin/fix_chat_prompt_submit_emits_event branch November 30, 2025 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants