@epicbagel/saas-kit 1.12.0 → 1.14.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/README.md CHANGED
@@ -439,3 +439,44 @@ export function Appearance() {
439
439
 
440
440
  MIT. Some components and styles are adapted from shadcn/ui, and the file
441
441
  drop handling from AI SDK Elements (Apache-2.0); see `THIRD_PARTY_NOTICES.md`.
442
+
443
+ ## Scroll navigation
444
+
445
+ `primitives/scroll-navigator` exports `ScrollNavigator`: pass an `items` array
446
+ with stable `id`, accessible `label`, optional `imageSrc`/`fallback`, and
447
+ `content`. It renders the list and a clickable avatar index while the list is
448
+ visible. The item at the top of the scroll viewport is highlighted; the index
449
+ scrolls independently for long lists. With insufficient left gutter, it uses
450
+ a compact horizontal row above the content. Single-item lists have no index.
451
+
452
+ It detects the nearest scrolling ancestor, supports an explicit `scrollRoot`
453
+ and `topOffset` for sticky headers, and offers `onNavigate` for custom scroll
454
+ controllers. Keyboard users Tab into the index, use arrows or Home/End, then
455
+ Enter to jump. Reduced-motion preferences are respected. No data or selected
456
+ record state changes when scrolling or navigating.
457
+
458
+ Inside a `MessageScrollerProvider`, use `MessageScrollerNavigator` from
459
+ `primitives/message-scroller` with the same props. It uses the thread's scroll
460
+ controller so incoming messages do not undo a jump into a long list.
461
+
462
+ ## Notes
463
+
464
+ `primitives/notes` provides a reusable `Notes` area and `NoteComposer` for any
465
+ record. Pass display data with stable IDs; the list owns rendering and count,
466
+ preserves order, and accepts author/date nodes and optional context, pinned,
467
+ voice/assistant and deletion metadata. Hosts resolve identity, authorize saves
468
+ and confirm deletions.
469
+
470
+ ```tsx
471
+ <Notes
472
+ notes={[{ id: "note-1", author: "You", date: "Today", text: "Follow up next week." }]}
473
+ composer={<NoteComposer onSubmit={saveNote} onVoice={openRecorder} />}
474
+ />
475
+ ```
476
+
477
+ Omit `onVoice` for a text-only composer. A rejected save retains the draft; all
478
+ conflicting actions are disabled during a save. `value` / `onValueChange` and
479
+ `editing` / `onEditingChange` support host-owned drafts, including failed voice
480
+ transcripts. A controlled host clears or replaces `value` after saving. Set
481
+ `maxLength` to the application's limit. `NoteList` and `NoteCard` are available
482
+ for existing compositions.
@@ -52,6 +52,26 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
52
52
  SOFTWARE.
53
53
  ```
54
54
 
55
+ ## Attributed notes
56
+
57
+ `primitives/notes` adapts the attributed note cards and voice/text composer
58
+ from the public `@recruiteros/recruit-ui` package, version 0.16.0. The adaptation
59
+ adds a generic notes-array API, controlled drafts, accessible labels and
60
+ serialized asynchronous saves. It contains no application data or persistence.
61
+
62
+ These adapted portions are licensed under the Apache License, Version 2.0,
63
+ reproduced below. The upstream notice is retained here:
64
+
65
+ ```
66
+ Recruit UI
67
+ Copyright 2026 RecruiterOS
68
+
69
+ RecruiterOS-authored portions are licensed under the Apache License,
70
+ Version 2.0. The UI primitives come from the separately installed
71
+ @epicbagel/saas-kit package (MIT). Third-party runtime dependencies retain
72
+ their own licenses and copyright notices. See THIRD_PARTY_NOTICES.md.
73
+ ```
74
+
55
75
  ## AI SDK Elements
56
76
 
57
77
  Source: https://github.com/vercel/ai-elements (`packages/elements/src/prompt-input.tsx`,
@@ -0,0 +1,256 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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
+
3
+
4
+
5
+
6
+ var _chunkWABCLRBRcjs = require('./chunk-WABCLRBR.cjs');
7
+
8
+
9
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
10
+
11
+ // src/primitives/scroll-navigator.tsx
12
+ var _react = require('react');
13
+ var _reactdom = require('react-dom');
14
+ var _jsxruntime = require('react/jsx-runtime');
15
+ var GAP = 12;
16
+ var RAIL_WIDTH = 56;
17
+ var ROW_HEIGHT = 64;
18
+ var initialPlacement = { mode: "row", top: 0, left: 0, width: 0, maxHeight: 0, active: 0, visible: false };
19
+ function findScrollRoot(element) {
20
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
21
+ if (/(auto|scroll|overlay)/.test(getComputedStyle(parent).overflowY) && parent.scrollHeight > parent.clientHeight) return parent;
22
+ }
23
+ return null;
24
+ }
25
+ function viewportBounds(root) {
26
+ const viewport = window.visualViewport;
27
+ const top = _nullishCoalesce(_optionalChain([viewport, 'optionalAccess', _ => _.offsetTop]), () => ( 0));
28
+ const left = _nullishCoalesce(_optionalChain([viewport, 'optionalAccess', _2 => _2.offsetLeft]), () => ( 0));
29
+ const bounds = { top, left, right: left + (_nullishCoalesce(_optionalChain([viewport, 'optionalAccess', _3 => _3.width]), () => ( innerWidth))), bottom: top + (_nullishCoalesce(_optionalChain([viewport, 'optionalAccess', _4 => _4.height]), () => ( innerHeight))) };
30
+ for (let parent = root; parent; parent = parent.parentElement) {
31
+ const style = getComputedStyle(parent);
32
+ if (parent !== root && !/(auto|scroll|hidden|clip)/.test(`${style.overflowX} ${style.overflowY}`)) continue;
33
+ const rect = parent.getBoundingClientRect();
34
+ bounds.top = Math.max(bounds.top, rect.top + parent.clientTop);
35
+ bounds.left = Math.max(bounds.left, rect.left + parent.clientLeft);
36
+ bounds.right = Math.min(bounds.right, rect.left + parent.clientLeft + parent.clientWidth);
37
+ bounds.bottom = Math.min(bounds.bottom, rect.top + parent.clientTop + parent.clientHeight);
38
+ }
39
+ return bounds;
40
+ }
41
+ var ScrollNavigator = _react.forwardRef.call(void 0, function ScrollNavigator2({
42
+ items,
43
+ label = "Browse items",
44
+ topOffset = 0,
45
+ scrollRoot,
46
+ onNavigate,
47
+ className,
48
+ ...props
49
+ }, forwardedRef) {
50
+ const rootRef = _react.useRef.call(void 0, null);
51
+ const targets = _react.useRef.call(void 0, /* @__PURE__ */ new Map());
52
+ const controls = _react.useRef.call(void 0, /* @__PURE__ */ new Map());
53
+ const indexRef = _react.useRef.call(void 0, null);
54
+ const scroller = _react.useRef.call(void 0, null);
55
+ const [placement, setPlacement] = _react.useState.call(void 0, initialPlacement);
56
+ const [focusId, setFocusId] = _react.useState.call(void 0, null);
57
+ const id = _react.useId.call(void 0, );
58
+ const keys = JSON.stringify(items.map((item) => item.id));
59
+ const navigable = items.length > 1;
60
+ _react.useLayoutEffect.call(void 0, () => {
61
+ const element = rootRef.current;
62
+ if (!element || !navigable) return;
63
+ let frame = 0;
64
+ let intersects = true;
65
+ const measure = () => {
66
+ frame = 0;
67
+ if (!intersects) {
68
+ setPlacement((previous) => previous.visible ? { ...previous, visible: false } : previous);
69
+ return;
70
+ }
71
+ const scrollElement = scrollRoot === void 0 ? findScrollRoot(element) : scrollRoot;
72
+ scroller.current = scrollElement;
73
+ const view = viewportBounds(scrollElement);
74
+ const box = element.getBoundingClientRect();
75
+ const mode = box.left - view.left >= RAIL_WIDTH + GAP ? "rail" : "row";
76
+ const top = Math.max(view.top + topOffset + GAP, box.top);
77
+ const bottom = Math.min(view.bottom, box.bottom) - GAP;
78
+ const visible = box.width > 0 && box.right > view.left && box.left < view.right && bottom - top >= ROW_HEIGHT;
79
+ const readingEdge = view.top + topOffset + GAP + (mode === "row" ? ROW_HEIGHT : 0);
80
+ const active = visible ? Math.max(0, items.findIndex((item) => (_nullishCoalesce(_optionalChain([targets, 'access', _5 => _5.current, 'access', _6 => _6.get, 'call', _7 => _7(item.id), 'optionalAccess', _8 => _8.getBoundingClientRect, 'call', _9 => _9(), 'access', _10 => _10.bottom]), () => ( 0))) > readingEdge + 1)) : 0;
81
+ const next = {
82
+ mode,
83
+ top,
84
+ left: mode === "rail" ? box.left - RAIL_WIDTH - GAP : Math.max(box.left, view.left),
85
+ width: mode === "rail" ? RAIL_WIDTH : Math.max(0, Math.min(box.right, view.right) - Math.max(box.left, view.left)),
86
+ maxHeight: Math.max(0, bottom - top),
87
+ active,
88
+ visible
89
+ };
90
+ setPlacement((previous) => Object.keys(next).every((key) => previous[key] === next[key]) ? previous : next);
91
+ };
92
+ const schedule = () => {
93
+ if (!frame) frame = requestAnimationFrame(measure);
94
+ };
95
+ const visibility = typeof IntersectionObserver === "undefined" ? null : new IntersectionObserver((entries) => {
96
+ intersects = _nullishCoalesce(_optionalChain([entries, 'access', _11 => _11[entries.length - 1], 'optionalAccess', _12 => _12.isIntersecting]), () => ( intersects));
97
+ schedule();
98
+ });
99
+ _optionalChain([visibility, 'optionalAccess', _13 => _13.observe, 'call', _14 => _14(element)]);
100
+ const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
101
+ for (let parent = element; parent; parent = parent.parentElement) {
102
+ _optionalChain([resize, 'optionalAccess', _15 => _15.observe, 'call', _16 => _16(parent)]);
103
+ }
104
+ targets.current.forEach((target) => _optionalChain([resize, 'optionalAccess', _17 => _17.observe, 'call', _18 => _18(target)]));
105
+ document.addEventListener("scroll", schedule, { passive: true, capture: true });
106
+ window.addEventListener("resize", schedule);
107
+ _optionalChain([window, 'access', _19 => _19.visualViewport, 'optionalAccess', _20 => _20.addEventListener, 'call', _21 => _21("resize", schedule)]);
108
+ _optionalChain([window, 'access', _22 => _22.visualViewport, 'optionalAccess', _23 => _23.addEventListener, 'call', _24 => _24("scroll", schedule)]);
109
+ measure();
110
+ return () => {
111
+ cancelAnimationFrame(frame);
112
+ _optionalChain([resize, 'optionalAccess', _25 => _25.disconnect, 'call', _26 => _26()]);
113
+ _optionalChain([visibility, 'optionalAccess', _27 => _27.disconnect, 'call', _28 => _28()]);
114
+ document.removeEventListener("scroll", schedule, true);
115
+ window.removeEventListener("resize", schedule);
116
+ _optionalChain([window, 'access', _29 => _29.visualViewport, 'optionalAccess', _30 => _30.removeEventListener, 'call', _31 => _31("resize", schedule)]);
117
+ _optionalChain([window, 'access', _32 => _32.visualViewport, 'optionalAccess', _33 => _33.removeEventListener, 'call', _34 => _34("scroll", schedule)]);
118
+ };
119
+ }, [keys, navigable, topOffset, scrollRoot]);
120
+ _react.useEffect.call(void 0, () => {
121
+ const index = indexRef.current;
122
+ const button = controls.current.get(_nullishCoalesce(_nullishCoalesce(focusId, () => ( _optionalChain([items, 'access', _35 => _35[placement.active], 'optionalAccess', _36 => _36.id]))), () => ( "")));
123
+ if (!index || !button || !placement.visible) return;
124
+ const horizontal = placement.mode === "row";
125
+ const start = horizontal ? button.offsetLeft : button.offsetTop;
126
+ const end = start + (horizontal ? button.offsetWidth : button.offsetHeight);
127
+ const position = horizontal ? index.scrollLeft : index.scrollTop;
128
+ const size = horizontal ? index.clientWidth : index.clientHeight;
129
+ const next = start < position ? start : end > position + size ? end - size : position;
130
+ if (horizontal) index.scrollLeft = next;
131
+ else index.scrollTop = next;
132
+ }, [placement.active, placement.mode, placement.visible, focusId, keys]);
133
+ function navigate(item) {
134
+ const target = targets.current.get(item.id);
135
+ if (!target) return;
136
+ const options = {
137
+ offset: topOffset + GAP + (placement.mode === "row" ? ROW_HEIGHT : 0),
138
+ behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "instant" : "smooth"
139
+ };
140
+ if (_optionalChain([onNavigate, 'optionalCall', _37 => _37(item.id, target, options)]) === true) return;
141
+ const scrollElement = scroller.current;
142
+ const view = viewportBounds(scrollElement);
143
+ const top = (_nullishCoalesce(_optionalChain([scrollElement, 'optionalAccess', _38 => _38.scrollTop]), () => ( window.scrollY))) + target.getBoundingClientRect().top - view.top - options.offset;
144
+ if (scrollElement) scrollElement.scrollTo({ top, behavior: options.behavior });
145
+ else window.scrollTo({ top, behavior: options.behavior });
146
+ }
147
+ const activeId = _optionalChain([items, 'access', _39 => _39[placement.active], 'optionalAccess', _40 => _40.id]);
148
+ const tabId = focusId && items.some((item) => item.id === focusId) ? focusId : activeId;
149
+ const row = placement.mode === "row";
150
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
151
+ "div",
152
+ {
153
+ ...props,
154
+ "data-slot": "scroll-navigator",
155
+ "data-orientation": row ? "horizontal" : "vertical",
156
+ ref: (element) => {
157
+ rootRef.current = element;
158
+ if (typeof forwardedRef === "function") forwardedRef(element);
159
+ else if (forwardedRef) forwardedRef.current = element;
160
+ },
161
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, "relative min-w-0", navigable && row && "pt-16", className),
162
+ children: [
163
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex min-w-0 flex-col gap-4", children: items.map((item, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
164
+ "div",
165
+ {
166
+ id: `${id}-${index}`,
167
+ "data-slot": "scroll-navigator-item",
168
+ "data-item-id": item.id,
169
+ ref: (element) => {
170
+ if (element) targets.current.set(item.id, element);
171
+ else targets.current.delete(item.id);
172
+ },
173
+ className: "min-w-0",
174
+ children: item.content
175
+ },
176
+ item.id
177
+ )) }),
178
+ navigable && placement.visible && _reactdom.createPortal.call(void 0,
179
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
180
+ "nav",
181
+ {
182
+ "aria-label": label,
183
+ "data-slot": "scroll-navigator-controls",
184
+ "data-orientation": row ? "horizontal" : "vertical",
185
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, "fixed z-20 flex rounded-full border border-border/60 bg-background/95 p-1 shadow-sm backdrop-blur-sm", row ? "items-center gap-1" : "flex-col items-center gap-1"),
186
+ style: { top: placement.top, left: placement.left, width: placement.width, maxHeight: placement.maxHeight },
187
+ onBlur: (event) => {
188
+ if (!event.currentTarget.contains(event.relatedTarget)) setFocusId(null);
189
+ },
190
+ children: [
191
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
192
+ "div",
193
+ {
194
+ ref: indexRef,
195
+ "data-slot": "scroll-navigator-index",
196
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, "relative flex min-h-0 min-w-0 gap-1 overscroll-contain p-0.5 scrollbar-none", row ? "flex-1 overflow-x-auto" : "flex-col overflow-y-auto"),
197
+ onKeyDown: (event) => {
198
+ const current = items.findIndex((item) => controls.current.get(item.id) === document.activeElement);
199
+ let next = current;
200
+ if (event.key === "Home") next = 0;
201
+ else if (event.key === "End") next = items.length - 1;
202
+ else if (event.key === (row ? "ArrowRight" : "ArrowDown")) next = Math.min(items.length - 1, current + 1);
203
+ else if (event.key === (row ? "ArrowLeft" : "ArrowUp")) next = Math.max(0, current - 1);
204
+ else return;
205
+ event.preventDefault();
206
+ const button = controls.current.get(items[next].id);
207
+ _optionalChain([button, 'optionalAccess', _41 => _41.focus, 'call', _42 => _42({ preventScroll: true })]);
208
+ const index = indexRef.current;
209
+ if (index && button) {
210
+ if (row) index.scrollLeft = button.offsetLeft - (index.clientWidth - button.offsetWidth) / 2;
211
+ else index.scrollTop = button.offsetTop - (index.clientHeight - button.offsetHeight) / 2;
212
+ }
213
+ },
214
+ children: items.map((item, index) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
215
+ "button",
216
+ {
217
+ type: "button",
218
+ ref: (element) => {
219
+ if (element) controls.current.set(item.id, element);
220
+ else controls.current.delete(item.id);
221
+ },
222
+ "aria-label": `${item.label}, ${index + 1} / ${items.length}`,
223
+ title: item.label,
224
+ "aria-controls": `${id}-${index}`,
225
+ "aria-current": item.id === activeId ? "location" : void 0,
226
+ tabIndex: item.id === tabId ? 0 : -1,
227
+ onFocus: () => setFocusId(item.id),
228
+ onClick: () => navigate(item),
229
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex size-11 shrink-0 items-center justify-center rounded-full border-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", item.id === activeId ? "border-primary bg-primary/10" : "border-transparent opacity-60 hover:bg-muted hover:opacity-100 focus-visible:opacity-100"),
230
+ children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkWABCLRBRcjs.Avatar, { size: "xs", "aria-hidden": "true", children: [
231
+ item.imageSrc ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWABCLRBRcjs.AvatarImage, { src: item.imageSrc, alt: "" }) : null,
232
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWABCLRBRcjs.AvatarFallback, { textSize: "micro", children: _nullishCoalesce(item.fallback, () => ( item.label.trim().split(/\s+/).map((word) => word[0]).slice(0, 2).join(""))) })
233
+ ] })
234
+ },
235
+ item.id
236
+ ))
237
+ }
238
+ ),
239
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { "aria-hidden": "true", className: _chunkKWLSBTB2cjs.cn.call(void 0, "shrink-0 text-center text-[10px] tabular-nums text-muted-foreground", row ? "px-2" : "pb-1"), children: [
240
+ placement.active + 1,
241
+ " / ",
242
+ items.length
243
+ ] })
244
+ ]
245
+ }
246
+ ),
247
+ document.body
248
+ )
249
+ ]
250
+ }
251
+ );
252
+ });
253
+
254
+
255
+
256
+ exports.ScrollNavigator = ScrollNavigator;
@@ -0,0 +1,53 @@
1
+ "use client";
2
+ import {
3
+ cn
4
+ } from "./chunk-G6SRSWXB.js";
5
+
6
+ // src/primitives/panel-section.tsx
7
+ import { forwardRef } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ var borders = {
10
+ none: "",
11
+ top: "border-t",
12
+ bottom: "border-b"
13
+ };
14
+ var paddings = {
15
+ none: "",
16
+ compact: "px-[22px] py-[18px]",
17
+ default: "px-[26px] py-[26px]"
18
+ };
19
+ var PanelSection = forwardRef(
20
+ function PanelSection2({ as, border = "none", padding = "default", paddingX = "none", paddingTop = "none", paddingBottom = "none", paddingY = "none", surface = "default", layout = "block", gap = "none", shrink = true, className, ...props }, ref) {
21
+ const Component = as ?? "section";
22
+ return /* @__PURE__ */ jsx(
23
+ Component,
24
+ {
25
+ ref,
26
+ className: cn(
27
+ borders[border],
28
+ paddings[padding],
29
+ paddingX === "md" && "px-[22px]",
30
+ paddingX === "lg" && "px-[26px]",
31
+ paddingTop === "sm" && "pt-2",
32
+ paddingTop === "md" && "pt-3",
33
+ paddingTop === "lg" && "pt-5",
34
+ paddingBottom === "xs" && "pb-1",
35
+ paddingBottom === "sm" && "pb-2",
36
+ paddingBottom === "md" && "pb-3",
37
+ paddingBottom === "lg" && "pb-4",
38
+ paddingY === "xl" && "py-12",
39
+ surface === "muted" && "bg-muted/40",
40
+ layout === "row" && "flex",
41
+ gap === "sm" && "gap-2",
42
+ !shrink && "shrink-0",
43
+ className
44
+ ),
45
+ ...props
46
+ }
47
+ );
48
+ }
49
+ );
50
+
51
+ export {
52
+ PanelSection
53
+ };
@@ -1,4 +1,7 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }"use client";
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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
+
3
+
4
+ var _chunk34AS3OCCcjs = require('./chunk-34AS3OCC.cjs');
2
5
 
3
6
 
4
7
  var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
@@ -18,6 +21,21 @@ var _jsxruntime = require('react/jsx-runtime');
18
21
  function MessageScrollerProvider(props) {
19
22
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _messagescroller.MessageScroller.Provider, { ...props });
20
23
  }
24
+ function MessageScrollerNavigator(props) {
25
+ const { scrollToMessage } = _messagescroller.useMessageScroller.call(void 0, );
26
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk34AS3OCCcjs.ScrollNavigator, { ...props, onNavigate: (id, target, options) => {
27
+ if (_optionalChain([props, 'access', _ => _.onNavigate, 'optionalCall', _2 => _2(id, target, options)]) === true) return true;
28
+ const message = target.closest("[data-message-id]");
29
+ const messageId = _optionalChain([message, 'optionalAccess', _3 => _3.dataset, 'access', _4 => _4.messageId]);
30
+ if (!messageId || !_optionalChain([message, 'optionalAccess', _5 => _5.parentElement])) return false;
31
+ const padding = Number.parseFloat(getComputedStyle(message.parentElement).paddingTop) || 0;
32
+ return scrollToMessage(messageId, {
33
+ align: "start",
34
+ behavior: options.behavior,
35
+ scrollMargin: message.getBoundingClientRect().top - target.getBoundingClientRect().top + options.offset - padding
36
+ });
37
+ } });
38
+ }
21
39
  function MessageScroller({
22
40
  className,
23
41
  ...props
@@ -129,4 +147,5 @@ function MessageScrollerButton({
129
147
 
130
148
 
131
149
 
132
- exports.useMessageScroller = _messagescroller.useMessageScroller; exports.useMessageScrollerScrollable = _messagescroller.useMessageScrollerScrollable; exports.useMessageScrollerVisibility = _messagescroller.useMessageScrollerVisibility; exports.MessageScrollerProvider = MessageScrollerProvider; exports.MessageScroller = MessageScroller; exports.MessageScrollerViewport = MessageScrollerViewport; exports.MessageScrollerContent = MessageScrollerContent; exports.MessageScrollerItem = MessageScrollerItem; exports.MessageScrollerButton = MessageScrollerButton;
150
+
151
+ exports.useMessageScroller = _messagescroller.useMessageScroller; exports.useMessageScrollerScrollable = _messagescroller.useMessageScrollerScrollable; exports.useMessageScrollerVisibility = _messagescroller.useMessageScrollerVisibility; exports.MessageScrollerProvider = MessageScrollerProvider; exports.MessageScrollerNavigator = MessageScrollerNavigator; exports.MessageScroller = MessageScroller; exports.MessageScrollerViewport = MessageScrollerViewport; exports.MessageScrollerContent = MessageScrollerContent; exports.MessageScrollerItem = MessageScrollerItem; exports.MessageScrollerButton = MessageScrollerButton;
@@ -0,0 +1,51 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+ var _chunkVGTINL5Qcjs = require('./chunk-VGTINL5Q.cjs');
5
+
6
+
7
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
8
+
9
+ // src/primitives/section-header.tsx
10
+ var _react = require('react');
11
+ var _classvarianceauthority = require('class-variance-authority');
12
+ var _jsxruntime = require('react/jsx-runtime');
13
+ var sectionHeaderVariants = _classvarianceauthority.cva.call(void 0, "flex items-center gap-1", {
14
+ variants: {
15
+ variant: {
16
+ muted: "text-xs font-semibold text-muted-foreground uppercase tracking-wide",
17
+ plain: "text-sm font-medium text-foreground"
18
+ },
19
+ spacing: {
20
+ tight: "mb-2",
21
+ loose: "mb-3",
22
+ none: ""
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ variant: "muted",
27
+ spacing: "tight"
28
+ }
29
+ });
30
+ var SectionHeader = _react.forwardRef.call(void 0,
31
+ function SectionHeader2({ icon, label, count, action, variant, spacing, className, ...rest }, ref) {
32
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
33
+ "h3",
34
+ {
35
+ ref,
36
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, sectionHeaderVariants({ variant, spacing }), className),
37
+ ...rest,
38
+ children: [
39
+ icon,
40
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: label }),
41
+ typeof count === "number" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVGTINL5Qcjs.CountBadge, { value: count, variant: "secondary" }),
42
+ action && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "ml-auto", children: action })
43
+ ]
44
+ }
45
+ );
46
+ }
47
+ );
48
+
49
+
50
+
51
+ exports.SectionHeader = SectionHeader;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import {
3
+ CountBadge
4
+ } from "./chunk-H7TETFLB.js";
5
+ import {
6
+ cn
7
+ } from "./chunk-G6SRSWXB.js";
8
+
9
+ // src/primitives/section-header.tsx
10
+ import { forwardRef } from "react";
11
+ import { cva } from "class-variance-authority";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ var sectionHeaderVariants = cva("flex items-center gap-1", {
14
+ variants: {
15
+ variant: {
16
+ muted: "text-xs font-semibold text-muted-foreground uppercase tracking-wide",
17
+ plain: "text-sm font-medium text-foreground"
18
+ },
19
+ spacing: {
20
+ tight: "mb-2",
21
+ loose: "mb-3",
22
+ none: ""
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ variant: "muted",
27
+ spacing: "tight"
28
+ }
29
+ });
30
+ var SectionHeader = forwardRef(
31
+ function SectionHeader2({ icon, label, count, action, variant, spacing, className, ...rest }, ref) {
32
+ return /* @__PURE__ */ jsxs(
33
+ "h3",
34
+ {
35
+ ref,
36
+ className: cn(sectionHeaderVariants({ variant, spacing }), className),
37
+ ...rest,
38
+ children: [
39
+ icon,
40
+ /* @__PURE__ */ jsx("span", { children: label }),
41
+ typeof count === "number" && /* @__PURE__ */ jsx(CountBadge, { value: count, variant: "secondary" }),
42
+ action && /* @__PURE__ */ jsx("span", { className: "ml-auto", children: action })
43
+ ]
44
+ }
45
+ );
46
+ }
47
+ );
48
+
49
+ export {
50
+ SectionHeader
51
+ };
@@ -1,4 +1,7 @@
1
1
  "use client";
2
+ import {
3
+ ScrollNavigator
4
+ } from "./chunk-VT2WDFSU.js";
2
5
  import {
3
6
  Button
4
7
  } from "./chunk-W326GKML.js";
@@ -18,6 +21,21 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
21
  function MessageScrollerProvider(props) {
19
22
  return /* @__PURE__ */ jsx(MessageScrollerPrimitive.Provider, { ...props });
20
23
  }
24
+ function MessageScrollerNavigator(props) {
25
+ const { scrollToMessage } = useMessageScroller();
26
+ return /* @__PURE__ */ jsx(ScrollNavigator, { ...props, onNavigate: (id, target, options) => {
27
+ if (props.onNavigate?.(id, target, options) === true) return true;
28
+ const message = target.closest("[data-message-id]");
29
+ const messageId = message?.dataset.messageId;
30
+ if (!messageId || !message?.parentElement) return false;
31
+ const padding = Number.parseFloat(getComputedStyle(message.parentElement).paddingTop) || 0;
32
+ return scrollToMessage(messageId, {
33
+ align: "start",
34
+ behavior: options.behavior,
35
+ scrollMargin: message.getBoundingClientRect().top - target.getBoundingClientRect().top + options.offset - padding
36
+ });
37
+ } });
38
+ }
21
39
  function MessageScroller({
22
40
  className,
23
41
  ...props
@@ -124,6 +142,7 @@ export {
124
142
  useMessageScrollerScrollable,
125
143
  useMessageScrollerVisibility,
126
144
  MessageScrollerProvider,
145
+ MessageScrollerNavigator,
127
146
  MessageScroller,
128
147
  MessageScrollerViewport,
129
148
  MessageScrollerContent,
@@ -0,0 +1,53 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }"use client";
2
+
3
+
4
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
5
+
6
+ // src/primitives/panel-section.tsx
7
+ var _react = require('react');
8
+ var _jsxruntime = require('react/jsx-runtime');
9
+ var borders = {
10
+ none: "",
11
+ top: "border-t",
12
+ bottom: "border-b"
13
+ };
14
+ var paddings = {
15
+ none: "",
16
+ compact: "px-[22px] py-[18px]",
17
+ default: "px-[26px] py-[26px]"
18
+ };
19
+ var PanelSection = _react.forwardRef.call(void 0,
20
+ function PanelSection2({ as, border = "none", padding = "default", paddingX = "none", paddingTop = "none", paddingBottom = "none", paddingY = "none", surface = "default", layout = "block", gap = "none", shrink = true, className, ...props }, ref) {
21
+ const Component = _nullishCoalesce(as, () => ( "section"));
22
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
23
+ Component,
24
+ {
25
+ ref,
26
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
27
+ borders[border],
28
+ paddings[padding],
29
+ paddingX === "md" && "px-[22px]",
30
+ paddingX === "lg" && "px-[26px]",
31
+ paddingTop === "sm" && "pt-2",
32
+ paddingTop === "md" && "pt-3",
33
+ paddingTop === "lg" && "pt-5",
34
+ paddingBottom === "xs" && "pb-1",
35
+ paddingBottom === "sm" && "pb-2",
36
+ paddingBottom === "md" && "pb-3",
37
+ paddingBottom === "lg" && "pb-4",
38
+ paddingY === "xl" && "py-12",
39
+ surface === "muted" && "bg-muted/40",
40
+ layout === "row" && "flex",
41
+ gap === "sm" && "gap-2",
42
+ !shrink && "shrink-0",
43
+ className
44
+ ),
45
+ ...props
46
+ }
47
+ );
48
+ }
49
+ );
50
+
51
+
52
+
53
+ exports.PanelSection = PanelSection;