feat(transcription): add shared voice input support (#4232)
* feat(webui): add voice transcription input * feat(webui): render ANSI output in code blocks * refactor(webui): isolate voice recorder logic * refactor(transcription): keep websocket ingress thin * refactor(transcription): resolve channel audio settings on demand * style(webui): neutralize voice waveform color * feat(webui): add voice input tooltip * feat(webui): add voice input keyboard shortcut * fix(webui): distinguish voice shortcut platforms * fix(webui): place voice button after model selector * refactor(webui): share voice hold recording helpers * fix(desktop): allow microphone voice input * fix(webui): stabilize token usage month labels * feat(webui): show voice input on settings overview * fix(webui): label voice capability as recognition * fix(webui): align capability overview status * refactor(webui): isolate transcription socket handling * fix(webui): soften silent voice waveform * refactor(audio): clarify transcription service location * docs(transcription): clarify audio and provider boundaries * fix(exec): reduce session output polling flake
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { CodeBlock } from "@/components/CodeBlock";
|
||||
@@ -87,6 +88,64 @@ describe("CodeBlock", () => {
|
||||
expect(screen.getByText("const value = 1;")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders ANSI output without mounting the syntax highlighter", () => {
|
||||
render(
|
||||
<ThemeProvider theme="dark">
|
||||
<CodeBlock
|
||||
language="ansi"
|
||||
code={"\x1b[32mPASS\x1b[0m <script>alert(1)</script>"}
|
||||
/>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("highlighted-code")).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId("ansi-code")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("ansi-code").closest(".not-prose")).toBeTruthy();
|
||||
expect(screen.getByText("ansi")).toBeInTheDocument();
|
||||
expect(screen.getByText("PASS")).toHaveStyle({ color: "#0dbc79" });
|
||||
expect(screen.getByText("<script>alert(1)</script>")).toBeInTheDocument();
|
||||
expect(document.querySelector("script")).toBeNull();
|
||||
});
|
||||
|
||||
it("detects ANSI sequences in regular code blocks", () => {
|
||||
render(
|
||||
<ThemeProvider theme="light">
|
||||
<CodeBlock
|
||||
language="text"
|
||||
code={"\x1b[38;2;35;209;139mtruecolor\x1b[0m"}
|
||||
/>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("highlighted-code")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("truecolor")).toHaveStyle({
|
||||
color: "rgb(35, 209, 139)",
|
||||
});
|
||||
});
|
||||
|
||||
it("copies ANSI output as clean text", async () => {
|
||||
const user = userEvent.setup();
|
||||
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||
Object.defineProperty(navigator, "clipboard", {
|
||||
configurable: true,
|
||||
value: { writeText },
|
||||
});
|
||||
|
||||
try {
|
||||
render(
|
||||
<ThemeProvider theme="dark">
|
||||
<CodeBlock language="ansi" code={"\x1b[32mPASS\x1b[0m"} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /copy/i }));
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith("PASS");
|
||||
} finally {
|
||||
Reflect.deleteProperty(navigator, "clipboard");
|
||||
}
|
||||
});
|
||||
|
||||
it("reads theme from context without creating per-block observers", async () => {
|
||||
const originalMutationObserver = globalThis.MutationObserver;
|
||||
const observer = vi.fn();
|
||||
|
||||
Reference in New Issue
Block a user