@corbits/react-ui 0.1.0 → 0.3.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 +174 -499
- package/README.md +228 -85
- package/dist/blocks/login/auth-layout.d.ts +1 -1
- package/dist/blocks/login/auth-layout.js +3 -33
- package/dist/blocks/login/login-form.js +6 -119
- package/dist/hooks/use-command-palette-navigation.js +18 -17
- package/dist/hooks/use-controllable-state.d.ts +2 -2
- package/dist/hooks/use-controllable-state.js +23 -15
- package/dist/hooks/use-delayed-autofocus.js +10 -11
- package/dist/hooks/use-dismissable-popover.d.ts +2 -2
- package/dist/hooks/use-dismissable-popover.js +21 -29
- package/dist/hooks/use-list-selection.js +54 -85
- package/dist/hooks/use-prefers-reduced-motion.js +8 -4
- package/dist/hooks/use-scroll-current-into-view.js +5 -7
- package/dist/index.d.ts +0 -11
- package/dist/index.js +10 -19
- package/dist/lib/artifact.js +16 -35
- package/dist/lib/chart-geometry.js +33 -20
- package/dist/lib/chart-palette.js +17 -31
- package/dist/lib/chat-message.js +32 -13
- package/dist/lib/chat-parts.js +2 -1
- package/dist/lib/csv.js +42 -26
- package/dist/lib/metrics.js +30 -48
- package/dist/lib/now-item.js +26 -25
- package/dist/lib/relative-time.js +14 -29
- package/dist/lib/theme.js +32 -30
- package/dist/lib/url.js +9 -8
- package/dist/lib/workflow-display-flow.js +37 -33
- package/dist/lib/workflow-registry.d.ts +0 -41
- package/dist/lib/workflow-registry.js +13 -23
- package/dist/lib/workflow-run-error.js +16 -20
- package/dist/lib/workflow-run-progress.js +94 -52
- package/dist/lib/workflow-run.js +17 -13
- package/dist/styles.css +257 -19
- package/dist/test/render-hook.js +11 -12
- package/dist/theme.css +92 -14
- package/dist/ui/agent-turn.d.ts +1 -1
- package/dist/ui/agent-turn.js +4 -37
- package/dist/ui/animated-number.js +15 -28
- package/dist/ui/approval-card.js +5 -90
- package/dist/ui/artifact-body.js +47 -125
- package/dist/ui/artifact-notice.d.ts +1 -1
- package/dist/ui/artifact-notice.js +9 -16
- package/dist/ui/avatar.d.ts +21 -4
- package/dist/ui/avatar.js +40 -49
- package/dist/ui/badge.js +4 -12
- package/dist/ui/bar-chart.d.ts +1 -1
- package/dist/ui/bar-chart.js +22 -74
- package/dist/ui/block-card.js +7 -45
- package/dist/ui/boot-screen.d.ts +1 -1
- package/dist/ui/boot-screen.js +4 -33
- package/dist/ui/bulk-action-bar.d.ts +1 -1
- package/dist/ui/bulk-action-bar.js +14 -33
- package/dist/ui/button.js +10 -24
- package/dist/ui/card.js +5 -25
- package/dist/ui/category-bars.js +14 -59
- package/dist/ui/chart-frame.d.ts +2 -2
- package/dist/ui/chart-frame.js +6 -85
- package/dist/ui/chat-composer.js +4 -16
- package/dist/ui/chat-dock-timing.js +7 -3
- package/dist/ui/chat-input.js +20 -100
- package/dist/ui/chat-panel.d.ts +1 -1
- package/dist/ui/chat-panel.js +7 -41
- package/dist/ui/chat-thread.js +13 -31
- package/dist/ui/checkbox.js +10 -52
- package/dist/ui/command-palette.js +85 -246
- package/dist/ui/compare-body.d.ts +1 -1
- package/dist/ui/compare-body.js +28 -98
- package/dist/ui/confirm-button.js +8 -15
- package/dist/ui/corbits-mark.js +7 -16
- package/dist/ui/csv-table.d.ts +1 -1
- package/dist/ui/csv-table.js +8 -50
- package/dist/ui/dialog.d.ts +1 -1
- package/dist/ui/dialog.js +32 -62
- package/dist/ui/dictation-status-line.js +7 -25
- package/dist/ui/dither-canvas.d.ts +1 -1
- package/dist/ui/dither-canvas.js +31 -56
- package/dist/ui/embed-body.d.ts +1 -1
- package/dist/ui/embed-body.js +7 -56
- package/dist/ui/empty-state.d.ts +1 -1
- package/dist/ui/empty-state.js +3 -26
- package/dist/ui/file-input.js +17 -37
- package/dist/ui/filter-bar.d.ts +1 -1
- package/dist/ui/filter-bar.js +11 -71
- package/dist/ui/filter-chip.d.ts +1 -1
- package/dist/ui/filter-chip.js +7 -15
- package/dist/ui/gate-block.d.ts +2 -1
- package/dist/ui/gate-block.js +4 -29
- package/dist/ui/horizontal-stepper.d.ts +1 -1
- package/dist/ui/horizontal-stepper.js +18 -45
- package/dist/ui/input.js +1 -6
- package/dist/ui/inspector-shell.d.ts +5 -5
- package/dist/ui/inspector-shell.js +14 -81
- package/dist/ui/intake-form.d.ts +1 -1
- package/dist/ui/intake-form.js +21 -97
- package/dist/ui/kind-card-grid.js +9 -31
- package/dist/ui/library-page-header.js +4 -32
- package/dist/ui/list-detail.js +5 -35
- package/dist/ui/live-run-banner.d.ts +1 -1
- package/dist/ui/live-run-banner.js +5 -15
- package/dist/ui/live-status-line.js +6 -24
- package/dist/ui/menu.js +7 -24
- package/dist/ui/message-bubble.d.ts +3 -3
- package/dist/ui/message-bubble.js +5 -44
- package/dist/ui/mic-button.d.ts +1 -1
- package/dist/ui/mic-button.js +15 -21
- package/dist/ui/mic-permission-dialog.js +5 -41
- package/dist/ui/notifications-bell.js +9 -43
- package/dist/ui/now-cards.d.ts +2 -2
- package/dist/ui/now-cards.js +16 -94
- package/dist/ui/page-panel.js +11 -11
- package/dist/ui/page-shell.js +4 -11
- package/dist/ui/parts-renderer.js +51 -149
- package/dist/ui/profile-card.js +7 -111
- package/dist/ui/progress-checklist.js +11 -49
- package/dist/ui/provider-mark.d.ts +2 -2
- package/dist/ui/provider-mark.js +9 -16
- package/dist/ui/quick-reply-chips.js +6 -14
- package/dist/ui/quote-card.js +12 -32
- package/dist/ui/reasoning-block.d.ts +1 -1
- package/dist/ui/reasoning-block.js +14 -33
- package/dist/ui/research-body.js +32 -213
- package/dist/ui/rich-empty-state.js +6 -48
- package/dist/ui/run-now-button.js +14 -15
- package/dist/ui/section.d.ts +1 -1
- package/dist/ui/section.js +4 -40
- package/dist/ui/select.js +1 -15
- package/dist/ui/selection-checkbox.d.ts +1 -1
- package/dist/ui/selection-checkbox.js +11 -29
- package/dist/ui/settings-panel.js +7 -50
- package/dist/ui/shimmer-text.js +3 -6
- package/dist/ui/sidebar-item-row.d.ts +1 -1
- package/dist/ui/sidebar-item-row.js +9 -39
- package/dist/ui/sidebar-panel.js +15 -40
- package/dist/ui/sidebar.js +15 -76
- package/dist/ui/skeleton.js +3 -7
- package/dist/ui/sortable-table.js +29 -72
- package/dist/ui/sparkline.js +14 -38
- package/dist/ui/stat-grid.js +17 -52
- package/dist/ui/status-dot.d.ts +1 -1
- package/dist/ui/status-dot.js +8 -20
- package/dist/ui/step-list.d.ts +1 -0
- package/dist/ui/step-list.js +11 -50
- package/dist/ui/switch.js +4 -16
- package/dist/ui/table.js +7 -32
- package/dist/ui/tabs.js +39 -54
- package/dist/ui/textarea.js +10 -19
- package/dist/ui/theme-provider.js +54 -78
- package/dist/ui/theme-toggle.js +13 -16
- package/dist/ui/thinking-indicator.js +3 -15
- package/dist/ui/thinking-label.js +16 -35
- package/dist/ui/thinking-mark.d.ts +1 -1
- package/dist/ui/thinking-mark.js +4 -145
- package/dist/ui/time-series-chart.js +43 -174
- package/dist/ui/toast.js +12 -20
- package/dist/ui/token-mosaic.js +5 -43
- package/dist/ui/tool-block.js +18 -70
- package/dist/ui/tool-narrative.d.ts +1 -1
- package/dist/ui/tool-narrative.js +10 -57
- package/dist/ui/tool-picker.js +57 -145
- package/dist/ui/tooltip.d.ts +1 -1
- package/dist/ui/tooltip.js +4 -33
- package/dist/ui/top-bar.js +14 -72
- package/dist/ui/trace-waterfall.d.ts +1 -1
- package/dist/ui/trace-waterfall.js +21 -93
- package/dist/ui/typing-indicator.d.ts +1 -1
- package/dist/ui/typing-indicator.js +4 -18
- package/dist/ui/view-toggle.js +10 -29
- package/dist/ui/voice-waveform.js +3 -11
- package/package.json +96 -535
package/dist/theme.css
CHANGED
|
@@ -274,7 +274,11 @@
|
|
|
274
274
|
a `.dark` override of their own. */
|
|
275
275
|
:root {
|
|
276
276
|
--sidebar-row-radius: var(--radius);
|
|
277
|
-
--sidebar-row-selected-bg: color-mix(
|
|
277
|
+
--sidebar-row-selected-bg: color-mix(
|
|
278
|
+
in srgb,
|
|
279
|
+
var(--primary) 10%,
|
|
280
|
+
transparent
|
|
281
|
+
);
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
@theme inline {
|
|
@@ -601,28 +605,102 @@
|
|
|
601
605
|
}
|
|
602
606
|
}
|
|
603
607
|
|
|
608
|
+
/* Avatar working orbit. The conic angle is a registered property so the arc
|
|
609
|
+
sweeps while the masked ring (and the avatar it encircles) never rotates.
|
|
610
|
+
Used by ui/avatar. */
|
|
611
|
+
@property --corbits-avatar-angle {
|
|
612
|
+
syntax: "<angle>";
|
|
613
|
+
inherits: false;
|
|
614
|
+
initial-value: 0deg;
|
|
615
|
+
}
|
|
616
|
+
@keyframes corbits-avatar-orbit {
|
|
617
|
+
to {
|
|
618
|
+
--corbits-avatar-angle: 360deg;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
.corbits-avatar-orbit {
|
|
622
|
+
position: absolute;
|
|
623
|
+
inset: -4px;
|
|
624
|
+
padding: 2px;
|
|
625
|
+
background:
|
|
626
|
+
conic-gradient(
|
|
627
|
+
from var(--corbits-avatar-angle),
|
|
628
|
+
var(--primary-emphasis) 0deg 90deg,
|
|
629
|
+
transparent 90deg 360deg
|
|
630
|
+
),
|
|
631
|
+
color-mix(in srgb, var(--foreground) 12%, transparent);
|
|
632
|
+
-webkit-mask:
|
|
633
|
+
linear-gradient(#000 0 0) content-box,
|
|
634
|
+
linear-gradient(#000 0 0);
|
|
635
|
+
-webkit-mask-composite: xor;
|
|
636
|
+
mask:
|
|
637
|
+
linear-gradient(#000 0 0) content-box exclude,
|
|
638
|
+
linear-gradient(#000 0 0);
|
|
639
|
+
animation: corbits-avatar-orbit 1.4s linear infinite;
|
|
640
|
+
pointer-events: none;
|
|
641
|
+
}
|
|
642
|
+
@media (prefers-reduced-motion: reduce) {
|
|
643
|
+
.corbits-avatar-orbit {
|
|
644
|
+
animation: none;
|
|
645
|
+
--corbits-avatar-angle: 120deg;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
604
649
|
/* Ridge-study motion: keep geometry and timing in the original 100 x 70 space. */
|
|
605
650
|
@keyframes corbits-thinking-sweep {
|
|
606
|
-
0% {
|
|
607
|
-
|
|
608
|
-
|
|
651
|
+
0% {
|
|
652
|
+
transform: translateX(8px);
|
|
653
|
+
opacity: 0;
|
|
654
|
+
}
|
|
655
|
+
8%,
|
|
656
|
+
86% {
|
|
657
|
+
opacity: 1;
|
|
658
|
+
}
|
|
659
|
+
100% {
|
|
660
|
+
transform: translateX(130px);
|
|
661
|
+
opacity: 0;
|
|
662
|
+
}
|
|
609
663
|
}
|
|
610
664
|
@keyframes corbits-thinking-rise {
|
|
611
|
-
0% {
|
|
612
|
-
|
|
665
|
+
0% {
|
|
666
|
+
transform: translateY(88px);
|
|
667
|
+
}
|
|
668
|
+
100% {
|
|
669
|
+
transform: translateY(-30px);
|
|
670
|
+
}
|
|
613
671
|
}
|
|
614
672
|
@keyframes corbits-thinking-wave {
|
|
615
|
-
0% {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
673
|
+
0% {
|
|
674
|
+
transform: scale(0.03);
|
|
675
|
+
opacity: 0;
|
|
676
|
+
}
|
|
677
|
+
10% {
|
|
678
|
+
opacity: 1;
|
|
679
|
+
}
|
|
680
|
+
75% {
|
|
681
|
+
opacity: 0.9;
|
|
682
|
+
}
|
|
683
|
+
100% {
|
|
684
|
+
transform: scale(1.35);
|
|
685
|
+
opacity: 0;
|
|
686
|
+
}
|
|
619
687
|
}
|
|
620
688
|
@media (prefers-reduced-motion: reduce) {
|
|
621
689
|
/* Static poses land the gradient mid-stroke. These offsets mirror the SVG
|
|
622
690
|
geometry in thinking-mark.tsx — change the rect/circle geometry there and
|
|
623
691
|
these poses have to be re-picked to match. */
|
|
624
|
-
.corbits-thinking-silk,
|
|
625
|
-
.corbits-thinking-
|
|
626
|
-
.corbits-thinking-
|
|
627
|
-
|
|
692
|
+
.corbits-thinking-silk,
|
|
693
|
+
.corbits-thinking-strata,
|
|
694
|
+
.corbits-thinking-echo {
|
|
695
|
+
animation: none;
|
|
696
|
+
}
|
|
697
|
+
.corbits-thinking-silk {
|
|
698
|
+
transform: translateX(44px);
|
|
699
|
+
}
|
|
700
|
+
.corbits-thinking-strata {
|
|
701
|
+
transform: translateY(30px);
|
|
702
|
+
}
|
|
703
|
+
.corbits-thinking-echo {
|
|
704
|
+
transform: scale(0.6);
|
|
705
|
+
}
|
|
628
706
|
}
|
package/dist/ui/agent-turn.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type AgentTurnProps = {
|
|
|
5
5
|
readonly identity: AgentIdentity;
|
|
6
6
|
/** Rich body for the answer — a markdown renderer. */
|
|
7
7
|
readonly children?: ReactNode;
|
|
8
|
-
readonly now?: number;
|
|
8
|
+
readonly now?: number | undefined;
|
|
9
9
|
/** e.g. "Thought for 4s". Shown on the reasoning block once it stops streaming. */
|
|
10
10
|
readonly reasoningDurationLabel?: string;
|
|
11
11
|
readonly className?: string;
|
package/dist/ui/agent-turn.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { agentInitials, isTurnWorking, messageText, reasoningText, toolParts } from "../lib/chat-message.js";
|
|
2
|
+
import { agentInitials, isTurnWorking, messageText, reasoningText, toolParts, } from "../lib/chat-message.js";
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { MessageBubble } from "./message-bubble.js";
|
|
5
5
|
import { ReasoningBlock } from "./reasoning-block.js";
|
|
@@ -16,45 +16,12 @@ import { ToolNarrative } from "./tool-narrative.js";
|
|
|
16
16
|
* The avatar is `aria-hidden` and the turn is a labelled region instead: a
|
|
17
17
|
* screen reader should hear "Ada said …" once, not two initials followed by the
|
|
18
18
|
* name.
|
|
19
|
-
*/
|
|
19
|
+
*/
|
|
20
|
+
export function AgentTurn({ message, identity, children, now, reasoningDurationLabel, className, }) {
|
|
20
21
|
const working = isTurnWorking(message);
|
|
21
22
|
const answer = messageText(message);
|
|
22
23
|
const thinking = reasoningText(message);
|
|
23
24
|
const tools = toolParts(message);
|
|
24
25
|
const hasWork = thinking.length > 0 || tools.length > 0;
|
|
25
|
-
return
|
|
26
|
-
"data-slot": "agent-turn",
|
|
27
|
-
"aria-label": `${identity.name} replied`,
|
|
28
|
-
className: cn("flex gap-3", className),
|
|
29
|
-
children: [
|
|
30
|
-
/*#__PURE__*/ _jsx("span", {
|
|
31
|
-
"aria-hidden": true,
|
|
32
|
-
className: "mt-0.5 grid size-7 shrink-0 place-items-center rounded-full bg-secondary text-[10px] font-bold text-secondary-foreground",
|
|
33
|
-
children: agentInitials(identity)
|
|
34
|
-
}),
|
|
35
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
36
|
-
className: "flex min-w-0 flex-1 flex-col gap-1.5",
|
|
37
|
-
children: [
|
|
38
|
-
hasWork ? /*#__PURE__*/ _jsxs("div", {
|
|
39
|
-
className: "flex flex-col",
|
|
40
|
-
children: [
|
|
41
|
-
/*#__PURE__*/ _jsx(ReasoningBlock, {
|
|
42
|
-
text: thinking,
|
|
43
|
-
streaming: working,
|
|
44
|
-
durationLabel: reasoningDurationLabel
|
|
45
|
-
}),
|
|
46
|
-
tools.map((part)=>/*#__PURE__*/ _jsx(ToolNarrative, {
|
|
47
|
-
part: part
|
|
48
|
-
}, part.toolCallId))
|
|
49
|
-
]
|
|
50
|
-
}) : null,
|
|
51
|
-
answer.length > 0 || children !== undefined ? /*#__PURE__*/ _jsx(MessageBubble, {
|
|
52
|
-
message: message,
|
|
53
|
-
now: now,
|
|
54
|
-
children: children
|
|
55
|
-
}) : null
|
|
56
|
-
]
|
|
57
|
-
})
|
|
58
|
-
]
|
|
59
|
-
});
|
|
26
|
+
return (_jsxs("article", { "data-slot": "agent-turn", "aria-label": `${identity.name} replied`, className: cn("flex gap-3", className), children: [_jsx("span", { "aria-hidden": true, className: "mt-0.5 grid size-7 shrink-0 place-items-center rounded-full bg-secondary text-[10px] font-bold text-secondary-foreground", children: agentInitials(identity) }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [hasWork ? (_jsxs("div", { className: "flex flex-col", children: [_jsx(ReasoningBlock, { text: thinking, streaming: working, durationLabel: reasoningDurationLabel }), tools.map((part) => (_jsx(ToolNarrative, { part: part }, part.toolCallId)))] })) : null, answer.length > 0 || children !== undefined ? (_jsx(MessageBubble, { message: message, now: now, children: children })) : null] })] }));
|
|
60
27
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useEffect, useRef, useState } from "react";
|
|
3
4
|
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
4
5
|
import { cn } from "../lib/utils.js";
|
|
5
|
-
const easeOut = (t)=>1 - (1 - t) ** 3;
|
|
6
|
+
const easeOut = (t) => 1 - (1 - t) ** 3;
|
|
6
7
|
/**
|
|
7
8
|
* A number that counts to its new value instead of snapping.
|
|
8
9
|
*
|
|
@@ -17,12 +18,13 @@ const easeOut = (t)=>1 - (1 - t) ** 3;
|
|
|
17
18
|
* Reduced motion is checked here rather than left to the theme's CSS override.
|
|
18
19
|
* The theme can collapse a CSS animation, but this is a JS interpolation the
|
|
19
20
|
* cascade cannot reach, so it snaps to the value instead.
|
|
20
|
-
*/
|
|
21
|
+
*/
|
|
22
|
+
export function AnimatedNumber({ value, decimals = 0, format, durationMs = 600, className, }) {
|
|
21
23
|
const [display, setDisplay] = useState(value);
|
|
22
24
|
const fromRef = useRef(value);
|
|
23
25
|
const frameRef = useRef(0);
|
|
24
26
|
const reduce = usePrefersReducedMotion();
|
|
25
|
-
useEffect(()=>{
|
|
27
|
+
useEffect(() => {
|
|
26
28
|
const from = fromRef.current;
|
|
27
29
|
if (reduce || from === value) {
|
|
28
30
|
fromRef.current = value;
|
|
@@ -30,34 +32,19 @@ const easeOut = (t)=>1 - (1 - t) ** 3;
|
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
34
|
const start = performance.now();
|
|
33
|
-
const step = (now)=>{
|
|
35
|
+
const step = (now) => {
|
|
34
36
|
const t = Math.min((now - start) / durationMs, 1);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const current = from + (value - from) * easeOut(t);
|
|
38
|
+
// A value change mid-count continues from what is on screen, not from
|
|
39
|
+
// the previous target.
|
|
40
|
+
fromRef.current = current;
|
|
41
|
+
setDisplay(current);
|
|
42
|
+
if (t < 1)
|
|
37
43
|
frameRef.current = requestAnimationFrame(step);
|
|
38
|
-
} else {
|
|
39
|
-
fromRef.current = value;
|
|
40
|
-
}
|
|
41
44
|
};
|
|
42
45
|
frameRef.current = requestAnimationFrame(step);
|
|
43
|
-
return ()=>cancelAnimationFrame(frameRef.current);
|
|
44
|
-
}, [
|
|
45
|
-
value,
|
|
46
|
-
durationMs,
|
|
47
|
-
reduce
|
|
48
|
-
]);
|
|
46
|
+
return () => cancelAnimationFrame(frameRef.current);
|
|
47
|
+
}, [value, durationMs, reduce]);
|
|
49
48
|
const text = format === undefined ? display.toFixed(decimals) : format(display);
|
|
50
|
-
return
|
|
51
|
-
className: cn("tabular-nums", className),
|
|
52
|
-
children: [
|
|
53
|
-
/*#__PURE__*/ _jsx("span", {
|
|
54
|
-
"aria-hidden": true,
|
|
55
|
-
children: text
|
|
56
|
-
}),
|
|
57
|
-
/*#__PURE__*/ _jsx("span", {
|
|
58
|
-
className: "sr-only",
|
|
59
|
-
children: format === undefined ? value.toFixed(decimals) : format(value)
|
|
60
|
-
})
|
|
61
|
-
]
|
|
62
|
-
});
|
|
49
|
+
return (_jsxs("span", { className: cn("tabular-nums", className), children: [_jsx("span", { "aria-hidden": true, children: text }), _jsx("span", { className: "sr-only", children: format === undefined ? value.toFixed(decimals) : format(value) })] }));
|
|
63
50
|
}
|
package/dist/ui/approval-card.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useState } from "react";
|
|
3
4
|
import { cn } from "../lib/utils.js";
|
|
@@ -17,100 +18,14 @@ const LONG_VALUE = 140;
|
|
|
17
18
|
* Every argument the action will run with is shown. A card that hides them
|
|
18
19
|
* behind "details" is asking for approval of something the user cannot see, so
|
|
19
20
|
* long values clamp rather than disappear.
|
|
20
|
-
*/
|
|
21
|
+
*/
|
|
22
|
+
export function ApprovalCard({ request, onApprove, onReject, onAllowAlways, state = "idle", error = null, className, children, }) {
|
|
21
23
|
const busy = state !== "idle";
|
|
22
24
|
const canAllowAlways = onAllowAlways !== undefined && request.actionKey !== undefined;
|
|
23
|
-
return
|
|
24
|
-
"aria-label": `Approval requested: ${request.headline}`,
|
|
25
|
-
className: cn("flex flex-col gap-3 rounded-lg border border-primary-emphasis bg-card p-4", className),
|
|
26
|
-
children: [
|
|
27
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
28
|
-
className: "flex flex-wrap items-center gap-2",
|
|
29
|
-
children: [
|
|
30
|
-
/*#__PURE__*/ _jsx(Badge, {
|
|
31
|
-
tone: "accent",
|
|
32
|
-
children: "Needs your approval"
|
|
33
|
-
}),
|
|
34
|
-
/*#__PURE__*/ _jsxs("span", {
|
|
35
|
-
className: "text-xs text-muted-foreground",
|
|
36
|
-
children: [
|
|
37
|
-
"Requested by ",
|
|
38
|
-
request.requestedBy
|
|
39
|
-
]
|
|
40
|
-
})
|
|
41
|
-
]
|
|
42
|
-
}),
|
|
43
|
-
/*#__PURE__*/ _jsx("p", {
|
|
44
|
-
className: "text-sm font-semibold",
|
|
45
|
-
children: request.headline
|
|
46
|
-
}),
|
|
47
|
-
request.details === undefined || request.details.length === 0 ? null : /*#__PURE__*/ _jsx("dl", {
|
|
48
|
-
className: "flex flex-col gap-1.5 rounded-md bg-muted p-3 text-xs",
|
|
49
|
-
children: request.details.map((detail)=>/*#__PURE__*/ _jsx(ApprovalDetailRow, {
|
|
50
|
-
detail: detail
|
|
51
|
-
}, detail.label))
|
|
52
|
-
}),
|
|
53
|
-
children,
|
|
54
|
-
error === null ? null : /*#__PURE__*/ _jsx("p", {
|
|
55
|
-
role: "alert",
|
|
56
|
-
className: "text-sm text-destructive",
|
|
57
|
-
children: error
|
|
58
|
-
}),
|
|
59
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
60
|
-
className: "flex flex-wrap items-center gap-2",
|
|
61
|
-
children: [
|
|
62
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
63
|
-
size: "sm",
|
|
64
|
-
onClick: onApprove,
|
|
65
|
-
disabled: busy,
|
|
66
|
-
children: state === "approving" ? "Approving…" : "Approve"
|
|
67
|
-
}),
|
|
68
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
69
|
-
variant: "outline",
|
|
70
|
-
size: "sm",
|
|
71
|
-
onClick: onReject,
|
|
72
|
-
disabled: busy,
|
|
73
|
-
children: state === "rejecting" ? "Rejecting…" : "Reject"
|
|
74
|
-
}),
|
|
75
|
-
canAllowAlways ? /*#__PURE__*/ _jsx(Button, {
|
|
76
|
-
variant: "ghost",
|
|
77
|
-
size: "sm",
|
|
78
|
-
onClick: ()=>onAllowAlways(request.actionKey),
|
|
79
|
-
disabled: busy,
|
|
80
|
-
className: "ml-auto",
|
|
81
|
-
children: state === "allowing-always" ? "Saving…" : "Always allow this"
|
|
82
|
-
}) : null
|
|
83
|
-
]
|
|
84
|
-
})
|
|
85
|
-
]
|
|
86
|
-
});
|
|
25
|
+
return (_jsxs("section", { "aria-label": `Approval requested: ${request.headline}`, className: cn("flex flex-col gap-3 rounded-lg border border-primary-emphasis bg-card p-4", className), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Badge, { tone: "accent", children: "Needs your approval" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Requested by ", request.requestedBy] })] }), _jsx("p", { className: "text-sm font-semibold", children: request.headline }), request.details === undefined || request.details.length === 0 ? null : (_jsx("dl", { className: "flex flex-col gap-1.5 rounded-md bg-muted p-3 text-xs", children: request.details.map((detail) => (_jsx(ApprovalDetailRow, { detail: detail }, detail.label))) })), children, error === null ? null : (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { size: "sm", onClick: onApprove, disabled: busy, children: state === "approving" ? "Approving…" : "Approve" }), _jsx(Button, { variant: "outline", size: "sm", onClick: onReject, disabled: busy, children: state === "rejecting" ? "Rejecting…" : "Reject" }), canAllowAlways ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onAllowAlways(request.actionKey), disabled: busy, className: "ml-auto", children: state === "allowing-always" ? "Saving…" : "Always allow this" })) : null] })] }));
|
|
87
26
|
}
|
|
88
27
|
function ApprovalDetailRow({ detail }) {
|
|
89
28
|
const [expanded, setExpanded] = useState(false);
|
|
90
29
|
const long = detail.value.length > LONG_VALUE;
|
|
91
|
-
return
|
|
92
|
-
className: "grid grid-cols-[minmax(0,8rem)_1fr] gap-2",
|
|
93
|
-
children: [
|
|
94
|
-
/*#__PURE__*/ _jsx("dt", {
|
|
95
|
-
className: "truncate text-muted-foreground",
|
|
96
|
-
children: detail.label
|
|
97
|
-
}),
|
|
98
|
-
/*#__PURE__*/ _jsxs("dd", {
|
|
99
|
-
className: "min-w-0",
|
|
100
|
-
children: [
|
|
101
|
-
/*#__PURE__*/ _jsx("span", {
|
|
102
|
-
className: cn("block break-words whitespace-pre-wrap", long && !expanded && "line-clamp-2"),
|
|
103
|
-
children: detail.value
|
|
104
|
-
}),
|
|
105
|
-
long ? /*#__PURE__*/ _jsx("button", {
|
|
106
|
-
type: "button",
|
|
107
|
-
onClick: ()=>setExpanded((value)=>!value),
|
|
108
|
-
"aria-expanded": expanded,
|
|
109
|
-
className: "mt-0.5 text-primary-emphasis hover:underline",
|
|
110
|
-
children: expanded ? "Show less" : "Show more"
|
|
111
|
-
}) : null
|
|
112
|
-
]
|
|
113
|
-
})
|
|
114
|
-
]
|
|
115
|
-
});
|
|
30
|
+
return (_jsxs("div", { className: "grid grid-cols-[minmax(0,8rem)_1fr] gap-2", children: [_jsx("dt", { className: "truncate text-muted-foreground", children: detail.label }), _jsxs("dd", { className: "min-w-0", children: [_jsx("span", { className: cn("block break-words whitespace-pre-wrap", long && !expanded && "line-clamp-2"), children: detail.value }), long ? (_jsx("button", { type: "button", onClick: () => setExpanded((value) => !value), "aria-expanded": expanded, className: "mt-0.5 text-primary-emphasis hover:underline", children: expanded ? "Show less" : "Show more" })) : null] })] }));
|
|
116
31
|
}
|
package/dist/ui/artifact-body.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Download } from "lucide-react";
|
|
3
|
-
import { artifactForm } from "../lib/artifact.js";
|
|
3
|
+
import { artifactForm, } from "../lib/artifact.js";
|
|
4
4
|
import { toSafeHref } from "../lib/url.js";
|
|
5
5
|
import { cn } from "../lib/utils.js";
|
|
6
6
|
import { ArtifactNotice } from "./artifact-notice.js";
|
|
@@ -22,133 +22,57 @@ import { ResearchBody, parseResearchArtifact } from "./research-body.js";
|
|
|
22
22
|
* "empty" is a real state here — artifacts are frequently rendered while they
|
|
23
23
|
* are still being written — and each form's own empty rendering (a table with
|
|
24
24
|
* no rows, a frame with no src) reads as a fault rather than as progress.
|
|
25
|
-
*/
|
|
25
|
+
*/
|
|
26
|
+
export function ArtifactBody({ artifact, form, className }) {
|
|
26
27
|
const resolved = form ?? artifactForm(artifact.kind);
|
|
27
28
|
const content = artifact.content;
|
|
28
29
|
const blank = content.trim() === "";
|
|
29
|
-
return
|
|
30
|
-
className: cn("w-full", className),
|
|
31
|
-
children: body()
|
|
32
|
-
});
|
|
30
|
+
return _jsx("div", { className: cn("w-full", className), children: body() });
|
|
33
31
|
function body() {
|
|
34
|
-
switch(resolved){
|
|
35
|
-
case "image":
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
src: src,
|
|
48
|
-
alt: artifact.title,
|
|
49
|
-
className: "max-h-[480px] max-w-full rounded-lg border border-border"
|
|
50
|
-
}),
|
|
51
|
-
blank ? null : /*#__PURE__*/ _jsx("figcaption", {
|
|
52
|
-
className: "text-xs text-muted-foreground",
|
|
53
|
-
children: content
|
|
54
|
-
})
|
|
55
|
-
]
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
case "download":
|
|
59
|
-
{
|
|
60
|
-
const downloadUrl = toSafeHref(artifact.downloadUrl);
|
|
61
|
-
if (downloadUrl === undefined) {
|
|
62
|
-
return /*#__PURE__*/ _jsx(ArtifactNotice, {
|
|
63
|
-
message: "This file has no download location, so it cannot be retrieved."
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return /*#__PURE__*/ _jsxs("a", {
|
|
67
|
-
href: downloadUrl,
|
|
68
|
-
download: true,
|
|
69
|
-
className: buttonVariants({
|
|
70
|
-
variant: "outline",
|
|
71
|
-
size: "sm"
|
|
72
|
-
}),
|
|
73
|
-
children: [
|
|
74
|
-
/*#__PURE__*/ _jsx(Download, {
|
|
75
|
-
"aria-hidden": true
|
|
76
|
-
}),
|
|
77
|
-
"Download ",
|
|
78
|
-
artifact.title
|
|
79
|
-
]
|
|
80
|
-
});
|
|
32
|
+
switch (resolved) {
|
|
33
|
+
case "image": {
|
|
34
|
+
// The thumbnail URL, not the content: an image artifact's bytes live
|
|
35
|
+
// behind a route, and `content` for one is either empty or a caption.
|
|
36
|
+
const src = toSafeHref(artifact.thumbnailUrl ?? artifact.downloadUrl);
|
|
37
|
+
if (src === undefined)
|
|
38
|
+
return _jsx(ArtifactNotice, { pending: artifact.pending });
|
|
39
|
+
return (_jsxs("figure", { className: "flex flex-col gap-3", children: [_jsx("img", { src: src, alt: artifact.title, className: "max-h-[480px] max-w-full rounded-lg border border-border" }), blank ? null : (_jsx("figcaption", { className: "text-xs text-muted-foreground", children: content }))] }));
|
|
40
|
+
}
|
|
41
|
+
case "download": {
|
|
42
|
+
const downloadUrl = toSafeHref(artifact.downloadUrl);
|
|
43
|
+
if (downloadUrl === undefined) {
|
|
44
|
+
return (_jsx(ArtifactNotice, { message: "This file has no download location, so it cannot be retrieved." }));
|
|
81
45
|
}
|
|
46
|
+
return (_jsxs("a", { href: downloadUrl, download: true, className: buttonVariants({ variant: "outline", size: "sm" }), children: [_jsx(Download, { "aria-hidden": true }), "Download ", artifact.title] }));
|
|
47
|
+
}
|
|
82
48
|
case "embed":
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (blank) return /*#__PURE__*/ _jsx(ArtifactNotice, {
|
|
93
|
-
pending: artifact.pending
|
|
94
|
-
});
|
|
95
|
-
const downloadUrl = toSafeHref(artifact.downloadUrl);
|
|
96
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
97
|
-
className: "flex flex-col gap-3",
|
|
98
|
-
children: [
|
|
99
|
-
downloadUrl === undefined ? null : /*#__PURE__*/ _jsxs("a", {
|
|
100
|
-
href: downloadUrl,
|
|
101
|
-
download: true,
|
|
102
|
-
className: cn(buttonVariants({
|
|
103
|
-
variant: "outline",
|
|
104
|
-
size: "sm"
|
|
105
|
-
}), "self-start"),
|
|
106
|
-
children: [
|
|
107
|
-
/*#__PURE__*/ _jsx(Download, {
|
|
108
|
-
"aria-hidden": true
|
|
109
|
-
}),
|
|
110
|
-
"Download"
|
|
111
|
-
]
|
|
112
|
-
}),
|
|
113
|
-
/*#__PURE__*/ _jsx(CsvTable, {
|
|
114
|
-
text: content,
|
|
115
|
-
caption: artifact.title
|
|
116
|
-
})
|
|
117
|
-
]
|
|
118
|
-
});
|
|
119
|
-
}
|
|
49
|
+
return (_jsx(EmbedBody, { url: content.trim(), title: artifact.title, ...(artifact.downloadUrl === undefined
|
|
50
|
+
? {}
|
|
51
|
+
: { downloadUrl: artifact.downloadUrl }) }));
|
|
52
|
+
case "table": {
|
|
53
|
+
if (blank)
|
|
54
|
+
return _jsx(ArtifactNotice, { pending: artifact.pending });
|
|
55
|
+
const downloadUrl = toSafeHref(artifact.downloadUrl);
|
|
56
|
+
return (_jsxs("div", { className: "flex flex-col gap-3", children: [downloadUrl === undefined ? null : (_jsxs("a", { href: downloadUrl, download: true, className: cn(buttonVariants({ variant: "outline", size: "sm" }), "self-start"), children: [_jsx(Download, { "aria-hidden": true }), "Download"] })), _jsx(CsvTable, { text: content, caption: artifact.title })] }));
|
|
57
|
+
}
|
|
120
58
|
case "comparison":
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// still the artifact, so fall through to prose and let it be read.
|
|
135
|
-
if (parsed === null) return /*#__PURE__*/ _jsx(Prose, {
|
|
136
|
-
text: content
|
|
137
|
-
});
|
|
138
|
-
return /*#__PURE__*/ _jsx(ResearchBody, {
|
|
139
|
-
brief: parsed.brief,
|
|
140
|
-
...parsed.body === undefined ? {} : {
|
|
141
|
-
body: parsed.body
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
59
|
+
return (_jsx(CompareBodyFromJson, { content: content, ...(artifact.pending === undefined
|
|
60
|
+
? {}
|
|
61
|
+
: { pending: artifact.pending }) }));
|
|
62
|
+
case "research": {
|
|
63
|
+
if (blank)
|
|
64
|
+
return _jsx(ArtifactNotice, { pending: artifact.pending });
|
|
65
|
+
const parsed = parseResearchArtifact(content);
|
|
66
|
+
// Unparseable is not a failure worth an error card — the payload is
|
|
67
|
+
// still the artifact, so fall through to prose and let it be read.
|
|
68
|
+
if (parsed === null)
|
|
69
|
+
return _jsx(Prose, { text: content });
|
|
70
|
+
return (_jsx(ResearchBody, { brief: parsed.brief, ...(parsed.body === undefined ? {} : { body: parsed.body }) }));
|
|
71
|
+
}
|
|
145
72
|
case "prose":
|
|
146
|
-
if (blank)
|
|
147
|
-
pending: artifact.pending
|
|
148
|
-
});
|
|
149
|
-
return /*#__PURE__*/ _jsx(Prose, {
|
|
150
|
-
text: content
|
|
151
|
-
});
|
|
73
|
+
if (blank)
|
|
74
|
+
return _jsx(ArtifactNotice, { pending: artifact.pending });
|
|
75
|
+
return _jsx(Prose, { text: content });
|
|
152
76
|
}
|
|
153
77
|
}
|
|
154
78
|
}
|
|
@@ -162,9 +86,7 @@ import { ResearchBody, parseResearchArtifact } from "./research-body.js";
|
|
|
162
86
|
*
|
|
163
87
|
* `max-w-prose` caps the measure at roughly 65 characters. Full-bleed body text
|
|
164
88
|
* on a wide screen is unreadable regardless of how good the type is.
|
|
165
|
-
*/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
children: text
|
|
169
|
-
});
|
|
89
|
+
*/
|
|
90
|
+
function Prose({ text }) {
|
|
91
|
+
return (_jsx("div", { className: "max-w-prose text-sm leading-relaxed whitespace-pre-wrap", children: text }));
|
|
170
92
|
}
|
|
@@ -16,6 +16,6 @@ export declare function ArtifactNotice({
|
|
|
16
16
|
pending,
|
|
17
17
|
/** Overrides the pending/empty wording — use for a specific failure. */
|
|
18
18
|
message, }: {
|
|
19
|
-
readonly pending?: boolean;
|
|
19
|
+
readonly pending?: boolean | undefined;
|
|
20
20
|
readonly message?: string;
|
|
21
21
|
}): import("react").JSX.Element;
|
|
@@ -13,24 +13,17 @@ import { EmptyState } from "./empty-state.js";
|
|
|
13
13
|
* artifact means the producer has not finished writing; an empty payload on a
|
|
14
14
|
* settled one means there is genuinely nothing. Same markup, opposite message,
|
|
15
15
|
* and getting it backwards makes a working system look broken.
|
|
16
|
-
*/
|
|
16
|
+
*/
|
|
17
|
+
export function ArtifactNotice({
|
|
18
|
+
/** The producer is still writing. Changes the message, not the layout. */
|
|
19
|
+
pending = false,
|
|
20
|
+
/** Overrides the pending/empty wording — use for a specific failure. */
|
|
21
|
+
message, }) {
|
|
17
22
|
if (message !== undefined) {
|
|
18
|
-
return
|
|
19
|
-
icon: /*#__PURE__*/ _jsx(FileWarning, {}),
|
|
20
|
-
title: "Nothing to preview",
|
|
21
|
-
description: message
|
|
22
|
-
});
|
|
23
|
+
return (_jsx(EmptyState, { icon: _jsx(FileWarning, {}), title: "Nothing to preview", description: message }));
|
|
23
24
|
}
|
|
24
25
|
if (pending) {
|
|
25
|
-
return
|
|
26
|
-
icon: /*#__PURE__*/ _jsx(Loader2, {}),
|
|
27
|
-
title: "Still being produced",
|
|
28
|
-
description: "This artifact has no content yet — check back shortly."
|
|
29
|
-
});
|
|
26
|
+
return (_jsx(EmptyState, { icon: _jsx(Loader2, {}), title: "Still being produced", description: "This artifact has no content yet \u2014 check back shortly." }));
|
|
30
27
|
}
|
|
31
|
-
return
|
|
32
|
-
icon: /*#__PURE__*/ _jsx(FileWarning, {}),
|
|
33
|
-
title: "No content",
|
|
34
|
-
description: "This artifact is empty."
|
|
35
|
-
});
|
|
28
|
+
return (_jsx(EmptyState, { icon: _jsx(FileWarning, {}), title: "No content", description: "This artifact is empty." }));
|
|
36
29
|
}
|