refactor(coach): drop non-functional mic icon from composer

The mic was rendered + disabled with a "voice arrives with iOS"
tooltip, drawing taps that did nothing. Voice input remains scheduled
for the iOS client (v1.5); the web composer no longer surfaces a
placeholder. Frees the row for the Enter-to-send hint to breathe.
This commit is contained in:
Marc-André Bombeck committed 2026-05-14 13:31:12 +02:00
1 parent 64d50fd32e
commit 70fe5a55c2
2 files changed
+17 -46

No files matched your search

@@ -11,16 +11,17 @@ function render(node: React.ReactNode, locale: "en" | "de" = "en") {
}
describe("<CoachInput>", () => {
it("mounts the textarea, mic, and send button slots", () => {
it("mounts the textarea + send button slots (mic dropped in W5)", () => {
// v1.4.22 B4 — the disclaimer moved to the sources rail; the
// composer no longer renders its own paragraph below the input.
// v1.4.25 W5 — the non-functional mic icon was removed.
const html = render(
<CoachInput value="" onChange={() => {}} onSubmit={() => {}} />,
);
expect(html).toContain('data-slot="coach-input"');
expect(html).toContain('data-slot="coach-input-textarea"');
expect(html).toContain('data-slot="coach-input-mic"');
expect(html).toContain('data-slot="coach-input-send"');
expect(html).not.toContain('data-slot="coach-input-mic"');
expect(html).not.toContain('data-slot="coach-input-disclaimer"');
expect(html).not.toContain("Coach replies are generated");
});
@@ -41,20 +42,16 @@ describe("<CoachInput>", () => {
expect(html).toContain("Frag mich etwas zu deinen Daten");
});
it("disables the mic button with the v1.5 tooltip text", () => {
it("no longer renders a mic button (W5 removed the placeholder)", () => {
// v1.4.25 W5 — the mic icon used to be rendered + disabled with a
// "voice arrives with iOS" tooltip. Marc flagged it as a click-
// trap: nothing happened on tap. The composer now drops the icon
// entirely; voice input remains a v1.5 iOS feature.
const html = render(
<CoachInput value="" onChange={() => {}} onSubmit={() => {}} />,
);
// The button carries both the disabled attribute and the
// localised v1.5 voice tooltip on aria-label. Attribute order is
// not guaranteed across React versions, so we check for both
// independently inside the same tag.
const micTag = html.match(/<button[^>]*data-slot="coach-input-mic"[^>]*>/);
expect(micTag).not.toBeNull();
expect(micTag?.[0]).toContain("disabled");
expect(micTag?.[0]).toContain(
'aria-label="Voice input arrives with the iOS app in v1.5."',
);
expect(html).not.toMatch(/data-slot="coach-input-mic"/);
expect(html).not.toContain("Voice input arrives with the iOS app");
});
it("disables the send button when the value is empty", () => {
@@ -1,23 +1,17 @@
"use client";
import { type FormEvent, type KeyboardEvent, useCallback } from "react";
import { Loader2, Mic, Send } from "lucide-react";
import { Loader2, Send } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { useTranslations } from "@/lib/i18n/context";
/**
* v1.4.20 phase B2b — Coach composer.
*
* `<textarea>` wrapped in a Dracula-styled card with a send button +
* mic placeholder. Submit fires on:
* `<textarea>` wrapped in a Dracula-styled card with a send button.
* Submit fires on:
* - Enter (no Shift) → send
* - Cmd/Ctrl + Enter → send (parity with the artboard ⌘↵ chip)
* - Shift + Enter → newline (default browser behaviour)
@@ -26,13 +20,13 @@ import { useTranslations } from "@/lib/i18n/context";
* the drawer. `disabled` flips during a streamed reply so we never
* fire two requests in parallel.
*
* Mic is rendered but disabled with a tooltip ("Voice input arrives
* with the iOS app in v1.5") because the natural shipper of voice is
* the native client, not the PWA.
*
* v1.4.22 B4: the disclaimer ("Coach replies are generated …") moved
* out of the composer and into the sources rail footer, so the
* composer stays focused on the input affordance.
*
* v1.4.25 W5: dropped the non-functional mic icon. The voice-input
* affordance ships with the iOS client; surfacing a placeholder in the
* web composer drew clicks for an action that did nothing.
*/
export interface CoachInputProps {
value: string;
@@ -104,26 +98,6 @@ export function CoachInput({
)}
/>
<div className="mt-1.5 flex items-center gap-2">
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon"
disabled
aria-label={t("insights.coach.voiceComingSoon")}
data-slot="coach-input-mic"
className="size-8"
>
<Mic className="size-4" aria-hidden="true" />
</Button>
</TooltipTrigger>
<TooltipContent>
{t("insights.coach.voiceComingSoon")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span
data-slot="coach-input-hint"
className="text-muted-foreground text-[11px]"