@corbits/react-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +501 -0
- package/README.md +125 -0
- package/dist/blocks/login/auth-layout.d.ts +38 -0
- package/dist/blocks/login/auth-layout.js +52 -0
- package/dist/blocks/login/login-form.d.ts +60 -0
- package/dist/blocks/login/login-form.js +151 -0
- package/dist/hooks/use-command-palette-navigation.d.ts +25 -0
- package/dist/hooks/use-command-palette-navigation.js +52 -0
- package/dist/hooks/use-controllable-state.d.ts +28 -0
- package/dist/hooks/use-controllable-state.js +44 -0
- package/dist/hooks/use-delayed-autofocus.d.ts +9 -0
- package/dist/hooks/use-delayed-autofocus.js +20 -0
- package/dist/hooks/use-dismissable-popover.d.ts +37 -0
- package/dist/hooks/use-dismissable-popover.js +64 -0
- package/dist/hooks/use-list-selection.d.ts +66 -0
- package/dist/hooks/use-list-selection.js +147 -0
- package/dist/hooks/use-prefers-reduced-motion.d.ts +13 -0
- package/dist/hooks/use-prefers-reduced-motion.js +28 -0
- package/dist/hooks/use-scroll-current-into-view.d.ts +19 -0
- package/dist/hooks/use-scroll-current-into-view.js +33 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +131 -0
- package/dist/lib/artifact.d.ts +72 -0
- package/dist/lib/artifact.js +67 -0
- package/dist/lib/chart-geometry.d.ts +62 -0
- package/dist/lib/chart-geometry.js +86 -0
- package/dist/lib/chart-palette.d.ts +76 -0
- package/dist/lib/chart-palette.js +92 -0
- package/dist/lib/chat-message.d.ts +115 -0
- package/dist/lib/chat-message.js +41 -0
- package/dist/lib/chat-parts.d.ts +64 -0
- package/dist/lib/chat-parts.js +16 -0
- package/dist/lib/csv.d.ts +60 -0
- package/dist/lib/csv.js +108 -0
- package/dist/lib/metrics.d.ts +70 -0
- package/dist/lib/metrics.js +101 -0
- package/dist/lib/now-item.d.ts +65 -0
- package/dist/lib/now-item.js +59 -0
- package/dist/lib/relative-time.d.ts +9 -0
- package/dist/lib/relative-time.js +47 -0
- package/dist/lib/theme.d.ts +30 -0
- package/dist/lib/theme.js +77 -0
- package/dist/lib/url.d.ts +26 -0
- package/dist/lib/url.js +37 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lib/workflow-display-flow.d.ts +83 -0
- package/dist/lib/workflow-display-flow.js +104 -0
- package/dist/lib/workflow-registry.d.ts +90 -0
- package/dist/lib/workflow-registry.js +56 -0
- package/dist/lib/workflow-run-error.d.ts +31 -0
- package/dist/lib/workflow-run-error.js +105 -0
- package/dist/lib/workflow-run-progress.d.ts +97 -0
- package/dist/lib/workflow-run-progress.js +135 -0
- package/dist/lib/workflow-run.d.ts +43 -0
- package/dist/lib/workflow-run.js +56 -0
- package/dist/styles.css +3080 -0
- package/dist/test/render-hook.d.ts +14 -0
- package/dist/test/render-hook.js +38 -0
- package/dist/theme.css +628 -0
- package/dist/ui/agent-turn.d.ts +26 -0
- package/dist/ui/agent-turn.js +60 -0
- package/dist/ui/animated-number.d.ts +25 -0
- package/dist/ui/animated-number.js +63 -0
- package/dist/ui/approval-card.d.ts +46 -0
- package/dist/ui/approval-card.js +116 -0
- package/dist/ui/artifact-body.d.ts +26 -0
- package/dist/ui/artifact-body.js +170 -0
- package/dist/ui/artifact-notice.d.ts +21 -0
- package/dist/ui/artifact-notice.js +36 -0
- package/dist/ui/avatar.d.ts +37 -0
- package/dist/ui/avatar.js +73 -0
- package/dist/ui/badge.d.ts +10 -0
- package/dist/ui/badge.js +35 -0
- package/dist/ui/bar-chart.d.ts +38 -0
- package/dist/ui/bar-chart.js +118 -0
- package/dist/ui/block-card.d.ts +29 -0
- package/dist/ui/block-card.js +66 -0
- package/dist/ui/boot-screen.d.ts +23 -0
- package/dist/ui/boot-screen.js +49 -0
- package/dist/ui/bulk-action-bar.d.ts +37 -0
- package/dist/ui/bulk-action-bar.js +64 -0
- package/dist/ui/button.d.ts +12 -0
- package/dist/ui/button.js +73 -0
- package/dist/ui/card.d.ts +6 -0
- package/dist/ui/card.js +37 -0
- package/dist/ui/category-bars.d.ts +20 -0
- package/dist/ui/category-bars.js +69 -0
- package/dist/ui/chart-frame.d.ts +54 -0
- package/dist/ui/chart-frame.js +108 -0
- package/dist/ui/chat-composer.d.ts +24 -0
- package/dist/ui/chat-composer.js +32 -0
- package/dist/ui/chat-dock-timing.d.ts +15 -0
- package/dist/ui/chat-dock-timing.js +11 -0
- package/dist/ui/chat-input.d.ts +38 -0
- package/dist/ui/chat-input.js +138 -0
- package/dist/ui/chat-panel.d.ts +20 -0
- package/dist/ui/chat-panel.js +56 -0
- package/dist/ui/chat-thread.d.ts +28 -0
- package/dist/ui/chat-thread.js +61 -0
- package/dist/ui/checkbox.d.ts +25 -0
- package/dist/ui/checkbox.js +63 -0
- package/dist/ui/command-palette.d.ts +100 -0
- package/dist/ui/command-palette.js +341 -0
- package/dist/ui/compare-body.d.ts +52 -0
- package/dist/ui/compare-body.js +140 -0
- package/dist/ui/confirm-button.d.ts +29 -0
- package/dist/ui/confirm-button.js +41 -0
- package/dist/ui/corbits-mark.d.ts +29 -0
- package/dist/ui/corbits-mark.js +33 -0
- package/dist/ui/csv-table.d.ts +22 -0
- package/dist/ui/csv-table.js +85 -0
- package/dist/ui/dialog.d.ts +37 -0
- package/dist/ui/dialog.js +105 -0
- package/dist/ui/dictation-status-line.d.ts +20 -0
- package/dist/ui/dictation-status-line.js +38 -0
- package/dist/ui/dither-canvas.d.ts +31 -0
- package/dist/ui/dither-canvas.js +169 -0
- package/dist/ui/embed-body.d.ts +30 -0
- package/dist/ui/embed-body.js +92 -0
- package/dist/ui/empty-state.d.ts +26 -0
- package/dist/ui/empty-state.js +42 -0
- package/dist/ui/file-input.d.ts +25 -0
- package/dist/ui/file-input.js +66 -0
- package/dist/ui/filter-bar.d.ts +42 -0
- package/dist/ui/filter-bar.js +95 -0
- package/dist/ui/filter-chip.d.ts +14 -0
- package/dist/ui/filter-chip.js +23 -0
- package/dist/ui/gate-block.d.ts +16 -0
- package/dist/ui/gate-block.js +37 -0
- package/dist/ui/horizontal-stepper.d.ts +13 -0
- package/dist/ui/horizontal-stepper.js +65 -0
- package/dist/ui/input.d.ts +2 -0
- package/dist/ui/input.js +14 -0
- package/dist/ui/inspector-shell.d.ts +47 -0
- package/dist/ui/inspector-shell.js +98 -0
- package/dist/ui/intake-form.d.ts +39 -0
- package/dist/ui/intake-form.js +119 -0
- package/dist/ui/kind-card-grid.d.ts +23 -0
- package/dist/ui/kind-card-grid.js +43 -0
- package/dist/ui/library-page-header.d.ts +20 -0
- package/dist/ui/library-page-header.js +43 -0
- package/dist/ui/list-detail.d.ts +28 -0
- package/dist/ui/list-detail.js +55 -0
- package/dist/ui/live-run-banner.d.ts +13 -0
- package/dist/ui/live-run-banner.js +23 -0
- package/dist/ui/live-status-line.d.ts +22 -0
- package/dist/ui/live-status-line.js +37 -0
- package/dist/ui/menu.d.ts +18 -0
- package/dist/ui/menu.js +45 -0
- package/dist/ui/message-bubble.d.ts +24 -0
- package/dist/ui/message-bubble.js +64 -0
- package/dist/ui/mic-button.d.ts +35 -0
- package/dist/ui/mic-button.js +61 -0
- package/dist/ui/mic-permission-dialog.d.ts +20 -0
- package/dist/ui/mic-permission-dialog.js +50 -0
- package/dist/ui/notifications-bell.d.ts +28 -0
- package/dist/ui/notifications-bell.js +69 -0
- package/dist/ui/now-cards.d.ts +37 -0
- package/dist/ui/now-cards.js +116 -0
- package/dist/ui/page-panel.d.ts +37 -0
- package/dist/ui/page-panel.js +23 -0
- package/dist/ui/page-shell.d.ts +21 -0
- package/dist/ui/page-shell.js +26 -0
- package/dist/ui/parts-renderer.d.ts +22 -0
- package/dist/ui/parts-renderer.js +234 -0
- package/dist/ui/profile-card.d.ts +39 -0
- package/dist/ui/profile-card.js +124 -0
- package/dist/ui/progress-checklist.d.ts +25 -0
- package/dist/ui/progress-checklist.js +70 -0
- package/dist/ui/provider-mark.d.ts +32 -0
- package/dist/ui/provider-mark.js +31 -0
- package/dist/ui/quick-reply-chips.d.ts +14 -0
- package/dist/ui/quick-reply-chips.js +24 -0
- package/dist/ui/quote-card.d.ts +39 -0
- package/dist/ui/quote-card.js +66 -0
- package/dist/ui/reasoning-block.d.ts +35 -0
- package/dist/ui/reasoning-block.js +60 -0
- package/dist/ui/research-body.d.ts +70 -0
- package/dist/ui/research-body.js +270 -0
- package/dist/ui/rich-empty-state.d.ts +35 -0
- package/dist/ui/rich-empty-state.js +65 -0
- package/dist/ui/run-now-button.d.ts +23 -0
- package/dist/ui/run-now-button.js +42 -0
- package/dist/ui/section.d.ts +20 -0
- package/dist/ui/section.js +48 -0
- package/dist/ui/select.d.ts +2 -0
- package/dist/ui/select.js +22 -0
- package/dist/ui/selection-checkbox.d.ts +42 -0
- package/dist/ui/selection-checkbox.js +54 -0
- package/dist/ui/settings-panel.d.ts +35 -0
- package/dist/ui/settings-panel.js +68 -0
- package/dist/ui/shimmer-text.d.ts +19 -0
- package/dist/ui/shimmer-text.js +20 -0
- package/dist/ui/sidebar-item-row.d.ts +40 -0
- package/dist/ui/sidebar-item-row.js +64 -0
- package/dist/ui/sidebar-panel.d.ts +25 -0
- package/dist/ui/sidebar-panel.js +56 -0
- package/dist/ui/sidebar.d.ts +46 -0
- package/dist/ui/sidebar.js +111 -0
- package/dist/ui/skeleton.d.ts +13 -0
- package/dist/ui/skeleton.js +20 -0
- package/dist/ui/sortable-table.d.ts +43 -0
- package/dist/ui/sortable-table.js +103 -0
- package/dist/ui/sparkline.d.ts +27 -0
- package/dist/ui/sparkline.js +68 -0
- package/dist/ui/stat-grid.d.ts +55 -0
- package/dist/ui/stat-grid.js +87 -0
- package/dist/ui/status-dot.d.ts +13 -0
- package/dist/ui/status-dot.js +51 -0
- package/dist/ui/step-list.d.ts +21 -0
- package/dist/ui/step-list.js +77 -0
- package/dist/ui/switch.d.ts +23 -0
- package/dist/ui/switch.js +29 -0
- package/dist/ui/table.d.ts +8 -0
- package/dist/ui/table.js +48 -0
- package/dist/ui/tabs.d.ts +44 -0
- package/dist/ui/tabs.js +101 -0
- package/dist/ui/textarea.d.ts +8 -0
- package/dist/ui/textarea.js +32 -0
- package/dist/ui/theme-provider.d.ts +36 -0
- package/dist/ui/theme-provider.js +126 -0
- package/dist/ui/theme-toggle.d.ts +11 -0
- package/dist/ui/theme-toggle.js +32 -0
- package/dist/ui/thinking-indicator.d.ts +7 -0
- package/dist/ui/thinking-indicator.js +19 -0
- package/dist/ui/thinking-label.d.ts +19 -0
- package/dist/ui/thinking-label.js +54 -0
- package/dist/ui/thinking-mark.d.ts +10 -0
- package/dist/ui/thinking-mark.js +153 -0
- package/dist/ui/time-series-chart.d.ts +55 -0
- package/dist/ui/time-series-chart.js +249 -0
- package/dist/ui/toast.d.ts +16 -0
- package/dist/ui/toast.js +41 -0
- package/dist/ui/token-mosaic.d.ts +22 -0
- package/dist/ui/token-mosaic.js +65 -0
- package/dist/ui/tool-block.d.ts +54 -0
- package/dist/ui/tool-block.js +170 -0
- package/dist/ui/tool-narrative.d.ts +22 -0
- package/dist/ui/tool-narrative.js +77 -0
- package/dist/ui/tool-picker.d.ts +44 -0
- package/dist/ui/tool-picker.js +200 -0
- package/dist/ui/tooltip.d.ts +21 -0
- package/dist/ui/tooltip.js +48 -0
- package/dist/ui/top-bar.d.ts +53 -0
- package/dist/ui/top-bar.js +98 -0
- package/dist/ui/trace-waterfall.d.ts +38 -0
- package/dist/ui/trace-waterfall.js +121 -0
- package/dist/ui/typing-indicator.d.ts +9 -0
- package/dist/ui/typing-indicator.js +25 -0
- package/dist/ui/view-toggle.d.ts +17 -0
- package/dist/ui/view-toggle.js +46 -0
- package/dist/ui/voice-waveform.d.ts +16 -0
- package/dist/ui/voice-waveform.js +23 -0
- package/package.json +583 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { formatRelativeTime } from "../lib/relative-time.js";
|
|
4
|
+
import { sortNowItems } from "../lib/now-item.js";
|
|
5
|
+
import { Badge } from "./badge.js";
|
|
6
|
+
/**
|
|
7
|
+
* The attention band at the top of the inbox. Its own container rather than the
|
|
8
|
+
* generic `Section` because it says something `Section` cannot: an eyebrow that
|
|
9
|
+
* marks urgency, a count in words, and a collapsed mode.
|
|
10
|
+
*
|
|
11
|
+
* Collapsing keeps the body mounted (so it can animate, and so anchors into it
|
|
12
|
+
* still resolve) but marks it `inert` — `aria-hidden` alone would hide it from
|
|
13
|
+
* a screen reader while leaving every card in the tab order.
|
|
14
|
+
*/ export function NowSection({ count, collapsed = false, children, className }) {
|
|
15
|
+
const countLabel = count === 1 ? "1 thing needs you" : `${count} things need you`;
|
|
16
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
17
|
+
"data-slot": "now-section",
|
|
18
|
+
"aria-label": "Now",
|
|
19
|
+
className: cn("border-b border-border bg-card px-4 py-3 text-card-foreground", className),
|
|
20
|
+
children: [
|
|
21
|
+
/*#__PURE__*/ _jsx("div", {
|
|
22
|
+
className: "flex items-end justify-between gap-2",
|
|
23
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
24
|
+
className: "min-w-0",
|
|
25
|
+
children: [
|
|
26
|
+
/*#__PURE__*/ _jsx("p", {
|
|
27
|
+
className: "text-xs font-semibold tracking-[0.14em] text-primary-emphasis uppercase",
|
|
28
|
+
children: "Now"
|
|
29
|
+
}),
|
|
30
|
+
/*#__PURE__*/ _jsx("p", {
|
|
31
|
+
className: "text-sm font-semibold tracking-tight",
|
|
32
|
+
children: count === 0 ? "Clear" : countLabel
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
})
|
|
36
|
+
}),
|
|
37
|
+
/*#__PURE__*/ _jsx("div", {
|
|
38
|
+
className: cn("grid transition-[grid-template-rows,opacity] duration-300", collapsed ? "grid-rows-[0fr] opacity-0" : "mt-3 grid-rows-[1fr] opacity-100"),
|
|
39
|
+
...collapsed ? {
|
|
40
|
+
inert: true
|
|
41
|
+
} : {},
|
|
42
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
43
|
+
className: "min-h-0 overflow-hidden",
|
|
44
|
+
children: children
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The highest-attention items as cards. Ordering is not the caller's problem —
|
|
52
|
+
* the list is sorted here by priority then recency, so two surfaces fed the
|
|
53
|
+
* same items always agree on what is most urgent.
|
|
54
|
+
*/ export function NowCards({ items, limit = 3, selectedId = null, now, className }) {
|
|
55
|
+
const shown = sortNowItems(items).slice(0, limit);
|
|
56
|
+
if (shown.length === 0) return null;
|
|
57
|
+
return /*#__PURE__*/ _jsx("ul", {
|
|
58
|
+
"aria-label": "Needs you now",
|
|
59
|
+
className: cn("grid gap-2.5 sm:grid-cols-2 lg:grid-cols-3", className),
|
|
60
|
+
children: shown.map((item, index)=>{
|
|
61
|
+
const selected = item.id === selectedId;
|
|
62
|
+
const body = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
63
|
+
children: [
|
|
64
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
65
|
+
className: "flex items-center gap-2",
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ _jsx("span", {
|
|
68
|
+
className: "grid size-5 shrink-0 place-items-center rounded-full bg-primary/15 text-xs font-bold text-primary-emphasis",
|
|
69
|
+
children: index + 1
|
|
70
|
+
}),
|
|
71
|
+
/*#__PURE__*/ _jsx(Badge, {
|
|
72
|
+
tone: item.priority === "now" ? "accent" : "neutral",
|
|
73
|
+
children: item.classification
|
|
74
|
+
}),
|
|
75
|
+
item.type === "mail" && !item.read ? /*#__PURE__*/ _jsx("span", {
|
|
76
|
+
className: "size-2 shrink-0 rounded-full bg-primary",
|
|
77
|
+
"aria-label": "Unread",
|
|
78
|
+
role: "img"
|
|
79
|
+
}) : null,
|
|
80
|
+
/*#__PURE__*/ _jsx("time", {
|
|
81
|
+
dateTime: item.when,
|
|
82
|
+
className: "ml-auto shrink-0 text-xs text-muted-foreground",
|
|
83
|
+
children: formatRelativeTime(item.when, now)
|
|
84
|
+
})
|
|
85
|
+
]
|
|
86
|
+
}),
|
|
87
|
+
/*#__PURE__*/ _jsx("p", {
|
|
88
|
+
className: "mt-2 line-clamp-2 text-sm leading-snug font-semibold",
|
|
89
|
+
children: item.title
|
|
90
|
+
}),
|
|
91
|
+
item.summary === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
92
|
+
className: "mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground",
|
|
93
|
+
children: item.summary
|
|
94
|
+
}),
|
|
95
|
+
/*#__PURE__*/ _jsx("p", {
|
|
96
|
+
className: "mt-2.5 text-xs font-medium text-primary-emphasis",
|
|
97
|
+
children: item.type === "gate" ? `${item.action} →` : `From ${item.from}`
|
|
98
|
+
})
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
const shell = cn("block h-full w-full rounded-lg border p-3.5 text-left transition-colors", selected ? "border-primary-emphasis bg-primary/10" : "border-border bg-background hover:bg-muted");
|
|
102
|
+
return /*#__PURE__*/ _jsx("li", {
|
|
103
|
+
children: item.href === undefined ? /*#__PURE__*/ _jsx("div", {
|
|
104
|
+
"aria-current": selected ? "true" : undefined,
|
|
105
|
+
className: shell,
|
|
106
|
+
children: body
|
|
107
|
+
}) : /*#__PURE__*/ _jsx("a", {
|
|
108
|
+
href: item.href,
|
|
109
|
+
"aria-current": selected ? "true" : undefined,
|
|
110
|
+
className: shell,
|
|
111
|
+
children: body
|
|
112
|
+
})
|
|
113
|
+
}, `${item.type}:${item.id}`);
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type PagePanelProps = {
|
|
3
|
+
readonly children: ReactNode;
|
|
4
|
+
/**
|
|
5
|
+
* When true the inner panel scrolls vertically; when false it clips overflow
|
|
6
|
+
* and leaves scrolling to its own children (e.g. a header + scroll body).
|
|
7
|
+
*/
|
|
8
|
+
readonly scroll?: boolean;
|
|
9
|
+
/** Drop the panel's shadow — some surfaces render flatter. */
|
|
10
|
+
readonly flat?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Size the panel to its content instead of filling the viewport. Short pages
|
|
13
|
+
* then leave no dead band below their last section. The framing border is
|
|
14
|
+
* dropped in this mode so a content-height panel has no floating edge where
|
|
15
|
+
* it stops — it blends into the identical fill below it. Still capped at the
|
|
16
|
+
* viewport, so overflowing content scrolls as normal.
|
|
17
|
+
*/
|
|
18
|
+
readonly fitContent?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Fill token for the outer bleed and the panel itself. `background` (the
|
|
21
|
+
* default) is for top-level routes hosted directly under the app shell; pass
|
|
22
|
+
* `card` when the panel is nested inside a surface that already supplies its
|
|
23
|
+
* own ground, so the panel blends in instead of reading as a stray card.
|
|
24
|
+
*/
|
|
25
|
+
readonly surface?: "background" | "card";
|
|
26
|
+
readonly className?: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The standard page-content frame: a full-height outer fill plus a bordered
|
|
30
|
+
* panel that owns the page's vertical scroll. Shared by the top-level pages so
|
|
31
|
+
* margins, border and background are decided once instead of per page.
|
|
32
|
+
*
|
|
33
|
+
* It is not `PageShell` — that caps and centres a measure of content — and not
|
|
34
|
+
* `Card`, which is a surface within a page. This is the boundary between the
|
|
35
|
+
* app shell and whatever the route renders.
|
|
36
|
+
*/
|
|
37
|
+
export declare function PagePanel({ children, scroll, flat, fitContent, surface, className, }: PagePanelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* The standard page-content frame: a full-height outer fill plus a bordered
|
|
5
|
+
* panel that owns the page's vertical scroll. Shared by the top-level pages so
|
|
6
|
+
* margins, border and background are decided once instead of per page.
|
|
7
|
+
*
|
|
8
|
+
* It is not `PageShell` — that caps and centres a measure of content — and not
|
|
9
|
+
* `Card`, which is a surface within a page. This is the boundary between the
|
|
10
|
+
* app shell and whatever the route renders.
|
|
11
|
+
*/ export function PagePanel({ children, scroll = true, flat = false, fitContent = false, surface = "background", className }) {
|
|
12
|
+
const sizing = fitContent ? "w-full max-h-full self-start" : "flex-1 border border-border";
|
|
13
|
+
const overflow = scroll ? fitContent ? "overflow-y-auto" : "min-h-full overflow-y-auto" : "overflow-hidden";
|
|
14
|
+
const fill = surface === "card" ? "bg-card" : "bg-background";
|
|
15
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
16
|
+
className: cn("flex h-full overflow-hidden", fill),
|
|
17
|
+
children: /*#__PURE__*/ _jsx("section", {
|
|
18
|
+
"data-slot": "page-panel",
|
|
19
|
+
className: cn("flex flex-col", fill, sizing, overflow, !flat && "shadow-sm", className),
|
|
20
|
+
children: children
|
|
21
|
+
})
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
export type PageShellProps = React.ComponentProps<"div"> & {
|
|
3
|
+
/**
|
|
4
|
+
* The shell owns the vertical scroll. Turn it off when the page has its own
|
|
5
|
+
* scrolling body under a sticky header — two nested scroll containers is how
|
|
6
|
+
* a header ends up scrolling away with the content under it.
|
|
7
|
+
*/
|
|
8
|
+
readonly scroll?: boolean;
|
|
9
|
+
/** Caps and centres the content. Omit for full-bleed pages like a dashboard. */
|
|
10
|
+
readonly width?: "prose" | "wide" | "full";
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The frame a top-level page sits in: full height, one scroll container, and a
|
|
14
|
+
* consistent measure.
|
|
15
|
+
*
|
|
16
|
+
* It exists so margins, max-width and scroll ownership are decided once instead
|
|
17
|
+
* of by whichever page was written most recently. It is not `Section` — that is
|
|
18
|
+
* a titled block *within* a page — and it is not `Card`, which is a surface.
|
|
19
|
+
* This is the page's outer boundary and it deliberately draws nothing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function PageShell({ scroll, width, className, children, ...props }: PageShellProps): React.JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
const WIDTH_CLASS = {
|
|
4
|
+
prose: "mx-auto w-full max-w-3xl",
|
|
5
|
+
wide: "mx-auto w-full max-w-5xl",
|
|
6
|
+
full: "w-full"
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The frame a top-level page sits in: full height, one scroll container, and a
|
|
10
|
+
* consistent measure.
|
|
11
|
+
*
|
|
12
|
+
* It exists so margins, max-width and scroll ownership are decided once instead
|
|
13
|
+
* of by whichever page was written most recently. It is not `Section` — that is
|
|
14
|
+
* a titled block *within* a page — and it is not `Card`, which is a surface.
|
|
15
|
+
* This is the page's outer boundary and it deliberately draws nothing.
|
|
16
|
+
*/ export function PageShell({ scroll = true, width = "wide", className, children, ...props }) {
|
|
17
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
18
|
+
"data-slot": "page-shell",
|
|
19
|
+
className: cn("flex h-full flex-col", scroll ? "overflow-y-auto" : "overflow-hidden", className),
|
|
20
|
+
...props,
|
|
21
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
22
|
+
className: cn("flex min-h-0 flex-1 flex-col gap-6 px-4 py-6", WIDTH_CLASS[width]),
|
|
23
|
+
children: children
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Part, PartToolTrace } from "../lib/chat-parts.js";
|
|
2
|
+
import { type ToolBlockState } from "./tool-block.js";
|
|
3
|
+
export type PartsRendererProps = {
|
|
4
|
+
readonly parts: readonly Part[];
|
|
5
|
+
readonly className?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Maps the wire's four-outcome status onto `ToolBlock`'s render-side state. */
|
|
8
|
+
export declare function toolTraceToBlockState(part: PartToolTrace): ToolBlockState;
|
|
9
|
+
/**
|
|
10
|
+
* Renders an ordered `Part[]` in the order the model produced it: text as
|
|
11
|
+
* prose, reasoning as a closed disclosure, tool calls through `ToolBlock`,
|
|
12
|
+
* files as an attachment chip, events as an inline system line, any block
|
|
13
|
+
* part as a labeled fallback card, and any `kind` outside the `Part` union
|
|
14
|
+
* itself as a neutral unknown-part fallback — the host's own generative-UI
|
|
15
|
+
* registry, if it has one, renders blocks by intercepting `kind: "block"`
|
|
16
|
+
* before it reaches this component.
|
|
17
|
+
*
|
|
18
|
+
* An empty `parts` array renders nothing — no placeholder, no empty-state
|
|
19
|
+
* chrome. A message with no parts is not a message a transcript should show
|
|
20
|
+
* anything for.
|
|
21
|
+
*/
|
|
22
|
+
export declare function PartsRenderer({ parts, className }: PartsRendererProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronRight, Paperclip } from "lucide-react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { BlockCard } from "./block-card.js";
|
|
6
|
+
import { ToolBlock } from "./tool-block.js";
|
|
7
|
+
/** `1.2s`, `45s`, `3m 05s` — never a raw millisecond count. */ function formatDuration(ms) {
|
|
8
|
+
const totalSeconds = ms / 1000;
|
|
9
|
+
if (totalSeconds < 60) {
|
|
10
|
+
return `${totalSeconds < 10 ? totalSeconds.toFixed(1) : Math.round(totalSeconds)}s`;
|
|
11
|
+
}
|
|
12
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
13
|
+
const seconds = Math.round(totalSeconds % 60);
|
|
14
|
+
return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
|
|
15
|
+
}
|
|
16
|
+
/** Maps the wire's four-outcome status onto `ToolBlock`'s render-side state. */ export function toolTraceToBlockState(part) {
|
|
17
|
+
switch(part.status){
|
|
18
|
+
case "pending":
|
|
19
|
+
return {
|
|
20
|
+
status: "pending"
|
|
21
|
+
};
|
|
22
|
+
case "running":
|
|
23
|
+
return {
|
|
24
|
+
status: "running"
|
|
25
|
+
};
|
|
26
|
+
case "output-available":
|
|
27
|
+
return {
|
|
28
|
+
status: "output-available",
|
|
29
|
+
output: stringifyOutput(part.output)
|
|
30
|
+
};
|
|
31
|
+
case "error":
|
|
32
|
+
return {
|
|
33
|
+
status: "error",
|
|
34
|
+
message: stringifyOutput(part.output)
|
|
35
|
+
};
|
|
36
|
+
case "approval-requested":
|
|
37
|
+
return {
|
|
38
|
+
status: "approval-requested",
|
|
39
|
+
reason: stringifyOutput(part.output)
|
|
40
|
+
};
|
|
41
|
+
case "output-denied":
|
|
42
|
+
return {
|
|
43
|
+
status: "output-denied",
|
|
44
|
+
reason: stringifyOutput(part.output)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function stringifyOutput(output) {
|
|
49
|
+
if (output === undefined) return "";
|
|
50
|
+
if (typeof output === "string") return output;
|
|
51
|
+
return JSON.stringify(output, null, 2);
|
|
52
|
+
}
|
|
53
|
+
function TextPartView({ text }) {
|
|
54
|
+
if (text.trim().length === 0) return null;
|
|
55
|
+
return /*#__PURE__*/ _jsx("p", {
|
|
56
|
+
className: "whitespace-pre-wrap text-sm leading-relaxed text-foreground",
|
|
57
|
+
children: text
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The agent's thinking, folded away, with how long it took once known.
|
|
62
|
+
*
|
|
63
|
+
* Closed by default, including while streaming: a block that expands itself
|
|
64
|
+
* mid-stream makes the transcript jump under the reader. A native
|
|
65
|
+
* `<details>`, not a div with state — it gets disclosure semantics, keyboard
|
|
66
|
+
* behaviour and find-in-page expansion from the browser for free.
|
|
67
|
+
*/ function ReasoningPartView({ text, durationMs }) {
|
|
68
|
+
const [open, setOpen] = useState(false);
|
|
69
|
+
if (text.trim().length === 0) return null;
|
|
70
|
+
return /*#__PURE__*/ _jsxs("details", {
|
|
71
|
+
"data-slot": "reasoning-part",
|
|
72
|
+
open: open,
|
|
73
|
+
onToggle: (event)=>setOpen(event.currentTarget.open),
|
|
74
|
+
className: "text-xs",
|
|
75
|
+
children: [
|
|
76
|
+
/*#__PURE__*/ _jsxs("summary", {
|
|
77
|
+
className: cn(// `relative` plus the `::after` pseudo-element extends the
|
|
78
|
+
// effective hit area to 40px tall without changing the row's own
|
|
79
|
+
// padding/density — see design-engineering.md's hit-area pattern.
|
|
80
|
+
"relative flex cursor-pointer list-none items-center gap-1.5 rounded-md px-2 py-1.5 text-muted-foreground transition-colors after:absolute after:inset-x-0 after:top-1/2 after:h-10 after:-translate-y-1/2 hover:bg-muted hover:text-foreground", "[&::-webkit-details-marker]:hidden"),
|
|
81
|
+
children: [
|
|
82
|
+
/*#__PURE__*/ _jsx(ChevronRight, {
|
|
83
|
+
className: cn("size-3.5 shrink-0 transition-transform duration-200 ease-out", open && "rotate-90"),
|
|
84
|
+
"aria-hidden": true
|
|
85
|
+
}),
|
|
86
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
87
|
+
children: [
|
|
88
|
+
"Thought",
|
|
89
|
+
durationMs === undefined ? "" : ` for ${formatDuration(durationMs)}`
|
|
90
|
+
]
|
|
91
|
+
})
|
|
92
|
+
]
|
|
93
|
+
}),
|
|
94
|
+
/*#__PURE__*/ _jsx("p", {
|
|
95
|
+
className: "mt-1 ml-4 border-l border-border pl-3 leading-relaxed whitespace-pre-wrap text-muted-foreground [animation:corbits-rail-block-in_200ms_var(--ease-out)_both]",
|
|
96
|
+
children: text
|
|
97
|
+
}, open ? "open" : "closed")
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function FilePartView({ name, mediaType, url }) {
|
|
102
|
+
const content = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
103
|
+
children: [
|
|
104
|
+
/*#__PURE__*/ _jsx(Paperclip, {
|
|
105
|
+
className: "size-3.5 shrink-0",
|
|
106
|
+
"aria-hidden": true
|
|
107
|
+
}),
|
|
108
|
+
/*#__PURE__*/ _jsx("span", {
|
|
109
|
+
className: "min-w-0 flex-1 truncate",
|
|
110
|
+
children: name
|
|
111
|
+
}),
|
|
112
|
+
/*#__PURE__*/ _jsx("span", {
|
|
113
|
+
className: "shrink-0 text-muted-foreground",
|
|
114
|
+
children: mediaType
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
});
|
|
118
|
+
if (url === undefined) {
|
|
119
|
+
return /*#__PURE__*/ _jsx("span", {
|
|
120
|
+
"data-slot": "file-part",
|
|
121
|
+
className: "inline-flex max-w-full items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5 text-xs text-foreground",
|
|
122
|
+
children: content
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return /*#__PURE__*/ _jsx("a", {
|
|
126
|
+
"data-slot": "file-part",
|
|
127
|
+
href: url,
|
|
128
|
+
target: "_blank",
|
|
129
|
+
rel: "noreferrer",
|
|
130
|
+
className: "inline-flex max-w-full items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5 text-xs text-foreground transition-colors hover:bg-muted",
|
|
131
|
+
children: content
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function EventPartView({ event }) {
|
|
135
|
+
const label = event.replace(/[.-]+/g, " ");
|
|
136
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
137
|
+
"data-slot": "event-part",
|
|
138
|
+
className: "flex items-center gap-2 py-1 text-xs text-muted-foreground italic",
|
|
139
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
140
|
+
children: label
|
|
141
|
+
})
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A block part this renderer has no host-specific registry for. Phase 1
|
|
146
|
+
* ships no generative-UI JSON-spec renderer, so a block always falls back to
|
|
147
|
+
* its type name and raw payload rather than going unrendered.
|
|
148
|
+
*/ function BlockPartFallback({ type, data }) {
|
|
149
|
+
return /*#__PURE__*/ _jsx(BlockCard, {
|
|
150
|
+
title: type,
|
|
151
|
+
children: /*#__PURE__*/ _jsx("pre", {
|
|
152
|
+
className: "max-h-64 overflow-auto font-mono text-[11px] whitespace-pre-wrap text-muted-foreground",
|
|
153
|
+
children: JSON.stringify(data, null, 2)
|
|
154
|
+
})
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* A `Part` whose `kind` this renderer has never heard of — a wire value
|
|
159
|
+
* outside `Part`'s own union, which can only ever arrive because this
|
|
160
|
+
* package validates nothing of its own (see `chat-parts.ts`). Renders a
|
|
161
|
+
* neutral labeled card naming the unrecognized kind rather than rendering
|
|
162
|
+
* nothing, so a producer/consumer version skew is visible in the transcript
|
|
163
|
+
* instead of silently swallowing content.
|
|
164
|
+
*/ function UnknownPartFallback({ kind }) {
|
|
165
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
166
|
+
"data-slot": "unknown-part",
|
|
167
|
+
className: "rounded-md border border-dashed border-border bg-muted/30 px-2.5 py-1.5 text-xs text-muted-foreground",
|
|
168
|
+
children: [
|
|
169
|
+
"Unsupported part: ",
|
|
170
|
+
/*#__PURE__*/ _jsx("span", {
|
|
171
|
+
className: "font-mono",
|
|
172
|
+
children: kind
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Renders an ordered `Part[]` in the order the model produced it: text as
|
|
179
|
+
* prose, reasoning as a closed disclosure, tool calls through `ToolBlock`,
|
|
180
|
+
* files as an attachment chip, events as an inline system line, any block
|
|
181
|
+
* part as a labeled fallback card, and any `kind` outside the `Part` union
|
|
182
|
+
* itself as a neutral unknown-part fallback — the host's own generative-UI
|
|
183
|
+
* registry, if it has one, renders blocks by intercepting `kind: "block"`
|
|
184
|
+
* before it reaches this component.
|
|
185
|
+
*
|
|
186
|
+
* An empty `parts` array renders nothing — no placeholder, no empty-state
|
|
187
|
+
* chrome. A message with no parts is not a message a transcript should show
|
|
188
|
+
* anything for.
|
|
189
|
+
*/ export function PartsRenderer({ parts, className }) {
|
|
190
|
+
if (parts.length === 0) return null;
|
|
191
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
192
|
+
"data-slot": "parts-renderer",
|
|
193
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
194
|
+
children: parts.map((part, index)=>{
|
|
195
|
+
const key = `${part.kind}-${index}`;
|
|
196
|
+
switch(part.kind){
|
|
197
|
+
case "text":
|
|
198
|
+
return /*#__PURE__*/ _jsx(TextPartView, {
|
|
199
|
+
text: part.text
|
|
200
|
+
}, key);
|
|
201
|
+
case "reasoning":
|
|
202
|
+
return /*#__PURE__*/ _jsx(ReasoningPartView, {
|
|
203
|
+
text: part.text,
|
|
204
|
+
durationMs: part.durationMs
|
|
205
|
+
}, key);
|
|
206
|
+
case "tool-trace":
|
|
207
|
+
return /*#__PURE__*/ _jsx(ToolBlock, {
|
|
208
|
+
name: part.name,
|
|
209
|
+
input: part.input,
|
|
210
|
+
state: toolTraceToBlockState(part)
|
|
211
|
+
}, key);
|
|
212
|
+
case "file":
|
|
213
|
+
return /*#__PURE__*/ _jsx(FilePartView, {
|
|
214
|
+
name: part.name,
|
|
215
|
+
mediaType: part.mediaType,
|
|
216
|
+
url: part.url
|
|
217
|
+
}, key);
|
|
218
|
+
case "event":
|
|
219
|
+
return /*#__PURE__*/ _jsx(EventPartView, {
|
|
220
|
+
event: part.event
|
|
221
|
+
}, key);
|
|
222
|
+
case "block":
|
|
223
|
+
return /*#__PURE__*/ _jsx(BlockPartFallback, {
|
|
224
|
+
type: part.block.type,
|
|
225
|
+
data: part.block.data
|
|
226
|
+
}, key);
|
|
227
|
+
default:
|
|
228
|
+
return /*#__PURE__*/ _jsx(UnknownPartFallback, {
|
|
229
|
+
kind: part.kind
|
|
230
|
+
}, key);
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
});
|
|
234
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type AvatarTone } from "./avatar.js";
|
|
3
|
+
import { type BadgeTone } from "./badge.js";
|
|
4
|
+
export type ProfileCardAction = {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly onClick?: () => void;
|
|
8
|
+
readonly tone?: "primary" | "outline";
|
|
9
|
+
};
|
|
10
|
+
export type ProfileCardChannel = {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly href?: string;
|
|
14
|
+
};
|
|
15
|
+
export type ProfileCardSkill = {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly href?: string;
|
|
19
|
+
};
|
|
20
|
+
export type ProfileCardProps = {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly initials: string;
|
|
23
|
+
readonly subtitle?: string;
|
|
24
|
+
readonly statusLabel?: string;
|
|
25
|
+
readonly statusTone?: BadgeTone;
|
|
26
|
+
readonly avatarTone?: AvatarTone;
|
|
27
|
+
readonly tenantMonogram?: string;
|
|
28
|
+
readonly actions?: readonly ProfileCardAction[];
|
|
29
|
+
readonly sharedChannels?: readonly ProfileCardChannel[];
|
|
30
|
+
readonly pinnedSkills?: readonly ProfileCardSkill[];
|
|
31
|
+
readonly footer?: ReactNode;
|
|
32
|
+
readonly className?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Canvas profile card: hero identity, status, action chips, shared
|
|
36
|
+
* channels, and pinned skills. Deep-links are plain hrefs so the host
|
|
37
|
+
* router owns navigation.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ProfileCard({ name, initials, subtitle, statusLabel, statusTone, avatarTone, tenantMonogram, actions, sharedChannels, pinnedSkills, footer, className, }: ProfileCardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { toSafeHref } from "../lib/url.js";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { Avatar } from "./avatar.js";
|
|
5
|
+
import { Badge } from "./badge.js";
|
|
6
|
+
import { Button } from "./button.js";
|
|
7
|
+
/**
|
|
8
|
+
* Canvas profile card: hero identity, status, action chips, shared
|
|
9
|
+
* channels, and pinned skills. Deep-links are plain hrefs so the host
|
|
10
|
+
* router owns navigation.
|
|
11
|
+
*/ export function ProfileCard({ name, initials, subtitle, statusLabel, statusTone = "neutral", avatarTone = "neutral", tenantMonogram, actions = [], sharedChannels = [], pinnedSkills = [], footer, className }) {
|
|
12
|
+
return /*#__PURE__*/ _jsxs("article", {
|
|
13
|
+
"data-slot": "profile-card",
|
|
14
|
+
className: cn("flex flex-col gap-4 border border-border bg-card p-4", className),
|
|
15
|
+
children: [
|
|
16
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
17
|
+
className: "flex items-start gap-3",
|
|
18
|
+
children: [
|
|
19
|
+
/*#__PURE__*/ _jsx(Avatar, {
|
|
20
|
+
initials: initials,
|
|
21
|
+
label: name,
|
|
22
|
+
tone: avatarTone,
|
|
23
|
+
size: "lg",
|
|
24
|
+
...tenantMonogram === undefined ? {} : {
|
|
25
|
+
tenantMonogram
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
28
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
29
|
+
className: "min-w-0 flex-1",
|
|
30
|
+
children: [
|
|
31
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
32
|
+
className: "flex flex-wrap items-center gap-2",
|
|
33
|
+
children: [
|
|
34
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
35
|
+
className: "truncate text-base font-semibold",
|
|
36
|
+
children: name
|
|
37
|
+
}),
|
|
38
|
+
statusLabel === undefined ? null : /*#__PURE__*/ _jsx(Badge, {
|
|
39
|
+
tone: statusTone,
|
|
40
|
+
children: statusLabel
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
}),
|
|
44
|
+
subtitle === undefined ? null : /*#__PURE__*/ _jsx("p", {
|
|
45
|
+
className: "truncate text-xs text-muted-foreground",
|
|
46
|
+
children: subtitle
|
|
47
|
+
})
|
|
48
|
+
]
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
}),
|
|
52
|
+
actions.length === 0 ? null : /*#__PURE__*/ _jsx("div", {
|
|
53
|
+
className: "flex flex-wrap gap-1.5",
|
|
54
|
+
children: actions.map((action)=>/*#__PURE__*/ _jsx(Button, {
|
|
55
|
+
type: "button",
|
|
56
|
+
size: "sm",
|
|
57
|
+
variant: action.tone === "primary" ? "primary" : "outline",
|
|
58
|
+
onClick: action.onClick,
|
|
59
|
+
children: action.label
|
|
60
|
+
}, action.id))
|
|
61
|
+
}),
|
|
62
|
+
sharedChannels.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
|
|
63
|
+
className: "flex flex-col gap-1.5",
|
|
64
|
+
children: [
|
|
65
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
66
|
+
className: "text-[10.5px] font-bold uppercase tracking-[0.06em] text-muted-foreground",
|
|
67
|
+
children: "Shared channels"
|
|
68
|
+
}),
|
|
69
|
+
/*#__PURE__*/ _jsx("ul", {
|
|
70
|
+
className: "flex flex-col gap-1",
|
|
71
|
+
children: sharedChannels.map((channel)=>{
|
|
72
|
+
const href = toSafeHref(channel.href);
|
|
73
|
+
return /*#__PURE__*/ _jsx("li", {
|
|
74
|
+
children: href === undefined ? /*#__PURE__*/ _jsxs("span", {
|
|
75
|
+
className: "text-sm",
|
|
76
|
+
children: [
|
|
77
|
+
"#",
|
|
78
|
+
channel.name
|
|
79
|
+
]
|
|
80
|
+
}) : /*#__PURE__*/ _jsxs("a", {
|
|
81
|
+
href: href,
|
|
82
|
+
className: "text-sm text-foreground hover:underline",
|
|
83
|
+
children: [
|
|
84
|
+
"#",
|
|
85
|
+
channel.name
|
|
86
|
+
]
|
|
87
|
+
})
|
|
88
|
+
}, channel.id);
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
}),
|
|
93
|
+
pinnedSkills.length === 0 ? null : /*#__PURE__*/ _jsxs("section", {
|
|
94
|
+
className: "flex flex-col gap-1.5",
|
|
95
|
+
children: [
|
|
96
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
97
|
+
className: "text-[10.5px] font-bold uppercase tracking-[0.06em] text-muted-foreground",
|
|
98
|
+
children: "Pinned skills"
|
|
99
|
+
}),
|
|
100
|
+
/*#__PURE__*/ _jsx("ul", {
|
|
101
|
+
className: "flex flex-wrap gap-1.5",
|
|
102
|
+
children: pinnedSkills.map((skill)=>{
|
|
103
|
+
const href = toSafeHref(skill.href);
|
|
104
|
+
return href === undefined ? /*#__PURE__*/ _jsx(Badge, {
|
|
105
|
+
tone: "neutral",
|
|
106
|
+
children: skill.name
|
|
107
|
+
}, skill.id) : /*#__PURE__*/ _jsx("a", {
|
|
108
|
+
href: href,
|
|
109
|
+
children: /*#__PURE__*/ _jsx(Badge, {
|
|
110
|
+
tone: "neutral",
|
|
111
|
+
children: skill.name
|
|
112
|
+
})
|
|
113
|
+
}, skill.id);
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
}),
|
|
118
|
+
footer === undefined ? null : /*#__PURE__*/ _jsx("footer", {
|
|
119
|
+
className: "border-t border-border pt-3",
|
|
120
|
+
children: footer
|
|
121
|
+
})
|
|
122
|
+
]
|
|
123
|
+
});
|
|
124
|
+
}
|