| 4 | 4 | | |
| 5 | 5 | | import { type AskSetup, type ChatLiveEvent, type ChatMessage, type Result } from "@g1t/contracts"; |
| 6 | 6 | | |
| 7 | | − | import { askPage, askWhere } from "../lib/ask"; |
| 7 | + | import { askPage, askWhere, browserTimeZone } from "../lib/ask"; |
| 8 | 8 | | import { anyAnswering, applyLive, fromG1t, suggestions } from "../lib/g1t-chat"; |
| 9 | 9 | | import { Conversation, ConversationContent, ConversationEmptyState, ConversationScrollButton } from "./ai-elements/conversation"; |
| 10 | 10 | | import { Message, MessageContent } from "./ai-elements/message"; |
| ⋯ |
| 17 | 17 | | import { Button } from "./ui/button"; |
| 18 | 18 | | import { Hint } from "./ui/hint"; |
| 19 | 19 | | import { Kbd } from "./ui/kbd"; |
| 20 | | − | import { Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "./ui/sheet"; |
| 20 | + | import { SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "./ui/sheet"; |
| 21 | 21 | | import { Skeleton } from "./ui/skeleton"; |
| 22 | 22 | | import { Textarea } from "./ui/textarea"; |
| 23 | 23 | | |
| ⋯ |
| 159 | 159 | | pending: true, |
| 160 | 160 | | }; |
| 161 | 161 | | setMessages((now) => [...now, pending]); |
| 162 | | − | const saved = await chatApi<ChatMessage>(slug, { intent: "post", channel_id: id, body, page: askPage(pathname) }); |
| 162 | + | const saved = await chatApi<ChatMessage>(slug, { intent: "post", channel_id: id, body, page: { ...askPage(pathname), time_zone: browserTimeZone() } }); |
| 163 | 163 | | setMessages((now) => (saved.ok ? byId([...now.filter((m) => m.id !== pendingId), saved.value]) : now.map((m) => (m.id === pendingId ? { ...m, failed: true } : m)))); |
| 164 | 164 | | // The answer arrives over the socket; should it have opened late, ask once more. |
| 165 | 165 | | if (saved.ok) setTimeout(() => void load(), 1_500); |
| ⋯ |
| 183 | 183 | | |
| 184 | 184 | | type G1tConversation = ReturnType<typeof useG1tConversation>; |
| 185 | 185 | | |
| 186 | | − | /** The top bar's Ask: the lavender pixel 1 beside its words. Opens the conversation beside the page. */ |
| 187 | | − | export function AskButton({ slug }: { slug: string }) { |
| 188 | | − | const [open, setOpen] = useState(false); |
| 186 | + | /** |
| 187 | + | * The Ask panel's sheet (components/ask-button.tsx loads it the first time |
| 188 | + | * the top bar's Ask opens, so pages don't carry it until then): the |
| 189 | + | * conversation, kept while the panel is closed. |
| 190 | + | */ |
| 191 | + | export function AskSheet({ slug, open }: { slug: string; open: boolean }) { |
| 189 | 192 | | const conversation = useG1tConversation(slug, open); |
| 190 | 193 | | const busy = anyAnswering(conversation.messages); |
| 191 | 194 | | return ( |
| 192 | | − | <Sheet open={open} onOpenChange={setOpen}> |
| 193 | | − | <Hint label="Ask g1t about your work"> |
| 194 | | − | <SheetTrigger asChild> |
| 195 | | − | <Button variant="soft" size="bar" aria-label="Ask g1t" className="gap-2 max-md:ring-0 lg:px-3"> |
| 196 | | − | <MarkGlyph className="h-3.5" /> |
| 197 | | − | <span className="max-lg:hidden">Ask</span> |
| 198 | | − | </Button> |
| 199 | | − | </SheetTrigger> |
| 200 | | − | </Hint> |
| 201 | | − | <SheetContent |
| 202 | | − | side="right" |
| 203 | | − | className="sm:max-w-xl" |
| 204 | | − | // Esc stops an answer still coming first; pressed again, it closes the panel. |
| 205 | | − | onEscapeKeyDown={(event) => { |
| 206 | | − | if (!busy) return; |
| 207 | | − | event.preventDefault(); |
| 208 | | − | void conversation.act("g1t_stop"); |
| 209 | | − | }} |
| 210 | | − | > |
| 211 | | − | <AskPanel slug={slug} open={open} conversation={conversation} /> |
| 212 | | − | </SheetContent> |
| 213 | | − | </Sheet> |
| 195 | + | <SheetContent |
| 196 | + | side="right" |
| 197 | + | className="sm:max-w-xl" |
| 198 | + | // Esc stops an answer still coming first; pressed again, it closes the panel. |
| 199 | + | onEscapeKeyDown={(event) => { |
| 200 | + | if (!busy) return; |
| 201 | + | event.preventDefault(); |
| 202 | + | void conversation.act("g1t_stop"); |
| 203 | + | }} |
| 204 | + | > |
| 205 | + | <AskPanel slug={slug} open={open} conversation={conversation} /> |
| 206 | + | </SheetContent> |
| 214 | 207 | | ); |
| 215 | 208 | | } |
| 216 | 209 | | |