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

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.1/**
2 * Artifacts as lists show them (docs/ARTIFACTS_MODE.md section 6.2): rows
3 * grouped by the day they were last edited, in the viewer's time zone, or
4 * cards with a picture drawn from each one's preview. Each has its kind,
5 * who can see it, its space, when it was edited and by whom, and its ⋯
6 * menu.
7 */
8import type { Folio } from "@g1t/contracts";
9import { useMemo } from "react";
10import { Link } from "react-router";
11
12import { dayGroups } from "../../lib/folios";
13import { TimeAgo } from "../ui";
14import { Hint } from "../ui/hint";
Artifacts' home keeps its filters on one row beside the search box, and an artifact in someone else's Private shows whose it is instead of reading "Private".15import { useFoliosData } from "./actions";
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.16import { FolioThumbnail, KindIcon } from "./kinds";
17import { FolioMenu } from "./menu";
18import { AccessMark } from "./parts";
19
Artifacts' home keeps its filters on one row beside the search box, and an artifact in someone else's Private shows whose it is instead of reading "Private".20/** Where it sits, in a word: its space; else your Private, or whose it is when it's someone else's. */
21function placeOf(folio: Folio, me: string | undefined): string {
22 if (folio.space) return folio.space.name;
23 return `user:${folio.owner.id}` === me ? "Private" : folio.owner.display_name;
24}
25
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.26function Edited({ folio }: { folio: Folio }) {
27 const by = folio.edited_by?.display_name;
28 return (
29 <Hint label={by ? `Edited by ${by}` : "Edited"}>
30 <span className="shrink-0 text-xs whitespace-nowrap text-faint" tabIndex={-1}>
31 Edited <TimeAgo at={folio.edited_at} />
32 </span>
33 </Hint>
34 );
35}
36
37export function FolioRow({ slug, folio, onError }: { slug: string; folio: Folio; onError?: (message: string) => void }) {
Artifacts' home keeps its filters on one row beside the search box, and an artifact in someone else's Private shows whose it is instead of reading "Private".38 const me = useFoliosData()?.me.key;
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 return (
40 <li className="group relative flex items-center gap-3 px-3 py-2 transition-colors hover:bg-raised/60 sm:px-4">
41 <KindIcon kind={folio.kind} />
42 <span className="flex min-w-0 grow items-center gap-2">
43 <Link to={folio.path} prefetch="intent" className="min-w-0 truncate text-sm text-fg after:absolute after:inset-0 hover:text-accent">
44 {folio.icon && <span className="mr-1.5">{folio.icon}</span>}
45 {folio.title || "Untitled"}
46 </Link>
47 {folio.stale && (
48 <Hint label="Possibly out of date: code it cites changed">
49 <span className="relative size-1.5 shrink-0 rounded-full bg-warn" aria-label="Possibly out of date" />
50 </Hint>
51 )}
52 </span>
53 <span className="relative hidden shrink-0 sm:flex">
54 <AccessMark folio={folio} />
55 </span>
Artifacts' home keeps its filters on one row beside the search box, and an artifact in someone else's Private shows whose it is instead of reading "Private".56 <span className="hidden w-32 shrink-0 truncate text-xs text-faint md:block">{placeOf(folio, me)}</span>
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.57 <span className="relative w-28 shrink-0 text-right max-sm:w-auto">
58 <Edited folio={folio} />
59 </span>
60 <span className="relative">
61 <FolioMenu slug={slug} folio={folio} onError={onError} />
62 </span>
63 </li>
64 );
65}
66
67/** Rows under Today, Yesterday, then dates. */
68export function FolioDays({ slug, items, zone, onError }: { slug: string; items: Folio[]; zone: string; onError?: (message: string) => void }) {
69 const days = useMemo(() => dayGroups(items, (f) => f.edited_at, zone), [items, zone]);
70 return (
71 <div className="space-y-6">
72 {days.map((day) => (
73 <section key={day.key} aria-label={day.label}>
74 <h3 className="mb-1.5 px-1 text-xs font-medium text-faint">{day.label}</h3>
75 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
76 {day.items.map((f) => (
77 <FolioRow key={f.id} slug={slug} folio={f} onError={onError} />
78 ))}
79 </ul>
80 </section>
81 ))}
82 </div>
83 );
84}
85
86export function FolioCard({ slug, folio, onError }: { slug: string; folio: Folio; onError?: (message: string) => void }) {
Artifacts' home keeps its filters on one row beside the search box, and an artifact in someone else's Private shows whose it is instead of reading "Private".87 const me = useFoliosData()?.me.key;
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.88 return (
89 <div className="group relative flex flex-col overflow-hidden rounded-xl border border-line bg-surface transition-colors hover:border-line-strong">
90 <div className="pointer-events-none h-32 shrink-0 border-b border-line">
91 <FolioThumbnail folio={folio} />
92 </div>
93 <div className="flex items-start gap-2.5 px-3.5 pt-3 pb-3">
94 <KindIcon kind={folio.kind} size={14} box={24} />
95 <span className="min-w-0 grow">
96 <Link to={folio.path} prefetch="intent" className="line-clamp-1 text-sm font-medium text-fg after:absolute after:inset-0 group-hover:text-accent">
97 {folio.icon && <span className="mr-1">{folio.icon}</span>}
98 {folio.title || "Untitled"}
99 </Link>
100 <span className="mt-0.5 flex items-center gap-1.5 text-[0.6875rem] text-faint">
Artifacts' home keeps its filters on one row beside the search box, and an artifact in someone else's Private shows whose it is instead of reading "Private".101 <span className="truncate">{placeOf(folio, me)}</span>
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 <span aria-hidden="true">·</span>
103 <span className="relative">
104 <Edited folio={folio} />
105 </span>
106 </span>
107 </span>
108 <span className="relative -mt-1 -mr-1.5 flex items-center">
109 <span className="relative">
110 <AccessMark folio={folio} />
111 </span>
112 <FolioMenu slug={slug} folio={folio} onError={onError} />
113 </span>
114 </div>
115 </div>
116 );
117}
118
119export function FolioGrid({ slug, items, onError }: { slug: string; items: Folio[]; onError?: (message: string) => void }) {
120 return (
121 <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
122 {items.map((f) => (
123 <FolioCard key={f.id} slug={slug} folio={f} onError={onError} />
124 ))}
125 </div>
126 );
127}