thikanaa 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.css CHANGED
@@ -1,7 +1,15 @@
1
1
  .chat-popup-wrapper {
2
2
  display: flex;
3
3
  flex-direction: column;
4
- height: 100%;
4
+ position: relative;
5
+ transition:
6
+ width 0.05s ease-out,
7
+ height 0.05s ease-out;
8
+ }
9
+
10
+ .chat-popup-wrapper.resizing {
11
+ user-select: none;
12
+ transition: none;
5
13
  }
6
14
 
7
15
  .input-container {
@@ -29,6 +37,7 @@
29
37
  font-family: inherit;
30
38
  overflow-y: auto;
31
39
  max-height: 100px;
40
+ height: auto !important;
32
41
  }
33
42
 
34
43
  .input-container textarea::placeholder {
@@ -110,3 +119,38 @@
110
119
  font-style: italic;
111
120
  color: #868e96;
112
121
  }
122
+
123
+ .stretch-icon {
124
+ position: absolute;
125
+ top: 0;
126
+ left: 0;
127
+ width: 40px;
128
+ height: 40px;
129
+ cursor: nwse-resize;
130
+ z-index: 10;
131
+ display: flex;
132
+ align-items: flex-start;
133
+ justify-content: flex-start;
134
+ transition:
135
+ opacity 0.2s ease,
136
+ transform 0.2s ease;
137
+ opacity: 0.3;
138
+ margin: -10px 0 0 -10px;
139
+ }
140
+
141
+ .stretch-icon:hover {
142
+ opacity: 1;
143
+ transform: scale(1.1);
144
+ }
145
+
146
+ .stretch-icon::before {
147
+ content: "";
148
+ width: 28px;
149
+ height: 28px;
150
+ border-top: 3px solid #c52c2cba;
151
+ border-left: 3px solid #c52c2cba;
152
+ border-top-left-radius: 6px;
153
+ position: absolute;
154
+ top: 6px;
155
+ left: 6px;
156
+ }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/chat-popup.css"],"sourcesContent":[".chat-popup-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.input-container {\n margin-top: auto;\n width: 100%;\n display: flex;\n align-items: flex-end;\n gap: 10px;\n background-color: #f8f9fa;\n padding: 4px 6px;\n border-radius: 20px;\n border: 1px solid #e9ecef;\n box-sizing: border-box;\n}\n\n.input-container textarea {\n flex: 1;\n border: none;\n background: transparent;\n padding: 6px 8px;\n font-size: 14px;\n line-height: 20px;\n outline: none;\n resize: none;\n font-family: inherit;\n overflow-y: auto;\n max-height: 100px;\n}\n\n.input-container textarea::placeholder {\n color: #adb5bd;\n}\n\n.input-container .send-button {\n background: #c52c2cba;\n color: white;\n border: none;\n border-radius: 50%;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: background-color 0.2s;\n flex-shrink: 0;\n padding: 0;\n}\n\n.input-container .send-button:hover:not(:disabled) {\n background-color: #c52c2c;\n}\n\n.input-container .send-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.messages-container {\n flex: 1;\n overflow-y: auto;\n margin-bottom: 10px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding-right: 4px;\n}\n\n.messages-container::-webkit-scrollbar {\n width: 4px;\n}\n\n.messages-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.messages-container::-webkit-scrollbar-thumb {\n background: #e9ecef;\n border-radius: 10px;\n}\n\n.message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n.message.user {\n align-self: flex-end;\n background-color: #c52c2cba;\n color: white;\n border-bottom-right-radius: 4px;\n}\n\n.message.ai {\n align-self: flex-start;\n background-color: #f1f3f5;\n color: #212529;\n border-bottom-left-radius: 4px;\n}\n\n.message.loading {\n font-style: italic;\n color: #868e96;\n}\n"],"mappings":"AAAA,CAAC,mBACC,QAAS,KACT,eAAgB,OAChB,OAAQ,IACV,CAEA,CAAC,gBACC,WAAY,KACZ,MAAO,KACP,QAAS,KACT,YAAa,SACb,IAAK,KACL,iBAAkB,QAZpB,QAaW,IAAI,IAbf,cAciB,KACf,OAAQ,IAAI,MAAM,QAClB,WAAY,UACd,CAEA,CAbC,gBAagB,SACf,KAAM,EACN,OAAQ,KACR,WAAY,YAtBd,QAuBW,IAAI,IACb,UAAW,KACX,YAAa,KACb,QAAS,KACT,OAAQ,KACR,YAAa,QACb,WAAY,KACZ,WAAY,KACd,CAEA,CA3BC,gBA2BgB,QAAQ,cACvB,MAAO,OACT,CAEA,CA/BC,gBA+BgB,CAAC,YAChB,WAAY,UACZ,MAAO,KACP,OAAQ,KAxCV,cAyCiB,IACf,MAAO,KACP,OAAQ,KACR,QAAS,KACT,YAAa,OACb,gBAAiB,OACjB,OAAQ,QACR,WAAY,iBAAiB,IAC7B,YAAa,EAjDf,QAkDW,CACX,CAEA,CA/CC,gBA+CgB,CAhBC,WAgBW,MAAM,KAAK,WACtC,iBAAkB,OACpB,CAEA,CAnDC,gBAmDgB,CApBC,WAoBW,UAC3B,QAAS,GACT,OAAQ,WACV,CAEA,CAAC,mBACC,KAAM,EACN,WAAY,KACZ,cAAe,KACf,QAAS,KACT,eAAgB,OAChB,IAAK,KACL,cAAe,GACjB,CAEA,CAVC,kBAUkB,oBACjB,MAAO,GACT,CAEA,CAdC,kBAckB,0BACjB,WAAY,WACd,CAEA,CAlBC,kBAkBkB,0BACjB,WAAY,QAjFd,cAkFiB,IACjB,CAEA,CAAC,QACC,UAAW,IAtFb,QAuFW,KAAK,KAvFhB,cAwFiB,KACf,UAAW,KACX,YAAa,IACb,UAAW,UACb,CAEA,CATC,OASO,CAAC,KACP,WAAY,SACZ,iBAAkB,UAClB,MAAO,KACP,2BAA4B,GAC9B,CAEA,CAhBC,OAgBO,CAAC,GACP,WAAY,WACZ,iBAAkB,QAClB,MAAO,QACP,0BAA2B,GAC7B,CAEA,CAvBC,OAuBO,CAAC,QACP,WAAY,OACZ,MAAO,OACT","names":[]}
1
+ {"version":3,"sources":["../src/components/chat-popup.css"],"sourcesContent":[".chat-popup-wrapper {\n display: flex;\n flex-direction: column;\n position: relative;\n transition:\n width 0.05s ease-out,\n height 0.05s ease-out;\n}\n\n.chat-popup-wrapper.resizing {\n user-select: none;\n transition: none;\n}\n\n.input-container {\n margin-top: auto;\n width: 100%;\n display: flex;\n align-items: flex-end;\n gap: 10px;\n background-color: #f8f9fa;\n padding: 4px 6px;\n border-radius: 20px;\n border: 1px solid #e9ecef;\n box-sizing: border-box;\n}\n\n.input-container textarea {\n flex: 1;\n border: none;\n background: transparent;\n padding: 6px 8px;\n font-size: 14px;\n line-height: 20px;\n outline: none;\n resize: none;\n font-family: inherit;\n overflow-y: auto;\n max-height: 100px;\n height: auto !important;\n}\n\n.input-container textarea::placeholder {\n color: #adb5bd;\n}\n\n.input-container .send-button {\n background: #c52c2cba;\n color: white;\n border: none;\n border-radius: 50%;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: background-color 0.2s;\n flex-shrink: 0;\n padding: 0;\n}\n\n.input-container .send-button:hover:not(:disabled) {\n background-color: #c52c2c;\n}\n\n.input-container .send-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.messages-container {\n flex: 1;\n overflow-y: auto;\n margin-bottom: 10px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding-right: 4px;\n}\n\n.messages-container::-webkit-scrollbar {\n width: 4px;\n}\n\n.messages-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.messages-container::-webkit-scrollbar-thumb {\n background: #e9ecef;\n border-radius: 10px;\n}\n\n.message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n.message.user {\n align-self: flex-end;\n background-color: #c52c2cba;\n color: white;\n border-bottom-right-radius: 4px;\n}\n\n.message.ai {\n align-self: flex-start;\n background-color: #f1f3f5;\n color: #212529;\n border-bottom-left-radius: 4px;\n}\n\n.message.loading {\n font-style: italic;\n color: #868e96;\n}\n\n.stretch-icon {\n position: absolute;\n top: 0;\n left: 0;\n width: 40px;\n height: 40px;\n cursor: nwse-resize;\n z-index: 10;\n display: flex;\n align-items: flex-start;\n justify-content: flex-start;\n transition:\n opacity 0.2s ease,\n transform 0.2s ease;\n opacity: 0.3;\n margin: -10px 0 0 -10px;\n}\n\n.stretch-icon:hover {\n opacity: 1;\n transform: scale(1.1);\n}\n\n.stretch-icon::before {\n content: \"\";\n width: 28px;\n height: 28px;\n border-top: 3px solid #c52c2cba;\n border-left: 3px solid #c52c2cba;\n border-top-left-radius: 6px;\n position: absolute;\n top: 6px;\n left: 6px;\n}\n"],"mappings":"AAAA,CAAC,mBACC,QAAS,KACT,eAAgB,OAChB,SAAU,SACV,WACE,MAAM,KAAM,QAAQ,CACpB,OAAO,KAAM,QACjB,CAEA,CATC,kBASkB,CAAC,SAClB,YAAa,KACb,WAAY,IACd,CAEA,CAAC,gBACC,WAAY,KACZ,MAAO,KACP,QAAS,KACT,YAAa,SACb,IAAK,KACL,iBAAkB,QApBpB,QAqBW,IAAI,IArBf,cAsBiB,KACf,OAAQ,IAAI,MAAM,QAClB,WAAY,UACd,CAEA,CAbC,gBAagB,SACf,KAAM,EACN,OAAQ,KACR,WAAY,YA9Bd,QA+BW,IAAI,IACb,UAAW,KACX,YAAa,KACb,QAAS,KACT,OAAQ,KACR,YAAa,QACb,WAAY,KACZ,WAAY,MACZ,OAAQ,cACV,CAEA,CA5BC,gBA4BgB,QAAQ,cACvB,MAAO,OACT,CAEA,CAhCC,gBAgCgB,CAAC,YAChB,WAAY,UACZ,MAAO,KACP,OAAQ,KAjDV,cAkDiB,IACf,MAAO,KACP,OAAQ,KACR,QAAS,KACT,YAAa,OACb,gBAAiB,OACjB,OAAQ,QACR,WAAY,iBAAiB,IAC7B,YAAa,EA1Df,QA2DW,CACX,CAEA,CAhDC,gBAgDgB,CAhBC,WAgBW,MAAM,KAAK,WACtC,iBAAkB,OACpB,CAEA,CApDC,gBAoDgB,CApBC,WAoBW,UAC3B,QAAS,GACT,OAAQ,WACV,CAEA,CAAC,mBACC,KAAM,EACN,WAAY,KACZ,cAAe,KACf,QAAS,KACT,eAAgB,OAChB,IAAK,KACL,cAAe,GACjB,CAEA,CAVC,kBAUkB,oBACjB,MAAO,GACT,CAEA,CAdC,kBAckB,0BACjB,WAAY,WACd,CAEA,CAlBC,kBAkBkB,0BACjB,WAAY,QA1Fd,cA2FiB,IACjB,CAEA,CAAC,QACC,UAAW,IA/Fb,QAgGW,KAAK,KAhGhB,cAiGiB,KACf,UAAW,KACX,YAAa,IACb,UAAW,UACb,CAEA,CATC,OASO,CAAC,KACP,WAAY,SACZ,iBAAkB,UAClB,MAAO,KACP,2BAA4B,GAC9B,CAEA,CAhBC,OAgBO,CAAC,GACP,WAAY,WACZ,iBAAkB,QAClB,MAAO,QACP,0BAA2B,GAC7B,CAEA,CAvBC,OAuBO,CAAC,QACP,WAAY,OACZ,MAAO,OACT,CAEA,CAAC,aACC,SAAU,SACV,IAAK,EACL,KAAM,EACN,MAAO,KACP,OAAQ,KACR,OAAQ,YACR,QAAS,GACT,QAAS,KACT,YAAa,WACb,gBAAiB,WACjB,WACE,QAAQ,IAAK,IAAI,CACjB,UAAU,IAAK,KACjB,QAAS,GAxIX,OAyIU,MAAM,EAAE,EAAE,KACpB,CAEA,CAlBC,YAkBY,OACX,QAAS,EACT,UAAW,MAAM,IACnB,CAEA,CAvBC,YAuBY,QACX,QAAS,GACT,MAAO,KACP,OAAQ,KACR,WAAY,IAAI,MAAM,UACtB,YAAa,IAAI,MAAM,UACvB,uBAAwB,IACxB,SAAU,SACV,IAAK,IACL,KAAM,GACR","names":[]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var w=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var R=(n,t)=>{for(var l in t)w(n,l,{get:t[l],enumerable:!0})},z=(n,t,l,d)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of H(t))!O.call(n,i)&&i!==l&&w(n,i,{get:()=>t[i],enumerable:!(d=L(t,i))||d.enumerable});return n};var A=n=>z(w({},"__esModule",{value:!0}),n);var $={};R($,{ChatPopup:()=>J});module.exports=A($);var s=require("react");var o=require("react/jsx-runtime"),J=({systemPrompt:n,portfolioData:t,apiUrl:l="/chatbot",assistantName:d="Thikana",initialMessage:i,model:N,messages:c,onMessageSend:v,onResponseReceive:k})=>{let[p,M]=(0,s.useState)(""),[m,g]=(0,s.useState)([]),[b,I]=(0,s.useState)(!1),[S,E]=(0,s.useState)(null),u=(0,s.useRef)(null),P=(0,s.useRef)(null),x=c||m,T=t||S;(0,s.useEffect)(()=>{if((!c||c.length===0)&&m.length===0){let e=localStorage.getItem("messages");g(e?JSON.parse(e):[{role:"assistant",content:i||`Hi this is ${d}! How can I help you today?`}])}},[c,i,d,m.length]),(0,s.useEffect)(()=>{t||(async()=>{try{let r=await(await fetch("https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json")).json();E(r)}catch(a){console.error("Error fetching portfolio data:",a)}})()},[t]);let C=()=>{P.current?.scrollIntoView({behavior:"smooth"})};(0,s.useEffect)(()=>{C()},[x]);let j=async e=>{let a=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,portfolioData:T,systemPrompt:n,model:N})});if(!a.ok){let h=await a.json();throw new Error(h.error||"Failed to get AI response")}let r=await a.json();return k&&k(r.text),r.text},D=async e=>{if(!e.trim()||b)return;let a={role:"user",content:e};v&&v(a);let r=[...x,a];c||g(r),M(""),I(!0);try{let y={role:"assistant",content:await j(r)},f=[...r,y];c||(g(f),localStorage.setItem("messages",JSON.stringify(f)))}catch(h){console.error("Error calling AI:",h);let y={role:"assistant",content:"Sorry, I encountered an error. Please try again later."},f=[...r,y];c||(g(f),localStorage.setItem("messages",JSON.stringify(f)))}finally{I(!1)}};return(0,s.useEffect)(()=>{u.current&&(u.current.style.height="auto",u.current.style.height=`${Math.min(u.current.scrollHeight,100)}px`)},[p]),(0,o.jsxs)("div",{className:"chat-popup-wrapper",children:[(0,o.jsxs)("div",{className:"messages-container",children:[x.map((e,a)=>(0,o.jsx)("div",{className:`message ${e.role==="user"?"user":"ai"}`,children:e.content},a)),b&&(0,o.jsx)("div",{className:"message ai loading",children:"Thinking..."}),(0,o.jsx)("div",{ref:P})]}),(0,o.jsxs)("div",{className:"input-container",children:[(0,o.jsx)("textarea",{ref:u,rows:1,placeholder:"What's on your mind?",value:p,onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),D(p))},onChange:e=>M(e.target.value)}),(0,o.jsx)("button",{onClick:()=>D(p),disabled:!p.trim()||b,className:"send-button",children:(0,o.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,o.jsx)("line",{x1:"22",y1:"2",x2:"11",y2:"13"}),(0,o.jsx)("polygon",{points:"22 2 15 22 11 13 2 9 22 2"})]})})]})]})};0&&(module.exports={ChatPopup});
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});
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 } 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\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 return (\n <div className=\"chat-popup-wrapper\">\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","textareaRef","messagesEndRef","activeMessages","activePortfolioData","saved","data","error","scrollToBottom","getAIResponse","currentMessages","response","errorData","callModel","userInput","userMessage","newMessages","assistantMessage","updatedMessages","defaultFailMessage","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}\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"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{useState as d,useRef as D,useEffect as p}from"react";import{jsx as a,jsxs as g}from"react/jsx-runtime";var $=({systemPrompt:N,portfolioData:u,apiUrl:S="/chatbot",assistantName:x="Thikana",initialMessage:y,model:E,messages:s,onMessageSend:w,onResponseReceive:v})=>{let[n,k]=d(""),[f,r]=d([]),[h,M]=d(!1),[T,C]=d(null),i=D(null),I=D(null),m=s||f,j=u||T;p(()=>{if((!s||s.length===0)&&f.length===0){let e=localStorage.getItem("messages");r(e?JSON.parse(e):[{role:"assistant",content:y||`Hi this is ${x}! How can I help you today?`}])}},[s,y,x,f.length]),p(()=>{u||(async()=>{try{let o=await(await fetch("https://raw.githubusercontent.com/hi-malay/portfolio-data/refs/heads/main/scrapped_data.json")).json();C(o)}catch(t){console.error("Error fetching portfolio data:",t)}})()},[u]);let L=()=>{I.current?.scrollIntoView({behavior:"smooth"})};p(()=>{L()},[m]);let H=async e=>{let t=await fetch(S,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,portfolioData:j,systemPrompt:N,model:E})});if(!t.ok){let l=await t.json();throw new Error(l.error||"Failed to get AI response")}let o=await t.json();return v&&v(o.text),o.text},P=async e=>{if(!e.trim()||h)return;let t={role:"user",content:e};w&&w(t);let o=[...m,t];s||r(o),k(""),M(!0);try{let b={role:"assistant",content:await H(o)},c=[...o,b];s||(r(c),localStorage.setItem("messages",JSON.stringify(c)))}catch(l){console.error("Error calling AI:",l);let b={role:"assistant",content:"Sorry, I encountered an error. Please try again later."},c=[...o,b];s||(r(c),localStorage.setItem("messages",JSON.stringify(c)))}finally{M(!1)}};return p(()=>{i.current&&(i.current.style.height="auto",i.current.style.height=`${Math.min(i.current.scrollHeight,100)}px`)},[n]),g("div",{className:"chat-popup-wrapper",children:[g("div",{className:"messages-container",children:[m.map((e,t)=>a("div",{className:`message ${e.role==="user"?"user":"ai"}`,children:e.content},t)),h&&a("div",{className:"message ai loading",children:"Thinking..."}),a("div",{ref:I})]}),g("div",{className:"input-container",children:[a("textarea",{ref:i,rows:1,placeholder:"What's on your mind?",value:n,onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),P(n))},onChange:e=>k(e.target.value)}),a("button",{onClick:()=>P(n),disabled:!n.trim()||h,className:"send-button",children:g("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{$ as ChatPopup};
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};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/chat-popup.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect } 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\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 return (\n <div className=\"chat-popup-wrapper\">\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","jsx","jsxs","ChatPopup","systemPrompt","externalPortfolioData","apiUrl","assistantName","initialMessage","model","externalMessages","onMessageSend","onResponseReceive","input","setInput","useState","internalMessages","setInternalMessages","isLoading","setIsLoading","internalPortfolioData","setInternalPortfolioData","textareaRef","useRef","messagesEndRef","activeMessages","activePortfolioData","useEffect","saved","data","error","scrollToBottom","getAIResponse","currentMessages","response","errorData","callModel","userInput","userMessage","newMessages","assistantMessage","updatedMessages","defaultFailMessage","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}\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":["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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thikanaa",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A beautiful, AI-powered chat popup component for React and Next.js applications",
5
5
  "keywords": [
6
6
  "react",
@@ -71,6 +71,9 @@
71
71
  "devDependencies": {
72
72
  "@types/node": "25.1.0",
73
73
  "@types/react": "19.2.10",
74
+ "next": "^16.1.6",
75
+ "react": "^19.2.4",
76
+ "react-dom": "^19.2.4",
74
77
  "tsup": "^8.5.1",
75
78
  "typescript": "^5.9.3"
76
79
  }