Skip to content
177 linesCodeBlameRaw

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 thread1import { FileText, LoaderCircle } from "lucide-react";
2import { 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.4import 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 thread5
6import { 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.7import { 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 thread8import { Input } from "../ui/input";
9import { 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.10import { 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 thread11
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.12// "Write this up as an artifact" (docs.g1t.sh/guides/artifacts/, "Write a
13// 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.14// where it goes, a title if you have one, and which agent writes it. The
15// kind is a doc for now. Sending posts the ask in the thread, as you,
16// where everyone sees it; the agent answers it as it answers any mention,
17// 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 thread18
19type Spaces = { state: "loading" } | { state: "ready"; spaces: WritableSpace[] } | { state: "failed"; message: string };
20
21export function WriteUpDialog({
22 open,
23 onOpenChange,
24 slug,
25 agents,
26 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.27 shared,
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread28 onSend,
29}: {
30 open: boolean;
31 onOpenChange: (open: boolean) => void;
32 slug: string;
33 /** Who can write it: @g1t, then the conversation's agents (lib/write-up.ts `writeUpAgents`). */
34 agents: WriteUpAgent[];
35 /** The thread's link, cited as the source. */
36 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.37 /** A DM or a private channel: "Shared with this conversation" is offered, and chosen first. */
38 shared: boolean;
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread39 /** Posts the ask in the thread; answers with what went wrong, or null. */
40 onSend: (body: string) => Promise<string | null>;
41}) {
42 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.43 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 thread44 const [agent, setAgent] = useState(ORCHESTRATOR);
45 const [title, setTitle] = useState("");
46 const [busy, setBusy] = useState(false);
47 const [error, setError] = useState<string | null>(null);
48
49 // The spaces, read each time it opens: one may have been made meanwhile.
50 useEffect(() => {
51 if (!open) return;
52 let live = true;
53 setSpaces({ state: "loading" });
54 setError(null);
55 setTitle("");
56 setAgent(ORCHESTRATOR);
57 (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.58 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 thread59 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.60 const response = await fetch(`/${slug}/-/artifacts/api`);
61 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 thread62 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.63 if (!result.ok) setSpaces({ state: "failed", message: result.error.message });
64 else {
65 list = writableSpaces(result.value.spaces);
66 setSpaces({ state: "ready", spaces: list });
67 }
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread68 } 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.69 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 thread70 }
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) return;
72 const first = defaultWhere(shared, list);
73 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 thread74 })();
75 return () => {
76 live = false;
77 };
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.78 }, [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 thread79
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 const list = spaces.state === "ready" ? spaces.spaces : [];
81 const space = list.find((s) => s.id === where);
82 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 thread83 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.84 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 thread85
86 const submit = async () => {
87 if (!body || busy) return;
88 setBusy(true);
89 setError(null);
90 const failed = await onSend(body);
91 setBusy(false);
92 if (failed) return setError(failed);
93 onOpenChange(false);
94 };
95
96 return (
97 <Dialog open={open} onOpenChange={onOpenChange}>
98 <DialogContent className="max-w-md">
99 <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.100 <DialogTitle>Write this up as an artifact</DialogTitle>
101 <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 thread102 </DialogHeader>
103 <form
104 className="grid gap-4"
105 onSubmit={(event) => {
106 event.preventDefault();
107 void submit();
108 }}
109 >
110 <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.111 <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 thread112 {spaces.state === "loading" ? (
113 <div className="flex h-9 items-center gap-2 rounded-md border border-line px-3 text-sm text-faint">
114 <LoaderCircle size={14} className="animate-spin" aria-hidden="true" />
115 Loading spaces…
116 </div>
117 ) : (
118 <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.119 id="write-up-where"
120 value={where}
121 onValueChange={setWhere}
122 options={[
123 ...(shared ? [{ value: "conversation", label: "Shared with this conversation" }] : []),
124 { value: "private", label: "Private (just me)" },
125 ...list.map((s) => ({ value: s.id, label: s.name })),
126 ]}
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread127 />
128 )}
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.129 {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 thread130 </Field>
131 <Field>
132 <FieldLabel htmlFor="write-up-title">
133 Title <span className="font-normal text-faint">(optional)</span>
134 </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.135 <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 thread136 </Field>
137 <Field>
138 <FieldLabel htmlFor="write-up-agent">Written by</FieldLabel>
139 <SelectField
140 id="write-up-agent"
141 value={writer.handle}
142 onValueChange={setAgent}
143 options={agents.map((a) => ({ value: a.handle, label: a.name === a.handle ? `@${a.handle}` : `${a.name} (@${a.handle})` }))}
144 />
145 </Field>
146 {body && (
147 <div className="rounded-lg border border-line bg-bg/60 p-3">
148 <p className="mb-1 flex items-center gap-1.5 text-xs font-medium text-muted">
149 <FileText size={13} aria-hidden="true" />
150 Posts in this thread, as you
151 </p>
152 <p className="text-[0.8125rem] leading-snug break-words text-fg-soft">{body}</p>
153 </div>
154 )}
155 <FieldError>{error}</FieldError>
156 <DialogFooter>
157 <button
158 type="button"
159 onClick={() => onOpenChange(false)}
160 className="inline-flex h-9 items-center rounded-md border border-line px-3 text-sm font-medium text-fg/90 transition-colors hover:border-line-strong hover:bg-surface"
161 >
162 Cancel
163 </button>
164 <button
165 type="submit"
166 disabled={!body || busy}
167 className="inline-flex h-9 items-center justify-center gap-1.5 rounded-md bg-accent px-3.5 text-sm font-medium text-bg transition-colors hover:bg-accent-hover disabled:opacity-50"
168 >
169 {busy && <LoaderCircle size={14} className="animate-spin" aria-hidden="true" />}
170 {busy ? "Asking…" : `Ask @${writer.handle}`}
171 </button>
172 </DialogFooter>
173 </form>
174 </DialogContent>
175 </Dialog>
176 );
177}