@corbits/react-ui 0.1.0 → 0.2.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 +11 -11
- package/dist/test/render-hook.js +11 -12
- package/dist/theme.css +51 -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 +1 -1
- package/dist/ui/avatar.js +11 -48
- 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 +71 -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 +94 -535
|
@@ -1,38 +1,53 @@
|
|
|
1
1
|
import { classifyRunError } from "./workflow-run-error.js";
|
|
2
|
-
/** Label colour for a step's status, shared by every stepper/sidebar rendering. */
|
|
3
|
-
|
|
4
|
-
if (status === "
|
|
2
|
+
/** Label colour for a step's status, shared by every stepper/sidebar rendering. */
|
|
3
|
+
export function workflowStepLabelClass(status) {
|
|
4
|
+
if (status === "current")
|
|
5
|
+
return "text-foreground";
|
|
6
|
+
if (status === "failed")
|
|
7
|
+
return "text-destructive";
|
|
5
8
|
return "text-muted-foreground";
|
|
6
9
|
}
|
|
7
|
-
/** Mark glyph for a step's status: a checkmark, an exclamation, or its number. */
|
|
8
|
-
|
|
9
|
-
if (status === "
|
|
10
|
+
/** Mark glyph for a step's status: a checkmark, an exclamation, or its number. */
|
|
11
|
+
export function workflowStepGlyph(status, number) {
|
|
12
|
+
if (status === "completed")
|
|
13
|
+
return "✓";
|
|
14
|
+
if (status === "failed")
|
|
15
|
+
return "!";
|
|
10
16
|
return String(number);
|
|
11
17
|
}
|
|
12
18
|
export function getStepPhase(progress, stepId) {
|
|
13
19
|
return progress?.steps.get(stepId)?.phase;
|
|
14
20
|
}
|
|
15
21
|
export function isStepRunning(phase) {
|
|
16
|
-
return phase === "in-flight" ||
|
|
22
|
+
return (phase === "in-flight" ||
|
|
23
|
+
phase === "awaiting-signal" ||
|
|
24
|
+
phase === "awaiting-timer");
|
|
17
25
|
}
|
|
18
|
-
/** The aggregate phase of a display step from its runtime steps. */
|
|
19
|
-
|
|
26
|
+
/** The aggregate phase of a display step from its runtime steps. */
|
|
27
|
+
export function displayStepPhase(progress, stepIds) {
|
|
28
|
+
if (stepIds.length === 0)
|
|
29
|
+
return undefined;
|
|
20
30
|
const terminal = stepIds[stepIds.length - 1];
|
|
21
|
-
if (terminal !== undefined &&
|
|
22
|
-
|
|
31
|
+
if (terminal !== undefined &&
|
|
32
|
+
getStepPhase(progress, terminal) === "completed")
|
|
33
|
+
return "completed";
|
|
34
|
+
for (const id of stepIds) {
|
|
23
35
|
const phase = getStepPhase(progress, id);
|
|
24
|
-
if (isStepRunning(phase))
|
|
36
|
+
if (isStepRunning(phase))
|
|
37
|
+
return phase;
|
|
25
38
|
}
|
|
26
|
-
for (const id of stepIds){
|
|
27
|
-
if (getStepPhase(progress, id) === "failed")
|
|
39
|
+
for (const id of stepIds) {
|
|
40
|
+
if (getStepPhase(progress, id) === "failed")
|
|
41
|
+
return "failed";
|
|
28
42
|
}
|
|
29
|
-
for (const id of stepIds){
|
|
30
|
-
if (getStepPhase(progress, id) !== undefined)
|
|
43
|
+
for (const id of stepIds) {
|
|
44
|
+
if (getStepPhase(progress, id) !== undefined)
|
|
45
|
+
return "in-flight";
|
|
31
46
|
}
|
|
32
47
|
return undefined;
|
|
33
48
|
}
|
|
34
49
|
function hasAnyProgress(progress, step) {
|
|
35
|
-
return step.stepIds.some((id)=>getStepPhase(progress, id) !== undefined);
|
|
50
|
+
return step.stepIds.some((id) => getStepPhase(progress, id) !== undefined);
|
|
36
51
|
}
|
|
37
52
|
/**
|
|
38
53
|
* Index of the display step the run is currently on. A step whose own phase
|
|
@@ -40,15 +55,22 @@ function hasAnyProgress(progress, step) {
|
|
|
40
55
|
* running branch has progressed — independent steps can run concurrently, so
|
|
41
56
|
* a later step's progress says nothing about whether this one failed.
|
|
42
57
|
* Returns the last index once every step is complete.
|
|
43
|
-
*/
|
|
44
|
-
|
|
58
|
+
*/
|
|
59
|
+
export function activeDisplayStepIndex(progress, steps) {
|
|
60
|
+
for (let i = 0; i < steps.length; i++) {
|
|
45
61
|
const step = steps[i];
|
|
46
|
-
if (step === undefined)
|
|
62
|
+
if (step === undefined)
|
|
63
|
+
continue;
|
|
47
64
|
const phase = displayStepPhase(progress, step.stepIds);
|
|
48
|
-
if (phase === "completed")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
if (phase === "completed")
|
|
66
|
+
continue;
|
|
67
|
+
if (phase === "failed")
|
|
68
|
+
return i;
|
|
69
|
+
const laterProgressed = steps
|
|
70
|
+
.slice(i + 1)
|
|
71
|
+
.some((later) => hasAnyProgress(progress, later));
|
|
72
|
+
if (laterProgressed)
|
|
73
|
+
continue;
|
|
52
74
|
return i;
|
|
53
75
|
}
|
|
54
76
|
return Math.max(0, steps.length - 1);
|
|
@@ -59,25 +81,30 @@ export function buildStepperSteps(progress, steps) {
|
|
|
59
81
|
// behind the run's own terminal phase, and without this it would linger
|
|
60
82
|
// as "current" on an already-finished run.
|
|
61
83
|
const runCompleted = progress?.phase === "completed";
|
|
62
|
-
return steps.map((step, i)=>{
|
|
84
|
+
return steps.map((step, i) => {
|
|
63
85
|
let status;
|
|
64
86
|
if (runCompleted || i < activeIdx) {
|
|
65
87
|
status = "completed";
|
|
66
|
-
}
|
|
88
|
+
}
|
|
89
|
+
else if (i > activeIdx) {
|
|
67
90
|
status = "pending";
|
|
68
|
-
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
69
93
|
const phase = displayStepPhase(progress, step.stepIds);
|
|
70
|
-
status =
|
|
94
|
+
status =
|
|
95
|
+
phase === "completed"
|
|
96
|
+
? "completed"
|
|
97
|
+
: phase === "failed"
|
|
98
|
+
? "failed"
|
|
99
|
+
: "current";
|
|
71
100
|
}
|
|
72
|
-
return {
|
|
73
|
-
number: i + 1,
|
|
74
|
-
label: step.label,
|
|
75
|
-
status
|
|
76
|
-
};
|
|
101
|
+
return { number: i + 1, label: step.label, status };
|
|
77
102
|
});
|
|
78
103
|
}
|
|
79
|
-
/** The active display step, or `null` while the run is terminal. */
|
|
80
|
-
|
|
104
|
+
/** The active display step, or `null` while the run is terminal. */
|
|
105
|
+
export function activeDisplayStep(progress, steps) {
|
|
106
|
+
if (steps.length === 0)
|
|
107
|
+
return null;
|
|
81
108
|
return steps[activeDisplayStepIndex(progress, steps)] ?? null;
|
|
82
109
|
}
|
|
83
110
|
/**
|
|
@@ -87,33 +114,45 @@ export function buildStepperSteps(progress, steps) {
|
|
|
87
114
|
* active group is done or parked on a human gate, or when the active step
|
|
88
115
|
* carries no `activityLabel` at all (a gate or intake step): silence there is
|
|
89
116
|
* correct, never a stepper noun standing in for it.
|
|
90
|
-
*/
|
|
91
|
-
|
|
92
|
-
if (progress
|
|
117
|
+
*/
|
|
118
|
+
export function liveStatusLabel(progress, steps) {
|
|
119
|
+
if (progress === null)
|
|
120
|
+
return null;
|
|
121
|
+
if (progress.phase === "failed" || progress.phase === "completed")
|
|
122
|
+
return null;
|
|
93
123
|
const idx = activeDisplayStepIndex(progress, steps);
|
|
94
|
-
if (idx === 0)
|
|
124
|
+
if (idx === 0)
|
|
125
|
+
return null;
|
|
95
126
|
const step = steps[idx];
|
|
96
|
-
if (step === undefined)
|
|
127
|
+
if (step === undefined)
|
|
128
|
+
return null;
|
|
97
129
|
const phase = displayStepPhase(progress, step.stepIds);
|
|
98
|
-
if (phase === "completed" || phase === "awaiting-signal")
|
|
130
|
+
if (phase === "completed" || phase === "awaiting-signal")
|
|
131
|
+
return null;
|
|
99
132
|
return step.activityLabel ?? null;
|
|
100
133
|
}
|
|
101
134
|
/** Label of the first display step whose aggregate phase is `failed`. Does
|
|
102
135
|
* not require `progress.phase === "failed"` — a caller flagging failure from
|
|
103
|
-
* a single failed step ahead of the run's own phase flip still gets a name. */
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
136
|
+
* a single failed step ahead of the run's own phase flip still gets a name. */
|
|
137
|
+
export function failedDisplayStepLabel(progress, steps) {
|
|
138
|
+
if (progress === null)
|
|
139
|
+
return null;
|
|
140
|
+
for (const step of steps) {
|
|
141
|
+
if (displayStepPhase(progress, step.stepIds) === "failed")
|
|
142
|
+
return step.label;
|
|
107
143
|
}
|
|
108
144
|
return null;
|
|
109
145
|
}
|
|
110
146
|
/** The sanitized end-user message from whichever step failed the run, or
|
|
111
147
|
* `null` if the run has not failed or no step carries an error. Uses the
|
|
112
148
|
* first error found in map-iteration order — a run design that attaches an
|
|
113
|
-
* error to more than one step at once is a design this cannot disambiguate. */
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
149
|
+
* error to more than one step at once is a design this cannot disambiguate. */
|
|
150
|
+
export function failedRunErrorMessage(progress) {
|
|
151
|
+
if (progress?.phase !== "failed")
|
|
152
|
+
return null;
|
|
153
|
+
for (const step of progress.steps.values()) {
|
|
154
|
+
if (step.lastErrorMessage !== undefined)
|
|
155
|
+
return classifyRunError(step.lastErrorMessage).userMessage;
|
|
117
156
|
}
|
|
118
157
|
return null;
|
|
119
158
|
}
|
|
@@ -124,12 +163,15 @@ export function buildStepperSteps(progress, steps) {
|
|
|
124
163
|
* (a legacy run, or a read error) that can synthesize the same empty-step
|
|
125
164
|
* shape regardless of phase — in that case whether it started is unknown, so
|
|
126
165
|
* this returns `false` rather than making a false "never started" claim.
|
|
127
|
-
*/
|
|
166
|
+
*/
|
|
167
|
+
export function runNeverStarted(progress, logRead) {
|
|
128
168
|
return progress !== null && logRead && progress.steps.size === 0;
|
|
129
169
|
}
|
|
130
170
|
/** Honest copy for the pre-interactive window before a run's first step has
|
|
131
171
|
* recorded any activity: queued (`pending`, or no progress yet) reads
|
|
132
|
-
* differently from a runtime already booting that first step. */
|
|
133
|
-
|
|
172
|
+
* differently from a runtime already booting that first step. */
|
|
173
|
+
export function runStartLabel(progress) {
|
|
174
|
+
if (progress === null || progress.phase === "pending")
|
|
175
|
+
return "Starting your workflow…";
|
|
134
176
|
return "Preparing your workflow…";
|
|
135
177
|
}
|
package/dist/lib/workflow-run.js
CHANGED
|
@@ -4,35 +4,39 @@ export const RUN_STATUS_LABEL = {
|
|
|
4
4
|
awaiting: "Needs you",
|
|
5
5
|
completed: "Completed",
|
|
6
6
|
failed: "Failed",
|
|
7
|
-
stopped: "Stopped"
|
|
7
|
+
stopped: "Stopped",
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
10
|
* State is the only colour on these surfaces. `awaiting` is the one orange:
|
|
11
11
|
* it is the single status a person can act on, and orange is the registry's
|
|
12
12
|
* "this is for you" tone.
|
|
13
|
-
*/
|
|
13
|
+
*/
|
|
14
|
+
export const RUN_STATUS_TONE = {
|
|
14
15
|
provisioning: "neutral",
|
|
15
16
|
running: "info",
|
|
16
17
|
awaiting: "accent",
|
|
17
18
|
completed: "success",
|
|
18
19
|
failed: "danger",
|
|
19
|
-
stopped: "neutral"
|
|
20
|
+
stopped: "neutral",
|
|
20
21
|
};
|
|
21
22
|
/**
|
|
22
23
|
* The dot only marks liveness and failure — the badge above carries the full
|
|
23
24
|
* status colour. See `status-dot.tsx` for why it is three tones and not six.
|
|
24
|
-
*/
|
|
25
|
+
*/
|
|
26
|
+
export const RUN_STATUS_DOT_TONE = {
|
|
25
27
|
provisioning: "neutral",
|
|
26
28
|
running: "emphasis",
|
|
27
29
|
awaiting: "emphasis",
|
|
28
30
|
completed: "neutral",
|
|
29
31
|
failed: "danger",
|
|
30
|
-
stopped: "neutral"
|
|
32
|
+
stopped: "neutral",
|
|
31
33
|
};
|
|
32
|
-
/** A run that will not change again without someone starting a new one. */
|
|
34
|
+
/** A run that will not change again without someone starting a new one. */
|
|
35
|
+
export function isTerminalRun(status) {
|
|
33
36
|
return status === "completed" || status === "failed" || status === "stopped";
|
|
34
37
|
}
|
|
35
|
-
/** Still moving or still asking — what an "active runs" strip shows. */
|
|
38
|
+
/** Still moving or still asking — what an "active runs" strip shows. */
|
|
39
|
+
export function isActiveRun(status) {
|
|
36
40
|
return !isTerminalRun(status);
|
|
37
41
|
}
|
|
38
42
|
// Needs-you first, then in-flight, then whatever finished. Failed sorts above
|
|
@@ -43,14 +47,14 @@ const ATTENTION_RANK = {
|
|
|
43
47
|
provisioning: 1,
|
|
44
48
|
failed: 2,
|
|
45
49
|
completed: 3,
|
|
46
|
-
stopped: 4
|
|
50
|
+
stopped: 4,
|
|
47
51
|
};
|
|
48
|
-
/** Attention order, newest first within a band. Sorts a copy. */
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
].sort((a, b)=>{
|
|
52
|
+
/** Attention order, newest first within a band. Sorts a copy. */
|
|
53
|
+
export function sortRunsByAttention(runs) {
|
|
54
|
+
return [...runs].sort((a, b) => {
|
|
52
55
|
const byAttention = ATTENTION_RANK[a.status] - ATTENTION_RANK[b.status];
|
|
53
|
-
if (byAttention !== 0)
|
|
56
|
+
if (byAttention !== 0)
|
|
57
|
+
return byAttention;
|
|
54
58
|
return Date.parse(b.startedAt) - Date.parse(a.startedAt);
|
|
55
59
|
});
|
|
56
60
|
}
|
package/dist/styles.css
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
--font-sans: "Red Hat Display", "Open Sans", Roboto, Arial, sans-serif;
|
|
7
7
|
--font-mono: "Space Mono", "Fira Code", Monaco, Consolas, monospace;
|
|
8
8
|
--color-black: #000;
|
|
9
|
-
--color-white: #fff;
|
|
10
9
|
--spacing: 0.25rem;
|
|
11
10
|
--container-xs: 20rem;
|
|
12
11
|
--container-sm: 24rem;
|
|
@@ -1169,9 +1168,6 @@
|
|
|
1169
1168
|
.bg-background {
|
|
1170
1169
|
background-color: var(--background);
|
|
1171
1170
|
}
|
|
1172
|
-
.bg-black {
|
|
1173
|
-
background-color: var(--color-black);
|
|
1174
|
-
}
|
|
1175
1171
|
.bg-black\/50 {
|
|
1176
1172
|
background-color: color-mix(in srgb, #000 50%, transparent);
|
|
1177
1173
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -1718,12 +1714,12 @@
|
|
|
1718
1714
|
.text-warn {
|
|
1719
1715
|
color: var(--warn);
|
|
1720
1716
|
}
|
|
1721
|
-
.text-white {
|
|
1722
|
-
color: var(--color-white);
|
|
1723
|
-
}
|
|
1724
1717
|
.capitalize {
|
|
1725
1718
|
text-transform: capitalize;
|
|
1726
1719
|
}
|
|
1720
|
+
.lowercase {
|
|
1721
|
+
text-transform: lowercase;
|
|
1722
|
+
}
|
|
1727
1723
|
.uppercase {
|
|
1728
1724
|
text-transform: uppercase;
|
|
1729
1725
|
}
|
|
@@ -2506,7 +2502,11 @@
|
|
|
2506
2502
|
--sidebar-row-radius: var(--radius);
|
|
2507
2503
|
--sidebar-row-selected-bg: var(--primary);
|
|
2508
2504
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2509
|
-
--sidebar-row-selected-bg: color-mix(
|
|
2505
|
+
--sidebar-row-selected-bg: color-mix(
|
|
2506
|
+
in srgb,
|
|
2507
|
+
var(--primary) 10%,
|
|
2508
|
+
transparent
|
|
2509
|
+
);
|
|
2510
2510
|
}
|
|
2511
2511
|
}
|
|
2512
2512
|
@keyframes corbits-fade-in {
|
|
@@ -2709,14 +2709,14 @@
|
|
|
2709
2709
|
}
|
|
2710
2710
|
@keyframes corbits-thinking-wave {
|
|
2711
2711
|
0% {
|
|
2712
|
-
transform: scale(.03);
|
|
2712
|
+
transform: scale(0.03);
|
|
2713
2713
|
opacity: 0;
|
|
2714
2714
|
}
|
|
2715
2715
|
10% {
|
|
2716
2716
|
opacity: 1;
|
|
2717
2717
|
}
|
|
2718
2718
|
75% {
|
|
2719
|
-
opacity: .9;
|
|
2719
|
+
opacity: 0.9;
|
|
2720
2720
|
}
|
|
2721
2721
|
100% {
|
|
2722
2722
|
transform: scale(1.35);
|
|
@@ -2734,7 +2734,7 @@
|
|
|
2734
2734
|
transform: translateY(30px);
|
|
2735
2735
|
}
|
|
2736
2736
|
.corbits-thinking-echo {
|
|
2737
|
-
transform: scale(.6);
|
|
2737
|
+
transform: scale(0.6);
|
|
2738
2738
|
}
|
|
2739
2739
|
}
|
|
2740
2740
|
@property --tw-translate-x {
|
package/dist/test/render-hook.js
CHANGED
|
@@ -6,33 +6,32 @@ import { createRoot } from "react-dom/client";
|
|
|
6
6
|
* `callback` and stashes its return value, re-render it on demand, and clean
|
|
7
7
|
* up. No `@testing-library` dependency — `act` plus `react-dom/client` cover
|
|
8
8
|
* everything a hook test in this package needs.
|
|
9
|
-
*/
|
|
9
|
+
*/
|
|
10
|
+
export function renderHook(callback) {
|
|
10
11
|
const container = document.createElement("div");
|
|
11
12
|
document.body.appendChild(container);
|
|
12
13
|
let root;
|
|
13
|
-
const resultBox = {
|
|
14
|
-
current: undefined
|
|
15
|
-
};
|
|
14
|
+
const resultBox = { current: undefined };
|
|
16
15
|
function Probe() {
|
|
17
16
|
resultBox.current = callback();
|
|
18
17
|
return null;
|
|
19
18
|
}
|
|
20
|
-
act(()=>{
|
|
19
|
+
act(() => {
|
|
21
20
|
root = createRoot(container);
|
|
22
|
-
root.render(
|
|
21
|
+
root.render(_jsx(Probe, {}));
|
|
23
22
|
});
|
|
24
23
|
return {
|
|
25
24
|
result: resultBox,
|
|
26
|
-
rerender
|
|
27
|
-
act(()=>{
|
|
28
|
-
root.render(
|
|
25
|
+
rerender() {
|
|
26
|
+
act(() => {
|
|
27
|
+
root.render(_jsx(Probe, {}));
|
|
29
28
|
});
|
|
30
29
|
},
|
|
31
|
-
unmount
|
|
32
|
-
act(()=>{
|
|
30
|
+
unmount() {
|
|
31
|
+
act(() => {
|
|
33
32
|
root.unmount();
|
|
34
33
|
});
|
|
35
34
|
container.remove();
|
|
36
|
-
}
|
|
35
|
+
},
|
|
37
36
|
};
|
|
38
37
|
}
|
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 {
|
|
@@ -603,26 +607,59 @@
|
|
|
603
607
|
|
|
604
608
|
/* Ridge-study motion: keep geometry and timing in the original 100 x 70 space. */
|
|
605
609
|
@keyframes corbits-thinking-sweep {
|
|
606
|
-
0% {
|
|
607
|
-
|
|
608
|
-
|
|
610
|
+
0% {
|
|
611
|
+
transform: translateX(8px);
|
|
612
|
+
opacity: 0;
|
|
613
|
+
}
|
|
614
|
+
8%,
|
|
615
|
+
86% {
|
|
616
|
+
opacity: 1;
|
|
617
|
+
}
|
|
618
|
+
100% {
|
|
619
|
+
transform: translateX(130px);
|
|
620
|
+
opacity: 0;
|
|
621
|
+
}
|
|
609
622
|
}
|
|
610
623
|
@keyframes corbits-thinking-rise {
|
|
611
|
-
0% {
|
|
612
|
-
|
|
624
|
+
0% {
|
|
625
|
+
transform: translateY(88px);
|
|
626
|
+
}
|
|
627
|
+
100% {
|
|
628
|
+
transform: translateY(-30px);
|
|
629
|
+
}
|
|
613
630
|
}
|
|
614
631
|
@keyframes corbits-thinking-wave {
|
|
615
|
-
0% {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
632
|
+
0% {
|
|
633
|
+
transform: scale(0.03);
|
|
634
|
+
opacity: 0;
|
|
635
|
+
}
|
|
636
|
+
10% {
|
|
637
|
+
opacity: 1;
|
|
638
|
+
}
|
|
639
|
+
75% {
|
|
640
|
+
opacity: 0.9;
|
|
641
|
+
}
|
|
642
|
+
100% {
|
|
643
|
+
transform: scale(1.35);
|
|
644
|
+
opacity: 0;
|
|
645
|
+
}
|
|
619
646
|
}
|
|
620
647
|
@media (prefers-reduced-motion: reduce) {
|
|
621
648
|
/* Static poses land the gradient mid-stroke. These offsets mirror the SVG
|
|
622
649
|
geometry in thinking-mark.tsx — change the rect/circle geometry there and
|
|
623
650
|
these poses have to be re-picked to match. */
|
|
624
|
-
.corbits-thinking-silk,
|
|
625
|
-
.corbits-thinking-
|
|
626
|
-
.corbits-thinking-
|
|
627
|
-
|
|
651
|
+
.corbits-thinking-silk,
|
|
652
|
+
.corbits-thinking-strata,
|
|
653
|
+
.corbits-thinking-echo {
|
|
654
|
+
animation: none;
|
|
655
|
+
}
|
|
656
|
+
.corbits-thinking-silk {
|
|
657
|
+
transform: translateX(44px);
|
|
658
|
+
}
|
|
659
|
+
.corbits-thinking-strata {
|
|
660
|
+
transform: translateY(30px);
|
|
661
|
+
}
|
|
662
|
+
.corbits-thinking-echo {
|
|
663
|
+
transform: scale(0.6);
|
|
664
|
+
}
|
|
628
665
|
}
|
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
|
}
|