@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
|
@@ -16,7 +16,8 @@ const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV ===
|
|
|
16
16
|
* logs a `console.error` in non-production builds, mirroring React's own
|
|
17
17
|
* controlled/uncontrolled `<input>` warning; it does not throw, and the
|
|
18
18
|
* production build is silent.
|
|
19
|
-
*/
|
|
19
|
+
*/
|
|
20
|
+
export function useControllableState(options) {
|
|
20
21
|
const { value: controlledValue, defaultValue, onChange, name } = options;
|
|
21
22
|
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
22
23
|
const isControlled = controlledValue !== undefined;
|
|
@@ -25,20 +26,27 @@ const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV ===
|
|
|
25
26
|
if (!isProduction && wasControlledRef.current !== isControlled) {
|
|
26
27
|
const from = wasControlledRef.current ? "controlled" : "uncontrolled";
|
|
27
28
|
const to = isControlled ? "controlled" : "uncontrolled";
|
|
28
|
-
console.error(`${name} is changing from ${from} to ${to}. Components should not switch between controlled ` +
|
|
29
|
+
console.error(`${name} is changing from ${from} to ${to}. Components should not switch between controlled ` +
|
|
30
|
+
"and uncontrolled across their lifetime. Decide between using a controlled or uncontrolled " +
|
|
31
|
+
`value for the lifetime of the component (i.e. always pass a value to \`${name}\`, or never do).`);
|
|
29
32
|
}
|
|
30
33
|
wasControlledRef.current = isControlled;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
isControlled
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
// Mirrors the uncontrolled state synchronously so batched functional updates
|
|
35
|
+
// chain off each other. Resolving inside a React updater instead would call
|
|
36
|
+
// `onChange` from that updater, which StrictMode invokes twice.
|
|
37
|
+
const latestUncontrolledRef = useRef(uncontrolledValue);
|
|
38
|
+
const setValue = useCallback((action) => {
|
|
39
|
+
const previous = isControlled
|
|
40
|
+
? controlledValue
|
|
41
|
+
: latestUncontrolledRef.current;
|
|
42
|
+
const next = typeof action === "function"
|
|
43
|
+
? action(previous)
|
|
44
|
+
: action;
|
|
45
|
+
if (!isControlled) {
|
|
46
|
+
latestUncontrolledRef.current = next;
|
|
47
|
+
setUncontrolledValue(next);
|
|
48
|
+
}
|
|
49
|
+
onChange?.(next);
|
|
50
|
+
}, [isControlled, controlledValue, onChange]);
|
|
51
|
+
return [value, setValue];
|
|
44
52
|
}
|
|
@@ -6,15 +6,14 @@ import { CHAT_DOCK_AUTOFOCUS_DELAY_MS } from "../ui/chat-dock-timing.js";
|
|
|
6
6
|
* still mid-animation, so the delay is deliberate rather than a magic number.
|
|
7
7
|
* `delayMs` defaults to `CHAT_DOCK_AUTOFOCUS_DELAY_MS`, which is pinned `>=`
|
|
8
8
|
* the dock's own entrance duration so focus never lands mid-animation.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
]);
|
|
9
|
+
*/
|
|
10
|
+
export function useDelayedAutofocus(ref, active, delayMs = CHAT_DOCK_AUTOFOCUS_DELAY_MS) {
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!active)
|
|
13
|
+
return;
|
|
14
|
+
const timer = window.setTimeout(() => ref.current?.focus(), delayMs);
|
|
15
|
+
return () => window.clearTimeout(timer);
|
|
16
|
+
// `ref` is intentionally excluded: it is a stable container, and including
|
|
17
|
+
// it would refocus on every render that happens to produce a new object.
|
|
18
|
+
}, [active, delayMs]);
|
|
20
19
|
}
|
|
@@ -2,8 +2,8 @@ import { type RefObject } from "react";
|
|
|
2
2
|
import { type ControllableStateAction } from "./use-controllable-state.js";
|
|
3
3
|
export type UseDismissablePopoverOptions = {
|
|
4
4
|
/** Controlled open flag. Supply this with `onOpenChange` to lift state to a parent. */
|
|
5
|
-
readonly open?: boolean;
|
|
6
|
-
readonly onOpenChange?: (open: boolean) => void;
|
|
5
|
+
readonly open?: boolean | undefined;
|
|
6
|
+
readonly onOpenChange?: ((open: boolean) => void) | undefined;
|
|
7
7
|
/** Seeds the hook's own state when `open` is not supplied. */
|
|
8
8
|
readonly defaultOpen?: boolean;
|
|
9
9
|
/** Escape closes the popover and returns focus to the trigger. Default `true`. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
import { useControllableState } from "./use-controllable-state.js";
|
|
2
|
+
import { useControllableState, } from "./use-controllable-state.js";
|
|
3
3
|
/**
|
|
4
4
|
* The behavior every dismissable popover trigger shares: an open flag, a
|
|
5
5
|
* pointer-down outside `rootRef` that closes it, and (by default) Escape that
|
|
@@ -16,49 +16,41 @@ import { useControllableState } from "./use-controllable-state.js";
|
|
|
16
16
|
* a caller switches between them after mount — is `useControllableState`;
|
|
17
17
|
* this hook only adds the popover-specific dismissal behavior on top.
|
|
18
18
|
* `setOpen` takes a value or an updater, same as `useState`.
|
|
19
|
-
*/
|
|
20
|
-
|
|
19
|
+
*/
|
|
20
|
+
export function useDismissablePopover(options = {}) {
|
|
21
|
+
const { open: controlledOpen, onOpenChange, defaultOpen = false, closeOnEscape = true, } = options;
|
|
21
22
|
const [open, setOpen] = useControllableState({
|
|
22
23
|
value: controlledOpen,
|
|
23
24
|
defaultValue: defaultOpen,
|
|
24
25
|
onChange: onOpenChange,
|
|
25
|
-
name: "useDismissablePopover"
|
|
26
|
+
name: "useDismissablePopover",
|
|
26
27
|
});
|
|
27
28
|
const rootRef = useRef(null);
|
|
28
29
|
const triggerRef = useRef(null);
|
|
29
|
-
const close = useCallback(()=>{
|
|
30
|
+
const close = useCallback(() => {
|
|
30
31
|
setOpen(false);
|
|
31
32
|
triggerRef.current?.focus();
|
|
32
|
-
}, [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (rootRef.current?.contains(event.target) === false)
|
|
33
|
+
}, [setOpen]);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!open)
|
|
36
|
+
return;
|
|
37
|
+
const onPointerDown = (event) => {
|
|
38
|
+
if (!(event.target instanceof Node))
|
|
39
|
+
return;
|
|
40
|
+
if (rootRef.current?.contains(event.target) === false)
|
|
41
|
+
setOpen(false);
|
|
40
42
|
};
|
|
41
|
-
const onKeyDown = (event)=>{
|
|
42
|
-
if (!closeOnEscape || event.key !== "Escape")
|
|
43
|
+
const onKeyDown = (event) => {
|
|
44
|
+
if (!closeOnEscape || event.key !== "Escape")
|
|
45
|
+
return;
|
|
43
46
|
close();
|
|
44
47
|
};
|
|
45
48
|
document.addEventListener("pointerdown", onPointerDown);
|
|
46
49
|
document.addEventListener("keydown", onKeyDown);
|
|
47
|
-
return ()=>{
|
|
50
|
+
return () => {
|
|
48
51
|
document.removeEventListener("pointerdown", onPointerDown);
|
|
49
52
|
document.removeEventListener("keydown", onKeyDown);
|
|
50
53
|
};
|
|
51
|
-
}, [
|
|
52
|
-
|
|
53
|
-
close,
|
|
54
|
-
setOpen,
|
|
55
|
-
closeOnEscape
|
|
56
|
-
]);
|
|
57
|
-
return {
|
|
58
|
-
open,
|
|
59
|
-
setOpen,
|
|
60
|
-
rootRef,
|
|
61
|
-
triggerRef,
|
|
62
|
-
close
|
|
63
|
-
};
|
|
54
|
+
}, [open, close, setOpen, closeOnEscape]);
|
|
55
|
+
return { open, setOpen, rootRef, triggerRef, close };
|
|
64
56
|
}
|
|
@@ -1,67 +1,47 @@
|
|
|
1
1
|
import { useCallback, useMemo, useReducer, useRef } from "react";
|
|
2
2
|
function reducer(state, action) {
|
|
3
|
-
switch(action.type){
|
|
3
|
+
switch (action.type) {
|
|
4
4
|
case "clear":
|
|
5
5
|
return {
|
|
6
6
|
selectedIds: new Set(),
|
|
7
7
|
anchorId: undefined,
|
|
8
|
-
baseSelection: new Set()
|
|
8
|
+
baseSelection: new Set(),
|
|
9
9
|
};
|
|
10
|
-
case "selectAll":
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
if (anchorIndex === -1 || targetIndex === -1) {
|
|
26
|
-
// The anchor or target scrolled out of the known set — fall back
|
|
27
|
-
// to a plain toggle rather than ranging over an index that no
|
|
28
|
-
// longer means anything.
|
|
29
|
-
const next = toggleOne(state.selectedIds, id);
|
|
30
|
-
return {
|
|
31
|
-
selectedIds: next,
|
|
32
|
-
anchorId: id,
|
|
33
|
-
baseSelection: next
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
const [start, end] = anchorIndex <= targetIndex ? [
|
|
37
|
-
anchorIndex,
|
|
38
|
-
targetIndex
|
|
39
|
-
] : [
|
|
40
|
-
targetIndex,
|
|
41
|
-
anchorIndex
|
|
42
|
-
];
|
|
43
|
-
const range = ids.slice(start, end + 1);
|
|
44
|
-
const selectedIds = new Set(state.baseSelection);
|
|
45
|
-
for (const rangeId of range)selectedIds.add(rangeId);
|
|
46
|
-
return {
|
|
47
|
-
...state,
|
|
48
|
-
selectedIds
|
|
49
|
-
};
|
|
10
|
+
case "selectAll": {
|
|
11
|
+
const all = new Set(action.ids);
|
|
12
|
+
return { selectedIds: all, anchorId: state.anchorId, baseSelection: all };
|
|
13
|
+
}
|
|
14
|
+
case "toggle": {
|
|
15
|
+
const { id, shiftKey, ids } = action;
|
|
16
|
+
if (shiftKey && state.anchorId !== undefined) {
|
|
17
|
+
const anchorIndex = ids.indexOf(state.anchorId);
|
|
18
|
+
const targetIndex = ids.indexOf(id);
|
|
19
|
+
if (anchorIndex === -1 || targetIndex === -1) {
|
|
20
|
+
// The anchor or target scrolled out of the known set — fall back
|
|
21
|
+
// to a plain toggle rather than ranging over an index that no
|
|
22
|
+
// longer means anything.
|
|
23
|
+
const next = toggleOne(state.selectedIds, id);
|
|
24
|
+
return { selectedIds: next, anchorId: id, baseSelection: next };
|
|
50
25
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
26
|
+
const [start, end] = anchorIndex <= targetIndex
|
|
27
|
+
? [anchorIndex, targetIndex]
|
|
28
|
+
: [targetIndex, anchorIndex];
|
|
29
|
+
const range = ids.slice(start, end + 1);
|
|
30
|
+
const selectedIds = new Set(state.baseSelection);
|
|
31
|
+
for (const rangeId of range)
|
|
32
|
+
selectedIds.add(rangeId);
|
|
33
|
+
return { ...state, selectedIds };
|
|
57
34
|
}
|
|
35
|
+
const next = toggleOne(state.selectedIds, id);
|
|
36
|
+
return { selectedIds: next, anchorId: id, baseSelection: next };
|
|
37
|
+
}
|
|
58
38
|
}
|
|
59
39
|
}
|
|
60
40
|
function createInitialState() {
|
|
61
41
|
return {
|
|
62
42
|
selectedIds: new Set(),
|
|
63
43
|
anchorId: undefined,
|
|
64
|
-
baseSelection: new Set()
|
|
44
|
+
baseSelection: new Set(),
|
|
65
45
|
};
|
|
66
46
|
}
|
|
67
47
|
/**
|
|
@@ -92,56 +72,45 @@ function createInitialState() {
|
|
|
92
72
|
* array each time (e.g. an inline `rows.map(row => row.id)`) — a memoized
|
|
93
73
|
* row downstream that depends on `toggle` does not re-render just because
|
|
94
74
|
* the caller didn't bother memoizing `ids`.
|
|
95
|
-
*/
|
|
96
|
-
|
|
75
|
+
*/
|
|
76
|
+
export function useListSelection({ ids, }) {
|
|
77
|
+
const [state, dispatch] = useReducer((reducer), undefined, (createInitialState));
|
|
97
78
|
// Updated on every render, read only inside event handlers — never during
|
|
98
79
|
// render — so `toggle`/`selectAll` can stay referentially stable while
|
|
99
80
|
// still acting on the `ids` most recently passed in.
|
|
100
81
|
const idsRef = useRef(ids);
|
|
101
82
|
idsRef.current = ids;
|
|
102
|
-
const toggle = useCallback((id, modifiers)=>dispatch({
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const selectAll = useCallback(()=>dispatch({
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}), []);
|
|
112
|
-
const clear = useCallback(()=>dispatch({
|
|
113
|
-
type: "clear"
|
|
114
|
-
}), []);
|
|
115
|
-
const selectedIds = useMemo(()=>{
|
|
83
|
+
const toggle = useCallback((id, modifiers) => dispatch({
|
|
84
|
+
type: "toggle",
|
|
85
|
+
id,
|
|
86
|
+
shiftKey: modifiers?.shiftKey ?? false,
|
|
87
|
+
ids: idsRef.current,
|
|
88
|
+
}), []);
|
|
89
|
+
const selectAll = useCallback(() => dispatch({ type: "selectAll", ids: idsRef.current }), []);
|
|
90
|
+
const clear = useCallback(() => dispatch({ type: "clear" }), []);
|
|
91
|
+
const selectedIds = useMemo(() => {
|
|
116
92
|
const known = new Set(ids);
|
|
117
93
|
const reconciled = new Set();
|
|
118
|
-
for (const id of state.selectedIds)
|
|
94
|
+
for (const id of state.selectedIds)
|
|
95
|
+
if (known.has(id))
|
|
96
|
+
reconciled.add(id);
|
|
119
97
|
return reconciled;
|
|
120
|
-
}, [
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
]);
|
|
124
|
-
const isSelected = useCallback((id)=>selectedIds.has(id), [
|
|
125
|
-
selectedIds
|
|
126
|
-
]);
|
|
127
|
-
return useMemo(()=>({
|
|
128
|
-
selectedIds,
|
|
129
|
-
selectedCount: selectedIds.size,
|
|
130
|
-
isSelected,
|
|
131
|
-
toggle,
|
|
132
|
-
selectAll,
|
|
133
|
-
clear
|
|
134
|
-
}), [
|
|
98
|
+
}, [state.selectedIds, ids]);
|
|
99
|
+
const isSelected = useCallback((id) => selectedIds.has(id), [selectedIds]);
|
|
100
|
+
return useMemo(() => ({
|
|
135
101
|
selectedIds,
|
|
102
|
+
selectedCount: selectedIds.size,
|
|
136
103
|
isSelected,
|
|
137
104
|
toggle,
|
|
138
105
|
selectAll,
|
|
139
|
-
clear
|
|
140
|
-
]);
|
|
106
|
+
clear,
|
|
107
|
+
}), [selectedIds, isSelected, toggle, selectAll, clear]);
|
|
141
108
|
}
|
|
142
109
|
function toggleOne(current, id) {
|
|
143
110
|
const next = new Set(current);
|
|
144
|
-
if (next.has(id))
|
|
145
|
-
|
|
111
|
+
if (next.has(id))
|
|
112
|
+
next.delete(id);
|
|
113
|
+
else
|
|
114
|
+
next.add(id);
|
|
146
115
|
return next;
|
|
147
116
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useSyncExternalStore } from "react";
|
|
2
2
|
const QUERY = "(prefers-reduced-motion: reduce)";
|
|
3
3
|
function subscribe(callback) {
|
|
4
|
-
if (typeof window === "undefined")
|
|
4
|
+
if (typeof window === "undefined")
|
|
5
|
+
return () => { };
|
|
5
6
|
const query = window.matchMedia(QUERY);
|
|
6
7
|
query.addEventListener("change", callback);
|
|
7
|
-
return ()=>query.removeEventListener("change", callback);
|
|
8
|
+
return () => query.removeEventListener("change", callback);
|
|
8
9
|
}
|
|
9
10
|
function getSnapshot() {
|
|
10
|
-
return typeof window === "undefined"
|
|
11
|
+
return typeof window === "undefined"
|
|
12
|
+
? false
|
|
13
|
+
: window.matchMedia(QUERY).matches;
|
|
11
14
|
}
|
|
12
15
|
function getServerSnapshot() {
|
|
13
16
|
return false;
|
|
@@ -23,6 +26,7 @@ function getServerSnapshot() {
|
|
|
23
26
|
* and `use-scroll-current-into-view` each read `matchMedia` by hand before
|
|
24
27
|
* this shipped; this is the one place that logic lives now, and — unlike
|
|
25
28
|
* those one-shot reads — it updates if the preference changes while mounted.
|
|
26
|
-
*/
|
|
29
|
+
*/
|
|
30
|
+
export function usePrefersReducedMotion() {
|
|
27
31
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
28
32
|
}
|
|
@@ -16,18 +16,16 @@ import { usePrefersReducedMotion } from "./use-prefers-reduced-motion.js";
|
|
|
16
16
|
* The scroll itself is smooth, but `scrollIntoView` isn't covered by the
|
|
17
17
|
* theme's global reduced-motion block, so a `prefers-reduced-motion: reduce`
|
|
18
18
|
* check here falls back to an instant jump.
|
|
19
|
-
*/
|
|
19
|
+
*/
|
|
20
|
+
export function useScrollCurrentIntoView(currentKey) {
|
|
20
21
|
const currentRef = useRef(null);
|
|
21
22
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
22
|
-
useEffect(()=>{
|
|
23
|
+
useEffect(() => {
|
|
23
24
|
currentRef.current?.scrollIntoView({
|
|
24
25
|
block: "nearest",
|
|
25
26
|
inline: "center",
|
|
26
|
-
behavior: prefersReducedMotion ? "auto" : "smooth"
|
|
27
|
+
behavior: prefersReducedMotion ? "auto" : "smooth",
|
|
27
28
|
});
|
|
28
|
-
}, [
|
|
29
|
-
currentKey,
|
|
30
|
-
prefersReducedMotion
|
|
31
|
-
]);
|
|
29
|
+
}, [currentKey, prefersReducedMotion]);
|
|
32
30
|
return currentRef;
|
|
33
31
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,15 +11,11 @@ export * from "./lib/artifact.js";
|
|
|
11
11
|
export * from "./lib/chart-palette.js";
|
|
12
12
|
export * from "./lib/chat-message.js";
|
|
13
13
|
export * from "./lib/chat-parts.js";
|
|
14
|
-
export * from "./lib/csv.js";
|
|
15
14
|
export * from "./lib/metrics.js";
|
|
16
15
|
export * from "./lib/now-item.js";
|
|
17
16
|
export * from "./lib/relative-time.js";
|
|
18
17
|
export * from "./lib/theme.js";
|
|
19
|
-
export * from "./lib/url.js";
|
|
20
|
-
export * from "./lib/utils.js";
|
|
21
18
|
export * from "./lib/workflow-display-flow.js";
|
|
22
|
-
export * from "./lib/workflow-registry.js";
|
|
23
19
|
export * from "./lib/workflow-run.js";
|
|
24
20
|
export * from "./lib/workflow-run-error.js";
|
|
25
21
|
export * from "./lib/workflow-run-progress.js";
|
|
@@ -39,17 +35,14 @@ export * from "./ui/card.js";
|
|
|
39
35
|
export * from "./ui/category-bars.js";
|
|
40
36
|
export * from "./ui/chart-frame.js";
|
|
41
37
|
export * from "./ui/chat-composer.js";
|
|
42
|
-
export * from "./ui/chat-dock-timing.js";
|
|
43
38
|
export * from "./ui/chat-input.js";
|
|
44
39
|
export * from "./ui/chat-panel.js";
|
|
45
40
|
export * from "./ui/chat-thread.js";
|
|
46
41
|
export * from "./ui/checkbox.js";
|
|
47
|
-
export * from "./ui/command-palette.js";
|
|
48
42
|
export * from "./ui/compare-body.js";
|
|
49
43
|
export * from "./ui/confirm-button.js";
|
|
50
44
|
export * from "./ui/corbits-mark.js";
|
|
51
45
|
export * from "./ui/csv-table.js";
|
|
52
|
-
export * from "./ui/dialog.js";
|
|
53
46
|
export * from "./ui/dictation-status-line.js";
|
|
54
47
|
export * from "./ui/dither-canvas.js";
|
|
55
48
|
export * from "./ui/embed-body.js";
|
|
@@ -67,10 +60,8 @@ export * from "./ui/library-page-header.js";
|
|
|
67
60
|
export * from "./ui/list-detail.js";
|
|
68
61
|
export * from "./ui/live-run-banner.js";
|
|
69
62
|
export * from "./ui/live-status-line.js";
|
|
70
|
-
export * from "./ui/menu.js";
|
|
71
63
|
export * from "./ui/message-bubble.js";
|
|
72
64
|
export * from "./ui/mic-button.js";
|
|
73
|
-
export * from "./ui/mic-permission-dialog.js";
|
|
74
65
|
export * from "./ui/notifications-bell.js";
|
|
75
66
|
export * from "./ui/now-cards.js";
|
|
76
67
|
export * from "./ui/page-panel.js";
|
|
@@ -109,12 +100,10 @@ export * from "./ui/thinking-indicator.js";
|
|
|
109
100
|
export * from "./ui/thinking-label.js";
|
|
110
101
|
export * from "./ui/thinking-mark.js";
|
|
111
102
|
export * from "./ui/time-series-chart.js";
|
|
112
|
-
export * from "./ui/toast.js";
|
|
113
103
|
export * from "./ui/token-mosaic.js";
|
|
114
104
|
export * from "./ui/tool-block.js";
|
|
115
105
|
export * from "./ui/tool-narrative.js";
|
|
116
106
|
export * from "./ui/tool-picker.js";
|
|
117
|
-
export * from "./ui/tooltip.js";
|
|
118
107
|
export * from "./ui/top-bar.js";
|
|
119
108
|
export * from "./ui/trace-waterfall.js";
|
|
120
109
|
export * from "./ui/typing-indicator.js";
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
//
|
|
1
|
+
// The public surface. Anything not re-exported here is internal.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
3
|
+
// Modules that statically import an optional peer are not re-exported: the
|
|
4
|
+
// barrel is one module, so any root import would load that peer. They have
|
|
5
|
+
// their own entries in the `exports` map instead:
|
|
6
|
+
// @corbits/react-ui/ui/command-palette
|
|
7
|
+
// @corbits/react-ui/ui/dialog
|
|
8
|
+
// @corbits/react-ui/ui/menu
|
|
9
|
+
// @corbits/react-ui/ui/mic-permission-dialog
|
|
10
|
+
// @corbits/react-ui/ui/toast
|
|
11
|
+
// @corbits/react-ui/ui/tooltip
|
|
10
12
|
export * from "./blocks/login/auth-layout.js";
|
|
11
13
|
export * from "./blocks/login/login-form.js";
|
|
12
14
|
export * from "./hooks/use-command-palette-navigation.js";
|
|
@@ -20,15 +22,11 @@ export * from "./lib/artifact.js";
|
|
|
20
22
|
export * from "./lib/chart-palette.js";
|
|
21
23
|
export * from "./lib/chat-message.js";
|
|
22
24
|
export * from "./lib/chat-parts.js";
|
|
23
|
-
export * from "./lib/csv.js";
|
|
24
25
|
export * from "./lib/metrics.js";
|
|
25
26
|
export * from "./lib/now-item.js";
|
|
26
27
|
export * from "./lib/relative-time.js";
|
|
27
28
|
export * from "./lib/theme.js";
|
|
28
|
-
export * from "./lib/url.js";
|
|
29
|
-
export * from "./lib/utils.js";
|
|
30
29
|
export * from "./lib/workflow-display-flow.js";
|
|
31
|
-
export * from "./lib/workflow-registry.js";
|
|
32
30
|
export * from "./lib/workflow-run.js";
|
|
33
31
|
export * from "./lib/workflow-run-error.js";
|
|
34
32
|
export * from "./lib/workflow-run-progress.js";
|
|
@@ -48,17 +46,14 @@ export * from "./ui/card.js";
|
|
|
48
46
|
export * from "./ui/category-bars.js";
|
|
49
47
|
export * from "./ui/chart-frame.js";
|
|
50
48
|
export * from "./ui/chat-composer.js";
|
|
51
|
-
export * from "./ui/chat-dock-timing.js";
|
|
52
49
|
export * from "./ui/chat-input.js";
|
|
53
50
|
export * from "./ui/chat-panel.js";
|
|
54
51
|
export * from "./ui/chat-thread.js";
|
|
55
52
|
export * from "./ui/checkbox.js";
|
|
56
|
-
export * from "./ui/command-palette.js";
|
|
57
53
|
export * from "./ui/compare-body.js";
|
|
58
54
|
export * from "./ui/confirm-button.js";
|
|
59
55
|
export * from "./ui/corbits-mark.js";
|
|
60
56
|
export * from "./ui/csv-table.js";
|
|
61
|
-
export * from "./ui/dialog.js";
|
|
62
57
|
export * from "./ui/dictation-status-line.js";
|
|
63
58
|
export * from "./ui/dither-canvas.js";
|
|
64
59
|
export * from "./ui/embed-body.js";
|
|
@@ -76,10 +71,8 @@ export * from "./ui/library-page-header.js";
|
|
|
76
71
|
export * from "./ui/list-detail.js";
|
|
77
72
|
export * from "./ui/live-run-banner.js";
|
|
78
73
|
export * from "./ui/live-status-line.js";
|
|
79
|
-
export * from "./ui/menu.js";
|
|
80
74
|
export * from "./ui/message-bubble.js";
|
|
81
75
|
export * from "./ui/mic-button.js";
|
|
82
|
-
export * from "./ui/mic-permission-dialog.js";
|
|
83
76
|
export * from "./ui/notifications-bell.js";
|
|
84
77
|
export * from "./ui/now-cards.js";
|
|
85
78
|
export * from "./ui/page-panel.js";
|
|
@@ -118,12 +111,10 @@ export * from "./ui/thinking-indicator.js";
|
|
|
118
111
|
export * from "./ui/thinking-label.js";
|
|
119
112
|
export * from "./ui/thinking-mark.js";
|
|
120
113
|
export * from "./ui/time-series-chart.js";
|
|
121
|
-
export * from "./ui/toast.js";
|
|
122
114
|
export * from "./ui/token-mosaic.js";
|
|
123
115
|
export * from "./ui/tool-block.js";
|
|
124
116
|
export * from "./ui/tool-narrative.js";
|
|
125
117
|
export * from "./ui/tool-picker.js";
|
|
126
|
-
export * from "./ui/tooltip.js";
|
|
127
118
|
export * from "./ui/top-bar.js";
|
|
128
119
|
export * from "./ui/trace-waterfall.js";
|
|
129
120
|
export * from "./ui/typing-indicator.js";
|
package/dist/lib/artifact.js
CHANGED
|
@@ -1,36 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* backend, so this is the projection a viewer actually needs — an identity, a
|
|
6
|
-
* name, a kind, a payload, and enough provenance to caption a tile. Map your
|
|
7
|
-
* own record onto it at the edge of your app and the whole artifacts family
|
|
8
|
-
* works unchanged.
|
|
9
|
-
*/ const FORM_BY_SUFFIX = [
|
|
10
|
-
[
|
|
11
|
-
/(^|-)csv(-|$)|(^|-)sheet(-|$)|(^|-)table(-|$)/,
|
|
12
|
-
"table"
|
|
13
|
-
],
|
|
14
|
-
[
|
|
15
|
-
/(^|-)comparison(-|$)|(^|-)compare(-|$)|^ab-/,
|
|
16
|
-
"comparison"
|
|
17
|
-
],
|
|
18
|
-
[
|
|
19
|
-
/(^|-)research(-|$)|(^|-)brief(-|$)/,
|
|
20
|
-
"research"
|
|
21
|
-
],
|
|
1
|
+
const FORM_BY_SUFFIX = [
|
|
2
|
+
[/(^|-)csv(-|$)|(^|-)sheet(-|$)|(^|-)table(-|$)/, "table"],
|
|
3
|
+
[/(^|-)comparison(-|$)|(^|-)compare(-|$)|^ab-/, "comparison"],
|
|
4
|
+
[/(^|-)research(-|$)|(^|-)brief(-|$)/, "research"],
|
|
22
5
|
[
|
|
23
6
|
/(^|-)deck(-|$)|(^|-)presentation(-|$)|(^|-)slides(-|$)|(^|-)embed(-|$)/,
|
|
24
|
-
"embed"
|
|
7
|
+
"embed",
|
|
25
8
|
],
|
|
26
|
-
[
|
|
27
|
-
|
|
28
|
-
"image"
|
|
29
|
-
],
|
|
30
|
-
[
|
|
31
|
-
/(^|-)file(-|$)|(^|-)upload(-|$)|(^|-)binary(-|$)/,
|
|
32
|
-
"download"
|
|
33
|
-
]
|
|
9
|
+
[/(^|-)image(-|$)|(^|-)photo(-|$)|(^|-)screenshot(-|$)/, "image"],
|
|
10
|
+
[/(^|-)file(-|$)|(^|-)upload(-|$)|(^|-)binary(-|$)/, "download"],
|
|
34
11
|
];
|
|
35
12
|
/**
|
|
36
13
|
* Guesses a form from a kind string, defaulting to prose.
|
|
@@ -46,10 +23,12 @@
|
|
|
46
23
|
* Prose is the default because it is the only form that cannot mislead: text
|
|
47
24
|
* rendered as text is always honest, whereas a non-tabular payload forced into
|
|
48
25
|
* a grid silently shifts cells into the wrong columns.
|
|
49
|
-
*/
|
|
26
|
+
*/
|
|
27
|
+
export function artifactForm(kind) {
|
|
50
28
|
const normalized = kind.trim().toLowerCase();
|
|
51
|
-
for (const [pattern, form] of FORM_BY_SUFFIX){
|
|
52
|
-
if (pattern.test(normalized))
|
|
29
|
+
for (const [pattern, form] of FORM_BY_SUFFIX) {
|
|
30
|
+
if (pattern.test(normalized))
|
|
31
|
+
return form;
|
|
53
32
|
}
|
|
54
33
|
return "prose";
|
|
55
34
|
}
|
|
@@ -60,8 +39,10 @@
|
|
|
60
39
|
* first time a producer ships a kind nobody told the UI about, and an unlabelled
|
|
61
40
|
* tile is worse than a mechanically-titled one. Pass your own labels through
|
|
62
41
|
* the `kindLabel` prop where a surface offers it if you need better copy.
|
|
63
|
-
*/
|
|
42
|
+
*/
|
|
43
|
+
export function artifactKindLabel(kind) {
|
|
64
44
|
const words = kind.trim().replace(/[_-]+/g, " ").replace(/\s+/g, " ");
|
|
65
|
-
if (words === "")
|
|
45
|
+
if (words === "")
|
|
46
|
+
return "Artifact";
|
|
66
47
|
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
67
48
|
}
|