@7onic-ui/react 0.2.2 → 0.2.4

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