Skip to content

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

Projects show what they are by their icon: the projects list and grid, and the sidebar's Pinned, Recent and Projects rows, drew the same box for every project. ProjectIcon (components/project-icon.tsx) now picks one from Project.kind: a window for an app or site, books for a library, a terminal for a tool or CLI, an open book for documentation, and the box for anything else. The sidebar still shows a lock for a private project, where nothing else on its row says so; the list keeps its private badge beside the name. Sidebar shortcuts (shortcutOf, ShortcutProject) carry the kind. Checked: apps/web typecheck, lib/pins tests.

G1t-Session:
ses_01m4ysye20ecnrq04gbbm7nzk1
syntaqxcommitted Parent5c0bf5dBrowse files
5 files+35−80/5 viewed
+23−0
1+import { AppWindow, BookOpen, Box, Library, Lock, SquareTerminal } from "lucide-react";
2+
3+import type { ProjectKind } from "@g1t/contracts";
4+
5+/** Each kind's glyph, so a list says what each project is at a glance. */
6+const GLYPH: Record<ProjectKind, typeof Box> = {
7+ app: AppWindow,
8+ library: Library,
9+ tool: SquareTerminal,
10+ docs: BookOpen,
11+ other: Box,
12+};
13+
14+/**
15+ * What a project is, as an icon: an app or site, a library, a tool, docs, or
16+ * a box for anything else or a kind not known. `lockPrivate` shows a lock
17+ * instead for a private one, where nothing else on the row says so.
18+ */
19+export function ProjectIcon({ kind, isPrivate, lockPrivate, size }: { kind?: ProjectKind | null; isPrivate?: boolean; lockPrivate?: boolean; size?: number }) {
20+ if (isPrivate && lockPrivate) return <Lock size={size} />;
21+ const Glyph = (kind && GLYPH[kind]) || Box;
22+ return <Glyph size={size} />;
23+}
+3−2
7575 import { STATUS_URL, statusTitle } from "../lib/status";
7676 import { G1T_INVITES, leftBadge } from "../lib/invites";
7777 import { type ShortcutProject, movedPin, recentWith } from "../lib/pins";
78+import { ProjectIcon } from "./project-icon";
7879 import type { AccountMenuData } from "../routes/settings-menu-json";
7980 import { useLiveBadges } from "../lib/notify-client";
8081 import { OwnPresenceDot, OwnPresenceItems, StatusDialog } from "./presence";
641642 const row = (project: ShortcutProject, isPinned: boolean) => (
642643 <SidebarLink
643644 to={`/${project.namespace}/${project.name}`}
644− icon={project.isPrivate ? <Lock /> : <Box />}
645+ icon={<ProjectIcon kind={project.kind} isPrivate={project.isPrivate} lockPrivate />}
645646 bare={isPinned}
646647 action={
647648 // At the row's end, where the row's other actions sit, and only on
11981199 <Rule />
11991200 <NavGroup title="Projects">
12001201 {projects.slice(0, 8).map((project) => (
1201− <SidebarLink key={project.name} to={`/${project.namespace}/${project.name}`} icon={<Box size={15} />}>
1202+ <SidebarLink key={project.name} to={`/${project.namespace}/${project.name}`} icon={<ProjectIcon kind={project.kind} isPrivate={project.isPrivate} lockPrivate size={15} />}>
12021203 {project.title}
12031204 </SidebarLink>
12041205 ))}
+3−1
55 * service keeps both; opening a project records it after the page is sent.
66 */
77
8+import type { ProjectKind } from "@g1t/contracts";
9+
810 /** A project as the sidebar lists it. */
9−export type ShortcutProject = { namespace: string; name: string; title?: string; isPrivate: boolean };
11+export type ShortcutProject = { namespace: string; name: string; title?: string; isPrivate: boolean; kind?: ProjectKind };
1012
1113 export const MAX_PINS = 8;
1214 export const MAX_RECENT = 5;
+2−2
1717 }
1818
1919 /** What a project is in the sidebar and the command palette. */
20−export function shortcutOf(project: Project): { namespace: string; name: string; title: string; isPrivate: boolean } {
21− return { namespace: project.workspace, name: project.slug, title: project.name, isPrivate: project.private };
20+export function shortcutOf(project: Project): { namespace: string; name: string; title: string; isPrivate: boolean; kind: Project["kind"] } {
21+ return { namespace: project.workspace, name: project.slug, title: project.name, isPrivate: project.private, kind: project.kind };
2222 }
+4−3
1−import { ArrowUpRight, Box, ChevronLeft, ChevronRight, LayoutGrid, List, Lock, Rocket, Search } from "lucide-react";
1+import { ArrowUpRight, ChevronLeft, ChevronRight, LayoutGrid, List, Rocket, Search } from "lucide-react";
22 import { type KeyboardEvent, useEffect, useRef, useState } from "react";
33 import { Form, Link, useLocation, useNavigation, useSubmit } from "react-router";
44
2828 readProjectQuery,
2929 } from "../../lib/project-list";
3030 import { kindLabel, primaryLink } from "../../lib/project-kind";
31+import { ProjectIcon } from "../../components/project-icon";
3132 import { deployments, projects } from "../../lib/services.server";
3233 import { getViewer, roleIn } from "../../lib/session.server";
3334 import { workspaceProjects } from "../../lib/workspace-projects.server";
120121 return (
121122 <li className="group relative flex items-center gap-3 px-4 py-3 transition-colors focus-within:bg-raised/60 hover:bg-raised/60">
122123 <span className="flex size-8 shrink-0 items-center justify-center rounded-md bg-raised text-muted ring-1 ring-line">
123− {project.private ? <Lock size={14} /> : <Box size={14} />}
124+ <ProjectIcon kind={project.kind} size={14} />
124125 </span>
125126 <div className="min-w-0 grow">
126127 <div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
160161 <li className="group relative flex flex-col rounded-2xl border border-line bg-surface p-5 transition-colors focus-within:border-line-strong hover:border-line-strong">
161162 <div className="flex items-start gap-3">
162163 <span className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-raised text-muted ring-1 ring-line">
163− {project.private ? <Lock size={15} /> : <Box size={15} />}
164+ <ProjectIcon kind={project.kind} size={15} />
164165 </span>
165166 <div className="min-w-0 grow">
166167 <Link

Comments

Sign in to comment.