@streamsai/ui 0.2.0 → 0.2.2
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/dist/components/ui/running-indicator.d.ts +5 -4
- package/dist/components/ui/running-indicator.js +7 -11
- package/dist/components/ui/status-dot.d.ts +9 -7
- package/dist/components/ui/status-dot.js +6 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/motion/config.d.ts +4 -1
- package/dist/motion/config.js +11 -7
- package/dist/motion/index.d.ts +1 -0
- package/dist/motion/index.js +1 -0
- package/dist/motion/running-loop.d.ts +14 -0
- package/dist/motion/running-loop.js +22 -0
- package/dist/styles/tokens.css +2 -0
- package/dist/styles/tokens.mjs +1 -0
- package/package.json +1 -1
|
@@ -8,10 +8,11 @@ interface RunningIndicatorProps {
|
|
|
8
8
|
* changes size, so a row's geometry is stable while it animates. Reduced motion renders the core
|
|
9
9
|
* static with no halo.
|
|
10
10
|
*
|
|
11
|
-
* The loops start imperatively
|
|
12
|
-
* `AnimatePresence initial={false}` (the leading slot of a NavItem row)
|
|
13
|
-
* that starts on mount, so a declarative loop sat still in rows while the
|
|
14
|
-
* filter chips.
|
|
11
|
+
* The loops start imperatively through `startRunningLoop`, not with `animate` keyframes on a
|
|
12
|
+
* `motion` element: inside an `AnimatePresence initial={false}` (the leading slot of a NavItem row)
|
|
13
|
+
* motion skips an animation that starts on mount, so a declarative loop sat still in rows while the
|
|
14
|
+
* same dot pulsed in the filter chips. `startRunningLoop` also puts the loop on the page clock, so
|
|
15
|
+
* every running dot blinks in step, whenever it mounted.
|
|
15
16
|
*/
|
|
16
17
|
declare function RunningIndicator({ label, className }: RunningIndicatorProps): import("react").JSX.Element;
|
|
17
18
|
export { RunningIndicator, type RunningIndicatorProps };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { runningIndicator } from '../../motion/config.js';
|
|
3
|
+
import { startRunningLoop, usePrefersReducedMotion } from '../../motion/index.js';
|
|
5
4
|
import { cn } from '../../lib/utils.js';
|
|
6
5
|
/**
|
|
7
6
|
* The "an agent is working" cue, the twin of iOS `RunningIndicator`: an 8px green core that
|
|
@@ -9,10 +8,11 @@ import { cn } from '../../lib/utils.js';
|
|
|
9
8
|
* changes size, so a row's geometry is stable while it animates. Reduced motion renders the core
|
|
10
9
|
* static with no halo.
|
|
11
10
|
*
|
|
12
|
-
* The loops start imperatively
|
|
13
|
-
* `AnimatePresence initial={false}` (the leading slot of a NavItem row)
|
|
14
|
-
* that starts on mount, so a declarative loop sat still in rows while the
|
|
15
|
-
* filter chips.
|
|
11
|
+
* The loops start imperatively through `startRunningLoop`, not with `animate` keyframes on a
|
|
12
|
+
* `motion` element: inside an `AnimatePresence initial={false}` (the leading slot of a NavItem row)
|
|
13
|
+
* motion skips an animation that starts on mount, so a declarative loop sat still in rows while the
|
|
14
|
+
* same dot pulsed in the filter chips. `startRunningLoop` also puts the loop on the page clock, so
|
|
15
|
+
* every running dot blinks in step, whenever it mounted.
|
|
16
16
|
*/
|
|
17
17
|
function RunningIndicator({ label, className }) {
|
|
18
18
|
const pulsing = !usePrefersReducedMotion();
|
|
@@ -23,11 +23,7 @@ function RunningIndicator({ label, className }) {
|
|
|
23
23
|
const halo = haloRef.current;
|
|
24
24
|
if (!pulsing || !core || !halo)
|
|
25
25
|
return;
|
|
26
|
-
|
|
27
|
-
animate(core, runningIndicator.core, runningIndicator.transition),
|
|
28
|
-
animate(halo, runningIndicator.halo, runningIndicator.transition),
|
|
29
|
-
];
|
|
30
|
-
return () => controls.forEach((control) => control.cancel());
|
|
26
|
+
return startRunningLoop({ core, halo });
|
|
31
27
|
}, [pulsing]);
|
|
32
28
|
return (_jsxs("span", { role: "img", "aria-label": label, "data-slot": "running-indicator", className: cn('relative inline-grid size-3 shrink-0 place-items-center', className), children: [pulsing ? (_jsx("span", { ref: haloRef, "aria-hidden": true, "data-slot": "running-indicator-halo", className: "absolute inset-0 rounded-full bg-home-running-halo" })) : null, _jsx("span", { ref: coreRef, "aria-hidden": true, "data-slot": "running-indicator-core", className: "relative size-2 rounded-full bg-home-running" })] }));
|
|
33
29
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { type VariantProps } from 'class-variance-authority';
|
|
2
2
|
declare const statusDotVariants: (props?: ({
|
|
3
|
-
status?: "
|
|
3
|
+
status?: "idle" | "done" | "stuck" | "attention" | "unread" | null | undefined;
|
|
4
4
|
size?: "sm" | "md" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
-
|
|
6
|
+
type RestingStatus = NonNullable<VariantProps<typeof statusDotVariants>['status']>;
|
|
7
|
+
interface StatusDotProps {
|
|
8
|
+
/** `working` is always the `RunningIndicator`; the other states are resting dots. */
|
|
9
|
+
status?: RestingStatus | 'working';
|
|
10
|
+
/** Dot diameter of a resting dot. Ignored by `working` and the `home` appearance. */
|
|
11
|
+
size?: VariantProps<typeof statusDotVariants>['size'];
|
|
7
12
|
label: string;
|
|
8
13
|
className?: string;
|
|
9
|
-
/**
|
|
10
|
-
* `home` is the mobile Home dot: an 8px core in a fixed 12px box so every state lines up.
|
|
11
|
-
* Working is the `RunningIndicator` (green, pulsing halo) instead of the purple fade.
|
|
12
|
-
*/
|
|
14
|
+
/** `home` is the mobile Home dot: an 8px core in a fixed 12px box so every state lines up. */
|
|
13
15
|
appearance?: 'default' | 'home';
|
|
14
16
|
}
|
|
15
|
-
declare function StatusDot({ status, size, label, className, appearance }: StatusDotProps): import("react").JSX.Element;
|
|
17
|
+
declare function StatusDot({ status, size, label, className, appearance, }: StatusDotProps): import("react").JSX.Element;
|
|
16
18
|
export { StatusDot, statusDotVariants, type StatusDotProps };
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from 'class-variance-authority';
|
|
3
|
-
import { motion } from '../../motion/index.js';
|
|
4
|
-
import { loops } from '../../motion/config.js';
|
|
5
3
|
import { cn } from '../../lib/utils.js';
|
|
6
4
|
import { RunningIndicator } from './running-indicator.js';
|
|
7
5
|
const statusDotVariants = cva('inline-block shrink-0 rounded-full', {
|
|
8
6
|
variants: {
|
|
9
7
|
status: {
|
|
10
8
|
idle: 'bg-status-idle',
|
|
11
|
-
working: 'bg-status-working',
|
|
12
9
|
done: 'bg-status-done',
|
|
13
10
|
stuck: 'bg-status-stuck',
|
|
14
11
|
attention: 'bg-warning',
|
|
@@ -18,12 +15,14 @@ const statusDotVariants = cva('inline-block shrink-0 rounded-full', {
|
|
|
18
15
|
},
|
|
19
16
|
defaultVariants: { status: 'idle', size: 'md' },
|
|
20
17
|
});
|
|
21
|
-
function StatusDot({ status, size, label, className, appearance = 'default' }) {
|
|
22
|
-
|
|
18
|
+
function StatusDot({ status = 'idle', size, label, className, appearance = 'default', }) {
|
|
19
|
+
// One running cue for every surface, so all of them blink in step (`startRunningLoop`).
|
|
20
|
+
if (status === 'working')
|
|
21
|
+
return _jsx(RunningIndicator, { label: label, className: className });
|
|
23
22
|
if (appearance === 'home') {
|
|
24
|
-
return
|
|
23
|
+
return _jsx(HomeStatusDot, { status: status, label: label, className: className });
|
|
25
24
|
}
|
|
26
|
-
return (_jsx(
|
|
25
|
+
return (_jsx("span", { role: "img", "aria-label": label, "data-slot": "status-dot", className: cn(statusDotVariants({ status, size }), className) }));
|
|
27
26
|
}
|
|
28
27
|
/** A resting Home dot, in the same 12px box as the `RunningIndicator` so rows line up. */
|
|
29
28
|
function HomeStatusDot({ status, label, className, }) {
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export * as Icons from './icons/index.js';
|
|
|
21
21
|
export type { Platform, PlatformProviderProps } from './platform/index.js';
|
|
22
22
|
export { PlatformProvider, usePlatform } from './platform/index.js';
|
|
23
23
|
export { AnimatePresence, LayoutGroup, animate, motion, useMotionValue, useReducedMotion, useTransform, type HTMLMotionProps, type MotionValue, type Transition, type Variants } from './motion/index.js';
|
|
24
|
-
export { duration, ease, spring, distance, scale, stagger, transitions, press, presets, loops, runningIndicator, loopAnimate, loopTransition, popup, sheet, slide, resize, reflow, edgeReveal, stepSwap, staggerDelay } from './motion/index.js';
|
|
24
|
+
export { duration, ease, spring, distance, scale, stagger, transitions, press, presets, loops, runningIndicator, startRunningLoop, loopAnimate, loopTransition, popup, sheet, slide, resize, reflow, edgeReveal, stepSwap, staggerDelay } from './motion/index.js';
|
|
25
25
|
export { MotionProvider, disableMotion, enableMotion, enablePresenceExits, disablePresenceExits, PageTransition, Collapse, AutoHeight, usePrefersReducedMotion, useAnyModalOpen, Swap, swapVariants, StaggerItem, type PageTransitionProps, type CollapseProps, type AutoHeightProps, type SwapProps, type SwapPresetName, type StaggerItemProps } from './motion/index.js';
|
|
26
26
|
export { AgentAvatar, type AgentPicture, type AgentPingoPicture, type AgentImagePicture, } from './components/ui/agent-avatar.js';
|
|
27
27
|
export { AGENT_AVATAR_SIZE, AGENT_AVATAR_RADIUS, AGENT_AVATAR_PX, type AgentAvatarSize, } from './components/ui/avatar-frame.js';
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ export { PlatformProvider, usePlatform } from './platform/index.js';
|
|
|
22
22
|
// prettier-ignore
|
|
23
23
|
export { AnimatePresence, LayoutGroup, animate, motion, useMotionValue, useReducedMotion, useTransform } from './motion/index.js';
|
|
24
24
|
// prettier-ignore
|
|
25
|
-
export { duration, ease, spring, distance, scale, stagger, transitions, press, presets, loops, runningIndicator, loopAnimate, loopTransition, popup, sheet, slide, resize, reflow, edgeReveal, stepSwap, staggerDelay } from './motion/index.js';
|
|
25
|
+
export { duration, ease, spring, distance, scale, stagger, transitions, press, presets, loops, runningIndicator, startRunningLoop, loopAnimate, loopTransition, popup, sheet, slide, resize, reflow, edgeReveal, stepSwap, staggerDelay } from './motion/index.js';
|
|
26
26
|
// prettier-ignore
|
|
27
27
|
export { MotionProvider, disableMotion, enableMotion, enablePresenceExits, disablePresenceExits, PageTransition, Collapse, AutoHeight, usePrefersReducedMotion, useAnyModalOpen, Swap, swapVariants, StaggerItem } from './motion/index.js';
|
|
28
28
|
// ── Components ───────────────────────────────────────────────────────────────
|
package/dist/motion/config.d.ts
CHANGED
|
@@ -758,7 +758,7 @@ export declare const loops: {
|
|
|
758
758
|
readonly duration: 1;
|
|
759
759
|
};
|
|
760
760
|
/**
|
|
761
|
-
* Skeleton
|
|
761
|
+
* Skeleton: smooth opacity float, slow enough to read as "still loading". Give it
|
|
762
762
|
* keyframes that come back to where they start (`[1, 0.4, 1]`): it loops rather than mirrors, so
|
|
763
763
|
* the browser runs it off the main thread (motion drives a mirrored loop in JS, every frame).
|
|
764
764
|
*/
|
|
@@ -802,6 +802,9 @@ export declare const runningIndicator: {
|
|
|
802
802
|
readonly halo: {
|
|
803
803
|
readonly opacity: [1, 0, 1];
|
|
804
804
|
};
|
|
805
|
+
readonly dot: {
|
|
806
|
+
readonly opacity: [1, 0.25, 1];
|
|
807
|
+
};
|
|
805
808
|
readonly transition: {
|
|
806
809
|
readonly repeat: number;
|
|
807
810
|
readonly repeatType: "loop";
|
package/dist/motion/config.js
CHANGED
|
@@ -364,7 +364,7 @@ export const loops = {
|
|
|
364
364
|
* the browser runs it (motion drives `rotate` in JS, every frame). */
|
|
365
365
|
spin: { repeat: Infinity, repeatType: 'loop', ease: 'linear', duration: 1 },
|
|
366
366
|
/**
|
|
367
|
-
* Skeleton
|
|
367
|
+
* Skeleton: smooth opacity float, slow enough to read as "still loading". Give it
|
|
368
368
|
* keyframes that come back to where they start (`[1, 0.4, 1]`): it loops rather than mirrors, so
|
|
369
369
|
* the browser runs it off the main thread (motion drives a mirrored loop in JS, every frame).
|
|
370
370
|
*/
|
|
@@ -387,17 +387,21 @@ export const loops = {
|
|
|
387
387
|
},
|
|
388
388
|
};
|
|
389
389
|
/**
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
390
|
+
* The one running loop, for every "an agent is working" dot in the product. `RunningIndicator`: the
|
|
391
|
+
* 8px core scales 0.88 to 1 and the 2px halo fades out and in, on the same 2s loop as the iOS dot
|
|
392
|
+
* (`RunningIndicator.swift`), cosine-like so both ends rest. `dot` is the same loop as a fade, for
|
|
393
|
+
* a dot too small to scale (the 6px phase dot of a run row). Start these only through
|
|
394
|
+
* `startRunningLoop` (`running-loop.ts`), which puts every loop on the same clock so dots mounted
|
|
395
|
+
* at different times still blink together. The core animates a whole `transform` string, not
|
|
396
|
+
* `scale`, so motion hands the loop to the Web Animations API and it keeps running on the
|
|
397
|
+
* compositor while a phone webview scrolls or its main thread is busy. Reduced motion stops the
|
|
398
|
+
* whole loop (the dot renders static, no halo), unlike `loops.pulse`, which carries on.
|
|
396
399
|
*/
|
|
397
400
|
const runningIndicatorEase = [0.37, 0, 0.63, 1];
|
|
398
401
|
export const runningIndicator = {
|
|
399
402
|
core: { transform: ['scale(1)', 'scale(0.88)', 'scale(1)'] },
|
|
400
403
|
halo: { opacity: [1, 0, 1] },
|
|
404
|
+
dot: { opacity: [1, 0.25, 1] },
|
|
401
405
|
transition: { repeat: Infinity, repeatType: 'loop', ease: runningIndicatorEase, duration: 2 },
|
|
402
406
|
};
|
|
403
407
|
/**
|
package/dist/motion/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { LayoutGroup, animate, motion, useMotionValue, useReducedMotion, useTransform, type HTMLMotionProps, type MotionValue, type Transition, type Variants, } from 'motion/react';
|
|
6
6
|
export { duration, ease, spring, distance, scale, stagger, transitions, press, presets, loops, runningIndicator, loopAnimate, loopTransition, popup, sheet, slide, resize, reflow, glide, edgeReveal, stepSwap, scrollReveal, staggerDelay, } from './config.js';
|
|
7
|
+
export { startRunningLoop } from './running-loop.js';
|
|
7
8
|
export { MotionProvider, disableMotion, enableMotion, enablePresenceExits, disablePresenceExits, AnimatePresence, PageTransition, Collapse, AutoHeight, usePrefersReducedMotion, Swap, swapVariants, StaggerItem, type PageTransitionProps, type CollapseProps, type AutoHeightProps, type SwapProps, type SwapPresetName, type StaggerItemProps, } from './components.js';
|
|
8
9
|
export { useAnyModalOpen } from './modal-presence.js';
|
package/dist/motion/index.js
CHANGED
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { LayoutGroup, animate, motion, useMotionValue, useReducedMotion, useTransform, } from 'motion/react';
|
|
6
6
|
export { duration, ease, spring, distance, scale, stagger, transitions, press, presets, loops, runningIndicator, loopAnimate, loopTransition, popup, sheet, slide, resize, reflow, glide, edgeReveal, stepSwap, scrollReveal, staggerDelay, } from './config.js';
|
|
7
|
+
export { startRunningLoop } from './running-loop.js';
|
|
7
8
|
export { MotionProvider, disableMotion, enableMotion, enablePresenceExits, disablePresenceExits, AnimatePresence, PageTransition, Collapse, AutoHeight, usePrefersReducedMotion, Swap, swapVariants, StaggerItem, } from './components.js';
|
|
8
9
|
export { useAnyModalOpen } from './modal-presence.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface RunningLoopTargets {
|
|
2
|
+
core?: Element | null;
|
|
3
|
+
halo?: Element | null;
|
|
4
|
+
dot?: Element | null;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Starts the running loop on the given elements and returns a function that stops it. Each loop is
|
|
8
|
+
* moved to the position the page clock gives it (time 0 is the start of the page), not the moment
|
|
9
|
+
* it mounted, so every running dot on screen shows the same frame at the same instant, however
|
|
10
|
+
* late or often it mounts. That is what the iOS dot gets from its wall-clock `TimelineView`.
|
|
11
|
+
* `performance.now()` and the document timeline share one origin, and jsdom has no timeline.
|
|
12
|
+
*/
|
|
13
|
+
export declare function startRunningLoop(targets: RunningLoopTargets): () => void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { animate } from 'motion/react';
|
|
2
|
+
import { runningIndicator } from './config.js';
|
|
3
|
+
const periodMs = runningIndicator.transition.duration * 1000;
|
|
4
|
+
/**
|
|
5
|
+
* Starts the running loop on the given elements and returns a function that stops it. Each loop is
|
|
6
|
+
* moved to the position the page clock gives it (time 0 is the start of the page), not the moment
|
|
7
|
+
* it mounted, so every running dot on screen shows the same frame at the same instant, however
|
|
8
|
+
* late or often it mounts. That is what the iOS dot gets from its wall-clock `TimelineView`.
|
|
9
|
+
* `performance.now()` and the document timeline share one origin, and jsdom has no timeline.
|
|
10
|
+
*/
|
|
11
|
+
export function startRunningLoop(targets) {
|
|
12
|
+
const { core, halo, dot } = targets;
|
|
13
|
+
const controls = [
|
|
14
|
+
core && animate(core, runningIndicator.core, runningIndicator.transition),
|
|
15
|
+
halo && animate(halo, runningIndicator.halo, runningIndicator.transition),
|
|
16
|
+
dot && animate(dot, runningIndicator.dot, runningIndicator.transition),
|
|
17
|
+
].filter((control) => control != null);
|
|
18
|
+
const seconds = (performance.now() % periodMs) / 1000;
|
|
19
|
+
for (const control of controls)
|
|
20
|
+
control.time = seconds;
|
|
21
|
+
return () => controls.forEach((control) => control.cancel());
|
|
22
|
+
}
|
package/dist/styles/tokens.css
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
--vibe-color-egg-yolk-on-selected: #5c4300; /* text on the solid yellow */
|
|
49
49
|
--vibe-color-winter: #9aadbd;
|
|
50
50
|
--vibe-color-running-yellow: #f7c55e;
|
|
51
|
+
--vibe-color-waiting-blue: #879ee7;
|
|
51
52
|
--vibe-color-running-halo: #fdeecf;
|
|
52
53
|
--vibe-color-running-green: #b7cf9f;
|
|
53
54
|
--vibe-color-running-green-halo: #e9f1e2;
|
|
@@ -94,6 +95,7 @@
|
|
|
94
95
|
--vibe-color-egg-yolk-on-selected: #5c4300;
|
|
95
96
|
--vibe-color-winter: #9aadbd;
|
|
96
97
|
--vibe-color-running-yellow: #f7c55e;
|
|
98
|
+
--vibe-color-waiting-blue: #879ee7;
|
|
97
99
|
--vibe-color-running-halo: #5f5237;
|
|
98
100
|
--vibe-color-running-green: #b7cf9f;
|
|
99
101
|
--vibe-color-running-green-halo: #4c554a;
|
package/dist/styles/tokens.mjs
CHANGED
|
@@ -34,6 +34,7 @@ export const colors = {
|
|
|
34
34
|
// Mobile Home (iOS Streams tab and mweb /home). Light values are sampled from the design PNG;
|
|
35
35
|
// dark values are derived from the dark theme until Design supplies them.
|
|
36
36
|
'color-running-yellow': { light: '#f7c55e', dark: '#f7c55e' }, // a Running session's dot
|
|
37
|
+
'color-waiting-blue': { light: '#879ee7', dark: '#879ee7' }, // a Waiting session's dot and the Waiting chip dot (iOS Home); dark awaits Design
|
|
37
38
|
'color-running-green': { light: '#b7cf9f', dark: '#b7cf9f' }, // mweb Running dot: the pastel green from Figma
|
|
38
39
|
'color-running-green-halo': { light: '#e9f1e2', dark: '#4c554a' }, // that dot at 30% over the card, composited
|
|
39
40
|
'color-running-halo': { light: '#fdeecf', dark: '#5f5237' }, // that dot at 30% over the card, composited
|
package/package.json
CHANGED