People wear their own avatars wherever g1t knows them only by name, and a workspace is never shown as a person. The activity feeds, recent changes, home's most active faces, issue and pull request authors, assignees, comments and the audit log drew a person from their username alone, so everyone got their letter avatar instead of the picture they uploaded. An avatar given no picture now draws the letter at once and lays the person's own picture over it once it loads, from /-/avatar/user/<username> (routes/avatar-user.ts): a redirect to the picture on the usercontent origin, or not found, read from identity's people only, so a workspace's icon can never stand in for someone. Kept five minutes, public like the profile. Agents, the product tour's people, "someone" and workspaces' square avatars are never looked up (image null), so an account with the same name never lends them its face.
Builds and deployments run as the workspace (a wsp_ id), and identity's usernames lookup names a workspace id by its slug, so a deploy read as a person called after the workspace ("syntaqx deployed production"). The project and dashboard feeds (lib/mission.ts) and a repository's Activity page now show a workspace acting as itself as g1t, as home already did, with a test. The feeds' avatars and names open the person's card and link to their profile (UserAvatar in components/user-card.tsx), on a repository's overview and Activity page, the dashboard and the audit log, where someone acted for is a person too; g1t's lines carry its mark rather than a bare "g1t:". Every Tailwind class in the site and sudo is written the way Tailwind spells it (its suggestCanonicalClasses check, against g1t's theme): scrollbar-thin for [scrollbar-width:thin], wrap-break-word for break-words, bg-warn/6 for bg-warn/[0.06], has-focus-visible: for has-[:focus-visible]:, spacing steps for pixel and rem values, and so on, 247 in 111 files. Each is the same CSS; both apps build and the site's tests pass.
| 114 | 114 | {after.map(([label, value], index) => ( | |
| 115 | 115 | <tr key={label} className="border-t border-line align-top"> | |
| 116 | 116 | <td className="py-1.5 pr-4 text-muted">{label}</td> | |
| 117 | − | <td className="py-1.5 pr-4 break-words text-faint">{before[index][1]}</td> | |
| 118 | − | <td className={`py-1.5 break-words ${value !== before[index][1] ? "font-medium text-fg" : "text-muted"}`}>{value}</td> | |
| 117 | + | <td className="py-1.5 pr-4 wrap-break-word text-faint">{before[index][1]}</td> | |
| 118 | + | <td className={`py-1.5 wrap-break-word ${value !== before[index][1] ? "font-medium text-fg" : "text-muted"}`}>{value}</td> | |
| 119 | 119 | </tr> | |
| 120 | 120 | ))} | |
| 121 | 121 | </tbody> | |
| ⋯ | |||
| 630 | 630 | " · no expiry" | |
| 631 | 631 | )} | |
| 632 | 632 | </p> | |
| 633 | − | <p className="mt-0.5 text-sm break-words"> | |
| 633 | + | <p className="mt-0.5 text-sm wrap-break-word"> | |
| 634 | 634 | {grant.kind === "refund" && grant.refundFor && <span className="text-muted">For {grant.refundFor}{grant.refundDay ? ` (${grant.refundDay})` : ""}: </span>} | |
| 635 | 635 | {grant.note} | |
| 636 | 636 | </p> | |
| ⋯ | |||
| 818 | 818 | <EmptyState title="Nothing yet" /> | |
| 819 | 819 | ) : ( | |
| 820 | 820 | <div className="-mx-4 -my-4 overflow-x-auto sm:-mx-5 sm:-my-5"> | |
| 821 | − | <table className="w-full min-w-[36rem] text-sm"> | |
| 821 | + | <table className="w-full min-w-xl text-sm"> | |
| 822 | 822 | <thead> | |
| 823 | 823 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 824 | 824 | <th className="px-4 py-2 font-medium sm:pl-5">When</th> | |
| ⋯ | |||
| 842 | 842 | {entry.workspace} | |
| 843 | 843 | </Link> | |
| 844 | 844 | )} | |
| 845 | − | <span className="break-words">{entry.description}</span> | |
| 845 | + | <span className="wrap-break-word">{entry.description}</span> | |
| 846 | 846 | {givenParts(entry).map((part) => ( | |
| 847 | 847 | <Badge key={part.label} tone="lavender"> | |
| 848 | 848 | Given · {part.label} {usd(part.micros)} | |
| ⋯ | |||
| 894 | 894 | <When at={entry.createdAt} time /> | |
| 895 | 895 | </span> | |
| 896 | 896 | </p> | |
| 897 | − | {entry.detail && <p className="mt-0.5 text-sm break-words text-fg-soft">{entry.detail}</p>} | |
| 897 | + | {entry.detail && <p className="mt-0.5 text-sm wrap-break-word text-fg-soft">{entry.detail}</p>} | |
| 898 | 898 | <p className="mt-0.5 flex items-center gap-1 font-mono text-xs text-faint"> | |
| 899 | 899 | <UserRound size={11} /> | |
| 900 | 900 | {entry.by} | |
| 110 | 110 | <span className="hidden group-open:inline">Hide the table</span> | |
| 111 | 111 | </summary> | |
| 112 | 112 | <div className="mt-2 overflow-x-auto"> | |
| 113 | − | <table className="w-full min-w-[34rem] text-xs"> | |
| 113 | + | <table className="w-full min-w-136 text-xs"> | |
| 114 | 114 | <thead> | |
| 115 | 115 | <tr className="border-b border-line text-left text-muted"> | |
| 116 | 116 | <th className="py-1.5 pr-3 font-medium">Month</th> |
| 116 | 116 | <span className="hidden group-open:inline">Hide the table</span> | |
| 117 | 117 | </summary> | |
| 118 | 118 | <div className="mt-2 overflow-x-auto"> | |
| 119 | − | <table className="w-full min-w-[28rem] text-xs"> | |
| 119 | + | <table className="w-full min-w-md text-xs"> | |
| 120 | 120 | <thead> | |
| 121 | 121 | <tr className="border-b border-line text-left text-muted"> | |
| 122 | 122 | <th className="py-1.5 pr-3 font-medium">Day</th> |
| 170 | 170 | <ol className="mt-4 space-y-3"> | |
| 171 | 171 | {sorted.map((note) => ( | |
| 172 | 172 | <li key={note.id} className="border-l-2 border-accent/40 pl-3"> | |
| 173 | − | <p className="text-sm break-words whitespace-pre-line text-fg-soft">{note.text}</p> | |
| 173 | + | <p className="text-sm wrap-break-word whitespace-pre-line text-fg-soft">{note.text}</p> | |
| 174 | 174 | <p className="mt-1 flex flex-wrap items-center gap-x-2 text-xs text-faint"> | |
| 175 | 175 | <StaffName email={note.by} me={me} /> | |
| 176 | 176 | <When at={note.createdAt} time /> |
| 143 | 143 | to={item.to} | |
| 144 | 144 | title={item.about} | |
| 145 | 145 | aria-current={current ? "page" : undefined} | |
| 146 | − | className={`relative flex h-8 items-center gap-2 rounded-md pr-2 pl-[2.375rem] text-[0.8125rem] transition-colors ${ | |
| 146 | + | className={`relative flex h-8 items-center gap-2 rounded-md pr-2 pl-9.5 text-[0.8125rem] transition-colors ${ | |
| 147 | 147 | current | |
| 148 | − | ? "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" | |
| 148 | + | ? "bg-raised font-medium text-fg before:absolute before:top-1.5 before:bottom-1.5 before:left-4.75 before:z-10 before:w-px before:bg-accent" | |
| 149 | 149 | : item.soon | |
| 150 | 150 | ? "text-faint hover:bg-raised/60 hover:text-muted" | |
| 151 | 151 | : "text-muted hover:bg-raised/60 hover:text-fg" | |
| ⋯ | |||
| 185 | 185 | )} | |
| 186 | 186 | <ChevronRight size={14} aria-hidden="true" className="shrink-0 text-faint transition-transform duration-150 group-open/section:rotate-90" /> | |
| 187 | 187 | </summary> | |
| 188 | − | <ul 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"> | |
| 188 | + | <ul className="relative mt-px mb-1 space-y-px before:absolute before:top-1 before:bottom-1 before:left-4.75 before:w-px before:bg-line"> | |
| 189 | 189 | {group.items.map((item) => ( | |
| 190 | 190 | <li key={item.to}> | |
| 191 | 191 | <SubLink item={item} pathname={pathname} counts={counts} /> | |
| 115 | 115 | return ( | |
| 116 | 116 | <main className="mx-auto max-w-xl px-4 py-24 text-center"> | |
| 117 | 117 | <h1 className="text-2xl font-semibold tracking-tight">{title}</h1> | |
| 118 | − | <p className="mt-3 break-words text-muted">{details}</p> | |
| 118 | + | <p className="mt-3 wrap-break-word text-muted">{details}</p> | |
| 119 | 119 | <div className="mt-8"> | |
| 120 | 120 | <ButtonLink to="/" variant="quiet"> | |
| 121 | 121 | Back to the overview |
| 59 | 59 | <When at={event.time} time /> | |
| 60 | 60 | </span> | |
| 61 | 61 | </p> | |
| 62 | − | <p className="font-mono text-xs break-words text-faint"> | |
| 62 | + | <p className="font-mono text-xs wrap-break-word text-faint"> | |
| 63 | 63 | {metricsLine(event.data.metrics)} | |
| 64 | 64 | {event.data.run ? ` · run ${event.data.run}` : ""} · sandbox {event.data.sandbox} | |
| 65 | 65 | </p> |
| 138 | 138 | </Link> | |
| 139 | 139 | )} | |
| 140 | 140 | </p> | |
| 141 | − | {entry.detail && <p className="mt-0.5 text-sm break-words text-muted">{entry.detail}</p>} | |
| 141 | + | {entry.detail && <p className="mt-0.5 text-sm wrap-break-word text-muted">{entry.detail}</p>} | |
| 142 | 142 | </div> | |
| 143 | 143 | <p className="flex shrink-0 items-center gap-1 text-xs text-faint sm:w-56 sm:justify-end sm:pt-0.5"> | |
| 144 | 144 | {stripe && entry.by === "stripe" ? ( |
| 49 | 49 | <p className="text-sm text-muted">Nothing has drifted.</p> | |
| 50 | 50 | ) : ( | |
| 51 | 51 | <div className="-mx-4 overflow-x-auto sm:-mx-5"> | |
| 52 | − | <table className="w-full min-w-[44rem] text-sm"> | |
| 52 | + | <table className="w-full min-w-176 text-sm"> | |
| 53 | 53 | <thead> | |
| 54 | 54 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 55 | 55 | <th className="px-4 py-2 font-medium sm:px-5">Product</th> | |
| ⋯ | |||
| 102 | 102 | ||
| 103 | 103 | <Section className="mt-6" title="Price versions" description="Every price as it was and will be. A version never changes once written; each charge records the version it was made at."> | |
| 104 | 104 | <div className="-mx-4 overflow-x-auto sm:-mx-5"> | |
| 105 | − | <table className="w-full min-w-[44rem] text-sm"> | |
| 105 | + | <table className="w-full min-w-176 text-sm"> | |
| 106 | 106 | <thead> | |
| 107 | 107 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 108 | 108 | <th className="px-4 py-2 font-medium sm:px-5">Meter</th> | |
| ⋯ | |||
| 151 | 151 | <EmptyState title="No lines yet">The bill is read once a day at 04:17 UTC, or now with “Run the analysis now”.</EmptyState> | |
| 152 | 152 | ) : ( | |
| 153 | 153 | <div className="-mx-4 overflow-x-auto sm:-mx-5"> | |
| 154 | − | <table className="w-full min-w-[48rem] text-sm"> | |
| 154 | + | <table className="w-full min-w-3xl text-sm"> | |
| 155 | 155 | <thead> | |
| 156 | 156 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 157 | 157 | <th className="px-4 py-2 font-medium sm:px-5">Product / meter</th> | |
| ⋯ | |||
| 217 | 217 | </Field> | |
| 218 | 218 | <div className="flex flex-col justify-end gap-2"> | |
| 219 | 219 | <label className="flex items-center gap-2 text-sm text-muted"> | |
| 220 | − | <input type="checkbox" name="scaleToOwn" className="accent-[var(--g1t-accent)]" /> Scale to g1t's count | |
| 220 | + | <input type="checkbox" name="scaleToOwn" className="accent-(--g1t-accent)" /> Scale to g1t's count | |
| 221 | 221 | </label> | |
| 222 | 222 | <div className="flex gap-2"> | |
| 223 | 223 | <Button type="submit">Save mapping</Button> | |
| ⋯ | |||
| 238 | 238 | <form method="post" action="#guardrails" className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4"> | |
| 239 | 239 | <input type="hidden" name="intent" value="settings" /> | |
| 240 | 240 | <Field label="Apply small moves on their own"> | |
| 241 | − | <label className="flex h-[38px] items-center gap-2 text-sm text-fg-soft"> | |
| 242 | − | <input type="checkbox" name="autoApply" defaultChecked={report.settings.autoApply} className="accent-[var(--g1t-accent)]" /> On | |
| 241 | + | <label className="flex h-9.5 items-center gap-2 text-sm text-fg-soft"> | |
| 242 | + | <input type="checkbox" name="autoApply" defaultChecked={report.settings.autoApply} className="accent-(--g1t-accent)" /> On | |
| 243 | 243 | </label> | |
| 244 | 244 | </Field> | |
| 245 | 245 | <Field label="Up to, either way, %"> | |
| ⋯ | |||
| 264 | 264 | <Input name="anomalyFloor" inputMode="decimal" defaultValue={dollarsField(report.settings.anomalyFloorMicros)} /> | |
| 265 | 265 | </Field> | |
| 266 | 266 | <Field label="Card fee on card payments"> | |
| 267 | − | <label className="flex h-[38px] items-center gap-2 text-sm text-fg-soft"> | |
| 268 | − | <input type="checkbox" name="cardFee" defaultChecked={report.settings.cardFee ?? true} className="accent-[var(--g1t-accent)]" /> Its own line; never on invoiced billing or bank transfers | |
| 267 | + | <label className="flex h-9.5 items-center gap-2 text-sm text-fg-soft"> | |
| 268 | + | <input type="checkbox" name="cardFee" defaultChecked={report.settings.cardFee ?? true} className="accent-(--g1t-accent)" /> Its own line; never on invoiced billing or bank transfers | |
| 269 | 269 | </label> | |
| 270 | 270 | </Field> | |
| 271 | 271 | <div className="sm:col-span-2 lg:col-span-4"> | |
| ⋯ | |||
| 371 | 371 | </div> | |
| 372 | 372 | )} | |
| 373 | 373 | <div className="-mx-4 mt-3 overflow-x-auto sm:-mx-5"> | |
| 374 | − | <table className="w-full min-w-[48rem] text-sm"> | |
| 374 | + | <table className="w-full min-w-3xl text-sm"> | |
| 375 | 375 | <thead> | |
| 376 | 376 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 377 | 377 | <th className="px-4 py-2 font-medium sm:px-5">Meter</th> | |
| ⋯ | |||
| 419 | 419 | function MappingsTable({ report }: { report: CostsReport }) { | |
| 420 | 420 | return ( | |
| 421 | 421 | <div className="-mx-4 overflow-x-auto sm:-mx-5"> | |
| 422 | − | <table className="w-full min-w-[44rem] text-sm"> | |
| 422 | + | <table className="w-full min-w-176 text-sm"> | |
| 423 | 423 | <thead> | |
| 424 | 424 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 425 | 425 | <th className="px-4 py-2 font-medium sm:px-5">Cloudflare</th> | |
| 86 | 86 | description={`Each of g1t's products over the last ${range} days: what customers were charged at price, given away or not, against what it cost. The margin is the charge less the cost, as a share of the charge; at cost plus ${MARKUP_PERCENT}% it is ${percentLabel(marginOnPrice(MARKUP_PERCENT))}. The floor is ${floor}%.`} | |
| 87 | 87 | > | |
| 88 | 88 | <div className="-mx-4 overflow-x-auto sm:-mx-5"> | |
| 89 | − | <table className="w-full min-w-[44rem] text-sm"> | |
| 89 | + | <table className="w-full min-w-176 text-sm"> | |
| 90 | 90 | <thead> | |
| 91 | 91 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 92 | 92 | <th className="px-4 py-2 font-medium sm:px-5">Product</th> | |
| ⋯ | |||
| 138 | 138 | <p className="text-sm text-muted">No costs shared out yet.</p> | |
| 139 | 139 | ) : ( | |
| 140 | 140 | <div className="-mx-4 overflow-x-auto sm:-mx-5"> | |
| 141 | − | <table className="w-full min-w-[34rem] text-sm"> | |
| 141 | + | <table className="w-full min-w-136 text-sm"> | |
| 142 | 142 | <thead> | |
| 143 | 143 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 144 | 144 | <th className="px-4 py-2 font-medium sm:px-5">Workspace</th> | |
| ⋯ | |||
| 321 | 321 | /> | |
| 322 | 322 | </div> | |
| 323 | 323 | <div className="mt-3 overflow-x-auto rounded-lg border border-line bg-surface"> | |
| 324 | − | <table className="w-full min-w-[36rem] text-sm"> | |
| 324 | + | <table className="w-full min-w-xl text-sm"> | |
| 325 | 325 | <thead> | |
| 326 | 326 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 327 | 327 | <th className="px-4 py-2 font-medium sm:px-5"> | |
| ⋯ | |||
| 452 | 452 | </div> | |
| 453 | 453 | )} | |
| 454 | 454 | <div className="-mx-4 mt-5 overflow-x-auto sm:-mx-5"> | |
| 455 | − | <table className="w-full min-w-[34rem] text-sm"> | |
| 455 | + | <table className="w-full min-w-136 text-sm"> | |
| 456 | 456 | <thead> | |
| 457 | 457 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 458 | 458 | <th className="px-4 py-2 font-medium sm:px-5">{caps.month}, so far</th> | |
| ⋯ | |||
| 646 | 646 | <p className="mt-2 text-sm text-muted">Nothing read yet.</p> | |
| 647 | 647 | ) : ( | |
| 648 | 648 | <div className="-mx-4 mt-2 overflow-x-auto sm:-mx-5"> | |
| 649 | − | <table className="w-full min-w-[36rem] text-sm"> | |
| 649 | + | <table className="w-full min-w-xl text-sm"> | |
| 650 | 650 | <thead> | |
| 651 | 651 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 652 | 652 | <th className="px-4 py-2 font-medium sm:px-5">Metric</th> | |
| 113 | 113 | {credits && credits.months.length > 0 && ( | |
| 114 | 114 | <Section className="mt-6" title="By month" description="The last 12 months by kind. Spent is what credit paid for that month, whenever it was given."> | |
| 115 | 115 | <div className="-mx-4 -my-4 overflow-x-auto sm:-mx-5 sm:-my-5"> | |
| 116 | − | <table className="w-full min-w-[34rem] text-sm"> | |
| 116 | + | <table className="w-full min-w-136 text-sm"> | |
| 117 | 117 | <thead> | |
| 118 | 118 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 119 | 119 | <th className="px-4 py-2 font-medium sm:pl-5">Month</th> |
| 86 | 86 | key={s.value} | |
| 87 | 87 | className="flex cursor-pointer gap-2.5 rounded-md border border-line px-3 py-2 has-checked:border-accent/60 has-checked:bg-accent/8" | |
| 88 | 88 | > | |
| 89 | − | <input type="radio" name="severity" value={s.value} required defaultChecked={(v.severity || "sev3") === s.value} className="mt-1 accent-[var(--g1t-accent)]" /> | |
| 89 | + | <input type="radio" name="severity" value={s.value} required defaultChecked={(v.severity || "sev3") === s.value} className="mt-1 accent-(--g1t-accent)" /> | |
| 90 | 90 | <span> | |
| 91 | 91 | <span className="block text-sm font-semibold">{s.label}</span> | |
| 92 | 92 | <span className="block text-xs text-muted">{s.about}</span> |
| 176 | 176 | {incident.source === "detected" && <Badge tone="warn">Detected by the checks</Badge>} | |
| 177 | 177 | {incident.visibility !== "public" && <Badge>Not on the status page</Badge>} | |
| 178 | 178 | </div> | |
| 179 | − | <h1 className="mt-2 text-2xl font-semibold tracking-tight break-words">{incident.title}</h1> | |
| 179 | + | <h1 className="mt-2 text-2xl font-semibold tracking-tight wrap-break-word">{incident.title}</h1> | |
| 180 | 180 | <p className="mt-1 text-sm text-muted"> | |
| 181 | 181 | Impact began <When at={incident.started_at} time /> · declared by <span className="font-mono text-xs">{incident.created_by === "status" ? "the checks" : incident.created_by}</span> | |
| 182 | 182 | {incident.commander && ( | |
| ⋯ | |||
| 216 | 216 | {!draft && ( | |
| 217 | 217 | <fieldset className="flex flex-wrap gap-2" aria-label="Who sees it"> | |
| 218 | 218 | <label className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-line px-3 py-1.5 text-sm has-checked:border-accent/60 has-checked:bg-accent/10"> | |
| 219 | − | <input type="radio" name="visibility" value="public" defaultChecked className="accent-[var(--g1t-accent)]" /> | |
| 219 | + | <input type="radio" name="visibility" value="public" defaultChecked className="accent-(--g1t-accent)" /> | |
| 220 | 220 | <Globe size={14} aria-hidden="true" /> Public update | |
| 221 | 221 | </label> | |
| 222 | 222 | <label className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-line px-3 py-1.5 text-sm has-checked:border-line-strong has-checked:bg-raised"> | |
| 223 | − | <input type="radio" name="visibility" value="internal" className="accent-[var(--g1t-accent)]" /> | |
| 223 | + | <input type="radio" name="visibility" value="internal" className="accent-(--g1t-accent)" /> | |
| 224 | 224 | <Lock size={14} aria-hidden="true" /> Internal note | |
| 225 | 225 | </label> | |
| 226 | 226 | </fieldset> | |
| ⋯ | |||
| 260 | 260 | <div className="flex flex-wrap items-center justify-between gap-3"> | |
| 261 | 261 | {!draft ? ( | |
| 262 | 262 | <label className="flex items-center gap-2 text-sm"> | |
| 263 | − | <input type="checkbox" name="notify" defaultChecked={notifyByDefault(incident.severity)} className="accent-[var(--g1t-accent)]" /> | |
| 263 | + | <input type="checkbox" name="notify" defaultChecked={notifyByDefault(incident.severity)} className="accent-(--g1t-accent)" /> | |
| 264 | 264 | Email subscribers (public updates only) | |
| 265 | 265 | </label> | |
| 266 | 266 | ) : ( | |
| ⋯ | |||
| 325 | 325 | </Field> | |
| 326 | 326 | <div className="flex flex-wrap items-center justify-between gap-3"> | |
| 327 | 327 | <label className="flex items-center gap-2 text-sm"> | |
| 328 | − | <input type="checkbox" name="notify" defaultChecked={notifyByDefault(incident.severity)} className="accent-[var(--g1t-accent)]" /> | |
| 328 | + | <input type="checkbox" name="notify" defaultChecked={notifyByDefault(incident.severity)} className="accent-(--g1t-accent)" /> | |
| 329 | 329 | Email subscribers | |
| 330 | 330 | </label> | |
| 331 | 331 | <Button type="submit" variant="lavender"> | |
| ⋯ | |||
| 376 | 376 | {errorFor("resolve") && <Notice tone="error">{errorFor("resolve")}</Notice>} | |
| 377 | 377 | <Textarea name="message" rows={3} required maxLength={4000} aria-label="Resolution update" placeholder="Pushes work again. A bad deploy was rolled back; nothing was lost." /> | |
| 378 | 378 | <label className="flex items-center gap-2 text-sm"> | |
| 379 | − | <input type="checkbox" name="notify" defaultChecked={notifyByDefault(incident.severity)} className="accent-[var(--g1t-accent)]" /> | |
| 379 | + | <input type="checkbox" name="notify" defaultChecked={notifyByDefault(incident.severity)} className="accent-(--g1t-accent)" /> | |
| 380 | 380 | Email subscribers | |
| 381 | 381 | </label> | |
| 382 | 382 | <Button type="submit" variant="lavender" className="w-full"> | |
| 63 | 63 | if (invites.length === 0) return <EmptyState title="No invites">Nothing matches.</EmptyState>; | |
| 64 | 64 | return ( | |
| 65 | 65 | <div className="overflow-x-auto rounded-lg border border-line bg-surface"> | |
| 66 | − | <table className="w-full min-w-[38rem] text-sm"> | |
| 66 | + | <table className="w-full min-w-152 text-sm"> | |
| 67 | 67 | <thead> | |
| 68 | 68 | <tr className="text-left text-xs text-faint"> | |
| 69 | 69 | <th className="px-4 py-2 font-medium">Code</th> |
| 90 | 90 | key={c.key} | |
| 91 | 91 | className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-line px-2.5 py-1.5 text-sm has-checked:border-accent/60 has-checked:bg-accent/10" | |
| 92 | 92 | > | |
| 93 | − | <input type="checkbox" name="components" value={c.key} defaultChecked={failed?.chosen.includes(c.key)} className="accent-[var(--g1t-accent)]" /> | |
| 93 | + | <input type="checkbox" name="components" value={c.key} defaultChecked={failed?.chosen.includes(c.key)} className="accent-(--g1t-accent)" /> | |
| 94 | 94 | {c.name} | |
| 95 | 95 | </label> | |
| 96 | 96 | ))} | |
| ⋯ | |||
| 99 | 99 | <Section title="Message" description="What will happen and what people will notice. Shown publicly."> | |
| 100 | 100 | <Textarea name="message" rows={4} required maxLength={4000} aria-label="Message" defaultValue={v.message} placeholder="We are upgrading the database behind git. Pushes may pause for up to five minutes; clones and the website keep working." /> | |
| 101 | 101 | <label className="mt-3 flex items-center gap-2 text-sm"> | |
| 102 | − | <input type="checkbox" name="notify" defaultChecked className="accent-[var(--g1t-accent)]" /> | |
| 102 | + | <input type="checkbox" name="notify" defaultChecked className="accent-(--g1t-accent)" /> | |
| 103 | 103 | Email subscribers now, when it starts, and when it ends | |
| 104 | 104 | </label> | |
| 105 | 105 | </Section> | |
| 93 | 93 | <Textarea name="message" rows={3} maxLength={4000} aria-label="Update" placeholder="Running a little long: about 20 more minutes." /> | |
| 94 | 94 | <div className="flex flex-wrap items-center justify-between gap-3"> | |
| 95 | 95 | <label className="flex items-center gap-2 text-sm"> | |
| 96 | − | <input type="checkbox" name="notify" className="accent-[var(--g1t-accent)]" /> | |
| 96 | + | <input type="checkbox" name="notify" className="accent-(--g1t-accent)" /> | |
| 97 | 97 | Email subscribers | |
| 98 | 98 | </label> | |
| 99 | 99 | <Button type="submit" name="intent" value="update" variant="lavender"> | |
| ⋯ | |||
| 128 | 128 | <Textarea name="message" rows={2} maxLength={4000} placeholder="Said for you when empty." /> | |
| 129 | 129 | </Field> | |
| 130 | 130 | <label className="flex items-center gap-2 text-sm"> | |
| 131 | − | <input type="checkbox" name="notify" defaultChecked className="accent-[var(--g1t-accent)]" /> | |
| 131 | + | <input type="checkbox" name="notify" defaultChecked className="accent-(--g1t-accent)" /> | |
| 132 | 132 | Email subscribers | |
| 133 | 133 | </label> | |
| 134 | 134 | <div className="flex flex-wrap gap-2"> | |
| 194 | 194 | <div className="grid gap-5 p-4 sm:p-5 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]"> | |
| 195 | 195 | <div className="min-w-0 space-y-4"> | |
| 196 | 196 | {row.request && ( | |
| 197 | − | <blockquote className="border-l-2 border-warn/50 pl-3 text-sm break-words whitespace-pre-line text-fg-soft"> | |
| 197 | + | <blockquote className="border-l-2 border-warn/50 pl-3 text-sm wrap-break-word whitespace-pre-line text-fg-soft"> | |
| 198 | 198 | “{row.request.reason}” <span className="text-xs text-faint">— {row.request.createdBy}</span> | |
| 199 | 199 | </blockquote> | |
| 200 | 200 | )} | |
| ⋯ | |||
| 215 | 215 | {row.topEntries.slice(0, 5).map((entry) => ( | |
| 216 | 216 | <li key={entry.id} className="flex items-start justify-between gap-3 px-3 py-2"> | |
| 217 | 217 | <span className="min-w-0"> | |
| 218 | − | <span className="block break-words text-fg-soft">{entry.description}</span> | |
| 218 | + | <span className="block wrap-break-word text-fg-soft">{entry.description}</span> | |
| 219 | 219 | <span className="font-mono text-faint"> | |
| 220 | 220 | {[entry.repo && (entry.number != null ? `${entry.repo}#${entry.number}` : entry.repo), entry.task, entry.model].filter(Boolean).join(" · ")} | |
| 221 | 221 | </span> | |
| 133 | 133 | <p className="text-sm text-muted">Nothing given yet this month.</p> | |
| 134 | 134 | ) : ( | |
| 135 | 135 | <div className="-mx-4 -my-4 overflow-x-auto sm:-mx-5 sm:-my-5"> | |
| 136 | − | <table className="w-full min-w-[26rem] text-sm"> | |
| 136 | + | <table className="w-full min-w-104 text-sm"> | |
| 137 | 137 | <thead> | |
| 138 | 138 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 139 | 139 | <th className="px-4 py-2 font-medium sm:pl-5">Source</th> |
| 164 | 164 | </div> | |
| 165 | 165 | </dl> | |
| 166 | 166 | )} | |
| 167 | − | <blockquote className="border-l-2 border-line-strong pl-3 text-sm break-words whitespace-pre-line text-fg-soft">{request.reason}</blockquote> | |
| 167 | + | <blockquote className="border-l-2 border-line-strong pl-3 text-sm wrap-break-word whitespace-pre-line text-fg-soft">{request.reason}</blockquote> | |
| 168 | 168 | {request.answer && ( | |
| 169 | 169 | <p className="text-sm text-muted"> | |
| 170 | 170 | Answer{request.decidedBy ? <> by <span className="font-mono">{request.decidedBy}</span></> : null}: {request.answer} |
| 254 | 254 | <ul className="divide-y divide-line text-sm"> | |
| 255 | 255 | {user.log.map((event, at) => ( | |
| 256 | 256 | <li key={`${event.createdAt}:${at}`} className="flex flex-col gap-1 py-2 sm:flex-row sm:justify-between sm:gap-4"> | |
| 257 | − | <span className="min-w-0 break-words"> | |
| 257 | + | <span className="min-w-0 wrap-break-word"> | |
| 258 | 258 | {securityEventLabel(event)} | |
| 259 | 259 | {event.staff && ( | |
| 260 | 260 | <span className="text-muted"> | |
| ⋯ | |||
| 441 | 441 | <Input name="confirm" required autoComplete="off" spellCheck={false} className="font-mono" /> | |
| 442 | 442 | </Field> | |
| 443 | 443 | <label className="flex items-start gap-2 text-sm"> | |
| 444 | − | <input type="checkbox" name="acknowledge" required className="mt-0.5 accent-[var(--g1t-accent)]" /> | |
| 444 | + | <input type="checkbox" name="acknowledge" required className="mt-0.5 accent-(--g1t-accent)" /> | |
| 445 | 445 | <span> | |
| 446 | 446 | {sole.length === 1 ? ( | |
| 447 | 447 | <> | |
| 43 | 43 | <EmptyState title="Nobody is spending right now" /> | |
| 44 | 44 | ) : ( | |
| 45 | 45 | <div className="overflow-x-auto rounded-lg border border-line bg-surface"> | |
| 46 | − | <table className="w-full min-w-[46rem] text-sm"> | |
| 46 | + | <table className="w-full min-w-184 text-sm"> | |
| 47 | 47 | <thead> | |
| 48 | 48 | <tr className="border-b border-line text-left text-xs text-muted"> | |
| 49 | 49 | <th className="px-4 py-2 font-medium">Workspace</th> |
| 67 | 67 | </ul> | |
| 68 | 68 | </Card> | |
| 69 | 69 | <Card tone="plain" className="hidden overflow-x-auto sm:block"> | |
| 70 | − | <table className="w-full min-w-[34rem] text-sm"> | |
| 70 | + | <table className="w-full min-w-136 text-sm"> | |
| 71 | 71 | <thead> | |
| 72 | 72 | <tr className="border-b border-line bg-surface text-left text-xs text-muted"> | |
| 73 | 73 | <th scope="col" className="px-4 py-2.5 font-medium"> | |
| 74 | 74 | Can | |
| 75 | 75 | </th> | |
| 76 | 76 | {REPO_ROLES.map((role) => ( | |
| 77 | − | <th key={role} scope="col" className="w-[4.5rem] px-1 py-2.5 text-center font-medium"> | |
| 77 | + | <th key={role} scope="col" className="w-18 px-1 py-2.5 text-center font-medium"> | |
| 78 | 78 | {REPO_ROLE_LABELS[role]} | |
| 79 | 79 | </th> | |
| 80 | 80 | ))} |
| 93 | 93 | <code> | |
| 94 | 94 | {agent.lang === "sh" | |
| 95 | 95 | ? agent.code.split("\n").map((line, index) => ( | |
| 96 | − | <span key={index} className="block whitespace-pre-wrap [overflow-wrap:anywhere]"> | |
| 96 | + | <span key={index} className="block whitespace-pre-wrap wrap-anywhere"> | |
| 97 | 97 | <span className="mr-2 text-faint select-none">$</span> | |
| 98 | 98 | {line} | |
| 99 | 99 | </span> |
| 24 | 24 | return ( | |
| 25 | 25 | <section | |
| 26 | 26 | aria-label="Analytics cookie" | |
| 27 | − | className="fixed inset-x-3 bottom-3 z-[60] max-md:bottom-[calc(var(--tabbar-h)+0.75rem)] max-md:in-data-[keyboard=open]:bottom-3 mx-auto max-w-lg rounded-2xl bg-surface p-4 text-sm shadow-2xl shadow-black/50 ring-1 ring-line-strong sm:inset-x-auto sm:right-4 sm:bottom-4" | |
| 27 | + | className="fixed inset-x-3 bottom-3 z-60 max-md:bottom-[calc(var(--tabbar-h)+0.75rem)] max-md:in-data-[keyboard=open]:bottom-3 mx-auto max-w-lg rounded-2xl bg-surface p-4 text-sm shadow-2xl shadow-black/50 ring-1 ring-line-strong sm:inset-x-auto sm:right-4 sm:bottom-4" | |
| 28 | 28 | > | |
| 29 | 29 | <p className="leading-6 text-fg-soft"> | |
| 30 | 30 | May g1t count how its pages are used? It sets one analytics cookie, and names inside your workspaces are |
| 10 | 10 | import { Avatar } from "./ui/avatar"; | |
| 11 | 11 | import { Card } from "./ui/card"; | |
| 12 | 12 | import { Hint } from "./ui/hint"; | |
| 13 | + | import { UserAvatar } from "./user-card"; | |
| 14 | + | import { PersonLink } from "./work"; | |
| 13 | 15 | ||
| 14 | 16 | /** Allowed or denied, and by which rule. */ | |
| 15 | 17 | export function OutcomeMark({ entry }: { entry: AuditEntry }) { | |
| ⋯ | |||
| 28 | 30 | ); | |
| 29 | 31 | } | |
| 30 | 32 | ||
| 31 | − | /** Who acted, with whoever acted for someone (an app, or an old agent) shown as working for them. */ | |
| 33 | + | /** | |
| 34 | + | * Who acted, with whoever acted for someone (an app, or an old agent) shown | |
| 35 | + | * as working for them. A person wears their own avatar and opens their | |
| 36 | + | * card; a workspace its square, never a person's face. | |
| 37 | + | */ | |
| 32 | 38 | export function ActorLine({ entry }: { entry: AuditEntry }) { | |
| 39 | + | const person = entry.actorKind === "person" && !entry.agent; | |
| 40 | + | const face = person ? ( | |
| 41 | + | <UserAvatar username={entry.actor} size={18} /> | |
| 42 | + | ) : ( | |
| 43 | + | <Avatar | |
| 44 | + | name={entry.agent ?? entry.actor} | |
| 45 | + | image={null} | |
| 46 | + | size={18} | |
| 47 | + | square={entry.actorKind === "workspace"} | |
| 48 | + | system={entry.actorKind === "system"} | |
| 49 | + | agent={entry.actorKind === "agent" || !!entry.agent} | |
| 50 | + | /> | |
| 51 | + | ); | |
| 33 | 52 | return ( | |
| 34 | 53 | <span className="flex min-w-0 items-center gap-2"> | |
| 35 | − | <Avatar name={entry.agent ?? entry.actor} size={18} square={entry.actorKind === "workspace"} /> | |
| 54 | + | {face} | |
| 36 | 55 | <span className="truncate"> | |
| 37 | 56 | {entry.onBehalfOf ? ( | |
| 38 | 57 | <> | |
| 39 | 58 | <span className="font-medium">{entry.agent ?? entry.actor}</span> | |
| 40 | 59 | <span className="text-muted"> on behalf of </span> | |
| 41 | − | <span className="font-medium">{entry.onBehalfOf}</span> | |
| 60 | + | <PersonLink name={entry.onBehalfOf} className="font-medium hover:text-accent" /> | |
| 42 | 61 | </> | |
| 62 | + | ) : person ? ( | |
| 63 | + | <PersonLink name={entry.actor} className="font-medium hover:text-accent" /> | |
| 43 | 64 | ) : ( | |
| 44 | 65 | <span className="font-medium">{entry.actor}</span> | |
| 45 | 66 | )} | |
| 40 | 40 | <Form method="post" encType="multipart/form-data" ref={form}> | |
| 41 | 41 | <input type="hidden" name="intent" value="avatar" /> | |
| 42 | 42 | <label | |
| 43 | − | className={`inline-flex cursor-pointer items-center gap-2 rounded-md px-3.5 py-2 text-sm font-medium ring-1 ring-line transition-colors hover:ring-line-strong has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-accent ${ | |
| 43 | + | className={`inline-flex cursor-pointer items-center gap-2 rounded-md px-3.5 py-2 text-sm font-medium ring-1 ring-line transition-colors hover:ring-line-strong has-focus-visible:ring-2 has-focus-visible:ring-accent ${ | |
| 44 | 44 | busy ? "pointer-events-none opacity-50" : "" | |
| 45 | 45 | }`} | |
| 46 | 46 | > |
| 289 | 289 | {current.issue.title} <span className="font-normal text-faint">#{current.issue.number}</span> | |
| 290 | 290 | </Link> | |
| 291 | 291 | {current.issue.body && ( | |
| 292 | − | <div className="mt-2 max-h-48 overflow-hidden text-muted [&_*]:text-[0.8125rem]! [&_*]:leading-5! mask-[linear-gradient(to_bottom,black_70%,transparent)]"> | |
| 292 | + | <div className="mt-2 max-h-48 overflow-hidden text-muted **:text-[0.8125rem]! **:leading-5! mask-[linear-gradient(to_bottom,black_70%,transparent)]"> | |
| 293 | 293 | <Markdown source={current.issue.body} repo={repoAt(base)} /> | |
| 294 | 294 | </div> | |
| 295 | 295 | )} |
| 136 | 136 | <section | |
| 137 | 137 | aria-label="Notices" | |
| 138 | 138 | aria-live="polite" | |
| 139 | − | className="pointer-events-none fixed z-[66] flex flex-col items-center gap-2 max-sm:inset-x-3 max-sm:top-[calc(env(safe-area-inset-top)+0.75rem)] sm:bottom-28 sm:left-1/2 sm:w-[min(26rem,calc(100vw-2rem))] sm:-translate-x-1/2" | |
| 139 | + | className="pointer-events-none fixed z-66 flex flex-col items-center gap-2 max-sm:inset-x-3 max-sm:top-[calc(env(safe-area-inset-top)+0.75rem)] sm:bottom-28 sm:left-1/2 sm:w-[min(26rem,calc(100vw-2rem))] sm:-translate-x-1/2" | |
| 140 | 140 | > | |
| 141 | 141 | {list.map((toast) => ( | |
| 142 | 142 | <div | |
| ⋯ | |||
| 239 | 239 | {card.fields.map((field) => ( | |
| 240 | 240 | <div key={field.label} className="min-w-0"> | |
| 241 | 241 | <dt className="text-[0.6875rem] font-medium text-faint">{field.label}</dt> | |
| 242 | − | <dd className="line-clamp-2 text-xs break-words text-fg-soft"> | |
| 242 | + | <dd className="line-clamp-2 text-xs wrap-break-word text-fg-soft"> | |
| 243 | 243 | {field.value} | |
| 244 | 244 | </dd> | |
| 245 | 245 | </div> | |
| ⋯ | |||
| 292 | 292 | <div | |
| 293 | 293 | ref={box} | |
| 294 | 294 | id={id} | |
| 295 | − | className={cn("card-md relative rounded-lg border border-line/70 bg-bg/40 px-3 py-2", !open && "max-h-[11.5rem] overflow-hidden")} | |
| 295 | + | className={cn("card-md relative rounded-lg border border-line/70 bg-bg/40 px-3 py-2", !open && "max-h-46 overflow-hidden")} | |
| 296 | 296 | > | |
| 297 | 297 | <Markdown source={body} repo={repoAt(repo)} /> | |
| 298 | 298 | {folded && ( | |
| 299 | − | <span aria-hidden="true" className="pointer-events-none absolute inset-x-0 bottom-0 h-12 rounded-b-lg bg-gradient-to-t from-surface to-transparent" /> | |
| 299 | + | <span aria-hidden="true" className="pointer-events-none absolute inset-x-0 bottom-0 h-12 rounded-b-lg bg-linear-to-t from-surface to-transparent" /> | |
| 300 | 300 | )} | |
| 301 | 301 | </div> | |
| 302 | 302 | {(folded || open) && ( | |
| 639 | 639 | const element = event.currentTarget; | |
| 640 | 640 | stick.current = atBottom(element); | |
| 641 | 641 | }} | |
| 642 | − | className="min-h-0 grow overflow-y-auto overscroll-contain [scrollbar-gutter:stable] [scrollbar-width:thin]" | |
| 642 | + | className="min-h-0 grow overflow-y-auto overscroll-contain scrollbar-gutter-stable scrollbar-thin" | |
| 643 | 643 | > | |
| 644 | − | <div className="mx-auto flex min-h-full max-w-[56rem] flex-col justify-end px-2 pt-6 pb-3 sm:px-4"> | |
| 644 | + | <div className="mx-auto flex min-h-full max-w-4xl flex-col justify-end px-2 pt-6 pb-3 sm:px-4"> | |
| 645 | 645 | {older ? ( | |
| 646 | 646 | <div className="flex justify-center pb-4"> | |
| 647 | 647 | <Button | |
| ⋯ | |||
| 682 | 682 | </div> | |
| 683 | 683 | </div> | |
| 684 | 684 | <div className="shrink-0 px-2 pb-3 sm:px-4 sm:pb-4 max-md:pb-[max(0.75rem,env(safe-area-inset-bottom))] in-data-[keyboard=open]:pb-2"> | |
| 685 | − | <div className="mx-auto max-w-[56rem]"> | |
| 685 | + | <div className="mx-auto max-w-4xl"> | |
| 686 | 686 | <TypingLine typers={typers} /> | |
| 687 | 687 | {archived ? ( | |
| 688 | 688 | <Card className="flex flex-wrap items-center justify-between gap-3 px-4 py-3"> | |
| ⋯ | |||
| 1017 | 1017 | .reverse(); | |
| 1018 | 1018 | const time = card ? localTime(card.timezone, Date.now()) : null; | |
| 1019 | 1019 | return ( | |
| 1020 | − | <div className="min-h-0 grow overflow-y-auto p-5 [scrollbar-width:thin]"> | |
| 1020 | + | <div className="min-h-0 grow overflow-y-auto p-5 scrollbar-thin"> | |
| 1021 | 1021 | <WithPresence person={{ username }} size={88}> | |
| 1022 | 1022 | <Avatar name={username} image={card?.avatar ?? null} size={88} /> | |
| 1023 | 1023 | </WithPresence> | |
| ⋯ | |||
| 1218 | 1218 | {...press} | |
| 1219 | 1219 | aria-label={`${shownName(author)}, ${time}`} | |
| 1220 | 1220 | className={`group/message relative flex gap-3 rounded-lg px-2 transition-colors hover:bg-surface/70 sm:px-3 max-md:min-h-11 max-md:[-webkit-touch-callout:none] max-md:active:bg-surface/70 ${head ? "mt-3 pt-1 pb-1" : "py-0.5"} ${ | |
| 1221 | − | threadOpen ? "bg-accent/[0.06] hover:bg-accent/[0.08]" : "" | |
| 1221 | + | threadOpen ? "bg-accent/6 hover:bg-accent/8" : "" | |
| 1222 | 1222 | }`} | |
| 1223 | 1223 | > | |
| 1224 | 1224 | <div className="w-9 shrink-0"> | |
| ⋯ | |||
| 1285 | 1285 | )} | |
| 1286 | 1286 | </div> | |
| 1287 | 1287 | {!inThread && onThread && !message.pending && ( | |
| 1288 | − | <Card radius="lg" className="absolute -top-3 right-3 hidden p-0.5 shadow-lg shadow-black/30 group-hover/message:flex group-focus-within/message:flex has-[[data-state=open]]:flex"> | |
| 1288 | + | <Card radius="lg" className="absolute -top-3 right-3 hidden p-0.5 shadow-lg shadow-black/30 group-hover/message:flex group-focus-within/message:flex has-data-[state=open]:flex"> | |
| 1289 | 1289 | <AddReaction messageId={message.id} /> | |
| 1290 | 1290 | <Hint label="Reply in thread"> | |
| 1291 | 1291 | <Button | |
| ⋯ | |||
| 1389 | 1389 | <aside | |
| 1390 | 1390 | {...swipe} | |
| 1391 | 1391 | aria-label={title} | |
| 1392 | − | className="fixed inset-0 z-40 flex flex-col bg-bg lg:static lg:z-auto lg:w-[22rem] lg:shrink-0 lg:border-l lg:border-line xl:w-[24rem] max-md:top-(--vv-top,0px) max-md:bottom-auto max-md:h-(--vv-height,100dvh) max-md:pb-[env(safe-area-inset-bottom)]" | |
| 1392 | + | className="fixed inset-0 z-40 flex flex-col bg-bg lg:static lg:z-auto lg:w-88 lg:shrink-0 lg:border-l lg:border-line xl:w-[24rem] max-md:top-(--vv-top,0px) max-md:bottom-auto max-md:h-(--vv-height,100dvh) max-md:pb-[env(safe-area-inset-bottom)]" | |
| 1393 | 1393 | > | |
| 1394 | 1394 | <div className="flex h-14 shrink-0 items-center gap-2 border-b border-line px-4 max-md:h-[calc(3.5rem+env(safe-area-inset-top))] max-md:pt-[env(safe-area-inset-top)] max-md:pl-1.5"> | |
| 1395 | 1395 | <Button type="button" onClick={onClose} aria-label="Back" variant="ghost" size="icon-lg" className="rounded-full active:bg-raised lg:hidden"> | |
| ⋯ | |||
| 1449 | 1449 | onScroll={(event) => { | |
| 1450 | 1450 | stick.current = atBottom(event.currentTarget); | |
| 1451 | 1451 | }} | |
| 1452 | − | className="min-h-0 grow overflow-y-auto overscroll-contain px-1 py-3 [scrollbar-width:thin]" | |
| 1452 | + | className="min-h-0 grow overflow-y-auto overscroll-contain px-1 py-3 scrollbar-thin" | |
| 1453 | 1453 | > | |
| 1454 | 1454 | {/* One box for what scrolls, so its growing is seen (./stick.ts). */} | |
| 1455 | 1455 | <div> | |
| ⋯ | |||
| 1655 | 1655 | const channel = data.channel; | |
| 1656 | 1656 | const humans = members.filter((m) => m.member.kind === "user"); | |
| 1657 | 1657 | return ( | |
| 1658 | − | <div className="min-h-0 grow overflow-y-auto p-4 [scrollbar-width:thin]"> | |
| 1658 | + | <div className="min-h-0 grow overflow-y-auto p-4 scrollbar-thin"> | |
| 1659 | 1659 | {channel.kind === "channel" && <ChannelAbout channel={channel} joined={joined} canManage={canManage} onUpdate={onUpdate} />} | |
| 1660 | 1660 | <section className="mt-5"> | |
| 1661 | 1661 | <div className="mb-1 flex items-center justify-between px-1"> | |
| 569 | 569 | /> | |
| 570 | 570 | )} | |
| 571 | 571 | <div | |
| 572 | − | className={`rounded-xl border border-line-strong bg-surface transition-colors focus-within:border-accent/35 focus-within:ring-4 focus-within:ring-accent/[0.06] ${disabled ? "opacity-60" : ""}`} | |
| 572 | + | className={`rounded-xl border border-line-strong bg-surface transition-colors focus-within:border-accent/35 focus-within:ring-4 focus-within:ring-accent/6 ${disabled ? "opacity-60" : ""}`} | |
| 573 | 573 | > | |
| 574 | 574 | {toolbar && ( | |
| 575 | 575 | <div | |
| 576 | 576 | role="toolbar" | |
| 577 | 577 | aria-label="Formatting" | |
| 578 | − | className="flex items-center gap-0.5 overflow-x-auto border-b border-line px-1.5 py-1 [scrollbar-width:none]" | |
| 578 | + | className="flex items-center gap-0.5 overflow-x-auto border-b border-line px-1.5 py-1 scrollbar-none" | |
| 579 | 579 | > | |
| 580 | 580 | {tool("Bold", <Bold size={15} strokeWidth={2.4} />, state?.bold, () => chain().toggleBold().run())} | |
| 581 | 581 | {tool("Italic", <Italic size={15} />, state?.italic, () => chain().toggleItalic().run())} | |
| ⋯ | |||
| 593 | 593 | </div> | |
| 594 | 594 | )} | |
| 595 | 595 | <div | |
| 596 | − | className={`overflow-y-auto px-3.5 text-[0.9375rem] leading-6 text-fg [scrollbar-width:thin] ${compact ? "pt-2.5 pb-1" : "pt-3 pb-1.5"}`} | |
| 596 | + | className={`overflow-y-auto px-3.5 text-[0.9375rem] leading-6 text-fg scrollbar-thin ${compact ? "pt-2.5 pb-1" : "pt-3 pb-1.5"}`} | |
| 597 | 597 | style={{ maxHeight: MAX_HEIGHT }} | |
| 598 | 598 | onClick={(event) => { | |
| 599 | 599 | // The whole box takes focus, not only its first line. | |
| 40 | 40 | return ( | |
| 41 | 41 | <span | |
| 42 | 42 | className={cn( | |
| 43 | − | "inline-flex h-4 shrink-0 items-center rounded-[4px] bg-accent/15 px-1 text-[0.5625rem] font-semibold tracking-[0.06em] text-accent uppercase", | |
| 43 | + | "inline-flex h-4 shrink-0 items-center rounded-sm bg-accent/15 px-1 text-[0.5625rem] font-semibold tracking-[0.06em] text-accent uppercase", | |
| 44 | 44 | deleted && "bg-raised text-muted", | |
| 45 | 45 | className, | |
| 46 | 46 | )} |
| 31 | 31 | if (count <= 0) return null; | |
| 32 | 32 | return ( | |
| 33 | 33 | <span | |
| 34 | − | className={`min-w-5 shrink-0 rounded-full group-hover/row:invisible px-1.5 text-center text-[0.6875rem] leading-[1.125rem] font-semibold tabular-nums ${ | |
| 34 | + | className={`min-w-5 shrink-0 rounded-full group-hover/row:invisible px-1.5 text-center text-[0.6875rem] leading-4.5 font-semibold tabular-nums ${ | |
| 35 | 35 | entry.mentions > 0 ? "bg-accent text-bg" : "bg-line-strong text-fg" | |
| 36 | 36 | }`} | |
| 37 | 37 | > | |
| ⋯ | |||
| 173 | 173 | {/* A phone's list (no heading): room at the end so the compose button never covers the last row. */} | |
| 174 | 174 | <nav | |
| 175 | 175 | aria-label="Conversations" | |
| 176 | − | className={`min-h-0 grow overflow-y-auto px-2.5 pt-3 [scrollbar-width:thin] ${heading ? "pb-4" : "pb-[calc(5.5rem+env(safe-area-inset-bottom))]"}`} | |
| 176 | + | className={`min-h-0 grow overflow-y-auto px-2.5 pt-3 scrollbar-thin ${heading ? "pb-4" : "pb-[calc(5.5rem+env(safe-area-inset-bottom))]"}`} | |
| 177 | 177 | > | |
| 178 | 178 | {loading ? ( | |
| 179 | 179 | <SidebarSkeleton /> | |
| 30 | 30 | codeLink?: (href: string) => string; | |
| 31 | 31 | }; | |
| 32 | 32 | ||
| 33 | − | const PILL = "rounded-[4px] px-1 py-px font-medium"; | |
| 33 | + | const PILL = "rounded-sm px-1 py-px font-medium"; | |
| 34 | 34 | ||
| 35 | 35 | function SpanView({ span, context }: { span: Span; context: TextContext }): ReactNode { | |
| 36 | 36 | const emoji = useEmojiContext(); | |
| ⋯ | |||
| 38 | 38 | case "text": | |
| 39 | 39 | return renderEmoji(span.v, emoji.byName, emoji.usercontent); | |
| 40 | 40 | case "code": | |
| 41 | − | return <code className="rounded-[4px] border border-line bg-raised px-1 py-px font-mono text-[0.84em] text-fg-soft">{span.v}</code>; | |
| 41 | + | return <code className="rounded-sm border border-line bg-raised px-1 py-px font-mono text-[0.84em] text-fg-soft">{span.v}</code>; | |
| 42 | 42 | case "strong": | |
| 43 | 43 | return <strong className="font-semibold text-fg">{spans(span.c, context)}</strong>; | |
| 44 | 44 | case "em": | |
| ⋯ | |||
| 232 | 232 | }, [language, code]); | |
| 233 | 233 | return ( | |
| 234 | 234 | <div className="group/code relative my-1"> | |
| 235 | − | <pre className="overflow-x-auto rounded-md border border-line bg-bg px-3 py-2 pointer-coarse:pr-10 font-mono text-[0.8125rem] leading-relaxed text-fg-soft [scrollbar-width:thin]"> | |
| 235 | + | <pre className="overflow-x-auto rounded-md border border-line bg-bg px-3 py-2 pointer-coarse:pr-10 font-mono text-[0.8125rem] leading-relaxed text-fg-soft scrollbar-thin"> | |
| 236 | 236 | <code> | |
| 237 | 237 | {rows | |
| 238 | 238 | ? rows.map((row, index) => ( | |
| ⋯ | |||
| 277 | 277 | if (onlyEmoji(body)) return <p className="text-3xl leading-tight">{body.trim()}</p>; | |
| 278 | 278 | if (onlyEmojiOrCustom(body, emoji.byName)) return <p className="text-3xl leading-tight">{renderEmoji(body.trim(), emoji.byName, emoji.usercontent, 32)}</p>; | |
| 279 | 279 | return ( | |
| 280 | − | <div className="space-y-1.5 text-[0.9375rem] leading-[1.55] break-words text-fg-soft"> | |
| 280 | + | <div className="space-y-1.5 text-[0.9375rem] leading-[1.55] wrap-break-word text-fg-soft"> | |
| 281 | 281 | {parsed.map((block, index) => ( | |
| 282 | 282 | <BlockView key={index} block={block} context={context} /> | |
| 283 | 283 | ))} | |
| 119 | 119 | > | |
| 120 | 120 | <Mark state={checks.state} /> | |
| 121 | 121 | </PopoverTrigger> | |
| 122 | − | <PopoverContent align="start" className="w-[28rem] p-0"> | |
| 122 | + | <PopoverContent align="start" className="w-md p-0"> | |
| 123 | 123 | <ChecksList checks={checks} /> | |
| 124 | 124 | </PopoverContent> | |
| 125 | 125 | </Popover> |
| 252 | 252 | <nav aria-label="Categories" className={cn(nav === "sidebar" && "md:sticky md:top-20 md:self-start")}> | |
| 253 | 253 | <ul | |
| 254 | 254 | className={cn( | |
| 255 | − | "-mx-4 flex gap-1.5 overflow-x-auto px-4 pb-1 [scrollbar-width:none] sm:mx-0 sm:px-0", | |
| 255 | + | "-mx-4 flex gap-1.5 overflow-x-auto px-4 pb-1 scrollbar-none sm:mx-0 sm:px-0", | |
| 256 | 256 | nav === "sidebar" && "md:mx-0 md:flex-col md:gap-px md:overflow-visible md:px-0", | |
| 257 | 257 | )} | |
| 258 | 258 | > | |
| ⋯ | |||
| 407 | 407 | )} | |
| 408 | 408 | > | |
| 409 | 409 | <div className="flex items-start gap-3"> | |
| 410 | − | <span className={cn(soon && "opacity-70 grayscale-[35%]")}> | |
| 410 | + | <span className={cn(soon && "opacity-70 grayscale-35")}> | |
| 411 | 411 | <ConnectorMark view={view} /> | |
| 412 | 412 | </span> | |
| 413 | 413 | <div className="min-w-0 grow"> | |
| 69 | 69 | <Card asChild tone="plain" className="bg-surface/50 px-4 pt-3.5 pb-3"> | |
| 70 | 70 | <section aria-label="Contributions"> | |
| 71 | 71 | <h2 className="text-sm font-medium">{heading}</h2> | |
| 72 | − | <div ref={scroller} className="mt-3 overflow-x-auto overflow-y-hidden pb-1 [scrollbar-width:thin]"> | |
| 72 | + | <div ref={scroller} className="mt-3 overflow-x-auto overflow-y-hidden pb-1 scrollbar-thin"> | |
| 73 | 73 | <div className="flex w-max text-[0.625rem] leading-none text-faint"> | |
| 74 | 74 | {/* The weekdays, under the months' row. */} | |
| 75 | 75 | <div aria-hidden="true" className="mr-1.5 flex shrink-0 flex-col" style={{ gap: GAP, paddingTop: 16 }}> | |
| ⋯ | |||
| 104 | 104 | data-date={day.date} | |
| 105 | 105 | data-count={day.count} | |
| 106 | 106 | data-commits={day.commits} | |
| 107 | − | className={`block rounded-[2px] ${SHADES[day.level]} ${hovered?.date === day.date ? "ring-1 ring-fg/60" : ""}`} | |
| 107 | + | className={`block rounded-xs ${SHADES[day.level]} ${hovered?.date === day.date ? "ring-1 ring-fg/60" : ""}`} | |
| 108 | 108 | style={{ width: CELL, height: CELL }} | |
| 109 | 109 | /> | |
| 110 | 110 | ) : ( | |
| ⋯ | |||
| 131 | 131 | <div aria-hidden="true" className="mt-2 flex items-center justify-end gap-1 text-[0.6875rem] text-faint"> | |
| 132 | 132 | <span className="mr-1">Less</span> | |
| 133 | 133 | {([0, 1, 2, 3, 4] as Level[]).map((level) => ( | |
| 134 | − | <span key={level} className={`block rounded-[2px] ${SHADES[level]}`} style={{ width: CELL, height: CELL }} /> | |
| 134 | + | <span key={level} className={`block rounded-xs ${SHADES[level]}`} style={{ width: CELL, height: CELL }} /> | |
| 135 | 135 | ))} | |
| 136 | 136 | <span className="ml-1">More</span> | |
| 137 | 137 | </div> | |
| 233 | 233 | return ( | |
| 234 | 234 | <div className="flex min-w-0 items-baseline gap-3"> | |
| 235 | 235 | <dt className="w-16 shrink-0 text-faint">{term}</dt> | |
| 236 | − | <dd className="min-w-0 break-words text-fg-soft">{children}</dd> | |
| 236 | + | <dd className="min-w-0 wrap-break-word text-fg-soft">{children}</dd> | |
| 237 | 237 | </div> | |
| 238 | 238 | ); | |
| 239 | 239 | } |
| 30 | 30 | ||
| 31 | 31 | const ROW_STYLES: Record<DiffLine["kind"], string> = { | |
| 32 | 32 | context: "", | |
| 33 | − | add: "bg-success/[0.09]", | |
| 34 | − | delete: "bg-danger/[0.09]", | |
| 33 | + | add: "bg-success/9", | |
| 34 | + | delete: "bg-danger/9", | |
| 35 | 35 | }; | |
| 36 | 36 | ||
| 37 | 37 | const GUTTER_STYLES: Record<DiffLine["kind"], string> = { | |
| ⋯ | |||
| 489 | 489 | <Checkbox | |
| 490 | 490 | checked={viewed} | |
| 491 | 491 | onCheckedChange={(checked) => onViewed(checked === true)} | |
| 492 | − | className="size-3.5 rounded-[4px] [&_svg]:size-2.5" | |
| 492 | + | className="size-3.5 rounded-sm [&_svg]:size-2.5" | |
| 493 | 493 | /> | |
| 494 | 494 | <span className="sr-only sm:not-sr-only">Viewed</span> | |
| 495 | 495 | </label> | |
| 209 | 209 | <input type="hidden" name="intent" value="email-privacy" /> | |
| 210 | 210 | <SwitchCard name="private" defaultChecked={data.privateEmail} title="Keep my email address private"> | |
| 211 | 211 | Merges and other commits g1t makes for you on the web, or an agent makes for you, use{" "} | |
| 212 | − | <span className="font-mono text-fg [overflow-wrap:anywhere]">{data.noreply}</span> instead of your primary address. | |
| 212 | + | <span className="font-mono text-fg wrap-anywhere">{data.noreply}</span> instead of your primary address. | |
| 213 | 213 | </SwitchCard> | |
| 214 | 214 | <SwitchCard name="block" defaultChecked={data.blockPrivatePushes} title="Block pushes that expose my email"> | |
| 215 | 215 | While your address is private, a push is refused if a commit in it has one of your confirmed | |
| ⋯ | |||
| 217 | 217 | </SwitchCard> | |
| 218 | 218 | <div className="flex flex-wrap items-center justify-between gap-3"> | |
| 219 | 219 | <p className="text-xs text-faint"> | |
| 220 | − | Commits g1t makes for you now carry <span className="font-mono [overflow-wrap:anywhere]">{data.commitEmail}</span>. | |
| 220 | + | Commits g1t makes for you now carry <span className="font-mono wrap-anywhere">{data.commitEmail}</span>. | |
| 221 | 221 | </p> | |
| 222 | 222 | <SubmitButton variant="outline" pending="Saving…" match={{ intent: "email-privacy" }}> | |
| 223 | 223 | Save | |
| 101 | 101 | ||
| 102 | 102 | let index = 0; | |
| 103 | 103 | return ( | |
| 104 | − | <div className="flex h-[26rem] w-[22.5rem] max-w-[calc(100vw-1rem)] flex-col" onKeyDown={onKeyDown}> | |
| 104 | + | <div className="flex h-104 w-90 max-w-[calc(100vw-1rem)] flex-col" onKeyDown={onKeyDown}> | |
| 105 | 105 | <div className="flex items-center gap-2 border-b border-line p-2"> | |
| 106 | 106 | <label className="flex h-8 min-w-0 grow items-center gap-2 rounded-md border border-line-strong bg-bg px-2 focus-within:border-accent/50"> | |
| 107 | 107 | <Search size={14} className="shrink-0 text-faint" /> | |
| ⋯ | |||
| 158 | 158 | </div> | |
| 159 | 159 | </div> | |
| 160 | 160 | {!query.trim() && ( | |
| 161 | − | <nav aria-label="Emoji groups" className="flex shrink-0 items-center gap-0.5 overflow-x-auto border-b border-line px-1.5 py-1 [scrollbar-width:none]"> | |
| 161 | + | <nav aria-label="Emoji groups" className="flex shrink-0 items-center gap-0.5 overflow-x-auto border-b border-line px-1.5 py-1 scrollbar-none"> | |
| 162 | 162 | {sections.map((section) => ( | |
| 163 | 163 | <Hint key={section.key} label={section.title}> | |
| 164 | 164 | <Button | |
| ⋯ | |||
| 175 | 175 | ))} | |
| 176 | 176 | </nav> | |
| 177 | 177 | )} | |
| 178 | − | <div ref={grid} id={list} role="listbox" aria-label="Emoji" className="relative min-h-0 grow overflow-y-auto px-2 pb-2 [scrollbar-width:thin]"> | |
| 178 | + | <div ref={grid} id={list} role="listbox" aria-label="Emoji" className="relative min-h-0 grow overflow-y-auto px-2 pb-2 scrollbar-thin"> | |
| 179 | 179 | {!data && !query.trim() && <p className="px-1 py-6 text-center text-sm text-faint">Loading emoji…</p>} | |
| 180 | 180 | {sections.map((section) => ( | |
| 181 | 181 | <section key={section.key} data-section={section.key} aria-label={section.title}> | |
| 182 | − | <h3 className="sticky top-0 z-[1] flex items-center justify-between bg-raised/95 px-1 pt-2 pb-1 text-[0.6875rem] font-semibold tracking-wide text-faint uppercase backdrop-blur"> | |
| 182 | + | <h3 className="sticky top-0 z-1 flex items-center justify-between bg-raised/95 px-1 pt-2 pb-1 text-[0.6875rem] font-semibold tracking-wide text-faint uppercase backdrop-blur"> | |
| 183 | 183 | {section.title} | |
| 184 | 184 | {section.key === "custom" && manageHref && ( | |
| 185 | 185 | <Link to={manageHref} className="font-medium tracking-normal normal-case text-accent hover:underline"> | |
| 347 | 347 | </p> | |
| 348 | 348 | <div className="mt-2 flex items-center gap-2"> | |
| 349 | 349 | <span className="flex h-6 items-center gap-1 rounded-full border border-accent/60 bg-accent/15 px-1.5 text-xs font-medium text-accent"> | |
| 350 | − | <img src={preview} alt="" className="size-[15px] object-contain" />3 | |
| 350 | + | <img src={preview} alt="" className="size-3.75 object-contain" />3 | |
| 351 | 351 | </span> | |
| 352 | 352 | <img src={preview} alt="" className="size-9 object-contain" /> | |
| 353 | 353 | <span className="ml-auto font-mono text-xs text-faint">:{clean || "name"}:</span> |
| 109 | 109 | onChange={(e) => setDraft(e.target.value)} | |
| 110 | 110 | onBlur={() => draft !== value && onChange(draft)} | |
| 111 | 111 | onKeyDown={(e) => e.stopPropagation()} | |
| 112 | − | className={`w-full resize-y rounded-md border border-line bg-bg px-2.5 py-2 text-[0.8125rem] leading-relaxed text-fg outline-none focus:border-[var(--color-accent)] ${mono ? "font-mono" : ""}`} | |
| 112 | + | className={`w-full resize-y rounded-md border border-line bg-bg px-2.5 py-2 text-[0.8125rem] leading-relaxed text-fg outline-none focus:border-(--color-accent) ${mono ? "font-mono" : ""}`} | |
| 113 | 113 | /> | |
| 114 | 114 | ); | |
| 115 | 115 | } | |
| ⋯ | |||
| 202 | 202 | }, [url]); | |
| 203 | 203 | const shown = card && card !== "missing" ? card : null; | |
| 204 | 204 | const state = shown?.state ?? null; | |
| 205 | − | const tone = state === "open" ? "text-success" : state === "merged" ? "text-merged" : state === "closed" ? "text-danger" : "text-[var(--g1t-muted)]"; | |
| 205 | + | const tone = state === "open" ? "text-success" : state === "merged" ? "text-merged" : state === "closed" ? "text-danger" : "text-(--g1t-muted)"; | |
| 206 | 206 | return ( | |
| 207 | 207 | <a | |
| 208 | 208 | href={shown?.href ?? url} | |
| ⋯ | |||
| 267 | 267 | const p = inlineContent.props; | |
| 268 | 268 | if (p.kind === "page") { | |
| 269 | 269 | return ( | |
| 270 | − | <a href={p.href || "#"} className="rounded px-0.5 font-medium text-[var(--color-accent)] no-underline hover:underline"> | |
| 270 | + | <a href={p.href || "#"} className="rounded px-0.5 font-medium text-(--color-accent) no-underline hover:underline"> | |
| 271 | 271 | <FileText size={13} className="mr-0.5 inline -translate-y-px" aria-hidden="true" /> | |
| 272 | 272 | {p.name || "Untitled"} | |
| 273 | 273 | </a> | |
| 274 | 274 | ); | |
| 275 | 275 | } | |
| 276 | − | return <span className={`rounded px-1 py-px font-medium ${p.kind === "agent" ? "bg-[color-mix(in_srgb,var(--color-accent)_15%,transparent)] text-[var(--color-accent)]" : "bg-info/12 text-info"}`}>@{p.name}</span>; | |
| 276 | + | return <span className={`rounded px-1 py-px font-medium ${p.kind === "agent" ? "bg-[color-mix(in_srgb,var(--color-accent)_15%,transparent)] text-(--color-accent)" : "bg-info/12 text-info"}`}>@{p.name}</span>; | |
| 277 | 277 | }, | |
| 278 | 278 | }, | |
| 279 | 279 | ); | |
| 311 | 311 | </div> | |
| 312 | 312 | {showComments && ( | |
| 313 | 313 | <aside ref={comments} aria-label="Comments" className="scroll-mt-20 max-xl:border-t max-xl:border-line max-xl:pt-6"> | |
| 314 | − | <div className="xl:sticky xl:top-[calc(var(--topbar-h)+1.5rem)] xl:max-h-[calc(100dvh-var(--topbar-h)-2.5rem)] xl:overflow-y-auto xl:[scrollbar-width:thin]"> | |
| 314 | + | <div className="xl:sticky xl:top-[calc(var(--topbar-h)+1.5rem)] xl:max-h-[calc(100dvh-var(--topbar-h)-2.5rem)] xl:overflow-y-auto xl:scrollbar-thin"> | |
| 315 | 315 | <ThreadsSidebar filter="all" sort="position" /> | |
| 316 | 316 | </div> | |
| 317 | 317 | </aside> |
| 17 | 17 | if (!text.trim()) return <p className="text-xs text-faint italic">{tone === "del" ? "Nothing removed" : "Nothing added"}</p>; | |
| 18 | 18 | return ( | |
| 19 | 19 | <pre | |
| 20 | − | className={`max-h-56 overflow-auto rounded-md px-2.5 py-2 font-sans text-[0.8125rem] leading-relaxed whitespace-pre-wrap [scrollbar-width:thin] ${ | |
| 20 | + | className={`max-h-56 overflow-auto rounded-md px-2.5 py-2 font-sans text-[0.8125rem] leading-relaxed whitespace-pre-wrap scrollbar-thin ${ | |
| 21 | 21 | tone === "del" ? "bg-danger/8 text-fg-soft line-through decoration-danger/60" : "bg-success/10 text-fg" | |
| 22 | 22 | }`} | |
| 23 | 23 | > |
| 130 | 130 | <Skeleton className="h-6 w-44" /> | |
| 131 | 131 | </div> | |
| 132 | 132 | ) : ( | |
| 133 | − | <ul className="max-h-64 space-y-0.5 overflow-y-auto [scrollbar-width:thin]"> | |
| 133 | + | <ul className="max-h-64 space-y-0.5 overflow-y-auto scrollbar-thin"> | |
| 134 | 134 | <Row face={<Face who={access.owner} size={26} />} name={access.owner.display_name} sub={`@${access.owner.name}`} end={<span className="text-xs text-faint">Owner</span>} /> | |
| 135 | 135 | {access.rows.map((r) => { | |
| 136 | 136 | const face = r.profile.kind === "team" ? <TeamFace /> : <Face who={r.profile} size={26} />; | |
| ⋯ | |||
| 296 | 296 | ||
| 297 | 297 | function TeamFace() { | |
| 298 | 298 | return ( | |
| 299 | − | <span className="flex size-[26px] shrink-0 items-center justify-center rounded-full bg-raised text-muted"> | |
| 299 | + | <span className="flex size-6.5 shrink-0 items-center justify-center rounded-full bg-raised text-muted"> | |
| 300 | 300 | <Users size={13} /> | |
| 301 | 301 | </span> | |
| 302 | 302 | ); | |
| 379 | 379 | </Button> | |
| 380 | 380 | )} | |
| 381 | 381 | </div> | |
| 382 | − | <nav aria-label="Artifacts" className="min-h-0 grow overflow-y-auto px-2.5 pt-3 pb-4 [scrollbar-width:thin]"> | |
| 382 | + | <nav aria-label="Artifacts" className="min-h-0 grow overflow-y-auto px-2.5 pt-3 pb-4 scrollbar-thin"> | |
| 383 | 383 | <form | |
| 384 | 384 | role="search" | |
| 385 | 385 | onSubmit={(e) => { |
| 69 | 69 | /** Hosts in a compact list. */ | |
| 70 | 70 | function Hosts({ hosts }: { hosts: string[] }) { | |
| 71 | 71 | return ( | |
| 72 | − | <span className="font-mono text-xs break-words text-faint">{hosts.length ? hosts.join(", ") : "none"}</span> | |
| 72 | + | <span className="font-mono text-xs wrap-break-word text-faint">{hosts.length ? hosts.join(", ") : "none"}</span> | |
| 73 | 73 | ); | |
| 74 | 74 | } | |
| 75 | 75 | ||
| ⋯ | |||
| 204 | 204 | {inheritedWorkflowDomains.length > 0 && ( | |
| 205 | 205 | <p className="mt-2 text-xs text-faint"> | |
| 206 | 206 | From the workspace:{" "} | |
| 207 | − | <span className="font-mono break-words">{inheritedWorkflowDomains.map(workflowDomainLine).join("; ")}</span> | |
| 207 | + | <span className="font-mono wrap-break-word">{inheritedWorkflowDomains.map(workflowDomainLine).join("; ")}</span> | |
| 208 | 208 | </p> | |
| 209 | 209 | )} | |
| 210 | 210 | </Card> | |
| 608 | 608 | <p className="mt-4 text-sm text-muted">Nothing on the statement {inSpan(span)}.</p> | |
| 609 | 609 | ) : ( | |
| 610 | 610 | <> | |
| 611 | − | <div className="mt-4 flex h-2.5 gap-[2px] overflow-hidden rounded-full" role="img" aria-label="Spend by kind of charge"> | |
| 611 | + | <div className="mt-4 flex h-2.5 gap-0.5 overflow-hidden rounded-full" role="img" aria-label="Spend by kind of charge"> | |
| 612 | 612 | {spend.lines.map((line) => ( | |
| 613 | − | <span key={line.kind} className={`h-full min-w-[3px] ${kindColour(line.kind)}`} style={{ flexGrow: line.micros }} /> | |
| 613 | + | <span key={line.kind} className={`h-full min-w-0.75 ${kindColour(line.kind)}`} style={{ flexGrow: line.micros }} /> | |
| 614 | 614 | ))} | |
| 615 | 615 | </div> | |
| 616 | 616 | <ul className="mt-3 space-y-2"> | |
| 617 | 617 | {spend.lines.map((line) => ( | |
| 618 | 618 | <li key={line.kind} className="flex items-center gap-2 text-sm"> | |
| 619 | − | <span className={`size-2 shrink-0 rounded-[2px] ${kindColour(line.kind)}`} aria-hidden /> | |
| 619 | + | <span className={`size-2 shrink-0 rounded-xs ${kindColour(line.kind)}`} aria-hidden /> | |
| 620 | 620 | <span className="min-w-0 grow truncate text-fg-soft">{line.kind}</span> | |
| 621 | 621 | <span className="text-fg tabular-nums">{money(line.micros)}</span> | |
| 622 | 622 | </li> |
| 198 | 198 | export function InboxTabs({ tab, counts, onChange }: { tab: InboxTab; counts: InboxCounts | null; onChange: (tab: InboxTab) => void }) { | |
| 199 | 199 | return ( | |
| 200 | 200 | <Tabs value={tab} onValueChange={(value) => onChange(value as InboxTab)}> | |
| 201 | − | <TabsList className="flex w-full overflow-x-auto [scrollbar-width:none]"> | |
| 201 | + | <TabsList className="flex w-full overflow-x-auto scrollbar-none"> | |
| 202 | 202 | {INBOX_TABS.map((entry) => { | |
| 203 | 203 | const count = tabCount(counts, entry.tab); | |
| 204 | 204 | return ( |
| 29 | 29 | import { Unavailable } from "./mission"; | |
| 30 | 30 | import { SubmitButton, TimeAgo } from "./ui"; | |
| 31 | 31 | import { Avatar } from "./ui/avatar"; | |
| 32 | + | import { UserAvatar } from "./user-card"; | |
| 33 | + | import { PersonLink } from "./work"; | |
| 32 | 34 | import { Card } from "./ui/card"; | |
| 33 | 35 | import { Hint } from "./ui/hint"; | |
| 34 | 36 | import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from "./ui/dropdown-menu"; | |
| ⋯ | |||
| 243 | 245 | <Facts facts={row.facts} /> | |
| 244 | 246 | </div> | |
| 245 | 247 | </div> | |
| 246 | − | <div className="rounded-lg border border-warn/20 bg-warn/[0.06] p-3.5 md:col-span-2 2xl:col-span-1"> | |
| 248 | + | <div className="rounded-lg border border-warn/20 bg-warn/6 p-3.5 md:col-span-2 2xl:col-span-1"> | |
| 247 | 249 | <p className="text-xs font-semibold text-warn">Why this needs you</p> | |
| 248 | 250 | <p className="mt-1.5 text-sm leading-6 text-fg-soft">{row.why}</p> | |
| 249 | 251 | <p className="mt-2 text-xs text-faint"> | |
| ⋯ | |||
| 412 | 414 | {day.count > 0 ? ( | |
| 413 | 415 | <span className="block rounded-t bg-accent" style={{ height: Math.max(3, Math.round((day.count / max) * (height - 4))) }} /> | |
| 414 | 416 | ) : ( | |
| 415 | − | <span className="block h-[2px] rounded-full bg-line-strong" /> | |
| 417 | + | <span className="block h-0.5 rounded-full bg-line-strong" /> | |
| 416 | 418 | )} | |
| 417 | 419 | </div> | |
| 418 | 420 | <div className="pointer-events-none absolute bottom-full left-1/2 z-10 mb-1 hidden -translate-x-1/2 rounded-md border border-line-strong bg-raised px-2.5 py-1.5 text-xs whitespace-nowrap shadow-lg shadow-black/40 md:group-hover:block"> | |
| ⋯ | |||
| 491 | 493 | const more = group.parts.length - 1 + Math.max(0, part.numbers.length - 1); | |
| 492 | 494 | return ( | |
| 493 | 495 | <li className="flex gap-2.5 py-2"> | |
| 494 | − | <span className="mt-px"> | |
| 495 | − | <Avatar name={actor} size={22} /> | |
| 496 | − | </span> | |
| 496 | + | <span className="mt-px">{agent ? <Avatar name={actor} image={null} size={22} /> : <UserAvatar username={actor} size={22} />}</span> | |
| 497 | 497 | <span className="min-w-0 grow text-[0.8125rem] leading-5 text-muted"> | |
| 498 | − | <span className={cn("font-medium", agent ? "text-accent" : "text-fg")}>{actor}</span> {VERB[part.verb]}{" "} | |
| 498 | + | {agent ? ( | |
| 499 | + | <span className="font-medium text-accent">{actor}</span> | |
| 500 | + | ) : ( | |
| 501 | + | <PersonLink name={actor} className="font-medium text-fg hover:text-accent" /> | |
| 502 | + | )}{" "} | |
| 503 | + | {VERB[part.verb]}{" "} | |
| 499 | 504 | {number != null ? ( | |
| 500 | 505 | <Link to={`${base}/issues/${number}`} prefetch="intent" className="text-fg-soft hover:text-fg"> | |
| 501 | 506 | <span className="font-mono text-xs">#{number}</span> | |
| ⋯ | |||
| 679 | 684 | <Card asChild className="min-w-0 scroll-mt-20 self-start overflow-hidden"> | |
| 680 | 685 | <section id="work"> | |
| 681 | 686 | <div className="flex items-center gap-2 border-b border-line px-2 sm:px-3"> | |
| 682 | − | <nav className="-mb-px flex min-w-0 grow gap-1 overflow-x-auto [scrollbar-width:none]" aria-label="Overview"> | |
| 687 | + | <nav className="-mb-px flex min-w-0 grow gap-1 overflow-x-auto scrollbar-none" aria-label="Overview"> | |
| 683 | 688 | {(["needs", "landed"] as const).map((value) => ( | |
| 684 | 689 | <Link | |
| 685 | 690 | key={value} | |
| 32 | 32 | import { type ActivityGroup, type Need, type NeedKind, type Verb, isAgent, sparkPoints } from "../lib/mission"; | |
| 33 | 33 | import { TimeAgo } from "./ui"; | |
| 34 | 34 | import { Avatar } from "./ui/avatar"; | |
| 35 | + | import { UserAvatar } from "./user-card"; | |
| 36 | + | import { PersonLink } from "./work"; | |
| 35 | 37 | import { Button } from "./ui/button"; | |
| 36 | 38 | import { Card } from "./ui/card"; | |
| 37 | 39 | import { Hint } from "./ui/hint"; | |
| ⋯ | |||
| 295 | 297 | <SelectContent> | |
| 296 | 298 | <SelectItem value="all">Anyone</SelectItem> | |
| 297 | 299 | {people.map((name) => ( | |
| 298 | − | <SelectItem key={name} value={name} icon={<Avatar name={name} size={16} />}> | |
| 300 | + | <SelectItem key={name} value={name} icon={<Avatar name={name} image={isAgent(name) ? null : undefined} size={16} />}> | |
| 299 | 301 | {name} | |
| 300 | 302 | </SelectItem> | |
| 301 | 303 | ))} | |
| ⋯ | |||
| 307 | 309 | {shown.length === 0 ? ( | |
| 308 | 310 | <Quiet>{groups.length === 0 ? empty : "Nothing matches these filters."}</Quiet> | |
| 309 | 311 | ) : ( | |
| 310 | − | <ol className="relative space-y-0.5 before:absolute before:top-3 before:bottom-3 before:left-[0.9375rem] before:w-px before:bg-line"> | |
| 312 | + | <ol className="relative space-y-0.5 before:absolute before:top-3 before:bottom-3 before:left-3.75 before:w-px before:bg-line"> | |
| 311 | 313 | {shown.map((group) => { | |
| 312 | 314 | const look = VERB[group.parts[0].verb]; | |
| 313 | 315 | const agent = group.actor == null || isAgent(group.actor); | |
| ⋯ | |||
| 315 | 317 | <li key={group.id} className="relative flex items-start gap-3 rounded-lg px-1 py-1.5 text-sm"> | |
| 316 | 318 | <span | |
| 317 | 319 | className={cn( | |
| 318 | − | "relative z-10 mt-0.5 flex size-[1.375rem] shrink-0 items-center justify-center rounded-full bg-bg ring-1 ring-line", | |
| 320 | + | "relative z-10 mt-0.5 flex size-5.5 shrink-0 items-center justify-center rounded-full bg-bg ring-1 ring-line", | |
| 319 | 321 | look.tone, | |
| 320 | 322 | )} | |
| 321 | 323 | > | |
| 322 | 324 | {look.icon} | |
| 323 | 325 | </span> | |
| 324 | 326 | <span className="min-w-0 grow leading-6 text-muted"> | |
| 325 | − | {group.actor ? ( | |
| 326 | − | <span className={cn("mr-1.5 inline-flex items-center gap-1.5 align-middle font-medium", agent ? "text-accent" : "text-fg")}> | |
| 327 | − | <Avatar name={group.actor} size={16} /> | |
| 328 | − | {group.actor} | |
| 329 | − | </span> | |
| 330 | − | ) : ( | |
| 331 | − | <span className="mr-1 text-fg-soft">g1t:</span> | |
| 332 | − | )} | |
| 327 | + | <span className={cn("mr-1.5 inline-flex items-center gap-1.5 align-middle font-medium", agent ? "text-accent" : "text-fg")}> | |
| 328 | + | {agent ? ( | |
| 329 | + | <> | |
| 330 | + | <Avatar name={group.actor ?? "g1t"} image={null} size={16} /> | |
| 331 | + | {group.actor ?? "g1t"} | |
| 332 | + | </> | |
| 333 | + | ) : ( | |
| 334 | + | <> | |
| 335 | + | <UserAvatar username={group.actor!} size={16} /> | |
| 336 | + | <PersonLink name={group.actor!} className="hover:text-accent" /> | |
| 337 | + | </> | |
| 338 | + | )} | |
| 339 | + | </span> | |
| 333 | 340 | <GroupLine group={group} /> | |
| 334 | 341 | {showRepo && ( | |
| 335 | 342 | <Link | |
| ⋯ | |||
| 467 | 474 | }) { | |
| 468 | 475 | const shown = builds.slice(0, slots).reverse(); | |
| 469 | 476 | return ( | |
| 470 | − | <div className="flex h-8 items-end gap-[3px]" aria-label="Recent builds, oldest first"> | |
| 477 | + | <div className="flex h-8 items-end gap-0.75" aria-label="Recent builds, oldest first"> | |
| 471 | 478 | {Array.from({ length: slots - shown.length }, (_, i) => ( | |
| 472 | 479 | <span key={`empty-${i}`} className="h-2 flex-1 rounded-sm bg-line/60" /> | |
| 473 | 480 | ))} | |
| 127 | 127 | className="items-start" | |
| 128 | 128 | > | |
| 129 | 129 | <span className="mt-0.5 flex size-4 shrink-0 items-center justify-center"> | |
| 130 | − | {selected && <Check size={14} className="!text-accent" />} | |
| 130 | + | {selected && <Check size={14} className="text-accent!" />} | |
| 131 | 131 | </span> | |
| 132 | 132 | <span className="min-w-0"> | |
| 133 | 133 | <span className="block text-sm text-fg">{choice.label}</span> | |
| ⋯ | |||
| 159 | 159 | on ? "border-accent bg-accent text-bg" : "border-line-strong", | |
| 160 | 160 | )} | |
| 161 | 161 | > | |
| 162 | − | {on && <Check size={11} className="!text-bg" />} | |
| 162 | + | {on && <Check size={11} className="text-bg!" />} | |
| 163 | 163 | </span> | |
| 164 | 164 | {WATCH_EVENT_LABEL[kind]} | |
| 165 | 165 | </DropdownMenuItem> | |
| 68 | 68 | {waiting.map((notification) => ( | |
| 69 | 69 | <Card asChild key={notification.id} radius="lg" className="relative p-3"> | |
| 70 | 70 | <li> | |
| 71 | − | <span aria-hidden="true" className="absolute inset-y-2 left-0 w-[3px] rounded-r bg-accent" /> | |
| 71 | + | <span aria-hidden="true" className="absolute inset-y-2 left-0 w-0.75 rounded-r bg-accent" /> | |
| 72 | 72 | <div className="flex items-start gap-2"> | |
| 73 | 73 | <button | |
| 74 | 74 | type="button" |
| 123 | 123 | role="status" | |
| 124 | 124 | className="g1t-toast group/toast pointer-events-auto relative overflow-hidden rounded-xl bg-surface shadow-[0_0_0_6px_var(--color-bg),0_20px_48px_-12px_rgba(0,0,0,0.9)] ring-1 ring-line-strong" | |
| 125 | 125 | > | |
| 126 | − | {loud && <span aria-hidden="true" className="absolute inset-y-0 left-0 w-[3px] bg-accent" />} | |
| 126 | + | {loud && <span aria-hidden="true" className="absolute inset-y-0 left-0 w-0.75 bg-accent" />} | |
| 127 | 127 | <button type="button" onClick={open} className="flex w-full items-start gap-3 p-3 pr-9 text-left outline-none focus-visible:bg-raised/60"> | |
| 128 | 128 | <span className="mt-0.5 shrink-0"> | |
| 129 | 129 | <ActorAvatar notification={notification} /> | |
| ⋯ | |||
| 251 | 251 | onBlur={(event) => { | |
| 252 | 252 | if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setFocused(false); | |
| 253 | 253 | }} | |
| 254 | − | className="pointer-events-none fixed z-[65] flex flex-col gap-2 max-sm:inset-x-3 max-sm:top-[calc(env(safe-area-inset-top)+0.75rem)] max-sm:flex-col-reverse sm:right-5 sm:bottom-5 sm:w-[376px]" | |
| 254 | + | className="pointer-events-none fixed z-65 flex flex-col gap-2 max-sm:inset-x-3 max-sm:top-[calc(env(safe-area-inset-top)+0.75rem)] max-sm:flex-col-reverse sm:right-5 sm:bottom-5 sm:w-94" | |
| 255 | 255 | > | |
| 256 | 256 | <style>{STYLES}</style> | |
| 257 | 257 | {offer && ( | |
| ⋯ | |||
| 260 | 260 | </div> | |
| 261 | 261 | )} | |
| 262 | 262 | {folded.length > 0 && ( | |
| 263 | − | <div className="pointer-events-auto flex h-[30px] items-center justify-between gap-2 self-center rounded-full bg-surface pr-1 pl-3 text-xs text-muted shadow-[0_0_0_6px_var(--color-bg),0_10px_30px_-10px_rgba(0,0,0,0.9)] ring-1 ring-line-strong"> | |
| 263 | + | <div className="pointer-events-auto flex h-7.5 items-center justify-between gap-2 self-center rounded-full bg-surface pr-1 pl-3 text-xs text-muted shadow-[0_0_0_6px_var(--color-bg),0_10px_30px_-10px_rgba(0,0,0,0.9)] ring-1 ring-line-strong"> | |
| 264 | 264 | <span className="font-medium text-fg tabular-nums">+{folded.length} more</span> | |
| 265 | 265 | <Button | |
| 266 | 266 | type="button" | |
| 39 | 39 | <span className="text-faint tabular-nums">{clock(log.time_us, true).slice(6)}</span> | |
| 40 | 40 | <LevelBadge level={log.severity} /> | |
| 41 | 41 | <span className="hidden truncate text-muted sm:block">{log.service || "—"}</span> | |
| 42 | − | <span className={cn("min-w-0 text-fg", open || !multiline ? "break-words whitespace-pre-wrap" : "truncate")}>{open ? log.body : log.body.split("\n")[0]}</span> | |
| 42 | + | <span className={cn("min-w-0 text-fg", open || !multiline ? "wrap-break-word whitespace-pre-wrap" : "truncate")}>{open ? log.body : log.body.split("\n")[0]}</span> | |
| 43 | 43 | </button> | |
| 44 | 44 | {open && ( | |
| 45 | 45 | <div className="space-y-2 px-3 pt-1 pb-3 pl-9"> |
| 287 | 287 | > | |
| 288 | 288 | {at === i && <div className="absolute inset-y-0 left-1/2 w-px bg-line-strong" aria-hidden="true" />} | |
| 289 | 289 | {kind === "bars" && totals[i]! > 0 && ( | |
| 290 | − | <div className="flex w-full max-w-5 flex-col-reverse gap-[2px] px-[1px]" style={{ height: `${(totals[i]! / top) * 100}%` }}> | |
| 290 | + | <div className="flex w-full max-w-5 flex-col-reverse gap-0.5 px-px" style={{ height: `${(totals[i]! / top) * 100}%` }}> | |
| 291 | 291 | {series | |
| 292 | 292 | .filter((s) => (s.values[i] ?? 0) > 0) | |
| 293 | 293 | .map((s, k, all) => ( | |
| 294 | 294 | <div | |
| 295 | 295 | key={s.key} | |
| 296 | − | className={cn("w-full min-h-[2px]", k === all.length - 1 && "rounded-t-[3px]")} | |
| 296 | + | className={cn("w-full min-h-0.5", k === all.length - 1 && "rounded-t-[3px]")} | |
| 297 | 297 | style={{ flexGrow: s.values[i] ?? 0, flexBasis: 0, background: s.color }} | |
| 298 | 298 | /> | |
| 299 | 299 | ))} |
| 432 | 432 | onValueChange={(role) => fetcher.submit({ intent: "actions-role", repo: entry.repo, role }, { method: "post" })} | |
| 433 | 433 | /> | |
| 434 | 434 | )} | |
| 435 | − | <span className="flex w-[2.125rem] justify-end"> | |
| 435 | + | <span className="flex w-8.5 justify-end"> | |
| 436 | 436 | {!entry.linked && ( | |
| 437 | 437 | <Hint label={`Stop ${entry.repo}'s workflows using it`}> | |
| 438 | 438 | <Button |
| 57 | 57 | const DOT: Record<Exclude<Shown, "offline">, string> = { | |
| 58 | 58 | active: "bg-success", | |
| 59 | 59 | // Away: a ring, the dot's colour gone out of it. | |
| 60 | − | away: "bg-[var(--presence-ring,var(--color-bg))] shadow-[inset_0_0_0_2px_var(--color-faint)]", | |
| 60 | + | away: "bg-(--presence-ring,var(--color-bg)) shadow-[inset_0_0_0_2px_var(--color-faint)]", | |
| 61 | 61 | // Paused: amber, with a bar across, as a "do not enter" sign. | |
| 62 | 62 | dnd: "bg-warn", | |
| 63 | 63 | }; | |
| ⋯ | |||
| 75 | 75 | className={cn("pointer-events-none flex items-center justify-center rounded-full", DOT[shown], className)} | |
| 76 | 76 | style={{ width: size, height: size, boxShadow: `0 0 0 2px ${ring}`, ["--presence-ring" as string]: ring }} | |
| 77 | 77 | > | |
| 78 | − | {shown === "dnd" && <span className="h-[2px] w-1/2 rounded-full bg-bg" aria-hidden="true" />} | |
| 78 | + | {shown === "dnd" && <span className="h-0.5 w-1/2 rounded-full bg-bg" aria-hidden="true" />} | |
| 79 | 79 | <span className="sr-only">{SHOWN_LABEL[shown]}</span> | |
| 80 | 80 | </span> | |
| 81 | 81 | ); | |
| ⋯ | |||
| 211 | 211 | </p> | |
| 212 | 212 | )} | |
| 213 | 213 | <p className="flex items-center gap-2"> | |
| 214 | − | <span className="flex size-[13px] items-center justify-center"> | |
| 214 | + | <span className="flex size-3.25 items-center justify-center"> | |
| 215 | 215 | {shown === "offline" ? <span className="size-2 rounded-full shadow-[inset_0_0_0_1.5px_var(--color-line-strong)]" /> : <PresenceDot entry={entry} size={9} ring="transparent" />} | |
| 216 | 216 | </span> | |
| 217 | 217 | {SHOWN_LABEL[shown]} | |
| 225 | 225 | ||
| 226 | 226 | /** A person's avatar, as the app draws it. */ | |
| 227 | 227 | function Face({ who, size = 28 }: { who: string; size?: number }) { | |
| 228 | − | return <Avatar name={who.toLowerCase()} size={size} />; | |
| 228 | + | return <Avatar name={who.toLowerCase()} image={null} size={size} />; | |
| 229 | 229 | } | |
| 230 | 230 | ||
| 231 | 231 | /** The coding agent's mark, its name and its badge, as commits and sessions show them. */ | |
| ⋯ | |||
| 243 | 243 | function PresenceDot({ away, ring = "var(--tour-side)" }: { away?: boolean; ring?: string }) { | |
| 244 | 244 | return ( | |
| 245 | 245 | <span | |
| 246 | − | className={cn("absolute -right-0.5 -bottom-0.5 size-[7px] rounded-full", away ? "bg-[var(--tour-side)] shadow-[inset_0_0_0_1.5px_var(--color-faint)]" : "bg-success")} | |
| 246 | + | className={cn("absolute -right-0.5 -bottom-0.5 size-1.75 rounded-full", away ? "bg-(--tour-side) shadow-[inset_0_0_0_1.5px_var(--color-faint)]" : "bg-success")} | |
| 247 | 247 | style={{ boxShadow: `0 0 0 1.5px ${ring}${away ? ", inset 0 0 0 1.5px var(--color-faint)" : ""}` }} | |
| 248 | 248 | /> | |
| 249 | 249 | ); | |
| ⋯ | |||
| 298 | 298 | /** One rail item: the square behind its icon, filled for the mode you are in, and its name under it. */ | |
| 299 | 299 | function RailItem({ icon, label, on, target, badge }: { icon: ReactNode; label: string; on: boolean; target?: CursorTarget; badge?: number }) { | |
| 300 | 300 | return ( | |
| 301 | − | <span className="flex w-full flex-col items-center gap-[3px] py-0.5"> | |
| 301 | + | <span className="flex w-full flex-col items-center gap-0.75 py-0.5"> | |
| 302 | 302 | <span | |
| 303 | 303 | data-tour={target} | |
| 304 | 304 | className={cn( | |
| 305 | − | "relative flex size-7 items-center justify-center rounded-[8px] transition-colors duration-500", | |
| 305 | + | "relative flex size-7 items-center justify-center rounded-lg transition-colors duration-500", | |
| 306 | 306 | on ? "bg-[#2c2c33] text-fg shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]" : "text-muted", | |
| 307 | 307 | )} | |
| 308 | 308 | > | |
| 309 | 309 | {icon} | |
| 310 | 310 | {badge ? ( | |
| 311 | − | <span className="absolute -top-1 -right-1.5 min-w-[0.875rem] rounded-full bg-accent px-[3px] text-center text-[8px] leading-[0.875rem] font-bold text-bg tabular-nums ring-[1.5px] ring-[#0b0b0d]"> | |
| 311 | + | <span className="absolute -top-1 -right-1.5 min-w-3.5 rounded-full bg-accent px-0.75 text-center text-[8px] leading-3.5 font-bold text-bg tabular-nums ring-[1.5px] ring-[#0b0b0d]"> | |
| 312 | 312 | {badge} | |
| 313 | 313 | </span> | |
| 314 | 314 | ) : null} | |
| ⋯ | |||
| 338 | 338 | <RailItem icon={<Building2 size={15} />} label="Workspace" on={false} /> | |
| 339 | 339 | <RailItem icon={<CircleHelp size={15} />} label="Help" on={false} /> | |
| 340 | 340 | <span className="relative mt-1"> | |
| 341 | − | <Avatar name="priya" size={26} /> | |
| 341 | + | <Avatar name="priya" image={null} size={26} /> | |
| 342 | 342 | <PresenceDot ring="#0b0b0d" /> | |
| 343 | 343 | </span> | |
| 344 | 344 | </div> | |
| ⋯ | |||
| 433 | 433 | <div className="space-y-2 px-2 pt-2.5"> | |
| 434 | 434 | <SideSearch>Jump to channel or person</SideSearch> | |
| 435 | 435 | <span className="grid grid-cols-3 rounded-md bg-surface p-0.5 text-center text-[10px] font-medium ring-1 ring-line"> | |
| 436 | − | <span className="rounded-[4px] bg-raised py-0.5 text-fg ring-1 ring-line-strong">All</span> | |
| 436 | + | <span className="rounded-sm bg-raised py-0.5 text-fg ring-1 ring-line-strong">All</span> | |
| 437 | 437 | <span className="py-0.5 text-muted">Unread</span> | |
| 438 | 438 | <span className="py-0.5 text-muted">Mentions</span> | |
| 439 | 439 | </span> | |
| ⋯ | |||
| 491 | 491 | ))} | |
| 492 | 492 | </span> | |
| 493 | 493 | </div> | |
| 494 | − | <div className="flex min-h-0 flex-1 flex-col justify-end overflow-hidden [mask-image:linear-gradient(to_bottom,transparent,black_2.5rem)] pb-1"> | |
| 494 | + | <div className="flex min-h-0 flex-1 flex-col justify-end overflow-hidden mask-[linear-gradient(to_bottom,transparent,black_2.5rem)] pb-1"> | |
| 495 | 495 | <div className="flex items-center gap-3 px-5 pt-1 pb-2 text-[11px] text-faint"> | |
| 496 | 496 | <span className="h-px flex-1 bg-line" /> | |
| 497 | 497 | Today | |
| ⋯ | |||
| 905 | 905 | return ( | |
| 906 | 906 | <div | |
| 907 | 907 | aria-hidden="true" | |
| 908 | − | className={cn("pointer-events-none absolute top-0 left-0 z-20 transition-[transform,opacity] duration-[900ms] ease-[cubic-bezier(.45,.05,.2,1)]", frame.cursorShown ? "opacity-100" : "opacity-0")} | |
| 908 | + | className={cn("pointer-events-none absolute top-0 left-0 z-20 transition-[transform,opacity] duration-900 ease-[cubic-bezier(.45,.05,.2,1)]", frame.cursorShown ? "opacity-100" : "opacity-0")} | |
| 909 | 909 | style={{ transform: `translate(${at.x}px, ${at.y}px)` }} | |
| 910 | 910 | > | |
| 911 | 911 | <span | |
| ⋯ | |||
| 1026 | 1026 | function Phone({ frame }: { frame: Frame }) { | |
| 1027 | 1027 | const scenes: Scene[] = ["code", "session", "chat", "docs"]; | |
| 1028 | 1028 | return ( | |
| 1029 | − | <div className="relative flex h-[34rem] w-full flex-col overflow-hidden rounded-2xl bg-bg text-left text-fg shadow-2xl shadow-black/50 ring-1 ring-line scheme-dark"> | |
| 1029 | + | <div className="relative flex h-136 w-full flex-col overflow-hidden rounded-2xl bg-bg text-left text-fg shadow-2xl shadow-black/50 ring-1 ring-line scheme-dark"> | |
| 1030 | 1030 | <div className="flex h-12 shrink-0 items-center gap-2 border-b border-line px-3"> | |
| 1031 | 1031 | <Avatar name="acme" size={26} square /> | |
| 1032 | 1032 | <span key={frame.scene} className="tour-fade flex min-w-0 items-center gap-1.5 text-[13px] font-semibold text-fg"> | |
| ⋯ | |||
| 1076 | 1076 | function PhoneChat({ frame }: { frame: Frame }) { | |
| 1077 | 1077 | return ( | |
| 1078 | 1078 | <div className="flex h-full flex-col"> | |
| 1079 | − | <div className="flex min-h-0 flex-1 flex-col justify-end overflow-hidden [mask-image:linear-gradient(to_bottom,transparent,black_2.5rem)] [&>div]:px-3"> | |
| 1079 | + | <div className="flex min-h-0 flex-1 flex-col justify-end overflow-hidden mask-[linear-gradient(to_bottom,transparent,black_2.5rem)] [&>div]:px-3"> | |
| 1080 | 1080 | <Message who="Dana" time="09:12"> | |
| 1081 | 1081 | <p>Two enterprise customers hit the CSV export timeout again this morning.</p> | |
| 1082 | 1082 | </Message> | |
| 34 | 34 | return ( | |
| 35 | 35 | <span | |
| 36 | 36 | className={cn( | |
| 37 | − | "absolute -top-1 -right-1.5 min-w-[1.125rem] rounded-full px-1 text-center text-[0.625rem] leading-[1.125rem] font-bold tabular-nums ring-2 ring-rail", | |
| 37 | + | "absolute -top-1 -right-1.5 min-w-4.5 rounded-full px-1 text-center text-[0.625rem] leading-4.5 font-bold tabular-nums ring-2 ring-rail", | |
| 38 | 38 | loud ? "bg-accent text-bg" : "bg-line-strong text-fg", | |
| 39 | 39 | className, | |
| 40 | 40 | )} | |
| ⋯ | |||
| 189 | 189 | <LayoutGrid /> | |
| 190 | 190 | </PopoverTrigger> | |
| 191 | 191 | </SidebarMenuButton> | |
| 192 | − | <PopoverContent side="right" align="end" sideOffset={12} aria-label="Apps" className="w-[23rem] p-2.5"> | |
| 192 | + | <PopoverContent side="right" align="end" sideOffset={12} aria-label="Apps" className="w-92 p-2.5"> | |
| 193 | 193 | <AppsLauncher slug={slug} pins={pins} onToggle={onToggle} onClose={() => setOpen(false)} /> | |
| 194 | 194 | </PopoverContent> | |
| 195 | 195 | </Popover> | |
| ⋯ | |||
| 248 | 248 | <SidebarMenuButton asChild size="icon" tooltip="Home"> | |
| 249 | 249 | <Link to={homePagePath(slug)} aria-label="g1t: Home"> | |
| 250 | 250 | <span className="flex"> | |
| 251 | − | <Mark tight className="h-[1.125rem] w-auto" /> | |
| 251 | + | <Mark tight className="h-4.5 w-auto" /> | |
| 252 | 252 | </span> | |
| 253 | 253 | </Link> | |
| 254 | 254 | </SidebarMenuButton> | |
| ⋯ | |||
| 270 | 270 | </SidebarMenu> | |
| 271 | 271 | {/* Your pinned apps: as many as you like, scrolling under a fade at either end. */} | |
| 272 | 272 | {shown.length > 0 && ( | |
| 273 | − | <SidebarMenu className="mt-2 w-auto min-h-0 grow gap-2 overflow-y-auto py-2 [mask-image:linear-gradient(to_bottom,transparent,#000_10px,#000_calc(100%-14px),transparent)] [scrollbar-width:none]"> | |
| 273 | + | <SidebarMenu className="mt-2 w-auto min-h-0 grow gap-2 overflow-y-auto py-2 mask-[linear-gradient(to_bottom,transparent,#000_10px,#000_calc(100%-14px),transparent)] scrollbar-none"> | |
| 274 | 274 | {shown.map((app) => ( | |
| 275 | 275 | <RailLink key={app.key} to={app.to} label={app.name} icon={app.icon(20)} current={at(app.to)} /> | |
| 276 | 276 | ))} | |
| 40 | 40 | <Card className="min-w-0 overflow-hidden"> | |
| 41 | 41 | <div className="border-b border-line px-5 py-4"> | |
| 42 | 42 | <div className="flex flex-wrap items-center gap-2"> | |
| 43 | − | <h3 className="min-w-0 text-xl font-semibold tracking-tight break-words"> | |
| 43 | + | <h3 className="min-w-0 text-xl font-semibold tracking-tight wrap-break-word"> | |
| 44 | 44 | {linkTitle ? ( | |
| 45 | 45 | <Link to={`${base}/releases/tag/${encodeTag(release.tagName)}`} className="hover:text-accent hover:underline"> | |
| 46 | 46 | {title} |
| 70 | 70 | return ( | |
| 71 | 71 | <span | |
| 72 | 72 | key={to} | |
| 73 | − | className={`${i < folded ? "hidden sm:flex" : "flex"} items-center ${last ? "min-w-0" : i === segments.length - 2 ? "min-w-0 shrink-[2]" : "shrink-0"}`} | |
| 73 | + | className={`${i < folded ? "hidden sm:flex" : "flex"} items-center ${last ? "min-w-0" : i === segments.length - 2 ? "min-w-0 shrink-2" : "shrink-0"}`} | |
| 74 | 74 | > | |
| 75 | 75 | {slash} | |
| 76 | 76 | {last ? ( | |
| ⋯ | |||
| 485 | 485 | ) : lines ? ( | |
| 486 | 486 | <CodeLines lines={lines} html={html} marked={marked} /> | |
| 487 | 487 | ) : isImagePath(path) ? ( | |
| 488 | − | <div className="flex justify-center bg-[repeating-conic-gradient(var(--color-raised)_0_25%,transparent_0_50%)] bg-[length:16px_16px] p-6"> | |
| 488 | + | <div className="flex justify-center bg-[repeating-conic-gradient(var(--color-raised)_0_25%,transparent_0_50%)] bg-size-[16px_16px] p-6"> | |
| 489 | 489 | <img src={raw} alt={path.split("/").pop() ?? path} className="max-h-[70vh] max-w-full" /> | |
| 490 | 490 | </div> | |
| 491 | 491 | ) : ( | |
| 368 | 368 | {total === 0 ? ( | |
| 369 | 369 | <p className="text-sm text-muted">No machine time yet this month.</p> | |
| 370 | 370 | ) : ( | |
| 371 | − | <div className="flex h-2.5 gap-[2px] overflow-hidden rounded-full" role="img" aria-label="This month's machine time by where it ran"> | |
| 372 | − | {cost.cloudSeconds > 0 && <span className="h-full min-w-[3px] bg-info" style={{ flexGrow: cost.cloudSeconds }} />} | |
| 373 | − | {cost.ownSeconds > 0 && <span className="h-full min-w-[3px] bg-success" style={{ flexGrow: cost.ownSeconds }} />} | |
| 371 | + | <div className="flex h-2.5 gap-0.5 overflow-hidden rounded-full" role="img" aria-label="This month's machine time by where it ran"> | |
| 372 | + | {cost.cloudSeconds > 0 && <span className="h-full min-w-0.75 bg-info" style={{ flexGrow: cost.cloudSeconds }} />} | |
| 373 | + | {cost.ownSeconds > 0 && <span className="h-full min-w-0.75 bg-success" style={{ flexGrow: cost.ownSeconds }} />} | |
| 374 | 374 | </div> | |
| 375 | 375 | )} | |
| 376 | 376 | <ul className="mt-3 space-y-2 text-sm"> | |
| 377 | 377 | <li className="flex items-center gap-2"> | |
| 378 | − | <span className="size-2 shrink-0 rounded-[2px] bg-info" aria-hidden /> | |
| 378 | + | <span className="size-2 shrink-0 rounded-xs bg-info" aria-hidden /> | |
| 379 | 379 | <span className="min-w-0 grow truncate text-fg-soft">g1t cloud</span> | |
| 380 | 380 | <span className="text-muted tabular-nums">{machineTime(cost.cloudSeconds)}</span> | |
| 381 | 381 | <span className="w-20 text-right text-fg tabular-nums">{money(cost.cloudMicros)}</span> | |
| 382 | 382 | </li> | |
| 383 | 383 | <li className="flex items-center gap-2"> | |
| 384 | − | <span className="size-2 shrink-0 rounded-[2px] bg-success" aria-hidden /> | |
| 384 | + | <span className="size-2 shrink-0 rounded-xs bg-success" aria-hidden /> | |
| 385 | 385 | <span className="min-w-0 grow truncate text-fg-soft">Your runners</span> | |
| 386 | 386 | <span className="text-muted tabular-nums">{machineTime(cost.ownSeconds)}</span> | |
| 387 | 387 | <span className="w-20 text-right text-fg tabular-nums">{money(0)}</span> |
| 145 | 145 | <span className={`mt-0.5 hidden shrink-0 sm:block ${alert.state === "open" ? "text-warn" : "text-muted"}`}>{STATE_ICON[alert.state]}</span> | |
| 146 | 146 | <div className="min-w-0 grow"> | |
| 147 | 147 | <div className="flex flex-wrap items-center gap-2"> | |
| 148 | − | <Link to={`${base}/security/code-scanning/${alert.number}`} className="min-w-0 text-sm font-medium break-words hover:underline"> | |
| 148 | + | <Link to={`${base}/security/code-scanning/${alert.number}`} className="min-w-0 text-sm font-medium wrap-break-word hover:underline"> | |
| 149 | 149 | {alert.ruleName && alert.ruleName !== alert.ruleId ? alert.ruleName : shortRule(alert.ruleId)} | |
| 150 | 150 | </Link> | |
| 151 | 151 | <SeverityBadge severity={alert.severity} /> | |
| ⋯ | |||
| 313 | 313 | } | |
| 314 | 314 | return ( | |
| 315 | 315 | <div className={CARD}> | |
| 316 | − | <Table className="min-w-[46rem]"> | |
| 316 | + | <Table className="min-w-184"> | |
| 317 | 317 | <TableHeader> | |
| 318 | 318 | <TableRow> | |
| 319 | 319 | <TableHead className="px-4">Repository</TableHead> | |