@sero-ai/ui 0.4.2 → 0.6.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/.turbo/turbo-build.log +636 -628
- package/.turbo/turbo-test.log +12 -11
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +20 -2
- package/dist/components/ai-elements/conversation-virtual.cjs +115 -0
- package/dist/components/ai-elements/conversation-virtual.d.cts +30 -0
- package/dist/components/ai-elements/conversation-virtual.d.ts +30 -0
- package/dist/components/ai-elements/conversation-virtual.js +115 -0
- package/dist/components/model-selection/available-model-picker.cjs +21 -77
- package/dist/components/model-selection/available-model-picker.js +22 -78
- package/dist/components/model-selection/model-picker-body.cjs +106 -0
- package/dist/components/model-selection/model-picker-body.d.cts +41 -0
- package/dist/components/model-selection/model-picker-body.d.ts +41 -0
- package/dist/components/model-selection/model-picker-body.js +106 -0
- package/dist/components/model-selection/thinking-level-picker.cjs +7 -3
- package/dist/components/model-selection/thinking-level-picker.js +7 -3
- package/dist/components/model-selection/thinking-picker.cjs +1 -1
- package/dist/components/model-selection/thinking-picker.js +1 -1
- package/dist/components/ui/calendar.cjs +1 -1
- package/dist/components/ui/calendar.js +1 -1
- package/dist/components/ui/combobox.cjs +1 -1
- package/dist/components/ui/combobox.js +1 -1
- package/dist/components/ui/form.d.cts +2 -2
- package/dist/components/ui/form.d.ts +2 -2
- package/dist/components/ui/input-group.cjs +1 -1
- package/dist/components/ui/input-group.js +1 -1
- package/dist/components/ui/input-otp.cjs +1 -1
- package/dist/components/ui/input-otp.js +1 -1
- package/dist/components/ui/input.cjs +3 -3
- package/dist/components/ui/input.js +3 -3
- package/dist/components/ui/native-select.cjs +2 -2
- package/dist/components/ui/native-select.js +2 -2
- package/dist/components/ui/select.cjs +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/slider.cjs +6 -0
- package/dist/components/ui/slider.d.cts +1 -1
- package/dist/components/ui/slider.d.ts +1 -1
- package/dist/components/ui/slider.js +6 -0
- package/dist/components/ui/spinner.d.cts +2 -1
- package/dist/components/ui/spinner.d.ts +2 -1
- package/dist/components/ui/textarea.cjs +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/index.cjs +0 -55
- package/dist/index.d.cts +2 -66
- package/dist/index.d.ts +2 -66
- package/dist/index.js +0 -55
- package/dist/reference.cjs +56 -83
- package/dist/reference.js +56 -83
- package/dist/styles/ai-elements.css +2 -0
- package/dist/styles/context-editor.css +2 -0
- package/dist/styles/globals.css +32 -0
- package/dist/styles/model-selection.css +2 -0
- package/dist/styles/plugin.css +4 -1
- package/dist/theme/apply-theme.cjs +65 -12
- package/dist/theme/apply-theme.js +55 -2
- package/dist/theme/index.d.cts +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/types.cjs +15 -2
- package/dist/theme/types.d.cts +21 -1
- package/dist/theme/types.d.ts +21 -1
- package/dist/theme/types.js +14 -1
- package/package.json +44 -30
- package/scripts/smoke-dist.mjs +106 -4
- package/src/components/ai-elements/conversation-virtual.test.tsx +126 -0
- package/src/components/ai-elements/conversation-virtual.tsx +162 -0
- package/src/components/model-selection/available-model-picker.tsx +26 -98
- package/src/components/model-selection/model-picker-body.test.tsx +152 -0
- package/src/components/model-selection/model-picker-body.tsx +188 -0
- package/src/components/model-selection/thinking-level-picker.tsx +9 -3
- package/src/components/model-selection/thinking-picker.test.tsx +41 -0
- package/src/components/model-selection/thinking-picker.tsx +1 -1
- package/src/components/ui/calendar.test.tsx +43 -0
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/combobox.tsx +1 -1
- package/src/components/ui/input-group.tsx +1 -1
- package/src/components/ui/input-otp.tsx +1 -1
- package/src/components/ui/input.tsx +3 -3
- package/src/components/ui/native-select.tsx +2 -2
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/slider.tsx +6 -0
- package/src/components/ui/spinner.tsx +1 -1
- package/src/components/ui/textarea.tsx +1 -1
- package/src/index.ts +4 -63
- package/src/styles/ai-elements.css +2 -0
- package/src/styles/context-editor.css +2 -0
- package/src/styles/globals.css +32 -0
- package/src/styles/model-selection.css +2 -0
- package/src/styles/plugin.css +4 -1
- package/src/theme/apply-theme.test.ts +126 -0
- package/src/theme/apply-theme.ts +66 -3
- package/src/theme/types.ts +33 -0
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
|
|
2
|
-
> @sero-ai/ui@0.
|
|
2
|
+
> @sero-ai/ui@0.5.0 test /Users/danielcarter/Documents/Dev/projects/sero/sero/packages/ui
|
|
3
3
|
> vitest run
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
RUN v4.1.
|
|
6
|
+
RUN v4.1.10 /Users/danielcarter/Documents/Dev/projects/sero/sero/packages/ui
|
|
7
7
|
|
|
8
|
-
✓ src/plugin-style-scope.test.tsx (1 test)
|
|
9
|
-
✓ src/components/dashboard/__tests__/
|
|
10
|
-
✓ src/components/dashboard/__tests__/
|
|
11
|
-
✓ src/components/dashboard/__tests__/
|
|
12
|
-
✓ src/components/dashboard/__tests__/
|
|
8
|
+
✓ src/plugin-style-scope.test.tsx (1 test) 19ms
|
|
9
|
+
✓ src/components/dashboard/__tests__/variants.test.tsx (13 tests) 36ms
|
|
10
|
+
✓ src/components/dashboard/__tests__/glass.test.tsx (4 tests) 11ms
|
|
11
|
+
✓ src/components/dashboard/__tests__/state.test.tsx (14 tests) 64ms
|
|
12
|
+
✓ src/components/dashboard/__tests__/catalog.test.ts (5 tests) 7ms
|
|
13
|
+
✓ src/components/model-selection/thinking-picker.test.tsx (2 tests) 32ms
|
|
13
14
|
|
|
14
|
-
Test Files
|
|
15
|
-
Tests
|
|
16
|
-
Start at
|
|
17
|
-
Duration
|
|
15
|
+
Test Files 6 passed (6)
|
|
16
|
+
Tests 39 passed (39)
|
|
17
|
+
Start at 22:43:26
|
|
18
|
+
Duration 1.83s (transform 862ms, setup 0ms, import 3.20s, tests 169ms, environment 5.28s)
|
|
18
19
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @sero-ai/ui changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- AI elements and model controls are no longer exported from the package root.
|
|
8
|
+
Migrate them to `@sero-ai/ui/ai-elements/*` and
|
|
9
|
+
`@sero-ai/ui/model-selection/*`. Plugins using those component families must
|
|
10
|
+
also import the matching specialized stylesheet after `styles/plugin.css`.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Root imports now expose primitives, dashboard components, hooks and theme
|
|
15
|
+
utilities without traversing specialized dependency graphs, so a plugin
|
|
16
|
+
importing `Button` and `cn` no longer emits Mermaid, Shiki or graph-rendering
|
|
17
|
+
assets or loads their transitive types. Specialized plugin styles opt in through
|
|
18
|
+
`styles/ai-elements.css`, `styles/model-selection.css`, and
|
|
19
|
+
`styles/context-editor.css`.
|
|
20
|
+
|
|
3
21
|
## 0.4.1
|
|
4
22
|
|
|
5
23
|
### Fixed
|
|
@@ -54,5 +72,5 @@
|
|
|
54
72
|
worked examples, importable to preview or copy-paste. (Replaces the removed
|
|
55
73
|
`sero-showcase-plugin`.)
|
|
56
74
|
|
|
57
|
-
See `docs/
|
|
58
|
-
`
|
|
75
|
+
See `apps/docs-site/docs/guide/dashboard-widgets.md` and
|
|
76
|
+
`apps/docs-site/docs/reference/dashboard-components.md` for authoring guidance.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
|
|
2
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
3
|
+
var _react = require('react');
|
|
4
|
+
var _reactvirtual = require('@tanstack/react-virtual');
|
|
5
|
+
var _usesticktobottom = require('use-stick-to-bottom');
|
|
6
|
+
var _utils = require('../../lib/utils');
|
|
7
|
+
function ConversationVirtualList({
|
|
8
|
+
items,
|
|
9
|
+
getItemKey,
|
|
10
|
+
renderItem,
|
|
11
|
+
onReachStart,
|
|
12
|
+
startThreshold = 240,
|
|
13
|
+
estimateSize = 96,
|
|
14
|
+
initialScrollToEnd = false,
|
|
15
|
+
overscan = 6,
|
|
16
|
+
rowClassName,
|
|
17
|
+
className
|
|
18
|
+
}) {
|
|
19
|
+
const { scrollRef, state: scrollState } = _usesticktobottom.useStickToBottomContext.call(void 0, );
|
|
20
|
+
const [initialPositioned, setInitialPositioned] = _react.useState.call(void 0, !initialScrollToEnd);
|
|
21
|
+
const [scrollElement, setScrollElement] = _react.useState.call(void 0, null);
|
|
22
|
+
_react.useEffect.call(void 0, () => {
|
|
23
|
+
setScrollElement(scrollRef.current);
|
|
24
|
+
}, [scrollRef]);
|
|
25
|
+
const virtualizer = _reactvirtual.useVirtualizer.call(void 0, {
|
|
26
|
+
count: items.length,
|
|
27
|
+
getScrollElement: () => scrollElement,
|
|
28
|
+
estimateSize: () => estimateSize,
|
|
29
|
+
overscan,
|
|
30
|
+
getItemKey: (index) => getItemKey(items[index], index)
|
|
31
|
+
});
|
|
32
|
+
_react.useEffect.call(void 0, () => {
|
|
33
|
+
if (initialPositioned) return;
|
|
34
|
+
const timeout = setTimeout(() => setInitialPositioned(true), 250);
|
|
35
|
+
return () => clearTimeout(timeout);
|
|
36
|
+
}, [initialPositioned]);
|
|
37
|
+
_react.useEffect.call(void 0, () => {
|
|
38
|
+
if (initialPositioned || !scrollElement || items.length === 0) return;
|
|
39
|
+
let frame;
|
|
40
|
+
let previousHeight = -1;
|
|
41
|
+
let stableFrames = 0;
|
|
42
|
+
const position = () => {
|
|
43
|
+
const height = scrollElement.scrollHeight;
|
|
44
|
+
const target = Math.max(0, scrollState.calculatedTargetScrollTop);
|
|
45
|
+
const lastRow = virtualizer.getVirtualItems().at(-1);
|
|
46
|
+
const settled = _optionalChain([lastRow, 'optionalAccess', _ => _.index]) === items.length - 1 && Math.abs(scrollElement.scrollTop - target) <= 1 && height === previousHeight;
|
|
47
|
+
stableFrames = settled ? stableFrames + 1 : 0;
|
|
48
|
+
previousHeight = height;
|
|
49
|
+
scrollState.scrollTop = target;
|
|
50
|
+
if (stableFrames >= 2) {
|
|
51
|
+
setInitialPositioned(true);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
frame = requestAnimationFrame(position);
|
|
55
|
+
};
|
|
56
|
+
frame = requestAnimationFrame(position);
|
|
57
|
+
return () => cancelAnimationFrame(frame);
|
|
58
|
+
}, [initialPositioned, items.length, scrollElement, scrollState, virtualizer]);
|
|
59
|
+
const firstKeyRef = _react.useRef.call(void 0, null);
|
|
60
|
+
const totalSizeRef = _react.useRef.call(void 0, 0);
|
|
61
|
+
const firstKey = items.length > 0 ? getItemKey(items[0], 0) : null;
|
|
62
|
+
_react.useLayoutEffect.call(void 0, () => {
|
|
63
|
+
const previousFirstKey = firstKeyRef.current;
|
|
64
|
+
const previousTotal = totalSizeRef.current;
|
|
65
|
+
firstKeyRef.current = firstKey;
|
|
66
|
+
totalSizeRef.current = virtualizer.getTotalSize();
|
|
67
|
+
if (previousFirstKey === null || previousFirstKey === firstKey) return;
|
|
68
|
+
const previousFirstIndex = items.findIndex((item, index) => getItemKey(item, index) === previousFirstKey);
|
|
69
|
+
if (previousFirstIndex <= 0) return;
|
|
70
|
+
const scrollElement2 = scrollRef.current;
|
|
71
|
+
if (!scrollElement2) return;
|
|
72
|
+
scrollElement2.scrollTop += totalSizeRef.current - previousTotal;
|
|
73
|
+
});
|
|
74
|
+
const onReachStartRef = _react.useRef.call(void 0, onReachStart);
|
|
75
|
+
_react.useLayoutEffect.call(void 0, () => {
|
|
76
|
+
onReachStartRef.current = onReachStart;
|
|
77
|
+
}, [onReachStart]);
|
|
78
|
+
_react.useEffect.call(void 0, () => {
|
|
79
|
+
const scrollElement2 = scrollRef.current;
|
|
80
|
+
if (!scrollElement2) return;
|
|
81
|
+
const handleScroll = () => {
|
|
82
|
+
if (scrollElement2.scrollTop <= startThreshold) _optionalChain([onReachStartRef, 'access', _2 => _2.current, 'optionalCall', _3 => _3()]);
|
|
83
|
+
};
|
|
84
|
+
scrollElement2.addEventListener("scroll", handleScroll, { passive: true });
|
|
85
|
+
return () => scrollElement2.removeEventListener("scroll", handleScroll);
|
|
86
|
+
}, [scrollRef, startThreshold]);
|
|
87
|
+
const measureRow = _react.useCallback.call(void 0,
|
|
88
|
+
(node) => virtualizer.measureElement(node),
|
|
89
|
+
[virtualizer]
|
|
90
|
+
);
|
|
91
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
className: _utils.cn.call(void 0, "relative w-full", className),
|
|
95
|
+
style: { height: virtualizer.getTotalSize(), visibility: initialPositioned ? void 0 : "hidden" },
|
|
96
|
+
children: virtualizer.getVirtualItems().map((virtualRow) => {
|
|
97
|
+
const content = renderItem(items[virtualRow.index], virtualRow.index);
|
|
98
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
99
|
+
"div",
|
|
100
|
+
{
|
|
101
|
+
"data-index": virtualRow.index,
|
|
102
|
+
ref: measureRow,
|
|
103
|
+
className: "absolute top-0 left-0 w-full",
|
|
104
|
+
style: { transform: `translateY(${virtualRow.start}px)` },
|
|
105
|
+
children: content === null ? null : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: rowClassName, children: content })
|
|
106
|
+
},
|
|
107
|
+
virtualRow.key
|
|
108
|
+
);
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
exports.ConversationVirtualList = ConversationVirtualList;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface ConversationVirtualListProps<T> {
|
|
5
|
+
items: T[];
|
|
6
|
+
getItemKey: (item: T, index: number) => string;
|
|
7
|
+
/** Return null for an item that renders nothing; it then takes no space. */
|
|
8
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
9
|
+
/** Called on a scroll within `startThreshold` of the top, including programmatic scrolls. */
|
|
10
|
+
onReachStart?: () => void;
|
|
11
|
+
startThreshold?: number;
|
|
12
|
+
estimateSize?: number;
|
|
13
|
+
/** Reveal the initial transcript only after its last row is positioned. */
|
|
14
|
+
initialScrollToEnd?: boolean;
|
|
15
|
+
overscan?: number;
|
|
16
|
+
/** Class for the wrapper around each rendered item, for example a bottom gap. */
|
|
17
|
+
rowClassName?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Windowed rows inside a `Conversation`. Only rows near the viewport are in
|
|
22
|
+
* the DOM, so node count stays bounded for any thread length. The scroll
|
|
23
|
+
* element and stick-to-bottom behavior stay with `StickToBottom`.
|
|
24
|
+
*
|
|
25
|
+
* Items prepended to `items` (older history) keep the viewport anchored:
|
|
26
|
+
* the scroll offset moves by the height the new rows added above it.
|
|
27
|
+
*/
|
|
28
|
+
declare function ConversationVirtualList<T>({ items, getItemKey, renderItem, onReachStart, startThreshold, estimateSize, initialScrollToEnd, overscan, rowClassName, className, }: ConversationVirtualListProps<T>): react_jsx_runtime.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { ConversationVirtualList, type ConversationVirtualListProps };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface ConversationVirtualListProps<T> {
|
|
5
|
+
items: T[];
|
|
6
|
+
getItemKey: (item: T, index: number) => string;
|
|
7
|
+
/** Return null for an item that renders nothing; it then takes no space. */
|
|
8
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
9
|
+
/** Called on a scroll within `startThreshold` of the top, including programmatic scrolls. */
|
|
10
|
+
onReachStart?: () => void;
|
|
11
|
+
startThreshold?: number;
|
|
12
|
+
estimateSize?: number;
|
|
13
|
+
/** Reveal the initial transcript only after its last row is positioned. */
|
|
14
|
+
initialScrollToEnd?: boolean;
|
|
15
|
+
overscan?: number;
|
|
16
|
+
/** Class for the wrapper around each rendered item, for example a bottom gap. */
|
|
17
|
+
rowClassName?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Windowed rows inside a `Conversation`. Only rows near the viewport are in
|
|
22
|
+
* the DOM, so node count stays bounded for any thread length. The scroll
|
|
23
|
+
* element and stick-to-bottom behavior stay with `StickToBottom`.
|
|
24
|
+
*
|
|
25
|
+
* Items prepended to `items` (older history) keep the viewport anchored:
|
|
26
|
+
* the scroll offset moves by the height the new rows added above it.
|
|
27
|
+
*/
|
|
28
|
+
declare function ConversationVirtualList<T>({ items, getItemKey, renderItem, onReachStart, startThreshold, estimateSize, initialScrollToEnd, overscan, rowClassName, className, }: ConversationVirtualListProps<T>): react_jsx_runtime.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { ConversationVirtualList, type ConversationVirtualListProps };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
5
|
+
import { useStickToBottomContext } from "use-stick-to-bottom";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
function ConversationVirtualList({
|
|
8
|
+
items,
|
|
9
|
+
getItemKey,
|
|
10
|
+
renderItem,
|
|
11
|
+
onReachStart,
|
|
12
|
+
startThreshold = 240,
|
|
13
|
+
estimateSize = 96,
|
|
14
|
+
initialScrollToEnd = false,
|
|
15
|
+
overscan = 6,
|
|
16
|
+
rowClassName,
|
|
17
|
+
className
|
|
18
|
+
}) {
|
|
19
|
+
const { scrollRef, state: scrollState } = useStickToBottomContext();
|
|
20
|
+
const [initialPositioned, setInitialPositioned] = useState(!initialScrollToEnd);
|
|
21
|
+
const [scrollElement, setScrollElement] = useState(null);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setScrollElement(scrollRef.current);
|
|
24
|
+
}, [scrollRef]);
|
|
25
|
+
const virtualizer = useVirtualizer({
|
|
26
|
+
count: items.length,
|
|
27
|
+
getScrollElement: () => scrollElement,
|
|
28
|
+
estimateSize: () => estimateSize,
|
|
29
|
+
overscan,
|
|
30
|
+
getItemKey: (index) => getItemKey(items[index], index)
|
|
31
|
+
});
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (initialPositioned) return;
|
|
34
|
+
const timeout = setTimeout(() => setInitialPositioned(true), 250);
|
|
35
|
+
return () => clearTimeout(timeout);
|
|
36
|
+
}, [initialPositioned]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (initialPositioned || !scrollElement || items.length === 0) return;
|
|
39
|
+
let frame;
|
|
40
|
+
let previousHeight = -1;
|
|
41
|
+
let stableFrames = 0;
|
|
42
|
+
const position = () => {
|
|
43
|
+
const height = scrollElement.scrollHeight;
|
|
44
|
+
const target = Math.max(0, scrollState.calculatedTargetScrollTop);
|
|
45
|
+
const lastRow = virtualizer.getVirtualItems().at(-1);
|
|
46
|
+
const settled = lastRow?.index === items.length - 1 && Math.abs(scrollElement.scrollTop - target) <= 1 && height === previousHeight;
|
|
47
|
+
stableFrames = settled ? stableFrames + 1 : 0;
|
|
48
|
+
previousHeight = height;
|
|
49
|
+
scrollState.scrollTop = target;
|
|
50
|
+
if (stableFrames >= 2) {
|
|
51
|
+
setInitialPositioned(true);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
frame = requestAnimationFrame(position);
|
|
55
|
+
};
|
|
56
|
+
frame = requestAnimationFrame(position);
|
|
57
|
+
return () => cancelAnimationFrame(frame);
|
|
58
|
+
}, [initialPositioned, items.length, scrollElement, scrollState, virtualizer]);
|
|
59
|
+
const firstKeyRef = useRef(null);
|
|
60
|
+
const totalSizeRef = useRef(0);
|
|
61
|
+
const firstKey = items.length > 0 ? getItemKey(items[0], 0) : null;
|
|
62
|
+
useLayoutEffect(() => {
|
|
63
|
+
const previousFirstKey = firstKeyRef.current;
|
|
64
|
+
const previousTotal = totalSizeRef.current;
|
|
65
|
+
firstKeyRef.current = firstKey;
|
|
66
|
+
totalSizeRef.current = virtualizer.getTotalSize();
|
|
67
|
+
if (previousFirstKey === null || previousFirstKey === firstKey) return;
|
|
68
|
+
const previousFirstIndex = items.findIndex((item, index) => getItemKey(item, index) === previousFirstKey);
|
|
69
|
+
if (previousFirstIndex <= 0) return;
|
|
70
|
+
const scrollElement2 = scrollRef.current;
|
|
71
|
+
if (!scrollElement2) return;
|
|
72
|
+
scrollElement2.scrollTop += totalSizeRef.current - previousTotal;
|
|
73
|
+
});
|
|
74
|
+
const onReachStartRef = useRef(onReachStart);
|
|
75
|
+
useLayoutEffect(() => {
|
|
76
|
+
onReachStartRef.current = onReachStart;
|
|
77
|
+
}, [onReachStart]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const scrollElement2 = scrollRef.current;
|
|
80
|
+
if (!scrollElement2) return;
|
|
81
|
+
const handleScroll = () => {
|
|
82
|
+
if (scrollElement2.scrollTop <= startThreshold) onReachStartRef.current?.();
|
|
83
|
+
};
|
|
84
|
+
scrollElement2.addEventListener("scroll", handleScroll, { passive: true });
|
|
85
|
+
return () => scrollElement2.removeEventListener("scroll", handleScroll);
|
|
86
|
+
}, [scrollRef, startThreshold]);
|
|
87
|
+
const measureRow = useCallback(
|
|
88
|
+
(node) => virtualizer.measureElement(node),
|
|
89
|
+
[virtualizer]
|
|
90
|
+
);
|
|
91
|
+
return /* @__PURE__ */ jsx(
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
className: cn("relative w-full", className),
|
|
95
|
+
style: { height: virtualizer.getTotalSize(), visibility: initialPositioned ? void 0 : "hidden" },
|
|
96
|
+
children: virtualizer.getVirtualItems().map((virtualRow) => {
|
|
97
|
+
const content = renderItem(items[virtualRow.index], virtualRow.index);
|
|
98
|
+
return /* @__PURE__ */ jsx(
|
|
99
|
+
"div",
|
|
100
|
+
{
|
|
101
|
+
"data-index": virtualRow.index,
|
|
102
|
+
ref: measureRow,
|
|
103
|
+
className: "absolute top-0 left-0 w-full",
|
|
104
|
+
style: { transform: `translateY(${virtualRow.start}px)` },
|
|
105
|
+
children: content === null ? null : /* @__PURE__ */ jsx("div", { className: rowClassName, children: content })
|
|
106
|
+
},
|
|
107
|
+
virtualRow.key
|
|
108
|
+
);
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
ConversationVirtualList
|
|
115
|
+
};
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }var _jsxruntime = require('react/jsx-runtime');
|
|
2
2
|
var _react = require('react');
|
|
3
3
|
var _lucidereact = require('lucide-react');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
8
|
var _common = require('@sero-ai/common');
|
|
11
9
|
var _popover = require('../ui/popover');
|
|
12
|
-
var
|
|
10
|
+
var _modelpickerbody = require('./model-picker-body');
|
|
13
11
|
var _utils = require('../../lib/utils');
|
|
14
12
|
function AvailableModelPicker({
|
|
15
13
|
groups,
|
|
@@ -24,8 +22,6 @@ function AvailableModelPicker({
|
|
|
24
22
|
className
|
|
25
23
|
}) {
|
|
26
24
|
const [open, setOpen] = _react.useState.call(void 0, false);
|
|
27
|
-
const [query, setQuery] = _react.useState.call(void 0, "");
|
|
28
|
-
const inputRef = _react.useRef.call(void 0, null);
|
|
29
25
|
const selected = _react.useMemo.call(void 0, () => {
|
|
30
26
|
const parsed = _common.parseModelKey.call(void 0, value);
|
|
31
27
|
if (!parsed) {
|
|
@@ -39,32 +35,16 @@ function AvailableModelPicker({
|
|
|
39
35
|
fallbackLabel: model ? null : value
|
|
40
36
|
};
|
|
41
37
|
}, [groups, value]);
|
|
42
|
-
const
|
|
43
|
-
()
|
|
44
|
-
[groups, query]
|
|
45
|
-
);
|
|
46
|
-
const totalResults = _react.useMemo.call(void 0,
|
|
47
|
-
() => filteredGroups.reduce((count, group) => count + group.models.length, 0),
|
|
48
|
-
[filteredGroups]
|
|
49
|
-
);
|
|
50
|
-
const handleOpenChange = _react.useCallback.call(void 0, (nextOpen) => {
|
|
51
|
-
setOpen(nextOpen);
|
|
52
|
-
if (!nextOpen) return;
|
|
53
|
-
setQuery("");
|
|
54
|
-
requestAnimationFrame(() => _optionalChain([inputRef, 'access', _ => _.current, 'optionalAccess', _2 => _2.focus, 'call', _3 => _3()]));
|
|
55
|
-
}, []);
|
|
56
|
-
const handleSelect = _react.useCallback.call(void 0, (provider, modelId) => {
|
|
57
|
-
onChange(_common.modelKey.call(void 0, provider, modelId));
|
|
38
|
+
const handleSelect = _react.useCallback.call(void 0, (nextValue) => {
|
|
39
|
+
onChange(nextValue);
|
|
58
40
|
setOpen(false);
|
|
59
|
-
setQuery("");
|
|
60
41
|
}, [onChange]);
|
|
61
42
|
const handleClear = _react.useCallback.call(void 0, (event) => {
|
|
62
43
|
event.stopPropagation();
|
|
63
44
|
onChange("");
|
|
64
45
|
setOpen(false);
|
|
65
|
-
setQuery("");
|
|
66
46
|
}, [onChange]);
|
|
67
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _popover.Popover, { open, onOpenChange:
|
|
47
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _popover.Popover, { open, onOpenChange: setOpen, children: [
|
|
68
48
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _popover.PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
69
49
|
"div",
|
|
70
50
|
{
|
|
@@ -79,11 +59,11 @@ function AvailableModelPicker({
|
|
|
79
59
|
children: [
|
|
80
60
|
selected.group && selected.model ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
81
61
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
82
|
-
|
|
62
|
+
_modelpickerbody.ProviderLogo,
|
|
83
63
|
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
className: "size-4
|
|
64
|
+
logo: selected.group.logo,
|
|
65
|
+
displayName: selected.group.displayName,
|
|
66
|
+
className: "size-4"
|
|
87
67
|
}
|
|
88
68
|
),
|
|
89
69
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 flex-1", children: [
|
|
@@ -108,7 +88,7 @@ function AvailableModelPicker({
|
|
|
108
88
|
]
|
|
109
89
|
}
|
|
110
90
|
) }),
|
|
111
|
-
/* @__PURE__ */ _jsxruntime.
|
|
91
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
112
92
|
_popover.PopoverContent,
|
|
113
93
|
{
|
|
114
94
|
side: "bottom",
|
|
@@ -116,53 +96,17 @@ function AvailableModelPicker({
|
|
|
116
96
|
sideOffset: 4,
|
|
117
97
|
className: "w-[var(--radix-popover-trigger-width)] overflow-hidden rounded-xl border-border/60 bg-background p-0 shadow-xl",
|
|
118
98
|
onWheel: (event) => event.stopPropagation(),
|
|
119
|
-
children:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "max-h-[280px] overflow-y-auto py-1", children: groups.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: noModelsLabel }) : totalResults === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyLabel }) : filteredGroups.map((group, index) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
131
|
-
index > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "mx-3 border-t border-border/30" }) : null,
|
|
132
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2 px-3 pb-1 pt-2", children: [
|
|
133
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
134
|
-
"img",
|
|
135
|
-
{
|
|
136
|
-
src: group.logo,
|
|
137
|
-
alt: group.displayName,
|
|
138
|
-
className: "size-3.5 shrink-0 rounded-sm dark:invert"
|
|
139
|
-
}
|
|
140
|
-
),
|
|
141
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm font-semibold uppercase tracking-wider text-muted-foreground", children: group.displayName })
|
|
142
|
-
] }),
|
|
143
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "px-1", children: group.models.map((model) => {
|
|
144
|
-
const nextValue = _common.modelKey.call(void 0, model.provider, model.modelId);
|
|
145
|
-
const isSelected = nextValue === value;
|
|
146
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
147
|
-
"button",
|
|
148
|
-
{
|
|
149
|
-
type: "button",
|
|
150
|
-
onClick: () => handleSelect(model.provider, model.modelId),
|
|
151
|
-
className: _utils.cn.call(void 0,
|
|
152
|
-
"flex w-full items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-left text-sm transition-colors",
|
|
153
|
-
isSelected ? "bg-secondary text-foreground" : "text-muted-foreground hover:bg-secondary/60 hover:text-foreground"
|
|
154
|
-
),
|
|
155
|
-
children: [
|
|
156
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex size-4 shrink-0 items-center justify-center", children: isSelected ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "size-1.5 rounded-full bg-border" }) }),
|
|
157
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "min-w-0 flex-1 truncate font-medium", children: model.name }),
|
|
158
|
-
model.reasoning ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Sparkles, { className: "size-3 shrink-0 text-amber-500/60" }) : null
|
|
159
|
-
]
|
|
160
|
-
},
|
|
161
|
-
nextValue
|
|
162
|
-
);
|
|
163
|
-
}) })
|
|
164
|
-
] }, group.provider)) })
|
|
165
|
-
]
|
|
99
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
100
|
+
_modelpickerbody.ModelPickerBody,
|
|
101
|
+
{
|
|
102
|
+
groups,
|
|
103
|
+
value,
|
|
104
|
+
onChange: handleSelect,
|
|
105
|
+
searchPlaceholder,
|
|
106
|
+
emptyLabel,
|
|
107
|
+
noModelsLabel
|
|
108
|
+
}
|
|
109
|
+
)
|
|
166
110
|
}
|
|
167
111
|
)
|
|
168
112
|
] });
|