Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

104 linesCodeBlameRaw
1/**
2 * Home's way into Ask (components/ask-composer): the box at the top of the
3 * page, Summarize on what happened in the span, and Explain on each change
4 * that landed. Each hands its question to the top bar's Ask panel
5 * (lib/ask.ts askG1t), which asks g1t in the viewer's direct message with
6 * it, so the answer streams there and stays in Chat.
7 */
8import { ArrowUp, Sparkles } from "lucide-react";
9import { type FormEvent, type KeyboardEvent, useState } from "react";
10
11import { askG1t } from "../lib/ask";
12import { type LandedChange, type Span, explainQuestion, homeStarters, summarizeQuestion } from "../lib/home";
13import { Suggestion, Suggestions } from "./ai-elements/suggestion";
14import { MarkGlyph } from "./logo";
15import { Button } from "./ui/button";
16import { Hint } from "./ui/hint";
17import { Textarea } from "./ui/textarea";
18
19type AskSpan = Pick<Span, "key" | "note" | "words" | "from">;
20
21/** The top of Home: what are we building today, a box that asks g1t, and a few things to ask first. */
22export function HomeAsk({ span, tz }: { span: AskSpan; tz: string | null }) {
23 const [draft, setDraft] = useState("");
24 const send = () => {
25 if (!draft.trim()) return;
26 askG1t(draft);
27 setDraft("");
28 };
29 const submit = (event: FormEvent) => {
30 event.preventDefault();
31 send();
32 };
33 const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
34 if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
35 event.preventDefault();
36 send();
37 }
38 };
39 return (
40 <section aria-labelledby="home-ask" className="mx-auto flex w-full max-w-3xl flex-col items-center gap-5 pt-4 pb-2 md:pt-8">
41 <h1 id="home-ask" className="text-center text-[1.75rem] leading-tight font-semibold tracking-tight md:text-[2rem]">
42 What are we building today?
43 </h1>
44 <form onSubmit={submit} className="w-full">
45 <div className="flex items-end gap-2 rounded-2xl border border-line bg-surface p-2 pl-4 shadow-sm transition-shadow focus-within:border-accent-dim focus-within:ring-2 focus-within:ring-accent/25">
46 <MarkGlyph className="mb-2.5 h-4 shrink-0" />
47 <label htmlFor="home-ask-box" className="sr-only">
48 Ask g1t
49 </label>
50 <Textarea
51 id="home-ask-box"
52 value={draft}
53 onChange={(event) => setDraft(event.target.value)}
54 onKeyDown={onKeyDown}
55 rows={1}
56 maxLength={16_000}
57 placeholder="Ask g1t what changed, what's blocked, or what to do next"
58 className="field-sizing-content max-h-40 min-h-10 resize-none border-0 bg-transparent px-1 py-2 text-[0.9375rem] shadow-none focus-visible:ring-0"
59 />
60 <Hint label="Ask (Enter)">
61 <Button type="submit" variant="accent" size="icon-sm" disabled={!draft.trim()} aria-label="Ask" className="mb-1">
62 <ArrowUp size={15} />
63 </Button>
64 </Hint>
65 </div>
66 </form>
67 <Suggestions className="justify-center">
68 {homeStarters(span, tz).map((starter) => (
69 <Suggestion key={starter.label} suggestion={starter.label} onPick={() => askG1t(starter.question)} />
70 ))}
71 </Suggestions>
72 </section>
73 );
74}
75
76/** Summarize what happened in the span, in plain English. */
77export function SummarizeButton({ span, tz }: { span: AskSpan; tz: string | null }) {
78 return (
79 <Hint label="g1t reads everything that happened in this span and tells you what changed">
80 <Button type="button" variant="soft" size="sm" onClick={() => askG1t(summarizeQuestion(span, tz))}>
81 <Sparkles size={14} />
82 Summarize
83 </Button>
84 </Hint>
85 );
86}
87
88/** Explain one change that landed: what it did and why, in plain English. */
89export function ExplainButton({ change }: { change: Pick<LandedChange, "kind" | "title" | "to"> }) {
90 return (
91 <Hint label="Explain this change">
92 <Button
93 type="button"
94 variant="ghost"
95 size="icon-xs"
96 aria-label={`Explain ${change.title}`}
97 onClick={() => askG1t(explainQuestion(change))}
98 className="shrink-0 text-muted hover:text-fg sm:opacity-0 sm:group-hover:opacity-100 sm:focus-visible:opacity-100"
99 >
100 <Sparkles size={13} />
101 </Button>
102 </Hint>
103 );
104}