1import { formatAttachmentSize } from "@t3tools/client-runtime/state/attachments";
2import { imageMimeType } from "@t3tools/shared/image";
3import { videoMimeType } from "@t3tools/shared/video";
4/**
5 * Every accent shares a lightness so no kind reads heavier than another; only hue carries
6 * identity. These are the sRGB form of the same OKLCH set web uses, so a chip looks the
7 * same on every surface. See `composerInlineChip.ts`.
8 */
9const CONTEXT_CHIP_PRESENTATIONS = {
10 image: { accent: "#d55665", symbol: "photo" },
11 video: { accent: "#d06217", symbol: "play.rectangle" },
12 file: { accent: "#0090cd", symbol: "doc" },
13 mention: { accent: "#0096af", symbol: "doc" },
14 terminal: { accent: "#009f6e", symbol: "terminal" },
15 element: { accent: "#b87501", symbol: "cursorarrow.click" },
16 "preview-annotation": { accent: "#b87501", symbol: "cursorarrow.click" },
17 "review-comment": { accent: "#8a70dd", symbol: "text.bubble" },
18 "pull-request": { accent: "#7079e4", symbol: "git-pull-request" },
19 skill: { accent: "#b261be", symbol: "cube" },
20} as const;
21
22/**
23 * The size an attachment chip reports beside its name, matching web. Rendered as its own
24 * smaller run, so it carries no separator. Only attachment-backed records have bytes.
25 */
26export function composerChipSizeSuffix(record?: {
27 readonly kind?: string;
28 readonly sizeBytes?: number;
29}): string {
30 if (record?.kind !== "file" && record?.kind !== "image") return "";
31 return typeof record.sizeBytes === "number" ? formatAttachmentSize(record.sizeBytes) : "";
32}
33
34/**
35 * A pull request chip is coloured by what the pull request *is*, the way web colours it and
36 * the way the forge itself does: green open, grey draft, purple merged, red closed. The glyph
37 * stays the same across all four, as it does on web — state is carried by colour alone.
38 */
39const PULL_REQUEST_CHIP_PRESENTATIONS = {
40 open: { accent: "#009f6e", symbol: "git-pull-request" },
41 draft: { accent: "#7f8793", symbol: "git-pull-request" },
42 merged: { accent: "#8a70dd", symbol: "git-pull-request" },
43 closed: { accent: "#d55665", symbol: "git-pull-request" },
44} as const;
45
46export function contextChipPresentation(
47 kind: string,
48 record?: {
49 readonly kind?: string;
50 readonly name?: string;
51 readonly mimeType?: string;
52 readonly sectionId?: string;
53 readonly pullRequest?: {
54 readonly state?: string;
55 readonly isDraft?: boolean;
56 };
57 },
58) {
59 const presentationKind =
60 kind === "file" &&
61 videoMimeType({
62 name: record?.name ?? "",
63 mimeType: record?.mimeType ?? "",
64 })
65 ? "video"
66 : // A picture chosen through the file picker is typed `file`, but it is still a
67 // picture: it reads as one to the user and should not wear the generic file chip.
68 kind === "file" &&
69 imageMimeType({ name: record?.name ?? "", mimeType: record?.mimeType ?? "" }) !== null
70 ? "image"
71 : kind === "review-comment" && record?.sectionId?.startsWith("pull-request:")
72 ? "pull-request"
73 : kind;
74 if (presentationKind === "pull-request") {
75 const pullRequest = record?.pullRequest;
76 const state =
77 pullRequest?.state === "open" && pullRequest.isDraft === true
78 ? "draft"
79 : (pullRequest?.state ?? "");
80 if (Object.hasOwn(PULL_REQUEST_CHIP_PRESENTATIONS, state)) {
81 return PULL_REQUEST_CHIP_PRESENTATIONS[state as keyof typeof PULL_REQUEST_CHIP_PRESENTATIONS];
82 }
83 }
84 return Object.hasOwn(CONTEXT_CHIP_PRESENTATIONS, presentationKind)
85 ? CONTEXT_CHIP_PRESENTATIONS[presentationKind as keyof typeof CONTEXT_CHIP_PRESENTATIONS]
86 : CONTEXT_CHIP_PRESENTATIONS.file;
87}