- Archive dormant apps (daemon, desktop, native, tui) and superseded packages/server into repos/ for reference - Archive 21 dead web components (chat page shell, work-os cluster, strays) into repos/web/; keep reused message-rendering primitives in components/chat - Merge @code/work-os into @code/primitives; work.ts absorbed via ./work subpath and barrel export; update all four importers - Add docs/futures.md tracking audio/video (blocked at Flue + provider), web layout cleanup, and message rendering quality - Repoint dev:zopu script to @code/agents (the live Flue server) - Note repos/ reference-code convention in AGENTS.md
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import { MobileChatComposer } from "@code/ui/components/mobile-chat";
|
|
import { LoaderCircle } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
|
|
import { useChatComposer } from "@/hooks/chat/use-chat-composer";
|
|
import type { ChatComposerProps } from "@/lib/chat/types";
|
|
|
|
export const ChatComposer = ({ error, onSend, status }: ChatComposerProps) => {
|
|
const busy =
|
|
status === "connecting" || status === "submitted" || status === "streaming";
|
|
const composer = useChatComposer({ busy, onSend });
|
|
|
|
let statusMessage: ReactNode;
|
|
if (busy) {
|
|
statusMessage = (
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<LoaderCircle className="size-3 animate-spin" />
|
|
{status === "connecting" ? "Preparing Zopu" : "Zopu is responding"}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<MobileChatComposer
|
|
busy={busy}
|
|
canSend={composer.canSend}
|
|
errorMessage={
|
|
error ? error.message || "Message failed to send" : undefined
|
|
}
|
|
onSubmit={composer.handleSubmit}
|
|
statusMessage={statusMessage}
|
|
textareaProps={{
|
|
...composer.inputProps,
|
|
"aria-describedby": error ? "composer-error" : undefined,
|
|
"aria-invalid": error ? true : undefined,
|
|
disabled: busy,
|
|
}}
|
|
/>
|
|
);
|
|
};
|