thikanaa 0.1.2 → 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/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.2",
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",