Mission control: a place to ask g1t, shown and not on yet, with the actions under it
Under the greeting, a composer for asking g1t a question, having it plan work into issues for agents, or starting work: a mode, a repository scope, context and @-mentions, and send. Every control is disabled with a tooltip saying asking g1t is coming later. Under it, the actions that work today: Put an agent on it / New issue, a new Plan work menu (a project's plans, which turn an outcome into issues agents take in order), and Review what needs you.
2 files+106−130/2 viewed
| 1 | + | import { ArrowUp, AtSign, BookMarked, ChevronDown, MessageSquare, Plus, Sparkles } from "lucide-react"; | |
| 2 | + | import type { ReactNode } from "react"; | |
| 3 | + | ||
| 4 | + | import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; | |
| 5 | + | ||
| 6 | + | /** | |
| 7 | + | * Mission control's place to ask g1t for anything: a question, a plan it | |
| 8 | + | * splits into issues for agents, or work started straight away. Shown so | |
| 9 | + | * it is clear where this will be, and not usable yet: nothing here sends, | |
| 10 | + | * and every control says so. Until then the actions under it do the work. | |
| 11 | + | */ | |
| 12 | + | export function AskComposer({ children }: { children?: ReactNode }) { | |
| 13 | + | return ( | |
| 14 | + | <section aria-label="Ask g1t" className="space-y-3"> | |
| 15 | + | <div aria-disabled="true" className="rounded-xl border border-line bg-surface opacity-80"> | |
| 16 | + | <label htmlFor="ask-g1t" className="sr-only"> | |
| 17 | + | Ask g1t (coming later) | |
| 18 | + | </label> | |
| 19 | + | <textarea | |
| 20 | + | id="ask-g1t" | |
| 21 | + | disabled | |
| 22 | + | rows={2} | |
| 23 | + | placeholder="Ask anything, plan something out, or describe work to start. Type @ to add context." | |
| 24 | + | className="block w-full cursor-not-allowed resize-none bg-transparent px-4 pt-3.5 pb-2 text-sm text-fg placeholder:text-muted focus:outline-none" | |
| 25 | + | /> | |
| 26 | + | <div className="flex flex-wrap items-center gap-2 px-3 pb-3"> | |
| 27 | + | <Soon label="Mode"> | |
| 28 | + | <MessageSquare size={14} /> Ask <ChevronDown size={13} className="text-faint" /> | |
| 29 | + | </Soon> | |
| 30 | + | <Soon label="Scope"> | |
| 31 | + | <BookMarked size={14} /> All repositories <ChevronDown size={13} className="text-faint" /> | |
| 32 | + | </Soon> | |
| 33 | + | <Soon label="Add context" icon> | |
| 34 | + | <Plus size={15} /> | |
| 35 | + | </Soon> | |
| 36 | + | <Soon label="Mention" icon> | |
| 37 | + | <AtSign size={14} /> | |
| 38 | + | </Soon> | |
| 39 | + | <span className="ml-auto inline-flex items-center gap-1.5 text-xs text-faint"> | |
| 40 | + | <Sparkles size={13} className="text-merged" aria-hidden="true" /> | |
| 41 | + | Coming later | |
| 42 | + | </span> | |
| 43 | + | <Soon label="Send" icon> | |
| 44 | + | <ArrowUp size={15} /> | |
| 45 | + | </Soon> | |
| 46 | + | </div> | |
| 47 | + | </div> | |
| 48 | + | {children && <div className="flex flex-wrap items-center gap-2">{children}</div>} | |
| 49 | + | </section> | |
| 50 | + | ); | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | /** One of the composer's controls: there, disabled, with a tooltip saying why. */ | |
| 54 | + | function Soon({ label, icon, children }: { label: string; icon?: boolean; children: ReactNode }) { | |
| 55 | + | return ( | |
| 56 | + | <Tooltip> | |
| 57 | + | <TooltipTrigger asChild> | |
| 58 | + | <span tabIndex={0} className="inline-flex rounded-md outline-none focus-visible:ring-2 focus-visible:ring-accent"> | |
| 59 | + | <button | |
| 60 | + | type="button" | |
| 61 | + | disabled | |
| 62 | + | aria-label={`${label} (coming later)`} | |
| 63 | + | className={`pointer-events-none inline-flex h-8 items-center gap-1.5 rounded-md border border-line text-sm text-muted ${icon ? "w-8 justify-center" : "px-2.5"}`} | |
| 64 | + | > | |
| 65 | + | {children} | |
| 66 | + | </button> | |
| 67 | + | </span> | |
| 68 | + | </TooltipTrigger> | |
| 69 | + | <TooltipContent>Asking g1t is coming later</TooltipContent> | |
| 70 | + | </Tooltip> | |
| 71 | + | ); | |
| 72 | + | } |
| 1 | − | import { ArrowDownWideNarrow, ArrowRight, ArrowUpRight, Check, ChevronDown, ChevronRight, Plus, Sparkles } from "lucide-react"; | |
| 1 | + | import { ArrowDownWideNarrow, ArrowRight, ArrowUpRight, Check, ChevronDown, ChevronRight, ListTree, Plus, Sparkles } from "lucide-react"; | |
| 2 | 2 | import { type ReactNode, useEffect, useState } from "react"; | |
| 3 | 3 | import { Link, useFetcher, useRouteLoaderData, useSearchParams } from "react-router"; | |
| 4 | 4 | ||
| 28 | 28 | } from "../lib/mission-control"; | |
| 29 | 29 | import { cn } from "../lib/cn"; | |
| 30 | 30 | import { AgentComposer, type ComposerResult } from "./agent-composer"; | |
| 31 | + | import { AskComposer } from "./ask-composer"; | |
| 31 | 32 | import { AgentSetup } from "./agent-setup"; | |
| 32 | 33 | import { InboxNeedsCard } from "./inbox"; | |
| 33 | 34 | import type { ShellData } from "./shell"; | |
| 866 | 867 | <p className="mt-1.5 text-sm text-muted" suppressHydrationWarning> | |
| 867 | 868 | <span className="text-fg-soft">{date}</span> · {loaded.summary} | |
| 868 | 869 | </p> | |
| 869 | − | </div> | |
| 870 | − | <div className="flex flex-wrap items-center gap-2"> | |
| 871 | − | {newIssue} | |
| 872 | − | {needs.length > 0 && ( | |
| 873 | − | <Link | |
| 874 | − | to={`${link({ tab: "needs", all: null })}#work`} | |
| 875 | − | preventScrollReset | |
| 876 | − | className="inline-flex items-center gap-1.5 rounded-md bg-merged px-3.5 py-2 text-sm font-semibold text-bg transition-colors hover:bg-[#c9bfff]" | |
| 877 | − | > | |
| 878 | − | Review {needs.length} that need{needs.length === 1 ? "s" : ""} you <ArrowRight size={14} /> | |
| 879 | − | </Link> | |
| 880 | − | )} | |
| 881 | 870 | </div> | |
| 882 | 871 | </header> | |
| 883 | 872 | ||
| 873 | + | {/* Asking g1t is not on yet; the actions under it do the work today. */} | |
| 874 | + | <AskComposer> | |
| 875 | + | {newIssue} | |
| 876 | + | {repos.length > 0 && ( | |
| 877 | + | <DropdownMenu> | |
| 878 | + | <DropdownMenuTrigger className="inline-flex items-center gap-1.5 rounded-md border border-line-strong px-3 py-2 text-sm font-medium text-fg/90 transition-colors hover:bg-raised hover:text-fg"> | |
| 879 | + | <ListTree size={14} /> Plan work | |
| 880 | + | </DropdownMenuTrigger> | |
| 881 | + | <DropdownMenuContent align="start" className="max-h-80 overflow-y-auto"> | |
| 882 | + | <DropdownMenuLabel>Plan in which project?</DropdownMenuLabel> | |
| 883 | + | {repos.map((repo) => ( | |
| 884 | + | <DropdownMenuItem key={repo.name} asChild> | |
| 885 | + | <Link to={`/${repo.namespace}/${repo.name}/plans`}> | |
| 886 | + | <Avatar name={repo.name} size={18} square /> | |
| 887 | + | {repo.name} | |
| 888 | + | </Link> | |
| 889 | + | </DropdownMenuItem> | |
| 890 | + | ))} | |
| 891 | + | </DropdownMenuContent> | |
| 892 | + | </DropdownMenu> | |
| 893 | + | )} | |
| 894 | + | {needs.length > 0 && ( | |
| 895 | + | <Link | |
| 896 | + | to={`${link({ tab: "needs", all: null })}#work`} | |
| 897 | + | preventScrollReset | |
| 898 | + | className="inline-flex items-center gap-1.5 rounded-md bg-merged px-3.5 py-2 text-sm font-semibold text-bg transition-colors hover:bg-[#c9bfff]" | |
| 899 | + | > | |
| 900 | + | Review {needs.length} that need{needs.length === 1 ? "s" : ""} you <ArrowRight size={14} /> | |
| 901 | + | </Link> | |
| 902 | + | )} | |
| 903 | + | </AskComposer> | |
| 904 | + | ||
| 884 | 905 | {starting && <GetStarted steps={steps} />} | |
| 885 | 906 | ||
| 886 | 907 | <section |