@adea-ai/ui 0.63.3 → 0.64.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.
Files changed (44) hide show
  1. package/README.md +46 -0
  2. package/dist/NOTICE +54 -0
  3. package/dist/components/conversation/busy-send-button.d.ts +28 -0
  4. package/dist/components/conversation/busy-send-button.d.ts.map +1 -0
  5. package/dist/components/conversation/busy-send-button.js +184 -0
  6. package/dist/components/conversation/busy-send-button.js.map +1 -0
  7. package/dist/components/conversation/conversation-surface.d.ts +5 -1
  8. package/dist/components/conversation/conversation-surface.d.ts.map +1 -1
  9. package/dist/components/conversation/conversation-surface.js +42 -62
  10. package/dist/components/conversation/conversation-surface.js.map +1 -1
  11. package/dist/components/conversation/index.d.ts +1 -0
  12. package/dist/components/conversation/index.d.ts.map +1 -1
  13. package/dist/components/conversation/index.js +2 -1
  14. package/dist/components/conversation/scroll-follow-core.d.ts +181 -0
  15. package/dist/components/conversation/scroll-follow-core.d.ts.map +1 -0
  16. package/dist/components/conversation/scroll-follow-core.js +139 -0
  17. package/dist/components/conversation/scroll-follow-core.js.map +1 -0
  18. package/dist/components/conversation/scroll-follow.d.ts +13 -0
  19. package/dist/components/conversation/scroll-follow.d.ts.map +1 -0
  20. package/dist/components/conversation/scroll-follow.js +102 -0
  21. package/dist/components/conversation/scroll-follow.js.map +1 -0
  22. package/dist/index.js +2 -1
  23. package/dist/lib/tokens.d.ts +3 -0
  24. package/dist/lib/tokens.d.ts.map +1 -1
  25. package/dist/lib/tokens.js +3 -0
  26. package/dist/lib/tokens.js.map +1 -1
  27. package/dist/r/conversation.json +17 -0
  28. package/dist/r/registry.json +17 -0
  29. package/dist/r/src/components/conversation/busy-send-button.tsx +161 -0
  30. package/dist/r/src/components/conversation/conversation-surface.tsx +45 -48
  31. package/dist/r/src/components/conversation/index.ts +1 -0
  32. package/dist/r/src/components/conversation/scroll-follow-core.ts +314 -0
  33. package/dist/r/src/components/conversation/scroll-follow.ts +133 -0
  34. package/dist/r/src/lib/tokens.ts +3 -0
  35. package/dist/r/src/styles/theme.css +9 -0
  36. package/package.json +1 -1
  37. package/registry.json +17 -0
  38. package/src/components/conversation/busy-send-button.tsx +161 -0
  39. package/src/components/conversation/conversation-surface.tsx +45 -48
  40. package/src/components/conversation/index.ts +1 -0
  41. package/src/components/conversation/scroll-follow-core.ts +314 -0
  42. package/src/components/conversation/scroll-follow.ts +133 -0
  43. package/src/lib/tokens.ts +3 -0
  44. package/src/styles/theme.css +9 -0
@@ -0,0 +1,181 @@
1
+ /** Default distance (px) from the bottom within which `isAtBottom` is true. */
2
+ export declare const DEFAULT_BOTTOM_THRESHOLD = 100;
3
+ /**
4
+ * Tolerance (px) for treating a scroll position as "the same" as a value we
5
+ * wrote programmatically. Covers sub-pixel rounding and 1px momentum overshoot.
6
+ * Must stay small so a deliberate user scroll of even a few px is still seen as
7
+ * a user scroll.
8
+ */
9
+ export declare const SELF_SCROLL_EPSILON = 2;
10
+ /**
11
+ * "At the bottom" tolerance (px) for deciding whether an auto-pin still has
12
+ * work to do. A flat 0.5 is UNDER one device pixel at fractional device-pixel
13
+ * ratios (0.67 CSS px at 150% zoom, 0.8 at 125%): the scroller's resting
14
+ * maximum scrollTop lands on a fractional value, so `|scrollTop - target|`
15
+ * stays just above 0.5 even when the viewport is visually pinned to the
16
+ * bottom — making the pin re-fire on every ResizeObserver tick. Scaling the
17
+ * epsilon to the device pixel (never below 1 CSS px) absorbs that fractional
18
+ * resting error. `devicePixelRatio` is read defensively so a jsdom / SSR
19
+ * environment that leaves it undefined falls back to 1 (→ 1.5px).
20
+ */
21
+ export declare function atBottomEpsilon(): number;
22
+ /** Live scroll geometry snapshot read from the scroller element. */
23
+ export interface ScrollGeom {
24
+ scrollTop: number;
25
+ scrollHeight: number;
26
+ clientHeight: number;
27
+ }
28
+ /** scrollTop that places the viewport exactly at the bottom (never negative). */
29
+ export declare function bottomTarget(geom: ScrollGeom): number;
30
+ /** Pixels between the current scroll position and the bottom. */
31
+ export declare function distanceFromBottom(geom: ScrollGeom): number;
32
+ /** Whether the scroller is within `threshold` px of the bottom. */
33
+ export declare function computeAtBottom(geom: ScrollGeom, threshold: number): boolean;
34
+ /**
35
+ * Recognise a `scroll` event caused by our own programmatic write rather than
36
+ * by the user. `lastWriteTop < 0` means "we have not written this session", so
37
+ * any scroll is treated as the user's.
38
+ */
39
+ export declare function isSelfScroll(scrollTop: number, lastWriteTop: number, epsilon?: number): boolean;
40
+ /**
41
+ * Distance (px) from the true bottom within which a user scroll RE-ENGAGES
42
+ * follow. Deliberately much tighter than DEFAULT_BOTTOM_THRESHOLD: that 100px
43
+ * band drives the jump-to-bottom pill's visibility, and reusing it for follow
44
+ * meant a deliberate 3-99px scroll-up kept `stick` armed — the next content
45
+ * change then yanked the reader back to the bottom. Re-engaging only when the
46
+ * user has returned essentially to the bottom keeps "scrolled up to read"
47
+ * positions belonging to the user.
48
+ */
49
+ export declare const FOLLOW_REENGAGE_PX = 16;
50
+ /**
51
+ * Direction-aware `stick` decision for a *user-initiated* scroll (self-scrolls
52
+ * filtered out by the caller via `isSelfScroll`):
53
+ *
54
+ * 1. At the true bottom (within the DPR-aware epsilon) → follow. This also
55
+ * absorbs the layout engine's clamp: a mid-stream content SHRINK drops
56
+ * scrollTop (which reads as an upward move) but lands exactly at the new
57
+ * bottom — releasing there froze streaming follow for the rest of the
58
+ * turn.
59
+ * 2. Any other upward move → release, regardless of distance from the
60
+ * bottom. The scroll position now belongs to the user; only returning to
61
+ * the bottom (3) re-engages.
62
+ * 3. A genuine DOWNWARD move that arrives within FOLLOW_REENGAGE_PX of the
63
+ * bottom → re-engage. A neutral event inside the band does NOT: that is
64
+ * how content collapsing under a still reader re-armed follow.
65
+ * 4. Otherwise (downward/neutral, still away from the bottom) → keep the
66
+ * previous state.
67
+ *
68
+ * `prevScrollTop < 0` means "no prior observation this session". Direction is
69
+ * unknowable then, so the decision is position-only and CONSERVATIVE: follow
70
+ * only within the re-engage band. Keeping a stale `stick` on an unattributable
71
+ * away-from-bottom scroll is how a reader gets yanked.
72
+ */
73
+ export declare function resolveUserScrollStick(args: {
74
+ stick: boolean;
75
+ followOutput: boolean;
76
+ scrollTop: number;
77
+ prevScrollTop: number;
78
+ geom: ScrollGeom;
79
+ /** Change in the scroller's own height since the previous scroll event.
80
+ *
81
+ * Positive = the viewport GREW (the composer shrank under a deletion, the
82
+ * keyboard closed). That growth lowers the maximum scrollTop, so the engine
83
+ * clamps any reader parked closer to the bottom than the growth — with no
84
+ * application write anywhere. The clamp then arrives here as an ordinary
85
+ * scroll event sitting at distance ~0, which rule 1 below used to read as
86
+ * "the reader came back to the bottom" and re-arm follow for someone who
87
+ * never touched the scroller. The next turn to start then took them to the
88
+ * end. Rule 1 exists to absorb a CONTENT-shrink clamp mid-stream, and content
89
+ * shrink moves `scrollHeight`, not `clientHeight` — so the two are
90
+ * distinguishable, and this is the delta that tells them apart. */
91
+ viewportGrowth?: number;
92
+ }): boolean;
93
+ /** Result of an automatic (RO / append) pin evaluation. */
94
+ export interface AutoPinResult {
95
+ /** Whether to write `el.scrollTop = target` now. */
96
+ pin: boolean;
97
+ /** Next value for `stick` (released to false if the user scrolled up). */
98
+ stick: boolean;
99
+ /** The bottom scrollTop the caller should write when `pin` is true. */
100
+ target: number;
101
+ }
102
+ /**
103
+ * Decide an automatic pin at the moment content changed (RO callback / append
104
+ * layout effect / its follow-up rAF), reading LIVE geometry.
105
+ *
106
+ * - Not sticking → never pin.
107
+ * - Sticking but the user has scrolled up since our last write
108
+ * (`scrollTop < lastWriteTop - epsilon`) → release stick, don't pin.
109
+ * This is the synchronous, race-proof guard.
110
+ * - Otherwise → pin to the bottom (only actually move if not already there).
111
+ *
112
+ * `lastWriteTop < 0` disables the scroll-up guard (used right after a slot
113
+ * switch, before we have written anything this session).
114
+ *
115
+ * `viewportShrink` (px, default 0) is how much the SCROLLER'S OWN BOX has
116
+ * shrunk since that reference was recorded — chrome mounting below the
117
+ * transcript (a queue band, an attachment strip, a tip card), often
118
+ * spring-animated over several frames. Our own shrink inflates
119
+ * `distanceFromBottom` with no user input, so without this allowance the
120
+ * distance guard reads it as "meaningfully away from the bottom". Paired with
121
+ * a content SHRINK in the same commit window — a tail-row remount clamping
122
+ * scrollTop below `lastWriteTop` — that produced a full user-scroll-up
123
+ * signature out of two of our own layout changes: follow released mid
124
+ * animation and the content settled a card-height low. Judging the distance
125
+ * against the box we were last a bottom FOR keeps the guard measuring the
126
+ * user's move rather than our own. Only the shrink's own pixels are forgiven,
127
+ * so a genuine drag inside the same tick still releases.
128
+ */
129
+ export declare function evaluateAutoPin(args: {
130
+ stick: boolean;
131
+ geom: ScrollGeom;
132
+ lastWriteTop: number;
133
+ epsilon?: number;
134
+ viewportShrink?: number;
135
+ /** Is a turn actually producing output right now?
136
+ *
137
+ * Follow means "keep me at the end of a LIVE turn". With nothing running there
138
+ * is no output to follow, so a reader sitting above the bottom is not
139
+ * following — and an automatic pin there is a yank with no cause, reported
140
+ * from a phone as the transcript springing back after scrolling up about a
141
+ * hundred pixels with nothing streaming.
142
+ *
143
+ * Defaults to `true` = assume a run is live, which keeps the behaviour of a
144
+ * caller that has no run signal to give (the app-SDK chat surface). The chat
145
+ * transcript passes the real thing. */
146
+ runActive?: boolean;
147
+ /** Is an anchor restore currently OWNING the scroll position?
148
+ *
149
+ * A restore places the reader at an absolute offset and then re-lands it as
150
+ * measurements arrive. An automatic pin during that window is a second owner
151
+ * writing the same scroller, and the two fight: captured on a phone as
152
+ * `WRITE autopin 3091->4245` answered by `WRITE settle 4245->3091`, twice in
153
+ * 120ms, 1,154px each way. The settle won those rounds, but only because its
154
+ * budget had not expired yet -- which is why the same switch sometimes landed
155
+ * at the bottom and sometimes did not.
156
+ *
157
+ * Released rather than merely skipped, for the reason the idle branch below
158
+ * gives: skipping leaves follow armed, so the next growth yanks the reader
159
+ * from wherever the restore just put them. */
160
+ restoreGate?: boolean;
161
+ /** Has hardware input -- wheel / touch / pointer / a scrolling key -- reached
162
+ * the scroller since we last placed the reader at the bottom?
163
+ *
164
+ * False means the reader has done nothing, so a gap that opened while they
165
+ * rest on our last write was opened by content -- a row settling from its
166
+ * estimate, a code-block stand-in swapping for the highlighted block, the
167
+ * top spacer repricing -- and is a gap WE owe them, not one they chose.
168
+ *
169
+ * The idle rule below cannot tell those apart from distance alone, and it
170
+ * errs toward release, which was invisible wherever the browser's native
171
+ * scroll anchoring quietly carried the reader through the growth. WebKit has
172
+ * no scroll anchoring at all, so on an iPhone every entry into an idle
173
+ * session paid the whole post-pin reprice as a displacement and then had
174
+ * follow released on top of it: the transcript opened a viewport or more
175
+ * above the end with nothing streaming to bring it back.
176
+ *
177
+ * Defaults to `true` = assume the reader may have moved, which is the
178
+ * release-leaning legacy behaviour for a caller that has no input signal. */
179
+ readerMovedSinceWrite?: boolean;
180
+ }): AutoPinResult;
181
+ //# sourceMappingURL=scroll-follow-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-follow-core.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/scroll-follow-core.ts"],"names":[],"mappings":"AAqBA,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAQxC;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAErD;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAE3D;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,MAA4B,GACpC,OAAO,CAET;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,KAAK,EAAE,OAAO,CAAA;IACd,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,UAAU,CAAA;IAChB;;;;;;;;;;;wEAWoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,GAAG,OAAO,CA4BV;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,GAAG,EAAE,OAAO,CAAA;IACZ,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;;;;;;4CAUwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;;;;;;;mDAY+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;;;;;;;;;;;;;;kFAiB8E;IAC9E,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,GAAG,aAAa,CAsDhB"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * "At the bottom" tolerance (px) for deciding whether an auto-pin still has
3
+ * work to do. A flat 0.5 is UNDER one device pixel at fractional device-pixel
4
+ * ratios (0.67 CSS px at 150% zoom, 0.8 at 125%): the scroller's resting
5
+ * maximum scrollTop lands on a fractional value, so `|scrollTop - target|`
6
+ * stays just above 0.5 even when the viewport is visually pinned to the
7
+ * bottom — making the pin re-fire on every ResizeObserver tick. Scaling the
8
+ * epsilon to the device pixel (never below 1 CSS px) absorbs that fractional
9
+ * resting error. `devicePixelRatio` is read defensively so a jsdom / SSR
10
+ * environment that leaves it undefined falls back to 1 (→ 1.5px).
11
+ */
12
+ function atBottomEpsilon() {
13
+ const dpr = typeof window !== "undefined" && typeof window.devicePixelRatio === "number" && window.devicePixelRatio > 0 ? window.devicePixelRatio : 1;
14
+ return Math.max(1, 1 / dpr + .5);
15
+ }
16
+ /** scrollTop that places the viewport exactly at the bottom (never negative). */
17
+ function bottomTarget(geom) {
18
+ return Math.max(0, geom.scrollHeight - geom.clientHeight);
19
+ }
20
+ /** Pixels between the current scroll position and the bottom. */
21
+ function distanceFromBottom(geom) {
22
+ return geom.scrollHeight - geom.scrollTop - geom.clientHeight;
23
+ }
24
+ /** Whether the scroller is within `threshold` px of the bottom. */
25
+ function computeAtBottom(geom, threshold) {
26
+ return distanceFromBottom(geom) <= threshold;
27
+ }
28
+ /**
29
+ * Recognise a `scroll` event caused by our own programmatic write rather than
30
+ * by the user. `lastWriteTop < 0` means "we have not written this session", so
31
+ * any scroll is treated as the user's.
32
+ */
33
+ function isSelfScroll(scrollTop, lastWriteTop, epsilon = 2) {
34
+ return lastWriteTop >= 0 && Math.abs(scrollTop - lastWriteTop) <= epsilon;
35
+ }
36
+ /**
37
+ * Direction-aware `stick` decision for a *user-initiated* scroll (self-scrolls
38
+ * filtered out by the caller via `isSelfScroll`):
39
+ *
40
+ * 1. At the true bottom (within the DPR-aware epsilon) → follow. This also
41
+ * absorbs the layout engine's clamp: a mid-stream content SHRINK drops
42
+ * scrollTop (which reads as an upward move) but lands exactly at the new
43
+ * bottom — releasing there froze streaming follow for the rest of the
44
+ * turn.
45
+ * 2. Any other upward move → release, regardless of distance from the
46
+ * bottom. The scroll position now belongs to the user; only returning to
47
+ * the bottom (3) re-engages.
48
+ * 3. A genuine DOWNWARD move that arrives within FOLLOW_REENGAGE_PX of the
49
+ * bottom → re-engage. A neutral event inside the band does NOT: that is
50
+ * how content collapsing under a still reader re-armed follow.
51
+ * 4. Otherwise (downward/neutral, still away from the bottom) → keep the
52
+ * previous state.
53
+ *
54
+ * `prevScrollTop < 0` means "no prior observation this session". Direction is
55
+ * unknowable then, so the decision is position-only and CONSERVATIVE: follow
56
+ * only within the re-engage band. Keeping a stale `stick` on an unattributable
57
+ * away-from-bottom scroll is how a reader gets yanked.
58
+ */
59
+ function resolveUserScrollStick(args) {
60
+ const { stick, followOutput, scrollTop, prevScrollTop, geom } = args;
61
+ if (!followOutput) return false;
62
+ const dist = distanceFromBottom(geom);
63
+ const clampedByViewport = (args.viewportGrowth ?? 0) > atBottomEpsilon() && scrollTop <= prevScrollTop + atBottomEpsilon();
64
+ if (dist <= atBottomEpsilon()) return clampedByViewport ? stick : true;
65
+ if (prevScrollTop < 0) return dist <= 16;
66
+ if (scrollTop < prevScrollTop - .5) return false;
67
+ if (scrollTop > prevScrollTop + .5 && dist <= 16) return true;
68
+ return stick;
69
+ }
70
+ /**
71
+ * Decide an automatic pin at the moment content changed (RO callback / append
72
+ * layout effect / its follow-up rAF), reading LIVE geometry.
73
+ *
74
+ * - Not sticking → never pin.
75
+ * - Sticking but the user has scrolled up since our last write
76
+ * (`scrollTop < lastWriteTop - epsilon`) → release stick, don't pin.
77
+ * This is the synchronous, race-proof guard.
78
+ * - Otherwise → pin to the bottom (only actually move if not already there).
79
+ *
80
+ * `lastWriteTop < 0` disables the scroll-up guard (used right after a slot
81
+ * switch, before we have written anything this session).
82
+ *
83
+ * `viewportShrink` (px, default 0) is how much the SCROLLER'S OWN BOX has
84
+ * shrunk since that reference was recorded — chrome mounting below the
85
+ * transcript (a queue band, an attachment strip, a tip card), often
86
+ * spring-animated over several frames. Our own shrink inflates
87
+ * `distanceFromBottom` with no user input, so without this allowance the
88
+ * distance guard reads it as "meaningfully away from the bottom". Paired with
89
+ * a content SHRINK in the same commit window — a tail-row remount clamping
90
+ * scrollTop below `lastWriteTop` — that produced a full user-scroll-up
91
+ * signature out of two of our own layout changes: follow released mid
92
+ * animation and the content settled a card-height low. Judging the distance
93
+ * against the box we were last a bottom FOR keeps the guard measuring the
94
+ * user's move rather than our own. Only the shrink's own pixels are forgiven,
95
+ * so a genuine drag inside the same tick still releases.
96
+ */
97
+ function evaluateAutoPin(args) {
98
+ const { stick, geom, lastWriteTop } = args;
99
+ const epsilon = args.epsilon ?? 2;
100
+ const viewportShrink = Math.max(0, args.viewportShrink ?? 0);
101
+ const runActive = args.runActive ?? true;
102
+ const readerMovedSinceWrite = args.readerMovedSinceWrite ?? true;
103
+ const target = bottomTarget(geom);
104
+ if (args.restoreGate) return {
105
+ pin: false,
106
+ stick: false,
107
+ target
108
+ };
109
+ if (!stick) return {
110
+ pin: false,
111
+ stick: false,
112
+ target
113
+ };
114
+ const restingOnOurWrite = lastWriteTop >= 0 && Math.abs(geom.scrollTop - lastWriteTop) <= epsilon;
115
+ if (!readerMovedSinceWrite && restingOnOurWrite) return {
116
+ pin: distanceFromBottom(geom) > atBottomEpsilon(),
117
+ stick: true,
118
+ target
119
+ };
120
+ if (!runActive && distanceFromBottom(geom) > atBottomEpsilon()) return {
121
+ pin: false,
122
+ stick: false,
123
+ target
124
+ };
125
+ if (lastWriteTop >= 0 && geom.scrollTop < lastWriteTop - epsilon && distanceFromBottom(geom) - viewportShrink > epsilon) return {
126
+ pin: false,
127
+ stick: false,
128
+ target
129
+ };
130
+ return {
131
+ pin: Math.abs(geom.scrollTop - target) > atBottomEpsilon(),
132
+ stick: true,
133
+ target
134
+ };
135
+ }
136
+ //#endregion
137
+ export { atBottomEpsilon, bottomTarget, computeAtBottom, distanceFromBottom, evaluateAutoPin, isSelfScroll, resolveUserScrollStick };
138
+
139
+ //# sourceMappingURL=scroll-follow-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-follow-core.js","names":[],"sources":["../../../src/components/conversation/scroll-follow-core.ts"],"sourcesContent":["/*\n * Substantially translated from KiroCrew website/src/hooks/virtualizer/FollowController.ts\n * (plain-scroller decision units), pinned at\n * 283e136c0f902e965a535a7c9548c57c7504fed0.\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * This product includes software developed at Amazon.com, Inc.\n * (https://www.amazon.com/).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Pure plain-scroller decisions retained from FollowController; virtual row\n * measurement, persisted anchor resolution and restore settling are excluded.\n */\n/** Default distance (px) from the bottom within which `isAtBottom` is true. */\nexport const DEFAULT_BOTTOM_THRESHOLD = 100\n\n/**\n * Tolerance (px) for treating a scroll position as \"the same\" as a value we\n * wrote programmatically. Covers sub-pixel rounding and 1px momentum overshoot.\n * Must stay small so a deliberate user scroll of even a few px is still seen as\n * a user scroll.\n */\nexport const SELF_SCROLL_EPSILON = 2\n\n/**\n * \"At the bottom\" tolerance (px) for deciding whether an auto-pin still has\n * work to do. A flat 0.5 is UNDER one device pixel at fractional device-pixel\n * ratios (0.67 CSS px at 150% zoom, 0.8 at 125%): the scroller's resting\n * maximum scrollTop lands on a fractional value, so `|scrollTop - target|`\n * stays just above 0.5 even when the viewport is visually pinned to the\n * bottom — making the pin re-fire on every ResizeObserver tick. Scaling the\n * epsilon to the device pixel (never below 1 CSS px) absorbs that fractional\n * resting error. `devicePixelRatio` is read defensively so a jsdom / SSR\n * environment that leaves it undefined falls back to 1 (→ 1.5px).\n */\nexport function atBottomEpsilon(): number {\n const dpr =\n typeof window !== 'undefined' &&\n typeof window.devicePixelRatio === 'number' &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1\n return Math.max(1, 1 / dpr + 0.5)\n}\n\n/** Live scroll geometry snapshot read from the scroller element. */\nexport interface ScrollGeom {\n scrollTop: number\n scrollHeight: number\n clientHeight: number\n}\n\n/** scrollTop that places the viewport exactly at the bottom (never negative). */\nexport function bottomTarget(geom: ScrollGeom): number {\n return Math.max(0, geom.scrollHeight - geom.clientHeight)\n}\n\n/** Pixels between the current scroll position and the bottom. */\nexport function distanceFromBottom(geom: ScrollGeom): number {\n return geom.scrollHeight - geom.scrollTop - geom.clientHeight\n}\n\n/** Whether the scroller is within `threshold` px of the bottom. */\nexport function computeAtBottom(geom: ScrollGeom, threshold: number): boolean {\n return distanceFromBottom(geom) <= threshold\n}\n\n/**\n * Recognise a `scroll` event caused by our own programmatic write rather than\n * by the user. `lastWriteTop < 0` means \"we have not written this session\", so\n * any scroll is treated as the user's.\n */\nexport function isSelfScroll(\n scrollTop: number,\n lastWriteTop: number,\n epsilon: number = SELF_SCROLL_EPSILON\n): boolean {\n return lastWriteTop >= 0 && Math.abs(scrollTop - lastWriteTop) <= epsilon\n}\n\n/**\n * Distance (px) from the true bottom within which a user scroll RE-ENGAGES\n * follow. Deliberately much tighter than DEFAULT_BOTTOM_THRESHOLD: that 100px\n * band drives the jump-to-bottom pill's visibility, and reusing it for follow\n * meant a deliberate 3-99px scroll-up kept `stick` armed — the next content\n * change then yanked the reader back to the bottom. Re-engaging only when the\n * user has returned essentially to the bottom keeps \"scrolled up to read\"\n * positions belonging to the user.\n */\nexport const FOLLOW_REENGAGE_PX = 16\n\n/**\n * Direction-aware `stick` decision for a *user-initiated* scroll (self-scrolls\n * filtered out by the caller via `isSelfScroll`):\n *\n * 1. At the true bottom (within the DPR-aware epsilon) → follow. This also\n * absorbs the layout engine's clamp: a mid-stream content SHRINK drops\n * scrollTop (which reads as an upward move) but lands exactly at the new\n * bottom — releasing there froze streaming follow for the rest of the\n * turn.\n * 2. Any other upward move → release, regardless of distance from the\n * bottom. The scroll position now belongs to the user; only returning to\n * the bottom (3) re-engages.\n * 3. A genuine DOWNWARD move that arrives within FOLLOW_REENGAGE_PX of the\n * bottom → re-engage. A neutral event inside the band does NOT: that is\n * how content collapsing under a still reader re-armed follow.\n * 4. Otherwise (downward/neutral, still away from the bottom) → keep the\n * previous state.\n *\n * `prevScrollTop < 0` means \"no prior observation this session\". Direction is\n * unknowable then, so the decision is position-only and CONSERVATIVE: follow\n * only within the re-engage band. Keeping a stale `stick` on an unattributable\n * away-from-bottom scroll is how a reader gets yanked.\n */\nexport function resolveUserScrollStick(args: {\n stick: boolean\n followOutput: boolean\n scrollTop: number\n prevScrollTop: number\n geom: ScrollGeom\n /** Change in the scroller's own height since the previous scroll event.\n *\n * Positive = the viewport GREW (the composer shrank under a deletion, the\n * keyboard closed). That growth lowers the maximum scrollTop, so the engine\n * clamps any reader parked closer to the bottom than the growth — with no\n * application write anywhere. The clamp then arrives here as an ordinary\n * scroll event sitting at distance ~0, which rule 1 below used to read as\n * \"the reader came back to the bottom\" and re-arm follow for someone who\n * never touched the scroller. The next turn to start then took them to the\n * end. Rule 1 exists to absorb a CONTENT-shrink clamp mid-stream, and content\n * shrink moves `scrollHeight`, not `clientHeight` — so the two are\n * distinguishable, and this is the delta that tells them apart. */\n viewportGrowth?: number\n}): boolean {\n const { stick, followOutput, scrollTop, prevScrollTop, geom } = args\n if (!followOutput) return false\n const dist = distanceFromBottom(geom)\n // A viewport growth large enough to explain the reader's arrival at the bottom\n // is the engine's clamp, not the reader. Leave `stick` exactly as it was.\n // A native clamp only ever LOWERS scrollTop, so a downward move concurrent with\n // the growth is the user's own and must still re-engage follow. Without the\n // direction term a reader who deliberately scrolls down while the keyboard\n // closes is refused their re-engagement.\n const clampedByViewport =\n (args.viewportGrowth ?? 0) > atBottomEpsilon() && scrollTop <= prevScrollTop + atBottomEpsilon()\n if (dist <= atBottomEpsilon()) return clampedByViewport ? stick : true\n if (prevScrollTop < 0) return dist <= FOLLOW_REENGAGE_PX\n if (scrollTop < prevScrollTop - 0.5) return false\n // Re-engagement requires a genuine DOWNWARD move, not merely a non-upward\n // event that finds the reader inside the band. A neutral event (identical\n // scrollTop -- the tail of an iOS momentum run, or any scroll fired while the\n // reader is at rest) used to satisfy this, so a reader sitting mid-transcript\n // could be re-armed by CONTENT rather than by their own hand: when rows\n // outside the window reprice smaller than their estimates, the remaining\n // content collapses under them and the bottom band arrives at the reader\n // instead of the reader arriving at it. Follow re-engaged, and the next pin\n // took them to the end -- reported as scrolling along and suddenly landing at\n // the bottom. Distance alone cannot tell those apart; the direction of the\n // reader's own move can.\n if (scrollTop > prevScrollTop + 0.5 && dist <= FOLLOW_REENGAGE_PX) return true\n return stick\n}\n\n/** Result of an automatic (RO / append) pin evaluation. */\nexport interface AutoPinResult {\n /** Whether to write `el.scrollTop = target` now. */\n pin: boolean\n /** Next value for `stick` (released to false if the user scrolled up). */\n stick: boolean\n /** The bottom scrollTop the caller should write when `pin` is true. */\n target: number\n}\n\n/**\n * Decide an automatic pin at the moment content changed (RO callback / append\n * layout effect / its follow-up rAF), reading LIVE geometry.\n *\n * - Not sticking → never pin.\n * - Sticking but the user has scrolled up since our last write\n * (`scrollTop < lastWriteTop - epsilon`) → release stick, don't pin.\n * This is the synchronous, race-proof guard.\n * - Otherwise → pin to the bottom (only actually move if not already there).\n *\n * `lastWriteTop < 0` disables the scroll-up guard (used right after a slot\n * switch, before we have written anything this session).\n *\n * `viewportShrink` (px, default 0) is how much the SCROLLER'S OWN BOX has\n * shrunk since that reference was recorded — chrome mounting below the\n * transcript (a queue band, an attachment strip, a tip card), often\n * spring-animated over several frames. Our own shrink inflates\n * `distanceFromBottom` with no user input, so without this allowance the\n * distance guard reads it as \"meaningfully away from the bottom\". Paired with\n * a content SHRINK in the same commit window — a tail-row remount clamping\n * scrollTop below `lastWriteTop` — that produced a full user-scroll-up\n * signature out of two of our own layout changes: follow released mid\n * animation and the content settled a card-height low. Judging the distance\n * against the box we were last a bottom FOR keeps the guard measuring the\n * user's move rather than our own. Only the shrink's own pixels are forgiven,\n * so a genuine drag inside the same tick still releases.\n */\nexport function evaluateAutoPin(args: {\n stick: boolean\n geom: ScrollGeom\n lastWriteTop: number\n epsilon?: number\n viewportShrink?: number\n /** Is a turn actually producing output right now?\n *\n * Follow means \"keep me at the end of a LIVE turn\". With nothing running there\n * is no output to follow, so a reader sitting above the bottom is not\n * following — and an automatic pin there is a yank with no cause, reported\n * from a phone as the transcript springing back after scrolling up about a\n * hundred pixels with nothing streaming.\n *\n * Defaults to `true` = assume a run is live, which keeps the behaviour of a\n * caller that has no run signal to give (the app-SDK chat surface). The chat\n * transcript passes the real thing. */\n runActive?: boolean\n /** Is an anchor restore currently OWNING the scroll position?\n *\n * A restore places the reader at an absolute offset and then re-lands it as\n * measurements arrive. An automatic pin during that window is a second owner\n * writing the same scroller, and the two fight: captured on a phone as\n * `WRITE autopin 3091->4245` answered by `WRITE settle 4245->3091`, twice in\n * 120ms, 1,154px each way. The settle won those rounds, but only because its\n * budget had not expired yet -- which is why the same switch sometimes landed\n * at the bottom and sometimes did not.\n *\n * Released rather than merely skipped, for the reason the idle branch below\n * gives: skipping leaves follow armed, so the next growth yanks the reader\n * from wherever the restore just put them. */\n restoreGate?: boolean\n /** Has hardware input -- wheel / touch / pointer / a scrolling key -- reached\n * the scroller since we last placed the reader at the bottom?\n *\n * False means the reader has done nothing, so a gap that opened while they\n * rest on our last write was opened by content -- a row settling from its\n * estimate, a code-block stand-in swapping for the highlighted block, the\n * top spacer repricing -- and is a gap WE owe them, not one they chose.\n *\n * The idle rule below cannot tell those apart from distance alone, and it\n * errs toward release, which was invisible wherever the browser's native\n * scroll anchoring quietly carried the reader through the growth. WebKit has\n * no scroll anchoring at all, so on an iPhone every entry into an idle\n * session paid the whole post-pin reprice as a displacement and then had\n * follow released on top of it: the transcript opened a viewport or more\n * above the end with nothing streaming to bring it back.\n *\n * Defaults to `true` = assume the reader may have moved, which is the\n * release-leaning legacy behaviour for a caller that has no input signal. */\n readerMovedSinceWrite?: boolean\n}): AutoPinResult {\n const { stick, geom, lastWriteTop } = args\n const epsilon = args.epsilon ?? SELF_SCROLL_EPSILON\n const viewportShrink = Math.max(0, args.viewportShrink ?? 0)\n const runActive = args.runActive ?? true\n const readerMovedSinceWrite = args.readerMovedSinceWrite ?? true\n const target = bottomTarget(geom)\n if (args.restoreGate) return { pin: false, stick: false, target }\n if (!stick) return { pin: false, stick: false, target }\n // The reader is resting exactly where we last put them and has given no input\n // since, so any gap is content settling under them -- carry them back, live\n // turn or not. Both conditions are load-bearing. Position alone would read our\n // own write as consent for a reader who wheeled up and happened to stop on it;\n // input alone would drag back a programmatic reveal -- a search hit, a pinned\n // prompt, find-in-page -- whose scroll event has not dispatched yet when a\n // height commit lands, since none of those touch the scroller's input\n // listeners. A reveal moves scrollTop off our write; a reprice does not.\n const restingOnOurWrite = lastWriteTop >= 0 && Math.abs(geom.scrollTop - lastWriteTop) <= epsilon\n if (!readerMovedSinceWrite && restingOnOurWrite) {\n return { pin: distanceFromBottom(geom) > atBottomEpsilon(), stick: true, target }\n }\n // Idle: release rather than merely skip the pin. Skipping would leave follow\n // armed, so the next turn to start would yank this reader to the bottom from\n // wherever they had settled — the same defect one event later.\n //\n // But distance alone cannot say WHO opened that gap, and the two causes want\n // opposite answers: a reader who scrolled up should be released, while a\n // reader the CONTENT moved away from should be carried back.\n if (!runActive && distanceFromBottom(geom) > atBottomEpsilon()) {\n // Released. The question this branch cannot answer from geometry -- did the\n // reader open this gap, or did the content -- is answered ABOVE by\n // `readerMovedSinceWrite`: reaching here means input or an unexplained\n // scroll has been seen since our last positioning, so a reader who is now\n // above the bottom while nothing runs is one who left it. Reading our own\n // last write as consent would be an automatic action authorizing itself;\n // the only evidence that the reader never moved is the absence of input,\n // and that is what the branch above requires.\n return { pin: false, stick: false, target }\n }\n // Release only on a genuine user scroll-UP: scrollTop dropped below our last\n // write AND we are now meaningfully away from the bottom. A pure content\n // SHRINK mid-stream (a partial markdown line re-parsing, a code fence opening\n // and reclassifying the block) clamps scrollTop below lastWriteTop too, but\n // leaves us still AT the new bottom (distance ~0). Without the distance guard\n // that shrink looked like a scroll-up and froze streaming follow — once\n // released, nothing re-armed stick for the rest of the response.\n if (\n lastWriteTop >= 0 &&\n geom.scrollTop < lastWriteTop - epsilon &&\n distanceFromBottom(geom) - viewportShrink > epsilon\n ) {\n return { pin: false, stick: false, target }\n }\n return { pin: Math.abs(geom.scrollTop - target) > atBottomEpsilon(), stick: true, target }\n}\n"],"mappings":";;;;;;;;;;;AA2CA,SAAgB,kBAA0B;CACxC,MAAM,MACJ,OAAO,WAAW,eAClB,OAAO,OAAO,qBAAqB,YACnC,OAAO,mBAAmB,IACtB,OAAO,mBACP;CACN,OAAO,KAAK,IAAI,GAAG,IAAI,MAAM,EAAG;AAClC;;AAUA,SAAgB,aAAa,MAA0B;CACrD,OAAO,KAAK,IAAI,GAAG,KAAK,eAAe,KAAK,YAAY;AAC1D;;AAGA,SAAgB,mBAAmB,MAA0B;CAC3D,OAAO,KAAK,eAAe,KAAK,YAAY,KAAK;AACnD;;AAGA,SAAgB,gBAAgB,MAAkB,WAA4B;CAC5E,OAAO,mBAAmB,IAAI,KAAK;AACrC;;;;;;AAOA,SAAgB,aACd,WACA,cACA,UAAA,GACS;CACT,OAAO,gBAAgB,KAAK,KAAK,IAAI,YAAY,YAAY,KAAK;AACpE;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,uBAAuB,MAmB3B;CACV,MAAM,EAAE,OAAO,cAAc,WAAW,eAAe,SAAS;CAChE,IAAI,CAAC,cAAc,OAAO;CAC1B,MAAM,OAAO,mBAAmB,IAAI;CAOpC,MAAM,qBACH,KAAK,kBAAkB,KAAK,gBAAgB,KAAK,aAAa,gBAAgB,gBAAgB;CACjG,IAAI,QAAQ,gBAAgB,GAAG,OAAO,oBAAoB,QAAQ;CAClE,IAAI,gBAAgB,GAAG,OAAO,QAAA;CAC9B,IAAI,YAAY,gBAAgB,IAAK,OAAO;CAY5C,IAAI,YAAY,gBAAgB,MAAO,QAAA,IAA4B,OAAO;CAC1E,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,gBAAgB,MAmDd;CAChB,MAAM,EAAE,OAAO,MAAM,iBAAiB;CACtC,MAAM,UAAU,KAAK,WAAA;CACrB,MAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,kBAAkB,CAAC;CAC3D,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,wBAAwB,KAAK,yBAAyB;CAC5D,MAAM,SAAS,aAAa,IAAI;CAChC,IAAI,KAAK,aAAa,OAAO;EAAE,KAAK;EAAO,OAAO;EAAO;CAAO;CAChE,IAAI,CAAC,OAAO,OAAO;EAAE,KAAK;EAAO,OAAO;EAAO;CAAO;CAStD,MAAM,oBAAoB,gBAAgB,KAAK,KAAK,IAAI,KAAK,YAAY,YAAY,KAAK;CAC1F,IAAI,CAAC,yBAAyB,mBAC5B,OAAO;EAAE,KAAK,mBAAmB,IAAI,IAAI,gBAAgB;EAAG,OAAO;EAAM;CAAO;CASlF,IAAI,CAAC,aAAa,mBAAmB,IAAI,IAAI,gBAAgB,GAS3D,OAAO;EAAE,KAAK;EAAO,OAAO;EAAO;CAAO;CAS5C,IACE,gBAAgB,KAChB,KAAK,YAAY,eAAe,WAChC,mBAAmB,IAAI,IAAI,iBAAiB,SAE5C,OAAO;EAAE,KAAK;EAAO,OAAO;EAAO;CAAO;CAE5C,OAAO;EAAE,KAAK,KAAK,IAAI,KAAK,YAAY,MAAM,IAAI,gBAAgB;EAAG,OAAO;EAAM;CAAO;AAC3F"}
@@ -0,0 +1,13 @@
1
+ /** Internal plain-scroller binding; host-owned restoration can disable follow. */
2
+ export declare function createScrollFollow(options: {
3
+ enabled: () => boolean;
4
+ resetKey: () => string | undefined;
5
+ threshold: () => number;
6
+ }): {
7
+ atBottom: import("solid-js").Accessor<boolean>;
8
+ onScroll: () => void;
9
+ jump: () => void;
10
+ bindScroller: (element: HTMLDivElement) => void;
11
+ bindContent: (element: HTMLDivElement) => void;
12
+ };
13
+ //# sourceMappingURL=scroll-follow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-follow.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/scroll-follow.ts"],"names":[],"mappings":"AA8BA,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,OAAO,EAAE,MAAM,OAAO,CAAA;IACtB,QAAQ,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IAClC,SAAS,EAAE,MAAM,MAAM,CAAA;CACxB;;;;4BA0F2B,cAAc;2BAGf,cAAc;EAIxC"}
@@ -0,0 +1,102 @@
1
+ import { bottomTarget, computeAtBottom, evaluateAutoPin, isSelfScroll, resolveUserScrollStick } from "./scroll-follow-core.js";
2
+ import { createEffect, createSignal, onCleanup } from "solid-js";
3
+ //#region src/components/conversation/scroll-follow.ts
4
+ var geometry = (element) => ({
5
+ scrollTop: element.scrollTop,
6
+ scrollHeight: element.scrollHeight,
7
+ clientHeight: element.clientHeight
8
+ });
9
+ /** Internal plain-scroller binding; host-owned restoration can disable follow. */
10
+ function createScrollFollow(options) {
11
+ const [atBottom, setAtBottom] = createSignal(true);
12
+ let scroller;
13
+ let content;
14
+ let stick = true;
15
+ let lastWriteTop = -1;
16
+ let lastWriteClientHeight = -1;
17
+ let previousTop = -1;
18
+ let lastScrollClientHeight = 0;
19
+ const writePin = (element, target) => {
20
+ element.scrollTop = target;
21
+ lastWriteTop = target;
22
+ lastWriteClientHeight = element.clientHeight;
23
+ previousTop = target;
24
+ };
25
+ const pinAuto = () => {
26
+ if (!scroller || !options.enabled()) return;
27
+ const geom = geometry(scroller);
28
+ const result = evaluateAutoPin({
29
+ stick,
30
+ geom,
31
+ lastWriteTop,
32
+ viewportShrink: lastWriteClientHeight >= 0 ? lastWriteClientHeight - geom.clientHeight : 0
33
+ });
34
+ stick = result.stick;
35
+ if (result.pin) writePin(scroller, result.target);
36
+ else if (result.stick) {
37
+ lastWriteTop = result.target;
38
+ lastWriteClientHeight = geom.clientHeight;
39
+ }
40
+ setAtBottom(computeAtBottom(geometry(scroller), options.threshold()));
41
+ };
42
+ const onScroll = () => {
43
+ if (!scroller || !options.enabled()) return;
44
+ const geom = geometry(scroller);
45
+ setAtBottom(computeAtBottom(geom, options.threshold()));
46
+ if (!isSelfScroll(geom.scrollTop, lastWriteTop)) {
47
+ stick = resolveUserScrollStick({
48
+ stick,
49
+ followOutput: true,
50
+ scrollTop: geom.scrollTop,
51
+ prevScrollTop: previousTop,
52
+ geom,
53
+ viewportGrowth: lastScrollClientHeight > 0 ? geom.clientHeight - lastScrollClientHeight : 0
54
+ });
55
+ if (!stick) {
56
+ lastWriteTop = -1;
57
+ lastWriteClientHeight = -1;
58
+ }
59
+ }
60
+ previousTop = geom.scrollTop;
61
+ lastScrollClientHeight = geom.clientHeight;
62
+ };
63
+ const jump = () => {
64
+ if (!scroller || !options.enabled()) return;
65
+ stick = true;
66
+ writePin(scroller, bottomTarget(geometry(scroller)));
67
+ setAtBottom(true);
68
+ scroller.focus({ preventScroll: true });
69
+ };
70
+ createEffect(() => {
71
+ options.resetKey();
72
+ const enabled = options.enabled();
73
+ stick = true;
74
+ lastWriteTop = -1;
75
+ lastWriteClientHeight = -1;
76
+ previousTop = -1;
77
+ lastScrollClientHeight = 0;
78
+ setAtBottom(true);
79
+ if (!enabled || !scroller) return;
80
+ writePin(scroller, bottomTarget(geometry(scroller)));
81
+ if (typeof ResizeObserver === "undefined") return;
82
+ const observer = new ResizeObserver(pinAuto);
83
+ observer.observe(scroller);
84
+ if (content) observer.observe(content);
85
+ onCleanup(() => observer.disconnect());
86
+ });
87
+ return {
88
+ atBottom,
89
+ onScroll,
90
+ jump,
91
+ bindScroller: (element) => {
92
+ scroller = element;
93
+ },
94
+ bindContent: (element) => {
95
+ content = element;
96
+ }
97
+ };
98
+ }
99
+ //#endregion
100
+ export { createScrollFollow };
101
+
102
+ //# sourceMappingURL=scroll-follow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-follow.js","names":[],"sources":["../../../src/components/conversation/scroll-follow.ts"],"sourcesContent":["/*\n * Substantially translated from KiroCrew website/src/app-sdk/useChatScrollFollow.ts,\n * pinned at 283e136c0f902e965a535a7c9548c57c7504fed0.\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * This product includes software developed at Amazon.com, Inc. (https://www.amazon.com/).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy at http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software distributed\n * under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n * CONDITIONS OF ANY KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations under the License.\n * Solid owner effects replace React refs/effects; native geometry and the\n * content/viewport observer contract are retained. No session store is copied.\n */\nimport { createEffect, createSignal, onCleanup } from 'solid-js'\nimport {\n bottomTarget,\n computeAtBottom,\n evaluateAutoPin,\n isSelfScroll,\n resolveUserScrollStick,\n} from './scroll-follow-core'\n\nconst geometry = (element: HTMLDivElement) => ({\n scrollTop: element.scrollTop,\n scrollHeight: element.scrollHeight,\n clientHeight: element.clientHeight,\n})\n\n/** Internal plain-scroller binding; host-owned restoration can disable follow. */\nexport function createScrollFollow(options: {\n enabled: () => boolean\n resetKey: () => string | undefined\n threshold: () => number\n}) {\n const [atBottom, setAtBottom] = createSignal(true)\n let scroller: HTMLDivElement | undefined\n let content: HTMLDivElement | undefined\n let stick = true\n let lastWriteTop = -1\n let lastWriteClientHeight = -1\n let previousTop = -1\n let lastScrollClientHeight = 0\n\n const writePin = (element: HTMLDivElement, target: number) => {\n // Instant writes keep the self-scroll reference synchronized. A smooth\n // animation would produce intermediate positions that resemble user input.\n element.scrollTop = target\n lastWriteTop = target\n lastWriteClientHeight = element.clientHeight\n previousTop = target\n }\n const pinAuto = () => {\n if (!scroller || !options.enabled()) return\n const geom = geometry(scroller)\n const result = evaluateAutoPin({\n stick,\n geom,\n lastWriteTop,\n viewportShrink: lastWriteClientHeight >= 0 ? lastWriteClientHeight - geom.clientHeight : 0,\n })\n stick = result.stick\n if (result.pin) writePin(scroller, result.target)\n else if (result.stick) {\n lastWriteTop = result.target\n lastWriteClientHeight = geom.clientHeight\n }\n setAtBottom(computeAtBottom(geometry(scroller), options.threshold()))\n }\n const onScroll = () => {\n if (!scroller || !options.enabled()) return\n const geom = geometry(scroller)\n setAtBottom(computeAtBottom(geom, options.threshold()))\n if (!isSelfScroll(geom.scrollTop, lastWriteTop)) {\n stick = resolveUserScrollStick({\n stick,\n followOutput: true,\n scrollTop: geom.scrollTop,\n prevScrollTop: previousTop,\n geom,\n viewportGrowth: lastScrollClientHeight > 0 ? geom.clientHeight - lastScrollClientHeight : 0,\n })\n if (!stick) {\n lastWriteTop = -1\n lastWriteClientHeight = -1\n }\n }\n previousTop = geom.scrollTop\n // This baseline belongs only to scroll events, not observer callbacks.\n // Advancing it during resize would erase the viewport-clamp evidence.\n lastScrollClientHeight = geom.clientHeight\n }\n const jump = () => {\n if (!scroller || !options.enabled()) return\n stick = true\n writePin(scroller, bottomTarget(geometry(scroller)))\n setAtBottom(true)\n // The jump control disappears at the bottom. Return its keyboard focus to\n // the transcript instead of leaving the reader on a detached button.\n scroller.focus({ preventScroll: true })\n }\n\n createEffect(() => {\n options.resetKey()\n const enabled = options.enabled()\n stick = true\n lastWriteTop = -1\n lastWriteClientHeight = -1\n previousTop = -1\n lastScrollClientHeight = 0\n setAtBottom(true)\n if (!enabled || !scroller) return\n writePin(scroller, bottomTarget(geometry(scroller)))\n if (typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(pinAuto)\n observer.observe(scroller)\n if (content) observer.observe(content)\n onCleanup(() => observer.disconnect())\n })\n\n return {\n atBottom,\n onScroll,\n jump,\n bindScroller: (element: HTMLDivElement) => {\n scroller = element\n },\n bindContent: (element: HTMLDivElement) => {\n content = element\n },\n }\n}\n"],"mappings":";;;AAwBA,IAAM,YAAY,aAA6B;CAC7C,WAAW,QAAQ;CACnB,cAAc,QAAQ;CACtB,cAAc,QAAQ;AACxB;;AAGA,SAAgB,mBAAmB,SAIhC;CACD,MAAM,CAAC,UAAU,eAAe,aAAa,IAAI;CACjD,IAAI;CACJ,IAAI;CACJ,IAAI,QAAQ;CACZ,IAAI,eAAe;CACnB,IAAI,wBAAwB;CAC5B,IAAI,cAAc;CAClB,IAAI,yBAAyB;CAE7B,MAAM,YAAY,SAAyB,WAAmB;EAG5D,QAAQ,YAAY;EACpB,eAAe;EACf,wBAAwB,QAAQ;EAChC,cAAc;CAChB;CACA,MAAM,gBAAgB;EACpB,IAAI,CAAC,YAAY,CAAC,QAAQ,QAAQ,GAAG;EACrC,MAAM,OAAO,SAAS,QAAQ;EAC9B,MAAM,SAAS,gBAAgB;GAC7B;GACA;GACA;GACA,gBAAgB,yBAAyB,IAAI,wBAAwB,KAAK,eAAe;EAC3F,CAAC;EACD,QAAQ,OAAO;EACf,IAAI,OAAO,KAAK,SAAS,UAAU,OAAO,MAAM;OAC3C,IAAI,OAAO,OAAO;GACrB,eAAe,OAAO;GACtB,wBAAwB,KAAK;EAC/B;EACA,YAAY,gBAAgB,SAAS,QAAQ,GAAG,QAAQ,UAAU,CAAC,CAAC;CACtE;CACA,MAAM,iBAAiB;EACrB,IAAI,CAAC,YAAY,CAAC,QAAQ,QAAQ,GAAG;EACrC,MAAM,OAAO,SAAS,QAAQ;EAC9B,YAAY,gBAAgB,MAAM,QAAQ,UAAU,CAAC,CAAC;EACtD,IAAI,CAAC,aAAa,KAAK,WAAW,YAAY,GAAG;GAC/C,QAAQ,uBAAuB;IAC7B;IACA,cAAc;IACd,WAAW,KAAK;IAChB,eAAe;IACf;IACA,gBAAgB,yBAAyB,IAAI,KAAK,eAAe,yBAAyB;GAC5F,CAAC;GACD,IAAI,CAAC,OAAO;IACV,eAAe;IACf,wBAAwB;GAC1B;EACF;EACA,cAAc,KAAK;EAGnB,yBAAyB,KAAK;CAChC;CACA,MAAM,aAAa;EACjB,IAAI,CAAC,YAAY,CAAC,QAAQ,QAAQ,GAAG;EACrC,QAAQ;EACR,SAAS,UAAU,aAAa,SAAS,QAAQ,CAAC,CAAC;EACnD,YAAY,IAAI;EAGhB,SAAS,MAAM,EAAE,eAAe,KAAK,CAAC;CACxC;CAEA,mBAAmB;EACjB,QAAQ,SAAS;EACjB,MAAM,UAAU,QAAQ,QAAQ;EAChC,QAAQ;EACR,eAAe;EACf,wBAAwB;EACxB,cAAc;EACd,yBAAyB;EACzB,YAAY,IAAI;EAChB,IAAI,CAAC,WAAW,CAAC,UAAU;EAC3B,SAAS,UAAU,aAAa,SAAS,QAAQ,CAAC,CAAC;EACnD,IAAI,OAAO,mBAAmB,aAAa;EAC3C,MAAM,WAAW,IAAI,eAAe,OAAO;EAC3C,SAAS,QAAQ,QAAQ;EACzB,IAAI,SAAS,SAAS,QAAQ,OAAO;EACrC,gBAAgB,SAAS,WAAW,CAAC;CACvC,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA,eAAe,YAA4B;GACzC,WAAW;EACb;EACA,cAAc,YAA4B;GACxC,UAAU;EACZ;CACF;AACF"}
package/dist/index.js CHANGED
@@ -67,6 +67,7 @@ import { SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHead
67
67
  import { SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavSection, SidebarNavTitle, sidebarNavItemClass, sidebarNavItemStateClass } from "./components/layout/sidebar-nav/sidebar-nav.js";
68
68
  import { StatusBar, StatusBarItem, StatusBarSpacer } from "./components/layout/status-bar/status-bar.js";
69
69
  import { TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle } from "./components/layout/top-bar/top-bar.js";
70
+ import { BusySendButton } from "./components/conversation/busy-send-button.js";
70
71
  import { ConversationAvatar } from "./components/conversation/conversation-avatar.js";
71
72
  import { ConversationSurface } from "./components/conversation/conversation-surface.js";
72
73
  import { ComposerAttachmentButton, ComposerHint, MessageComposer } from "./components/conversation/message-composer.js";
@@ -88,4 +89,4 @@ import { formatBytes, formatReleaseDate, plainTextFromMarkdown } from "./lib/ver
88
89
  import { UpdateDialog } from "./components/composites/update-dialog/update-dialog.js";
89
90
  import { WorkspaceMark } from "./components/composites/workspace-mark/workspace-mark.js";
90
91
  import { keyedRows } from "./lib/keyed-rows.js";
91
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposerAttachmentButton, ComposerHint, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ConversationAvatar, ConversationSurface, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EntityIcon, Field, FieldDescription, FieldError, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdGroup, Label, ListGroup, ListRow, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavSection, SidebarNavTitle, Skeleton, Slider, Spinner, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, UpdateDialog, WorkspaceMark, accentPresets, alertVariants, allTokens, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, cn, colorTokens, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, fontOptions, formatBytes, formatReleaseDate, initialsFrom, inlineScriptLiteral, itemVariants, keyedRows, monogram, motionTokens, parseDiff, plainTextFromMarkdown, radiusTokens, resolvedDensity, sheetVariants, sidebarNavItemClass, sidebarNavItemStateClass, statusDotVariants, surfaceInteractive, themeById, themeCssVariables, themeFamilies, themeScript, themesForAppearance, toast, toaster, toggleVariants, typographyTokens, undocumentedTokenAliases, useSideRail, useTheme, validateTheme, validateThemeRegistry, zIndexTokens };
92
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, BusySendButton, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposerAttachmentButton, ComposerHint, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ConversationAvatar, ConversationSurface, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EntityIcon, Field, FieldDescription, FieldError, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdGroup, Label, ListGroup, ListRow, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavSection, SidebarNavTitle, Skeleton, Slider, Spinner, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, UpdateDialog, WorkspaceMark, accentPresets, alertVariants, allTokens, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, cn, colorTokens, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, fontOptions, formatBytes, formatReleaseDate, initialsFrom, inlineScriptLiteral, itemVariants, keyedRows, monogram, motionTokens, parseDiff, plainTextFromMarkdown, radiusTokens, resolvedDensity, sheetVariants, sidebarNavItemClass, sidebarNavItemStateClass, statusDotVariants, surfaceInteractive, themeById, themeCssVariables, themeFamilies, themeScript, themesForAppearance, toast, toaster, toggleVariants, typographyTokens, undocumentedTokenAliases, useSideRail, useTheme, validateTheme, validateThemeRegistry, zIndexTokens };
@@ -34,6 +34,9 @@ export type TokenDefinition = {
34
34
  export declare const colorTokens: TokenDefinition[];
35
35
  export declare const radiusTokens: TokenDefinition[];
36
36
  export declare const typographyTokens: TokenDefinition[];
37
+ /** Height tokens also size both axes of icon controls through Tailwind's --size
38
+ * namespace. Padding has its own --spacing mapping. Nominal pixel descriptions
39
+ * use a 16px rem; rendered dimensions follow the consumer's root font size. */
37
40
  export declare const densityTokens: TokenDefinition[];
38
41
  export declare const elevationTokens: TokenDefinition[];
39
42
  export declare const motionTokens: TokenDefinition[];
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/lib/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,WAAW,GACX,aAAa,GACb,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,MAAM,CAAA;AAEV,MAAM,MAAM,eAAe,GAAG;IAC5B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;IACf,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,eAAe,EAwSxC,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,eAAe,EAezC,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,eAAe,EAyE7C,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,eAAe,EAmE1C,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAAe,EA2B5C,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,eAAe,EAkBzC,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,eAAe,EAyBzC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EAQ/C,CAAA;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,UAAU,EA+B3C,CAAA;AAEF,qCAAqC;AACrC,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAA;AAEV,yDAAyD;AACzD,eAAO,MAAM,SAAS,EAAE,eAAe,EAAuC,CAAA;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAAe,CAAA"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/lib/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,WAAW,GACX,aAAa,GACb,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,MAAM,CAAA;AAEV,MAAM,MAAM,eAAe,GAAG;IAC5B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;IACf,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,eAAe,EAwSxC,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,eAAe,EAezC,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,eAAe,EAyE7C,CAAA;AAED;;+EAE+E;AAC/E,eAAO,MAAM,aAAa,EAAE,eAAe,EAmE1C,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAAe,EA2B5C,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,eAAe,EAkBzC,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,eAAe,EAyBzC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EAQ/C,CAAA;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,UAAU,EA+B3C,CAAA;AAEF,qCAAqC;AACrC,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAA;AAEV,yDAAyD;AACzD,eAAO,MAAM,SAAS,EAAE,eAAe,EAAuC,CAAA;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAAe,CAAA"}
@@ -525,6 +525,9 @@ var typographyTokens = [
525
525
  description: "30px — the largest size in the system. Reserved for an empty-state headline."
526
526
  }
527
527
  ];
528
+ /** Height tokens also size both axes of icon controls through Tailwind's --size
529
+ * namespace. Padding has its own --spacing mapping. Nominal pixel descriptions
530
+ * use a 16px rem; rendered dimensions follow the consumer's root font size. */
528
531
  var densityTokens = [
529
532
  {
530
533
  name: "control-height-2xs",