Repository: gradio-app/gradio

Issue:
Need to rework the 'static while pending' behaviour.
### Describe the bug

#6157 and #6160 implemented some behaviour where components are set to static when they are waiting for input (see #4733 ); this is very problematic (especially for inputs that are outputs):

This works well for form inputs, but for many components, setting them to static destroys the core component and reinitialises a new one. This causes specific issues:

- Data loss: any state held only in that component (specific props, other internal state) is completely lost and cannot be recreated.
- Performance: Some components are very expensive to initialise, and a lot of work has gone into making sure the minimum work happens when the data is updated.
- StatusTrackers: Any components implementing different status trackers for static and interactive mode will not work correctly. This is especially confusing for custom component authors unfamiliar with the internals. The static StatusTracker is the only one that will be displayed because the component will always be in 'static' mode.

I'm not sure this needs to be addressed at an architectural level. Components are best placed to know how they should 'disabled' themselves when events are pending (just as they are best placed to decide what their loading states should look like) and that information is already passed down via the `loading_status` prop. For form elements, as mentioned in the issue, they should set their status to `disabled`, maybe with an indicator, to resolve the issue. For components with a full-screen status tracker, it isn't an issue. Custom components can choose their own route.



### Have you searched existing issues?  🔎

- [X] I have searched and found no existing issues

### Reproduction

```python
import gradio as gr

```


### Screenshot

_No response_

### Logs

_No response_

### System Info

```shell
-
```


### Severity

I can work around it


Discussion:

Resolving patch:
diff --git a/demo/chatinterface_streaming_echo/run.py b/demo/chatinterface_streaming_echo/run.py
--- a/demo/chatinterface_streaming_echo/run.py
+++ b/demo/chatinterface_streaming_echo/run.py
@@ -1,10 +1,12 @@
 import time
 import gradio as gr
 
+
 def slow_echo(message, history):
     for i in range(len(message)):
         time.sleep(0.05)
-        yield "You typed: " + message[: i+1]
+        yield "You typed: " + message[: i + 1]
+
 
 demo = gr.ChatInterface(slow_echo).queue()
 


Test patch:
diff --git a/demo/test_chatinterface_streaming_echo/run.ipynb b/demo/test_chatinterface_streaming_echo/run.ipynb
new file mode 100644
--- /dev/null
+++ b/demo/test_chatinterface_streaming_echo/run.ipynb
@@ -0,0 +1 @@
+{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: test_chatinterface_streaming_echo"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "runs = 0\n", "\n", "\n", "def slow_echo(message, history):\n", "    global runs  # i didn't want to add state or anything to this demo\n", "    runs = runs + 1\n", "    for i in range(len(message)):\n", "        yield f\"Run {runs} - You typed: \" + message[: i + 1]\n", "\n", "\n", "demo = gr.ChatInterface(slow_echo).queue()\n", "\n", "if __name__ == \"__main__\":\n", "    demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
\ No newline at end of file
diff --git a/demo/test_chatinterface_streaming_echo/run.py b/demo/test_chatinterface_streaming_echo/run.py
new file mode 100644
--- /dev/null
+++ b/demo/test_chatinterface_streaming_echo/run.py
@@ -0,0 +1,16 @@
+import gradio as gr
+
+runs = 0
+
+
+def slow_echo(message, history):
+    global runs  # i didn't want to add state or anything to this demo
+    runs = runs + 1
+    for i in range(len(message)):
+        yield f"Run {runs} - You typed: " + message[: i + 1]
+
+
+demo = gr.ChatInterface(slow_echo).queue()
+
+if __name__ == "__main__":
+    demo.launch()
diff --git a/js/app/test/chatinterface_streaming_echo.spec.ts b/js/app/test/chatinterface_streaming_echo.spec.ts
deleted file mode 100644
--- a/js/app/test/chatinterface_streaming_echo.spec.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { test, expect } from "@gradio/tootils";
-
-test("chatinterface works with streaming functions and all buttons behave as expected", async ({
-	page
-}) => {
-	const submit_button = await page.getByRole("button", { name: "Submit" });
-	const retry_button = await page.getByRole("button", { name: "🔄 Retry" });
-	const undo_button = await page.getByRole("button", { name: "↩️ Undo" });
-	const clear_button = await page.getByRole("button", { name: "🗑️ Clear" });
-	const textbox = await page.getByPlaceholder("Type a message...");
-
-	await textbox.fill("hello");
-	await submit_button.click();
-	await expect(textbox).toHaveValue("");
-	const bot_message_0 = await page.locator(".bot.message").nth(0);
-	await expect(bot_message_0).toContainText("You typed: hello");
-
-	await textbox.fill("hi");
-	await submit_button.click();
-	await expect(textbox).toHaveValue("");
-	const bot_message_1 = await page.locator(".bot").nth(1);
-	await expect(bot_message_1).toContainText("You typed: hi");
-
-	await retry_button.click();
-	await expect(textbox).toHaveValue("");
-	await expect(page.locator(".bot").nth(1)).toContainText("You typed: hi");
-
-	await undo_button.click();
-	await expect
-		.poll(async () => page.locator(".message.bot").count(), { timeout: 5000 })
-		.toBe(1);
-	await expect(textbox).toHaveValue("hi");
-
-	await textbox.fill("salaam");
-	await submit_button.click();
-	await expect(textbox).toHaveValue("");
-	await expect(page.locator(".bot").nth(1)).toContainText("You typed: salaam");
-
-	await clear_button.click();
-	await expect
-		.poll(async () => page.locator(".bot.message").count(), { timeout: 5000 })
-		.toBe(0);
-});
diff --git a/js/app/test/test_chatinterface_streaming_echo.spec.ts b/js/app/test/test_chatinterface_streaming_echo.spec.ts
new file mode 100644
--- /dev/null
+++ b/js/app/test/test_chatinterface_streaming_echo.spec.ts
@@ -0,0 +1,67 @@
+import { test, expect } from "@gradio/tootils";
+
+test("chatinterface works with streaming functions and all buttons behave as expected", async ({
+	page
+}) => {
+	const submit_button = page.getByRole("button", { name: "Submit" });
+	const retry_button = page.getByRole("button", { name: "🔄 Retry" });
+	const undo_button = page.getByRole("button", { name: "↩️ Undo" });
+	const clear_button = page.getByRole("button", { name: "🗑️ Clear" });
+	const textbox = page.getByPlaceholder("Type a message...");
+
+	await textbox.fill("hello");
+	await submit_button.click();
+
+	await expect(textbox).toHaveValue("");
+	const expected_text_el_0 = page.locator(".bot  p", {
+		hasText: "Run 1 - You typed: hello"
+	});
+	await expect(expected_text_el_0).toBeVisible();
+	await expect
+		.poll(async () => page.locator(".bot.message").count(), { timeout: 2000 })
+		.toBe(1);
+
+	await textbox.fill("hi");
+	await submit_button.click();
+	await expect(textbox).toHaveValue("");
+	const expected_text_el_1 = page.locator(".bot p", {
+		hasText: "Run 2 - You typed: hi"
+	});
+	await expect(expected_text_el_1).toBeVisible();
+	await expect
+		.poll(async () => page.locator(".bot.message").count(), { timeout: 2000 })
+		.toBe(2);
+
+	await undo_button.click();
+	await expect
+		.poll(async () => page.locator(".message.bot").count(), { timeout: 5000 })
+		.toBe(1);
+	await expect(textbox).toHaveValue("hi");
+
+	await retry_button.click();
+	const expected_text_el_2 = page.locator(".bot p", {
+		hasText: "Run 3 - You typed: hello"
+	});
+	expect(textbox).toHaveValue("");
+	await expect(expected_text_el_2).toBeVisible();
+
+	await expect
+		.poll(async () => page.locator(".message.bot").count(), { timeout: 5000 })
+		.toBe(1);
+
+	await textbox.fill("hi");
+	await submit_button.click();
+	await expect(textbox).toHaveValue("");
+	const expected_text_el_3 = page.locator(".bot p", {
+		hasText: "Run 4 - You typed: hi"
+	});
+	await expect(expected_text_el_3).toBeVisible();
+	await expect
+		.poll(async () => page.locator(".bot.message").count(), { timeout: 2000 })
+		.toBe(2);
+
+	await clear_button.click();
+	await expect
+		.poll(async () => page.locator(".bot.message").count(), { timeout: 5000 })
+		.toBe(0);
+});