Slice 1 polish: archive dormant code, merge work-os into primitives, add futures

- 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
This commit is contained in:
-Puter
2026-07-27 16:34:17 +05:30
parent 302fd159df
commit cc47007fa9
101 changed files with 38 additions and 93 deletions

View File

@@ -0,0 +1,40 @@
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,
}}
/>
);
};

View File

@@ -0,0 +1,56 @@
import { ConversationEmptyState } from "@code/ui/components/ai-elements/conversation";
import { Button } from "@code/ui/components/button";
import { MobileChatMark } from "@code/ui/components/mobile-chat";
import { SUGGESTIONS } from "@/lib/chat/constants";
import type { ChatConversationProps } from "@/lib/chat/types";
import { ChatMessage } from "./chat-message";
import { ChatThinkingResponse } from "./chat-thinking-response";
export const ChatConversation = ({
historyReady,
messages,
onSuggestion,
status,
}: ChatConversationProps) => {
if (historyReady && messages.length === 0) {
return (
<ConversationEmptyState className="min-h-full items-start justify-end px-0 pt-14 pb-5 text-left">
<MobileChatMark className="size-10 rounded-[12px] [&_svg]:size-5" />
<div className="space-y-1">
<h2 className="text-[22px] font-semibold tracking-[-0.035em]">
What can I help with?
</h2>
<p className="max-w-[320px] text-[14px] leading-5 text-muted-foreground">
Think, write, plan, or explore an idea with Zopu.
</p>
</div>
<div className="mt-3 grid w-full gap-2">
{SUGGESTIONS.map(([title, prompt]) => (
<Button
key={title}
className="group h-auto min-h-14 w-full flex-col items-start justify-center gap-0.5 rounded-[16px] border-[#e7e7e4] bg-[#f7f7f5] px-4 py-3 text-left shadow-none transition-colors active:bg-[#eeeeeb]"
onClick={() => onSuggestion(prompt)}
variant="outline"
>
<span className="font-medium text-foreground">{title}</span>
<span className="text-xs leading-4 font-normal text-muted-foreground whitespace-normal">
{prompt}
</span>
</Button>
))}
</div>
</ConversationEmptyState>
);
}
return (
<div className="flex min-w-0 flex-col gap-5 pb-5">
{messages.map((message) => (
<ChatMessage key={message.id} message={message} />
))}
{status === "submitted" && <ChatThinkingResponse />}
</div>
);
};

View File

@@ -0,0 +1,11 @@
import { MobileChatHeader } from "@code/ui/components/mobile-chat";
import { STATUS_COPY } from "@/lib/chat/constants";
import type { ChatHeaderProps } from "@/lib/chat/types";
export const ChatHeader = ({ status }: ChatHeaderProps) => (
<MobileChatHeader
active={status !== "connecting" && status !== "error"}
statusLabel={STATUS_COPY[status]}
/>
);

View File

@@ -0,0 +1,45 @@
import {
Conversation,
ConversationContent,
ConversationScrollButton,
} from "@code/ui/components/ai-elements/conversation";
import { useChatAgent } from "@/hooks/chat/use-chat-agent";
import { ChatComposer } from "./chat-composer";
import { ChatConversation } from "./chat-conversation";
import { ChatHeader } from "./chat-header";
export const ChatPage = () => {
const agent = useChatAgent();
const handleSendMessage = (message: string) => agent.sendMessage(message);
return (
<section className="chat-surface flex h-full min-h-0 flex-col bg-[#fefefe]">
<ChatHeader status={agent.status} />
<main aria-label="Conversation" className="min-h-0 flex-1">
<Conversation className="ai-conversation-mobile h-full">
<ConversationContent className="min-h-full w-full gap-0 px-4 pt-4 pb-6">
<ChatConversation
historyReady={agent.historyReady}
messages={agent.messages}
onSuggestion={(suggestion) => void handleSendMessage(suggestion)}
status={agent.status}
/>
</ConversationContent>
<ConversationScrollButton
aria-label="Scroll to latest message"
className="bottom-3 size-9 rounded-full border border-[#dededb] bg-[#fefefe]/95 shadow-sm backdrop-blur"
/>
</Conversation>
</main>
<ChatComposer
error={agent.error}
onSend={handleSendMessage}
status={agent.status}
/>
</section>
);
};