Align page typography to the unified scale
Sweep the remaining inline type across every page onto the system scale: oversized metric numbers (22/24px) down to the 18px title step, body text 13px → 14px, captions/meta 11px and 12.5px → 12px, and small control icons 13/11px → 16/14px. No structural or layout changes — the foundation already gridded spacing, radius, and color; this finishes the type so the whole app reads from one ramp. Full interactive e2e and the overflow guards stay green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f5aab15920
commit
fa01aa682f
31 files changed
+120
-120
No files matched your search
@@ -143,14 +143,14 @@ function ApprovalDetailDialog({
|
||||
<IconButton icon="x" label="Close" onClick={onClose} />
|
||||
</div>
|
||||
<div className="row" style={{ flexWrap: "wrap", gap: 6, marginBottom: 14 }}>
|
||||
<span style={{ fontSize: 11, color: tone.color, fontWeight: 540, letterSpacing: "0.02em" }}>{tone.label}</span>
|
||||
<span style={{ fontSize: 12, color: tone.color, fontWeight: 540, letterSpacing: "0.02em" }}>{tone.label}</span>
|
||||
<span style={{ color: "var(--text-4)" }}>·</span>
|
||||
<Pill>{approval.kind}</Pill>
|
||||
</div>
|
||||
<p style={{ margin: "0 0 16px", color: "var(--text-2)", fontSize: 13, lineHeight: 1.55, whiteSpace: "pre-wrap" }}>
|
||||
<p style={{ margin: "0 0 16px", color: "var(--text-2)", fontSize: 14, lineHeight: 1.55, whiteSpace: "pre-wrap" }}>
|
||||
{approval.details}
|
||||
</p>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: "10px 14px", fontSize: 12.5 }}>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: "10px 14px", fontSize: 12 }}>
|
||||
<span style={{ color: "var(--text-3)" }}>Requested by</span>
|
||||
<span>{requester}</span>
|
||||
{approval.sourceEntityType && (<><span style={{ color: "var(--text-3)" }}>Source</span><span>{humanizeKind(approval.sourceEntityType)}</span></>)}
|
||||
@@ -199,7 +199,7 @@ function StatTile({ label, value, tone }: { label: string; value: string; tone?:
|
||||
<div className="card-label" style={{ marginBottom: 6 }}>{label}</div>
|
||||
<div
|
||||
className="num"
|
||||
style={{ fontSize: 22, fontWeight: 600, color: tone === "amber" ? "var(--amber)" : "var(--text-1)", letterSpacing: "-0.02em" }}
|
||||
style={{ fontSize: 18, fontWeight: 600, color: tone === "amber" ? "var(--amber)" : "var(--text-1)", letterSpacing: "-0.02em" }}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
@@ -241,12 +241,12 @@ function ApprovalRow({
|
||||
<div style={{ width: 3, alignSelf: "stretch", background: tone.color, borderRadius: 2 }} />
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div className="row" style={{ marginBottom: 4 }}>
|
||||
<span style={{ fontSize: 11, color: tone.color, fontWeight: 540, letterSpacing: "0.02em" }}>{tone.label}</span>
|
||||
<span style={{ fontSize: 12, color: tone.color, fontWeight: 540, letterSpacing: "0.02em" }}>{tone.label}</span>
|
||||
<span style={{ color: "var(--text-4)" }}>·</span>
|
||||
<Pill>{approval.kind}</Pill>
|
||||
</div>
|
||||
<div style={{ fontSize: 15, fontWeight: 540, letterSpacing: "-0.01em", marginBottom: 4 }}>{approval.title}</div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.5, marginBottom: 12, whiteSpace: "pre-wrap" }}>
|
||||
<div style={{ fontSize: 14, color: "var(--text-2)", lineHeight: 1.5, marginBottom: 12, whiteSpace: "pre-wrap" }}>
|
||||
{approval.details}
|
||||
</div>
|
||||
<div className="row" style={{ gap: 8 }}>
|
||||
@@ -282,7 +282,7 @@ function DecidedRow({
|
||||
style={{
|
||||
gap: 12,
|
||||
padding: "12px 18px",
|
||||
fontSize: 13,
|
||||
fontSize: 14,
|
||||
borderBottom: last ? "none" : "1px solid var(--border-soft)",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -70,7 +70,7 @@ export function DocumentsPage({ snapshot, actions }: PageProps) {
|
||||
<option value="all">All types</option>
|
||||
{DOC_TYPES.map((t) => <option key={t} value={t}>{t}</option>)}
|
||||
</select>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={13} /> New document</Button>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={16} /> New document</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Documents</h1>
|
||||
<p className="page-sub">{documents.length} {documents.length === 1 ? "document" : "documents"}</p>
|
||||
|
||||
@@ -75,7 +75,7 @@ export function FilesPage({ snapshot, actions }: PageProps) {
|
||||
]}
|
||||
/>
|
||||
<Button tone="primary" onClick={upload}>
|
||||
<Icon name="upload" size={13} /> Upload
|
||||
<Icon name="upload" size={16} /> Upload
|
||||
</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Files</h1>
|
||||
@@ -104,7 +104,7 @@ export function FilesPage({ snapshot, actions }: PageProps) {
|
||||
|
||||
<div className="row" style={{ marginBottom: 10 }}>
|
||||
<div className="section-label">{labelFor(section)}</div>
|
||||
<div style={{ marginLeft: "auto", fontSize: 11, color: "var(--text-3)" }}>Modified ↓</div>
|
||||
<div style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-3)" }}>Modified ↓</div>
|
||||
</div>
|
||||
|
||||
{view === "list" ? (
|
||||
@@ -148,7 +148,7 @@ function FilesFilter({
|
||||
<Popover
|
||||
trigger={({ toggle }) => (
|
||||
<button type="button" className="button ghost" aria-label="Filter files" onClick={toggle}>
|
||||
<Icon name="filter" size={13} color={scan !== "all" ? "var(--accent)" : "currentColor"} />
|
||||
<Icon name="filter" size={16} color={scan !== "all" ? "var(--accent)" : "currentColor"} />
|
||||
</button>
|
||||
)}
|
||||
>
|
||||
@@ -158,7 +158,7 @@ function FilesFilter({
|
||||
{options.map((option) => (
|
||||
<MenuRow key={option.id} active={scan === option.id} onClick={() => onScan(option.id)}>
|
||||
{option.label}
|
||||
{scan === option.id && <span className="menu-check"><Icon name="check" size={13} /></span>}
|
||||
{scan === option.id && <span className="menu-check"><Icon name="check" size={16} /></span>}
|
||||
</MenuRow>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -62,11 +62,11 @@ export function InboxPage({ snapshot }: PageProps) {
|
||||
<div className="row" style={{ gap: 10 }}>
|
||||
{item.agentName && <Avatar name={item.agentName} size="sm" bot />}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{item.title}</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{item.title}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{item.detail}</div>
|
||||
</div>
|
||||
<Pill tone={item.tone}>{humanize(item.kind)}</Pill>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)", flexShrink: 0 }}>{formatRelative(item.timestamp)}</span>
|
||||
<span className="mono" style={{ fontSize: 12, color: "var(--text-3)", flexShrink: 0 }}>{formatRelative(item.timestamp)}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -155,7 +155,7 @@ function MetricCard({ label, value, sub }: { label: string; value: string; sub?:
|
||||
<div className="card" style={{ padding: 14 }}>
|
||||
<div className="card-label" style={{ marginBottom: 6 }}>{label}</div>
|
||||
<div className="row" style={{ alignItems: "baseline", gap: 4 }}>
|
||||
<span className="num" style={{ fontSize: 24, fontWeight: 600 }}>{value}</span>
|
||||
<span className="num" style={{ fontSize: 18, fontWeight: 600 }}>{value}</span>
|
||||
{sub && <span style={{ fontSize: 12, color: "var(--text-3)" }}>{sub}</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function KnowledgePage({ snapshot, actions }: PageProps) {
|
||||
<option value="all">All categories</option>
|
||||
{CATEGORIES.map((c) => <option key={c} value={c}>{c}</option>)}
|
||||
</select>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={13} /> Add entry</Button>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={16} /> Add entry</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Knowledge Base</h1>
|
||||
<p className="page-sub">{entries.length} {entries.length === 1 ? "entry" : "entries"}</p>
|
||||
@@ -61,10 +61,10 @@ export function KnowledgePage({ snapshot, actions }: PageProps) {
|
||||
<Pill>{entry.category}</Pill>
|
||||
<Pill tone={importanceTone(entry.importance)}>{entry.importance}</Pill>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.5, whiteSpace: "pre-wrap", maxHeight: 120, overflow: "hidden" }}>
|
||||
<div style={{ fontSize: 14, color: "var(--text-2)", lineHeight: 1.5, whiteSpace: "pre-wrap", maxHeight: 120, overflow: "hidden" }}>
|
||||
{entry.content}
|
||||
</div>
|
||||
<div style={{ marginTop: 8, fontSize: 11, color: "var(--text-3)" }}>{formatRelative(entry.updatedAt)}</div>
|
||||
<div style={{ marginTop: 8, fontSize: 12, color: "var(--text-3)" }}>{formatRelative(entry.updatedAt)}</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ export function MeetingsPage({ snapshot, actions }: PageProps) {
|
||||
return (
|
||||
<div className="page">
|
||||
<TopbarActions>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={13} /> Schedule</Button>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={16} /> Schedule</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Meetings</h1>
|
||||
<p className="page-sub">{scheduled.length} upcoming · {completed.length} past</p>
|
||||
@@ -91,7 +91,7 @@ function MeetingCard({ meeting, onDelete }: { meeting: { id: string; title: stri
|
||||
<Pill>{meeting.durationMinutes}m</Pill>
|
||||
</div>
|
||||
{meeting.scheduledAt && <div style={{ fontSize: 12, color: "var(--text-3)" }}>{new Date(meeting.scheduledAt).toLocaleString()}</div>}
|
||||
{meeting.agenda && <div style={{ fontSize: 13, color: "var(--text-2)", marginTop: 6, whiteSpace: "pre-wrap", lineHeight: 1.5 }}>{meeting.agenda}</div>}
|
||||
{meeting.agenda && <div style={{ fontSize: 14, color: "var(--text-2)", marginTop: 6, whiteSpace: "pre-wrap", lineHeight: 1.5 }}>{meeting.agenda}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ export function SettingsPage({ snapshot, actions }: PageProps) {
|
||||
return (
|
||||
<div className="split-main">
|
||||
<aside className="secondary-pane">
|
||||
<div style={{ fontSize: 13, fontWeight: 600, padding: "4px 8px 14px" }}>Settings</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 600, padding: "4px 8px 14px" }}>Settings</div>
|
||||
<div className="pane-scroll">
|
||||
<NavGroup items={TABS} active={tab} onChange={setTab} />
|
||||
</div>
|
||||
|
||||
@@ -82,9 +82,9 @@ export function SocialPage({ snapshot, actions }: PageProps) {
|
||||
return (
|
||||
<div className="page" style={{ display: "flex", flexDirection: "column", minHeight: 0, height: "100%", padding: "28px 32px 0" }}>
|
||||
<TopbarActions>
|
||||
<Button onClick={() => setConnectOpen(true)}><Icon name="plus" size={13} /> Connect account</Button>
|
||||
<Button onClick={() => setConnectOpen(true)}><Icon name="plus" size={16} /> Connect account</Button>
|
||||
<Button tone="primary" onClick={() => setDraftOpen(true)} disabled={accounts.length === 0}>
|
||||
<Icon name="sparkle" size={13} /> Draft post
|
||||
<Icon name="sparkle" size={16} /> Draft post
|
||||
</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Social</h1>
|
||||
@@ -224,12 +224,12 @@ function AccountCard({
|
||||
<div className="row" style={{ gap: 10, minWidth: 0 }}>
|
||||
<PlatformIcon platform={account.platform} />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{account.handle}</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{account.displayName}</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{account.handle}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{account.displayName}</div>
|
||||
</div>
|
||||
</div>
|
||||
{authorizedName && (
|
||||
<div className="row" style={{ fontSize: 11, color: "var(--text-3)", gap: 6 }}>
|
||||
<div className="row" style={{ fontSize: 12, color: "var(--text-3)", gap: 6 }}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: 3, background: "var(--green)" }} /> Authorized to {authorizedName}
|
||||
</div>
|
||||
)}
|
||||
@@ -286,10 +286,10 @@ function PostRow({
|
||||
{post.state === "published" && <Pill tone="green" dot>Published</Pill>}
|
||||
{post.state === "cancelled" && <Pill tone="red">Cancelled</Pill>}
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-1)", lineHeight: 1.5, marginBottom: 8, whiteSpace: "pre-wrap" }}>{post.bodyMd}</div>
|
||||
<div style={{ fontSize: 14, color: "var(--text-1)", lineHeight: 1.5, marginBottom: 8, whiteSpace: "pre-wrap" }}>{post.bodyMd}</div>
|
||||
<div className="row" style={{ gap: 6 }}>
|
||||
<Avatar name={authorName} size="sm" bot={post.createdByEmployeeId === null} />
|
||||
<span style={{ fontSize: 11, color: "var(--text-3)" }}>By {authorName}</span>
|
||||
<span style={{ fontSize: 12, color: "var(--text-3)" }}>By {authorName}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function SprintsPage({ snapshot, actions }: PageProps) {
|
||||
return (
|
||||
<div className="page">
|
||||
<TopbarActions>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={13} /> New sprint</Button>
|
||||
<Button tone="primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={16} /> New sprint</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Sprints</h1>
|
||||
<p className="page-sub">{sprints.length} {sprints.length === 1 ? "sprint" : "sprints"}</p>
|
||||
@@ -78,7 +78,7 @@ function SprintCard({ sprint, tasks, onDelete }: { sprint: SprintRecord; tasks:
|
||||
<Pill tone={sprintStatusTone(sprint.status)}>{sprint.status}</Pill>
|
||||
<Pill>{sprint.startDate} to {sprint.endDate}</Pill>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: "var(--text-2)", lineHeight: 1.5, marginBottom: 12 }}>{sprint.goal}</div>
|
||||
<div style={{ fontSize: 14, color: "var(--text-2)", lineHeight: 1.5, marginBottom: 12 }}>{sprint.goal}</div>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
||||
<ProgressBar label="Time" pct={progressPct} />
|
||||
<ProgressBar label="Tasks" pct={taskPct} />
|
||||
|
||||
@@ -97,9 +97,9 @@ export function TeamPage({ snapshot, actions }: PageProps) {
|
||||
<div className="page">
|
||||
<TopbarActions>
|
||||
<Button tone="ghost" onClick={() => cycleFilter(filter, setFilter)} title="Filter">
|
||||
<Icon name="filter" size={13} /> {filterLabel(filter)}
|
||||
<Icon name="filter" size={16} /> {filterLabel(filter)}
|
||||
</Button>
|
||||
<Button tone="primary" onClick={openHireDialog}><Icon name="plus" size={13} /> Hire</Button>
|
||||
<Button tone="primary" onClick={openHireDialog}><Icon name="plus" size={16} /> Hire</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Team</h1>
|
||||
<p className="page-sub">
|
||||
@@ -266,7 +266,7 @@ function RosterRow({ row, last, onEdit }: { row: RosterEntry; last: boolean; onE
|
||||
<Avatar name={row.name} size="sm" bot={!row.human} />
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{row.name}</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{row.role}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{row.role}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ color: "var(--text-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{row.dept}</div>
|
||||
@@ -296,7 +296,7 @@ function RosterRow({ row, last, onEdit }: { row: RosterEntry; last: boolean; onE
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="num mono" style={{ fontSize: 11, color: "var(--text-3)" }}>{money(row.budgetCents)}</span>
|
||||
<span className="num mono" style={{ fontSize: 12, color: "var(--text-3)" }}>{money(row.budgetCents)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -92,9 +92,9 @@ export function WorkPage({ snapshot, actions }: PageProps) {
|
||||
onPriority={setPriorityFilter}
|
||||
onAssignee={setAssigneeFilter}
|
||||
/>
|
||||
<Button tone="ghost" onClick={() => setNewGoalOpen(true)}><Icon name="plus" size={13} /> Goal</Button>
|
||||
<Button tone="ghost" onClick={() => setNewProjectOpen(true)}><Icon name="plus" size={13} /> Project</Button>
|
||||
<Button tone="primary" onClick={() => setNewTaskOpen(true)}><Icon name="plus" size={13} /> New task</Button>
|
||||
<Button tone="ghost" onClick={() => setNewGoalOpen(true)}><Icon name="plus" size={16} /> Goal</Button>
|
||||
<Button tone="ghost" onClick={() => setNewProjectOpen(true)}><Icon name="plus" size={16} /> Project</Button>
|
||||
<Button tone="primary" onClick={() => setNewTaskOpen(true)}><Icon name="plus" size={16} /> New task</Button>
|
||||
</TopbarActions>
|
||||
<h1 className="page-h1">Work</h1>
|
||||
<p className="page-sub">
|
||||
@@ -178,7 +178,7 @@ function WorkFilter({
|
||||
<Popover
|
||||
trigger={({ toggle }) => (
|
||||
<button type="button" className="button ghost" aria-label="Filter tasks" onClick={toggle}>
|
||||
<Icon name="filter" size={13} color={active ? "var(--accent)" : "currentColor"} />
|
||||
<Icon name="filter" size={16} color={active ? "var(--accent)" : "currentColor"} />
|
||||
</button>
|
||||
)}
|
||||
>
|
||||
@@ -188,19 +188,19 @@ function WorkFilter({
|
||||
{priorities.map((option) => (
|
||||
<MenuRow key={option.id} active={priority === option.id} onClick={() => onPriority(option.id)}>
|
||||
{option.label}
|
||||
{priority === option.id && <span className="menu-check"><Icon name="check" size={13} /></span>}
|
||||
{priority === option.id && <span className="menu-check"><Icon name="check" size={16} /></span>}
|
||||
</MenuRow>
|
||||
))}
|
||||
<MenuDivider />
|
||||
<MenuLabel>Assignee</MenuLabel>
|
||||
<MenuRow active={assignee === "all"} onClick={() => onAssignee("all")}>
|
||||
All assignees
|
||||
{assignee === "all" && <span className="menu-check"><Icon name="check" size={13} /></span>}
|
||||
{assignee === "all" && <span className="menu-check"><Icon name="check" size={16} /></span>}
|
||||
</MenuRow>
|
||||
{agents.map((agent) => (
|
||||
<MenuRow key={agent.id} active={assignee === agent.id} onClick={() => onAssignee(agent.id)}>
|
||||
{agent.name}
|
||||
{assignee === agent.id && <span className="menu-check"><Icon name="check" size={13} /></span>}
|
||||
{assignee === agent.id && <span className="menu-check"><Icon name="check" size={16} /></span>}
|
||||
</MenuRow>
|
||||
))}
|
||||
</>
|
||||
@@ -242,7 +242,7 @@ function BoardView({
|
||||
<div key={column.id} style={{ display: "flex", flexDirection: "column", gap: 8, minWidth: 0 }}>
|
||||
<div className="row" style={{ padding: "4px 6px 8px" }}>
|
||||
<span style={{ width: 8, height: 8, borderRadius: 2, background: column.tone, flexShrink: 0 }} />
|
||||
<span style={{ fontSize: 12.5, fontWeight: 540 }}>{column.label}</span>
|
||||
<span style={{ fontSize: 12, fontWeight: 540 }}>{column.label}</span>
|
||||
<span className="num" style={{ fontSize: 12, color: "var(--text-3)" }}>{columnTasks.length}</span>
|
||||
<IconButton icon="plus" label="Add task" onClick={onCreate} />
|
||||
</div>
|
||||
@@ -427,7 +427,7 @@ function TaskCard({
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
fontSize: 14,
|
||||
fontWeight: 480,
|
||||
lineHeight: 1.35,
|
||||
letterSpacing: "-0.005em",
|
||||
|
||||
@@ -61,7 +61,7 @@ export function ChatComposer({
|
||||
<IconButton icon="sparkle" label="Commands" onClick={() => setSlashOpen((open) => !open)} />
|
||||
{attachment && (
|
||||
<span className="pill" style={{ maxWidth: 220 }}>
|
||||
<Icon name="file" size={11} />
|
||||
<Icon name="file" size={14} />
|
||||
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{attachment.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
@@ -69,12 +69,12 @@ export function ChatComposer({
|
||||
onClick={() => onAttachmentChange("")}
|
||||
style={{ display: "inline-flex", border: 0, background: "transparent", padding: 0, color: "var(--text-3)", cursor: "pointer" }}
|
||||
>
|
||||
<Icon name="x" size={11} />
|
||||
<Icon name="x" size={14} />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
<div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 6 }}>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>Ctrl+Enter</span>
|
||||
<span className="mono" style={{ fontSize: 12, color: "var(--text-3)" }}>Ctrl+Enter</span>
|
||||
<Button tone="primary" size="small" type="submit" disabled={disabled}><Icon name="send" size={12} /> Send</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,13 +58,13 @@ export function ChatMessageRow({
|
||||
<Avatar name={name} size={compact ? "sm" : ""} bot={bot} />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div className="row" style={{ alignItems: "baseline", gap: 8, marginBottom: 2 }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 540 }}>{name}</span>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>{formatTime(message.createdAt)}</span>
|
||||
{message.editedAt && !deleted && <span style={{ fontSize: 11, color: "var(--text-3)" }}>(edited)</span>}
|
||||
<span style={{ fontSize: 14, fontWeight: 540 }}>{name}</span>
|
||||
<span className="mono" style={{ fontSize: 12, color: "var(--text-3)" }}>{formatTime(message.createdAt)}</span>
|
||||
{message.editedAt && !deleted && <span style={{ fontSize: 12, color: "var(--text-3)" }}>(edited)</span>}
|
||||
{canModify && !editing && (
|
||||
<span style={{ marginLeft: "auto", display: "flex", gap: 2 }}>
|
||||
<IconButton icon="edit" label="Edit message" size={13} onClick={() => setEditing(true)} />
|
||||
<IconButton icon="x" label="Delete message" size={13} onClick={onDelete} />
|
||||
<IconButton icon="edit" label="Edit message" size={16} onClick={() => setEditing(true)} />
|
||||
<IconButton icon="x" label="Delete message" size={16} onClick={onDelete} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -74,7 +74,7 @@ export function ChatMessageRow({
|
||||
</div>
|
||||
)}
|
||||
{deleted ? (
|
||||
<div style={{ fontSize: 13, color: "var(--text-3)", fontStyle: "italic" }}>Message deleted</div>
|
||||
<div style={{ fontSize: 14, color: "var(--text-3)", fontStyle: "italic" }}>Message deleted</div>
|
||||
) : editing ? (
|
||||
<form onSubmit={submitEdit}>
|
||||
<textarea
|
||||
@@ -91,7 +91,7 @@ export function ChatMessageRow({
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
<div style={{ fontSize: 13, color: "var(--text-1)", lineHeight: 1.5, whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
|
||||
<div style={{ fontSize: 14, color: "var(--text-1)", lineHeight: 1.5, whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
|
||||
{renderBody(message.bodyMd, messageMentions, context)}
|
||||
</div>
|
||||
)}
|
||||
@@ -123,7 +123,7 @@ export function ChatMessageRow({
|
||||
border: 0,
|
||||
}}
|
||||
>
|
||||
<Icon name="chat" size={11} /> {replyCount > 0 ? `${replyCount} ${replyCount === 1 ? "reply" : "replies"}` : "Thread"} →
|
||||
<Icon name="chat" size={14} /> {replyCount > 0 ? `${replyCount} ${replyCount === 1 ? "reply" : "replies"}` : "Thread"} →
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -205,14 +205,14 @@ function AttachmentCard({ file, handoff = false, onOpen }: { file: FileObjectRec
|
||||
</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div className="row" style={{ gap: 6, minWidth: 0 }}>
|
||||
<span style={{ fontSize: 12.5, fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{file.name}</span>
|
||||
<span style={{ fontSize: 12, fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{file.name}</span>
|
||||
{handoff && (
|
||||
<span className="pill violet" style={{ flexShrink: 0 }}>
|
||||
<Icon name="bot" size={10} /> AI → AI
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{file.kind.toUpperCase()} · {file.sha256.slice(0, 8)}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{file.kind.toUpperCase()} · {file.sha256.slice(0, 8)}</div>
|
||||
</div>
|
||||
{canOpen && <IconButton icon="arrowUpRight" label={`Open ${file.name}`} onClick={onOpen ?? undefined} />}
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ export function ChatSidebar({
|
||||
return (
|
||||
<aside className="secondary-pane">
|
||||
<div className="row" style={{ padding: "4px 8px 12px" }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 600 }}>Messages</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 600 }}>Messages</div>
|
||||
<IconButton icon="plus" label="New space" onClick={onCreateSpace} />
|
||||
</div>
|
||||
<div className="pane-scroll">
|
||||
@@ -81,7 +81,7 @@ export function ChatSidebar({
|
||||
function NavGroup({ label, children }: { label: string; children: ReactNode }) {
|
||||
return (
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)", padding: "6px 8px", fontWeight: 540, letterSpacing: "0.02em" }}>{label}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", padding: "6px 8px", fontWeight: 540, letterSpacing: "0.02em" }}>{label}</div>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 1 }}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
@@ -104,7 +104,7 @@ function ChannelItem({
|
||||
}) {
|
||||
return (
|
||||
<button type="button" className={`nav-item ${active ? "active" : ""}`.trim()} onClick={onClick}>
|
||||
<Icon name={space.visibility === "restricted" ? "lock" : "hash"} size={13} color={active ? "var(--text-1)" : "var(--text-3)"} />
|
||||
<Icon name={space.visibility === "restricted" ? "lock" : "hash"} size={16} color={active ? "var(--text-1)" : "var(--text-3)"} />
|
||||
<span>{space.name}</span>
|
||||
{count > 0 && <span className="nav-badge">{count}</span>}
|
||||
</button>
|
||||
@@ -136,7 +136,7 @@ function DirectItem({
|
||||
{dotColor && <span className="presence-dot" style={{ background: dotColor }} />}
|
||||
</span>
|
||||
) : (
|
||||
<Icon name="user" size={13} />
|
||||
<Icon name="user" size={16} />
|
||||
)}
|
||||
<span>{space.name}</span>
|
||||
{count > 0 && <span className="nav-badge">{count}</span>}
|
||||
|
||||
@@ -68,7 +68,7 @@ export function ChatThreadPanel({
|
||||
}}
|
||||
>
|
||||
<div className="row" style={{ padding: "14px 16px", borderBottom: "1px solid var(--border-soft)", flexShrink: 0 }}>
|
||||
<div style={{ fontSize: 12.5, fontWeight: 600 }}>Thread</div>
|
||||
<div style={{ fontSize: 12, fontWeight: 600 }}>Thread</div>
|
||||
<Pill tone={boardStateTone(thread.boardState)}>{boardStateLabel(thread.boardState)}</Pill>
|
||||
<div style={{ marginLeft: "auto" }}>
|
||||
<IconButton icon="x" label="Close thread" onClick={onClose} />
|
||||
@@ -134,7 +134,7 @@ export function ChatThreadPanel({
|
||||
<IconButton icon="attach" label="Attach file" onClick={onAttach} />
|
||||
{attachment && (
|
||||
<span className="pill" style={{ maxWidth: 150 }}>
|
||||
<Icon name="file" size={11} />
|
||||
<Icon name="file" size={14} />
|
||||
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{attachment.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
@@ -142,7 +142,7 @@ export function ChatThreadPanel({
|
||||
onClick={() => onAttachmentChange("")}
|
||||
style={{ display: "inline-flex", border: 0, background: "transparent", padding: 0, color: "var(--text-3)", cursor: "pointer" }}
|
||||
>
|
||||
<Icon name="x" size={11} />
|
||||
<Icon name="x" size={14} />
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
@@ -215,7 +215,7 @@ function BoardControl({
|
||||
{interventions.map((intervention) => (
|
||||
<div key={intervention.id} style={{ borderTop: "1px solid var(--border-soft)", paddingTop: 7 }}>
|
||||
<div className="row" style={{ gap: 6, marginBottom: 3 }}>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>{formatTime(intervention.createdAt)}</span>
|
||||
<span className="mono" style={{ fontSize: 12, color: "var(--text-3)" }}>{formatTime(intervention.createdAt)}</span>
|
||||
<Pill tone={intervention.mode === "takeover" ? "violet" : intervention.action === "freeze" ? "red" : undefined}>
|
||||
{formatBoardAction(intervention.action)}
|
||||
</Pill>
|
||||
@@ -264,7 +264,7 @@ function AuditReplay({ events }: { events: AuditEventRecord[] }) {
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)", flexShrink: 0 }}>{formatTime(event.createdAt)}</span>
|
||||
<span className="mono" style={{ fontSize: 12, color: "var(--text-3)", flexShrink: 0 }}>{formatTime(event.createdAt)}</span>
|
||||
<span style={{ fontSize: 12, color: "var(--text-1)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
{auditEventLabel(event.entityType, event.action)}
|
||||
</span>
|
||||
|
||||
@@ -94,7 +94,7 @@ function DayDivider({ label }: { label: string }) {
|
||||
return (
|
||||
<div className="row" style={{ gap: 12, margin: "8px 0 16px" }}>
|
||||
<div style={{ flex: 1, height: 1, background: "var(--border-soft)" }} />
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)", fontWeight: 540 }}>{label}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", fontWeight: 540 }}>{label}</div>
|
||||
<div style={{ flex: 1, height: 1, background: "var(--border-soft)" }} />
|
||||
</div>
|
||||
);
|
||||
@@ -126,7 +126,7 @@ function ApprovalInline({
|
||||
<Pill tone={approval.risk === "high" ? "red" : approval.risk === "medium" ? "amber" : undefined}>{approval.risk}</Pill>
|
||||
</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 540, marginBottom: 4 }}>{approval.title}</div>
|
||||
<div style={{ fontSize: 12.5, color: "var(--text-3)", marginBottom: 12, lineHeight: 1.5 }}>{approval.details}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", marginBottom: 12, lineHeight: 1.5 }}>{approval.details}</div>
|
||||
<div className="row" style={{ gap: 6 }}>
|
||||
<Button tone="primary" size="small" onClick={onApprove}>Approve</Button>
|
||||
<Button size="small" onClick={onDecline}>Decline</Button>
|
||||
|
||||
@@ -133,7 +133,7 @@ export function MentionTextarea({
|
||||
border: 0,
|
||||
background: i === index ? "var(--accent-soft)" : "transparent",
|
||||
cursor: "pointer",
|
||||
fontSize: 13,
|
||||
fontSize: 14,
|
||||
color: "var(--text-1)",
|
||||
textAlign: "left",
|
||||
}}
|
||||
@@ -155,7 +155,7 @@ export function MentionTextarea({
|
||||
border: 0,
|
||||
resize: "none",
|
||||
outline: "none",
|
||||
fontSize: 13,
|
||||
fontSize: 14,
|
||||
color: "var(--text-1)",
|
||||
background: "transparent",
|
||||
padding: 0,
|
||||
|
||||
@@ -84,7 +84,7 @@ export function PinnedCard({
|
||||
<Icon name="pin" size={14} />
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, fontWeight: 540, letterSpacing: "-0.005em", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
<div style={{ fontSize: 14, fontWeight: 540, letterSpacing: "-0.005em", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
{file.name}
|
||||
</div>
|
||||
<div className="mono" style={{ color: "var(--text-3)", fontSize: 10, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
@@ -92,12 +92,12 @@ export function PinnedCard({
|
||||
</div>
|
||||
<div className="row" style={{ gap: 6 }}>
|
||||
<Pill tone={file.scanState === "approved" ? "green" : "red"}>{file.scanState === "approved" ? "Clean" : "Blocked"}</Pill>
|
||||
<span className="mono" style={{ color: "var(--text-3)", fontSize: 11 }}>{grantCount}</span>
|
||||
<span className="mono" style={{ color: "var(--text-3)", fontSize: 12 }}>{grantCount}</span>
|
||||
</div>
|
||||
<ScanLabelStrip file={file} />
|
||||
<div className="row" style={{ gap: 8, marginTop: "auto", minWidth: 0 }}>
|
||||
<Avatar name={ownerName} size="sm" bot={isAgentOwner(file)} />
|
||||
<span style={{ fontSize: 11, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
<span style={{ fontSize: 12, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||
{formatRelative(file.updatedAt ?? file.createdAt)} · {ownerName.split(" ")[0]}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,7 @@ function FileRow({
|
||||
</div>
|
||||
<div className="row" style={{ gap: 6, minWidth: 0 }}>
|
||||
<Pill tone={canOpen ? "green" : "red"}>{canOpen ? "Clean" : "Blocked"}</Pill>
|
||||
<span className="mono" style={{ color: "var(--text-3)", fontSize: 11 }}>{grantCount}</span>
|
||||
<span className="mono" style={{ color: "var(--text-3)", fontSize: 12 }}>{grantCount}</span>
|
||||
</div>
|
||||
<div style={{ color: "var(--text-3)", fontSize: 12 }}>{formatRelative(file.updatedAt ?? file.createdAt)}</div>
|
||||
<div style={{ textAlign: "right" }} onClick={(event) => event.stopPropagation()}>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function BillingPanel({
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 20 }}>
|
||||
<div className="card" style={{ padding: 14 }}>
|
||||
<div className="card-label" style={{ marginBottom: 8 }}>Token usage (30d)</div>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: "6px 14px", fontSize: 12.5 }}>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: "6px 14px", fontSize: 12 }}>
|
||||
<span style={{ color: "var(--text-3)" }}>Input chars</span><span className="num">{totalInputChars.toLocaleString()}</span>
|
||||
<span style={{ color: "var(--text-3)" }}>Output chars</span><span className="num">{totalOutputChars.toLocaleString()}</span>
|
||||
<span style={{ color: "var(--text-3)" }}>Total</span><span className="num">{(totalInputChars + totalOutputChars).toLocaleString()}</span>
|
||||
@@ -66,8 +66,8 @@ export function BillingPanel({
|
||||
</div>
|
||||
<div className="num mono">{money(row.agent.budgetCents)}</div>
|
||||
<div className="num mono">{row.requestCount}</div>
|
||||
<div className="num mono" style={{ fontSize: 11 }}>{row.inputChars.toLocaleString()}</div>
|
||||
<div className="num mono" style={{ fontSize: 11 }}>{row.outputChars.toLocaleString()}</div>
|
||||
<div className="num mono" style={{ fontSize: 12 }}>{row.inputChars.toLocaleString()}</div>
|
||||
<div className="num mono" style={{ fontSize: 12 }}>{row.outputChars.toLocaleString()}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -85,7 +85,7 @@ export function BillingPanel({
|
||||
<div key={provider.id} className="table-row" style={{ gridTemplateColumns: "minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr)", borderBottom: index === providers.length - 1 ? "none" : "1px solid var(--border-soft)" }}>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 540 }}>{provider.label}</div>
|
||||
{provider.model && <div style={{ fontSize: 11, color: "var(--text-3)" }}>{provider.model}</div>}
|
||||
{provider.model && <div style={{ fontSize: 12, color: "var(--text-3)" }}>{provider.model}</div>}
|
||||
</div>
|
||||
<div className="num mono">{requests.length}</div>
|
||||
<div>{errors > 0 ? <Pill tone="amber">{errors}</Pill> : <span className="num mono" style={{ color: "var(--text-3)" }}>0</span>}</div>
|
||||
@@ -102,7 +102,7 @@ function StatTile({ label, value, tone }: { label: string; value: string; tone?:
|
||||
return (
|
||||
<div className="card" style={{ padding: 14 }}>
|
||||
<div className="card-label" style={{ marginBottom: 6 }}>{label}</div>
|
||||
<div className="num" style={{ fontSize: 22, fontWeight: 600, color: tone === "red" ? "var(--red)" : "var(--text-1)" }}>{value}</div>
|
||||
<div className="num" style={{ fontSize: 18, fontWeight: 600, color: tone === "red" ? "var(--red)" : "var(--text-1)" }}>{value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export function MigrationPanel({
|
||||
<p className="page-sub" style={{ marginBottom: 0 }}>{reports.length} migration {reports.length === 1 ? "report" : "reports"}.</p>
|
||||
</div>
|
||||
<div style={{ marginLeft: "auto" }}>
|
||||
<Button tone="primary" onClick={() => setOpen(true)}><Icon name="plus" size={13} /> Report</Button>
|
||||
<Button tone="primary" onClick={() => setOpen(true)}><Icon name="plus" size={16} /> Report</Button>
|
||||
</div>
|
||||
</div>
|
||||
{reports.length > 0 && (
|
||||
@@ -67,7 +67,7 @@ export function MigrationPanel({
|
||||
<section className="card" style={{ marginTop: 16, padding: 0, overflow: "hidden" }}>
|
||||
<div className="row" style={{ padding: "14px 16px", borderBottom: "1px solid var(--border-soft)" }}>
|
||||
<Icon name="refresh" size={14} color="var(--text-3)" />
|
||||
<div style={{ fontSize: 13, fontWeight: 600 }}>Rollback snapshots</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 600 }}>Rollback snapshots</div>
|
||||
</div>
|
||||
{rollbackSnapshots.length > 0 ? rollbackSnapshots.map((snapshot, index) => (
|
||||
<RollbackRow
|
||||
@@ -83,7 +83,7 @@ export function MigrationPanel({
|
||||
<section className="card" style={{ marginTop: 16, padding: 0, overflow: "hidden" }}>
|
||||
<div className="row" style={{ padding: "14px 16px", borderBottom: "1px solid var(--border-soft)" }}>
|
||||
<Icon name="list" size={14} color="var(--text-3)" />
|
||||
<div style={{ fontSize: 13, fontWeight: 600 }}>Reconciliation</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 600 }}>Reconciliation</div>
|
||||
</div>
|
||||
{reconciliationItems.length > 0 ? reconciliationItems.map((item, index) => (
|
||||
<ReconciliationRow
|
||||
@@ -125,7 +125,7 @@ function ReportRow({
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={report.sourceRef}>{report.sourceRef}</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{formatRelative(report.createdAt)}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{formatRelative(report.createdAt)}</div>
|
||||
</div>
|
||||
<div className="row" style={{ flexWrap: "wrap", gap: 4 }}>
|
||||
{Object.entries(body.tableCounts).map(([table, count]) => (
|
||||
@@ -169,7 +169,7 @@ function RollbackRow({
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={snapshot.sourceDatabasePath}>{snapshot.sourceDatabasePath}</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{formatRelative(snapshot.createdAt)}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{formatRelative(snapshot.createdAt)}</div>
|
||||
</div>
|
||||
<Pill>{snapshot.sourceDatabaseSha256.slice(0, 12)}</Pill>
|
||||
<Pill>{manifest.workspaces.length} workspaces</Pill>
|
||||
@@ -206,7 +206,7 @@ function ReconciliationRow({
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{item.targetType} · {item.targetId}</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{formatRelative(item.createdAt)}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{formatRelative(item.createdAt)}</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{item.reason}</div>
|
||||
<Pill tone={resolved ? "green" : "amber"}>{item.state}</Pill>
|
||||
|
||||
@@ -22,7 +22,7 @@ export function ProviderConnectFields({
|
||||
if (entry.authType === "api_key") {
|
||||
return (
|
||||
<>
|
||||
<p style={{ fontSize: 12.5, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<p style={{ fontSize: 12, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<Field label="API key"><input className="input" name="secret" type="password" required autoFocus /></Field>
|
||||
<ModelPicker entry={entry} provider={provider} />
|
||||
</>
|
||||
@@ -50,7 +50,7 @@ export function ProviderOAuthFields({
|
||||
const [model, setModel] = useState(provider.model ?? entry.defaultModel ?? entry.models[0]?.id ?? "");
|
||||
return (
|
||||
<>
|
||||
<p style={{ fontSize: 12.5, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<p style={{ fontSize: 12, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<Field label="Default model">
|
||||
{entry.models.length ? (
|
||||
<select className="input" value={model} onChange={(event) => setModel(event.target.value)}>
|
||||
@@ -77,7 +77,7 @@ function CompatFieldsForm({ entry, provider }: { entry: ProviderCatalogEntry; pr
|
||||
const [baseUrl, setBaseUrl] = useState("");
|
||||
return (
|
||||
<>
|
||||
<p style={{ fontSize: 12.5, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<p style={{ fontSize: 12, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<Field label="Base URL">
|
||||
<input
|
||||
className="input"
|
||||
@@ -139,7 +139,7 @@ function CloudFieldsForm({ entry, provider }: { entry: ProviderCatalogEntry; pro
|
||||
const update = (patch: Partial<CloudFields>) => setState((current) => ({ ...current, ...patch }));
|
||||
return (
|
||||
<>
|
||||
<p style={{ fontSize: 12.5, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<p style={{ fontSize: 12, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<Field label="Base URL">
|
||||
<input className="input" name="cloudBaseUrl" value={state.baseUrl} onChange={(event) => update({ baseUrl: event.target.value })} />
|
||||
</Field>
|
||||
|
||||
@@ -105,7 +105,7 @@ export function ProviderRequestPanel({
|
||||
</div>
|
||||
<Button type="submit" tone="primary">
|
||||
<span className="row" style={{ gap: 6 }}>
|
||||
<Icon name="send" size={13} />
|
||||
<Icon name="send" size={16} />
|
||||
Run
|
||||
</span>
|
||||
</Button>
|
||||
@@ -169,7 +169,7 @@ function ProviderRequestRow({
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{provider.label}</div>
|
||||
<div className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>{request.model}</div>
|
||||
<div className="mono" style={{ fontSize: 12, color: "var(--text-3)" }}>{request.model}</div>
|
||||
</div>
|
||||
<div className="mono" style={{ color: "var(--text-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{workspace?.name ?? "Organization"}</div>
|
||||
<div className="mono" style={{ color: "var(--text-2)" }}>{request.executionMode}</div>
|
||||
|
||||
@@ -302,8 +302,8 @@ function ProviderRow({
|
||||
</div>
|
||||
<ProviderHealth provider={provider} />
|
||||
<div style={{ textAlign: "right", minWidth: 120 }}>
|
||||
<div className="num mono" style={{ fontSize: 13, fontWeight: 540 }}>{boundCount} {boundCount === 1 ? "agent" : "agents"}</div>
|
||||
<div className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>bound</div>
|
||||
<div className="num mono" style={{ fontSize: 14, fontWeight: 540 }}>{boundCount} {boundCount === 1 ? "agent" : "agents"}</div>
|
||||
<div className="mono" style={{ fontSize: 12, color: "var(--text-3)" }}>bound</div>
|
||||
</div>
|
||||
<Button onClick={onCheck}>Check</Button>
|
||||
<Button onClick={onManage}>Manage</Button>
|
||||
|
||||
@@ -53,7 +53,7 @@ export function SkillsPanel({ orgId, skills, revisions, bindings, tests, agents,
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ marginLeft: "auto" }}>
|
||||
<Button tone="primary" onClick={() => setImportOpen(true)}><Icon name="plus" size={13} /> Import</Button>
|
||||
<Button tone="primary" onClick={() => setImportOpen(true)}><Icon name="plus" size={16} /> Import</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -77,7 +77,7 @@ export function SkillsPanel({ orgId, skills, revisions, bindings, tests, agents,
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{skill.name}</div>
|
||||
<div style={{ color: "var(--text-3)", fontSize: 11 }}>{skill.slug}</div>
|
||||
<div style={{ color: "var(--text-3)", fontSize: 12 }}>{skill.slug}</div>
|
||||
</div>
|
||||
<Pill>{latest ? latest.versionSemver : "—"}</Pill>
|
||||
<Pill>{testSummary(revisionTests)}</Pill>
|
||||
|
||||
@@ -71,7 +71,7 @@ export function WorkspacePanel({
|
||||
<div className="card" style={{ padding: 18 }}>
|
||||
<div className="row" style={{ marginBottom: 12 }}>
|
||||
<div className="section-label">Workspaces</div>
|
||||
<span style={{ marginLeft: "auto" }}><Button tone="ghost" onClick={() => setCreateOpen(true)}><Icon name="plus" size={13} /> Add</Button></span>
|
||||
<span style={{ marginLeft: "auto" }}><Button tone="ghost" onClick={() => setCreateOpen(true)}><Icon name="plus" size={16} /> Add</Button></span>
|
||||
</div>
|
||||
{workspaces.map((workspace) => (
|
||||
<div key={workspace.id} className="row" style={{ padding: "8px 0", borderBottom: "1px solid var(--border-soft)" }}>
|
||||
@@ -84,7 +84,7 @@ export function WorkspacePanel({
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{workspaces.length === 0 && <div style={{ fontSize: 13, color: "var(--text-3)" }}>No workspaces configured</div>}
|
||||
{workspaces.length === 0 && <div style={{ fontSize: 14, color: "var(--text-3)" }}>No workspaces configured</div>}
|
||||
</div>
|
||||
|
||||
<Dialog title="Add workspace" open={createOpen} submitLabel="Create" onClose={() => setCreateOpen(false)} onSubmit={submit}>
|
||||
@@ -96,7 +96,7 @@ export function WorkspacePanel({
|
||||
const form = document.querySelector<HTMLFormElement>("form:has([name='localPath'])");
|
||||
if (form) void chooseDirectory(form);
|
||||
}}>
|
||||
<Icon name="folder" size={13} /> Browse
|
||||
<Icon name="folder" size={16} /> Browse
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
@@ -82,7 +82,7 @@ export function SocialActivity({
|
||||
</span>
|
||||
<span style={{ color: "var(--text-3)", fontSize: 12 }}>{accountHandle(grant.socialAccountId)}</span>
|
||||
<Pill tone="violet">{humanize(grant.role)}</Pill>
|
||||
<span style={{ marginLeft: "auto", fontSize: 11, color: "var(--text-3)" }}>{scopeSummary(grant.scopeJson)}</span>
|
||||
<span style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-3)" }}>{scopeSummary(grant.scopeJson)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -95,10 +95,10 @@ export function SocialActivity({
|
||||
className="row"
|
||||
style={{ gap: 10, padding: "10px 16px", borderBottom: index === entries.length - 1 ? "none" : "1px solid var(--border-soft)" }}
|
||||
>
|
||||
<span style={{ fontSize: 12.5, fontWeight: 500 }}>{entry.label}</span>
|
||||
<span style={{ fontSize: 12, fontWeight: 500 }}>{entry.label}</span>
|
||||
<span style={{ fontSize: 12, color: "var(--text-3)" }}>{accountHandle(entry.accountId)}</span>
|
||||
<Pill tone={policyTone(entry.policyDecision)}>{humanize(entry.policyDecision)}</Pill>
|
||||
<span style={{ marginLeft: "auto", fontSize: 11, color: "var(--text-3)" }}>
|
||||
<span style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-3)" }}>
|
||||
{entry.actorType === "agent" ? "agent" : employeeName(entry.actorId)} · {formatRelative(entry.createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ export function SocialCalendar({
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 3,
|
||||
fontSize: 11,
|
||||
fontSize: 12,
|
||||
color: cell.inMonth ? "var(--text-2)" : "var(--text-4)",
|
||||
fontWeight: isToday ? 600 : 480,
|
||||
background: isToday ? "var(--text-1)" : "transparent",
|
||||
@@ -82,7 +82,7 @@ export function SocialCalendar({
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="row" style={{ marginTop: 16, fontSize: 11, color: "var(--text-3)", gap: 10 }}>
|
||||
<div className="row" style={{ marginTop: 16, fontSize: 12, color: "var(--text-3)", gap: 10 }}>
|
||||
<Legend label="X" color="var(--accent)" />
|
||||
<Legend label="LinkedIn" color="var(--green)" />
|
||||
<Legend label="YouTube" color="var(--amber)" />
|
||||
|
||||
@@ -54,12 +54,12 @@ export function HiringPipeline({ agents, approvals }: { agents: AgentRecord[]; a
|
||||
<div className="row" style={{ gap: 8 }}>
|
||||
<Avatar name={entry.agent.name} size="sm" bot />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 12.5, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{entry.agent.name}</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{entry.agent.role} · {entry.agent.department}</div>
|
||||
<div style={{ fontSize: 12, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{entry.agent.name}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)" }}>{entry.agent.role} · {entry.agent.department}</div>
|
||||
</div>
|
||||
</div>
|
||||
{entry.approval && (
|
||||
<div style={{ marginTop: 6, fontSize: 11, color: "var(--text-3)" }}>
|
||||
<div style={{ marginTop: 6, fontSize: 12, color: "var(--text-3)" }}>
|
||||
{formatRelative(entry.approval.createdAt)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -121,9 +121,9 @@ export function TaskDetailDialog({
|
||||
)}
|
||||
|
||||
<div className="modal-actions">
|
||||
<Button tone="ghost" onClick={deleteTask}><Icon name="x" size={13} /> Delete</Button>
|
||||
{assignee && <Button onClick={() => onRun(task)} disabled={!canRun(provider)}><Icon name="send" size={13} /> Run</Button>}
|
||||
{advanceTo && <Button tone="primary" onClick={() => onAdvance(task)}><Icon name="check" size={13} /> {prettyStatus(advanceTo)}</Button>}
|
||||
<Button tone="ghost" onClick={deleteTask}><Icon name="x" size={16} /> Delete</Button>
|
||||
{assignee && <Button onClick={() => onRun(task)} disabled={!canRun(provider)}><Icon name="send" size={16} /> Run</Button>}
|
||||
{advanceTo && <Button tone="primary" onClick={() => onAdvance(task)}><Icon name="check" size={16} /> {prettyStatus(advanceTo)}</Button>}
|
||||
</div>
|
||||
|
||||
{editing && <EditOverlay task={task} agents={agents} allTasks={allTasks} onSubmit={submitEdit} onCancel={() => setEditing(false)} />}
|
||||
@@ -140,9 +140,9 @@ function OverviewTab({ task, assignee, project, goal, workspace, provider, runs
|
||||
return (
|
||||
<>
|
||||
{task.description && (
|
||||
<p style={{ margin: "0 0 14px", color: "var(--text-2)", fontSize: 13, lineHeight: 1.55, whiteSpace: "pre-wrap" }}>{task.description}</p>
|
||||
<p style={{ margin: "0 0 14px", color: "var(--text-2)", fontSize: 14, lineHeight: 1.55, whiteSpace: "pre-wrap" }}>{task.description}</p>
|
||||
)}
|
||||
<div style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: "8px 14px", fontSize: 12.5 }}>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: "8px 14px", fontSize: 12 }}>
|
||||
<MetaLabel>Assignee</MetaLabel>
|
||||
<div className="row" style={{ gap: 8 }}>
|
||||
{assignee ? (<><Avatar name={assignee.name} size="sm" bot /><span>{assignee.name}</span>{provider && <span style={{ color: "var(--text-3)" }}>({provider.label})</span>}</>) : <span style={{ color: "var(--text-4)" }}>Unassigned</span>}
|
||||
@@ -177,13 +177,13 @@ function OverviewTab({ task, assignee, project, goal, workspace, provider, runs
|
||||
function SubtasksTab({ subtasks, agents }: { subtasks: TaskRecord[]; agents: AgentRecord[] }) {
|
||||
return (
|
||||
<div style={{ minHeight: 60 }}>
|
||||
{subtasks.length === 0 && <div style={{ fontSize: 13, color: "var(--text-3)" }}>No subtasks</div>}
|
||||
{subtasks.length === 0 && <div style={{ fontSize: 14, color: "var(--text-3)" }}>No subtasks</div>}
|
||||
{subtasks.map((st) => {
|
||||
const owner = agents.find((a) => a.id === st.assigneeAgentId);
|
||||
return (
|
||||
<div key={st.id} className="row" style={{ gap: 10, padding: "8px 0", borderBottom: "1px solid var(--border-soft)" }}>
|
||||
<Icon name={st.status === TASK_STATUS.DONE ? "check" : "work"} size={13} color={st.status === TASK_STATUS.DONE ? "var(--green)" : "var(--text-3)"} />
|
||||
<span style={{ flex: 1, fontSize: 13, fontWeight: 480, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{st.title}</span>
|
||||
<Icon name={st.status === TASK_STATUS.DONE ? "check" : "work"} size={16} color={st.status === TASK_STATUS.DONE ? "var(--green)" : "var(--text-3)"} />
|
||||
<span style={{ flex: 1, fontSize: 14, fontWeight: 480, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{st.title}</span>
|
||||
<Pill>{prettyStatus(st.status)}</Pill>
|
||||
{owner && <Avatar name={owner.name} size="sm" bot />}
|
||||
</div>
|
||||
@@ -202,18 +202,18 @@ function ActivityTab({ audit, approvals }: { audit: AuditEventRecord[]; approval
|
||||
{approvals.map((a) => (
|
||||
<div key={a.id} className="row" style={{ gap: 8, padding: "6px 0" }}>
|
||||
<Pill tone={a.state === "approved" ? "green" : a.state === "pending" ? "amber" : "red"}>{a.state}</Pill>
|
||||
<span style={{ fontSize: 12.5 }}>{a.title}</span>
|
||||
<span className="mono" style={{ marginLeft: "auto", fontSize: 11, color: "var(--text-3)" }}>{formatRelative(a.createdAt)}</span>
|
||||
<span style={{ fontSize: 12 }}>{a.title}</span>
|
||||
<span className="mono" style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-3)" }}>{formatRelative(a.createdAt)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="section-label">Events</div>
|
||||
{audit.length === 0 && <div style={{ fontSize: 13, color: "var(--text-3)" }}>No events</div>}
|
||||
{audit.length === 0 && <div style={{ fontSize: 14, color: "var(--text-3)" }}>No events</div>}
|
||||
{audit.map((e) => (
|
||||
<div key={e.id} className="row" style={{ gap: 8, padding: "5px 0" }}>
|
||||
<span className="mono" style={{ fontSize: 11, color: "var(--text-3)", flexShrink: 0 }}>{formatRelative(e.createdAt)}</span>
|
||||
<span style={{ fontSize: 12.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{auditEventLabel(e.entityType, e.action)}</span>
|
||||
<span className="mono" style={{ fontSize: 12, color: "var(--text-3)", flexShrink: 0 }}>{formatRelative(e.createdAt)}</span>
|
||||
<span style={{ fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{auditEventLabel(e.entityType, e.action)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -116,10 +116,10 @@ export function SearchDialog({
|
||||
>
|
||||
<Pill>{LABELS[result.entityType]}</Pill>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{result.title}</div>
|
||||
<div style={{ fontSize: 14, fontWeight: 540, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{result.title}</div>
|
||||
<div style={{ fontSize: 12, color: "var(--text-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{result.preview}</div>
|
||||
</div>
|
||||
<span style={{ fontSize: 11, color: "var(--text-3)" }}>{result.facets.type}</span>
|
||||
<span style={{ fontSize: 12, color: "var(--text-3)" }}>{result.facets.type}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user