@plasius/chatbot 1.0.0 → 1.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.
@@ -1,106 +1,139 @@
1
- /* src/components/Chatbot.css */
2
1
  .chatbotcontainer {
3
2
  display: flex;
4
3
  flex-direction: column;
5
- height: 100vh;
6
4
  width: 100%;
7
- max-width: 600px;
5
+ max-width: 720px;
8
6
  margin: 0 auto;
9
- border: 1px solid #ddd;
10
- border-radius: 8px;
11
- background: #f9f9f9;
7
+ border: 1px solid #d1d7e0;
8
+ border-radius: 12px;
9
+ background: #f7f9fc;
10
+ min-height: 70vh;
11
+ overflow: hidden;
12
+ }
13
+
14
+ .header {
15
+ display: flex;
16
+ justify-content: space-between;
17
+ align-items: center;
18
+ padding: 12px 16px;
19
+ border-bottom: 1px solid #d1d7e0;
20
+ background: #eef3fb;
21
+ }
22
+
23
+ .title {
24
+ font-size: 16px;
25
+ font-weight: 700;
26
+ color: #1f2a3d;
27
+ }
28
+
29
+ .usage {
30
+ font-size: 13px;
31
+ font-weight: 600;
32
+ color: #4c5970;
33
+ }
34
+
35
+ .notice {
36
+ padding: 10px 16px;
37
+ font-size: 13px;
38
+ border-bottom: 1px solid #d1d7e0;
39
+ background: #fff8db;
40
+ color: #6f5200;
12
41
  }
13
42
 
14
43
  .messagesbox {
15
44
  flex: 1;
16
- padding: 10px;
45
+ padding: 12px;
17
46
  overflow-y: auto;
18
- background: #fff;
47
+ background: #ffffff;
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 8px;
19
51
  }
20
52
 
21
53
  .message {
22
54
  display: flex;
23
- margin-bottom: 10px;
24
55
  }
25
56
 
26
- .message.user {
27
- justify-content: flex-end;
57
+ .user {
58
+ margin-left: auto;
28
59
  }
29
60
 
30
- .message.system {
31
- justify-content: flex-start;
61
+ .assistant,
62
+ .system {
63
+ margin-right: auto;
32
64
  }
33
65
 
34
66
  .bubble {
35
- max-width: 60%;
36
- padding: 10px 15px;
37
- border-radius: 15px;
67
+ max-width: 85%;
68
+ padding: 10px 12px;
69
+ border-radius: 12px;
70
+ line-height: 1.4;
71
+ word-break: break-word;
72
+ white-space: pre-wrap;
38
73
  font-size: 14px;
39
- line-height: 1.5;
40
- position: relative;
41
- word-wrap: break-word;
42
- }
43
-
44
- .message.user .bubble {
45
- background: #007bff;
46
- color: #fff;
47
- }
48
-
49
- .message.system .bubble {
50
- background: #e9ecef;
51
- color: #333;
52
- }
53
-
54
- .bubble::after {
55
- content: '';
56
- position: absolute;
57
- width: 0;
58
- height: 0;
59
- border-style: solid;
60
74
  }
61
75
 
62
- .message.user .bubble::after {
63
- right: -10px;
64
- top: 50%;
65
- border-width: 10px 0 10px 10px;
66
- border-color: transparent transparent transparent #007bff;
67
- transform: translateY(-50%);
76
+ .user .bubble {
77
+ background: #2463eb;
78
+ color: #ffffff;
68
79
  }
69
80
 
70
- .message.system .bubble::after {
71
- left: -10px;
72
- top: 50%;
73
- border-width: 10px 10px 10px 0;
74
- border-color: transparent #e9ecef transparent transparent;
75
- transform: translateY(-50%);
81
+ .assistant .bubble,
82
+ .system .bubble {
83
+ background: #edf2fa;
84
+ color: #182230;
76
85
  }
77
86
 
78
87
  .inputbox {
79
88
  display: flex;
80
89
  align-items: center;
90
+ gap: 8px;
81
91
  padding: 10px;
82
- border-top: 1px solid #ddd;
83
- background: #fff;
92
+ border-top: 1px solid #d1d7e0;
93
+ background: #f7f9fc;
94
+ position: relative;
84
95
  }
85
96
 
86
97
  .inputbox input {
87
98
  flex: 1;
88
- padding: 10px;
89
- border: 1px solid #ddd;
90
- border-radius: 20px;
99
+ border: 1px solid #c3cedf;
100
+ border-radius: 999px;
101
+ padding: 10px 14px;
91
102
  font-size: 14px;
92
103
  }
93
104
 
94
- .emojiicon, .sendicon {
95
- margin-left: 10px;
105
+ .inputbox input:disabled {
106
+ cursor: not-allowed;
107
+ opacity: 0.75;
108
+ background: #f1f4f9;
109
+ }
110
+
111
+ .iconButton {
112
+ width: 36px;
113
+ height: 36px;
114
+ display: inline-flex;
115
+ align-items: center;
116
+ justify-content: center;
117
+ border: 1px solid #c3cedf;
118
+ border-radius: 999px;
119
+ background: #ffffff;
96
120
  cursor: pointer;
97
- color: whitesmoke;
98
121
  }
99
122
 
100
- .emojiicon {
101
- font-size: 20px;
123
+ .iconButton:disabled {
124
+ cursor: not-allowed;
125
+ opacity: 0.6;
102
126
  }
103
127
 
128
+ .emojiicon,
104
129
  .sendicon {
105
- font-size: 20px;
106
- }
130
+ color: #2f4f95;
131
+ font-size: 16px;
132
+ }
133
+
134
+ .emojiPicker {
135
+ position: absolute;
136
+ right: 44px;
137
+ bottom: 52px;
138
+ z-index: 20;
139
+ }
@@ -1,9 +1,12 @@
1
1
  import React from "react";
2
- interface ChatBotProps {
3
- openaiOrgID: string;
4
- openaiProjectKey: string;
5
- openaiAPIKey: string;
2
+ import { type ChatMessage, type ChatbotClientOptions, type ChatbotUsage } from "./client.js";
3
+ export interface ChatBotProps extends ChatbotClientOptions {
4
+ initialMessages?: ChatMessage[];
5
+ systemPrompt?: string;
6
+ placeholder?: string;
7
+ title?: string;
8
+ onUsageChange?: (usage: ChatbotUsage) => void;
9
+ onAuthRequired?: () => void;
6
10
  }
7
11
  export default function ChatBot(props: React.PropsWithChildren<ChatBotProps>): React.ReactElement;
8
- export {};
9
12
  //# sourceMappingURL=chatbot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chatbot.d.ts","sourceRoot":"","sources":["../src/chatbot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8C,MAAM,OAAO,CAAC;AAiBnE,UAAU,YAAY;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,KAAK,EAAE,KAAK,CAAC,iBAAiB,CAAC,YAAY,CAAC,GAC3C,KAAK,CAAC,YAAY,CAyNpB"}
1
+ {"version":3,"file":"chatbot.d.ts","sourceRoot":"","sources":["../src/chatbot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoE,MAAM,OAAO,CAAC;AAIzF,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAUrB,MAAM,WAAW,YAAa,SAAQ,oBAAoB;IACxD,eAAe,CAAC,EAAE,WAAW,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAoBD,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,KAAK,EAAE,KAAK,CAAC,iBAAiB,CAAC,YAAY,CAAC,GAC3C,KAAK,CAAC,YAAY,CA2NpB"}
@@ -39,181 +39,161 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = ChatBot;
40
40
  const jsx_runtime_1 = require("react/jsx-runtime");
41
41
  const react_1 = require("react");
42
+ const fa_1 = require("react-icons/fa");
43
+ const chatbot_module_css_1 = __importDefault(require("./styles/chatbot.module.css"));
44
+ const client_js_1 = require("./client.js");
42
45
  const EmojiPicker = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require("emoji-picker-react/dist/emoji-picker-react.esm.js"))).then((module) => ({
43
- default: module.EmojiPicker, // <--- force the correct component export
46
+ default: module.EmojiPicker,
44
47
  })));
45
- const fa_1 = require("react-icons/fa");
46
- const openai_1 = __importDefault(require("openai"));
47
- const chatbot_module_css_1 = __importDefault(require("./styles/chatbot.module.css")); // Import a CSS file for styling
48
+ const DEFAULT_TITLE = "Plasius Chatbot";
49
+ const DEFAULT_PLACEHOLDER = "Ask Plasius something...";
50
+ const DEFAULT_SYSTEM_PROMPT = "You are the Plasius assistant. Keep responses concise, practical, and factual.";
51
+ function statusMessage(state, usage) {
52
+ if (state === "loading")
53
+ return "Checking access...";
54
+ if (state === "signed_out")
55
+ return "Sign in to use chatbot.";
56
+ if (state === "limit_reached") {
57
+ if (usage) {
58
+ return `Demo limit reached (${usage.used}/${usage.limit} messages).`;
59
+ }
60
+ return "Demo limit reached.";
61
+ }
62
+ if (state === "error")
63
+ return "Chatbot is currently unavailable.";
64
+ return "";
65
+ }
48
66
  function ChatBot(props) {
49
- const [messages, setMessages] = (0, react_1.useState)([]);
67
+ const [messages, setMessages] = (0, react_1.useState)(props.initialMessages ?? []);
50
68
  const [input, setInput] = (0, react_1.useState)("");
51
69
  const [showEmojiPicker, setShowEmojiPicker] = (0, react_1.useState)(false);
52
- const openai = new openai_1.default({
53
- apiKey: props.openaiAPIKey,
54
- project: props.openaiProjectKey,
55
- organization: props.openaiOrgID,
56
- dangerouslyAllowBrowser: true,
57
- });
58
- const chat = async (msgs, callback) => {
59
- try {
60
- const value = await openai.chat.completions.create({
61
- model: "gpt-o1",
62
- messages: msgs,
63
- });
64
- value.choices.forEach((choice) => {
65
- callback({ content: choice.message.content ?? "", role: "system" });
66
- });
67
- }
68
- catch (err) {
69
- console.error("chat() failed", err);
70
- }
71
- };
72
- const objects = window.location.origin + "/api/objects/list";
73
- const decorations = window.location.origin + "/api/decorations/list";
74
- const locations = window.location.origin + "/api/locations/list";
75
- const surfaces = window.location.origin + "/api/surfaces/list";
70
+ const [isSending, setIsSending] = (0, react_1.useState)(false);
71
+ const [state, setState] = (0, react_1.useState)("loading");
72
+ const [usage, setUsage] = (0, react_1.useState)(null);
73
+ const [errorMessage, setErrorMessage] = (0, react_1.useState)(null);
74
+ const clientOptions = (0, react_1.useMemo)(() => ({
75
+ endpoint: props.endpoint,
76
+ credentials: props.credentials,
77
+ headers: props.headers,
78
+ fetchFn: props.fetchFn,
79
+ csrfCookieName: props.csrfCookieName,
80
+ csrfHeaderName: props.csrfHeaderName,
81
+ bootstrapCsrf: props.bootstrapCsrf,
82
+ }), [
83
+ props.endpoint,
84
+ props.credentials,
85
+ props.headers,
86
+ props.fetchFn,
87
+ props.csrfCookieName,
88
+ props.csrfHeaderName,
89
+ props.bootstrapCsrf,
90
+ ]);
91
+ const applyUsage = (0, react_1.useCallback)((nextUsage) => {
92
+ setUsage(nextUsage);
93
+ props.onUsageChange?.(nextUsage);
94
+ setState(nextUsage.exhausted ? "limit_reached" : "ready");
95
+ }, [props.onUsageChange]);
76
96
  (0, react_1.useEffect)(() => {
77
- void chat([
78
- {
79
- role: "system",
80
- content: `You are a game designer, you are responsible for helping build the world and game mechanics, adjusting the game to be more fun for the player playing,
81
- using your knowledge of gameplay mechanics and world building you are going to help assign objects to the map.
82
-
83
- You can find the list of objects from the following url: ${objects}
84
- You can find the list of decorations from the following url: ${decorations}
85
- You can find the list of surfaces from the following url: ${surfaces}
86
-
87
- Each location is a hexagon with a radius of 10 meters and 10m tall (allowing for locations to be on top of each other!), and a q and r coordinate system.
88
- The q coordinate is the horizontal axis, and the r coordinate is the vertical axis. The center of the hexagon is at (0, 0),
89
- and the corners are at (5, 8.66), (10, 0), (5, -8.66), (-5, -8.66), (-10, 0), and (-5, 8.66).
90
- Adjacent hexagons are at (q + 1, r), (q - 1, r), (q, r + 1), (q, r - 1), (q + 1, r - 1), and (q - 1, r + 1) and you should try and
91
- coordinate over the hexagons to make sure the objects are placed in a way that makes sense.
92
-
93
- Try and align surfaces, decorations, and objects to a 1m size hexagon when placing items so they align to each other in the world,
94
- but avoid overlapping the objects with each other, unless they are meant to overlap (like a chair under a table, or a tree in a bush).
95
- Surfaces should not overlap with each other, and should be placed in a way that makes sense for the location,
96
- such as a road should be continuous and have purpose, to or from somewhere,
97
- use the locations map to identify good roads, forests, mountains, lakes and oceans locations.
98
-
99
- for each prompt the user gives you, will relate to a specific location in the game world, you should take in the location,
100
- some basic information about the users expectations for the location and return a json object with the following fields:
101
- {
102
- "location": {
103
- "r": "number", // 10m hexagon radius
104
- "q": "number", // 10m hexagon radius
105
- "elevation": "number",
106
- "name": "string",
107
- "description": "string",
108
- "type": "string",
109
-
110
- "surfaces": [{
111
- "location": {
112
- "q": "number", // 1m hexagon radius
113
- "r": "number", // 1m hexagon radius
114
- "elevation": "number"
115
- },
116
- "name": "string",
117
- "type": "string",
118
- "description": "string",
119
- "url": "string",
120
- "image": "string",
121
- "rotation": "number",
122
- "color": "string"
123
- }],
124
- "decorations": [
125
- {
126
- "name": "string",
127
- "type": "string",
128
- "description": "string",
129
- "url": "string",
130
- "image": "string",
131
- "rotation": "number",
132
- "scale": "number",
133
- "color": "string",
134
- "location": {
135
- "x": "number",
136
- "y": "number",
137
- "z": "number"
138
- }
139
- }
140
- ],
141
- "objects": [
142
- {
143
- "name": "string",
144
- "type": "string",
145
- "description": "string",
146
- "url": "string",
147
- "image": "string",
148
- "rotation": "number",
149
- "scale": "number",
150
- "color": "string",
151
- "location": {
152
- "x": "number",
153
- "y": "number",
154
- "z": "number"
155
- }
156
- }
157
- ]
158
- }
159
- }
160
-
161
- You can find the list of populated locations from the following url: ${locations} for reference and to allow you to be more creative in your assignments.
162
- If your current location is in the list, then take the current objects and decorations into account when placing the new objects, and remove or replace the old ones.`,
163
- },
164
- ], (arg) => {
165
- setMessages((prev) => [...prev, arg]);
166
- });
167
- }, []);
168
- const handleSend = async () => {
169
- if (input.trim()) {
170
- setMessages((prev) => [...prev, { content: input, role: "user" }]);
171
- setInput("");
172
- setShowEmojiPicker(false);
97
+ let active = true;
98
+ const loadUsage = async () => {
99
+ setState("loading");
100
+ setErrorMessage(null);
173
101
  try {
174
- const value = await openai.chat.completions.create({
175
- model: "gpt-4o-mini",
176
- messages: [{ content: input, role: "user" }],
177
- });
178
- value.choices.forEach((choice) => {
179
- setMessages((prev) => [
180
- ...prev,
181
- { content: choice.message.content ?? "", role: "system" },
182
- ]);
183
- });
102
+ const result = await (0, client_js_1.getChatbotUsage)(clientOptions);
103
+ if (!active)
104
+ return;
105
+ applyUsage(result.usage);
184
106
  }
185
- catch (err) {
186
- console.error("handleSend() failed", err);
107
+ catch (error) {
108
+ if (!active)
109
+ return;
110
+ if (error instanceof client_js_1.ChatbotClientError && error.status === 401) {
111
+ setState("signed_out");
112
+ setErrorMessage("Sign in to start chatting.");
113
+ props.onAuthRequired?.();
114
+ return;
115
+ }
116
+ setState("error");
117
+ setErrorMessage(error instanceof Error ? error.message : "Failed to load chatbot.");
187
118
  }
188
- }
189
- };
119
+ };
120
+ void loadUsage();
121
+ return () => {
122
+ active = false;
123
+ };
124
+ }, [applyUsage, clientOptions, props.onAuthRequired]);
125
+ const sendDisabled = isSending ||
126
+ state === "loading" ||
127
+ state === "signed_out" ||
128
+ state === "limit_reached" ||
129
+ !input.trim();
190
130
  const handleEmojiClick = (emojiData) => {
191
131
  setInput((prev) => prev + (emojiData.emoji ?? ""));
192
132
  };
193
- const contentToString = (content) => {
194
- if (typeof content === "string" || content == null)
195
- return content ?? "";
196
- if (Array.isArray(content)) {
197
- return content
198
- .map((part) => {
199
- if (typeof part === "string")
200
- return part;
201
- if (typeof part === "object" &&
202
- part !== null &&
203
- Object.prototype.hasOwnProperty.call(part, "text")) {
204
- const text = part.text;
205
- return typeof text === "string" ? text : "";
133
+ const handleSend = (0, react_1.useCallback)(async () => {
134
+ const message = input.trim();
135
+ if (!message || sendDisabled)
136
+ return;
137
+ const userMessage = { role: "user", content: message };
138
+ const nextHistory = [...messages, userMessage].slice(-20);
139
+ setMessages((prev) => [...prev, userMessage]);
140
+ setInput("");
141
+ setShowEmojiPicker(false);
142
+ setIsSending(true);
143
+ setErrorMessage(null);
144
+ try {
145
+ const response = await (0, client_js_1.sendChatbotMessage)({
146
+ message,
147
+ history: nextHistory,
148
+ systemPrompt: props.systemPrompt ?? DEFAULT_SYSTEM_PROMPT,
149
+ }, clientOptions);
150
+ setMessages((prev) => [
151
+ ...prev,
152
+ { role: "assistant", content: response.reply },
153
+ ]);
154
+ applyUsage(response.usage);
155
+ }
156
+ catch (error) {
157
+ if (error instanceof client_js_1.ChatbotClientError) {
158
+ if (error.status === 401) {
159
+ setState("signed_out");
160
+ setErrorMessage("You must be signed in to use chatbot.");
161
+ props.onAuthRequired?.();
162
+ return;
163
+ }
164
+ if (error.status === 429) {
165
+ if (error.usage) {
166
+ applyUsage(error.usage);
167
+ }
168
+ else {
169
+ setState("limit_reached");
170
+ }
171
+ setErrorMessage("You reached the 10 message demo limit.");
172
+ return;
206
173
  }
207
- return "";
208
- })
209
- .join("");
174
+ setState("error");
175
+ setErrorMessage(error.message);
176
+ return;
177
+ }
178
+ setState("error");
179
+ setErrorMessage(error instanceof Error ? error.message : "Message failed.");
210
180
  }
211
- return "";
212
- };
213
- return ((0, jsx_runtime_1.jsxs)("div", { className: chatbot_module_css_1.default.chatbotcontainer, children: [(0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.messagesbox, children: messages.map((msg, index) => ((0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.message + ` ${chatbot_module_css_1.default[msg.role]}`, children: (0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.bubble, children: contentToString(msg.content) }) }, index))) }), (0, jsx_runtime_1.jsxs)("div", { className: chatbot_module_css_1.default.inputbox, children: [(0, jsx_runtime_1.jsx)("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), onKeyUp: async (e) => {
214
- if (e.key === "Enter" && e.shiftKey === false) {
181
+ finally {
182
+ setIsSending(false);
183
+ }
184
+ }, [
185
+ applyUsage,
186
+ clientOptions,
187
+ input,
188
+ messages,
189
+ props.onAuthRequired,
190
+ props.systemPrompt,
191
+ sendDisabled,
192
+ ]);
193
+ return ((0, jsx_runtime_1.jsxs)("div", { className: chatbot_module_css_1.default.chatbotcontainer, children: [(0, jsx_runtime_1.jsxs)("div", { className: chatbot_module_css_1.default.header, children: [(0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.title, children: props.title ?? DEFAULT_TITLE }), (0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.usage, children: usage ? `${usage.used}/${usage.limit} used` : "No usage data" })] }), (state !== "ready" || errorMessage) && ((0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.notice, children: errorMessage ?? statusMessage(state, usage) })), (0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.messagesbox, children: messages.map((msg, index) => ((0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.message, children: (0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default[msg.role], children: (0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.bubble, children: msg.content }) }) }, `${msg.role}-${index}`))) }), (0, jsx_runtime_1.jsxs)("div", { className: chatbot_module_css_1.default.inputbox, children: [(0, jsx_runtime_1.jsx)("input", { type: "text", value: input, disabled: sendDisabled, onChange: (event) => setInput(event.target.value), onKeyUp: async (event) => {
194
+ if (event.key === "Enter" && !event.shiftKey) {
215
195
  await handleSend();
216
- e.stopPropagation();
196
+ event.stopPropagation();
217
197
  }
218
- }, placeholder: "Type a message..." }), (0, jsx_runtime_1.jsx)(fa_1.FaSmile, { onClick: () => setShowEmojiPicker(!showEmojiPicker), className: chatbot_module_css_1.default.emojiicon }), showEmojiPicker && ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: (0, jsx_runtime_1.jsx)("div", { children: "Loading emoji picker..." }), children: (0, jsx_runtime_1.jsx)(EmojiPicker, { onEmojiClick: handleEmojiClick }) })), (0, jsx_runtime_1.jsx)(fa_1.FaPaperPlane, { onClick: handleSend, className: chatbot_module_css_1.default.sendicon })] })] }));
198
+ }, placeholder: props.placeholder ?? DEFAULT_PLACEHOLDER }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: chatbot_module_css_1.default.iconButton, onClick: () => setShowEmojiPicker((current) => !current), disabled: state === "signed_out" || state === "limit_reached", "aria-label": "Open emoji picker", children: (0, jsx_runtime_1.jsx)(fa_1.FaSmile, { className: chatbot_module_css_1.default.emojiicon }) }), showEmojiPicker && ((0, jsx_runtime_1.jsx)("div", { className: chatbot_module_css_1.default.emojiPicker, children: (0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: (0, jsx_runtime_1.jsx)("div", { children: "Loading emoji picker..." }), children: (0, jsx_runtime_1.jsx)(EmojiPicker, { onEmojiClick: handleEmojiClick }) }) })), (0, jsx_runtime_1.jsx)("button", { type: "button", className: chatbot_module_css_1.default.iconButton, onClick: () => void handleSend(), disabled: sendDisabled, "aria-label": "Send message", children: (0, jsx_runtime_1.jsx)(fa_1.FaPaperPlane, { className: chatbot_module_css_1.default.sendicon }) })] })] }));
219
199
  }
@@ -0,0 +1,41 @@
1
+ export type ChatRole = "system" | "user" | "assistant";
2
+ export interface ChatMessage {
3
+ role: ChatRole;
4
+ content: string;
5
+ }
6
+ export interface ChatbotUsage {
7
+ limit: number;
8
+ used: number;
9
+ remaining: number;
10
+ exhausted: boolean;
11
+ }
12
+ export interface ChatbotReply {
13
+ reply: string;
14
+ model: string;
15
+ usage: ChatbotUsage;
16
+ }
17
+ export interface ChatbotUsageResponse {
18
+ usage: ChatbotUsage;
19
+ }
20
+ export interface ChatbotClientOptions {
21
+ endpoint?: string;
22
+ credentials?: RequestCredentials;
23
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
24
+ fetchFn?: typeof fetch;
25
+ csrfCookieName?: string;
26
+ csrfHeaderName?: string;
27
+ bootstrapCsrf?: boolean;
28
+ }
29
+ export declare class ChatbotClientError extends Error {
30
+ status: number;
31
+ code?: string;
32
+ usage?: ChatbotUsage;
33
+ constructor(status: number, message: string, code?: string, usage?: ChatbotUsage);
34
+ }
35
+ export declare function getChatbotUsage(options?: ChatbotClientOptions): Promise<ChatbotUsageResponse>;
36
+ export declare function sendChatbotMessage(payload: {
37
+ message: string;
38
+ history?: ChatMessage[];
39
+ systemPrompt?: string;
40
+ }, options?: ChatbotClientOptions): Promise<ChatbotReply>;
41
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAQD,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,YAAY,CAAC;gBAET,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY;CAOjF;AAiHD,wBAAsB,eAAe,CACnC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,oBAAoB,CAAC,CA4B/B;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE;IACP,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,YAAY,CAAC,CAiDvB"}