@vllnt/ui 0.2.1-canary.9e6a7be → 0.2.1-canary.ab2c69a

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.
@@ -0,0 +1,348 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ createContext,
5
+ forwardRef,
6
+ useCallback,
7
+ useContext,
8
+ useEffect,
9
+ useMemo,
10
+ useRef,
11
+ useState
12
+ } from "react";
13
+ import { ArrowDown, RefreshCw, ThumbsDown, ThumbsUp } from "lucide-react";
14
+ import { cn } from "../../lib/utils";
15
+ import { ThinkingBlock } from "../thinking-block";
16
+ const ConversationThreadContext = createContext(null);
17
+ function useConversationThreadContext() {
18
+ const ctx = useContext(ConversationThreadContext);
19
+ if (!ctx) {
20
+ throw new Error(
21
+ "ConversationThread compound components must be used within <ConversationThread>"
22
+ );
23
+ }
24
+ return ctx;
25
+ }
26
+ function MessageActions({ messageId }) {
27
+ const { onFeedback, onRetry } = useConversationThreadContext();
28
+ return /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-1", children: [
29
+ onRetry ? /* @__PURE__ */ jsx(
30
+ "button",
31
+ {
32
+ "aria-label": "Retry message",
33
+ className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
34
+ onClick: () => {
35
+ onRetry(messageId);
36
+ },
37
+ type: "button",
38
+ children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-3 w-3" })
39
+ }
40
+ ) : null,
41
+ onFeedback ? /* @__PURE__ */ jsxs(Fragment, { children: [
42
+ /* @__PURE__ */ jsx(
43
+ "button",
44
+ {
45
+ "aria-label": "Positive feedback",
46
+ className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
47
+ onClick: () => {
48
+ onFeedback(messageId, "positive");
49
+ },
50
+ type: "button",
51
+ children: /* @__PURE__ */ jsx(ThumbsUp, { className: "h-3 w-3" })
52
+ }
53
+ ),
54
+ /* @__PURE__ */ jsx(
55
+ "button",
56
+ {
57
+ "aria-label": "Negative feedback",
58
+ className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
59
+ onClick: () => {
60
+ onFeedback(messageId, "negative");
61
+ },
62
+ type: "button",
63
+ children: /* @__PURE__ */ jsx(ThumbsDown, { className: "h-3 w-3" })
64
+ }
65
+ )
66
+ ] }) : null
67
+ ] });
68
+ }
69
+ function MessageItem({ message }) {
70
+ const isUser = message.role === "user";
71
+ return /* @__PURE__ */ jsx(
72
+ "div",
73
+ {
74
+ className: cn(
75
+ "mb-4 flex gap-3",
76
+ isUser ? "justify-end" : "justify-start"
77
+ ),
78
+ children: /* @__PURE__ */ jsxs(
79
+ "div",
80
+ {
81
+ className: cn(
82
+ "max-w-[80%] rounded-2xl px-4 py-3 text-sm",
83
+ isUser ? "rounded-br-sm bg-primary text-primary-foreground" : "rounded-bl-sm bg-muted text-foreground"
84
+ ),
85
+ children: [
86
+ !isUser && message.thinking ? /* @__PURE__ */ jsx(
87
+ ThinkingBlock,
88
+ {
89
+ isStreaming: message.isStreaming,
90
+ thinking: message.thinking
91
+ }
92
+ ) : null,
93
+ message.toolCalls && message.toolCalls.length > 0 ? /* @__PURE__ */ jsx(
94
+ "ul",
95
+ {
96
+ "aria-label": "Tool calls",
97
+ className: "mb-2 flex flex-col gap-1 text-xs text-muted-foreground",
98
+ children: message.toolCalls.map((toolCall) => /* @__PURE__ */ jsx("li", { className: "font-mono", children: toolCall.name }, toolCall.id))
99
+ }
100
+ ) : null,
101
+ /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap leading-relaxed", children: message.content }),
102
+ isUser ? null : /* @__PURE__ */ jsx(MessageActions, { messageId: message.id })
103
+ ]
104
+ }
105
+ )
106
+ }
107
+ );
108
+ }
109
+ function useConversationScroll(messages, isStreaming) {
110
+ const scrollContainerRef = useRef(null);
111
+ const messagesEndRef = useRef(null);
112
+ const isAtBottomRef = useRef(true);
113
+ const [isAtBottom, setIsAtBottom] = useState(true);
114
+ const scrollToBottom = useCallback(() => {
115
+ const element = messagesEndRef.current;
116
+ if (element && typeof element.scrollIntoView === "function") {
117
+ element.scrollIntoView({ behavior: "smooth" });
118
+ }
119
+ }, []);
120
+ const scrollToBottomInstant = useCallback(() => {
121
+ const element = messagesEndRef.current;
122
+ if (element && typeof element.scrollIntoView === "function") {
123
+ element.scrollIntoView({ behavior: "instant" });
124
+ }
125
+ }, []);
126
+ const handleScroll = useCallback(() => {
127
+ const container = scrollContainerRef.current;
128
+ if (!container) return;
129
+ const { clientHeight, scrollHeight, scrollTop } = container;
130
+ const nearBottom = scrollHeight - scrollTop - clientHeight <= 100;
131
+ isAtBottomRef.current = nearBottom;
132
+ setIsAtBottom(nearBottom);
133
+ }, []);
134
+ useEffect(() => {
135
+ if (!isAtBottomRef.current) return;
136
+ scrollToBottomInstant();
137
+ }, [messages, scrollToBottomInstant]);
138
+ useEffect(() => {
139
+ if (!isStreaming || !isAtBottomRef.current) return;
140
+ scrollToBottomInstant();
141
+ }, [isStreaming, scrollToBottomInstant]);
142
+ return {
143
+ handleScroll,
144
+ isAtBottom,
145
+ messagesEndRef,
146
+ scrollContainerRef,
147
+ scrollToBottom
148
+ };
149
+ }
150
+ const ConversationThread = forwardRef(
151
+ ({
152
+ children,
153
+ className,
154
+ isStreaming = false,
155
+ messages,
156
+ onFeedback,
157
+ onRetry,
158
+ onSend
159
+ }, reference) => {
160
+ const {
161
+ handleScroll,
162
+ isAtBottom,
163
+ messagesEndRef,
164
+ scrollContainerRef,
165
+ scrollToBottom
166
+ } = useConversationScroll(messages, isStreaming);
167
+ const contextValue = useMemo(
168
+ () => ({
169
+ handleScroll,
170
+ isAtBottom,
171
+ isStreaming,
172
+ messages,
173
+ messagesEndRef,
174
+ onFeedback,
175
+ onRetry,
176
+ onSend,
177
+ scrollContainerRef,
178
+ scrollToBottom
179
+ }),
180
+ [
181
+ handleScroll,
182
+ isAtBottom,
183
+ isStreaming,
184
+ messages,
185
+ messagesEndRef,
186
+ onFeedback,
187
+ onRetry,
188
+ onSend,
189
+ scrollContainerRef,
190
+ scrollToBottom
191
+ ]
192
+ );
193
+ return /* @__PURE__ */ jsx(ConversationThreadContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
194
+ "div",
195
+ {
196
+ className: cn("flex h-full flex-col overflow-hidden", className),
197
+ ref: reference,
198
+ children
199
+ }
200
+ ) });
201
+ }
202
+ );
203
+ ConversationThread.displayName = "ConversationThread";
204
+ const ConversationHeader = forwardRef(({ children, className }, reference) => {
205
+ return /* @__PURE__ */ jsx(
206
+ "div",
207
+ {
208
+ className: cn("flex shrink-0 items-center border-b px-4 py-3", className),
209
+ ref: reference,
210
+ children
211
+ }
212
+ );
213
+ });
214
+ ConversationHeader.displayName = "ConversationHeader";
215
+ const ConversationTitle = forwardRef(({ children, className }, reference) => {
216
+ return /* @__PURE__ */ jsx(
217
+ "h2",
218
+ {
219
+ className: cn("text-sm font-semibold leading-none", className),
220
+ ref: reference,
221
+ children
222
+ }
223
+ );
224
+ });
225
+ ConversationTitle.displayName = "ConversationTitle";
226
+ const ConversationMessages = forwardRef(({ children, className }, reference) => {
227
+ const { handleScroll, messages, messagesEndRef, scrollContainerRef } = useConversationThreadContext();
228
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative min-h-0 flex-1", className), ref: reference, children: [
229
+ /* @__PURE__ */ jsx(
230
+ "div",
231
+ {
232
+ "aria-label": "Conversation messages",
233
+ "aria-live": "polite",
234
+ className: "absolute inset-0 overflow-y-auto",
235
+ onScroll: handleScroll,
236
+ ref: scrollContainerRef,
237
+ role: "log",
238
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col p-4", children: [
239
+ messages.map((message) => /* @__PURE__ */ jsx(MessageItem, { message }, message.id)),
240
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", ref: messagesEndRef })
241
+ ] })
242
+ }
243
+ ),
244
+ children
245
+ ] });
246
+ });
247
+ ConversationMessages.displayName = "ConversationMessages";
248
+ const ConversationEmpty = forwardRef(({ children, className }, reference) => {
249
+ const { messages } = useConversationThreadContext();
250
+ if (messages.length > 0) return null;
251
+ return /* @__PURE__ */ jsx(
252
+ "div",
253
+ {
254
+ className: cn(
255
+ "pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-4 p-8",
256
+ className
257
+ ),
258
+ ref: reference,
259
+ children: /* @__PURE__ */ jsx("div", { className: "pointer-events-auto flex flex-col items-center gap-4", children })
260
+ }
261
+ );
262
+ });
263
+ ConversationEmpty.displayName = "ConversationEmpty";
264
+ const ConversationSuggestions = forwardRef(({ className, suggestions = [] }, reference) => {
265
+ const { onSend } = useConversationThreadContext();
266
+ return /* @__PURE__ */ jsx(
267
+ "div",
268
+ {
269
+ className: cn("flex flex-wrap justify-center gap-2", className),
270
+ ref: reference,
271
+ children: suggestions.map((suggestion) => /* @__PURE__ */ jsx(
272
+ "button",
273
+ {
274
+ className: "rounded-full border bg-background px-4 py-2 text-sm transition-colors hover:bg-muted",
275
+ onClick: () => onSend?.(suggestion),
276
+ type: "button",
277
+ children: suggestion
278
+ },
279
+ suggestion
280
+ ))
281
+ }
282
+ );
283
+ });
284
+ ConversationSuggestions.displayName = "ConversationSuggestions";
285
+ const ConversationScrollButton = forwardRef(({ className }, reference) => {
286
+ const { isAtBottom, scrollToBottom } = useConversationThreadContext();
287
+ if (isAtBottom) return null;
288
+ return /* @__PURE__ */ jsx(
289
+ "button",
290
+ {
291
+ "aria-label": "Scroll to bottom",
292
+ className: cn(
293
+ "absolute bottom-4 right-4 flex h-8 w-8 items-center justify-center rounded-full border bg-background shadow-md transition-colors hover:bg-muted",
294
+ className
295
+ ),
296
+ onClick: scrollToBottom,
297
+ ref: reference,
298
+ type: "button",
299
+ children: /* @__PURE__ */ jsx(ArrowDown, { className: "h-4 w-4" })
300
+ }
301
+ );
302
+ });
303
+ ConversationScrollButton.displayName = "ConversationScrollButton";
304
+ const ConversationLoading = forwardRef(({ className }, reference) => {
305
+ const { isStreaming, messages } = useConversationThreadContext();
306
+ const lastMessage = messages.at(-1);
307
+ if (!isStreaming || lastMessage?.role !== "assistant") return null;
308
+ return /* @__PURE__ */ jsxs(
309
+ "div",
310
+ {
311
+ "aria-label": "Assistant is typing",
312
+ className: cn(
313
+ "absolute bottom-4 left-4 flex items-center gap-1",
314
+ className
315
+ ),
316
+ ref: reference,
317
+ role: "status",
318
+ children: [
319
+ /* @__PURE__ */ jsx(
320
+ "span",
321
+ {
322
+ className: "h-2 w-2 animate-bounce rounded-full bg-muted-foreground",
323
+ style: { animationDelay: "-0.3s" }
324
+ }
325
+ ),
326
+ /* @__PURE__ */ jsx(
327
+ "span",
328
+ {
329
+ className: "h-2 w-2 animate-bounce rounded-full bg-muted-foreground",
330
+ style: { animationDelay: "-0.15s" }
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-muted-foreground" })
334
+ ]
335
+ }
336
+ );
337
+ });
338
+ ConversationLoading.displayName = "ConversationLoading";
339
+ export {
340
+ ConversationEmpty,
341
+ ConversationHeader,
342
+ ConversationLoading,
343
+ ConversationMessages,
344
+ ConversationScrollButton,
345
+ ConversationSuggestions,
346
+ ConversationThread,
347
+ ConversationTitle
348
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ ConversationEmpty,
3
+ ConversationHeader,
4
+ ConversationLoading,
5
+ ConversationMessages,
6
+ ConversationScrollButton,
7
+ ConversationSuggestions,
8
+ ConversationThread,
9
+ ConversationTitle
10
+ } from "./conversation-thread";
11
+ export {
12
+ ConversationEmpty,
13
+ ConversationHeader,
14
+ ConversationLoading,
15
+ ConversationMessages,
16
+ ConversationScrollButton,
17
+ ConversationSuggestions,
18
+ ConversationThread,
19
+ ConversationTitle
20
+ };
@@ -306,6 +306,9 @@ import {
306
306
  ChatDockSection
307
307
  } from "./chat-dock-section";
308
308
  import { GlassPanel } from "./glass-panel";
309
+ import {
310
+ InfinitePlane
311
+ } from "./infinite-plane";
309
312
  import { LeftRail } from "./left-rail";
310
313
  import {
311
314
  MiniMapPanel
@@ -323,6 +326,12 @@ import { Sidebar } from "./sidebar";
323
326
  import { SidebarProvider, useSidebar } from "./sidebar-provider";
324
327
  import { TableOfContents } from "./table-of-contents";
325
328
  import { TopBar } from "./top-bar";
329
+ import {
330
+ ViewportBookmarks
331
+ } from "./viewport-bookmarks";
332
+ import {
333
+ WorldBreadcrumbs
334
+ } from "./world-breadcrumbs";
326
335
  import { ZoomHUD } from "./zoom-hud";
327
336
  import {
328
337
  ActivityLog
@@ -539,6 +548,25 @@ import {
539
548
  ObjectCard
540
549
  } from "./object-card";
541
550
  import { ObjectHandle } from "./object-handle";
551
+ import {
552
+ PresenceStack
553
+ } from "./presence-stack";
554
+ import {
555
+ SelectionPresence
556
+ } from "./selection-presence";
557
+ import {
558
+ ThreadBubble
559
+ } from "./thread-bubble";
560
+ import {
561
+ ConversationEmpty,
562
+ ConversationHeader,
563
+ ConversationLoading,
564
+ ConversationMessages,
565
+ ConversationScrollButton,
566
+ ConversationSuggestions,
567
+ ConversationThread,
568
+ ConversationTitle
569
+ } from "./conversation-thread";
542
570
  import { InlineInput } from "./inline-input";
543
571
  import {
544
572
  ModelSelector
@@ -644,6 +672,14 @@ export {
644
672
  ContextMenuSubContent,
645
673
  ContextMenuSubTrigger,
646
674
  ContextMenuTrigger,
675
+ ConversationEmpty,
676
+ ConversationHeader,
677
+ ConversationLoading,
678
+ ConversationMessages,
679
+ ConversationScrollButton,
680
+ ConversationSuggestions,
681
+ ConversationThread,
682
+ ConversationTitle,
647
683
  CookieConsent,
648
684
  CountdownTimer,
649
685
  CreditBadge,
@@ -719,6 +755,7 @@ export {
719
755
  HoverCard,
720
756
  HoverCardContent,
721
757
  HoverCardTrigger,
758
+ InfinitePlane,
722
759
  InlineInput,
723
760
  Input,
724
761
  InputOTP,
@@ -780,6 +817,7 @@ export {
780
817
  PopoverContent,
781
818
  PopoverTrigger,
782
819
  Prerequisites,
820
+ PresenceStack,
783
821
  ProTip,
784
822
  ProfileSection,
785
823
  ProgressBar,
@@ -817,6 +855,7 @@ export {
817
855
  SelectSeparator,
818
856
  SelectTrigger,
819
857
  SelectValue,
858
+ SelectionPresence,
820
859
  Separator,
821
860
  SeverityBadge,
822
861
  ShareDialog,
@@ -872,6 +911,7 @@ export {
872
911
  ThemeProvider,
873
912
  ThemeToggle,
874
913
  ThinkingBlock,
914
+ ThreadBubble,
875
915
  TickerTape,
876
916
  Toast,
877
917
  ToastAction,
@@ -898,9 +938,11 @@ export {
898
938
  UsageBreakdown,
899
939
  VideoEmbed,
900
940
  ViewSwitcher,
941
+ ViewportBookmarks,
901
942
  WalletCard,
902
943
  Watchlist,
903
944
  WorkspaceSwitcher,
945
+ WorldBreadcrumbs,
904
946
  WorldClockBar,
905
947
  ZoomHUD,
906
948
  alertVariants,
@@ -0,0 +1,6 @@
1
+ import {
2
+ InfinitePlane
3
+ } from "./infinite-plane";
4
+ export {
5
+ InfinitePlane
6
+ };
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const DEFAULT_LABELS = {
8
+ region: "Infinite plane"
9
+ };
10
+ const safeSpacing = (value) => value < 4 ? 4 : value;
11
+ const safeZoom = (value) => {
12
+ if (value < 0.1) {
13
+ return 0.1;
14
+ }
15
+ if (value > 10) {
16
+ return 10;
17
+ }
18
+ return value;
19
+ };
20
+ const buildBackground = (input) => {
21
+ if (input.pattern === "blank") {
22
+ return {};
23
+ }
24
+ const size = safeSpacing(input.spacing) * safeZoom(input.zoom);
25
+ const pos = `${input.translate.x}px ${input.translate.y}px`;
26
+ if (input.pattern === "grid") {
27
+ return {
28
+ backgroundImage: "linear-gradient(to right, hsl(var(--border)) 1px, transparent 1px), linear-gradient(to bottom, hsl(var(--border)) 1px, transparent 1px)",
29
+ backgroundPosition: pos,
30
+ backgroundSize: `${size}px ${size}px`
31
+ };
32
+ }
33
+ return {
34
+ backgroundImage: "radial-gradient(circle, hsl(var(--border)) 1px, transparent 1px)",
35
+ backgroundPosition: pos,
36
+ backgroundSize: `${size}px ${size}px`
37
+ };
38
+ };
39
+ const InfinitePlane = forwardRef(
40
+ (props, ref) => {
41
+ const {
42
+ children,
43
+ className,
44
+ labels,
45
+ pattern = "dot",
46
+ spacing = 32,
47
+ translate = { x: 0, y: 0 },
48
+ zoom = 1,
49
+ ...rest
50
+ } = props;
51
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
52
+ const background = buildBackground({ pattern, spacing, translate, zoom });
53
+ return /* @__PURE__ */ jsx(
54
+ "div",
55
+ {
56
+ "aria-label": resolvedLabels.region,
57
+ className: cn(
58
+ "relative h-full w-full overflow-hidden bg-background",
59
+ className
60
+ ),
61
+ "data-infinite-plane": true,
62
+ "data-infinite-plane-pattern": pattern,
63
+ ref,
64
+ role: "region",
65
+ style: background,
66
+ ...rest,
67
+ children
68
+ }
69
+ );
70
+ }
71
+ );
72
+ InfinitePlane.displayName = "InfinitePlane";
73
+ export {
74
+ InfinitePlane
75
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ PresenceStack
3
+ } from "./presence-stack";
4
+ export {
5
+ PresenceStack
6
+ };
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const STATUS_DOT = {
8
+ active: "bg-emerald-500",
9
+ away: "bg-amber-500",
10
+ idle: "bg-muted-foreground",
11
+ offline: "bg-muted-foreground/40"
12
+ };
13
+ const DEFAULT_LABELS = {
14
+ overflowSuffix: "more",
15
+ region: "Live presence"
16
+ };
17
+ const Avatar = (props) => {
18
+ const { user } = props;
19
+ const status = user.status ?? "active";
20
+ return /* @__PURE__ */ jsxs(
21
+ "span",
22
+ {
23
+ className: "relative -ml-2 inline-flex h-7 w-7 items-center justify-center rounded-full border-2 border-background text-[11px] font-semibold text-white shadow-sm first:ml-0",
24
+ "data-presence-stack-status": status,
25
+ "data-presence-stack-user": user.id,
26
+ style: { backgroundColor: user.color ?? "var(--foreground)" },
27
+ title: user.name,
28
+ children: [
29
+ user.initial,
30
+ /* @__PURE__ */ jsx(
31
+ "span",
32
+ {
33
+ "aria-hidden": "true",
34
+ className: cn(
35
+ "absolute -bottom-0.5 -right-0.5 h-2 w-2 rounded-full border border-background",
36
+ STATUS_DOT[status]
37
+ ),
38
+ "data-presence-stack-dot": true
39
+ }
40
+ )
41
+ ]
42
+ }
43
+ );
44
+ };
45
+ const PresenceStack = forwardRef(
46
+ (props, ref) => {
47
+ const {
48
+ className,
49
+ labels,
50
+ max = 5,
51
+ onOverflowActivate,
52
+ users,
53
+ ...rest
54
+ } = props;
55
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
56
+ const visible = max >= users.length ? users : users.slice(0, max);
57
+ const hidden = users.length - visible.length;
58
+ const handleOverflow = () => {
59
+ onOverflowActivate?.();
60
+ };
61
+ return /* @__PURE__ */ jsxs(
62
+ "div",
63
+ {
64
+ "aria-label": resolvedLabels.region,
65
+ className: cn("inline-flex items-center pl-2", className),
66
+ "data-presence-stack": true,
67
+ ref,
68
+ role: "group",
69
+ ...rest,
70
+ children: [
71
+ visible.map((user) => /* @__PURE__ */ jsx(Avatar, { user }, user.id)),
72
+ hidden > 0 ? renderOverflow({
73
+ count: hidden,
74
+ handleClick: handleOverflow,
75
+ handlerProvided: Boolean(onOverflowActivate),
76
+ labels: resolvedLabels
77
+ }) : null
78
+ ]
79
+ }
80
+ );
81
+ }
82
+ );
83
+ PresenceStack.displayName = "PresenceStack";
84
+ const renderOverflow = (input) => {
85
+ const text = `+${input.count}`;
86
+ const aria = `${input.count} ${input.labels.overflowSuffix}`;
87
+ const className = "relative -ml-2 inline-flex h-7 min-w-7 items-center justify-center rounded-full border-2 border-background bg-muted px-1.5 text-[10px] font-semibold text-muted-foreground shadow-sm";
88
+ if (input.handlerProvided) {
89
+ return /* @__PURE__ */ jsx(
90
+ "button",
91
+ {
92
+ "aria-label": aria,
93
+ className: cn(
94
+ className,
95
+ "transition-colors hover:bg-muted/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
96
+ ),
97
+ "data-presence-stack-overflow": true,
98
+ onClick: input.handleClick,
99
+ type: "button",
100
+ children: text
101
+ }
102
+ );
103
+ }
104
+ return /* @__PURE__ */ jsx("span", { "aria-label": aria, className, "data-presence-stack-overflow": true, children: text });
105
+ };
106
+ export {
107
+ PresenceStack
108
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ SelectionPresence
3
+ } from "./selection-presence";
4
+ export {
5
+ SelectionPresence
6
+ };