Skip to content

Commit a2296cf

Browse files
Add chat.html demo
1 parent 9b1c25a commit a2296cf

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed

demo/chat.html

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6+
<title>看板娘聊天平台</title>
7+
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
8+
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/css/all.min.css">
9+
<script src="../dist/live2d.min.js"></script>
10+
<style>
11+
html, body {
12+
height: 100%;
13+
}
14+
body {
15+
display: flex;
16+
align-items: center;
17+
justify-content: center;
18+
padding-top: 40px;
19+
padding-bottom: 40px;
20+
background-color: #f5f5f5;
21+
}
22+
#waifu {
23+
bottom: 0;
24+
left: 0;
25+
line-height: 0;
26+
margin-bottom: -10px;
27+
position: fixed;
28+
transform: translateY(3px);
29+
transition: transform .3s ease-in-out, bottom 3s ease-in-out;
30+
z-index: 1;
31+
}
32+
#waifu:hover {
33+
transform: translateY(0);
34+
}
35+
#waifu-tips {
36+
animation: shake 50s ease-in-out 5s infinite;
37+
background-color: rgba(236, 217, 188, .5);
38+
border: 1px solid rgba(224, 186, 140, .62);
39+
border-radius: 12px;
40+
box-shadow: 0 3px 15px 2px rgba(191, 158, 118, .2);
41+
font-size: 14px;
42+
line-height: 24px;
43+
margin: -100px 20px;
44+
min-height: 70px;
45+
overflow: hidden;
46+
padding: 5px 10px;
47+
position: absolute;
48+
text-overflow: ellipsis;
49+
transition: opacity 1s;
50+
width: 250px;
51+
word-break: break-all;
52+
}
53+
#waifu-tips.waifu-tips-active {
54+
opacity: 1;
55+
transition: opacity .2s;
56+
}
57+
#waifu-tips span {
58+
color: #0099cc;
59+
}
60+
#live2d {
61+
cursor: grab;
62+
height: 300px;
63+
position: relative;
64+
width: 300px;
65+
}
66+
#live2d:active {
67+
cursor: grabbing;
68+
}
69+
textarea {
70+
background-color: transparent;
71+
border: none;
72+
width: 100%;
73+
resize: none;
74+
min-height: 100px;
75+
}
76+
textarea:focus {
77+
outline: none;
78+
}
79+
</style>
80+
</head>
81+
<body>
82+
<div id="waifu">
83+
<div id="waifu-tips"><textarea id="text" disabled="true"></textarea></div>
84+
<canvas id="live2d" width="800" height="800"></canvas>
85+
</div>
86+
<script type="module">
87+
/*
88+
* _(:з」∠)_
89+
* Created by Shuqiao Zhang in 2025.
90+
* https://zhangshuqiao.org
91+
*/
92+
93+
/*
94+
* This program is free software: you can redistribute it and/or modify
95+
* it under the terms of the GNU General Public License as published by
96+
* the Free Software Foundation, either version 3 of the License, or
97+
* (at your option) any later version.
98+
*
99+
* This program is distributed in the hope that it will be useful,
100+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
101+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
102+
* GNU General Public License for more details.
103+
*/
104+
import { Cubism2Model } from "../build/index.js";
105+
106+
class ChatManager {
107+
constructor() {
108+
this.apiUrl = null;
109+
this.apiKey = null;
110+
this.conversationHistory = [];
111+
this.systemPrompt = '请扮演Potion Maker中的Pio,在接下来的对话中,使用萌萌哒的语气回答问题。';
112+
}
113+
114+
async create() {
115+
// 提示用户输入API URL和Key
116+
this.apiUrl = prompt("请输入大模型API URL (例如: https://api.openai.com/v1/chat/completions):");
117+
if (!this.apiUrl) {
118+
return "呜呜~ 你没有输入API URL呢,Pio无法开始对话啦~";
119+
}
120+
121+
this.apiKey = prompt("请输入API Key:");
122+
if (!this.apiKey) {
123+
return "呜呜~ 你没有输入API Key呢,Pio无法开始对话啦~";
124+
}
125+
126+
// 初始化对话历史,添加系统提示词
127+
this.conversationHistory = [
128+
{
129+
role: "system",
130+
content: this.systemPrompt
131+
}
132+
];
133+
134+
return "嗨~ 我是Pio哦!有什么可以帮助你的吗?(按Enter发送消息)";
135+
}
136+
137+
async message(content) {
138+
if (!this.apiUrl || !this.apiKey) {
139+
return "呜呜~ API配置不完整,请刷新页面重新配置~";
140+
}
141+
142+
// 添加用户消息到历史
143+
this.conversationHistory.push({
144+
role: "user",
145+
content: content
146+
});
147+
148+
try {
149+
const response = await fetch(this.apiUrl, {
150+
method: 'POST',
151+
headers: {
152+
'Content-Type': 'application/json',
153+
'Authorization': `Bearer ${this.apiKey}`
154+
},
155+
body: JSON.stringify({
156+
messages: this.conversationHistory,
157+
temperature: 0.7,
158+
max_tokens: 1000
159+
})
160+
});
161+
162+
if (!response.ok) {
163+
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
164+
}
165+
166+
const data = await response.json();
167+
const assistantMessage = data.choices[0].message.content;
168+
169+
// 添加助手回复到历史
170+
this.conversationHistory.push({
171+
role: "assistant",
172+
content: assistantMessage
173+
});
174+
175+
return assistantMessage;
176+
} catch (error) {
177+
console.error("API调用出错:", error);
178+
return `呜呜~ 出错了呢: ${error.message}`;
179+
}
180+
}
181+
}
182+
183+
window.addEventListener("load", async () => {
184+
"use strict";
185+
186+
const apiPath = "https://live2d.fghrsh.net/api";
187+
const text = document.getElementById("text");
188+
const manager = new ChatManager();
189+
let state = 0, loading = false,
190+
modelId = localStorage.getItem("modelId"),
191+
modelTexturesId = localStorage.getItem("modelTexturesId");
192+
if (modelId === null) {
193+
modelId = 1;
194+
modelTexturesId = 53;
195+
}
196+
const model = new Cubism2Model();
197+
await loadModel(modelId, modelTexturesId);
198+
199+
async function loadModel(modelId, modelTexturesId) {
200+
localStorage.setItem("modelId", modelId);
201+
if (modelTexturesId === undefined) modelTexturesId = 0;
202+
localStorage.setItem("modelTexturesId", modelTexturesId);
203+
const modelSettingPath = `${apiPath}/get/?id=${modelId}-${modelTexturesId}`;
204+
const response = await fetch(modelSettingPath);
205+
const modelSetting = await response.json();
206+
if (!model.gl) {
207+
await model.init('live2d', modelSettingPath, modelSetting);
208+
} else {
209+
await model.changeModelWithJSON(modelSettingPath, modelSetting);
210+
}
211+
console.log("live2d", `模型 ${modelId}-${modelTexturesId} 加载完成`);
212+
setTimeout(() => {
213+
state = 2;
214+
}, 2000);
215+
}
216+
217+
text.addEventListener("keydown", async (e) => {
218+
if (e.code === "Enter") {
219+
e.preventDefault();
220+
let content = text.value;
221+
if (content === "") return;
222+
console.log(content);
223+
text.disabled = true;
224+
text.value = await manager.message(content);
225+
text.disabled = false;
226+
}
227+
});
228+
229+
const message = await manager.create();
230+
text.value = message;
231+
text.disabled = false;
232+
});
233+
</script>
234+
</body>
235+
</html>

0 commit comments

Comments
 (0)