@devalok/shilp-sutra 0.32.0 → 0.33.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_chunks/date-time-picker.js +851 -0
  3. package/dist/_chunks/mention-suggestion.js +362 -0
  4. package/dist/_chunks/tiptap.js +11595 -7731
  5. package/dist/_chunks/use-calendar.js +115 -956
  6. package/dist/_chunks/vendor-client.js +1055 -894
  7. package/dist/composed/date-picker/index.js +9 -8
  8. package/dist/composed/emoji-picker.d.ts +8 -2
  9. package/dist/composed/emoji-picker.d.ts.map +1 -1
  10. package/dist/composed/emoji-picker.js +65 -56
  11. package/dist/composed/extensions/emoji-data.d.ts +35 -0
  12. package/dist/composed/extensions/emoji-data.d.ts.map +1 -0
  13. package/dist/composed/extensions/emoji-node.d.ts +11 -0
  14. package/dist/composed/extensions/emoji-node.d.ts.map +1 -0
  15. package/dist/composed/extensions/emoji-suggestion.d.ts +4 -6
  16. package/dist/composed/extensions/emoji-suggestion.d.ts.map +1 -1
  17. package/dist/composed/extensions/slash-command.d.ts +35 -0
  18. package/dist/composed/extensions/slash-command.d.ts.map +1 -0
  19. package/dist/composed/index.d.ts +7 -1
  20. package/dist/composed/index.d.ts.map +1 -1
  21. package/dist/composed/index.js +79 -70
  22. package/dist/composed/rich-chat-input/attachment-strip.d.ts +15 -0
  23. package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -0
  24. package/dist/composed/rich-chat-input/audio-player.d.ts +11 -0
  25. package/dist/composed/rich-chat-input/audio-player.d.ts.map +1 -0
  26. package/dist/composed/rich-chat-input/audio-waveform.d.ts +24 -0
  27. package/dist/composed/rich-chat-input/audio-waveform.d.ts.map +1 -0
  28. package/dist/composed/rich-chat-input/chat-toolbar.d.ts +51 -0
  29. package/dist/composed/rich-chat-input/chat-toolbar.d.ts.map +1 -0
  30. package/dist/composed/rich-chat-input/recording-overlay.d.ts +15 -0
  31. package/dist/composed/rich-chat-input/recording-overlay.d.ts.map +1 -0
  32. package/dist/composed/rich-chat-input/reply-banner.d.ts +8 -0
  33. package/dist/composed/rich-chat-input/reply-banner.d.ts.map +1 -0
  34. package/dist/composed/rich-chat-input/schedule-send.d.ts +22 -0
  35. package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -0
  36. package/dist/composed/rich-chat-input/use-voice-recorder.d.ts +18 -0
  37. package/dist/composed/rich-chat-input/use-voice-recorder.d.ts.map +1 -0
  38. package/dist/composed/rich-chat-input.d.ts +91 -0
  39. package/dist/composed/rich-chat-input.d.ts.map +1 -0
  40. package/dist/composed/rich-chat-input.js +1861 -0
  41. package/dist/composed/rich-text-editor.d.ts +3 -0
  42. package/dist/composed/rich-text-editor.d.ts.map +1 -1
  43. package/dist/composed/rich-text-editor.js +278 -448
  44. package/dist/ui/button-group.d.ts +27 -37
  45. package/dist/ui/button-group.d.ts.map +1 -1
  46. package/dist/ui/button-group.js +95 -29
  47. package/dist/ui/button.d.ts.map +1 -1
  48. package/dist/ui/button.js +110 -106
  49. package/dist/ui/index.d.ts +1 -0
  50. package/dist/ui/index.d.ts.map +1 -1
  51. package/dist/ui/index.js +335 -333
  52. package/dist/ui/split-button.d.ts +45 -0
  53. package/dist/ui/split-button.d.ts.map +1 -0
  54. package/dist/ui/split-button.js +282 -0
  55. package/docs/components/composed/emoji-picker.md +9 -0
  56. package/docs/components/composed/rich-chat-input.md +88 -0
  57. package/docs/components/composed/rich-text-editor.md +5 -0
  58. package/docs/components/ui/button-group.md +9 -0
  59. package/docs/components/ui/button.md +4 -0
  60. package/docs/components/ui/split-button.md +41 -0
  61. package/llms-full.txt +157 -1
  62. package/llms.txt +22 -2
  63. package/package.json +30 -26
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { Placement } from '@floating-ui/dom';
3
+ import { VariantProps } from 'class-variance-authority';
4
+ import { buttonVariants } from './button';
5
+ import * as React from 'react';
6
+ type SplitButtonVariant = 'solid' | 'soft' | 'outline';
7
+ type SplitButtonColor = NonNullable<VariantProps<typeof buttonVariants>['color']>;
8
+ type SplitButtonSize = 'xs' | 'sm' | 'md' | 'icon-xs' | 'icon-sm' | 'icon-md';
9
+ export interface SplitButtonProps {
10
+ /** Primary action content (left side). */
11
+ children: React.ReactNode;
12
+ /** Primary click handler (left side). */
13
+ onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
14
+ /** Content rendered inside the floating dropdown panel. */
15
+ dropdownContent?: React.ReactNode;
16
+ /** Controlled open state. */
17
+ open?: boolean;
18
+ /** Called when open state changes. Uncontrolled if omitted. */
19
+ onOpenChange?: (open: boolean) => void;
20
+ /** @default 'solid' */
21
+ variant?: SplitButtonVariant;
22
+ /** @default 'accent' */
23
+ color?: SplitButtonColor;
24
+ /** @default 'md' */
25
+ size?: SplitButtonSize;
26
+ /** Disable both halves. */
27
+ disabled?: boolean;
28
+ /** aria-label for the primary button. */
29
+ 'aria-label'?: string;
30
+ /** aria-label for the dropdown trigger. @default 'More options' */
31
+ dropdownLabel?: string;
32
+ /** Custom icon for the dropdown trigger. Defaults to chevron-down. */
33
+ dropdownIcon?: React.ReactNode;
34
+ /** Which side the dropdown trigger sits on. @default 'right' */
35
+ triggerSide?: 'left' | 'right';
36
+ /** Preferred placement for the dropdown panel. @default 'top-end' */
37
+ placement?: Placement;
38
+ /** Width of the trigger (chevron) half. @default 'auto' */
39
+ triggerWidth?: number | string;
40
+ className?: string;
41
+ }
42
+ declare const SplitButton: React.ForwardRefExoticComponent<SplitButtonProps & React.RefAttributes<HTMLDivElement>>;
43
+ export { SplitButton };
44
+ export type { SplitButtonVariant, SplitButtonColor, SplitButtonSize };
45
+ //# sourceMappingURL=split-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-button.d.ts","sourceRoot":"","sources":["../../src/ui/split-button.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEnG,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAKzC,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;AACtD,KAAK,gBAAgB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;AACjF,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,yCAAyC;IACzC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACzD,2DAA2D;IAC3D,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACjC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,+DAA+D;IAC/D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,uBAAuB;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B,wBAAwB;IACxB,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACxB,oBAAoB;IACpB,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC9B,qEAAqE;IACrE,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA2FD,QAAA,MAAM,WAAW,yFAuNhB,CAAA;AAID,OAAO,EAAE,WAAW,EAAE,CAAA;AACtB,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAA"}
@@ -0,0 +1,282 @@
1
+ "use client";
2
+ import { jsxs as m, Fragment as C, jsx as s } from "react/jsx-runtime";
3
+ import * as n from "react";
4
+ import { d as G, q as J, i as Q, e as T, g as V } from "../_chunks/vendor-client.js";
5
+ import { cn as o } from "./lib/utils.js";
6
+ import { A as X, m as Y } from "../_chunks/framer.js";
7
+ const A = {
8
+ solid: {
9
+ accent: "bg-accent-11/20",
10
+ error: "bg-error-11/20",
11
+ success: "bg-success-11/20",
12
+ warning: "bg-warning-11/20",
13
+ neutral: "bg-neutral-8/30"
14
+ },
15
+ soft: {
16
+ accent: "bg-accent-6",
17
+ error: "bg-error-6",
18
+ success: "bg-success-6",
19
+ warning: "bg-warning-6",
20
+ neutral: "bg-surface-border"
21
+ },
22
+ outline: {
23
+ accent: "bg-accent-7",
24
+ error: "bg-error-7",
25
+ success: "bg-success-7",
26
+ warning: "bg-warning-7",
27
+ neutral: "bg-surface-border-strong"
28
+ }
29
+ }, N = {
30
+ xs: "h-ds-xs-plus",
31
+ sm: "h-ds-sm",
32
+ md: "h-ds-md",
33
+ "icon-xs": "h-ds-xs-plus",
34
+ "icon-sm": "h-ds-sm",
35
+ "icon-md": "h-ds-md"
36
+ }, Z = {
37
+ xs: "text-ds-sm",
38
+ sm: "text-ds-sm",
39
+ md: "text-ds-md",
40
+ "icon-xs": "text-ds-sm",
41
+ "icon-sm": "text-ds-sm",
42
+ "icon-md": "text-ds-md"
43
+ }, _ = {
44
+ xs: "px-ds-03 gap-1",
45
+ sm: "px-ds-04 gap-1.5",
46
+ md: "px-ds-05 gap-2",
47
+ "icon-xs": "px-ds-02",
48
+ "icon-sm": "px-ds-03",
49
+ "icon-md": "px-ds-04"
50
+ }, I = {
51
+ xs: "px-ds-02",
52
+ sm: "px-ds-02",
53
+ md: "px-ds-03",
54
+ "icon-xs": "px-ds-01",
55
+ "icon-sm": "px-ds-02",
56
+ "icon-md": "px-ds-02"
57
+ }, W = {
58
+ xs: "rounded-ds-md",
59
+ sm: "rounded-ds-md",
60
+ md: "rounded-ds-lg",
61
+ "icon-xs": "rounded-ds-sm",
62
+ "icon-sm": "rounded-ds-md",
63
+ "icon-md": "rounded-ds-md"
64
+ };
65
+ function z(c, i) {
66
+ const u = {
67
+ solid: {
68
+ accent: "bg-accent-9 text-accent-fg hover:bg-accent-10 active:bg-accent-11",
69
+ error: "bg-error-9 text-error-fg hover:bg-error-10 active:bg-error-11",
70
+ success: "bg-success-9 text-success-fg hover:bg-success-10 active:bg-success-11",
71
+ warning: "bg-warning-9 text-warning-fg hover:bg-warning-10 active:bg-warning-11",
72
+ neutral: "bg-neutral-5 text-surface-fg hover:bg-neutral-7 active:bg-neutral-8"
73
+ },
74
+ soft: {
75
+ accent: "bg-accent-3 text-accent-11 hover:bg-accent-4 active:bg-accent-5",
76
+ error: "bg-error-3 text-error-11 hover:bg-error-4 active:bg-error-5",
77
+ success: "bg-success-3 text-success-11 hover:bg-success-4 active:bg-success-5",
78
+ warning: "bg-warning-3 text-warning-11 hover:bg-warning-4 active:bg-warning-5",
79
+ neutral: "bg-surface-raised-hover text-surface-fg-muted hover:bg-surface-raised-active active:bg-neutral-5"
80
+ },
81
+ outline: {
82
+ accent: "bg-transparent text-accent-11 hover:bg-accent-3 active:bg-accent-4",
83
+ error: "bg-transparent text-error-11 hover:bg-error-3 active:bg-error-4",
84
+ success: "bg-transparent text-success-11 hover:bg-success-3 active:bg-success-4",
85
+ warning: "bg-transparent text-warning-11 hover:bg-warning-3 active:bg-warning-4",
86
+ neutral: "bg-transparent text-surface-fg hover:bg-surface-raised-hover active:bg-surface-raised-active"
87
+ }
88
+ };
89
+ return u[c][i] ?? u[c].accent;
90
+ }
91
+ function ee(c) {
92
+ const i = {
93
+ accent: "border-accent-7",
94
+ error: "border-error-7",
95
+ success: "border-success-7",
96
+ warning: "border-warning-7",
97
+ neutral: "border-surface-border-strong"
98
+ };
99
+ return i[c] ?? i.accent;
100
+ }
101
+ function M({ className: c }) {
102
+ return /* @__PURE__ */ s("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", className: c, "aria-hidden": "true", children: /* @__PURE__ */ s("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
103
+ }
104
+ const se = n.forwardRef(
105
+ function({
106
+ children: i,
107
+ onClick: u,
108
+ dropdownContent: E,
109
+ open: j,
110
+ onOpenChange: p,
111
+ variant: l = "solid",
112
+ color: v = "accent",
113
+ size: r = "md",
114
+ disabled: x = !1,
115
+ "aria-label": L,
116
+ dropdownLabel: R = "More options",
117
+ dropdownIcon: B,
118
+ triggerSide: O = "right",
119
+ placement: S = "top-end",
120
+ triggerWidth: g,
121
+ className: q
122
+ }, D) {
123
+ const [F, H] = n.useState(!1), h = j !== void 0, e = h ? j : F, b = n.useRef(null), f = n.useRef(null), w = n.useId(), d = n.useCallback(
124
+ (t) => {
125
+ p && p(t), h || H(t);
126
+ },
127
+ [p, h]
128
+ );
129
+ n.useEffect(() => {
130
+ if (!e || !b.current || !f.current) return;
131
+ const t = b.current, a = f.current;
132
+ return G(t, a, () => {
133
+ J(t, a, {
134
+ placement: S,
135
+ middleware: [
136
+ Q(8),
137
+ T({ fallbackPlacements: ["bottom-end", "top-start", "bottom-start"] }),
138
+ V({ padding: 8 })
139
+ ]
140
+ }).then(({ x: K, y: U }) => {
141
+ Object.assign(a.style, { left: `${K}px`, top: `${U}px` });
142
+ });
143
+ });
144
+ }, [e, S]), n.useEffect(() => {
145
+ if (!e) return;
146
+ const t = (a) => {
147
+ const k = a.target;
148
+ b.current?.contains(k) || f.current?.contains(k) || d(!1);
149
+ };
150
+ return document.addEventListener("mousedown", t), () => document.removeEventListener("mousedown", t);
151
+ }, [e, d]), n.useEffect(() => {
152
+ if (!e) return;
153
+ const t = (a) => {
154
+ a.key === "Escape" && d(!1);
155
+ };
156
+ return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
157
+ }, [e, d]);
158
+ const y = z(l, v), $ = A[l][v] ?? A[l].accent, P = g != null ? { width: typeof g == "number" ? `${g}px` : g } : void 0;
159
+ return /* @__PURE__ */ m(C, { children: [
160
+ /* @__PURE__ */ s(
161
+ "div",
162
+ {
163
+ ref: D,
164
+ role: "group",
165
+ "aria-label": L ?? void 0,
166
+ className: o("relative inline-flex", q),
167
+ children: /* @__PURE__ */ m(
168
+ "div",
169
+ {
170
+ ref: b,
171
+ className: o(
172
+ "inline-flex items-stretch overflow-hidden",
173
+ W[r],
174
+ l === "solid" && "shadow-raised",
175
+ l === "outline" && `border ${ee(v)}`
176
+ ),
177
+ children: [
178
+ O === "left" && /* @__PURE__ */ m(C, { children: [
179
+ /* @__PURE__ */ s(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ onClick: () => d(!e),
184
+ disabled: x,
185
+ "aria-label": R,
186
+ "aria-haspopup": "menu",
187
+ "aria-expanded": e,
188
+ "aria-controls": e ? w : void 0,
189
+ className: o(
190
+ "inline-flex items-center justify-center select-none",
191
+ "transition-colors duration-fast-01 ease-productive-standard",
192
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-inset",
193
+ "disabled:pointer-events-none disabled:opacity-action-disabled",
194
+ y,
195
+ N[r],
196
+ I[r]
197
+ ),
198
+ style: P,
199
+ children: B ?? /* @__PURE__ */ s(M, {})
200
+ }
201
+ ),
202
+ /* @__PURE__ */ s("div", { className: o("w-px self-stretch", $) })
203
+ ] }),
204
+ /* @__PURE__ */ s(
205
+ "button",
206
+ {
207
+ type: "button",
208
+ onClick: u,
209
+ disabled: x,
210
+ "aria-label": L,
211
+ className: o(
212
+ "inline-flex items-center justify-center font-semibold select-none whitespace-nowrap",
213
+ "transition-colors duration-fast-01 ease-productive-standard",
214
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-inset",
215
+ "disabled:pointer-events-none disabled:opacity-action-disabled",
216
+ y,
217
+ N[r],
218
+ Z[r],
219
+ _[r]
220
+ ),
221
+ children: i
222
+ }
223
+ ),
224
+ O === "right" && /* @__PURE__ */ m(C, { children: [
225
+ /* @__PURE__ */ s("div", { className: o("w-px self-stretch", $) }),
226
+ /* @__PURE__ */ s(
227
+ "button",
228
+ {
229
+ type: "button",
230
+ onClick: () => d(!e),
231
+ disabled: x,
232
+ "aria-label": R,
233
+ "aria-haspopup": "menu",
234
+ "aria-expanded": e,
235
+ "aria-controls": e ? w : void 0,
236
+ className: o(
237
+ "inline-flex items-center justify-center select-none",
238
+ "transition-colors duration-fast-01 ease-productive-standard",
239
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-inset",
240
+ "disabled:pointer-events-none disabled:opacity-action-disabled",
241
+ y,
242
+ N[r],
243
+ I[r]
244
+ ),
245
+ style: P,
246
+ children: B ?? /* @__PURE__ */ s(M, {})
247
+ }
248
+ )
249
+ ] })
250
+ ]
251
+ }
252
+ )
253
+ }
254
+ ),
255
+ /* @__PURE__ */ s(X, { children: e && E && /* @__PURE__ */ s(
256
+ "div",
257
+ {
258
+ ref: f,
259
+ id: w,
260
+ role: "menu",
261
+ className: "fixed z-popover",
262
+ style: { top: 0, left: 0 },
263
+ children: /* @__PURE__ */ s(
264
+ Y.div,
265
+ {
266
+ initial: { opacity: 0, scale: 0.95 },
267
+ animate: { opacity: 1, scale: 1 },
268
+ exit: { opacity: 0, scale: 0.95 },
269
+ transition: { duration: 0.15 },
270
+ className: "rounded-ds-lg border border-surface-border-strong bg-surface-overlay shadow-floating",
271
+ children: E
272
+ }
273
+ )
274
+ }
275
+ ) })
276
+ ] });
277
+ }
278
+ );
279
+ se.displayName = "SplitButton";
280
+ export {
281
+ se as SplitButton
282
+ };
@@ -41,3 +41,12 @@ EmojiPicker, EmojiPickerPopover
41
41
  - Wraps `@emoji-mart/react` which is lazy-loaded — shows a Skeleton placeholder while loading
42
42
  - `theme="auto"` reads the `.dark` class on `<html>` to pick light/dark
43
43
  - EmojiPickerPopover auto-closes after selection
44
+
45
+ ## Changes
46
+
47
+ ### v0.33.0
48
+ - **Added** `set` prop on EmojiPicker and EmojiPickerPopover — `EmojiSet` type: 'native' | 'apple' | 'google' | 'twitter' | 'facebook'
49
+ - **Added** `EmojiNode` TipTap extension — inline atom node with spritesheet rendering for consistent emoji art styles
50
+ - **Added** `createEmojiSuggestion(set?)` factory — replaces `EmojiSuggestion` named export
51
+ - **Added** `EmojiSet` type exported from barrel
52
+ - **Breaking** `EmojiSuggestion` named export removed — use `createEmojiSuggestion()` factory
@@ -0,0 +1,88 @@
1
+ # RichChatInput
2
+
3
+ - Import: @devalok/shilp-sutra/composed/rich-chat-input
4
+ - Server-safe: No
5
+ - Category: composed
6
+
7
+ Compact rich text chat input for unified human+AI workspaces. Built on TipTap.
8
+
9
+ ## Props
10
+
11
+ ### RichChatInputProps
12
+ onSubmit: (html: string, plainText: string) => void (REQUIRED)
13
+ placeholder: string (default: "Type a message...")
14
+ disabled: boolean (default: false)
15
+ variant: 'compact' | 'expanded' | 'minimal' (default: 'compact')
16
+ maxRows: number
17
+ enterBehavior: 'send' | 'newline' (default: 'send')
18
+ maxLength: number — enables character counter
19
+ mentions: MentionItem[] — static list for @mention autocomplete
20
+ onMentionSearch: (query: string) => Promise<MentionItem[]> — async search
21
+ onMentionSelect: (item: MentionItem) => void
22
+ onFileUpload: (file: File) => Promise<{ url: string; name: string; size: number }>
23
+ onImageUpload: (file: File) => Promise<string>
24
+ slashCommands: SlashCommandGroup[] — enables / command palette
25
+ onTyping: (isTyping: boolean) => void — typing indicator callback
26
+ onEmpty: (isEmpty: boolean) => void
27
+ isStreaming: boolean (default: false) — shows stop button instead of send
28
+ onCancel: () => void — called when stop button is clicked
29
+ leadingSlot: ReactNode — rendered above the editor
30
+ trailingSlot: ReactNode — rendered below the toolbar
31
+ disclaimer: string — small text below the input
32
+ toolbar: boolean | ChatToolbarItem[] (default: true)
33
+
34
+ ChatToolbarItem: 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'code' | 'bulletList' | 'orderedList' | 'mention' | 'emoji' | 'attach' | 'slash'
35
+
36
+ MentionItem: { id: string; label: string; avatar?: string }
37
+
38
+ SlashCommand: { id: string; label: string; description?: string; icon?: ComponentType; action: (editor: Editor) => void }
39
+
40
+ SlashCommandGroup: { label: string; commands: SlashCommand[] }
41
+
42
+ ## Variants
43
+ - `compact` (default) — 2-3 lines, inline toolbar
44
+ - `expanded` — 5+ lines, always-visible toolbar, suited for AI prompts
45
+ - `minimal` — single line, toolbar appears on focus
46
+
47
+ ## Example
48
+ ```jsx
49
+ <RichChatInput
50
+ onSubmit={(html, text) => sendMessage(html)}
51
+ mentions={teamMembers}
52
+ onFileUpload={uploadFile}
53
+ slashCommands={[{ label: 'Actions', commands: [...] }]}
54
+ />
55
+
56
+ <RichChatInput
57
+ variant="expanded"
58
+ onSubmit={handleSubmit}
59
+ maxLength={2000}
60
+ disclaimer="AI-generated content may be inaccurate."
61
+ />
62
+
63
+ <RichChatInput
64
+ variant="minimal"
65
+ enterBehavior="newline"
66
+ onSubmit={handleReply}
67
+ />
68
+ ```
69
+
70
+ ## Gotchas
71
+ - Tiptap is bundled — no need to install `@tiptap/*` packages separately
72
+ - Enter sends by default; use `enterBehavior="newline"` for long-form composition (Cmd/Ctrl+Enter always sends)
73
+ - `maxLength` enables both a character limit and the visual counter in the toolbar
74
+ - File/image upload buttons only appear when the corresponding handler is provided
75
+ - Slash command button only appears when `slashCommands` is provided
76
+ - Mention button only appears when `mentions` or `onMentionSearch` is provided
77
+
78
+ ## Changes
79
+ ### v0.33.0
80
+ - **Added** Custom EmojiNode with spritesheet rendering — `emojiSet` prop
81
+ - **Added** `onSchedule` prop — schedule send with smart presets + DateTimePicker, SplitButton UX
82
+ - **Added** `actionButton` prop — composable left-side button (replaces default attach, or pass false to hide)
83
+ - **Added** Animated mic ↔ send button transitions (AnimatePresence)
84
+ - **Changed** Voice recording buttons from outline to soft variant
85
+ - **Fixed** ToolbarButton width for custom-width buttons (e.g. "Refine" with text label)
86
+
87
+ ### v0.32.0
88
+ - Initial release
@@ -55,6 +55,11 @@ MentionItem: { id: string; label: string; avatar?: string }
55
55
  - Features: bold, italic, underline, strikethrough, highlight, headings, blockquote, lists, task lists, code, links, images, file attachments, mentions, emoji, text alignment, horizontal rule
56
56
 
57
57
  ## Changes
58
+ ### v0.33.0
59
+ - **Added** `emojiSet` prop — `EmojiSet` type for consistent emoji art style rendering
60
+ - **Changed** TipTap v2 → v3 upgrade (useEditorState, immediatelyRender: false for SSR, ListKit)
61
+ - **Added** EmojiNode + createEmojiSuggestion(set) registered internally
62
+
58
63
  ### v0.30.0
59
64
  - **Added** `toolbar` prop — whitelist of `ToolbarItem` names to control which toolbar buttons appear. Dividers render only between groups that have visible items. `ToolbarItem` type exported from barrel.
60
65
 
@@ -25,6 +25,15 @@
25
25
  - Children can override variant/size individually
26
26
 
27
27
  ## Changes
28
+ ### v0.33.0
29
+ - **Rebuilt** with compound component pattern — Button reads position from ButtonGroup context and applies border-radius inline
30
+ - **Added** `disabled` prop (propagates to all children via context)
31
+ - **Added** `attached` prop (default true; false = spaced with gap)
32
+ - **Added** `fullWidth` prop (stretch to fill container)
33
+ - **Added** Tonal divider elements between buttons for solid/soft/ghost variants
34
+ - **Added** Focus z-index isolation (focused button rises above siblings)
35
+ - **Fixed** Border-radius not working with custom `rounded-ds-*` design tokens
36
+
28
37
  ### v0.4.2
29
38
  - **Fixed** `Omit<HTMLAttributes, 'color'>` resolves TS2320 conflict with CVA color prop
30
39
 
@@ -47,6 +47,10 @@
47
47
  - Grain children (DevalokGrain) are auto-separated and rendered as direct button children for absolute positioning
48
48
 
49
49
  ## Changes
50
+ ### v0.33.0
51
+ - **Added** `disabled` inherited from ButtonGroup context
52
+ - **Added** Position-aware border-radius when inside an attached ButtonGroup (compound component pattern)
53
+
50
54
  ### v0.29.0
51
55
  - **Added** `soft` variant — tinted background, colored text (new middle ground between solid and ghost)
52
56
  - **Added** 5 color options: `accent` (default), `error`, `success`, `warning`, `neutral` (replaces old `default`/`error`-only axis)
@@ -0,0 +1,41 @@
1
+ # SplitButton
2
+
3
+ A compound button that combines a primary action with a dropdown trigger, rendered as a single visual unit `[Action | ▼]`.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { SplitButton } from '@devalok/shilp-sutra'
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ <SplitButton onClick={handleSave} dropdownContent={<SaveOptions />}>
15
+ Save
16
+ </SplitButton>
17
+ ```
18
+
19
+ ## Props
20
+
21
+ | Prop | Type | Default | Description |
22
+ |------|------|---------|-------------|
23
+ | children | ReactNode | — | Primary action content (left side) |
24
+ | onClick | (e) => void | — | Primary click handler |
25
+ | dropdownContent | ReactNode | — | Content rendered inside floating dropdown |
26
+ | variant | 'solid' \| 'soft' \| 'outline' | 'solid' | Visual style |
27
+ | color | 'accent' \| 'error' \| 'success' \| 'warning' \| 'neutral' | 'accent' | Color scheme |
28
+ | size | 'xs' \| 'sm' \| 'md' \| 'icon-xs' \| 'icon-sm' \| 'icon-md' | 'md' | Size |
29
+ | triggerSide | 'left' \| 'right' | 'right' | Which side the dropdown chevron sits on |
30
+ | triggerWidth | number \| string | auto | Custom width for the trigger half |
31
+ | placement | Placement | 'top-end' | Floating UI placement for dropdown |
32
+ | disabled | boolean | false | Disable both halves |
33
+ | open | boolean | — | Controlled open state |
34
+ | onOpenChange | (open: boolean) => void | — | Open state callback |
35
+ | dropdownLabel | string | 'More options' | aria-label for trigger |
36
+ | dropdownIcon | ReactNode | chevron-down | Custom trigger icon |
37
+
38
+ ## Changes
39
+
40
+ ### v0.33.0
41
+ - **Added** Initial release