@nikala-ui/core 0.10.1 → 0.11.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 (55) hide show
  1. package/package.json +1 -1
  2. package/registry/bubble.json +18 -0
  3. package/registry/button-group.json +20 -0
  4. package/registry/create-chat-scroll.json +13 -0
  5. package/registry/create-drop-zone.json +13 -0
  6. package/registry/create-pagination.json +13 -0
  7. package/registry/dropzone.json +21 -0
  8. package/registry/footer.json +18 -0
  9. package/registry/forgot-password-01.json +28 -0
  10. package/registry/hero-01.json +22 -0
  11. package/registry/index.json +331 -0
  12. package/registry/login-01.json +28 -0
  13. package/registry/marker.json +17 -0
  14. package/registry/marquee.json +17 -0
  15. package/registry/message.json +17 -0
  16. package/registry/navbar.json +19 -0
  17. package/registry/navigation-menu.json +22 -0
  18. package/registry/otp-verification-01.json +26 -0
  19. package/registry/pagination.json +22 -0
  20. package/registry/rating.json +19 -0
  21. package/registry/register-01.json +31 -0
  22. package/registry/review-card.json +23 -0
  23. package/registry/scroll-area.json +1 -1
  24. package/registry/sidebar.json +24 -0
  25. package/registry/spinner.json +1 -1
  26. package/registry/stat.json +19 -0
  27. package/registry/table.json +17 -0
  28. package/registry/timeline.json +18 -0
  29. package/registry/toggle-group.json +21 -0
  30. package/src/registry/blocks/forgot-password-01.tsx +141 -0
  31. package/src/registry/blocks/hero-01.tsx +28 -0
  32. package/src/registry/blocks/login-01.tsx +231 -0
  33. package/src/registry/blocks/otp-verification-01.tsx +170 -0
  34. package/src/registry/blocks/register-01.tsx +318 -0
  35. package/src/registry/components/ui/bubble.tsx +142 -0
  36. package/src/registry/components/ui/button-group.tsx +42 -0
  37. package/src/registry/components/ui/dropzone.tsx +189 -0
  38. package/src/registry/components/ui/footer.tsx +247 -0
  39. package/src/registry/components/ui/marker.tsx +102 -0
  40. package/src/registry/components/ui/marquee.tsx +118 -0
  41. package/src/registry/components/ui/message.tsx +168 -0
  42. package/src/registry/components/ui/navbar.tsx +368 -0
  43. package/src/registry/components/ui/navigation-menu.tsx +358 -0
  44. package/src/registry/components/ui/pagination.tsx +258 -0
  45. package/src/registry/components/ui/rating.tsx +185 -0
  46. package/src/registry/components/ui/review-card.tsx +195 -0
  47. package/src/registry/components/ui/scroll-area.tsx +2 -2
  48. package/src/registry/components/ui/sidebar.tsx +692 -0
  49. package/src/registry/components/ui/spinner.tsx +1 -1
  50. package/src/registry/components/ui/stat.tsx +245 -0
  51. package/src/registry/components/ui/table.tsx +160 -0
  52. package/src/registry/components/ui/timeline.tsx +350 -0
  53. package/src/registry/components/ui/toggle-group.tsx +203 -0
  54. package/src/registry/index.ts +3 -3
  55. package/src/registry/metadata.ts +141 -0
@@ -0,0 +1,247 @@
1
+ import { splitProps, type JSX, type ParentComponent } from "solid-js";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "@/lib/cn";
4
+
5
+ /* --- Root Footer --- */
6
+ export const footerVariants = cva("w-full transition-colors", {
7
+ variants: {
8
+ variant: {
9
+ default: "bg-background border-t border-border/60 text-foreground",
10
+ muted: "bg-muted/40 border-t border-border text-foreground",
11
+ bordered: "bg-background border-t-2 border-border text-foreground",
12
+ floating: "my-8 rounded-lg border border-border/80 bg-card text-card-foreground shadow-xs",
13
+ transparent: "bg-transparent border-transparent text-foreground",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ });
20
+
21
+ export interface FooterProps
22
+ extends JSX.HTMLAttributes<HTMLElement>,
23
+ VariantProps<typeof footerVariants> {
24
+ maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
25
+ }
26
+
27
+ export const Footer: ParentComponent<FooterProps> = (props) => {
28
+ const [local, rest] = splitProps(props, ["class", "variant", "maxWidth", "children"]);
29
+
30
+ const maxWidthClass = () => {
31
+ switch (local.maxWidth) {
32
+ case "sm":
33
+ return "max-w-screen-sm";
34
+ case "md":
35
+ return "max-w-screen-md";
36
+ case "lg":
37
+ return "max-w-screen-lg";
38
+ case "xl":
39
+ return "max-w-screen-xl";
40
+ case "full":
41
+ return "max-w-full";
42
+ case "2xl":
43
+ default:
44
+ return "max-w-screen-2xl";
45
+ }
46
+ };
47
+
48
+ return (
49
+ <footer
50
+ class={cn(
51
+ footerVariants({ variant: local.variant }),
52
+ local.variant === "floating" && cn("mx-auto", maxWidthClass()),
53
+ local.class
54
+ )}
55
+ {...rest}
56
+ >
57
+ <div class={cn("w-full", local.variant !== "floating" && cn("mx-auto", maxWidthClass()))}>
58
+ {local.children}
59
+ </div>
60
+ </footer>
61
+ );
62
+ };
63
+
64
+ /* --- Footer Container --- */
65
+ export interface FooterContainerProps extends JSX.HTMLAttributes<HTMLDivElement> {
66
+ class?: string;
67
+ }
68
+
69
+ export const FooterContainer: ParentComponent<FooterContainerProps> = (props) => {
70
+ const [local, rest] = splitProps(props, ["class", "children"]);
71
+
72
+ return (
73
+ <div
74
+ class={cn("px-4 sm:px-6 lg:px-8 py-10 sm:py-12 md:py-16 w-full", local.class)}
75
+ {...rest}
76
+ >
77
+ {local.children}
78
+ </div>
79
+ );
80
+ };
81
+
82
+ /* --- Footer Content (Columns Grid / Layout) --- */
83
+ export interface FooterContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
84
+ class?: string;
85
+ }
86
+
87
+ export const FooterContent: ParentComponent<FooterContentProps> = (props) => {
88
+ const [local, rest] = splitProps(props, ["class", "children"]);
89
+
90
+ return (
91
+ <div
92
+ class={cn(
93
+ "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8 lg:gap-12",
94
+ local.class
95
+ )}
96
+ {...rest}
97
+ >
98
+ {local.children}
99
+ </div>
100
+ );
101
+ };
102
+
103
+ /* --- Footer Column --- */
104
+ export interface FooterColumnProps extends JSX.HTMLAttributes<HTMLDivElement> {
105
+ class?: string;
106
+ }
107
+
108
+ export const FooterColumn: ParentComponent<FooterColumnProps> = (props) => {
109
+ const [local, rest] = splitProps(props, ["class", "children"]);
110
+
111
+ return (
112
+ <div class={cn("flex flex-col space-y-3", local.class)} {...rest}>
113
+ {local.children}
114
+ </div>
115
+ );
116
+ };
117
+
118
+ /* --- Footer Column Title --- */
119
+ export interface FooterColumnTitleProps extends JSX.HTMLAttributes<HTMLHeadingElement> {
120
+ class?: string;
121
+ }
122
+
123
+ export const FooterColumnTitle: ParentComponent<FooterColumnTitleProps> = (props) => {
124
+ const [local, rest] = splitProps(props, ["class", "children"]);
125
+
126
+ return (
127
+ <h4
128
+ class={cn(
129
+ "text-xs font-semibold uppercase tracking-wider text-foreground",
130
+ local.class
131
+ )}
132
+ {...rest}
133
+ >
134
+ {local.children}
135
+ </h4>
136
+ );
137
+ };
138
+
139
+ /* --- Footer Column List --- */
140
+ export interface FooterColumnListProps extends JSX.HTMLAttributes<HTMLUListElement> {
141
+ class?: string;
142
+ }
143
+
144
+ export const FooterColumnList: ParentComponent<FooterColumnListProps> = (props) => {
145
+ const [local, rest] = splitProps(props, ["class", "children"]);
146
+
147
+ return (
148
+ <ul class={cn("space-y-2 list-none p-0 m-0", local.class)} {...rest}>
149
+ {local.children}
150
+ </ul>
151
+ );
152
+ };
153
+
154
+ /* --- Footer Link --- */
155
+ export interface FooterLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {
156
+ class?: string;
157
+ }
158
+
159
+ export const FooterLink: ParentComponent<FooterLinkProps> = (props) => {
160
+ const [local, rest] = splitProps(props, ["class", "children"]);
161
+
162
+ return (
163
+ <li>
164
+ <a
165
+ class={cn(
166
+ "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
167
+ local.class
168
+ )}
169
+ {...rest}
170
+ >
171
+ {local.children}
172
+ </a>
173
+ </li>
174
+ );
175
+ };
176
+
177
+ /* --- Footer Brand / Info Section --- */
178
+ export interface FooterBrandProps extends JSX.HTMLAttributes<HTMLDivElement> {
179
+ class?: string;
180
+ }
181
+
182
+ export const FooterBrand: ParentComponent<FooterBrandProps> = (props) => {
183
+ const [local, rest] = splitProps(props, ["class", "children"]);
184
+
185
+ return (
186
+ <div
187
+ class={cn(
188
+ "col-span-2 sm:col-span-2 md:col-span-4 lg:col-span-2 flex flex-col space-y-3 mb-4 lg:mb-0",
189
+ local.class
190
+ )}
191
+ {...rest}
192
+ >
193
+ {local.children}
194
+ </div>
195
+ );
196
+ };
197
+
198
+ /* --- Footer Bottom (Copyright and Secondary Links Bar) --- */
199
+ export interface FooterBottomProps extends JSX.HTMLAttributes<HTMLDivElement> {
200
+ class?: string;
201
+ }
202
+
203
+ export const FooterBottom: ParentComponent<FooterBottomProps> = (props) => {
204
+ const [local, rest] = splitProps(props, ["class", "children"]);
205
+
206
+ return (
207
+ <div
208
+ class={cn(
209
+ "mt-12 sm:mt-16 pt-8 border-t border-border/50 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-muted-foreground",
210
+ local.class
211
+ )}
212
+ {...rest}
213
+ >
214
+ {local.children}
215
+ </div>
216
+ );
217
+ };
218
+
219
+ /* --- Footer Copyright Text --- */
220
+ export interface FooterCopyrightProps extends JSX.HTMLAttributes<HTMLParagraphElement> {
221
+ class?: string;
222
+ }
223
+
224
+ export const FooterCopyright: ParentComponent<FooterCopyrightProps> = (props) => {
225
+ const [local, rest] = splitProps(props, ["class", "children"]);
226
+
227
+ return (
228
+ <p class={cn("text-xs text-muted-foreground leading-relaxed", local.class)} {...rest}>
229
+ {local.children}
230
+ </p>
231
+ );
232
+ };
233
+
234
+ /* --- Footer Social Links Container --- */
235
+ export interface FooterSocialsProps extends JSX.HTMLAttributes<HTMLDivElement> {
236
+ class?: string;
237
+ }
238
+
239
+ export const FooterSocials: ParentComponent<FooterSocialsProps> = (props) => {
240
+ const [local, rest] = splitProps(props, ["class", "children"]);
241
+
242
+ return (
243
+ <div class={cn("flex items-center gap-3 text-muted-foreground", local.class)} {...rest}>
244
+ {local.children}
245
+ </div>
246
+ );
247
+ };
@@ -0,0 +1,102 @@
1
+ import {
2
+ splitProps,
3
+ type Component,
4
+ type JSX,
5
+ type ParentComponent,
6
+ Show,
7
+ } from "solid-js";
8
+ import { cn } from "@/lib/cn";
9
+
10
+ /* --- 1. Marker Root --- */
11
+ export interface MarkerProps extends JSX.HTMLAttributes<HTMLDivElement> {
12
+ class?: string;
13
+ }
14
+
15
+ export const Marker: ParentComponent<MarkerProps> = (props) => {
16
+ const [local, rest] = splitProps(props, ["class", "children"]);
17
+
18
+ return (
19
+ <div
20
+ role="status"
21
+ class={cn("flex w-full items-center justify-center my-3 text-center select-none", local.class)}
22
+ {...rest}
23
+ >
24
+ {local.children}
25
+ </div>
26
+ );
27
+ };
28
+
29
+ /* --- 2. MarkerContent --- */
30
+ export interface MarkerContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
31
+ class?: string;
32
+ }
33
+
34
+ export const MarkerContent: ParentComponent<MarkerContentProps> = (props) => {
35
+ const [local, rest] = splitProps(props, ["class", "children"]);
36
+
37
+ return (
38
+ <div
39
+ class={cn(
40
+ "inline-flex items-center gap-1.5 rounded-lg border border-border/60 bg-muted/40 px-3 py-1 text-xs text-muted-foreground shadow-2xs backdrop-blur-xs",
41
+ local.class
42
+ )}
43
+ {...rest}
44
+ >
45
+ {local.children}
46
+ </div>
47
+ );
48
+ };
49
+
50
+ /* --- 3. MarkerDate (Date Divider) --- */
51
+ export interface MarkerDateProps extends JSX.HTMLAttributes<HTMLDivElement> {
52
+ date?: string;
53
+ class?: string;
54
+ }
55
+
56
+ export const MarkerDate: ParentComponent<MarkerDateProps> = (props) => {
57
+ const [local, rest] = splitProps(props, ["date", "class", "children"]);
58
+
59
+ return (
60
+ <div
61
+ class={cn("relative flex w-full items-center justify-center my-4", local.class)}
62
+ {...rest}
63
+ >
64
+ <div class="absolute inset-0 flex items-center">
65
+ <div class="w-full border-t border-border/50" />
66
+ </div>
67
+ <div class="relative flex items-center gap-1 bg-background px-3 py-0.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground rounded-md border border-border/40">
68
+ {local.children || local.date}
69
+ </div>
70
+ </div>
71
+ );
72
+ };
73
+
74
+ /* --- 4. MarkerTyping (Live Typing Indicator) --- */
75
+ export interface MarkerTypingProps extends JSX.HTMLAttributes<HTMLDivElement> {
76
+ name?: string;
77
+ class?: string;
78
+ }
79
+
80
+ export const MarkerTyping: Component<MarkerTypingProps> = (props) => {
81
+ const [local, rest] = splitProps(props, ["name", "class"]);
82
+
83
+ return (
84
+ <div
85
+ role="status"
86
+ aria-label={`${local.name || "Someone"} is typing`}
87
+ class={cn("flex w-full items-center gap-2 text-xs text-muted-foreground my-2", local.class)}
88
+ {...rest}
89
+ >
90
+ <Show when={local.name}>
91
+ <span class="font-medium text-foreground">{local.name}</span> is typing
92
+ </Show>
93
+
94
+ {/* 3 Animated Bouncing Dots */}
95
+ <span class="inline-flex items-center gap-1 px-2 py-1 rounded-md bg-muted/60 border border-border/40">
96
+ <span class="size-1.5 rounded-lg bg-foreground/60 animate-bounce [animation-delay:-0.3s]" />
97
+ <span class="size-1.5 rounded-lg bg-foreground/60 animate-bounce [animation-delay:-0.15s]" />
98
+ <span class="size-1.5 rounded-lg bg-foreground/60 animate-bounce" />
99
+ </span>
100
+ </div>
101
+ );
102
+ };
@@ -0,0 +1,118 @@
1
+ import {
2
+ splitProps,
3
+ createMemo,
4
+ Index,
5
+ type JSX,
6
+ type ParentComponent,
7
+ } from "solid-js";
8
+ import { cn } from "@/lib/cn";
9
+
10
+ export interface MarqueeProps extends JSX.HTMLAttributes<HTMLDivElement> {
11
+ class?: string;
12
+ reverse?: boolean;
13
+ pauseOnHover?: boolean;
14
+ vertical?: boolean;
15
+ repeat?: number;
16
+ duration?: number | string;
17
+ gap?: string;
18
+ fadeEdges?: boolean;
19
+ }
20
+
21
+ export const Marquee: ParentComponent<MarqueeProps> = (props) => {
22
+ const [local, rest] = splitProps(props, [
23
+ "class",
24
+ "reverse",
25
+ "pauseOnHover",
26
+ "vertical",
27
+ "repeat",
28
+ "duration",
29
+ "gap",
30
+ "fadeEdges",
31
+ "children",
32
+ ]);
33
+
34
+ const repeatCount = () => local.repeat ?? 4;
35
+ const durationValue = () =>
36
+ typeof local.duration === "number"
37
+ ? `${local.duration}s`
38
+ : local.duration || "40s";
39
+ const gapValue = () => local.gap || "1rem";
40
+
41
+ const repeatArray = createMemo(() =>
42
+ Array.from({ length: repeatCount() }, (_, i) => i)
43
+ );
44
+
45
+ return (
46
+ <>
47
+ <style>{`
48
+ @keyframes nikala-marquee {
49
+ from {
50
+ transform: translateX(0);
51
+ }
52
+ to {
53
+ transform: translateX(calc(-100% - var(--marquee-gap, 1rem)));
54
+ }
55
+ }
56
+ @keyframes nikala-marquee-vertical {
57
+ from {
58
+ transform: translateY(0);
59
+ }
60
+ to {
61
+ transform: translateY(calc(-100% - var(--marquee-gap, 1rem)));
62
+ }
63
+ }
64
+ .nikala-marquee-track {
65
+ animation: nikala-marquee var(--marquee-duration, 40s) linear infinite;
66
+ }
67
+ .nikala-marquee-track-vertical {
68
+ animation: nikala-marquee-vertical var(--marquee-duration, 40s) linear infinite;
69
+ }
70
+ [data-reverse="true"] .nikala-marquee-track,
71
+ [data-reverse="true"] .nikala-marquee-track-vertical {
72
+ animation-direction: reverse;
73
+ }
74
+ [data-pause="true"]:hover .nikala-marquee-track,
75
+ [data-pause="true"]:hover .nikala-marquee-track-vertical {
76
+ animation-play-state: paused;
77
+ }
78
+ `}</style>
79
+ <div
80
+ data-reverse={local.reverse ? "true" : "false"}
81
+ data-pause={local.pauseOnHover ? "true" : "false"}
82
+ style={{
83
+ "--marquee-duration": durationValue(),
84
+ "--marquee-gap": gapValue(),
85
+ }}
86
+ class={cn(
87
+ "group flex overflow-hidden p-2 select-none",
88
+ local.vertical ? "flex-col h-full" : "flex-row w-full",
89
+ local.fadeEdges &&
90
+ (local.vertical
91
+ ? "[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]"
92
+ : "[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]"),
93
+ local.class
94
+ )}
95
+ {...rest}
96
+ >
97
+ <Index each={repeatArray()}>
98
+ {() => (
99
+ <div
100
+ class={cn(
101
+ "flex shrink-0 justify-around items-center",
102
+ local.vertical
103
+ ? "flex-col min-h-full nikala-marquee-track-vertical"
104
+ : "flex-row min-w-full nikala-marquee-track"
105
+ )}
106
+ style={{
107
+ gap: gapValue(),
108
+ [local.vertical ? "margin-bottom" : "margin-right"]: gapValue(),
109
+ }}
110
+ >
111
+ {local.children}
112
+ </div>
113
+ )}
114
+ </Index>
115
+ </div>
116
+ </>
117
+ );
118
+ };
@@ -0,0 +1,168 @@
1
+ import {
2
+ createContext,
3
+ useContext,
4
+ splitProps,
5
+ type Component,
6
+ type JSX,
7
+ type ParentComponent,
8
+ type Accessor,
9
+ } from "solid-js";
10
+ import { cn } from "@/lib/cn";
11
+
12
+ /* --- 1. Message Context --- */
13
+ export type MessageAlignment = "start" | "end";
14
+
15
+ interface MessageContextValue {
16
+ align: Accessor<MessageAlignment>;
17
+ }
18
+
19
+ const MessageContext = createContext<MessageContextValue>();
20
+
21
+ export function useMessage() {
22
+ const context = useContext(MessageContext);
23
+ if (!context) {
24
+ throw new Error("useMessage must be used within a <Message />");
25
+ }
26
+ return context;
27
+ }
28
+
29
+ /* --- 2. Message Root --- */
30
+ export interface MessageProps extends JSX.HTMLAttributes<HTMLDivElement> {
31
+ align?: MessageAlignment;
32
+ class?: string;
33
+ }
34
+
35
+ export const Message: ParentComponent<MessageProps> = (props) => {
36
+ const [local, rest] = splitProps(props, ["align", "class", "children"]);
37
+ const align = () => local.align ?? "start";
38
+
39
+ const contextValue: MessageContextValue = {
40
+ align,
41
+ };
42
+
43
+ return (
44
+ <MessageContext.Provider value={contextValue}>
45
+ <div
46
+ data-align={align()}
47
+ class={cn(
48
+ "group/message flex w-full gap-3",
49
+ align() === "end" ? "flex-row-reverse items-start" : "flex-row items-start",
50
+ local.class
51
+ )}
52
+ {...rest}
53
+ >
54
+ {local.children}
55
+ </div>
56
+ </MessageContext.Provider>
57
+ );
58
+ };
59
+
60
+ /* --- 3. MessageAvatar --- */
61
+ export interface MessageAvatarProps extends JSX.HTMLAttributes<HTMLDivElement> {
62
+ class?: string;
63
+ }
64
+
65
+ export const MessageAvatar: ParentComponent<MessageAvatarProps> = (props) => {
66
+ const [local, rest] = splitProps(props, ["class", "children"]);
67
+
68
+ return (
69
+ <div
70
+ class={cn("shrink-0 select-none pt-0.5", local.class)}
71
+ {...rest}
72
+ >
73
+ {local.children}
74
+ </div>
75
+ );
76
+ };
77
+
78
+ /* --- 4. MessageHeader --- */
79
+ export interface MessageHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {
80
+ class?: string;
81
+ }
82
+
83
+ export const MessageHeader: ParentComponent<MessageHeaderProps> = (props) => {
84
+ const [local, rest] = splitProps(props, ["class", "children"]);
85
+ const { align } = useMessage();
86
+
87
+ return (
88
+ <div
89
+ class={cn(
90
+ "flex items-center gap-2 text-xs text-muted-foreground select-none mb-1",
91
+ align() === "end" ? "justify-end" : "justify-start",
92
+ local.class
93
+ )}
94
+ {...rest}
95
+ >
96
+ {local.children}
97
+ </div>
98
+ );
99
+ };
100
+
101
+ /* --- 5. MessageContent --- */
102
+ export interface MessageContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
103
+ class?: string;
104
+ }
105
+
106
+ export const MessageContent: ParentComponent<MessageContentProps> = (props) => {
107
+ const [local, rest] = splitProps(props, ["class", "children"]);
108
+ const { align } = useMessage();
109
+
110
+ return (
111
+ <div
112
+ class={cn(
113
+ "flex flex-col gap-1.5 max-w-[85%] sm:max-w-[75%]",
114
+ align() === "end" ? "items-end text-right" : "items-start text-left",
115
+ local.class
116
+ )}
117
+ {...rest}
118
+ >
119
+ {local.children}
120
+ </div>
121
+ );
122
+ };
123
+
124
+ /* --- 6. MessageFooter --- */
125
+ export interface MessageFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
126
+ class?: string;
127
+ }
128
+
129
+ export const MessageFooter: ParentComponent<MessageFooterProps> = (props) => {
130
+ const [local, rest] = splitProps(props, ["class", "children"]);
131
+ const { align } = useMessage();
132
+
133
+ return (
134
+ <div
135
+ class={cn(
136
+ "flex items-center gap-1.5 text-[11px] text-muted-foreground select-none mt-1",
137
+ align() === "end" ? "justify-end" : "justify-start",
138
+ local.class
139
+ )}
140
+ {...rest}
141
+ >
142
+ {local.children}
143
+ </div>
144
+ );
145
+ };
146
+
147
+ /* --- 7. MessageActions --- */
148
+ export interface MessageActionsProps extends JSX.HTMLAttributes<HTMLDivElement> {
149
+ class?: string;
150
+ }
151
+
152
+ export const MessageActions: ParentComponent<MessageActionsProps> = (props) => {
153
+ const [local, rest] = splitProps(props, ["class", "children"]);
154
+ const { align } = useMessage();
155
+
156
+ return (
157
+ <div
158
+ class={cn(
159
+ "opacity-0 group-hover/message:opacity-100 focus-within:opacity-100 transition-opacity flex items-center gap-1 p-0.5 rounded-md border border-border bg-background shadow-xs",
160
+ align() === "end" ? "flex-row-reverse" : "flex-row",
161
+ local.class
162
+ )}
163
+ {...rest}
164
+ >
165
+ {local.children}
166
+ </div>
167
+ );
168
+ };