@happiest-team/ai-chat 1.0.1

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/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # @happiest-team/ai-chat
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/@happiest-team/ai-chat.svg?style=flat-square)](https://www.npmjs.com/package/@happiest-team/ai-chat)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Strict-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
6
+
7
+ The official React Web SDK for **Happiest Chat**. Easily integrate intelligent, context-aware AI agents directly into your React and Next.js applications with our fully customizable headless hooks and premium UI components.
8
+
9
+ ## 🚀 Features
10
+
11
+ - **Plug & Play UI:** Drop-in `<ChatPanel />` with sleek, modern, "PhD-level" responsive styling out of the box.
12
+ - **Headless Hooks:** Build your own bespoke UI using the `useHappiestChat()` hook for total architectural freedom.
13
+ - **Zero Configuration:** Minimal setup with the `<HappiestProvider />`.
14
+ - **First-Class TypeScript:** Strict type definitions for an incredible developer experience and IDE autocompletion.
15
+ - **Edge Compatible:** Works perfectly with Next.js App Router, SSR, and Edge runtimes.
16
+
17
+ ## 📦 Installation
18
+
19
+ ```bash
20
+ npm install @happiest-team/ai-chat @happiest-team/ai-chat-core
21
+ ```
22
+ *(Yarn and pnpm are also supported)*
23
+
24
+ ## 🛠 Quick Start
25
+
26
+ ### 1. Setup the Provider
27
+ Wrap your application's root (or specific feature tree) with the `HappiestProvider`.
28
+
29
+ ```tsx
30
+ import { HappiestProvider } from '@happiest-team/ai-chat';
31
+
32
+ export default function App({ children }) {
33
+ return (
34
+ <HappiestProvider
35
+ clubId="YOUR_CLUB_ID"
36
+ config={{
37
+ publishableKey: "pk_live_your_key",
38
+ gatewayUrl: "https://api.happiest.com/api" // Optional custom gateway
39
+ }}
40
+ >
41
+ {children}
42
+ </HappiestProvider>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ### 2. Render the Chat Widget
48
+ Render the built-in `ChatPanel` anywhere inside the provider. It is pre-styled with optimized dimensions, z-indexing, and interactive typing animations.
49
+
50
+ ```tsx
51
+ import { ChatPanel } from '@happiest-team/ai-chat';
52
+
53
+ export function AgentWidget() {
54
+ return (
55
+ <div className="fixed bottom-4 right-4 z-50">
56
+ <ChatPanel />
57
+ </div>
58
+ );
59
+ }
60
+ ```
61
+
62
+ ## 🧠 Advanced: Headless Usage
63
+ Don't want our UI? Build your own widget by extracting the raw data logic using `useHappiestChat`.
64
+
65
+ ```tsx
66
+ import { useHappiestChat } from '@happiest-team/ai-chat';
67
+
68
+ export function CustomChat() {
69
+ const { messages, isTyping, sendMessage } = useHappiestChat();
70
+
71
+ return (
72
+ <div>
73
+ {messages.map(msg => (
74
+ <p key={msg.id} style={{ color: msg.isUser ? 'blue' : 'gray' }}>
75
+ {msg.text}
76
+ </p>
77
+ ))}
78
+ {isTyping && <p>Agent is typing...</p>}
79
+
80
+ <button onClick={() => sendMessage("Hello AI!")}>
81
+ Send
82
+ </button>
83
+ </div>
84
+ );
85
+ }
86
+ ```
87
+
88
+ ## 📄 License
89
+ MIT © Happiest
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { HappiestClient, HappiestConfig, HappiestState } from '@happiest-team/ai-chat-core';
3
+
4
+ declare const HappiestContext: React.Context<HappiestClient | null>;
5
+ interface HappiestProviderProps {
6
+ config: HappiestConfig;
7
+ children: React.ReactNode;
8
+ }
9
+ declare function HappiestProvider({ config, children }: HappiestProviderProps): React.JSX.Element;
10
+
11
+ declare function ChatPanel({ className }: {
12
+ className?: string;
13
+ }): React.JSX.Element;
14
+
15
+ declare function useHappiestChat(): HappiestState & {
16
+ sendMessage: (text: string) => void;
17
+ retryMessage: (id: string) => void;
18
+ clearSession: () => void;
19
+ };
20
+
21
+ export { ChatPanel, HappiestContext, HappiestProvider, type HappiestProviderProps, useHappiestChat };
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { HappiestClient, HappiestConfig, HappiestState } from '@happiest-team/ai-chat-core';
3
+
4
+ declare const HappiestContext: React.Context<HappiestClient | null>;
5
+ interface HappiestProviderProps {
6
+ config: HappiestConfig;
7
+ children: React.ReactNode;
8
+ }
9
+ declare function HappiestProvider({ config, children }: HappiestProviderProps): React.JSX.Element;
10
+
11
+ declare function ChatPanel({ className }: {
12
+ className?: string;
13
+ }): React.JSX.Element;
14
+
15
+ declare function useHappiestChat(): HappiestState & {
16
+ sendMessage: (text: string) => void;
17
+ retryMessage: (id: string) => void;
18
+ clearSession: () => void;
19
+ };
20
+
21
+ export { ChatPanel, HappiestContext, HappiestProvider, type HappiestProviderProps, useHappiestChat };
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ 'use strict';var react=require('react'),aiChatCore=require('@happiest-team/ai-chat-core'),jsxRuntime=require('react/jsx-runtime'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge');var h=react.createContext(null);function U({config:a,children:p}){let o=react.useRef(null);return o.current||(o.current=new aiChatCore.HappiestClient(a)),jsxRuntime.jsx(h.Provider,{value:o.current,children:p})}function f(){let a=react.useContext(h);if(!a)throw new Error("useHappiestChat must be used within a HappiestProvider");let[p,o]=react.useState(a.getState());return react.useEffect(()=>{let r=a.subscribe(o);return a.initSession(),r},[a]),{...p,sendMessage:r=>a.sendMessage(r),retryMessage:r=>a.retryMessage(r),clearSession:()=>a.clearSession()}}function d(...a){return tailwindMerge.twMerge(clsx.clsx(a))}function O({className:a}){let{messages:p,isTyping:o,sendMessage:r,retryMessage:w,widgetConfig:C}=f(),[c,x]=react.useState(""),[m,b]=react.useState(false),g=react.useRef(null);react.useEffect(()=>{g.current?.scrollIntoView({behavior:"smooth"});},[p,o,m]);let n=C||{},l=n.theme?.primary||n.primaryColor||"#0A65CC",i=n.iconUrl,u=n.iconNoBackground,N=n.agentName||"Chat Assistant",H=n.clubName||"Club",k=n.welcomeMessage||"Hi there! How can I help you today?",S=`
2
+ .happiest-chat-container {
3
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
4
+ }
5
+
6
+ .happiest-fab {
7
+ position: fixed;
8
+ bottom: 24px;
9
+ right: 24px;
10
+ transition: transform 0.2s, background-color 0.2s, filter 0.2s;
11
+ z-index: 2147483647;
12
+ }
13
+ .happiest-fab:hover {
14
+ transform: scale(1.05);
15
+ }
16
+
17
+ .happiest-panel {
18
+ position: fixed;
19
+ bottom: 100px;
20
+ right: 24px;
21
+ width: 360px;
22
+ height: 600px;
23
+ max-height: calc(100vh - 120px);
24
+ max-width: calc(100vw - 48px);
25
+ background: #ffffff;
26
+ border-radius: 16px;
27
+ box-shadow: 0 8px 32px rgba(0,0,0,0.15);
28
+ display: flex;
29
+ flex-direction: column;
30
+ overflow: hidden;
31
+ opacity: 0;
32
+ pointer-events: none;
33
+ transform: translateY(20px);
34
+ transition: opacity 0.3s, transform 0.3s;
35
+ border: 1px solid #e5e7eb;
36
+ z-index: 2147483647;
37
+ }
38
+
39
+ .happiest-panel.open {
40
+ opacity: 1;
41
+ pointer-events: auto;
42
+ transform: translateY(0);
43
+ }
44
+
45
+ .happiest-typing-indicator {
46
+ display: flex;
47
+ align-self: flex-start;
48
+ background: white;
49
+ border: 1px solid #e5e7eb;
50
+ padding: 12px 16px;
51
+ border-radius: 12px;
52
+ border-bottom-left-radius: 4px;
53
+ gap: 4px;
54
+ }
55
+ .happiest-dot {
56
+ width: 6px;
57
+ height: 6px;
58
+ background: #666666;
59
+ border-radius: 50%;
60
+ animation: happiest-bounce 1.4s infinite ease-in-out both;
61
+ }
62
+ .happiest-dot:nth-child(1) { animation-delay: -0.32s; }
63
+ .happiest-dot:nth-child(2) { animation-delay: -0.16s; }
64
+
65
+ @keyframes happiest-bounce {
66
+ 0%, 80%, 100% { transform: scale(0); }
67
+ 40% { transform: scale(1); }
68
+ }
69
+
70
+ @media (max-width: 480px) {
71
+ .happiest-fab {
72
+ bottom: 16px;
73
+ right: 16px;
74
+ }
75
+ .happiest-fab.open {
76
+ display: none !important;
77
+ }
78
+ .happiest-panel {
79
+ bottom: 0 !important;
80
+ left: 0 !important;
81
+ right: auto !important;
82
+ width: 100vw !important;
83
+ max-width: 100vw !important;
84
+ height: 85vh !important;
85
+ max-height: 85vh !important;
86
+ border-radius: 24px 24px 0 0 !important;
87
+ transform: translateY(100%);
88
+ }
89
+ .happiest-panel.open {
90
+ transform: translateY(0) !important;
91
+ }
92
+ }
93
+ `,M=i&&u?{width:"54px",height:"54px",borderRadius:"0",backgroundColor:"transparent",boxShadow:"none",filter:"drop-shadow(0 4px 8px rgba(0,0,0,0.2))"}:{width:"60px",height:"60px",borderRadius:"50%",backgroundColor:l,boxShadow:"0 4px 12px rgba(0,0,0,0.15)"};return jsxRuntime.jsxs("div",{className:d("happiest-chat-container",a),children:[jsxRuntime.jsx("style",{dangerouslySetInnerHTML:{__html:S}}),jsxRuntime.jsx("button",{onClick:()=>b(!m),className:d("happiest-fab flex items-center justify-center cursor-pointer outline-none border-none",m&&"open"),style:M,children:i?u?jsxRuntime.jsx("img",{src:i,alt:"Chat",style:{width:"100%",height:"100%",objectFit:"contain"}}):jsxRuntime.jsx("img",{src:i,alt:"Chat",style:{width:"32px",height:"32px",borderRadius:"50%",objectFit:"cover"}}):jsxRuntime.jsx(lucideReact.MessageSquare,{className:"w-7 h-7 text-white"})}),jsxRuntime.jsxs("div",{className:d("happiest-panel",m&&"open"),children:[jsxRuntime.jsxs("div",{className:"p-5 text-white flex justify-between items-center",style:{backgroundColor:l},children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-3",children:[i&&!u?jsxRuntime.jsx("img",{src:i,alt:"Agent",className:"w-8 h-8 rounded-full bg-white object-cover"}):i?jsxRuntime.jsx("img",{src:i,alt:"Agent",className:"w-8 h-8 object-contain"}):jsxRuntime.jsx("div",{className:"w-8 h-8 rounded-full bg-white/20 flex items-center justify-center",children:jsxRuntime.jsx(lucideReact.MessageSquare,{className:"w-4 h-4 text-white"})}),jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("h3",{className:"font-semibold text-[16px] m-0 leading-tight",children:N}),jsxRuntime.jsxs("p",{className:"text-[13px] opacity-90 m-0 mt-1 leading-tight",children:["Powered by Happiest for ",H]})]})]}),jsxRuntime.jsx("button",{onClick:()=>b(false),className:"text-white/80 hover:text-white bg-transparent border-none cursor-pointer p-1",children:jsxRuntime.jsx(lucideReact.X,{className:"w-5 h-5"})})]}),jsxRuntime.jsxs("div",{className:"flex-1 overflow-y-auto p-5 flex flex-col gap-3 bg-[#f9fafb]",children:[jsxRuntime.jsx("div",{className:"flex justify-start",children:jsxRuntime.jsx("div",{className:"max-w-[85%] rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm",children:k})}),p.map(t=>jsxRuntime.jsx("div",{className:d("flex",t.isUser?"justify-end":"justify-start"),children:jsxRuntime.jsxs("div",{className:d("flex flex-col",t.isUser?"items-end":"items-start","max-w-[85%]"),children:[jsxRuntime.jsx("div",{className:d("rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words",t.isUser?"text-white rounded-br-sm":"bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm",t.status==="failed"&&"opacity-90"),style:t.isUser?{backgroundColor:t.status==="failed"?"#ef4444":l}:void 0,children:t.text}),t.status==="failed"&&jsxRuntime.jsxs("button",{onClick:()=>w(t.id),className:"flex items-center gap-1 mt-1.5 text-[11px] font-medium text-red-500 bg-transparent border-none cursor-pointer hover:underline p-0 outline-none",children:[jsxRuntime.jsx(lucideReact.AlertCircle,{className:"w-3.5 h-3.5"}),"Failed to send. Click to retry"]})]})},t.id)),o&&jsxRuntime.jsxs("div",{className:"happiest-typing-indicator shadow-sm",children:[jsxRuntime.jsx("div",{className:"happiest-dot"}),jsxRuntime.jsx("div",{className:"happiest-dot"}),jsxRuntime.jsx("div",{className:"happiest-dot"})]}),jsxRuntime.jsx("div",{ref:g})]}),jsxRuntime.jsxs("form",{onSubmit:t=>{t.preventDefault(),r(c),x("");},className:"p-4 bg-white border-t border-gray-100 flex gap-3",children:[jsxRuntime.jsx("input",{type:"text",value:c,onChange:t=>x(t.target.value),placeholder:"Type your message...",className:"flex-1 border border-gray-200 rounded-[24px] px-4 py-2.5 text-[14px] outline-none font-sans transition-colors",onFocus:t=>t.target.style.borderColor=l,onBlur:t=>t.target.style.borderColor="#e5e7eb"}),jsxRuntime.jsx("button",{type:"submit",disabled:!c.trim(),className:"text-white border-none rounded-full w-10 h-10 flex items-center justify-center cursor-pointer transition-colors disabled:bg-[#d1d5db] disabled:cursor-not-allowed",style:{backgroundColor:c.trim()?l:"#d1d5db"},children:jsxRuntime.jsx(lucideReact.Send,{className:"w-4 h-4 ml-0.5"})})]})]})]})}exports.ChatPanel=O;exports.HappiestContext=h;exports.HappiestProvider=U;exports.useHappiestChat=f;//# sourceMappingURL=index.js.map
94
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/HappiestProvider.tsx","../src/hooks/useHappiestChat.ts","../src/components/ChatPanel.tsx"],"names":["HappiestContext","createContext","HappiestProvider","config","children","clientRef","useRef","HappiestClient","jsx","useHappiestChat","client","useContext","state","setState","useState","useEffect","unsubscribe","text","id","cn","inputs","twMerge","clsx","ChatPanel","className","messages","isTyping","sendMessage","retryMessage","widgetConfig","input","setInput","isOpen","setIsOpen","endRef","safeConfig","primaryColor","iconUrl","iconNoBackground","agentName","clubName","welcomeMessage","customCss","fabStyle","jsxs","MessageSquare","X","msg","AlertCircle","e","Send"],"mappings":"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","file":"index.js","sourcesContent":["import React, { createContext, useRef } from 'react';\nimport { HappiestClient, HappiestConfig } from '@happiest-team/ai-chat-core';\n\nexport const HappiestContext = createContext<HappiestClient | null>(null);\n\nexport interface HappiestProviderProps {\n config: HappiestConfig;\n children: React.ReactNode;\n}\n\nexport function HappiestProvider({ config, children }: HappiestProviderProps) {\n // Use a ref to ensure the client is only instantiated once per provider\n const clientRef = useRef<HappiestClient | null>(null);\n\n if (!clientRef.current) {\n clientRef.current = new HappiestClient(config);\n }\n\n return (\n <HappiestContext.Provider value={clientRef.current}>\n {children}\n </HappiestContext.Provider>\n );\n}\n","import { useContext, useEffect, useState } from 'react';\nimport { HappiestState } from '@happiest-team/ai-chat-core';\nimport { HappiestContext } from '../components/HappiestProvider';\n\nexport function useHappiestChat(): HappiestState & { sendMessage: (text: string) => void, retryMessage: (id: string) => void, clearSession: () => void } {\n const client = useContext(HappiestContext);\n \n if (!client) {\n throw new Error('useHappiestChat must be used within a HappiestProvider');\n }\n\n const [state, setState] = useState<HappiestState>(client.getState());\n\n useEffect(() => {\n const unsubscribe = client.subscribe(setState);\n client.initSession(); // Ensures session is initialized once mounted\n return unsubscribe;\n }, [client]);\n\n return {\n ...state,\n sendMessage: (text: string) => client.sendMessage(text),\n retryMessage: (id: string) => client.retryMessage(id),\n clearSession: () => client.clearSession(),\n };\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport { Send, X, MessageSquare, AlertCircle } from 'lucide-react';\nimport { useHappiestChat } from '../hooks/useHappiestChat';\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nfunction cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function ChatPanel({ className }: { className?: string }) {\n const { messages, isTyping, sendMessage, retryMessage, widgetConfig } = useHappiestChat();\n const [input, setInput] = useState('');\n const [isOpen, setIsOpen] = useState(false);\n const endRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n endRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages, isTyping, isOpen]);\n\n // Safely extract widget configs with fallbacks\n const safeConfig = widgetConfig || {};\n const primaryColor = safeConfig.theme?.primary || safeConfig.primaryColor || '#0A65CC';\n const iconUrl = safeConfig.iconUrl;\n const iconNoBackground = safeConfig.iconNoBackground;\n const agentName = safeConfig.agentName || 'Chat Assistant';\n const clubName = safeConfig.clubName || 'Club';\n const welcomeMessage = safeConfig.welcomeMessage || 'Hi there! How can I help you today?';\n\n const customCss = `\n .happiest-chat-container {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n }\n \n .happiest-fab {\n position: fixed;\n bottom: 24px;\n right: 24px;\n transition: transform 0.2s, background-color 0.2s, filter 0.2s;\n z-index: 2147483647;\n }\n .happiest-fab:hover {\n transform: scale(1.05);\n }\n \n .happiest-panel {\n position: fixed;\n bottom: 100px;\n right: 24px;\n width: 360px;\n height: 600px;\n max-height: calc(100vh - 120px);\n max-width: calc(100vw - 48px);\n background: #ffffff;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.15);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n transform: translateY(20px);\n transition: opacity 0.3s, transform 0.3s;\n border: 1px solid #e5e7eb;\n z-index: 2147483647;\n }\n \n .happiest-panel.open {\n opacity: 1;\n pointer-events: auto;\n transform: translateY(0);\n }\n\n .happiest-typing-indicator {\n display: flex;\n align-self: flex-start;\n background: white;\n border: 1px solid #e5e7eb;\n padding: 12px 16px;\n border-radius: 12px;\n border-bottom-left-radius: 4px;\n gap: 4px;\n }\n .happiest-dot {\n width: 6px;\n height: 6px;\n background: #666666;\n border-radius: 50%;\n animation: happiest-bounce 1.4s infinite ease-in-out both;\n }\n .happiest-dot:nth-child(1) { animation-delay: -0.32s; }\n .happiest-dot:nth-child(2) { animation-delay: -0.16s; }\n \n @keyframes happiest-bounce {\n 0%, 80%, 100% { transform: scale(0); }\n 40% { transform: scale(1); }\n }\n\n @media (max-width: 480px) {\n .happiest-fab {\n bottom: 16px;\n right: 16px;\n }\n .happiest-fab.open {\n display: none !important;\n }\n .happiest-panel {\n bottom: 0 !important;\n left: 0 !important;\n right: auto !important;\n width: 100vw !important;\n max-width: 100vw !important;\n height: 85vh !important;\n max-height: 85vh !important;\n border-radius: 24px 24px 0 0 !important;\n transform: translateY(100%);\n }\n .happiest-panel.open {\n transform: translateY(0) !important;\n }\n }\n `;\n\n const fabStyle = (iconUrl && iconNoBackground) ? {\n width: '54px',\n height: '54px',\n borderRadius: '0',\n backgroundColor: 'transparent',\n boxShadow: 'none',\n filter: 'drop-shadow(0 4px 8px rgba(0,0,0,0.2))'\n } : {\n width: '60px',\n height: '60px',\n borderRadius: '50%',\n backgroundColor: primaryColor,\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)'\n };\n\n return (\n <div className={cn(\"happiest-chat-container\", className)}>\n <style dangerouslySetInnerHTML={{ __html: customCss }} />\n\n {/* FAB */}\n <button\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\"happiest-fab flex items-center justify-center cursor-pointer outline-none border-none\", isOpen && \"open\")}\n style={fabStyle}\n >\n {iconUrl ? (\n iconNoBackground ? (\n <img src={iconUrl} alt=\"Chat\" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />\n ) : (\n <img src={iconUrl} alt=\"Chat\" style={{ width: '32px', height: '32px', borderRadius: '50%', objectFit: 'cover' }} />\n )\n ) : (\n <MessageSquare className=\"w-7 h-7 text-white\" />\n )}\n </button>\n\n {/* Chat Panel */}\n <div className={cn(\"happiest-panel\", isOpen && \"open\")}>\n {/* Header */}\n <div\n className=\"p-5 text-white flex justify-between items-center\"\n style={{ backgroundColor: primaryColor }}\n >\n <div className=\"flex items-center gap-3\">\n {iconUrl && !iconNoBackground ? (\n <img src={iconUrl} alt=\"Agent\" className=\"w-8 h-8 rounded-full bg-white object-cover\" />\n ) : iconUrl ? (\n <img src={iconUrl} alt=\"Agent\" className=\"w-8 h-8 object-contain\" />\n ) : (\n <div className=\"w-8 h-8 rounded-full bg-white/20 flex items-center justify-center\">\n <MessageSquare className=\"w-4 h-4 text-white\" />\n </div>\n )}\n <div>\n <h3 className=\"font-semibold text-[16px] m-0 leading-tight\">{agentName}</h3>\n <p className=\"text-[13px] opacity-90 m-0 mt-1 leading-tight\">Powered by Happiest for {clubName}</p>\n </div>\n </div>\n <button onClick={() => setIsOpen(false)} className=\"text-white/80 hover:text-white bg-transparent border-none cursor-pointer p-1\">\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-5 flex flex-col gap-3 bg-[#f9fafb]\">\n {/* Default Welcome Message */}\n <div className=\"flex justify-start\">\n <div className=\"max-w-[85%] rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm\">\n {welcomeMessage}\n </div>\n </div>\n\n {messages.map((msg) => (\n <div key={msg.id} className={cn(\"flex\", msg.isUser ? \"justify-end\" : \"justify-start\")}>\n <div className={cn(\"flex flex-col\", msg.isUser ? \"items-end\" : \"items-start\", \"max-w-[85%]\")}>\n <div\n className={cn(\n \"rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words\",\n msg.isUser ? \"text-white rounded-br-sm\" : \"bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm\",\n msg.status === 'failed' && \"opacity-90\"\n )}\n style={msg.isUser ? { backgroundColor: msg.status === 'failed' ? '#ef4444' : primaryColor } : undefined}\n >\n {msg.text}\n </div>\n {msg.status === 'failed' && (\n <button\n onClick={() => retryMessage(msg.id)}\n className=\"flex items-center gap-1 mt-1.5 text-[11px] font-medium text-red-500 bg-transparent border-none cursor-pointer hover:underline p-0 outline-none\"\n >\n <AlertCircle className=\"w-3.5 h-3.5\" />\n Failed to send. Click to retry\n </button>\n )}\n </div>\n </div>\n ))}\n {isTyping && (\n <div className=\"happiest-typing-indicator shadow-sm\">\n <div className=\"happiest-dot\"></div>\n <div className=\"happiest-dot\"></div>\n <div className=\"happiest-dot\"></div>\n </div>\n )}\n <div ref={endRef} />\n </div>\n\n {/* Input */}\n <form\n onSubmit={(e) => {\n e.preventDefault();\n sendMessage(input);\n setInput('');\n }}\n className=\"p-4 bg-white border-t border-gray-100 flex gap-3\"\n >\n <input\n type=\"text\"\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder=\"Type your message...\"\n className=\"flex-1 border border-gray-200 rounded-[24px] px-4 py-2.5 text-[14px] outline-none font-sans transition-colors\"\n onFocus={(e) => e.target.style.borderColor = primaryColor}\n onBlur={(e) => e.target.style.borderColor = '#e5e7eb'}\n />\n <button\n type=\"submit\"\n disabled={!input.trim()}\n className=\"text-white border-none rounded-full w-10 h-10 flex items-center justify-center cursor-pointer transition-colors disabled:bg-[#d1d5db] disabled:cursor-not-allowed\"\n style={{ backgroundColor: input.trim() ? primaryColor : '#d1d5db' }}\n >\n <Send className=\"w-4 h-4 ml-0.5\" />\n </button>\n </form>\n </div>\n </div>\n );\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,94 @@
1
+ import {createContext,useRef,useContext,useState,useEffect}from'react';import {HappiestClient}from'@happiest-team/ai-chat-core';import {jsx,jsxs}from'react/jsx-runtime';import {MessageSquare,X,AlertCircle,Send}from'lucide-react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';var h=createContext(null);function U({config:a,children:p}){let o=useRef(null);return o.current||(o.current=new HappiestClient(a)),jsx(h.Provider,{value:o.current,children:p})}function f(){let a=useContext(h);if(!a)throw new Error("useHappiestChat must be used within a HappiestProvider");let[p,o]=useState(a.getState());return useEffect(()=>{let r=a.subscribe(o);return a.initSession(),r},[a]),{...p,sendMessage:r=>a.sendMessage(r),retryMessage:r=>a.retryMessage(r),clearSession:()=>a.clearSession()}}function d(...a){return twMerge(clsx(a))}function O({className:a}){let{messages:p,isTyping:o,sendMessage:r,retryMessage:w,widgetConfig:C}=f(),[c,x]=useState(""),[m,b]=useState(false),g=useRef(null);useEffect(()=>{g.current?.scrollIntoView({behavior:"smooth"});},[p,o,m]);let n=C||{},l=n.theme?.primary||n.primaryColor||"#0A65CC",i=n.iconUrl,u=n.iconNoBackground,N=n.agentName||"Chat Assistant",H=n.clubName||"Club",k=n.welcomeMessage||"Hi there! How can I help you today?",S=`
2
+ .happiest-chat-container {
3
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
4
+ }
5
+
6
+ .happiest-fab {
7
+ position: fixed;
8
+ bottom: 24px;
9
+ right: 24px;
10
+ transition: transform 0.2s, background-color 0.2s, filter 0.2s;
11
+ z-index: 2147483647;
12
+ }
13
+ .happiest-fab:hover {
14
+ transform: scale(1.05);
15
+ }
16
+
17
+ .happiest-panel {
18
+ position: fixed;
19
+ bottom: 100px;
20
+ right: 24px;
21
+ width: 360px;
22
+ height: 600px;
23
+ max-height: calc(100vh - 120px);
24
+ max-width: calc(100vw - 48px);
25
+ background: #ffffff;
26
+ border-radius: 16px;
27
+ box-shadow: 0 8px 32px rgba(0,0,0,0.15);
28
+ display: flex;
29
+ flex-direction: column;
30
+ overflow: hidden;
31
+ opacity: 0;
32
+ pointer-events: none;
33
+ transform: translateY(20px);
34
+ transition: opacity 0.3s, transform 0.3s;
35
+ border: 1px solid #e5e7eb;
36
+ z-index: 2147483647;
37
+ }
38
+
39
+ .happiest-panel.open {
40
+ opacity: 1;
41
+ pointer-events: auto;
42
+ transform: translateY(0);
43
+ }
44
+
45
+ .happiest-typing-indicator {
46
+ display: flex;
47
+ align-self: flex-start;
48
+ background: white;
49
+ border: 1px solid #e5e7eb;
50
+ padding: 12px 16px;
51
+ border-radius: 12px;
52
+ border-bottom-left-radius: 4px;
53
+ gap: 4px;
54
+ }
55
+ .happiest-dot {
56
+ width: 6px;
57
+ height: 6px;
58
+ background: #666666;
59
+ border-radius: 50%;
60
+ animation: happiest-bounce 1.4s infinite ease-in-out both;
61
+ }
62
+ .happiest-dot:nth-child(1) { animation-delay: -0.32s; }
63
+ .happiest-dot:nth-child(2) { animation-delay: -0.16s; }
64
+
65
+ @keyframes happiest-bounce {
66
+ 0%, 80%, 100% { transform: scale(0); }
67
+ 40% { transform: scale(1); }
68
+ }
69
+
70
+ @media (max-width: 480px) {
71
+ .happiest-fab {
72
+ bottom: 16px;
73
+ right: 16px;
74
+ }
75
+ .happiest-fab.open {
76
+ display: none !important;
77
+ }
78
+ .happiest-panel {
79
+ bottom: 0 !important;
80
+ left: 0 !important;
81
+ right: auto !important;
82
+ width: 100vw !important;
83
+ max-width: 100vw !important;
84
+ height: 85vh !important;
85
+ max-height: 85vh !important;
86
+ border-radius: 24px 24px 0 0 !important;
87
+ transform: translateY(100%);
88
+ }
89
+ .happiest-panel.open {
90
+ transform: translateY(0) !important;
91
+ }
92
+ }
93
+ `,M=i&&u?{width:"54px",height:"54px",borderRadius:"0",backgroundColor:"transparent",boxShadow:"none",filter:"drop-shadow(0 4px 8px rgba(0,0,0,0.2))"}:{width:"60px",height:"60px",borderRadius:"50%",backgroundColor:l,boxShadow:"0 4px 12px rgba(0,0,0,0.15)"};return jsxs("div",{className:d("happiest-chat-container",a),children:[jsx("style",{dangerouslySetInnerHTML:{__html:S}}),jsx("button",{onClick:()=>b(!m),className:d("happiest-fab flex items-center justify-center cursor-pointer outline-none border-none",m&&"open"),style:M,children:i?u?jsx("img",{src:i,alt:"Chat",style:{width:"100%",height:"100%",objectFit:"contain"}}):jsx("img",{src:i,alt:"Chat",style:{width:"32px",height:"32px",borderRadius:"50%",objectFit:"cover"}}):jsx(MessageSquare,{className:"w-7 h-7 text-white"})}),jsxs("div",{className:d("happiest-panel",m&&"open"),children:[jsxs("div",{className:"p-5 text-white flex justify-between items-center",style:{backgroundColor:l},children:[jsxs("div",{className:"flex items-center gap-3",children:[i&&!u?jsx("img",{src:i,alt:"Agent",className:"w-8 h-8 rounded-full bg-white object-cover"}):i?jsx("img",{src:i,alt:"Agent",className:"w-8 h-8 object-contain"}):jsx("div",{className:"w-8 h-8 rounded-full bg-white/20 flex items-center justify-center",children:jsx(MessageSquare,{className:"w-4 h-4 text-white"})}),jsxs("div",{children:[jsx("h3",{className:"font-semibold text-[16px] m-0 leading-tight",children:N}),jsxs("p",{className:"text-[13px] opacity-90 m-0 mt-1 leading-tight",children:["Powered by Happiest for ",H]})]})]}),jsx("button",{onClick:()=>b(false),className:"text-white/80 hover:text-white bg-transparent border-none cursor-pointer p-1",children:jsx(X,{className:"w-5 h-5"})})]}),jsxs("div",{className:"flex-1 overflow-y-auto p-5 flex flex-col gap-3 bg-[#f9fafb]",children:[jsx("div",{className:"flex justify-start",children:jsx("div",{className:"max-w-[85%] rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm",children:k})}),p.map(t=>jsx("div",{className:d("flex",t.isUser?"justify-end":"justify-start"),children:jsxs("div",{className:d("flex flex-col",t.isUser?"items-end":"items-start","max-w-[85%]"),children:[jsx("div",{className:d("rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words",t.isUser?"text-white rounded-br-sm":"bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm",t.status==="failed"&&"opacity-90"),style:t.isUser?{backgroundColor:t.status==="failed"?"#ef4444":l}:void 0,children:t.text}),t.status==="failed"&&jsxs("button",{onClick:()=>w(t.id),className:"flex items-center gap-1 mt-1.5 text-[11px] font-medium text-red-500 bg-transparent border-none cursor-pointer hover:underline p-0 outline-none",children:[jsx(AlertCircle,{className:"w-3.5 h-3.5"}),"Failed to send. Click to retry"]})]})},t.id)),o&&jsxs("div",{className:"happiest-typing-indicator shadow-sm",children:[jsx("div",{className:"happiest-dot"}),jsx("div",{className:"happiest-dot"}),jsx("div",{className:"happiest-dot"})]}),jsx("div",{ref:g})]}),jsxs("form",{onSubmit:t=>{t.preventDefault(),r(c),x("");},className:"p-4 bg-white border-t border-gray-100 flex gap-3",children:[jsx("input",{type:"text",value:c,onChange:t=>x(t.target.value),placeholder:"Type your message...",className:"flex-1 border border-gray-200 rounded-[24px] px-4 py-2.5 text-[14px] outline-none font-sans transition-colors",onFocus:t=>t.target.style.borderColor=l,onBlur:t=>t.target.style.borderColor="#e5e7eb"}),jsx("button",{type:"submit",disabled:!c.trim(),className:"text-white border-none rounded-full w-10 h-10 flex items-center justify-center cursor-pointer transition-colors disabled:bg-[#d1d5db] disabled:cursor-not-allowed",style:{backgroundColor:c.trim()?l:"#d1d5db"},children:jsx(Send,{className:"w-4 h-4 ml-0.5"})})]})]})]})}export{O as ChatPanel,h as HappiestContext,U as HappiestProvider,f as useHappiestChat};//# sourceMappingURL=index.mjs.map
94
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/HappiestProvider.tsx","../src/hooks/useHappiestChat.ts","../src/components/ChatPanel.tsx"],"names":["HappiestContext","createContext","HappiestProvider","config","children","clientRef","useRef","HappiestClient","jsx","useHappiestChat","client","useContext","state","setState","useState","useEffect","unsubscribe","text","id","cn","inputs","twMerge","clsx","ChatPanel","className","messages","isTyping","sendMessage","retryMessage","widgetConfig","input","setInput","isOpen","setIsOpen","endRef","safeConfig","primaryColor","iconUrl","iconNoBackground","agentName","clubName","welcomeMessage","customCss","fabStyle","jsxs","MessageSquare","X","msg","AlertCircle","e","Send"],"mappings":"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","file":"index.mjs","sourcesContent":["import React, { createContext, useRef } from 'react';\nimport { HappiestClient, HappiestConfig } from '@happiest-team/ai-chat-core';\n\nexport const HappiestContext = createContext<HappiestClient | null>(null);\n\nexport interface HappiestProviderProps {\n config: HappiestConfig;\n children: React.ReactNode;\n}\n\nexport function HappiestProvider({ config, children }: HappiestProviderProps) {\n // Use a ref to ensure the client is only instantiated once per provider\n const clientRef = useRef<HappiestClient | null>(null);\n\n if (!clientRef.current) {\n clientRef.current = new HappiestClient(config);\n }\n\n return (\n <HappiestContext.Provider value={clientRef.current}>\n {children}\n </HappiestContext.Provider>\n );\n}\n","import { useContext, useEffect, useState } from 'react';\nimport { HappiestState } from '@happiest-team/ai-chat-core';\nimport { HappiestContext } from '../components/HappiestProvider';\n\nexport function useHappiestChat(): HappiestState & { sendMessage: (text: string) => void, retryMessage: (id: string) => void, clearSession: () => void } {\n const client = useContext(HappiestContext);\n \n if (!client) {\n throw new Error('useHappiestChat must be used within a HappiestProvider');\n }\n\n const [state, setState] = useState<HappiestState>(client.getState());\n\n useEffect(() => {\n const unsubscribe = client.subscribe(setState);\n client.initSession(); // Ensures session is initialized once mounted\n return unsubscribe;\n }, [client]);\n\n return {\n ...state,\n sendMessage: (text: string) => client.sendMessage(text),\n retryMessage: (id: string) => client.retryMessage(id),\n clearSession: () => client.clearSession(),\n };\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport { Send, X, MessageSquare, AlertCircle } from 'lucide-react';\nimport { useHappiestChat } from '../hooks/useHappiestChat';\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nfunction cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport function ChatPanel({ className }: { className?: string }) {\n const { messages, isTyping, sendMessage, retryMessage, widgetConfig } = useHappiestChat();\n const [input, setInput] = useState('');\n const [isOpen, setIsOpen] = useState(false);\n const endRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n endRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages, isTyping, isOpen]);\n\n // Safely extract widget configs with fallbacks\n const safeConfig = widgetConfig || {};\n const primaryColor = safeConfig.theme?.primary || safeConfig.primaryColor || '#0A65CC';\n const iconUrl = safeConfig.iconUrl;\n const iconNoBackground = safeConfig.iconNoBackground;\n const agentName = safeConfig.agentName || 'Chat Assistant';\n const clubName = safeConfig.clubName || 'Club';\n const welcomeMessage = safeConfig.welcomeMessage || 'Hi there! How can I help you today?';\n\n const customCss = `\n .happiest-chat-container {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n }\n \n .happiest-fab {\n position: fixed;\n bottom: 24px;\n right: 24px;\n transition: transform 0.2s, background-color 0.2s, filter 0.2s;\n z-index: 2147483647;\n }\n .happiest-fab:hover {\n transform: scale(1.05);\n }\n \n .happiest-panel {\n position: fixed;\n bottom: 100px;\n right: 24px;\n width: 360px;\n height: 600px;\n max-height: calc(100vh - 120px);\n max-width: calc(100vw - 48px);\n background: #ffffff;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.15);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n transform: translateY(20px);\n transition: opacity 0.3s, transform 0.3s;\n border: 1px solid #e5e7eb;\n z-index: 2147483647;\n }\n \n .happiest-panel.open {\n opacity: 1;\n pointer-events: auto;\n transform: translateY(0);\n }\n\n .happiest-typing-indicator {\n display: flex;\n align-self: flex-start;\n background: white;\n border: 1px solid #e5e7eb;\n padding: 12px 16px;\n border-radius: 12px;\n border-bottom-left-radius: 4px;\n gap: 4px;\n }\n .happiest-dot {\n width: 6px;\n height: 6px;\n background: #666666;\n border-radius: 50%;\n animation: happiest-bounce 1.4s infinite ease-in-out both;\n }\n .happiest-dot:nth-child(1) { animation-delay: -0.32s; }\n .happiest-dot:nth-child(2) { animation-delay: -0.16s; }\n \n @keyframes happiest-bounce {\n 0%, 80%, 100% { transform: scale(0); }\n 40% { transform: scale(1); }\n }\n\n @media (max-width: 480px) {\n .happiest-fab {\n bottom: 16px;\n right: 16px;\n }\n .happiest-fab.open {\n display: none !important;\n }\n .happiest-panel {\n bottom: 0 !important;\n left: 0 !important;\n right: auto !important;\n width: 100vw !important;\n max-width: 100vw !important;\n height: 85vh !important;\n max-height: 85vh !important;\n border-radius: 24px 24px 0 0 !important;\n transform: translateY(100%);\n }\n .happiest-panel.open {\n transform: translateY(0) !important;\n }\n }\n `;\n\n const fabStyle = (iconUrl && iconNoBackground) ? {\n width: '54px',\n height: '54px',\n borderRadius: '0',\n backgroundColor: 'transparent',\n boxShadow: 'none',\n filter: 'drop-shadow(0 4px 8px rgba(0,0,0,0.2))'\n } : {\n width: '60px',\n height: '60px',\n borderRadius: '50%',\n backgroundColor: primaryColor,\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)'\n };\n\n return (\n <div className={cn(\"happiest-chat-container\", className)}>\n <style dangerouslySetInnerHTML={{ __html: customCss }} />\n\n {/* FAB */}\n <button\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\"happiest-fab flex items-center justify-center cursor-pointer outline-none border-none\", isOpen && \"open\")}\n style={fabStyle}\n >\n {iconUrl ? (\n iconNoBackground ? (\n <img src={iconUrl} alt=\"Chat\" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />\n ) : (\n <img src={iconUrl} alt=\"Chat\" style={{ width: '32px', height: '32px', borderRadius: '50%', objectFit: 'cover' }} />\n )\n ) : (\n <MessageSquare className=\"w-7 h-7 text-white\" />\n )}\n </button>\n\n {/* Chat Panel */}\n <div className={cn(\"happiest-panel\", isOpen && \"open\")}>\n {/* Header */}\n <div\n className=\"p-5 text-white flex justify-between items-center\"\n style={{ backgroundColor: primaryColor }}\n >\n <div className=\"flex items-center gap-3\">\n {iconUrl && !iconNoBackground ? (\n <img src={iconUrl} alt=\"Agent\" className=\"w-8 h-8 rounded-full bg-white object-cover\" />\n ) : iconUrl ? (\n <img src={iconUrl} alt=\"Agent\" className=\"w-8 h-8 object-contain\" />\n ) : (\n <div className=\"w-8 h-8 rounded-full bg-white/20 flex items-center justify-center\">\n <MessageSquare className=\"w-4 h-4 text-white\" />\n </div>\n )}\n <div>\n <h3 className=\"font-semibold text-[16px] m-0 leading-tight\">{agentName}</h3>\n <p className=\"text-[13px] opacity-90 m-0 mt-1 leading-tight\">Powered by Happiest for {clubName}</p>\n </div>\n </div>\n <button onClick={() => setIsOpen(false)} className=\"text-white/80 hover:text-white bg-transparent border-none cursor-pointer p-1\">\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-5 flex flex-col gap-3 bg-[#f9fafb]\">\n {/* Default Welcome Message */}\n <div className=\"flex justify-start\">\n <div className=\"max-w-[85%] rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm\">\n {welcomeMessage}\n </div>\n </div>\n\n {messages.map((msg) => (\n <div key={msg.id} className={cn(\"flex\", msg.isUser ? \"justify-end\" : \"justify-start\")}>\n <div className={cn(\"flex flex-col\", msg.isUser ? \"items-end\" : \"items-start\", \"max-w-[85%]\")}>\n <div\n className={cn(\n \"rounded-xl px-4 py-3 text-[14px] leading-relaxed break-words\",\n msg.isUser ? \"text-white rounded-br-sm\" : \"bg-white text-gray-800 shadow-sm border border-gray-100 rounded-bl-sm\",\n msg.status === 'failed' && \"opacity-90\"\n )}\n style={msg.isUser ? { backgroundColor: msg.status === 'failed' ? '#ef4444' : primaryColor } : undefined}\n >\n {msg.text}\n </div>\n {msg.status === 'failed' && (\n <button\n onClick={() => retryMessage(msg.id)}\n className=\"flex items-center gap-1 mt-1.5 text-[11px] font-medium text-red-500 bg-transparent border-none cursor-pointer hover:underline p-0 outline-none\"\n >\n <AlertCircle className=\"w-3.5 h-3.5\" />\n Failed to send. Click to retry\n </button>\n )}\n </div>\n </div>\n ))}\n {isTyping && (\n <div className=\"happiest-typing-indicator shadow-sm\">\n <div className=\"happiest-dot\"></div>\n <div className=\"happiest-dot\"></div>\n <div className=\"happiest-dot\"></div>\n </div>\n )}\n <div ref={endRef} />\n </div>\n\n {/* Input */}\n <form\n onSubmit={(e) => {\n e.preventDefault();\n sendMessage(input);\n setInput('');\n }}\n className=\"p-4 bg-white border-t border-gray-100 flex gap-3\"\n >\n <input\n type=\"text\"\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder=\"Type your message...\"\n className=\"flex-1 border border-gray-200 rounded-[24px] px-4 py-2.5 text-[14px] outline-none font-sans transition-colors\"\n onFocus={(e) => e.target.style.borderColor = primaryColor}\n onBlur={(e) => e.target.style.borderColor = '#e5e7eb'}\n />\n <button\n type=\"submit\"\n disabled={!input.trim()}\n className=\"text-white border-none rounded-full w-10 h-10 flex items-center justify-center cursor-pointer transition-colors disabled:bg-[#d1d5db] disabled:cursor-not-allowed\"\n style={{ backgroundColor: input.trim() ? primaryColor : '#d1d5db' }}\n >\n <Send className=\"w-4 h-4 ml-0.5\" />\n </button>\n </form>\n </div>\n </div>\n );\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@happiest-team/ai-chat",
3
+ "version": "1.0.1",
4
+ "description": "React Web SDK for Happiest Chat",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch"
18
+ },
19
+ "keywords": [
20
+ "happiest",
21
+ "chat",
22
+ "ai",
23
+ "agent",
24
+ "react",
25
+ "hooks"
26
+ ],
27
+ "author": "Happiest",
28
+ "license": "MIT",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "package.json"
36
+ ],
37
+ "dependencies": {
38
+ "@happiest-team/ai-chat-core": "*",
39
+ "clsx": "^2.1.0",
40
+ "lucide-react": "^1.21.0",
41
+ "tailwind-merge": "^3.6.0"
42
+ },
43
+ "peerDependencies": {
44
+ "react": ">=18.0.0",
45
+ "react-dom": ">=18.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/react": "^19.0.0",
49
+ "@types/react-dom": "^19.0.0",
50
+ "react": "^19.2.7",
51
+ "react-dom": "^19.2.7",
52
+ "tsup": "^8.0.0",
53
+ "typescript": "^5.5.0"
54
+ }
55
+ }