@plasius/chatbot 1.0.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/CHANGELOG.md +66 -0
- package/CODE_OF_CONDUCT.md +79 -0
- package/CONTRIBUTORS.md +27 -0
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/SECURITY.md +17 -0
- package/dist/chatbot.d.ts +9 -0
- package/dist/chatbot.d.ts.map +1 -0
- package/dist/chatbot.js +180 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/inputbox.d.ts +7 -0
- package/dist/inputbox.d.ts.map +1 -0
- package/dist/inputbox.js +4 -0
- package/dist/renderpriority.d.ts +8 -0
- package/dist/renderpriority.d.ts.map +1 -0
- package/dist/renderpriority.js +8 -0
- package/dist/styles/chatbot.module.css +106 -0
- package/dist-cjs/chatbot.d.ts +9 -0
- package/dist-cjs/chatbot.d.ts.map +1 -0
- package/dist-cjs/chatbot.js +219 -0
- package/dist-cjs/index.d.ts +2 -0
- package/dist-cjs/index.d.ts.map +1 -0
- package/dist-cjs/index.js +8 -0
- package/dist-cjs/inputbox.d.ts +7 -0
- package/dist-cjs/inputbox.d.ts.map +1 -0
- package/dist-cjs/inputbox.js +7 -0
- package/dist-cjs/renderpriority.d.ts +8 -0
- package/dist-cjs/renderpriority.d.ts.map +1 -0
- package/dist-cjs/renderpriority.js +10 -0
- package/dist-cjs/styles/chatbot.module.css +106 -0
- package/docs/adrs/adr-0001-chatbot-package-scope.md +21 -0
- package/docs/adrs/adr-0002-public-repo-governance.md +24 -0
- package/docs/adrs/adr-template.md +35 -0
- package/legal/CLA-REGISTRY.csv +1 -0
- package/legal/CLA.md +22 -0
- package/legal/CORPORATE_CLA.md +57 -0
- package/legal/INDIVIDUAL_CLA.md +91 -0
- package/package.json +116 -0
- package/src/chatbot.tsx +306 -0
- package/src/global.d.ts +9 -0
- package/src/index.ts +1 -0
- package/src/inputbox.tsx +11 -0
- package/src/renderpriority.ts +8 -0
- package/src/styles/chatbot.module.css +106 -0
- package/src/types/emoji-picker-react-esm.d.ts +3 -0
package/package.json
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@plasius/chatbot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenAI Chatbot",
|
|
5
|
+
"main": "./dist-cjs/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"private": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc --build --listEmittedFiles && rsync -av --include '*/' --include '*.module.css' --exclude '*' src/ dist/ || true && npm run build:cjs",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"test:coverage": "vitest run --coverage",
|
|
14
|
+
"test:coverage:watch": "vitest --coverage",
|
|
15
|
+
"clean": "rimraf dist-cjs dist tsconfig.tsbuildinfo",
|
|
16
|
+
"reset:clean": "rm -rf node_modules package-lock.json && npm run clean",
|
|
17
|
+
"audit:ts": "tsc --noEmit --pretty",
|
|
18
|
+
"audit:eslint": "eslint \"{src,apps,packages}/**/*.{ts,tsx}\" --max-warnings=0 --ext .ts,.tsx",
|
|
19
|
+
"audit:deps": "depcheck --skip-missing=true",
|
|
20
|
+
"audit:npm": "npm audit --audit-level=moderate || true",
|
|
21
|
+
"audit:test": "vitest run --coverage",
|
|
22
|
+
"audit:all": "npm-run-all -l audit:ts audit:eslint audit:deps audit:npm audit:test",
|
|
23
|
+
"build:cjs": "tsc -p tsconfig.json --module commonjs --moduleResolution node --outDir dist-cjs --tsBuildInfoFile dist-cjs/tsconfig.tsbuildinfo --listEmittedFiles && rsync -av --include '*/' --include '*.module.css' --exclude '*' src/ dist-cjs/ || true",
|
|
24
|
+
"lint": "eslint .",
|
|
25
|
+
"prepare": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"author": "Plasius LTD <development@plasius.co.uk>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@plasius/entity-manager": "^1.0.4",
|
|
31
|
+
"@plasius/error": "^1.0.0",
|
|
32
|
+
"@plasius/profile": "^1.0.0",
|
|
33
|
+
"@plasius/schema": "^1.0.0",
|
|
34
|
+
"emoji-picker-react": "^4.12.2",
|
|
35
|
+
"react": "19.1.0",
|
|
36
|
+
"react-dom": "19.1.0",
|
|
37
|
+
"react-router-dom": "^7.6.2",
|
|
38
|
+
"react-icons": "^5.5.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"openai": "^5.19.1",
|
|
42
|
+
"react": "^19.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@testing-library/react": "^16.3.0",
|
|
46
|
+
"@types/react": "^19.1.8",
|
|
47
|
+
"@types/uuid": "^10.0.0",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
49
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
50
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
51
|
+
"ajv": "^6.12.6",
|
|
52
|
+
"depcheck": "^1.4.7",
|
|
53
|
+
"eslint": "^9.33.0",
|
|
54
|
+
"npm-run-all": "^4.1.5",
|
|
55
|
+
"openai": "^5.19.1",
|
|
56
|
+
"react": "19.1.0",
|
|
57
|
+
"react-dom": "19.1.0",
|
|
58
|
+
"tsx": "^4.20.3",
|
|
59
|
+
"typescript": "^5.8.3",
|
|
60
|
+
"vitest": "^3.2.4",
|
|
61
|
+
"zod": "^4.1.5"
|
|
62
|
+
},
|
|
63
|
+
"overrides": {
|
|
64
|
+
"react": "19.1.0",
|
|
65
|
+
"react-dom": "19.1.0"
|
|
66
|
+
},
|
|
67
|
+
"sideEffects": [
|
|
68
|
+
"*.css"
|
|
69
|
+
],
|
|
70
|
+
"exports": {
|
|
71
|
+
".": {
|
|
72
|
+
"types": "./dist/index.d.ts",
|
|
73
|
+
"import": "./dist/index.js",
|
|
74
|
+
"require": "./dist-cjs/index.js"
|
|
75
|
+
},
|
|
76
|
+
"./package.json": "./package.json"
|
|
77
|
+
},
|
|
78
|
+
"module": "./dist/index.js",
|
|
79
|
+
"files": [
|
|
80
|
+
"dist",
|
|
81
|
+
"dist-cjs",
|
|
82
|
+
"src",
|
|
83
|
+
"README.md",
|
|
84
|
+
"CHANGELOG.md",
|
|
85
|
+
"LICENSE",
|
|
86
|
+
"SECURITY.md",
|
|
87
|
+
"CODE_OF_CONDUCT.md",
|
|
88
|
+
"CONTRIBUTORS.md",
|
|
89
|
+
"docs",
|
|
90
|
+
"legal"
|
|
91
|
+
],
|
|
92
|
+
"repository": {
|
|
93
|
+
"type": "git",
|
|
94
|
+
"url": "git+https://github.com/Plasius-LTD/chatbot.git"
|
|
95
|
+
},
|
|
96
|
+
"bugs": {
|
|
97
|
+
"url": "https://github.com/Plasius-LTD/chatbot/issues"
|
|
98
|
+
},
|
|
99
|
+
"homepage": "https://github.com/Plasius-LTD/chatbot#readme",
|
|
100
|
+
"publishConfig": {
|
|
101
|
+
"access": "public"
|
|
102
|
+
},
|
|
103
|
+
"funding": [
|
|
104
|
+
{
|
|
105
|
+
"type": "patreon",
|
|
106
|
+
"url": "https://www.patreon.com/c/plasiusltd/membership"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"type": "github",
|
|
110
|
+
"url": "https://github.com/sponsors/Plasius-LTD"
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
"engines": {
|
|
114
|
+
"node": ">=22.12"
|
|
115
|
+
}
|
|
116
|
+
}
|
package/src/chatbot.tsx
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import React, { lazy, Suspense, useState, useEffect } from "react";
|
|
2
|
+
import type { EmojiClickData } from "emoji-picker-react";
|
|
3
|
+
|
|
4
|
+
const EmojiPicker = lazy(() =>
|
|
5
|
+
import("emoji-picker-react/dist/emoji-picker-react.esm.js").then(
|
|
6
|
+
(module) => ({
|
|
7
|
+
default: module.EmojiPicker, // <--- force the correct component export
|
|
8
|
+
})
|
|
9
|
+
)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
import { FaPaperPlane, FaSmile } from "react-icons/fa";
|
|
13
|
+
|
|
14
|
+
import OpenAI from "openai";
|
|
15
|
+
|
|
16
|
+
import styles from "./styles/chatbot.module.css"; // Import a CSS file for styling
|
|
17
|
+
|
|
18
|
+
interface ChatBotProps {
|
|
19
|
+
openaiOrgID: string;
|
|
20
|
+
openaiProjectKey: string;
|
|
21
|
+
openaiAPIKey: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function ChatBot(
|
|
25
|
+
props: React.PropsWithChildren<ChatBotProps>
|
|
26
|
+
): React.ReactElement {
|
|
27
|
+
const [messages, setMessages] = useState<
|
|
28
|
+
OpenAI.Chat.Completions.ChatCompletionMessageParam[]
|
|
29
|
+
>([]);
|
|
30
|
+
const [input, setInput] = useState<string>("");
|
|
31
|
+
const [showEmojiPicker, setShowEmojiPicker] = useState<boolean>(false);
|
|
32
|
+
|
|
33
|
+
const openai = new OpenAI({
|
|
34
|
+
apiKey: props.openaiAPIKey,
|
|
35
|
+
project: props.openaiProjectKey,
|
|
36
|
+
organization: props.openaiOrgID,
|
|
37
|
+
dangerouslyAllowBrowser: true,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const chat = async (
|
|
41
|
+
msgs: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
|
42
|
+
callback: (arg: OpenAI.Chat.Completions.ChatCompletionMessageParam) => void
|
|
43
|
+
): Promise<void> => {
|
|
44
|
+
try {
|
|
45
|
+
const value = await openai.chat.completions.create({
|
|
46
|
+
model: "gpt-o1",
|
|
47
|
+
messages: msgs,
|
|
48
|
+
});
|
|
49
|
+
value.choices.forEach((choice: OpenAI.ChatCompletion.Choice) => {
|
|
50
|
+
callback({ content: choice.message.content ?? "", role: "system" });
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error("chat() failed", err);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const objects = window.location.origin + "/api/objects/list";
|
|
58
|
+
const decorations = window.location.origin + "/api/decorations/list";
|
|
59
|
+
const locations = window.location.origin + "/api/locations/list";
|
|
60
|
+
const surfaces = window.location.origin + "/api/surfaces/list";
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
void chat(
|
|
64
|
+
[
|
|
65
|
+
{
|
|
66
|
+
role: "system",
|
|
67
|
+
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,
|
|
68
|
+
using your knowledge of gameplay mechanics and world building you are going to help assign objects to the map.
|
|
69
|
+
|
|
70
|
+
You can find the list of objects from the following url: ${objects}
|
|
71
|
+
You can find the list of decorations from the following url: ${decorations}
|
|
72
|
+
You can find the list of surfaces from the following url: ${surfaces}
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
The q coordinate is the horizontal axis, and the r coordinate is the vertical axis. The center of the hexagon is at (0, 0),
|
|
76
|
+
and the corners are at (5, 8.66), (10, 0), (5, -8.66), (-5, -8.66), (-10, 0), and (-5, 8.66).
|
|
77
|
+
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
|
|
78
|
+
coordinate over the hexagons to make sure the objects are placed in a way that makes sense.
|
|
79
|
+
|
|
80
|
+
Try and align surfaces, decorations, and objects to a 1m size hexagon when placing items so they align to each other in the world,
|
|
81
|
+
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).
|
|
82
|
+
Surfaces should not overlap with each other, and should be placed in a way that makes sense for the location,
|
|
83
|
+
such as a road should be continuous and have purpose, to or from somewhere,
|
|
84
|
+
use the locations map to identify good roads, forests, mountains, lakes and oceans locations.
|
|
85
|
+
|
|
86
|
+
for each prompt the user gives you, will relate to a specific location in the game world, you should take in the location,
|
|
87
|
+
some basic information about the users expectations for the location and return a json object with the following fields:
|
|
88
|
+
{
|
|
89
|
+
"location": {
|
|
90
|
+
"r": "number", // 10m hexagon radius
|
|
91
|
+
"q": "number", // 10m hexagon radius
|
|
92
|
+
"elevation": "number",
|
|
93
|
+
"name": "string",
|
|
94
|
+
"description": "string",
|
|
95
|
+
"type": "string",
|
|
96
|
+
|
|
97
|
+
"surfaces": [{
|
|
98
|
+
"location": {
|
|
99
|
+
"q": "number", // 1m hexagon radius
|
|
100
|
+
"r": "number", // 1m hexagon radius
|
|
101
|
+
"elevation": "number"
|
|
102
|
+
},
|
|
103
|
+
"name": "string",
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "string",
|
|
106
|
+
"url": "string",
|
|
107
|
+
"image": "string",
|
|
108
|
+
"rotation": "number",
|
|
109
|
+
"color": "string"
|
|
110
|
+
}],
|
|
111
|
+
"decorations": [
|
|
112
|
+
{
|
|
113
|
+
"name": "string",
|
|
114
|
+
"type": "string",
|
|
115
|
+
"description": "string",
|
|
116
|
+
"url": "string",
|
|
117
|
+
"image": "string",
|
|
118
|
+
"rotation": "number",
|
|
119
|
+
"scale": "number",
|
|
120
|
+
"color": "string",
|
|
121
|
+
"location": {
|
|
122
|
+
"x": "number",
|
|
123
|
+
"y": "number",
|
|
124
|
+
"z": "number"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"objects": [
|
|
129
|
+
{
|
|
130
|
+
"name": "string",
|
|
131
|
+
"type": "string",
|
|
132
|
+
"description": "string",
|
|
133
|
+
"url": "string",
|
|
134
|
+
"image": "string",
|
|
135
|
+
"rotation": "number",
|
|
136
|
+
"scale": "number",
|
|
137
|
+
"color": "string",
|
|
138
|
+
"location": {
|
|
139
|
+
"x": "number",
|
|
140
|
+
"y": "number",
|
|
141
|
+
"z": "number"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
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.
|
|
149
|
+
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.`,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
(arg: OpenAI.Chat.Completions.ChatCompletionMessageParam) => {
|
|
153
|
+
setMessages((prev) => [...prev, arg]);
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
const handleSend = async (): Promise<void> => {
|
|
159
|
+
if (input.trim()) {
|
|
160
|
+
setMessages((prev) => [...prev, { content: input, role: "user" }]);
|
|
161
|
+
setInput("");
|
|
162
|
+
setShowEmojiPicker(false);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const value = await openai.chat.completions.create({
|
|
166
|
+
model: "gpt-4o-mini",
|
|
167
|
+
messages: [{ content: input, role: "user" }],
|
|
168
|
+
});
|
|
169
|
+
value.choices.forEach((choice: OpenAI.ChatCompletion.Choice) => {
|
|
170
|
+
setMessages((prev) => [
|
|
171
|
+
...prev,
|
|
172
|
+
{ content: choice.message.content ?? "", role: "system" },
|
|
173
|
+
]);
|
|
174
|
+
});
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.error("handleSend() failed", err);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleEmojiClick = (emojiData: EmojiClickData): void => {
|
|
182
|
+
setInput((prev) => prev + (emojiData.emoji ?? ""));
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const contentToString = (
|
|
186
|
+
content: OpenAI.Chat.Completions.ChatCompletionMessageParam["content"]
|
|
187
|
+
): string => {
|
|
188
|
+
if (typeof content === "string" || content == null) return content ?? "";
|
|
189
|
+
if (Array.isArray(content)) {
|
|
190
|
+
return content
|
|
191
|
+
.map((part: unknown) => {
|
|
192
|
+
if (typeof part === "string") return part;
|
|
193
|
+
if (
|
|
194
|
+
typeof part === "object" &&
|
|
195
|
+
part !== null &&
|
|
196
|
+
Object.prototype.hasOwnProperty.call(part, "text")
|
|
197
|
+
) {
|
|
198
|
+
const text = (part as Record<string, unknown>).text;
|
|
199
|
+
return typeof text === "string" ? text : "";
|
|
200
|
+
}
|
|
201
|
+
return "";
|
|
202
|
+
})
|
|
203
|
+
.join("");
|
|
204
|
+
}
|
|
205
|
+
return "";
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<div className={styles.chatbotcontainer}>
|
|
210
|
+
<div className={styles.messagesbox}>
|
|
211
|
+
{messages.map((msg, index) => (
|
|
212
|
+
<div key={index} className={styles.message + ` ${styles[msg.role]}`}>
|
|
213
|
+
<div className={styles.bubble}>{contentToString(msg.content)}</div>
|
|
214
|
+
</div>
|
|
215
|
+
))}
|
|
216
|
+
</div>
|
|
217
|
+
<div className={styles.inputbox}>
|
|
218
|
+
<input
|
|
219
|
+
type="text"
|
|
220
|
+
value={input}
|
|
221
|
+
onChange={(e) => setInput(e.target.value)}
|
|
222
|
+
onKeyUp={async (e) => {
|
|
223
|
+
if (e.key === "Enter" && e.shiftKey === false) {
|
|
224
|
+
await handleSend();
|
|
225
|
+
e.stopPropagation();
|
|
226
|
+
}
|
|
227
|
+
}}
|
|
228
|
+
placeholder="Type a message..."
|
|
229
|
+
/>
|
|
230
|
+
<FaSmile
|
|
231
|
+
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
|
|
232
|
+
className={styles.emojiicon}
|
|
233
|
+
/>
|
|
234
|
+
{showEmojiPicker && (
|
|
235
|
+
<Suspense fallback={<div>Loading emoji picker...</div>}>
|
|
236
|
+
<EmojiPicker onEmojiClick={handleEmojiClick} />
|
|
237
|
+
</Suspense>
|
|
238
|
+
)}
|
|
239
|
+
<FaPaperPlane onClick={handleSend} className={styles.sendicon} />
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
|
|
247
|
+
// Chatbot.tsx
|
|
248
|
+
import React, { useState } from 'react';
|
|
249
|
+
import { FaPaperPlane, FaSmile } from 'react-icons/fa';
|
|
250
|
+
import Picker, { IEmojiData } from 'emoji-picker-react';
|
|
251
|
+
|
|
252
|
+
interface Message {
|
|
253
|
+
text: string;
|
|
254
|
+
user: 'me' | 'bot';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const Chatbot: React.FC = () => {
|
|
258
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
259
|
+
const [input, setInput] = useState<string>('');
|
|
260
|
+
const [showEmojiPicker, setShowEmojiPicker] = useState<boolean>(false);
|
|
261
|
+
|
|
262
|
+
const handleSend = () => {
|
|
263
|
+
if (input.trim()) {
|
|
264
|
+
setMessages([...messages, { text: input, user: 'me' }]);
|
|
265
|
+
setInput('');
|
|
266
|
+
setShowEmojiPicker(false);
|
|
267
|
+
|
|
268
|
+
// Here you would also call the OpenAI API and handle the response
|
|
269
|
+
// Example:
|
|
270
|
+
// fetchOpenAIResponse(input).then(response => {
|
|
271
|
+
// setMessages([...messages, { text: input, user: 'me' }, { text: response, user: 'bot' }]);
|
|
272
|
+
// });
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const handleEmojiClick = (event: React.MouseEvent<Element, MouseEvent>, emojiObject: IEmojiData) => {
|
|
277
|
+
setInput(input + emojiObject.emoji);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div className="chatbot-container">
|
|
282
|
+
<div className="messages-box">
|
|
283
|
+
{messages.map((msg, index) => (
|
|
284
|
+
<div key={index} className={`message ${msg.user}`}>
|
|
285
|
+
{msg.text}
|
|
286
|
+
</div>
|
|
287
|
+
))}
|
|
288
|
+
</div>
|
|
289
|
+
<div className="input-box">
|
|
290
|
+
<input
|
|
291
|
+
type="text"
|
|
292
|
+
value={input}
|
|
293
|
+
onChange={(e) => setInput(e.target.value)}
|
|
294
|
+
placeholder="Type a message..."
|
|
295
|
+
/>
|
|
296
|
+
<FaSmile onClick={() => setShowEmojiPicker(!showEmojiPicker)} className="emoji-icon" />
|
|
297
|
+
{showEmojiPicker && <Picker onEmojiClick={handleEmojiClick} />}
|
|
298
|
+
<FaPaperPlane onClick={handleSend} className="send-icon" />
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export default Chatbot;
|
|
305
|
+
|
|
306
|
+
*/
|
package/src/global.d.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ChatBot } from "./chatbot.js";
|
package/src/inputbox.tsx
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* src/components/Chatbot.css */
|
|
2
|
+
.chatbotcontainer {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
height: 100vh;
|
|
6
|
+
width: 100%;
|
|
7
|
+
max-width: 600px;
|
|
8
|
+
margin: 0 auto;
|
|
9
|
+
border: 1px solid #ddd;
|
|
10
|
+
border-radius: 8px;
|
|
11
|
+
background: #f9f9f9;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.messagesbox {
|
|
15
|
+
flex: 1;
|
|
16
|
+
padding: 10px;
|
|
17
|
+
overflow-y: auto;
|
|
18
|
+
background: #fff;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.message {
|
|
22
|
+
display: flex;
|
|
23
|
+
margin-bottom: 10px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.message.user {
|
|
27
|
+
justify-content: flex-end;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.message.system {
|
|
31
|
+
justify-content: flex-start;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.bubble {
|
|
35
|
+
max-width: 60%;
|
|
36
|
+
padding: 10px 15px;
|
|
37
|
+
border-radius: 15px;
|
|
38
|
+
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
|
+
}
|
|
61
|
+
|
|
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%);
|
|
68
|
+
}
|
|
69
|
+
|
|
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%);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.inputbox {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
padding: 10px;
|
|
82
|
+
border-top: 1px solid #ddd;
|
|
83
|
+
background: #fff;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.inputbox input {
|
|
87
|
+
flex: 1;
|
|
88
|
+
padding: 10px;
|
|
89
|
+
border: 1px solid #ddd;
|
|
90
|
+
border-radius: 20px;
|
|
91
|
+
font-size: 14px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.emojiicon, .sendicon {
|
|
95
|
+
margin-left: 10px;
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
color: whitesmoke;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.emojiicon {
|
|
101
|
+
font-size: 20px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.sendicon {
|
|
105
|
+
font-size: 20px;
|
|
106
|
+
}
|