flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

Commit

The project menu opens by section, and the workspace has its own panel

Like GitLab's: Plan, Code, Agents, Build, Deploy, Secure, Operate, Analyze and Settings, each one row that opens to its pages, built and Soon alike, instead of headings over one link each. The section holding the current page opens itself; others remember being left open. Sections with nothing built yet carry one Soon. Project settings are listed in the menu. The workspace's usage, settings and the account sit in a panel of their own at the bottom, ruled off full width.

syntaqxcommitted Parent54755e1Browse files
1 file+238−990/1 viewed
+238−99
11 import {
22 ArrowLeft,
3+ ChevronRight,
34 BarChart3,
45 BookMarked,
56 BookOpen,
1617 Fingerprint,
1718 KeyRound,
1819 Box,
19− Layers,
2020 LayoutDashboard,
2121 LayoutGrid,
2222 ListTree,
3333 Activity,
3434 Bot,
3535 Kanban,
36− Package,
37− PackageCheck,
38− ScrollText,
39− Server,
4036 ShieldCheck,
41− Siren,
42− Tag,
43− ToggleRight,
4437 Rocket,
4538 X,
4639 } from "lucide-react";
154147 );
155148 }
156149
150+/**
151+ * A section of the project menu that opens to show its pages, as GitLab's
152+ * does: one row with an icon, and its pages indented under it. It opens
153+ * by itself when one of its pages is the current one, and otherwise
154+ * remembers whether it was left open.
155+ */
156+function SidebarSection({
157+ title,
158+ icon,
159+ paths = [],
160+ soon = false,
161+ children,
162+}: {
163+ title: string;
164+ icon: ReactNode;
165+ /** Path prefixes of its pages: one being current opens it. */
166+ paths?: string[];
167+ /** Nothing in it is built yet. */
168+ soon?: boolean;
169+ children: ReactNode;
170+}) {
171+ const { pathname } = useLocation();
172+ const current = paths.some((prefix) => pathname === prefix || pathname.startsWith(prefix + "/"));
173+ const key = `g1t.sidebar.${title}`;
174+ const [open, setOpen] = useState<boolean>(current);
175+ // What the person left it as, once the page is in the browser.
176+ useEffect(() => {
177+ if (current) {
178+ setOpen(true);
179+ return;
180+ }
181+ try {
182+ const saved = localStorage.getItem(key);
183+ if (saved != null) setOpen(saved === "1");
184+ } catch {
185+ // No storage: it stays as it is.
186+ }
187+ }, [current, key]);
188+ const toggle = () => {
189+ const next = !open;
190+ setOpen(next);
191+ try {
192+ localStorage.setItem(key, next ? "1" : "0");
193+ } catch {
194+ // Remembered for this page only.
195+ }
196+ };
197+ return (
198+ <section>
199+ <button
200+ type="button"
201+ onClick={toggle}
202+ aria-expanded={open}
203+ className={`group flex h-8 w-full items-center gap-2.5 rounded-md px-2 text-left text-[0.8125rem] transition-colors hover:bg-raised/60 ${
204+ current ? "text-fg" : soon ? "text-faint hover:text-muted" : "text-muted hover:text-fg"
205+ }`}
206+ >
207+ <span className={`shrink-0 ${current ? "text-muted" : "text-faint group-hover:text-muted"}`}>{icon}</span>
208+ <span className={`min-w-0 grow truncate ${current ? "font-medium" : ""}`}>{title}</span>
209+ {soon && !open && (
210+ <span className="rounded-full px-1.5 py-px text-[0.625rem] font-medium tracking-wide text-muted uppercase ring-1 ring-line">
211+ Soon
212+ </span>
213+ )}
214+ <ChevronRight
215+ size={14}
216+ className={`shrink-0 text-faint transition-transform duration-150 ${open ? "rotate-90" : ""}`}
217+ aria-hidden="true"
218+ />
219+ </button>
220+ {open && (
221+ <div className="relative mt-px mb-1 space-y-px before:absolute before:top-1 before:bottom-1 before:left-[1.1875rem] before:w-px before:bg-line">
222+ {children}
223+ </div>
224+ )}
225+ </section>
226+ );
227+}
228+
229+/** A page inside a section: indented, without an icon of its own. */
230+function SidebarSubLink({
231+ to,
232+ end,
233+ count,
234+ also,
235+ children,
236+}: {
237+ to: string;
238+ end?: boolean;
239+ count?: number;
240+ also?: string | string[];
241+ children: ReactNode;
242+}) {
243+ const { pathname } = useLocation();
244+ return (
245+ <NavLink
246+ to={to}
247+ end={end}
248+ prefetch="intent"
249+ className={({ isActive, isPending }) => {
250+ const current =
251+ isActive || [also ?? []].flat().some((prefix) => pathname === prefix || pathname.startsWith(prefix + "/"));
252+ return `relative flex h-8 items-center gap-2 rounded-md pr-2 pl-[2.375rem] text-[0.8125rem] transition-colors ${
253+ current
254+ ? "bg-raised font-medium text-fg before:absolute before:top-1.5 before:bottom-1.5 before:left-[1.1875rem] before:z-10 before:w-px before:bg-accent"
255+ : isPending
256+ ? "bg-raised/60 text-fg"
257+ : "text-muted hover:bg-raised/60 hover:text-fg"
258+ }`;
259+ }}
260+ >
261+ <span className="min-w-0 grow truncate">{children}</span>
262+ {count != null && count > 0 && (
263+ <span className="rounded bg-line px-1.5 text-[0.6875rem] tabular-nums text-muted">{count}</span>
264+ )}
265+ </NavLink>
266+ );
267+}
268+
269+/** A page a section will have: shown so people can see where g1t is going. */
270+function SidebarSubSoon({ about, children }: { about: string; children: ReactNode }) {
271+ return (
272+ <div
273+ title={about}
274+ aria-disabled="true"
275+ className="flex h-8 cursor-default items-center gap-2 rounded-md pr-2 pl-[2.375rem] text-[0.8125rem] text-faint"
276+ >
277+ <span className="grow truncate">{children}</span>
278+ <span className="rounded-full px-1.5 py-px text-[0.625rem] font-medium tracking-wide text-muted uppercase ring-1 ring-line">
279+ Soon
280+ </span>
281+ </div>
282+ );
283+}
284+
157285 function SidebarGroup({ title, action, children }: { title: string; action?: ReactNode; children: ReactNode }) {
158286 return (
159287 <section className="mt-6">
414542 <SidebarLink to={base} end icon={<LayoutGrid size={15} />}>
415543 Overview
416544 </SidebarLink>
545+
546+ <SidebarSection title="Plan" icon={<Kanban size={15} />} paths={[`${base}/issues`, `${base}/plans`]}>
547+ <SidebarSubLink to={`${base}/issues`} count={repo.issues}>
548+ Issues
549+ </SidebarSubLink>
550+ {repo.member && <SidebarSubLink to={`${base}/plans`}>Outcomes</SidebarSubLink>}
551+ <SidebarSubSoon about="Issues and pull requests on boards, by state, owner or outcome.">Boards</SidebarSubSoon>
552+ <SidebarSubSoon about="Dates to land outcomes by, with what is left and what is at risk.">Milestones</SidebarSubSoon>
553+ <SidebarSubSoon about="Pages about the project that agents keep current as the code changes.">Wiki</SidebarSubSoon>
554+ </SidebarSection>
555+
556+ <SidebarSection
557+ title="Code"
558+ icon={<Code2 size={15} />}
559+ paths={[`${base}/pulls`, `${base}/pull`, `${base}/queue`, `${base}/code`, `${base}/tree`, `${base}/blob`, `${base}/commits`, `${base}/commit`]}
560+ >
561+ <SidebarSubLink to={`${base}/pulls`} also={`${base}/pull`} count={repo.pulls}>
562+ Pull requests
563+ </SidebarSubLink>
564+ <SidebarSubLink to={`${base}/queue`}>Merge queue</SidebarSubLink>
565+ <SidebarSubLink to={`${base}/code`} also={[`${base}/tree`, `${base}/blob`]}>
566+ Files
567+ </SidebarSubLink>
568+ <SidebarSubLink to={`${base}/commits`} also={`${base}/commit`}>
569+ Commits
570+ </SidebarSubLink>
571+ <SidebarSubSoon about="Every branch, who and what is on it, how far behind it is, and its preview.">Branches</SidebarSubSoon>
572+ <SidebarSubSoon about="Tags, and the releases made from them.">Tags</SidebarSubSoon>
573+ </SidebarSection>
574+
575+ <SidebarSection title="Agents" icon={<Bot size={15} />} soon>
576+ <SidebarSubSoon about="Every agent at work on this project now: what it is doing, what it knows, and a way to steer it.">
577+ At work
578+ </SidebarSubSoon>
579+ <SidebarSubSoon about="Every agent session that changed this project, searchable, with why-blame back to the lines it wrote.">
580+ Sessions
581+ </SidebarSubSoon>
582+ <SidebarSubSoon about="How agents should work here: conventions, commands and checks, kept with the code.">
583+ Playbooks
584+ </SidebarSubSoon>
585+ </SidebarSection>
586+
587+ <SidebarSection title="Build" icon={<PlayCircle size={15} />} paths={[`${base}/actions`]}>
588+ <SidebarSubLink to={`${base}/actions`}>Actions</SidebarSubLink>
589+ <SidebarSubSoon about="g1t's machines, or your own, that run workflow jobs and checks.">Runners</SidebarSubSoon>
590+ <SidebarSubSoon about="What builds and workflow jobs produce, kept and downloadable.">Artifacts</SidebarSubSoon>
591+ <SidebarSubSoon about="Workflows that run on a timetable, and their history.">Schedules</SidebarSubSoon>
592+ </SidebarSection>
593+
594+ <SidebarSection title="Deploy" icon={<Rocket size={15} />} paths={repo.member ? [`${base}/deployments`] : []}>
595+ {repo.member && <SidebarSubLink to={`${base}/deployments`}>Deployments</SidebarSubLink>}
596+ <SidebarSubSoon about="Staging and other environments, with required approvers and branch rules.">Environments</SidebarSubSoon>
597+ <SidebarSubSoon about="Tagged releases with notes written from what landed.">Releases</SidebarSubSoon>
598+ <SidebarSubSoon about="npm, container and other packages published from the project.">Packages</SidebarSubSoon>
599+ <SidebarSubSoon about="Turn features on per environment or per user, without a deploy.">Feature flags</SidebarSubSoon>
600+ </SidebarSection>
601+
602+ <SidebarSection title="Secure" icon={<ShieldCheck size={15} />} soon>
603+ <SidebarSubSoon about="Every finding in one place, by severity, with the agent fixing each.">Security overview</SidebarSubSoon>
604+ <SidebarSubSoon about="Keys and tokens found in the code or its history, revoked and removed by an agent.">
605+ Secret scanning
606+ </SidebarSubSoon>
607+ <SidebarSubSoon about="Outdated and vulnerable packages, updated by agents, tested and landed through the queue.">
608+ Dependency updates
609+ </SidebarSubSoon>
610+ <SidebarSubSoon about="Code scanned for vulnerabilities on every change, each finding fixed through the queue.">
611+ Code scanning
612+ </SidebarSubSoon>
613+ </SidebarSection>
614+
615+ <SidebarSection title="Operate" icon={<Activity size={15} />} soon>
616+ <SidebarSubSoon about="Requests, errors and CPU time of each deployment, and its logs.">Logs and metrics</SidebarSubSoon>
617+ <SidebarSubSoon about="Errors and incidents from the running app, each becoming an issue an agent can take.">
618+ Errors and incidents
619+ </SidebarSubSoon>
620+ <SidebarSubSoon about="Checks that the app answers, from around the world, and who is told when it does not.">Uptime</SidebarSubSoon>
621+ </SidebarSection>
622+
623+ <SidebarSection title="Analyze" icon={<BarChart3 size={15} />} soon>
624+ <SidebarSubSoon about="How work flows: lead time, review time, and the share of changes agents make.">Insights</SidebarSubSoon>
625+ <SidebarSubSoon about="What the project costs, by agent run, sandbox, build and app.">Costs</SidebarSubSoon>
626+ <SidebarSubSoon about="Deploy frequency, lead time, change failure rate and time to restore.">Delivery metrics</SidebarSubSoon>
627+ </SidebarSection>
628+
417629 {repo.member && (
418− <SidebarLink to={`${base}/deployments`} icon={<Rocket size={15} />}>
419− Deployments
420− </SidebarLink>
630+ <SidebarSection title="Settings" icon={<Settings size={15} />} paths={[`${base}/settings`]}>
631+ <SidebarSubLink to={`${base}/settings`} end>
632+ General
633+ </SidebarSubLink>
634+ <SidebarSubLink to={`${base}/settings/repository`}>Repository</SidebarSubLink>
635+ <SidebarSubLink to={`${base}/settings/deployments`}>Deployments</SidebarSubLink>
636+ <SidebarSubLink to={`${base}/settings/dependencies`}>Dependencies</SidebarSubLink>
637+ <SidebarSubLink to={`${base}/settings/secrets`}>Secrets and variables</SidebarSubLink>
638+ <SidebarSubLink to={`${base}/settings/webhooks`}>Webhooks</SidebarSubLink>
639+ </SidebarSection>
421640 )}
422641 </div>
423− <SidebarGroup title="Code">
424− <SidebarLink to={`${base}/code`} icon={<Code2 size={15} />} also={[`${base}/tree`, `${base}/blob`]}>
425− Code
426− </SidebarLink>
427− <SidebarLink to={`${base}/commits`} also={`${base}/commit`} icon={<History size={15} />}>
428− Commits
429− </SidebarLink>
430− </SidebarGroup>
431− <SidebarGroup title="Work">
432− <SidebarLink to={`${base}/issues`} icon={<CircleDot size={15} />} count={repo.issues}>
433− Issues
434− </SidebarLink>
435− <SidebarLink to={`${base}/pulls`} also={`${base}/pull`} icon={<GitPullRequest size={15} />} count={repo.pulls}>
436− Pull requests
437− </SidebarLink>
438− {repo.member && (
439− <SidebarLink to={`${base}/plans`} icon={<ListTree size={15} />}>
440− Plan
441− </SidebarLink>
442− )}
443− <SidebarLink to={`${base}/queue`} icon={<Layers size={15} />}>
444− Merge queue
445− </SidebarLink>
446− <SidebarSoon icon={<Kanban size={15} />} about="Issues and pull requests on boards, by state, owner or outcome.">
447− Boards
448− </SidebarSoon>
449− <SidebarSoon icon={<BookOpen size={15} />} about="Pages about the project that agents keep current as the code changes.">
450− Wiki
451− </SidebarSoon>
452− </SidebarGroup>
453− <SidebarGroup title="Agents">
454− <SidebarSoon icon={<Bot size={15} />} about="Every agent at work on this project now: what it is doing, what it knows, and a way to steer it.">
455− Agents at work
456− </SidebarSoon>
457− <SidebarSoon icon={<ScrollText size={15} />} about="Every agent session that changed this project, searchable, with why-blame back to the lines it wrote.">
458− Sessions
459− </SidebarSoon>
460− </SidebarGroup>
461− <SidebarGroup title="Automate">
462− <SidebarLink to={`${base}/actions`} icon={<PlayCircle size={15} />}>
463− Actions
464− </SidebarLink>
465− </SidebarGroup>
466− <SidebarGroup title="Secure">
467− <SidebarSoon icon={<ShieldCheck size={15} />} about="Secret scanning, vulnerable dependencies and code scanning, each finding fixed by an agent through the queue.">
468− Security and quality
469− </SidebarSoon>
470− <SidebarSoon icon={<PackageCheck size={15} />} about="Outdated packages updated by agents, tested and landed through the queue.">
471− Dependency updates
472− </SidebarSoon>
473− </SidebarGroup>
474− <SidebarGroup title="Deploy">
475− <SidebarSoon icon={<Server size={15} />} about="Staging and other environments, with required approvers and branch rules.">
476− Environments
477− </SidebarSoon>
478− <SidebarSoon icon={<Tag size={15} />} about="Tagged releases with notes written from what landed.">
479− Releases
480− </SidebarSoon>
481− <SidebarSoon icon={<Package size={15} />} about="npm, container and other packages published from the project.">
482− Packages
483− </SidebarSoon>
484− <SidebarSoon icon={<ToggleRight size={15} />} about="Turn features on per environment or per user, without a deploy.">
485− Feature flags
486− </SidebarSoon>
487− </SidebarGroup>
488− <SidebarGroup title="Operate">
489− <SidebarSoon icon={<Activity size={15} />} about="Requests, errors and CPU time of each deployment, and its logs.">
490− Logs and metrics
491− </SidebarSoon>
492− <SidebarSoon icon={<Siren size={15} />} about="Errors and incidents from the running app, each becoming an issue an agent can take.">
493− Errors and incidents
494− </SidebarSoon>
495− </SidebarGroup>
496− <SidebarGroup title="Insights">
497− <SidebarSoon icon={<BarChart3 size={15} />} about="How work flows: lead time, agents' share of changes, review time, what costs most.">
498− Insights
499− </SidebarSoon>
500− </SidebarGroup>
501− {repo.member && (
502− <SidebarGroup title="Project">
503− <SidebarLink to={`${base}/settings`} icon={<Settings size={15} />}>
504− Settings
505− </SidebarLink>
506− </SidebarGroup>
507− )}
508642 </nav>
509643 );
510644 }
685819 </div>
686820 </div>
687821
688− {/* The workspace's own things sit at the bottom, by its usage and the account. */}
689− <div className="space-y-2 p-2">
822+ {/* The workspace's own things sit at the bottom, by its usage and the
823+ account: a panel of their own, ruled off from whatever menu is above,
824+ however long it grows. */}
825+ <div className="shrink-0 space-y-2 border-t border-line bg-surface/50 p-2 pt-3">
690826 {ws && (
691827 <div className="space-y-px">
692− <p className="px-2 pb-1 text-[0.6875rem] font-medium uppercase tracking-wider text-faint">{ws.slug}</p>
828+ <p className="flex items-center gap-2 px-2 pb-1 text-[0.6875rem] font-medium uppercase tracking-wider text-faint">
829+ <span>Workspace</span>
830+ <span className="min-w-0 truncate font-mono normal-case tracking-normal text-muted">{ws.slug}</span>
831+ </p>
693832 <SidebarLink to={`/${ws.slug}/-/usage`} icon={<BarChart3 size={15} />}>
694833 Usage
695834 </SidebarLink>