thikanaa 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # [Thikana](https://thikana.malaymishra.com/)
2
+
3
+ **Thikana** (meaning "Destination" or "Home" in Hindi) is a premium AI-powered portfolio assistant designed to provide an interactive way to explore details of your project.
4
+
5
+ Built with modern web technologies, Thikana offers a seamless, conversational interface that acts as a digital twin for your app, answering questions about his experience, skills, and projects in real-time.
6
+
7
+ ## ✨ Features
8
+
9
+ - **Multi-Model AI Integration**: Leverages the power of **Groq** (Llama 3.3, 3.1) and **Google Gemini** (1.5 Flash) via the Vercel AI SDK.
10
+ - **Intelligent Fallback Mechanism**: If one AI model is unavailable or hits rate limits, the system automatically tries the next best model to ensure zero downtime.
11
+ - **Dynamic Context Injection**: Responds accurately based on a comprehensive professional dataset (experience at Adeptmind, expertise in Go, Python, React, and more).
12
+ - **Modern UI/UX**: Features a sleek, responsive chat interface with glassmorphism effects and smooth micro-animations.
13
+ - **Persistent Chat History**: Remembers your conversation using local storage.
14
+
15
+ ## 🛠️ Tech Stack
16
+
17
+ - **Frontend**: [Next.js](https://nextjs.org/) (App Router), React 19, TypeScript
18
+ - **Styling**: Vanilla CSS (Modern CSS variables, Flexbox/Grid)
19
+ - **AI Engine**: [Vercel AI SDK](https://sdk.vercel.ai/docs), Groq, Google Generative AI
20
+ - **Deployment**: Optimized for Vercel
21
+
22
+ ## 🚀 Getting Started
23
+
24
+ ### Prerequisites
25
+
26
+ - Node.js (v18 or later)
27
+ - Yarn or NPM
28
+ - API Keys for Groq and/or Google Gemini
29
+
30
+ ### Installation
31
+
32
+ 1. **Clone the repository**:
33
+ ```bash
34
+ git clone [repository-url]
35
+ cd thikana
36
+ ```
37
+
38
+ 2. **Install dependencies**:
39
+ ```bash
40
+ yarn install
41
+ ```
42
+
43
+ 3. **Set up Environment Variables**:
44
+ Create a `.env` file in the root directory and add your API keys:
45
+ ```env
46
+ GROQ_API_KEY=your_groq_api_key_here
47
+ GEMINI_API_KEY=your_gemini_api_key_here
48
+ ```
49
+
50
+ 4. **Run the development server**:
51
+ ```bash
52
+ yarn dev
53
+ ```
54
+
55
+ 5. **Open the app**:
56
+ Navigate to [http://localhost:3000](http://localhost:3000) to see the application in action.
57
+
58
+ ## 📦 NPM Package Usage
59
+
60
+ You can now use Thikana as an npm package in your own React/Next.js projects.
61
+
62
+ ### Installation
63
+
64
+ ```bash
65
+ npm install thikanaa
66
+ ```
67
+
68
+ ### Usage
69
+
70
+ ```tsx
71
+ import { ChatPopup } from 'thikanaa';
72
+ import 'thikanaa/dist/index.css';
73
+
74
+
75
+ const App = () => {
76
+ return (
77
+ <ChatPopup
78
+ systemPrompt="Your custom system prompt here"
79
+ assistantName="Thikana"
80
+ apiUrl="/api/chatbot" // Your backend endpoint
81
+ />
82
+ );
83
+ };
84
+ ```
85
+
86
+ #### Props
87
+
88
+ | Prop | Type | Description |
89
+ | --- | --- | --- |
90
+ | `systemPrompt` | `string` | Custom instructions for the AI model |
91
+ | `portfolioData` | `any` | Base context data for the assistant |
92
+ | `apiUrl` | `string` | Endpoint to call for AI responses (default: `/chatbot`) |
93
+ | `assistantName` | `string` | Name shown in the chat (default: `Thikana`) |
94
+ | `model` | `string` | Specific model ID to use |
95
+
96
+ ---
97
+
98
+ Crafted with ❤️ by **Malay Mishra**
99
+
package/dist/index.css ADDED
@@ -0,0 +1,112 @@
1
+ .chat-popup-wrapper {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ }
6
+
7
+ .input-container {
8
+ margin-top: auto;
9
+ width: 100%;
10
+ display: flex;
11
+ align-items: flex-end;
12
+ gap: 10px;
13
+ background-color: #f8f9fa;
14
+ padding: 4px 6px;
15
+ border-radius: 20px;
16
+ border: 1px solid #e9ecef;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ .input-container textarea {
21
+ flex: 1;
22
+ border: none;
23
+ background: transparent;
24
+ padding: 6px 8px;
25
+ font-size: 14px;
26
+ line-height: 20px;
27
+ outline: none;
28
+ resize: none;
29
+ font-family: inherit;
30
+ overflow-y: auto;
31
+ max-height: 100px;
32
+ }
33
+
34
+ .input-container textarea::placeholder {
35
+ color: #adb5bd;
36
+ }
37
+
38
+ .input-container .send-button {
39
+ background: #c52c2cba;
40
+ color: white;
41
+ border: none;
42
+ border-radius: 50%;
43
+ width: 32px;
44
+ height: 32px;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ cursor: pointer;
49
+ transition: background-color 0.2s;
50
+ flex-shrink: 0;
51
+ padding: 0;
52
+ }
53
+
54
+ .input-container .send-button:hover:not(:disabled) {
55
+ background-color: #c52c2c;
56
+ }
57
+
58
+ .input-container .send-button:disabled {
59
+ opacity: 0.5;
60
+ cursor: not-allowed;
61
+ }
62
+
63
+ .messages-container {
64
+ flex: 1;
65
+ overflow-y: auto;
66
+ margin-bottom: 10px;
67
+ display: flex;
68
+ flex-direction: column;
69
+ gap: 12px;
70
+ padding-right: 4px;
71
+ }
72
+
73
+ .messages-container::-webkit-scrollbar {
74
+ width: 4px;
75
+ }
76
+
77
+ .messages-container::-webkit-scrollbar-track {
78
+ background: transparent;
79
+ }
80
+
81
+ .messages-container::-webkit-scrollbar-thumb {
82
+ background: #e9ecef;
83
+ border-radius: 10px;
84
+ }
85
+
86
+ .message {
87
+ max-width: 85%;
88
+ padding: 10px 14px;
89
+ border-radius: 18px;
90
+ font-size: 14px;
91
+ line-height: 1.4;
92
+ word-wrap: break-word;
93
+ }
94
+
95
+ .message.user {
96
+ align-self: flex-end;
97
+ background-color: #c52c2cba;
98
+ color: white;
99
+ border-bottom-right-radius: 4px;
100
+ }
101
+
102
+ .message.ai {
103
+ align-self: flex-start;
104
+ background-color: #f1f3f5;
105
+ color: #212529;
106
+ border-bottom-left-radius: 4px;
107
+ }
108
+
109
+ .message.loading {
110
+ font-style: italic;
111
+ color: #868e96;
112
+ }
@@ -0,0 +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":[]}
@@ -0,0 +1,20 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type Message = {
4
+ role: "user" | "assistant" | "system";
5
+ content: string;
6
+ };
7
+ interface ChatPopupProps {
8
+ systemPrompt?: string;
9
+ portfolioData?: any;
10
+ apiUrl?: string;
11
+ assistantName?: string;
12
+ initialMessage?: string;
13
+ model?: string;
14
+ messages?: Message[];
15
+ onMessageSend?: (message: Message) => void;
16
+ onResponseReceive?: (response: string) => void;
17
+ }
18
+ declare const ChatPopup: ({ systemPrompt, portfolioData: externalPortfolioData, apiUrl, assistantName, initialMessage, model, messages: externalMessages, onMessageSend, onResponseReceive, }: ChatPopupProps) => react_jsx_runtime.JSX.Element;
19
+
20
+ export { ChatPopup, type ChatPopupProps };
@@ -0,0 +1,20 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type Message = {
4
+ role: "user" | "assistant" | "system";
5
+ content: string;
6
+ };
7
+ interface ChatPopupProps {
8
+ systemPrompt?: string;
9
+ portfolioData?: any;
10
+ apiUrl?: string;
11
+ assistantName?: string;
12
+ initialMessage?: string;
13
+ model?: string;
14
+ messages?: Message[];
15
+ onMessageSend?: (message: Message) => void;
16
+ onResponseReceive?: (response: string) => void;
17
+ }
18
+ declare const ChatPopup: ({ systemPrompt, portfolioData: externalPortfolioData, apiUrl, assistantName, initialMessage, model, messages: externalMessages, onMessageSend, onResponseReceive, }: ChatPopupProps) => react_jsx_runtime.JSX.Element;
19
+
20
+ export { ChatPopup, type ChatPopupProps };
package/dist/index.js ADDED
@@ -0,0 +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});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/dist/index.mjs ADDED
@@ -0,0 +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};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "thikanaa",
3
+ "version": "0.1.0",
4
+ "description": "A beautiful, AI-powered chat popup component for React and Next.js applications",
5
+ "keywords": [
6
+ "react",
7
+ "nextjs",
8
+ "chat",
9
+ "chatbot",
10
+ "ai",
11
+ "popup",
12
+ "component"
13
+ ],
14
+ "author": "Malay Mishra",
15
+ "license": "MIT",
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.mjs",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.mjs",
23
+ "require": "./dist/index.js"
24
+ },
25
+ "./dist/index.css": "./dist/index.css"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "dependencies": {
31
+ "@ai-sdk/google": "^3.0.15",
32
+ "@ai-sdk/groq": "^3.0.16",
33
+ "@ai-sdk/openai": "^3.0.21",
34
+ "ai": "^6.0.57",
35
+ "zod": "^4.3.6"
36
+ },
37
+ "peerDependencies": {
38
+ "next": ">=13",
39
+ "react": ">=18",
40
+ "react-dom": ">=18"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "dev": "next dev",
47
+ "build": "next build",
48
+ "build:lib": "tsup",
49
+ "prepublishOnly": "npm run build:lib",
50
+ "start": "next start",
51
+ "lint": "next lint"
52
+ },
53
+ "eslintConfig": {
54
+ "extends": [
55
+ "react-app",
56
+ "react-app/jest"
57
+ ]
58
+ },
59
+ "browserslist": {
60
+ "production": [
61
+ ">0.2%",
62
+ "not dead",
63
+ "not op_mini all"
64
+ ],
65
+ "development": [
66
+ "last 1 chrome version",
67
+ "last 1 firefox version",
68
+ "last 1 safari version"
69
+ ]
70
+ },
71
+ "devDependencies": {
72
+ "@types/node": "25.1.0",
73
+ "@types/react": "19.2.10",
74
+ "tsup": "^8.5.1",
75
+ "typescript": "^5.9.3"
76
+ }
77
+ }