thikanaa 0.1.1 → 0.1.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/README.md CHANGED
@@ -29,15 +29,9 @@ Built with modern web technologies, Thikana offers a seamless, conversational in
29
29
 
30
30
  ### Installation
31
31
 
32
- 1. **Clone the repository**:
33
- ```bash
34
- git clone [repository-url]
35
- cd thikana
36
- ```
37
-
38
32
  2. **Install dependencies**:
39
33
  ```bash
40
- yarn install
34
+ yarn add thikanaa
41
35
  ```
42
36
 
43
37
  3. **Set up Environment Variables**:
@@ -62,7 +56,7 @@ You can now use Thikana as an npm package in your own React/Next.js projects.
62
56
  ### Installation
63
57
 
64
58
  ```bash
65
- npm install thikanaa
59
+ yarn add thikanaa
66
60
  ```
67
61
 
68
62
  ### Usage
@@ -78,6 +72,19 @@ const App = () => {
78
72
  systemPrompt="Your custom system prompt here"
79
73
  assistantName="Thikana"
80
74
  apiUrl="/api/chatbot" // Your backend endpoint
75
+ initialMessage="Hi this is Thikana! How can I help you today?"
76
+ messages={[
77
+ {
78
+ role: 'user',
79
+ content: 'All you list of messages',
80
+ },
81
+ ]}
82
+ onMessageSend={(message) => {
83
+ console.log(message);
84
+ }}
85
+ onMessageReceive={(message) => {
86
+ console.log(message);
87
+ }}
81
88
  />
82
89
  );
83
90
  };
@@ -92,6 +99,9 @@ const App = () => {
92
99
  | `apiUrl` | `string` | Endpoint to call for AI responses (default: `/chatbot`) |
93
100
  | `assistantName` | `string` | Name shown in the chat (default: `Thikana`) |
94
101
  | `model` | `string` | Specific model ID to use |
102
+ | `messages` | `Message[](Optional)` | List of messages to display in the chat |
103
+ | `onMessageSend` | `function(Optional)` | Callback function to handle message sending |
104
+ | `onMessageReceive` | `function(Optional)` | Callback function to handle message receiving |
95
105
 
96
106
  ---
97
107
 
package/dist/index.d.mts CHANGED
@@ -14,7 +14,9 @@ interface ChatPopupProps {
14
14
  messages?: Message[];
15
15
  onMessageSend?: (message: Message) => void;
16
16
  onResponseReceive?: (response: string) => void;
17
+ mockMode?: boolean;
18
+ position?: "fixed" | "relative";
17
19
  }
18
- declare const ChatPopup: ({ systemPrompt, portfolioData: externalPortfolioData, apiUrl, assistantName, initialMessage, model, messages: externalMessages, onMessageSend, onResponseReceive, }: ChatPopupProps) => react_jsx_runtime.JSX.Element;
20
+ declare const ChatPopup: ({ systemPrompt, portfolioData: externalPortfolioData, apiUrl, assistantName, initialMessage, model, messages: externalMessages, onMessageSend, onResponseReceive, mockMode, position, }: ChatPopupProps) => react_jsx_runtime.JSX.Element;
19
21
 
20
22
  export { ChatPopup, type ChatPopupProps };
package/dist/index.d.ts CHANGED
@@ -14,7 +14,9 @@ interface ChatPopupProps {
14
14
  messages?: Message[];
15
15
  onMessageSend?: (message: Message) => void;
16
16
  onResponseReceive?: (response: string) => void;
17
+ mockMode?: boolean;
18
+ position?: "fixed" | "relative";
17
19
  }
18
- declare const ChatPopup: ({ systemPrompt, portfolioData: externalPortfolioData, apiUrl, assistantName, initialMessage, model, messages: externalMessages, onMessageSend, onResponseReceive, }: ChatPopupProps) => react_jsx_runtime.JSX.Element;
20
+ declare const ChatPopup: ({ systemPrompt, portfolioData: externalPortfolioData, apiUrl, assistantName, initialMessage, model, messages: externalMessages, onMessageSend, onResponseReceive, mockMode, position, }: ChatPopupProps) => react_jsx_runtime.JSX.Element;
19
21
 
20
22
  export { ChatPopup, type ChatPopupProps };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var M=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var F=(a,n)=>{for(var g in n)M(a,g,{get:n[g],enumerable:!0})},K=(a,n,g,u)=>{if(n&&typeof n=="object"||typeof n=="function")for(let c of B(n))!W.call(a,c)&&c!==g&&M(a,c,{get:()=>n[c],enumerable:!(u=J(n,c))||u.enumerable});return a};var U=a=>K(M({},"__esModule",{value:!0}),a);var X={};F(X,{ChatPopup:()=>V});module.exports=U(X);var t=require("react");var s=require("react/jsx-runtime"),V=({systemPrompt:a,portfolioData:n,apiUrl:g="/chatbot",assistantName:u="Thikana",initialMessage:c,model:C,messages:l,onMessageSend:D,onResponseReceive:E})=>{let[h,I]=(0,t.useState)(""),[x,f]=(0,t.useState)([]),[b,P]=(0,t.useState)(!1),[T,j]=(0,t.useState)(null),[L,N]=(0,t.useState)({width:330,height:480}),[w,z]=(0,t.useState)(!1),y=(0,t.useRef)(null),m=(0,t.useRef)(null),R=(0,t.useRef)(null),v=l||x,H=n||T;(0,t.useEffect)(()=>{if((!l||l.length===0)&&x.length===0){let e=localStorage.getItem("messages");f(e?JSON.parse(e):[{role:"assistant",content:c||`Hi this is ${u}! How can I help you today?`}])}},[l,c,u,x.length]),(0,t.useEffect)(()=>{n||(async()=>{try{let r=await(await fetch("https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json")).json();j(r)}catch(o){console.error("Error fetching portfolio data:",o)}})()},[n]);let $=()=>{R.current?.scrollIntoView({behavior:"smooth"})};(0,t.useEffect)(()=>{$()},[v]);let O=async e=>{let o=await fetch(g,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,portfolioData:H,systemPrompt:a,model:C})});if(!o.ok){let d=await o.json();throw new Error(d.error||"Failed to get AI response")}let r=await o.json();return E&&E(r.text),r.text},S=async e=>{if(!e.trim()||b)return;let o={role:"user",content:e};D&&D(o);let r=[...v,o];l||f(r),I(""),P(!0);try{let p={role:"assistant",content:await O(r)},i=[...r,p];l||(f(i),localStorage.setItem("messages",JSON.stringify(i)))}catch(d){console.error("Error calling AI:",d);let p={role:"assistant",content:"Sorry, I encountered an error. Please try again later."},i=[...r,p];l||(f(i),localStorage.setItem("messages",JSON.stringify(i)))}finally{P(!1)}};(0,t.useEffect)(()=>{m.current&&(m.current.style.height="auto",m.current.style.height=`${Math.min(m.current.scrollHeight,100)}px`)},[h]);let A=(0,t.useCallback)(e=>{e.preventDefault(),z(!0)},[]);return(0,t.useEffect)(()=>{if(!w)return;let e=r=>{if(!y.current)return;let d=y.current.getBoundingClientRect(),p=d.right-r.clientX,i=d.bottom-r.clientY;p>250&&p<800&&N(k=>({...k,width:p})),i>300&&i<800&&N(k=>({...k,height:i}))},o=()=>{z(!1)};return window.addEventListener("mousemove",e),window.addEventListener("mouseup",o),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",o)}},[w]),(0,s.jsxs)("div",{ref:y,className:`chat-popup-wrapper ${w?"resizing":""}`,style:{width:`${L.width}px`,height:`${L.height}px`},children:[(0,s.jsx)("div",{className:"stretch-icon",onMouseDown:A}),(0,s.jsxs)("div",{className:"messages-container",children:[v.map((e,o)=>(0,s.jsx)("div",{className:`message ${e.role==="user"?"user":"ai"}`,children:e.content},o)),b&&(0,s.jsx)("div",{className:"message ai loading",children:"Thinking..."}),(0,s.jsx)("div",{ref:R})]}),(0,s.jsxs)("div",{className:"input-container",children:[(0,s.jsx)("textarea",{ref:m,rows:1,placeholder:"What's on your mind?",value:h,onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),S(h))},onChange:e=>I(e.target.value)}),(0,s.jsx)("button",{onClick:()=>S(h),disabled:!h.trim()||b,className:"send-button",children:(0,s.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,s.jsx)("line",{x1:"22",y1:"2",x2:"11",y2:"13"}),(0,s.jsx)("polygon",{points:"22 2 15 22 11 13 2 9 22 2"})]})})]})]})};0&&(module.exports={ChatPopup});
1
+ "use strict";var I=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var K=(l,n)=>{for(var g in n)I(l,g,{get:n[g],enumerable:!0})},U=(l,n,g,u)=>{if(n&&typeof n=="object"||typeof n=="function")for(let d of q(n))!F.call(l,d)&&d!==g&&I(l,d,{get:()=>n[d],enumerable:!(u=W(n,d))||u.enumerable});return l};var G=l=>U(I({},"__esModule",{value:!0}),l);var X={};K(X,{ChatPopup:()=>V});module.exports=G(X);var t=require("react");var s=require("react/jsx-runtime"),V=({systemPrompt:l,portfolioData:n,apiUrl:g="/chatbot",assistantName:u="Thikana",initialMessage:d,model:C,messages:p,onMessageSend:D,onResponseReceive:x,mockMode:H=!1,position:j="relative"})=>{let[h,E]=(0,t.useState)(""),[b,f]=(0,t.useState)([]),[w,L]=(0,t.useState)(!1),[R,$]=(0,t.useState)(null),[P,N]=(0,t.useState)({width:330,height:480}),[y,T]=(0,t.useState)(!1),v=(0,t.useRef)(null),m=(0,t.useRef)(null),z=(0,t.useRef)(null),k=p||b,O=n||R;(0,t.useEffect)(()=>{if((!p||p.length===0)&&b.length===0){let e=localStorage.getItem("messages");f(e?JSON.parse(e):[{role:"assistant",content:d||`Hi this is ${u}! How can I help you today?`}])}},[p,d,u,b.length]),(0,t.useEffect)(()=>{n||(async()=>{try{let r=await(await fetch("https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json")).json();$(r)}catch(o){console.error("Error fetching portfolio data:",o)}})()},[n]);let A=()=>{z.current?.scrollIntoView({behavior:"smooth"})};(0,t.useEffect)(()=>{A()},[k]);let B=async e=>{if(H){await new Promise(a=>setTimeout(a,1e3));let i=["That's an interesting question! Let me help you with that.","I understand what you're asking. Here's what I think...","Great question! Based on what you've told me, I'd suggest...","Let me break that down for you.","I'm here to help! Here's my take on that..."],c=i[Math.floor(Math.random()*i.length)];return x&&x(c),c}let o=await fetch(g,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,portfolioData:O,systemPrompt:l,model:C})});if(!o.ok){let i=await o.json();throw new Error(i.error||"Failed to get AI response")}let r=await o.json();return x&&x(r.text),r.text},S=async e=>{if(!e.trim()||w)return;let o={role:"user",content:e};D&&D(o);let r=[...k,o];p||f(r),E(""),L(!0);try{let c={role:"assistant",content:await B(r)},a=[...r,c];p||(f(a),localStorage.setItem("messages",JSON.stringify(a)))}catch(i){console.error("Error calling AI:",i);let c={role:"assistant",content:"Sorry, I encountered an error. Please try again later."},a=[...r,c];p||(f(a),localStorage.setItem("messages",JSON.stringify(a)))}finally{L(!1)}};(0,t.useEffect)(()=>{m.current&&(m.current.style.height="auto",m.current.style.height=`${Math.min(m.current.scrollHeight,100)}px`)},[h]);let J=(0,t.useCallback)(e=>{e.preventDefault(),T(!0)},[]);return(0,t.useEffect)(()=>{if(!y)return;let e=r=>{if(!v.current)return;let i=v.current.getBoundingClientRect(),c=i.right-r.clientX,a=i.bottom-r.clientY;c>250&&c<800&&N(M=>({...M,width:c})),a>300&&a<800&&N(M=>({...M,height:a}))},o=()=>{T(!1)};return window.addEventListener("mousemove",e),window.addEventListener("mouseup",o),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",o)}},[y]),(0,s.jsxs)("div",{ref:v,className:`chat-popup-wrapper ${y?"resizing":""}`,style:{width:`${P.width}px`,height:`${P.height}px`,position:j},children:[(0,s.jsx)("div",{className:"stretch-icon",onMouseDown:J}),(0,s.jsxs)("div",{className:"messages-container",children:[k.map((e,o)=>(0,s.jsx)("div",{className:`message ${e.role==="user"?"user":"ai"}`,children:e.content},o)),w&&(0,s.jsx)("div",{className:"message ai loading",children:"Thinking..."}),(0,s.jsx)("div",{ref:z})]}),(0,s.jsxs)("div",{className:"input-container",children:[(0,s.jsx)("textarea",{ref:m,rows:1,placeholder:"What's on your mind?",value:h,onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),S(h))},onChange:e=>E(e.target.value)}),(0,s.jsx)("button",{onClick:()=>S(h),disabled:!h.trim()||w,className:"send-button",children:(0,s.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,s.jsx)("line",{x1:"22",y1:"2",x2:"11",y2:"13"}),(0,s.jsx)("polygon",{points:"22 2 15 22 11 13 2 9 22 2"})]})})]})]})};0&&(module.exports={ChatPopup});
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/chat-popup.tsx"],"sourcesContent":["export * from \"./components/chat-popup\";\n","import React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport \"./chat-popup.css\";\n\ntype Message = {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n};\n\nexport interface ChatPopupProps {\n systemPrompt?: string;\n portfolioData?: any;\n apiUrl?: string;\n assistantName?: string;\n initialMessage?: string;\n model?: string;\n messages?: Message[];\n onMessageSend?: (message: Message) => void;\n onResponseReceive?: (response: string) => void;\n}\n\nexport const ChatPopup = ({\n systemPrompt,\n portfolioData: externalPortfolioData,\n apiUrl = \"/chatbot\",\n assistantName = \"Thikana\",\n initialMessage,\n model,\n messages: externalMessages,\n onMessageSend,\n onResponseReceive,\n}: ChatPopupProps) => {\n const [input, setInput] = useState<string>(\"\");\n const [internalMessages, setInternalMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [internalPortfolioData, setInternalPortfolioData] = useState<any>(null);\n const [dimensions, setDimensions] = useState({ width: 330, height: 480 });\n const [isResizing, setIsResizing] = useState(false);\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const activeMessages = externalMessages || internalMessages;\n const activePortfolioData = externalPortfolioData || internalPortfolioData;\n\n useEffect(() => {\n if (!externalMessages || externalMessages.length === 0) {\n if (internalMessages.length === 0) {\n const saved = localStorage.getItem(\"messages\");\n if (saved) {\n setInternalMessages(JSON.parse(saved));\n } else {\n setInternalMessages([\n {\n role: \"assistant\",\n content:\n initialMessage ||\n `Hi this is ${assistantName}! How can I help you today?`,\n },\n ]);\n }\n }\n }\n }, [\n externalMessages,\n initialMessage,\n assistantName,\n internalMessages.length,\n ]);\n\n useEffect(() => {\n if (!externalPortfolioData) {\n const fetchPortfolioData = async () => {\n try {\n const response = await fetch(\n \"https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json\",\n );\n const data = await response.json();\n setInternalPortfolioData(data);\n } catch (error) {\n console.error(\"Error fetching portfolio data:\", error);\n }\n };\n fetchPortfolioData();\n }\n }, [externalPortfolioData]);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [activeMessages]);\n\n const getAIResponse = async (currentMessages: Message[]): Promise<string> => {\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messages: currentMessages,\n portfolioData: activePortfolioData,\n systemPrompt,\n model,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error || \"Failed to get AI response\");\n }\n\n const data = await response.json();\n if (onResponseReceive) onResponseReceive(data.text);\n return data.text;\n };\n\n const callModel = async (userInput: string) => {\n if (!userInput.trim() || isLoading) return;\n\n const userMessage: Message = { role: \"user\", content: userInput };\n if (onMessageSend) onMessageSend(userMessage);\n\n const newMessages = [...activeMessages, userMessage];\n\n if (!externalMessages) {\n setInternalMessages(newMessages);\n }\n\n setInput(\"\");\n setIsLoading(true);\n\n try {\n const text = await getAIResponse(newMessages);\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: text,\n };\n\n const updatedMessages = [...newMessages, assistantMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } catch (error) {\n console.error(\"Error calling AI:\", error);\n const defaultFailMessage: Message = {\n role: \"assistant\",\n content: \"Sorry, I encountered an error. Please try again later.\",\n };\n const updatedMessages = [...newMessages, defaultFailMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } finally {\n setIsLoading(false);\n }\n };\n\n useEffect(() => {\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 100)}px`;\n }\n }, [input]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setIsResizing(true);\n }, []);\n\n useEffect(() => {\n if (!isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!wrapperRef.current) return;\n\n const rect = wrapperRef.current.getBoundingClientRect();\n const newWidth = rect.right - e.clientX;\n const newHeight = rect.bottom - e.clientY;\n\n if (newWidth > 250 && newWidth < 800) {\n setDimensions((prev) => ({ ...prev, width: newWidth }));\n }\n if (newHeight > 300 && newHeight < 800) {\n setDimensions((prev) => ({ ...prev, height: newHeight }));\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n };\n }, [isResizing]);\n\n return (\n <div\n ref={wrapperRef}\n className={`chat-popup-wrapper ${isResizing ? \"resizing\" : \"\"}`}\n style={{\n width: `${dimensions.width}px`,\n height: `${dimensions.height}px`,\n }}\n >\n <div className=\"stretch-icon\" onMouseDown={handleMouseDown}></div>\n <div className=\"messages-container\">\n {activeMessages.map((msg: Message, index: number) => (\n <div\n key={index}\n className={`message ${msg.role === \"user\" ? \"user\" : \"ai\"}`}\n >\n {msg.content}\n </div>\n ))}\n {isLoading && <div className=\"message ai loading\">Thinking...</div>}\n <div ref={messagesEndRef} />\n </div>\n\n <div className=\"input-container\">\n <textarea\n ref={textareaRef}\n rows={1}\n placeholder=\"What's on your mind?\"\n value={input}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n callModel(input);\n }\n }}\n onChange={(e) => setInput(e.target.value)}\n />\n <button\n onClick={() => callModel(input)}\n disabled={!input.trim() || isLoading}\n className=\"send-button\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon>\n </svg>\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":"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","names":["index_exports","__export","ChatPopup","__toCommonJS","import_react","import_jsx_runtime","ChatPopup","systemPrompt","externalPortfolioData","apiUrl","assistantName","initialMessage","model","externalMessages","onMessageSend","onResponseReceive","input","setInput","internalMessages","setInternalMessages","isLoading","setIsLoading","internalPortfolioData","setInternalPortfolioData","dimensions","setDimensions","isResizing","setIsResizing","wrapperRef","textareaRef","messagesEndRef","activeMessages","activePortfolioData","saved","data","error","scrollToBottom","getAIResponse","currentMessages","response","errorData","callModel","userInput","userMessage","newMessages","assistantMessage","updatedMessages","defaultFailMessage","handleMouseDown","handleMouseMove","e","rect","newWidth","newHeight","prev","handleMouseUp","msg","index"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/chat-popup.tsx"],"sourcesContent":["export * from \"./components/chat-popup\";\n","import React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport \"./chat-popup.css\";\n\ntype Message = {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n};\n\nexport interface ChatPopupProps {\n systemPrompt?: string;\n portfolioData?: any;\n apiUrl?: string;\n assistantName?: string;\n initialMessage?: string;\n model?: string;\n messages?: Message[];\n onMessageSend?: (message: Message) => void;\n onResponseReceive?: (response: string) => void;\n mockMode?: boolean; // If true, uses mock responses instead of API calls\n position?: \"fixed\" | \"relative\"; // Position style for the wrapper\n}\n\nexport const ChatPopup = ({\n systemPrompt,\n portfolioData: externalPortfolioData,\n apiUrl = \"/chatbot\",\n assistantName = \"Thikana\",\n initialMessage,\n model,\n messages: externalMessages,\n onMessageSend,\n onResponseReceive,\n mockMode = false,\n position = \"relative\",\n}: ChatPopupProps) => {\n const [input, setInput] = useState<string>(\"\");\n const [internalMessages, setInternalMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [internalPortfolioData, setInternalPortfolioData] = useState<any>(null);\n const [dimensions, setDimensions] = useState({ width: 330, height: 480 });\n const [isResizing, setIsResizing] = useState(false);\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const activeMessages = externalMessages || internalMessages;\n const activePortfolioData = externalPortfolioData || internalPortfolioData;\n\n useEffect(() => {\n if (!externalMessages || externalMessages.length === 0) {\n if (internalMessages.length === 0) {\n const saved = localStorage.getItem(\"messages\");\n if (saved) {\n setInternalMessages(JSON.parse(saved));\n } else {\n setInternalMessages([\n {\n role: \"assistant\",\n content:\n initialMessage ||\n `Hi this is ${assistantName}! How can I help you today?`,\n },\n ]);\n }\n }\n }\n }, [\n externalMessages,\n initialMessage,\n assistantName,\n internalMessages.length,\n ]);\n\n useEffect(() => {\n if (!externalPortfolioData) {\n const fetchPortfolioData = async () => {\n try {\n const response = await fetch(\n \"https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json\",\n );\n const data = await response.json();\n setInternalPortfolioData(data);\n } catch (error) {\n console.error(\"Error fetching portfolio data:\", error);\n }\n };\n fetchPortfolioData();\n }\n }, [externalPortfolioData]);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [activeMessages]);\n\n const getAIResponse = async (currentMessages: Message[]): Promise<string> => {\n // Mock mode for testing without API\n if (mockMode) {\n await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API delay\n const mockResponses = [\n \"That's an interesting question! Let me help you with that.\",\n \"I understand what you're asking. Here's what I think...\",\n \"Great question! Based on what you've told me, I'd suggest...\",\n \"Let me break that down for you.\",\n \"I'm here to help! Here's my take on that...\",\n ];\n const response =\n mockResponses[Math.floor(Math.random() * mockResponses.length)];\n if (onResponseReceive) onResponseReceive(response);\n return response;\n }\n\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messages: currentMessages,\n portfolioData: activePortfolioData,\n systemPrompt,\n model,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error || \"Failed to get AI response\");\n }\n\n const data = await response.json();\n if (onResponseReceive) onResponseReceive(data.text);\n return data.text;\n };\n\n const callModel = async (userInput: string) => {\n if (!userInput.trim() || isLoading) return;\n\n const userMessage: Message = { role: \"user\", content: userInput };\n if (onMessageSend) onMessageSend(userMessage);\n\n const newMessages = [...activeMessages, userMessage];\n\n if (!externalMessages) {\n setInternalMessages(newMessages);\n }\n\n setInput(\"\");\n setIsLoading(true);\n\n try {\n const text = await getAIResponse(newMessages);\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: text,\n };\n\n const updatedMessages = [...newMessages, assistantMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } catch (error) {\n console.error(\"Error calling AI:\", error);\n const defaultFailMessage: Message = {\n role: \"assistant\",\n content: \"Sorry, I encountered an error. Please try again later.\",\n };\n const updatedMessages = [...newMessages, defaultFailMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } finally {\n setIsLoading(false);\n }\n };\n\n useEffect(() => {\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 100)}px`;\n }\n }, [input]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setIsResizing(true);\n }, []);\n\n useEffect(() => {\n if (!isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!wrapperRef.current) return;\n\n const rect = wrapperRef.current.getBoundingClientRect();\n const newWidth = rect.right - e.clientX;\n const newHeight = rect.bottom - e.clientY;\n\n if (newWidth > 250 && newWidth < 800) {\n setDimensions((prev) => ({ ...prev, width: newWidth }));\n }\n if (newHeight > 300 && newHeight < 800) {\n setDimensions((prev) => ({ ...prev, height: newHeight }));\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n };\n }, [isResizing]);\n\n return (\n <div\n ref={wrapperRef}\n className={`chat-popup-wrapper ${isResizing ? \"resizing\" : \"\"}`}\n style={{\n width: `${dimensions.width}px`,\n height: `${dimensions.height}px`,\n position: position,\n }}\n >\n <div className=\"stretch-icon\" onMouseDown={handleMouseDown}></div>\n <div className=\"messages-container\">\n {activeMessages.map((msg: Message, index: number) => (\n <div\n key={index}\n className={`message ${msg.role === \"user\" ? \"user\" : \"ai\"}`}\n >\n {msg.content}\n </div>\n ))}\n {isLoading && <div className=\"message ai loading\">Thinking...</div>}\n <div ref={messagesEndRef} />\n </div>\n\n <div className=\"input-container\">\n <textarea\n ref={textareaRef}\n rows={1}\n placeholder=\"What's on your mind?\"\n value={input}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n callModel(input);\n }\n }}\n onChange={(e) => setInput(e.target.value)}\n />\n <button\n onClick={() => callModel(input)}\n disabled={!input.trim() || isLoading}\n className=\"send-button\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon>\n </svg>\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":"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","names":["index_exports","__export","ChatPopup","__toCommonJS","import_react","import_jsx_runtime","ChatPopup","systemPrompt","externalPortfolioData","apiUrl","assistantName","initialMessage","model","externalMessages","onMessageSend","onResponseReceive","mockMode","position","input","setInput","internalMessages","setInternalMessages","isLoading","setIsLoading","internalPortfolioData","setInternalPortfolioData","dimensions","setDimensions","isResizing","setIsResizing","wrapperRef","textareaRef","messagesEndRef","activeMessages","activePortfolioData","saved","data","error","scrollToBottom","getAIResponse","currentMessages","resolve","mockResponses","response","errorData","callModel","userInput","userMessage","newMessages","assistantMessage","updatedMessages","defaultFailMessage","handleMouseDown","handleMouseMove","e","rect","newWidth","newHeight","prev","handleMouseUp","msg","index"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{useState as c,useRef as v,useEffect as g,useCallback as W}from"react";import{jsx as n,jsxs as u}from"react/jsx-runtime";var Y=({systemPrompt:C,portfolioData:h,apiUrl:T="/chatbot",assistantName:k="Thikana",initialMessage:M,model:j,messages:r,onMessageSend:D,onResponseReceive:E})=>{let[l,I]=c(""),[f,d]=c([]),[m,P]=c(!1),[H,$]=c(null),[L,N]=c({width:330,height:480}),[x,z]=c(!1),b=v(null),p=v(null),R=v(null),w=r||f,O=h||H;g(()=>{if((!r||r.length===0)&&f.length===0){let e=localStorage.getItem("messages");d(e?JSON.parse(e):[{role:"assistant",content:M||`Hi this is ${k}! How can I help you today?`}])}},[r,M,k,f.length]),g(()=>{h||(async()=>{try{let o=await(await fetch("https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json")).json();$(o)}catch(t){console.error("Error fetching portfolio data:",t)}})()},[h]);let A=()=>{R.current?.scrollIntoView({behavior:"smooth"})};g(()=>{A()},[w]);let J=async e=>{let t=await fetch(T,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,portfolioData:O,systemPrompt:C,model:j})});if(!t.ok){let a=await t.json();throw new Error(a.error||"Failed to get AI response")}let o=await t.json();return E&&E(o.text),o.text},S=async e=>{if(!e.trim()||m)return;let t={role:"user",content:e};D&&D(t);let o=[...w,t];r||d(o),I(""),P(!0);try{let i={role:"assistant",content:await J(o)},s=[...o,i];r||(d(s),localStorage.setItem("messages",JSON.stringify(s)))}catch(a){console.error("Error calling AI:",a);let i={role:"assistant",content:"Sorry, I encountered an error. Please try again later."},s=[...o,i];r||(d(s),localStorage.setItem("messages",JSON.stringify(s)))}finally{P(!1)}};g(()=>{p.current&&(p.current.style.height="auto",p.current.style.height=`${Math.min(p.current.scrollHeight,100)}px`)},[l]);let B=W(e=>{e.preventDefault(),z(!0)},[]);return g(()=>{if(!x)return;let e=o=>{if(!b.current)return;let a=b.current.getBoundingClientRect(),i=a.right-o.clientX,s=a.bottom-o.clientY;i>250&&i<800&&N(y=>({...y,width:i})),s>300&&s<800&&N(y=>({...y,height:s}))},t=()=>{z(!1)};return window.addEventListener("mousemove",e),window.addEventListener("mouseup",t),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",t)}},[x]),u("div",{ref:b,className:`chat-popup-wrapper ${x?"resizing":""}`,style:{width:`${L.width}px`,height:`${L.height}px`},children:[n("div",{className:"stretch-icon",onMouseDown:B}),u("div",{className:"messages-container",children:[w.map((e,t)=>n("div",{className:`message ${e.role==="user"?"user":"ai"}`,children:e.content},t)),m&&n("div",{className:"message ai loading",children:"Thinking..."}),n("div",{ref:R})]}),u("div",{className:"input-container",children:[n("textarea",{ref:p,rows:1,placeholder:"What's on your mind?",value:l,onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),S(l))},onChange:e=>I(e.target.value)}),n("button",{onClick:()=>S(l),disabled:!l.trim()||m,className:"send-button",children:u("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[n("line",{x1:"22",y1:"2",x2:"11",y2:"13"}),n("polygon",{points:"22 2 15 22 11 13 2 9 22 2"})]})})]})]})};export{Y as ChatPopup};
1
+ import{useState as c,useRef as k,useEffect as g,useCallback as F}from"react";import{jsx as a,jsxs as h}from"react/jsx-runtime";var Y=({systemPrompt:C,portfolioData:f,apiUrl:H="/chatbot",assistantName:M="Thikana",initialMessage:I,model:j,messages:i,onMessageSend:D,onResponseReceive:u,mockMode:R=!1,position:$="relative"})=>{let[l,E]=c(""),[m,d]=c([]),[x,L]=c(!1),[O,A]=c(null),[P,N]=c({width:330,height:480}),[b,T]=c(!1),w=k(null),p=k(null),z=k(null),y=i||m,B=f||O;g(()=>{if((!i||i.length===0)&&m.length===0){let e=localStorage.getItem("messages");d(e?JSON.parse(e):[{role:"assistant",content:I||`Hi this is ${M}! How can I help you today?`}])}},[i,I,M,m.length]),g(()=>{f||(async()=>{try{let o=await(await fetch("https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json")).json();A(o)}catch(t){console.error("Error fetching portfolio data:",t)}})()},[f]);let J=()=>{z.current?.scrollIntoView({behavior:"smooth"})};g(()=>{J()},[y]);let W=async e=>{if(R){await new Promise(s=>setTimeout(s,1e3));let n=["That's an interesting question! Let me help you with that.","I understand what you're asking. Here's what I think...","Great question! Based on what you've told me, I'd suggest...","Let me break that down for you.","I'm here to help! Here's my take on that..."],r=n[Math.floor(Math.random()*n.length)];return u&&u(r),r}let t=await fetch(H,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,portfolioData:B,systemPrompt:C,model:j})});if(!t.ok){let n=await t.json();throw new Error(n.error||"Failed to get AI response")}let o=await t.json();return u&&u(o.text),o.text},S=async e=>{if(!e.trim()||x)return;let t={role:"user",content:e};D&&D(t);let o=[...y,t];i||d(o),E(""),L(!0);try{let r={role:"assistant",content:await W(o)},s=[...o,r];i||(d(s),localStorage.setItem("messages",JSON.stringify(s)))}catch(n){console.error("Error calling AI:",n);let r={role:"assistant",content:"Sorry, I encountered an error. Please try again later."},s=[...o,r];i||(d(s),localStorage.setItem("messages",JSON.stringify(s)))}finally{L(!1)}};g(()=>{p.current&&(p.current.style.height="auto",p.current.style.height=`${Math.min(p.current.scrollHeight,100)}px`)},[l]);let q=F(e=>{e.preventDefault(),T(!0)},[]);return g(()=>{if(!b)return;let e=o=>{if(!w.current)return;let n=w.current.getBoundingClientRect(),r=n.right-o.clientX,s=n.bottom-o.clientY;r>250&&r<800&&N(v=>({...v,width:r})),s>300&&s<800&&N(v=>({...v,height:s}))},t=()=>{T(!1)};return window.addEventListener("mousemove",e),window.addEventListener("mouseup",t),()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",t)}},[b]),h("div",{ref:w,className:`chat-popup-wrapper ${b?"resizing":""}`,style:{width:`${P.width}px`,height:`${P.height}px`,position:$},children:[a("div",{className:"stretch-icon",onMouseDown:q}),h("div",{className:"messages-container",children:[y.map((e,t)=>a("div",{className:`message ${e.role==="user"?"user":"ai"}`,children:e.content},t)),x&&a("div",{className:"message ai loading",children:"Thinking..."}),a("div",{ref:z})]}),h("div",{className:"input-container",children:[a("textarea",{ref:p,rows:1,placeholder:"What's on your mind?",value:l,onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),S(l))},onChange:e=>E(e.target.value)}),a("button",{onClick:()=>S(l),disabled:!l.trim()||x,className:"send-button",children:h("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[a("line",{x1:"22",y1:"2",x2:"11",y2:"13"}),a("polygon",{points:"22 2 15 22 11 13 2 9 22 2"})]})})]})]})};export{Y as ChatPopup};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/chat-popup.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport \"./chat-popup.css\";\n\ntype Message = {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n};\n\nexport interface ChatPopupProps {\n systemPrompt?: string;\n portfolioData?: any;\n apiUrl?: string;\n assistantName?: string;\n initialMessage?: string;\n model?: string;\n messages?: Message[];\n onMessageSend?: (message: Message) => void;\n onResponseReceive?: (response: string) => void;\n}\n\nexport const ChatPopup = ({\n systemPrompt,\n portfolioData: externalPortfolioData,\n apiUrl = \"/chatbot\",\n assistantName = \"Thikana\",\n initialMessage,\n model,\n messages: externalMessages,\n onMessageSend,\n onResponseReceive,\n}: ChatPopupProps) => {\n const [input, setInput] = useState<string>(\"\");\n const [internalMessages, setInternalMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [internalPortfolioData, setInternalPortfolioData] = useState<any>(null);\n const [dimensions, setDimensions] = useState({ width: 330, height: 480 });\n const [isResizing, setIsResizing] = useState(false);\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const activeMessages = externalMessages || internalMessages;\n const activePortfolioData = externalPortfolioData || internalPortfolioData;\n\n useEffect(() => {\n if (!externalMessages || externalMessages.length === 0) {\n if (internalMessages.length === 0) {\n const saved = localStorage.getItem(\"messages\");\n if (saved) {\n setInternalMessages(JSON.parse(saved));\n } else {\n setInternalMessages([\n {\n role: \"assistant\",\n content:\n initialMessage ||\n `Hi this is ${assistantName}! How can I help you today?`,\n },\n ]);\n }\n }\n }\n }, [\n externalMessages,\n initialMessage,\n assistantName,\n internalMessages.length,\n ]);\n\n useEffect(() => {\n if (!externalPortfolioData) {\n const fetchPortfolioData = async () => {\n try {\n const response = await fetch(\n \"https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json\",\n );\n const data = await response.json();\n setInternalPortfolioData(data);\n } catch (error) {\n console.error(\"Error fetching portfolio data:\", error);\n }\n };\n fetchPortfolioData();\n }\n }, [externalPortfolioData]);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [activeMessages]);\n\n const getAIResponse = async (currentMessages: Message[]): Promise<string> => {\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messages: currentMessages,\n portfolioData: activePortfolioData,\n systemPrompt,\n model,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error || \"Failed to get AI response\");\n }\n\n const data = await response.json();\n if (onResponseReceive) onResponseReceive(data.text);\n return data.text;\n };\n\n const callModel = async (userInput: string) => {\n if (!userInput.trim() || isLoading) return;\n\n const userMessage: Message = { role: \"user\", content: userInput };\n if (onMessageSend) onMessageSend(userMessage);\n\n const newMessages = [...activeMessages, userMessage];\n\n if (!externalMessages) {\n setInternalMessages(newMessages);\n }\n\n setInput(\"\");\n setIsLoading(true);\n\n try {\n const text = await getAIResponse(newMessages);\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: text,\n };\n\n const updatedMessages = [...newMessages, assistantMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } catch (error) {\n console.error(\"Error calling AI:\", error);\n const defaultFailMessage: Message = {\n role: \"assistant\",\n content: \"Sorry, I encountered an error. Please try again later.\",\n };\n const updatedMessages = [...newMessages, defaultFailMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } finally {\n setIsLoading(false);\n }\n };\n\n useEffect(() => {\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 100)}px`;\n }\n }, [input]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setIsResizing(true);\n }, []);\n\n useEffect(() => {\n if (!isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!wrapperRef.current) return;\n\n const rect = wrapperRef.current.getBoundingClientRect();\n const newWidth = rect.right - e.clientX;\n const newHeight = rect.bottom - e.clientY;\n\n if (newWidth > 250 && newWidth < 800) {\n setDimensions((prev) => ({ ...prev, width: newWidth }));\n }\n if (newHeight > 300 && newHeight < 800) {\n setDimensions((prev) => ({ ...prev, height: newHeight }));\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n };\n }, [isResizing]);\n\n return (\n <div\n ref={wrapperRef}\n className={`chat-popup-wrapper ${isResizing ? \"resizing\" : \"\"}`}\n style={{\n width: `${dimensions.width}px`,\n height: `${dimensions.height}px`,\n }}\n >\n <div className=\"stretch-icon\" onMouseDown={handleMouseDown}></div>\n <div className=\"messages-container\">\n {activeMessages.map((msg: Message, index: number) => (\n <div\n key={index}\n className={`message ${msg.role === \"user\" ? \"user\" : \"ai\"}`}\n >\n {msg.content}\n </div>\n ))}\n {isLoading && <div className=\"message ai loading\">Thinking...</div>}\n <div ref={messagesEndRef} />\n </div>\n\n <div className=\"input-container\">\n <textarea\n ref={textareaRef}\n rows={1}\n placeholder=\"What's on your mind?\"\n value={input}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n callModel(input);\n }\n }}\n onChange={(e) => setInput(e.target.value)}\n />\n <button\n onClick={() => callModel(input)}\n disabled={!input.trim() || isLoading}\n className=\"send-button\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon>\n </svg>\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":"AAAA,OAAgB,YAAAA,EAAU,UAAAC,EAAQ,aAAAC,EAAW,eAAAC,MAAmB,QAwN1D,cAAAC,EACA,QAAAC,MADA,oBApMC,IAAMC,EAAY,CAAC,CACxB,aAAAC,EACA,cAAeC,EACf,OAAAC,EAAS,WACT,cAAAC,EAAgB,UAChB,eAAAC,EACA,MAAAC,EACA,SAAUC,EACV,cAAAC,EACA,kBAAAC,CACF,IAAsB,CACpB,GAAM,CAACC,EAAOC,CAAQ,EAAIC,EAAiB,EAAE,EACvC,CAACC,EAAkBC,CAAmB,EAAIF,EAAoB,CAAC,CAAC,EAChE,CAACG,EAAWC,CAAY,EAAIJ,EAAS,EAAK,EAC1C,CAACK,EAAuBC,CAAwB,EAAIN,EAAc,IAAI,EACtE,CAACO,EAAYC,CAAa,EAAIR,EAAS,CAAE,MAAO,IAAK,OAAQ,GAAI,CAAC,EAClE,CAACS,EAAYC,CAAa,EAAIV,EAAS,EAAK,EAE5CW,EAAaC,EAAuB,IAAI,EAExCC,EAAcD,EAA4B,IAAI,EAC9CE,EAAiBF,EAAuB,IAAI,EAE5CG,EAAiBpB,GAAoBM,EACrCe,EAAsB1B,GAAyBe,EAErDY,EAAU,IAAM,CACd,IAAI,CAACtB,GAAoBA,EAAiB,SAAW,IAC/CM,EAAiB,SAAW,EAAG,CACjC,IAAMiB,EAAQ,aAAa,QAAQ,UAAU,EAE3ChB,EADEgB,EACkB,KAAK,MAAMA,CAAK,EAEhB,CAClB,CACE,KAAM,YACN,QACEzB,GACA,cAAcD,CAAa,6BAC/B,CACF,CATqC,CAWzC,CAEJ,EAAG,CACDG,EACAF,EACAD,EACAS,EAAiB,MACnB,CAAC,EAEDgB,EAAU,IAAM,CACT3B,IACwB,SAAY,CACrC,GAAI,CAIF,IAAM6B,EAAO,MAHI,MAAM,MACrB,8FACF,GAC4B,KAAK,EACjCb,EAAyBa,CAAI,CAC/B,OAASC,EAAO,CACd,QAAQ,MAAM,iCAAkCA,CAAK,CACvD,CACF,GACmB,CAEvB,EAAG,CAAC9B,CAAqB,CAAC,EAE1B,IAAM+B,EAAiB,IAAM,CAC3BP,EAAe,SAAS,eAAe,CAAE,SAAU,QAAS,CAAC,CAC/D,EAEAG,EAAU,IAAM,CACdI,EAAe,CACjB,EAAG,CAACN,CAAc,CAAC,EAEnB,IAAMO,EAAgB,MAAOC,GAAgD,CAC3E,IAAMC,EAAW,MAAM,MAAMjC,EAAQ,CACnC,OAAQ,OACR,QAAS,CACP,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CACnB,SAAUgC,EACV,cAAeP,EACf,aAAA3B,EACA,MAAAK,CACF,CAAC,CACH,CAAC,EAED,GAAI,CAAC8B,EAAS,GAAI,CAChB,IAAMC,EAAY,MAAMD,EAAS,KAAK,EACtC,MAAM,IAAI,MAAMC,EAAU,OAAS,2BAA2B,CAChE,CAEA,IAAMN,EAAO,MAAMK,EAAS,KAAK,EACjC,OAAI3B,GAAmBA,EAAkBsB,EAAK,IAAI,EAC3CA,EAAK,IACd,EAEMO,EAAY,MAAOC,GAAsB,CAC7C,GAAI,CAACA,EAAU,KAAK,GAAKxB,EAAW,OAEpC,IAAMyB,EAAuB,CAAE,KAAM,OAAQ,QAASD,CAAU,EAC5D/B,GAAeA,EAAcgC,CAAW,EAE5C,IAAMC,EAAc,CAAC,GAAGd,EAAgBa,CAAW,EAE9CjC,GACHO,EAAoB2B,CAAW,EAGjC9B,EAAS,EAAE,EACXK,EAAa,EAAI,EAEjB,GAAI,CAGF,IAAM0B,EAA4B,CAChC,KAAM,YACN,QAJW,MAAMR,EAAcO,CAAW,CAK5C,EAEME,EAAkB,CAAC,GAAGF,EAAaC,CAAgB,EACpDnC,IACHO,EAAoB6B,CAAe,EACnC,aAAa,QAAQ,WAAY,KAAK,UAAUA,CAAe,CAAC,EAEpE,OAASX,EAAO,CACd,QAAQ,MAAM,oBAAqBA,CAAK,EACxC,IAAMY,EAA8B,CAClC,KAAM,YACN,QAAS,wDACX,EACMD,EAAkB,CAAC,GAAGF,EAAaG,CAAkB,EACtDrC,IACHO,EAAoB6B,CAAe,EACnC,aAAa,QAAQ,WAAY,KAAK,UAAUA,CAAe,CAAC,EAEpE,QAAE,CACA3B,EAAa,EAAK,CACpB,CACF,EAEAa,EAAU,IAAM,CACVJ,EAAY,UACdA,EAAY,QAAQ,MAAM,OAAS,OACnCA,EAAY,QAAQ,MAAM,OAAS,GAAG,KAAK,IAAIA,EAAY,QAAQ,aAAc,GAAG,CAAC,KAEzF,EAAG,CAACf,CAAK,CAAC,EAEV,IAAMmC,EAAkBC,EAAa,GAAwB,CAC3D,EAAE,eAAe,EACjBxB,EAAc,EAAI,CACpB,EAAG,CAAC,CAAC,EAEL,OAAAO,EAAU,IAAM,CACd,GAAI,CAACR,EAAY,OAEjB,IAAM0B,EAAmBC,GAAkB,CACzC,GAAI,CAACzB,EAAW,QAAS,OAEzB,IAAM0B,EAAO1B,EAAW,QAAQ,sBAAsB,EAChD2B,EAAWD,EAAK,MAAQD,EAAE,QAC1BG,EAAYF,EAAK,OAASD,EAAE,QAE9BE,EAAW,KAAOA,EAAW,KAC/B9B,EAAegC,IAAU,CAAE,GAAGA,EAAM,MAAOF,CAAS,EAAE,EAEpDC,EAAY,KAAOA,EAAY,KACjC/B,EAAegC,IAAU,CAAE,GAAGA,EAAM,OAAQD,CAAU,EAAE,CAE5D,EAEME,EAAgB,IAAM,CAC1B/B,EAAc,EAAK,CACrB,EAEA,cAAO,iBAAiB,YAAayB,CAAe,EACpD,OAAO,iBAAiB,UAAWM,CAAa,EAEzC,IAAM,CACX,OAAO,oBAAoB,YAAaN,CAAe,EACvD,OAAO,oBAAoB,UAAWM,CAAa,CACrD,CACF,EAAG,CAAChC,CAAU,CAAC,EAGbtB,EAAC,OACC,IAAKwB,EACL,UAAW,sBAAsBF,EAAa,WAAa,EAAE,GAC7D,MAAO,CACL,MAAO,GAAGF,EAAW,KAAK,KAC1B,OAAQ,GAAGA,EAAW,MAAM,IAC9B,EAEA,UAAArB,EAAC,OAAI,UAAU,eAAe,YAAa+C,EAAiB,EAC5D9C,EAAC,OAAI,UAAU,qBACZ,UAAA4B,EAAe,IAAI,CAAC2B,EAAcC,IACjCzD,EAAC,OAEC,UAAW,WAAWwD,EAAI,OAAS,OAAS,OAAS,IAAI,GAExD,SAAAA,EAAI,SAHAC,CAIP,CACD,EACAxC,GAAajB,EAAC,OAAI,UAAU,qBAAqB,uBAAW,EAC7DA,EAAC,OAAI,IAAK4B,EAAgB,GAC5B,EAEA3B,EAAC,OAAI,UAAU,kBACb,UAAAD,EAAC,YACC,IAAK2B,EACL,KAAM,EACN,YAAY,uBACZ,MAAOf,EACP,UAAY,GAAM,CACZ,EAAE,MAAQ,SAAW,CAAC,EAAE,WAC1B,EAAE,eAAe,EACjB4B,EAAU5B,CAAK,EAEnB,EACA,SAAW,GAAMC,EAAS,EAAE,OAAO,KAAK,EAC1C,EACAb,EAAC,UACC,QAAS,IAAMwC,EAAU5B,CAAK,EAC9B,SAAU,CAACA,EAAM,KAAK,GAAKK,EAC3B,UAAU,cAEV,SAAAhB,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,UAAAD,EAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,EACrCA,EAAC,WAAQ,OAAO,4BAA4B,GAC9C,EACF,GACF,GACF,CAEJ","names":["useState","useRef","useEffect","useCallback","jsx","jsxs","ChatPopup","systemPrompt","externalPortfolioData","apiUrl","assistantName","initialMessage","model","externalMessages","onMessageSend","onResponseReceive","input","setInput","useState","internalMessages","setInternalMessages","isLoading","setIsLoading","internalPortfolioData","setInternalPortfolioData","dimensions","setDimensions","isResizing","setIsResizing","wrapperRef","useRef","textareaRef","messagesEndRef","activeMessages","activePortfolioData","useEffect","saved","data","error","scrollToBottom","getAIResponse","currentMessages","response","errorData","callModel","userInput","userMessage","newMessages","assistantMessage","updatedMessages","defaultFailMessage","handleMouseDown","useCallback","handleMouseMove","e","rect","newWidth","newHeight","prev","handleMouseUp","msg","index"]}
1
+ {"version":3,"sources":["../src/components/chat-popup.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport \"./chat-popup.css\";\n\ntype Message = {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n};\n\nexport interface ChatPopupProps {\n systemPrompt?: string;\n portfolioData?: any;\n apiUrl?: string;\n assistantName?: string;\n initialMessage?: string;\n model?: string;\n messages?: Message[];\n onMessageSend?: (message: Message) => void;\n onResponseReceive?: (response: string) => void;\n mockMode?: boolean; // If true, uses mock responses instead of API calls\n position?: \"fixed\" | \"relative\"; // Position style for the wrapper\n}\n\nexport const ChatPopup = ({\n systemPrompt,\n portfolioData: externalPortfolioData,\n apiUrl = \"/chatbot\",\n assistantName = \"Thikana\",\n initialMessage,\n model,\n messages: externalMessages,\n onMessageSend,\n onResponseReceive,\n mockMode = false,\n position = \"relative\",\n}: ChatPopupProps) => {\n const [input, setInput] = useState<string>(\"\");\n const [internalMessages, setInternalMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [internalPortfolioData, setInternalPortfolioData] = useState<any>(null);\n const [dimensions, setDimensions] = useState({ width: 330, height: 480 });\n const [isResizing, setIsResizing] = useState(false);\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const activeMessages = externalMessages || internalMessages;\n const activePortfolioData = externalPortfolioData || internalPortfolioData;\n\n useEffect(() => {\n if (!externalMessages || externalMessages.length === 0) {\n if (internalMessages.length === 0) {\n const saved = localStorage.getItem(\"messages\");\n if (saved) {\n setInternalMessages(JSON.parse(saved));\n } else {\n setInternalMessages([\n {\n role: \"assistant\",\n content:\n initialMessage ||\n `Hi this is ${assistantName}! How can I help you today?`,\n },\n ]);\n }\n }\n }\n }, [\n externalMessages,\n initialMessage,\n assistantName,\n internalMessages.length,\n ]);\n\n useEffect(() => {\n if (!externalPortfolioData) {\n const fetchPortfolioData = async () => {\n try {\n const response = await fetch(\n \"https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json\",\n );\n const data = await response.json();\n setInternalPortfolioData(data);\n } catch (error) {\n console.error(\"Error fetching portfolio data:\", error);\n }\n };\n fetchPortfolioData();\n }\n }, [externalPortfolioData]);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [activeMessages]);\n\n const getAIResponse = async (currentMessages: Message[]): Promise<string> => {\n // Mock mode for testing without API\n if (mockMode) {\n await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API delay\n const mockResponses = [\n \"That's an interesting question! Let me help you with that.\",\n \"I understand what you're asking. Here's what I think...\",\n \"Great question! Based on what you've told me, I'd suggest...\",\n \"Let me break that down for you.\",\n \"I'm here to help! Here's my take on that...\",\n ];\n const response =\n mockResponses[Math.floor(Math.random() * mockResponses.length)];\n if (onResponseReceive) onResponseReceive(response);\n return response;\n }\n\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messages: currentMessages,\n portfolioData: activePortfolioData,\n systemPrompt,\n model,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error || \"Failed to get AI response\");\n }\n\n const data = await response.json();\n if (onResponseReceive) onResponseReceive(data.text);\n return data.text;\n };\n\n const callModel = async (userInput: string) => {\n if (!userInput.trim() || isLoading) return;\n\n const userMessage: Message = { role: \"user\", content: userInput };\n if (onMessageSend) onMessageSend(userMessage);\n\n const newMessages = [...activeMessages, userMessage];\n\n if (!externalMessages) {\n setInternalMessages(newMessages);\n }\n\n setInput(\"\");\n setIsLoading(true);\n\n try {\n const text = await getAIResponse(newMessages);\n\n const assistantMessage: Message = {\n role: \"assistant\",\n content: text,\n };\n\n const updatedMessages = [...newMessages, assistantMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } catch (error) {\n console.error(\"Error calling AI:\", error);\n const defaultFailMessage: Message = {\n role: \"assistant\",\n content: \"Sorry, I encountered an error. Please try again later.\",\n };\n const updatedMessages = [...newMessages, defaultFailMessage];\n if (!externalMessages) {\n setInternalMessages(updatedMessages);\n localStorage.setItem(\"messages\", JSON.stringify(updatedMessages));\n }\n } finally {\n setIsLoading(false);\n }\n };\n\n useEffect(() => {\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 100)}px`;\n }\n }, [input]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setIsResizing(true);\n }, []);\n\n useEffect(() => {\n if (!isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!wrapperRef.current) return;\n\n const rect = wrapperRef.current.getBoundingClientRect();\n const newWidth = rect.right - e.clientX;\n const newHeight = rect.bottom - e.clientY;\n\n if (newWidth > 250 && newWidth < 800) {\n setDimensions((prev) => ({ ...prev, width: newWidth }));\n }\n if (newHeight > 300 && newHeight < 800) {\n setDimensions((prev) => ({ ...prev, height: newHeight }));\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n };\n }, [isResizing]);\n\n return (\n <div\n ref={wrapperRef}\n className={`chat-popup-wrapper ${isResizing ? \"resizing\" : \"\"}`}\n style={{\n width: `${dimensions.width}px`,\n height: `${dimensions.height}px`,\n position: position,\n }}\n >\n <div className=\"stretch-icon\" onMouseDown={handleMouseDown}></div>\n <div className=\"messages-container\">\n {activeMessages.map((msg: Message, index: number) => (\n <div\n key={index}\n className={`message ${msg.role === \"user\" ? \"user\" : \"ai\"}`}\n >\n {msg.content}\n </div>\n ))}\n {isLoading && <div className=\"message ai loading\">Thinking...</div>}\n <div ref={messagesEndRef} />\n </div>\n\n <div className=\"input-container\">\n <textarea\n ref={textareaRef}\n rows={1}\n placeholder=\"What's on your mind?\"\n value={input}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n callModel(input);\n }\n }}\n onChange={(e) => setInput(e.target.value)}\n />\n <button\n onClick={() => callModel(input)}\n disabled={!input.trim() || isLoading}\n className=\"send-button\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon>\n </svg>\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":"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","names":["useState","useRef","useEffect","useCallback","jsx","jsxs","ChatPopup","systemPrompt","externalPortfolioData","apiUrl","assistantName","initialMessage","model","externalMessages","onMessageSend","onResponseReceive","mockMode","position","input","setInput","useState","internalMessages","setInternalMessages","isLoading","setIsLoading","internalPortfolioData","setInternalPortfolioData","dimensions","setDimensions","isResizing","setIsResizing","wrapperRef","useRef","textareaRef","messagesEndRef","activeMessages","activePortfolioData","useEffect","saved","data","error","scrollToBottom","getAIResponse","currentMessages","resolve","mockResponses","response","errorData","callModel","userInput","userMessage","newMessages","assistantMessage","updatedMessages","defaultFailMessage","handleMouseDown","useCallback","handleMouseMove","e","rect","newWidth","newHeight","prev","handleMouseUp","msg","index"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thikanaa",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A beautiful, AI-powered chat popup component for React and Next.js applications",
5
5
  "keywords": [
6
6
  "react",