@intentface/chat 0.2.0 → 0.2.1
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.
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export declare const prefersReducedMotion: () => boolean;
|
|
2
2
|
export declare const resolveScrollBehavior: (behavior: ScrollBehavior) => ScrollBehavior;
|
|
3
3
|
export declare const scrollContainerTo: (el: HTMLElement, top: number, behavior: ScrollBehavior) => void;
|
|
4
|
+
export type ViewportBox = {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
export declare const readViewportBox: (el: HTMLElement | null) => ViewportBox;
|
|
9
|
+
export declare const sameViewportBox: (a: ViewportBox, b: ViewportBox) => boolean;
|
|
4
10
|
export declare const wasPrepended: (mutations: MutationRecord[], previousFirst: Element | null, content: HTMLElement) => boolean;
|
|
5
11
|
export declare const DEFAULT_BOTTOM_OFFSET = 128;
|
|
6
12
|
export declare const DOCK_SELECTOR = "[data-thread-composer]";
|
package/dist/thread/geometry.js
CHANGED
|
@@ -11,6 +11,8 @@ export const resolveScrollBehavior = (behavior) => behavior === "smooth" && pref
|
|
|
11
11
|
export const scrollContainerTo = (el, top, behavior) => {
|
|
12
12
|
el.scrollTo({ top, behavior: resolveScrollBehavior(behavior) });
|
|
13
13
|
};
|
|
14
|
+
export const readViewportBox = (el) => el ? { width: el.clientWidth, height: el.clientHeight } : { width: 0, height: 0 };
|
|
15
|
+
export const sameViewportBox = (a, b) => a.width === b.width && a.height === b.height;
|
|
14
16
|
// A prepend = rows were added, nothing removed, and the previously-first row
|
|
15
17
|
// is still connected but no longer first (older history loading in above).
|
|
16
18
|
export const wasPrepended = (mutations, previousFirst, content) => previousFirst?.isConnected === true &&
|
package/dist/thread/thread.js
CHANGED
|
@@ -12,7 +12,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
12
12
|
import { createContext, memo, use, useCallback, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore, } from "react";
|
|
13
13
|
import { useRefWithInit } from "../internal/render/useRefWithInit.js";
|
|
14
14
|
import { useRenderElement } from "../internal/render/useRenderElement.js";
|
|
15
|
-
import { DEFAULT_BOTTOM_OFFSET, measureDockInset, measureTopInset, queryDock, resolveScrollBehavior, scrollContainerTo, wasPrepended, } from "./geometry.js";
|
|
15
|
+
import { DEFAULT_BOTTOM_OFFSET, measureDockInset, measureTopInset, queryDock, readViewportBox, resolveScrollBehavior, sameViewportBox, scrollContainerTo, wasPrepended, } from "./geometry.js";
|
|
16
16
|
import { createEdgeStore, createVisibilityStore, EMPTY_VISIBILITY, } from "./stores.js";
|
|
17
17
|
// Latest-ref: read the current value from long-lived effects/callbacks without
|
|
18
18
|
// re-subscribing them when it changes.
|
|
@@ -299,14 +299,24 @@ const useThreadScroll = (rootRef, mode, preserveScrollOnPrepend) => {
|
|
|
299
299
|
// late, so a position check here would scroll on stale "at bottom" the
|
|
300
300
|
// instant after the user wheels up, hijacking their scroll and re-arming
|
|
301
301
|
// the follow in a loop they can't escape.
|
|
302
|
+
//
|
|
303
|
+
// A resize alone can't say whether content grew or the viewport did: the
|
|
304
|
+
// reserve tracks --thread-turn-area, so a container animating open resizes
|
|
305
|
+
// `content` every frame. Only a viewport change moves the scroller's own
|
|
306
|
+
// box — re-pin those instantly instead of smooth-scrolling a moving target
|
|
307
|
+
// for the length of the animation.
|
|
308
|
+
let viewport = readViewportBox(scrollRef.current);
|
|
302
309
|
const follow = () => {
|
|
310
|
+
const previous = viewport;
|
|
311
|
+
viewport = readViewportBox(scrollRef.current);
|
|
312
|
+
const resized = !sameViewportBox(previous, viewport);
|
|
303
313
|
if (skipNextResize) {
|
|
304
314
|
skipNextResize = false;
|
|
305
315
|
return;
|
|
306
316
|
}
|
|
307
|
-
if (followingRef.current)
|
|
308
|
-
|
|
309
|
-
|
|
317
|
+
if (!followingRef.current)
|
|
318
|
+
return;
|
|
319
|
+
scrollToBottom(resized ? "instant" : "smooth");
|
|
310
320
|
};
|
|
311
321
|
land();
|
|
312
322
|
const turns = new MutationObserver(land);
|
|
@@ -522,13 +532,15 @@ const useThreadInsets = (rootRef) => {
|
|
|
522
532
|
const root = rootRef.current;
|
|
523
533
|
if (!root)
|
|
524
534
|
return;
|
|
535
|
+
// Read both insets before writing either property: a write followed by a forced
|
|
536
|
+
// layout read dips scrollHeight for a frame, and that scrollTop clamp is final.
|
|
525
537
|
const apply = () => {
|
|
526
538
|
const bottomInset = measureDockInset(root);
|
|
539
|
+
const topInset = measureTopInset(root);
|
|
540
|
+
const area = Math.max(0, Math.round(root.clientHeight - topInset - (bottomInset ?? DEFAULT_BOTTOM_OFFSET)));
|
|
527
541
|
if (bottomInset !== null) {
|
|
528
542
|
root.style.setProperty("--thread-overlay-bottom-height", `${bottomInset}px`);
|
|
529
543
|
}
|
|
530
|
-
const topInset = measureTopInset(root);
|
|
531
|
-
const area = Math.max(0, Math.round(root.clientHeight - topInset - (bottomInset ?? DEFAULT_BOTTOM_OFFSET)));
|
|
532
544
|
root.style.setProperty("--thread-turn-area", `${area}px`);
|
|
533
545
|
};
|
|
534
546
|
apply();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentface/chat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Headless chat UI primitives — unstyled compound components, hooks, and wire formats for building AI chat interfaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"build": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/add-dist-extensions.mjs && node scripts/smoke-dist.mjs",
|
|
81
81
|
"test": "bun test",
|
|
82
82
|
"typecheck": "tsc --noEmit",
|
|
83
|
+
"verify:release": "bun run typecheck && bun test && bun run build && publint",
|
|
83
84
|
"prepack": "bun run build"
|
|
84
85
|
},
|
|
85
86
|
"dependencies": {
|
|
@@ -97,6 +98,7 @@
|
|
|
97
98
|
"@types/react": "^19",
|
|
98
99
|
"@types/react-dom": "^19",
|
|
99
100
|
"axe-core": "^4.12.1",
|
|
101
|
+
"publint": "0.3.23",
|
|
100
102
|
"typescript": "^5"
|
|
101
103
|
},
|
|
102
104
|
"publishConfig": {
|