@wingmanjs/react 0.1.1 → 0.1.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/hooks/use-auto-scroll.d.ts +20 -0
- package/dist/hooks/use-auto-scroll.d.ts.map +1 -0
- package/dist/hooks/use-auto-scroll.js +58 -0
- package/dist/hooks/use-auto-scroll.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook that auto-scrolls a container to the bottom as new content streams in.
|
|
3
|
+
*
|
|
4
|
+
* Works with both shadcn ScrollArea (data-slot="scroll-area-viewport") and
|
|
5
|
+
* plain scrollable divs. Respects the user's scroll position — if they've
|
|
6
|
+
* scrolled up to read history, auto-scroll pauses until they scroll back
|
|
7
|
+
* near the bottom.
|
|
8
|
+
*
|
|
9
|
+
* @param deps - Reactive values that trigger a scroll check (e.g., messages, streaming content)
|
|
10
|
+
* @param threshold - Pixels from bottom to consider "at bottom" (default: 80)
|
|
11
|
+
* @returns ref to attach to the scroll container (or its wrapper)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const scrollRef = useAutoScroll([messages, streamingContent], 80);
|
|
16
|
+
* return <ScrollArea ref={scrollRef}>...</ScrollArea>;
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function useAutoScroll<T extends HTMLElement = HTMLDivElement>(deps: unknown[], threshold?: number): import("react").RefObject<T | null>;
|
|
20
|
+
//# sourceMappingURL=use-auto-scroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-auto-scroll.d.ts","sourceRoot":"","sources":["../../src/hooks/use-auto-scroll.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,cAAc,EAClE,IAAI,EAAE,OAAO,EAAE,EACf,SAAS,SAAK,uCA0Cf"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Hook that auto-scrolls a container to the bottom as new content streams in.
|
|
4
|
+
*
|
|
5
|
+
* Works with both shadcn ScrollArea (data-slot="scroll-area-viewport") and
|
|
6
|
+
* plain scrollable divs. Respects the user's scroll position — if they've
|
|
7
|
+
* scrolled up to read history, auto-scroll pauses until they scroll back
|
|
8
|
+
* near the bottom.
|
|
9
|
+
*
|
|
10
|
+
* @param deps - Reactive values that trigger a scroll check (e.g., messages, streaming content)
|
|
11
|
+
* @param threshold - Pixels from bottom to consider "at bottom" (default: 80)
|
|
12
|
+
* @returns ref to attach to the scroll container (or its wrapper)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const scrollRef = useAutoScroll([messages, streamingContent], 80);
|
|
17
|
+
* return <ScrollArea ref={scrollRef}>...</ScrollArea>;
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function useAutoScroll(deps, threshold = 80) {
|
|
21
|
+
const containerRef = useRef(null);
|
|
22
|
+
const isUserScrolledUp = useRef(false);
|
|
23
|
+
// Resolve the actual scrollable element (handles shadcn ScrollArea wrapper)
|
|
24
|
+
const getScrollEl = useCallback(() => {
|
|
25
|
+
const el = containerRef.current;
|
|
26
|
+
if (!el)
|
|
27
|
+
return null;
|
|
28
|
+
// shadcn ScrollArea wraps the viewport in [data-slot="scroll-area-viewport"]
|
|
29
|
+
return el.querySelector('[data-slot="scroll-area-viewport"]') ?? el;
|
|
30
|
+
}, []);
|
|
31
|
+
// Track whether the user has scrolled away from the bottom
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const scrollEl = getScrollEl();
|
|
34
|
+
if (!scrollEl)
|
|
35
|
+
return;
|
|
36
|
+
const handleScroll = () => {
|
|
37
|
+
const { scrollTop, scrollHeight, clientHeight } = scrollEl;
|
|
38
|
+
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
39
|
+
isUserScrolledUp.current = distanceFromBottom > threshold;
|
|
40
|
+
};
|
|
41
|
+
scrollEl.addEventListener('scroll', handleScroll, { passive: true });
|
|
42
|
+
return () => scrollEl.removeEventListener('scroll', handleScroll);
|
|
43
|
+
}, [getScrollEl, threshold]);
|
|
44
|
+
// Auto-scroll when deps change, unless user has scrolled up
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (isUserScrolledUp.current)
|
|
47
|
+
return;
|
|
48
|
+
const scrollEl = getScrollEl();
|
|
49
|
+
if (!scrollEl)
|
|
50
|
+
return;
|
|
51
|
+
// Use requestAnimationFrame for smooth scroll after DOM paint
|
|
52
|
+
requestAnimationFrame(() => {
|
|
53
|
+
scrollEl.scrollTo({ top: scrollEl.scrollHeight, behavior: 'smooth' });
|
|
54
|
+
});
|
|
55
|
+
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
|
56
|
+
return containerRef;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=use-auto-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-auto-scroll.js","sourceRoot":"","sources":["../../src/hooks/use-auto-scroll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAe,EACf,SAAS,GAAG,EAAE;IAEd,MAAM,YAAY,GAAG,MAAM,CAAI,IAAI,CAAC,CAAC;IACrC,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEvC,4EAA4E;IAC5E,MAAM,WAAW,GAAG,WAAW,CAAC,GAAuB,EAAE;QACvD,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACrB,6EAA6E;QAC7E,OAAO,EAAE,CAAC,aAAa,CAAc,oCAAoC,CAAC,IAAI,EAAE,CAAC;IACnF,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,2DAA2D;IAC3D,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,MAAM,YAAY,GAAG,GAAG,EAAE;YACxB,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC;YAC3D,MAAM,kBAAkB,GAAG,YAAY,GAAG,SAAS,GAAG,YAAY,CAAC;YACnE,gBAAgB,CAAC,OAAO,GAAG,kBAAkB,GAAG,SAAS,CAAC;QAC5D,CAAC,CAAC;QAEF,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACpE,CAAC,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;IAE7B,4DAA4D;IAC5D,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,gBAAgB,CAAC,OAAO;YAAE,OAAO;QAErC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,8DAA8D;QAC9D,qBAAqB,CAAC,GAAG,EAAE;YACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,kDAAkD;IAE5D,OAAO,YAAY,CAAC;AACtB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { ChatProvider, useChat } from './providers/chat-provider.js';
|
|
8
8
|
export type { ChatProviderProps, ChatState, ChatContextValue } from './providers/chat-provider.js';
|
|
9
|
+
export { useAutoScroll } from './hooks/use-auto-scroll.js';
|
|
9
10
|
export { ThemeProvider, useTheme } from './providers/theme-provider.js';
|
|
10
11
|
export type { ThemeProviderProps, ThemeContextValue, WingmanTheme, WingmanThemeColors, } from './providers/theme-provider.js';
|
|
11
12
|
export { ChatMessage } from './components/chat-message.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEnG,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACxE,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAQ/E,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEnG,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACxE,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAQ/E,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
// Provider + hooks
|
|
8
8
|
export { ChatProvider, useChat } from './providers/chat-provider.js';
|
|
9
|
+
export { useAutoScroll } from './hooks/use-auto-scroll.js';
|
|
9
10
|
export { ThemeProvider, useTheme } from './providers/theme-provider.js';
|
|
10
11
|
// Components
|
|
11
12
|
export { ChatMessage } from './components/chat-message.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAQxE,aAAa;AACb,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG3D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAGvD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGzD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,qBAAqB;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGzD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGzD,qCAAqC;AACrC,kEAAkE;AAClE,wDAAwD;AACxD,0DAA0D;AAC1D,wEAAwE;AAExE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAQxE,aAAa;AACb,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG3D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAGvD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGzD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,qBAAqB;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGzD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGzD,qCAAqC;AACrC,kEAAkE;AAClE,wDAAwD;AACxD,0DAA0D;AAC1D,wEAAwE;AAExE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wingmanjs/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "React hooks and components for Wingman chat",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,6 +30,15 @@
|
|
|
30
30
|
],
|
|
31
31
|
"author": "Eric Hansen",
|
|
32
32
|
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/ericchansen/wingman.git",
|
|
36
|
+
"directory": "packages/react"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/ericchansen/wingman#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/ericchansen/wingman/issues"
|
|
41
|
+
},
|
|
33
42
|
"engines": {
|
|
34
43
|
"node": ">=20.11.0"
|
|
35
44
|
},
|
|
@@ -40,7 +49,7 @@
|
|
|
40
49
|
"peerDependencies": {
|
|
41
50
|
"react": "^19.0.0",
|
|
42
51
|
"react-dom": "^19.0.0",
|
|
43
|
-
"@wingmanjs/core": "0.1.
|
|
52
|
+
"@wingmanjs/core": "0.1.2"
|
|
44
53
|
},
|
|
45
54
|
"devDependencies": {
|
|
46
55
|
"@types/react": "^19.0.0",
|