@7onic-ui/react 0.2.2 → 0.2.3

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/dist/index.mjs CHANGED
@@ -3542,7 +3542,7 @@ var CardTitle = React22.forwardRef(
3542
3542
  {
3543
3543
  ref,
3544
3544
  className: cn(
3545
- "font-semibold text-foreground leading-none tracking-tight",
3545
+ "text-base font-semibold text-foreground tracking-tight",
3546
3546
  icon && "flex items-center gap-2",
3547
3547
  className
3548
3548
  ),
@@ -5629,9 +5629,9 @@ var alertVariants = cva26(
5629
5629
  {
5630
5630
  variants: {
5631
5631
  size: {
5632
- sm: "gap-2 p-3 text-sm",
5633
- default: "gap-2.5 p-4 text-md",
5634
- lg: "gap-3 p-5 text-md"
5632
+ sm: "gap-2 p-2.5 text-sm",
5633
+ default: "gap-2.5 p-3.5 text-md",
5634
+ lg: "gap-3 p-4 text-md"
5635
5635
  },
5636
5636
  radius: {
5637
5637
  none: "rounded-none",
@@ -5659,15 +5659,20 @@ var ICON_SIZE_MAP = {
5659
5659
  lg: "icon-md"
5660
5660
  // 20px
5661
5661
  };
5662
+ var ICON_LINE_MAP = {
5663
+ sm: "text-sm",
5664
+ default: "text-md",
5665
+ lg: "text-base"
5666
+ };
5662
5667
  var TITLE_SIZE_MAP = {
5663
- sm: "text-sm font-semibold leading-4 tracking-tight",
5664
- default: "font-semibold leading-[18px] tracking-tight",
5665
- lg: "text-base font-semibold leading-5 tracking-tight"
5668
+ sm: "text-sm font-semibold tracking-tight",
5669
+ default: "text-md font-semibold tracking-tight",
5670
+ lg: "text-base font-semibold tracking-tight"
5666
5671
  };
5667
5672
  var DESC_SIZE_MAP = {
5668
5673
  sm: "text-xs mt-0.5",
5669
- default: "text-sm mt-1",
5670
- lg: "text-md mt-1.5"
5674
+ default: "text-sm mt-0.5",
5675
+ lg: "text-md mt-0.5"
5671
5676
  };
5672
5677
  var AlertContext = React31.createContext({
5673
5678
  variant: "default",
@@ -5707,7 +5712,7 @@ var AlertRoot = React31.forwardRef(
5707
5712
  ),
5708
5713
  ...props
5709
5714
  },
5710
- !hideIcon && /* @__PURE__ */ React31.createElement("span", { className: "shrink-0" }, icon || /* @__PURE__ */ React31.createElement(StatusIcon, { className: ICON_SIZE_MAP[size] })),
5715
+ !hideIcon && /* @__PURE__ */ React31.createElement("span", { className: cn("shrink-0 flex items-center min-h-[1lh]", ICON_LINE_MAP[size]) }, icon || /* @__PURE__ */ React31.createElement(StatusIcon, { className: ICON_SIZE_MAP[size] })),
5711
5716
  /* @__PURE__ */ React31.createElement("div", { className: "flex-1 min-w-0" }, children),
5712
5717
  closable && /* @__PURE__ */ React31.createElement(
5713
5718
  "button",
@@ -6838,6 +6843,862 @@ var Skeleton = React35.forwardRef(
6838
6843
  }
6839
6844
  );
6840
6845
  Skeleton.displayName = "Skeleton";
6846
+
6847
+ // src/components/ui/typing-indicator.tsx
6848
+ import * as React36 from "react";
6849
+ import { cva as cva31 } from "class-variance-authority";
6850
+ var DOT_SIZES2 = { sm: 4, default: 6, lg: 8 };
6851
+ var DOT_GAP2 = { sm: 3, default: 4, lg: 5 };
6852
+ var SPEED_MS2 = { slow: 1600, default: 1100, fast: 700 };
6853
+ var DOT_COUNT2 = 3;
6854
+ var bgColorMap2 = {
6855
+ default: "bg-foreground",
6856
+ primary: "bg-primary",
6857
+ muted: "bg-text-subtle"
6858
+ };
6859
+ var typingIndicatorVariants = cva31(
6860
+ "inline-flex items-center",
6861
+ {
6862
+ variants: {
6863
+ size: {
6864
+ sm: "gap-1.5 text-xs",
6865
+ default: "gap-2 text-sm",
6866
+ lg: "gap-2.5 text-base"
6867
+ }
6868
+ },
6869
+ defaultVariants: {
6870
+ size: "default"
6871
+ }
6872
+ }
6873
+ );
6874
+ var TypingIndicator = React36.forwardRef(
6875
+ ({
6876
+ className,
6877
+ variant = "dots",
6878
+ size,
6879
+ color = "muted",
6880
+ speed = "default",
6881
+ label = "Typing",
6882
+ showLabel = false,
6883
+ ...props
6884
+ }, ref) => {
6885
+ const resolvedSize = size ?? "default";
6886
+ return /* @__PURE__ */ React36.createElement(
6887
+ "div",
6888
+ {
6889
+ ref,
6890
+ role: "status",
6891
+ "aria-live": "polite",
6892
+ "aria-label": label,
6893
+ className: cn(typingIndicatorVariants({ size }), className),
6894
+ ...props
6895
+ },
6896
+ variant === "dots" && /* @__PURE__ */ React36.createElement(DotsIndicator, { size: resolvedSize, color, speed }),
6897
+ variant === "cursor" && /* @__PURE__ */ React36.createElement(CursorIndicator, { size: resolvedSize, color }),
6898
+ showLabel && /* @__PURE__ */ React36.createElement("span", { className: "text-text-muted select-none" }, label)
6899
+ );
6900
+ }
6901
+ );
6902
+ TypingIndicator.displayName = "TypingIndicator";
6903
+ function DotsIndicator({
6904
+ size,
6905
+ color,
6906
+ speed
6907
+ }) {
6908
+ const dotSize = DOT_SIZES2[size];
6909
+ const gap = DOT_GAP2[size];
6910
+ const duration = SPEED_MS2[speed];
6911
+ return /* @__PURE__ */ React36.createElement("div", { className: "inline-flex items-center", style: { gap } }, Array.from({ length: DOT_COUNT2 }, (_, i) => /* @__PURE__ */ React36.createElement(
6912
+ "div",
6913
+ {
6914
+ key: i,
6915
+ className: cn(
6916
+ "rounded-full animate-spinner-dot motion-reduce:animate-none motion-reduce:opacity-60",
6917
+ bgColorMap2[color]
6918
+ ),
6919
+ style: {
6920
+ width: dotSize,
6921
+ height: dotSize,
6922
+ animationDuration: `${duration}ms`,
6923
+ animationDelay: `${i * (duration / DOT_COUNT2 / 1.5)}ms`
6924
+ }
6925
+ }
6926
+ )));
6927
+ }
6928
+ var CURSOR_SIZE = {
6929
+ sm: { width: 2, height: 14 },
6930
+ default: { width: 2.5, height: 20 },
6931
+ lg: { width: 3, height: 26 }
6932
+ };
6933
+ function CursorIndicator({
6934
+ size,
6935
+ color
6936
+ }) {
6937
+ const { width, height } = CURSOR_SIZE[size];
6938
+ return /* @__PURE__ */ React36.createElement(
6939
+ "div",
6940
+ {
6941
+ "aria-hidden": "true",
6942
+ className: cn(
6943
+ "rounded-full animate-typing-cursor motion-reduce:animate-none motion-reduce:opacity-60",
6944
+ bgColorMap2[color]
6945
+ ),
6946
+ style: { width, height, animationDuration: "1200ms" }
6947
+ }
6948
+ );
6949
+ }
6950
+
6951
+ // src/components/ui/quick-reply.tsx
6952
+ import * as React37 from "react";
6953
+ import { Slot as Slot7 } from "@radix-ui/react-slot";
6954
+ import { cva as cva32 } from "class-variance-authority";
6955
+ var colorMap5 = {
6956
+ default: {
6957
+ outline: "border border-border text-foreground bg-background hover:bg-background-muted",
6958
+ filled: "bg-background-muted text-foreground hover:bg-background-elevated",
6959
+ ghost: "text-foreground hover:bg-background-muted"
6960
+ },
6961
+ primary: {
6962
+ outline: "border border-primary text-text-primary bg-background hover:bg-primary-tint",
6963
+ filled: "bg-primary-tint text-text-primary hover:bg-primary/20",
6964
+ ghost: "text-text-primary hover:bg-primary-tint"
6965
+ }
6966
+ };
6967
+ var QuickReplyContext = React37.createContext({
6968
+ size: "default",
6969
+ variant: "outline",
6970
+ color: "default",
6971
+ radius: "full"
6972
+ });
6973
+ var quickReplyRootVariants = cva32(
6974
+ "flex items-center",
6975
+ {
6976
+ variants: {
6977
+ layout: {
6978
+ scroll: "overflow-x-auto",
6979
+ wrap: "flex-wrap"
6980
+ },
6981
+ gap: {
6982
+ sm: "gap-1.5",
6983
+ default: "gap-2",
6984
+ lg: "gap-2.5"
6985
+ }
6986
+ },
6987
+ defaultVariants: {
6988
+ layout: "scroll",
6989
+ gap: "default"
6990
+ }
6991
+ }
6992
+ );
6993
+ var quickReplyItemVariants = cva32(
6994
+ [
6995
+ "inline-flex items-center justify-center",
6996
+ "whitespace-nowrap font-semibold shrink-0",
6997
+ "transition-colors duration-fast cursor-pointer",
6998
+ "focus-visible:outline-none focus-visible:focus-ring",
6999
+ "disabled:pointer-events-none disabled:opacity-50",
7000
+ "select-none"
7001
+ ].join(" "),
7002
+ {
7003
+ variants: {
7004
+ size: {
7005
+ sm: "h-7 px-2.5 text-xs gap-1.5",
7006
+ // 28px
7007
+ default: "h-8 px-3 text-sm gap-1.5",
7008
+ // 32px
7009
+ lg: "h-9 px-3.5 text-md gap-1.5"
7010
+ // 36px
7011
+ },
7012
+ radius: {
7013
+ md: "rounded-md",
7014
+ // 6px
7015
+ lg: "rounded-lg",
7016
+ // 8px
7017
+ full: "rounded-full"
7018
+ // 9999px
7019
+ }
7020
+ },
7021
+ defaultVariants: {
7022
+ size: "default",
7023
+ radius: "full"
7024
+ }
7025
+ }
7026
+ );
7027
+ var iconSizeMap = {
7028
+ sm: "icon-xs",
7029
+ // 14px
7030
+ default: "icon-xs",
7031
+ // 14px
7032
+ lg: "icon-sm"
7033
+ // 16px
7034
+ };
7035
+ var QuickReplyRoot = React37.forwardRef(
7036
+ ({
7037
+ className,
7038
+ layout = "scroll",
7039
+ gap = "default",
7040
+ variant = "outline",
7041
+ color = "default",
7042
+ size = "default",
7043
+ radius = "full",
7044
+ role = "group",
7045
+ children,
7046
+ ...props
7047
+ }, ref) => {
7048
+ const contextValue = React37.useMemo(
7049
+ () => ({ size, variant, color, radius }),
7050
+ [size, variant, color, radius]
7051
+ );
7052
+ return /* @__PURE__ */ React37.createElement(QuickReplyContext.Provider, { value: contextValue }, /* @__PURE__ */ React37.createElement(
7053
+ "div",
7054
+ {
7055
+ ref,
7056
+ role,
7057
+ className: cn(quickReplyRootVariants({ layout, gap }), className),
7058
+ ...props
7059
+ },
7060
+ children
7061
+ ));
7062
+ }
7063
+ );
7064
+ QuickReplyRoot.displayName = "QuickReply";
7065
+ var QuickReplyItem = React37.forwardRef(
7066
+ ({ className, icon, asChild = false, children, ...props }, ref) => {
7067
+ const { size, variant, color, radius } = React37.useContext(QuickReplyContext);
7068
+ const Comp = asChild ? Slot7 : "button";
7069
+ const colorClasses = colorMap5[color][variant];
7070
+ return /* @__PURE__ */ React37.createElement(
7071
+ Comp,
7072
+ {
7073
+ ref,
7074
+ type: !asChild ? "button" : void 0,
7075
+ ...props,
7076
+ className: cn(
7077
+ quickReplyItemVariants({ size, radius }),
7078
+ colorClasses,
7079
+ className
7080
+ )
7081
+ },
7082
+ icon && /* @__PURE__ */ React37.createElement(
7083
+ "span",
7084
+ {
7085
+ className: cn("shrink-0 [&>svg]:w-full [&>svg]:h-full", iconSizeMap[size]),
7086
+ "aria-hidden": "true"
7087
+ },
7088
+ icon
7089
+ ),
7090
+ children
7091
+ );
7092
+ }
7093
+ );
7094
+ QuickReplyItem.displayName = "QuickReply.Item";
7095
+ var QuickReply = Object.assign(QuickReplyRoot, {
7096
+ Item: QuickReplyItem
7097
+ });
7098
+
7099
+ // src/components/ui/chat-input.tsx
7100
+ import * as React38 from "react";
7101
+ import { cva as cva33 } from "class-variance-authority";
7102
+ var fieldPaddingMap = {
7103
+ sm: "pt-2.5 px-3 text-md",
7104
+ default: "pt-3 px-4 text-md",
7105
+ lg: "pt-4 px-5 text-base"
7106
+ };
7107
+ var submitWrapperMap = {
7108
+ sm: "px-2.5 pb-2.5",
7109
+ default: "px-3 pb-3",
7110
+ lg: "px-4 pb-4"
7111
+ };
7112
+ var submitSizeMap = {
7113
+ sm: "h-7 w-7",
7114
+ default: "h-8 w-8",
7115
+ lg: "h-9 w-9"
7116
+ };
7117
+ var submitRadiusMap = {
7118
+ sm: "rounded-md",
7119
+ md: "rounded-md",
7120
+ lg: "rounded-md",
7121
+ xl: "rounded-lg",
7122
+ "2xl": "rounded-xl",
7123
+ full: "rounded-full"
7124
+ };
7125
+ var buttonRadiusClassMap = {
7126
+ sm: "rounded-sm",
7127
+ md: "rounded-md",
7128
+ lg: "rounded-lg",
7129
+ xl: "rounded-xl",
7130
+ "2xl": "rounded-2xl",
7131
+ full: "rounded-full"
7132
+ };
7133
+ var submitIconSizeMap = {
7134
+ sm: "icon-xs",
7135
+ default: "icon-xs",
7136
+ lg: "icon-sm"
7137
+ };
7138
+ var countStyleMap = {
7139
+ sm: "text-xs",
7140
+ default: "text-xs",
7141
+ lg: "text-sm"
7142
+ };
7143
+ var lineHeightMap = {
7144
+ sm: 20,
7145
+ default: 22,
7146
+ lg: 24
7147
+ };
7148
+ var inlineFieldPaddingMap = {
7149
+ sm: "px-3 text-md",
7150
+ default: "px-4 text-md",
7151
+ lg: "px-5 text-base"
7152
+ };
7153
+ var submitWrapperInlineMap = {
7154
+ sm: "px-2.5 py-2.5",
7155
+ default: "px-3 py-3",
7156
+ lg: "px-4 py-4"
7157
+ };
7158
+ var chatInputVariants = cva33(
7159
+ [
7160
+ "flex flex-col w-full overflow-hidden cursor-text",
7161
+ "transition-colors duration-micro"
7162
+ ].join(" "),
7163
+ {
7164
+ variants: {
7165
+ variant: {
7166
+ outline: [
7167
+ "border border-border bg-background",
7168
+ "hover:border-border-strong",
7169
+ "focus-within:border-border-strong focus-within:hover:border-border-strong"
7170
+ ].join(" "),
7171
+ filled: [
7172
+ "border border-transparent bg-background-muted",
7173
+ "focus-within:border-border"
7174
+ ].join(" ")
7175
+ },
7176
+ radius: {
7177
+ sm: "rounded-sm",
7178
+ md: "rounded-md",
7179
+ lg: "rounded-lg",
7180
+ xl: "rounded-xl",
7181
+ "2xl": "rounded-2xl",
7182
+ full: "rounded-full"
7183
+ }
7184
+ },
7185
+ defaultVariants: {
7186
+ variant: "outline",
7187
+ radius: "xl"
7188
+ }
7189
+ }
7190
+ );
7191
+ var ChatInputContext = React38.createContext({
7192
+ size: "default",
7193
+ color: "default",
7194
+ layout: "default",
7195
+ radius: "xl",
7196
+ disabled: false,
7197
+ isEmpty: true,
7198
+ charCount: 0,
7199
+ showCount: false,
7200
+ maxLength: void 0,
7201
+ _setIsEmpty: () => {
7202
+ },
7203
+ _setCharCount: () => {
7204
+ },
7205
+ _setShowCount: () => {
7206
+ },
7207
+ _setMaxLength: () => {
7208
+ },
7209
+ _fieldRef: { current: null },
7210
+ _handleSubmit: () => {
7211
+ }
7212
+ });
7213
+ var ChatInputRoot = React38.forwardRef(
7214
+ ({
7215
+ className,
7216
+ variant = "outline",
7217
+ radius = "xl",
7218
+ size = "default",
7219
+ color = "default",
7220
+ layout = "default",
7221
+ disabled = false,
7222
+ onSubmit,
7223
+ children,
7224
+ ...props
7225
+ }, ref) => {
7226
+ const [isEmpty, setIsEmpty] = React38.useState(true);
7227
+ const [charCount, setCharCount] = React38.useState(0);
7228
+ const [showCount, setShowCount] = React38.useState(false);
7229
+ const [maxLength, setMaxLength] = React38.useState(void 0);
7230
+ const fieldRef = React38.useRef(null);
7231
+ const handleSubmit = React38.useCallback(() => {
7232
+ const el = fieldRef.current;
7233
+ if (!el || disabled) return;
7234
+ const value = el.value.trim();
7235
+ if (!value) return;
7236
+ onSubmit?.(value);
7237
+ el.value = "";
7238
+ el.style.height = "auto";
7239
+ setIsEmpty(true);
7240
+ setCharCount(0);
7241
+ }, [disabled, onSubmit]);
7242
+ const contextValue = React38.useMemo(
7243
+ () => ({
7244
+ size,
7245
+ color,
7246
+ layout,
7247
+ radius,
7248
+ disabled,
7249
+ isEmpty,
7250
+ charCount,
7251
+ showCount,
7252
+ maxLength,
7253
+ _setIsEmpty: setIsEmpty,
7254
+ _setCharCount: setCharCount,
7255
+ _setShowCount: setShowCount,
7256
+ _setMaxLength: setMaxLength,
7257
+ _fieldRef: fieldRef,
7258
+ _handleSubmit: handleSubmit
7259
+ }),
7260
+ [size, color, layout, radius, disabled, isEmpty, charCount, showCount, maxLength, handleSubmit]
7261
+ );
7262
+ const handleContainerClick = React38.useCallback(
7263
+ (e) => {
7264
+ if (!disabled && !e.target.closest("button")) {
7265
+ fieldRef.current?.focus();
7266
+ }
7267
+ },
7268
+ [disabled]
7269
+ );
7270
+ return /* @__PURE__ */ React38.createElement(ChatInputContext.Provider, { value: contextValue }, /* @__PURE__ */ React38.createElement(
7271
+ "div",
7272
+ {
7273
+ ref,
7274
+ "data-disabled": disabled || void 0,
7275
+ onClick: handleContainerClick,
7276
+ className: cn(
7277
+ chatInputVariants({ variant, radius }),
7278
+ layout === "inline" && "flex-row",
7279
+ disabled && "opacity-50 pointer-events-none",
7280
+ className
7281
+ ),
7282
+ ...props
7283
+ },
7284
+ children
7285
+ ));
7286
+ }
7287
+ );
7288
+ ChatInputRoot.displayName = "ChatInput";
7289
+ var ChatInputField = React38.forwardRef(
7290
+ ({
7291
+ className,
7292
+ maxRows = 8,
7293
+ showCount = false,
7294
+ maxLength,
7295
+ placeholder,
7296
+ onChange,
7297
+ onKeyDown,
7298
+ disabled,
7299
+ ...props
7300
+ }, forwardedRef) => {
7301
+ const ctx = React38.useContext(ChatInputContext);
7302
+ const internalRef = React38.useRef(null);
7303
+ const isDisabled = disabled ?? ctx.disabled;
7304
+ React38.useEffect(() => {
7305
+ ctx._setShowCount(showCount);
7306
+ ctx._setMaxLength(maxLength);
7307
+ }, [showCount, maxLength]);
7308
+ const setRef = React38.useCallback(
7309
+ (el) => {
7310
+ internalRef.current = el;
7311
+ ctx._fieldRef.current = el;
7312
+ if (typeof forwardedRef === "function") {
7313
+ forwardedRef(el);
7314
+ } else if (forwardedRef) {
7315
+ forwardedRef.current = el;
7316
+ }
7317
+ },
7318
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7319
+ [forwardedRef]
7320
+ );
7321
+ const autoResize = React38.useCallback(() => {
7322
+ const el = internalRef.current;
7323
+ if (!el) return;
7324
+ el.style.height = "auto";
7325
+ const maxHeight = maxRows * lineHeightMap[ctx.size];
7326
+ const newHeight = Math.min(el.scrollHeight, maxHeight);
7327
+ el.style.height = `${newHeight}px`;
7328
+ el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden";
7329
+ }, [ctx.size, maxRows]);
7330
+ const handleChange = React38.useCallback(
7331
+ (e) => {
7332
+ const val = e.target.value;
7333
+ ctx._setIsEmpty(val.trim() === "");
7334
+ ctx._setCharCount(val.length);
7335
+ autoResize();
7336
+ onChange?.(e);
7337
+ },
7338
+ [ctx, autoResize, onChange]
7339
+ );
7340
+ const handleKeyDown = React38.useCallback(
7341
+ (e) => {
7342
+ if (e.key === "Enter" && !e.shiftKey) {
7343
+ e.preventDefault();
7344
+ ctx._handleSubmit();
7345
+ }
7346
+ onKeyDown?.(e);
7347
+ },
7348
+ [ctx, onKeyDown]
7349
+ );
7350
+ return /* @__PURE__ */ React38.createElement("div", { className: cn("flex-1 min-w-0", ctx.layout === "inline" && "flex items-center self-stretch") }, /* @__PURE__ */ React38.createElement(
7351
+ "textarea",
7352
+ {
7353
+ ref: setRef,
7354
+ rows: 1,
7355
+ placeholder,
7356
+ maxLength,
7357
+ disabled: isDisabled,
7358
+ className: cn(
7359
+ "w-full bg-transparent text-foreground placeholder:text-foreground/30",
7360
+ "resize-none outline-none focus:outline-none",
7361
+ "disabled:cursor-not-allowed",
7362
+ "overflow-hidden",
7363
+ ctx.layout === "inline" ? inlineFieldPaddingMap[ctx.size] : fieldPaddingMap[ctx.size],
7364
+ className
7365
+ ),
7366
+ onChange: handleChange,
7367
+ onKeyDown: handleKeyDown,
7368
+ ...props
7369
+ }
7370
+ ));
7371
+ }
7372
+ );
7373
+ ChatInputField.displayName = "ChatInput.Field";
7374
+ var ChatInputSubmit = React38.forwardRef(
7375
+ ({
7376
+ className,
7377
+ loading = false,
7378
+ onStop,
7379
+ children,
7380
+ disabled,
7381
+ onClick,
7382
+ buttonRadius,
7383
+ ...props
7384
+ }, ref) => {
7385
+ const ctx = React38.useContext(ChatInputContext);
7386
+ const isDisabled = (disabled ?? ctx.disabled) || !loading && ctx.isEmpty;
7387
+ const handleClick = React38.useCallback(
7388
+ (e) => {
7389
+ if (loading) {
7390
+ onStop?.();
7391
+ } else if (!isDisabled) {
7392
+ ctx._handleSubmit();
7393
+ }
7394
+ onClick?.(e);
7395
+ },
7396
+ [ctx, isDisabled, loading, onStop, onClick]
7397
+ );
7398
+ return /* @__PURE__ */ React38.createElement("div", { className: cn("flex items-center justify-end gap-2 shrink-0 cursor-default", ctx.layout === "inline" ? submitWrapperInlineMap[ctx.size] : submitWrapperMap[ctx.size]) }, ctx.showCount && ctx.maxLength != null && /* @__PURE__ */ React38.createElement("span", { className: cn("text-text-subtle select-none", countStyleMap[ctx.size]) }, ctx.charCount, " / ", ctx.maxLength), /* @__PURE__ */ React38.createElement(
7399
+ "button",
7400
+ {
7401
+ ref,
7402
+ type: "button",
7403
+ disabled: isDisabled && !loading,
7404
+ "aria-label": loading ? "Stop generating" : "Send message",
7405
+ className: cn(
7406
+ "inline-flex items-center justify-center shrink-0",
7407
+ buttonRadius ? buttonRadiusClassMap[buttonRadius] : submitRadiusMap[ctx.radius],
7408
+ "transition-all duration-fast",
7409
+ "focus-visible:outline-none focus-visible:focus-ring",
7410
+ submitSizeMap[ctx.size],
7411
+ // Loading state — enabled, pulsing
7412
+ loading && ctx.color === "default" && "bg-foreground text-background animate-pulse cursor-pointer",
7413
+ loading && ctx.color === "primary" && "bg-primary text-primary-foreground animate-pulse cursor-pointer",
7414
+ // Enabled state
7415
+ !loading && !isDisabled && ctx.color === "default" && [
7416
+ "bg-foreground text-background",
7417
+ "hover:bg-foreground/90 active:bg-foreground/80 active:scale-pressed cursor-pointer"
7418
+ ],
7419
+ !loading && !isDisabled && ctx.color === "primary" && [
7420
+ "bg-primary text-primary-foreground",
7421
+ "hover:bg-primary-hover active:scale-pressed cursor-pointer"
7422
+ ],
7423
+ // Disabled state
7424
+ !loading && isDisabled && "bg-background-muted text-text-subtle pointer-events-none",
7425
+ className
7426
+ ),
7427
+ onClick: handleClick,
7428
+ ...props
7429
+ },
7430
+ /* @__PURE__ */ React38.createElement(
7431
+ "span",
7432
+ {
7433
+ "aria-hidden": "true",
7434
+ className: cn("flex items-center justify-center", submitIconSizeMap[ctx.size])
7435
+ },
7436
+ children ?? (loading ? /* @__PURE__ */ React38.createElement(StopIcon, null) : /* @__PURE__ */ React38.createElement(SendIcon, null))
7437
+ )
7438
+ ));
7439
+ }
7440
+ );
7441
+ ChatInputSubmit.displayName = "ChatInput.Submit";
7442
+ function SendIcon() {
7443
+ return /* @__PURE__ */ React38.createElement(
7444
+ "svg",
7445
+ {
7446
+ viewBox: "0 0 24 24",
7447
+ fill: "none",
7448
+ stroke: "currentColor",
7449
+ strokeWidth: 2.5,
7450
+ strokeLinecap: "round",
7451
+ strokeLinejoin: "round",
7452
+ className: "w-full h-full"
7453
+ },
7454
+ /* @__PURE__ */ React38.createElement("path", { d: "M22 2 11 13" }),
7455
+ /* @__PURE__ */ React38.createElement("path", { d: "M22 2 15 22 11 13 2 9l20-7Z" })
7456
+ );
7457
+ }
7458
+ function StopIcon() {
7459
+ return /* @__PURE__ */ React38.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "w-full h-full" }, /* @__PURE__ */ React38.createElement("rect", { x: "5", y: "5", width: "14", height: "14", rx: "2" }));
7460
+ }
7461
+ var ChatInput = Object.assign(ChatInputRoot, {
7462
+ Field: ChatInputField,
7463
+ Submit: ChatInputSubmit
7464
+ });
7465
+
7466
+ // src/components/ui/chat-message.tsx
7467
+ import * as React39 from "react";
7468
+ var ChatMessageContext = React39.createContext({
7469
+ role: "assistant",
7470
+ variant: "bubble",
7471
+ color: "default",
7472
+ size: "default",
7473
+ radius: "2xl",
7474
+ tail: true,
7475
+ typing: false,
7476
+ actions: void 0
7477
+ });
7478
+ var bubblePaddingMap = {
7479
+ sm: "px-3 py-1.5",
7480
+ default: "px-3.5 py-2",
7481
+ lg: "px-4 py-2.5"
7482
+ };
7483
+ var bubbleTextMap = {
7484
+ sm: "text-sm",
7485
+ default: "text-md",
7486
+ lg: "text-base"
7487
+ };
7488
+ var footerTextMap = {
7489
+ sm: "text-2xs",
7490
+ default: "text-2xs",
7491
+ lg: "text-xs"
7492
+ };
7493
+ var rowGapMap = {
7494
+ sm: "gap-2",
7495
+ default: "gap-2.5",
7496
+ lg: "gap-3"
7497
+ };
7498
+ var avatarGapMap = {
7499
+ sm: "gap-1.5",
7500
+ md: "gap-2.5",
7501
+ lg: "gap-3"
7502
+ };
7503
+ var chatAvatarSizeMap = {
7504
+ sm: "w-6 h-6",
7505
+ // 24px
7506
+ md: "w-7 h-7",
7507
+ // 28px
7508
+ lg: "w-8 h-8"
7509
+ // 32px
7510
+ };
7511
+ var chatAvatarFontMap = {
7512
+ sm: "text-2xs",
7513
+ md: "text-2xs",
7514
+ lg: "text-xs"
7515
+ };
7516
+ var bubbleRadiusTailMap = {
7517
+ md: { assistant: "rounded-tl-md rounded-tr-md rounded-br-md rounded-bl-none", user: "rounded-tl-md rounded-tr-md rounded-br-none rounded-bl-md" },
7518
+ lg: { assistant: "rounded-tl-lg rounded-tr-lg rounded-br-lg rounded-bl-none", user: "rounded-tl-lg rounded-tr-lg rounded-br-none rounded-bl-lg" },
7519
+ xl: { assistant: "rounded-tl-xl rounded-tr-xl rounded-br-xl rounded-bl-none", user: "rounded-tl-xl rounded-tr-xl rounded-br-none rounded-bl-xl" },
7520
+ "2xl": { assistant: "rounded-tl-2xl rounded-tr-2xl rounded-br-2xl rounded-bl-none", user: "rounded-tl-2xl rounded-tr-2xl rounded-br-none rounded-bl-2xl" }
7521
+ };
7522
+ var bubbleRadiusSymMap = {
7523
+ md: "rounded-md",
7524
+ lg: "rounded-lg",
7525
+ xl: "rounded-xl",
7526
+ "2xl": "rounded-2xl"
7527
+ };
7528
+ function getBubbleColors(color, variant) {
7529
+ if (variant === "flat") {
7530
+ return color === "muted" ? "bg-background-muted" : "";
7531
+ }
7532
+ switch (color) {
7533
+ case "default":
7534
+ return "bg-background-paper border border-border text-foreground";
7535
+ case "muted":
7536
+ return "bg-background-muted text-foreground";
7537
+ case "primary":
7538
+ return "bg-primary text-primary-foreground";
7539
+ case "dark":
7540
+ return "bg-foreground text-background";
7541
+ }
7542
+ }
7543
+ var builtInStatusLabels = {
7544
+ sending: { label: "Sending...", className: "text-text-subtle" },
7545
+ sent: { label: "Sent", className: "text-text-subtle" },
7546
+ read: { label: "Read", className: "text-text-subtle" },
7547
+ error: { label: "Failed", className: "text-error" }
7548
+ };
7549
+ var typingSizeMap = {
7550
+ sm: "sm",
7551
+ default: "sm",
7552
+ lg: "default"
7553
+ };
7554
+ var typingMinWidthMap = {
7555
+ sm: "min-w-12",
7556
+ default: "min-w-12",
7557
+ lg: "min-w-16"
7558
+ };
7559
+ var ChatMessageRoot = React39.forwardRef(
7560
+ ({
7561
+ className,
7562
+ role = "assistant",
7563
+ variant = "bubble",
7564
+ color = "default",
7565
+ size = "default",
7566
+ radius = "2xl",
7567
+ tail = true,
7568
+ typing = false,
7569
+ avatarSize,
7570
+ actions,
7571
+ children,
7572
+ ...props
7573
+ }, ref) => {
7574
+ const ctx = React39.useMemo(
7575
+ () => ({ role, variant, color, size, radius, tail, typing, actions }),
7576
+ [role, variant, color, size, radius, tail, typing, actions]
7577
+ );
7578
+ const gapClass = avatarSize ? avatarGapMap[avatarSize] : rowGapMap[size];
7579
+ return /* @__PURE__ */ React39.createElement(ChatMessageContext.Provider, { value: ctx }, /* @__PURE__ */ React39.createElement(
7580
+ "div",
7581
+ {
7582
+ ref,
7583
+ className: cn(
7584
+ "group/message flex items-end",
7585
+ gapClass,
7586
+ role === "user" && variant !== "flat" ? "flex-row-reverse" : "flex-row",
7587
+ className
7588
+ ),
7589
+ ...props
7590
+ },
7591
+ children
7592
+ ));
7593
+ }
7594
+ );
7595
+ ChatMessageRoot.displayName = "ChatMessage";
7596
+ function DefaultAiIcon() {
7597
+ return /* @__PURE__ */ React39.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "w-3/5 h-3/5", "aria-hidden": "true" }, /* @__PURE__ */ React39.createElement("path", { d: "M12 3l1.8 5.4 5.4 1.8-5.4 1.8L12 17.4l-1.8-5.4L4.8 10.2l5.4-1.8z" }), /* @__PURE__ */ React39.createElement("path", { d: "M19.5 3.5l.8 2.4 2.4.8-2.4.8L19.5 10l-.8-2.5-2.4-.8 2.4-.8z", opacity: ".6" }), /* @__PURE__ */ React39.createElement("path", { d: "M4.5 14.5l.7 2 2 .7-2 .7L4.5 20l-.7-2-2-.7 2-.7z", opacity: ".4" }));
7598
+ }
7599
+ var ChatMessageAvatar = React39.forwardRef(
7600
+ ({ className, size: avatarSize = "md", src, alt = "Avatar", initials, icon }, ref) => /* @__PURE__ */ React39.createElement(
7601
+ Avatar,
7602
+ {
7603
+ ref,
7604
+ "aria-hidden": "true",
7605
+ className: cn(chatAvatarSizeMap[avatarSize], "shrink-0", className)
7606
+ },
7607
+ src && /* @__PURE__ */ React39.createElement(AvatarImage, { src, alt }),
7608
+ /* @__PURE__ */ React39.createElement(AvatarFallback, { className: cn("font-semibold", chatAvatarFontMap[avatarSize]) }, icon ?? (initials || /* @__PURE__ */ React39.createElement(DefaultAiIcon, null)))
7609
+ )
7610
+ );
7611
+ ChatMessageAvatar.displayName = "ChatMessage.Avatar";
7612
+ var ChatMessageContent = React39.forwardRef(
7613
+ ({ className, children, ...props }, ref) => {
7614
+ const { role, variant, color, size, radius, tail, typing, actions } = React39.useContext(ChatMessageContext);
7615
+ const isTyping = !!typing;
7616
+ const typingProps = typeof typing === "object" ? typing : {};
7617
+ const isDarkBg = color === "primary" || color === "dark";
7618
+ const invertDots = variant === "bubble" && isDarkBg;
7619
+ const effectiveTail = variant === "flat" ? false : tail;
7620
+ const radiusClasses2 = effectiveTail ? bubbleRadiusTailMap[radius][role] : bubbleRadiusSymMap[radius];
7621
+ const bubble = /* @__PURE__ */ React39.createElement(
7622
+ "div",
7623
+ {
7624
+ ref,
7625
+ className: cn(
7626
+ bubbleTextMap[size],
7627
+ variant === "bubble" && "max-w-[75%]",
7628
+ isTyping && [typingMinWidthMap[size], "text-center"],
7629
+ getBubbleColors(color, variant),
7630
+ (variant === "bubble" || variant === "flat") && bubblePaddingMap[size],
7631
+ (variant === "bubble" || variant === "flat" && color === "muted") && radiusClasses2,
7632
+ className
7633
+ ),
7634
+ ...props
7635
+ },
7636
+ isTyping ? /* @__PURE__ */ React39.createElement(
7637
+ TypingIndicator,
7638
+ {
7639
+ size: typingSizeMap[size],
7640
+ color: "muted",
7641
+ ...typingProps,
7642
+ className: cn("align-middle", invertDots && "[&>div>div]:bg-current")
7643
+ }
7644
+ ) : children
7645
+ );
7646
+ if (!actions) return bubble;
7647
+ return /* @__PURE__ */ React39.createElement("div", { className: cn("flex items-end gap-1.5", role === "user" && variant !== "flat" ? "flex-row-reverse" : "flex-row") }, bubble, /* @__PURE__ */ React39.createElement("div", { className: cn(
7648
+ "shrink-0 flex items-center gap-1 pb-0.5",
7649
+ "opacity-0 group-hover/message:opacity-100",
7650
+ "transition-opacity duration-normal"
7651
+ ) }, actions));
7652
+ }
7653
+ );
7654
+ ChatMessageContent.displayName = "ChatMessage.Content";
7655
+ var ChatMessageFooter = React39.forwardRef(
7656
+ ({
7657
+ className,
7658
+ timestamp,
7659
+ status,
7660
+ role: roleProp,
7661
+ size: sizeProp,
7662
+ children,
7663
+ ...props
7664
+ }, ref) => {
7665
+ const ctx = React39.useContext(ChatMessageContext);
7666
+ const role = roleProp ?? ctx.role;
7667
+ const size = sizeProp ?? ctx.size;
7668
+ const variant = ctx.variant;
7669
+ const statusContent = React39.useMemo(() => {
7670
+ if (status == null) return null;
7671
+ if (typeof status === "string" && status in builtInStatusLabels) {
7672
+ const { label, className: cls } = builtInStatusLabels[status];
7673
+ return /* @__PURE__ */ React39.createElement("span", { className: cls }, label);
7674
+ }
7675
+ return status;
7676
+ }, [status]);
7677
+ return /* @__PURE__ */ React39.createElement(
7678
+ "div",
7679
+ {
7680
+ ref,
7681
+ className: cn(
7682
+ "flex items-center gap-1 select-none",
7683
+ footerTextMap[size],
7684
+ "text-text-subtle",
7685
+ role === "user" && variant !== "flat" ? "justify-end" : "justify-start",
7686
+ className
7687
+ ),
7688
+ ...props
7689
+ },
7690
+ children,
7691
+ statusContent,
7692
+ timestamp && /* @__PURE__ */ React39.createElement("span", null, timestamp)
7693
+ );
7694
+ }
7695
+ );
7696
+ ChatMessageFooter.displayName = "ChatMessage.Footer";
7697
+ var ChatMessage = Object.assign(ChatMessageRoot, {
7698
+ Avatar: ChatMessageAvatar,
7699
+ Content: ChatMessageContent,
7700
+ Footer: ChatMessageFooter
7701
+ });
6841
7702
  export {
6842
7703
  Accordion,
6843
7704
  AccordionContent,
@@ -6881,6 +7742,15 @@ export {
6881
7742
  CardHeader,
6882
7743
  CardImage,
6883
7744
  CardTitle,
7745
+ ChatInput,
7746
+ ChatInputField,
7747
+ ChatInputRoot,
7748
+ ChatInputSubmit,
7749
+ ChatMessage,
7750
+ ChatMessageAvatar,
7751
+ ChatMessageContent,
7752
+ ChatMessageFooter,
7753
+ ChatMessageRoot,
6884
7754
  Checkbox,
6885
7755
  Divider,
6886
7756
  Drawer,
@@ -6961,6 +7831,9 @@ export {
6961
7831
  PopoverRoot,
6962
7832
  PopoverTrigger,
6963
7833
  Progress,
7834
+ QuickReply,
7835
+ QuickReplyItem,
7836
+ QuickReplyRoot,
6964
7837
  RadioGroup,
6965
7838
  RadioGroupItem,
6966
7839
  Segmented,
@@ -7001,6 +7874,7 @@ export {
7001
7874
  TooltipProvider,
7002
7875
  TooltipRoot,
7003
7876
  TooltipTrigger,
7877
+ TypingIndicator,
7004
7878
  accordionTriggerVariants,
7005
7879
  accordionVariants,
7006
7880
  alertModalContentVariants,
@@ -7011,6 +7885,7 @@ export {
7011
7885
  breadcrumbSizeMap,
7012
7886
  buttonVariants,
7013
7887
  cardVariants,
7888
+ chatInputVariants,
7014
7889
  checkboxVariants,
7015
7890
  dividerVariants,
7016
7891
  drawerSizeHorizontal,
@@ -7026,6 +7901,8 @@ export {
7026
7901
  paginationItemVariants,
7027
7902
  paginationSizeMap,
7028
7903
  popoverContentVariants,
7904
+ quickReplyItemVariants,
7905
+ quickReplyRootVariants,
7029
7906
  radioGroupVariants,
7030
7907
  radioItemVariants,
7031
7908
  segmentedItemVariants,
@@ -7043,6 +7920,7 @@ export {
7043
7920
  toggleGroupVariants,
7044
7921
  toggleVariants,
7045
7922
  tooltipContentVariants,
7923
+ typingIndicatorVariants,
7046
7924
  useButtonGroup,
7047
7925
  useFieldContext,
7048
7926
  usePagination