Skip to content
61 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.

Chat and workspace agents: channels, DMs and named agents you talk to1import { Outlet, data, type ShouldRevalidateFunctionArgs } from "react-router";
2
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar3import { type ChatSidebar, type WorkspaceAgent, shownUsername } from "@g1t/contracts";
Chat and workspace agents: channels, DMs and named agents you talk to4
5import type { Route } from "./+types/layout";
6import type { Mentionable } from "../../../lib/chat";
7import { sidebarOrNull, workspacePeople } from "../../../lib/chat.server";
8import { requireUser, roleIn } from "../../../lib/session.server";
9
10/**
11 * Chat mode: what its sidebar shows (the shell draws it, from this data)
12 * and who can be mentioned or messaged. Each part is empty rather than an
13 * error when its service does not answer.
14 */
15export type ChatLayoutData = {
16 slug: string;
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)17 /** The viewer's role: owners may make agents. */
18 role: string;
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar19 /** The viewer, named as chat names them (`memberName`). */
20 me: { id: string; username: string; display_username: string | null; display_name: string; avatar: string | null };
Chat and workspace agents: channels, DMs and named agents you talk to21 sidebar: ChatSidebar | null;
22 people: Mentionable[];
23 agents: WorkspaceAgent[];
24};
25
26export async function loader({ params, context, request }: Route.LoaderArgs): Promise<ChatLayoutData> {
27 const viewer = requireUser(context, request);
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)28 const role = roleIn(viewer, params.owner);
29 if (!role) throw data(null, { status: 404 });
Chat and workspace agents: channels, DMs and named agents you talk to30 const slug = params.owner.toLowerCase();
31 const [sidebar, { people, agents }] = await Promise.all([sidebarOrNull(slug, viewer), workspacePeople(slug, viewer)]);
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar32 const self = people.find((person) => person.name === viewer.username.toLowerCase());
33 const display_username = viewer.display_username ?? self?.display_username ?? null;
34 return {
35 slug,
36 role,
37 me: {
38 id: viewer.id,
39 username: viewer.username,
40 display_username,
41 display_name: self?.display_name ?? shownUsername({ username: viewer.username, display_username }),
42 avatar: viewer.avatar ?? null,
43 },
44 sidebar,
45 people,
46 agents,
47 };
Chat and workspace agents: channels, DMs and named agents you talk to48}
49
50/**
51 * Moving between conversations keeps the sidebar: it refreshes itself
52 * (components/chat/sidebar.tsx) and after anything is sent.
53 */
54export function shouldRevalidate({ currentParams, nextParams, formMethod, defaultShouldRevalidate }: ShouldRevalidateFunctionArgs) {
55 if (formMethod && formMethod !== "GET") return defaultShouldRevalidate;
56 return currentParams.owner !== nextParams.owner;
57}
58
59export default function ChatLayout() {
60 return <Outlet />;
61}

This file's history is long; its oldest lines are credited to the oldest commit read.