Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 1 | import { FileText, LoaderCircle } from "lucide-react"; |
| 2 | import { useEffect, useState } from "react"; | |
| 3 | ||
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 4 | import type { FoliosSidebar, Result } from "@g1t/contracts"; |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 5 | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 6 | import { Button } from "../ui/button"; |
| 7 | import { Card } from "../ui/card"; | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 8 | import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog"; |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 9 | import { Field, FieldError, FieldLabel } from "../ui/field"; |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 10 | import { Input } from "../ui/input"; |
| 11 | import { SelectField } from "../ui/select"; | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 12 | import { ORCHESTRATOR, type WritableSpace, type WriteUpAgent, type WriteUpWhere, defaultWhere, writableSpaces, writeUpMessage } from "../../lib/write-up"; |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 13 | |
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 14 | // "Write this up as an artifact" (docs.g1t.sh/guides/artifacts/, "Write a |
| 15 | // thread up"): | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 16 | // where it goes, a title if you have one, and which agent writes it. The |
| 17 | // kind is a doc for now. Sending posts the ask in the thread, as you, | |
| 18 | // where everyone sees it; the agent answers it as it answers any mention, | |
| 19 | // and links the artifact it made. | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 20 | |
| 21 | type Spaces = { state: "loading" } | { state: "ready"; spaces: WritableSpace[] } | { state: "failed"; message: string }; | |
| 22 | ||
| 23 | export function WriteUpDialog({ | |
| 24 | open, | |
| 25 | onOpenChange, | |
| 26 | slug, | |
| 27 | agents, | |
| 28 | link, | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 29 | shared, |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 30 | onSend, |
| 31 | }: { | |
| 32 | open: boolean; | |
| 33 | onOpenChange: (open: boolean) => void; | |
| 34 | slug: string; | |
| 35 | /** Who can write it: @g1t, then the conversation's agents (lib/write-up.ts `writeUpAgents`). */ | |
| 36 | agents: WriteUpAgent[]; | |
| 37 | /** The thread's link, cited as the source. */ | |
| 38 | link: string; | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 39 | /** A DM or a private channel: "Shared with this conversation" is offered, and chosen first. */ |
| 40 | shared: boolean; | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 41 | /** Posts the ask in the thread; answers with what went wrong, or null. */ |
| 42 | onSend: (body: string) => Promise<string | null>; | |
| 43 | }) { | |
| 44 | const [spaces, setSpaces] = useState<Spaces>({ state: "loading" }); | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 45 | const [where, setWhere] = useState<string>("private"); |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 46 | const [agent, setAgent] = useState(ORCHESTRATOR); |
| 47 | const [title, setTitle] = useState(""); | |
| 48 | const [busy, setBusy] = useState(false); | |
| 49 | const [error, setError] = useState<string | null>(null); | |
| 50 | ||
| 51 | // The spaces, read each time it opens: one may have been made meanwhile. | |
| 52 | useEffect(() => { | |
| 53 | if (!open) return; | |
| 54 | let live = true; | |
| 55 | setSpaces({ state: "loading" }); | |
| 56 | setError(null); | |
| 57 | setTitle(""); | |
| 58 | setAgent(ORCHESTRATOR); | |
| 59 | (async () => { | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 60 | let list: WritableSpace[] = []; |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 61 | try { |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 62 | const response = await fetch(`/${slug}/-/artifacts/api`); |
| 63 | const result = (await response.json()) as Result<FoliosSidebar>; | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 64 | if (!live) return; |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 65 | if (!result.ok) setSpaces({ state: "failed", message: result.error.message }); |
| 66 | else { | |
| 67 | list = writableSpaces(result.value.spaces); | |
| 68 | setSpaces({ state: "ready", spaces: list }); | |
| 69 | } | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 70 | } catch { |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 71 | if (live) setSpaces({ state: "failed", message: "Couldn't load your spaces. Private and this conversation still work." }); |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 72 | } |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 73 | if (!live) return; |
| 74 | const first = defaultWhere(shared, list); | |
| 75 | setWhere(first.kind === "space" ? first.id : first.kind); | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 76 | })(); |
| 77 | return () => { | |
| 78 | live = false; | |
| 79 | }; | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 80 | }, [open, slug, shared]); |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 81 | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 82 | const list = spaces.state === "ready" ? spaces.spaces : []; |
| 83 | const space = list.find((s) => s.id === where); | |
| 84 | const chosen: WriteUpWhere | null = where === "private" ? { kind: "private" } : where === "conversation" ? { kind: "conversation" } : space ? { kind: "space", id: space.id, name: space.name } : null; | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 85 | const writer = agents.find((a) => a.handle === agent) ?? agents[0] ?? { handle: ORCHESTRATOR, name: ORCHESTRATOR }; |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 86 | const body = chosen ? writeUpMessage({ agent: writer.handle, where: chosen, title, link }) : null; |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 87 | |
| 88 | const submit = async () => { | |
| 89 | if (!body || busy) return; | |
| 90 | setBusy(true); | |
| 91 | setError(null); | |
| 92 | const failed = await onSend(body); | |
| 93 | setBusy(false); | |
| 94 | if (failed) return setError(failed); | |
| 95 | onOpenChange(false); | |
| 96 | }; | |
| 97 | ||
| 98 | return ( | |
| 99 | <Dialog open={open} onOpenChange={onOpenChange}> | |
| 100 | <DialogContent className="max-w-md"> | |
| 101 | <DialogHeader> | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 102 | <DialogTitle>Write this up as an artifact</DialogTitle> |
| 103 | <DialogDescription>An agent turns this thread into a doc: what was decided, why, and what's next, with a link back here.</DialogDescription> | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 104 | </DialogHeader> |
| 105 | <form | |
| 106 | className="grid gap-4" | |
| 107 | onSubmit={(event) => { | |
| 108 | event.preventDefault(); | |
| 109 | void submit(); | |
| 110 | }} | |
| 111 | > | |
| 112 | <Field> | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 113 | <FieldLabel htmlFor="write-up-where">Where</FieldLabel> |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 114 | {spaces.state === "loading" ? ( |
| 115 | <div className="flex h-9 items-center gap-2 rounded-md border border-line px-3 text-sm text-faint"> | |
| 116 | <LoaderCircle size={14} className="animate-spin" aria-hidden="true" /> | |
| 117 | Loading spaces… | |
| 118 | </div> | |
| 119 | ) : ( | |
| 120 | <SelectField | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 121 | id="write-up-where" |
| 122 | value={where} | |
| 123 | onValueChange={setWhere} | |
| 124 | options={[ | |
| 125 | ...(shared ? [{ value: "conversation", label: "Shared with this conversation" }] : []), | |
| 126 | { value: "private", label: "Private (just me)" }, | |
| 127 | ...list.map((s) => ({ value: s.id, label: s.name })), | |
| 128 | ]} | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 129 | /> |
| 130 | )} | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 131 | {spaces.state === "failed" && <FieldError>{spaces.message}</FieldError>} |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 132 | </Field> |
| 133 | <Field> | |
| 134 | <FieldLabel htmlFor="write-up-title"> | |
| 135 | Title <span className="font-normal text-faint">(optional)</span> | |
| 136 | </FieldLabel> | |
| Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash. | 137 | <Input id="write-up-title" value={title} onChange={(event) => setTitle(event.target.value)} placeholder="Left to the agent" maxLength={120} autoComplete="off" /> |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 138 | </Field> |
| 139 | <Field> | |
| 140 | <FieldLabel htmlFor="write-up-agent">Written by</FieldLabel> | |
| 141 | <SelectField | |
| 142 | id="write-up-agent" | |
| 143 | value={writer.handle} | |
| 144 | onValueChange={setAgent} | |
| 145 | options={agents.map((a) => ({ value: a.handle, label: a.name === a.handle ? `@${a.handle}` : `${a.name} (@${a.handle})` }))} | |
| 146 | /> | |
| 147 | </Field> | |
| 148 | {body && ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 149 | <Card tone="plain" radius="lg" className="bg-bg/60 p-3"> |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 150 | <p className="mb-1 flex items-center gap-1.5 text-xs font-medium text-muted"> |
| 151 | <FileText size={13} aria-hidden="true" /> | |
| 152 | Posts in this thread, as you | |
| 153 | </p> | |
| 154 | <p className="text-[0.8125rem] leading-snug break-words text-fg-soft">{body}</p> | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 155 | </Card> |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 156 | )} |
| 157 | <FieldError>{error}</FieldError> | |
| 158 | <DialogFooter> | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 159 | <Button |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 160 | type="button" |
| 161 | onClick={() => onOpenChange(false)} | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 162 | variant="outline" |
| 163 | className="px-3 text-fg/90" | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 164 | > |
| 165 | Cancel | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 166 | </Button> |
| 167 | <Button | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 168 | type="submit" |
| 169 | disabled={!body || busy} | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 170 | variant="accent" |
| 171 | className="gap-1.5" | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 172 | > |
| 173 | {busy && <LoaderCircle size={14} className="animate-spin" aria-hidden="true" />} | |
| 174 | {busy ? "Asking…" : `Ask @${writer.handle}`} | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 175 | </Button> |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 176 | </DialogFooter> |
| 177 | </form> | |
| 178 | </DialogContent> | |
| 179 | </Dialog> | |
| 180 | ); | |
| 181 | } |