Spaces:
Running
Running
Remove image support; inform users model does not support image input
Browse files- README.md +2 -2
- index.html +6 -68
README.md
CHANGED
|
@@ -12,13 +12,13 @@ license: other
|
|
| 12 |
|
| 13 |
A lightweight chat interface for [Moonshot AI's Kimi K3](https://huggingface.co/moonshotai/Kimi-K3) — a 2.8T-parameter native multimodal agentic model.
|
| 14 |
|
| 15 |
-
Built with vanilla HTML/CSS/JS. No backend required — connects directly to the Kimi API from your browser.
|
| 16 |
|
| 17 |
## Usage
|
| 18 |
|
| 19 |
1. Get an API key from [platform.kimi.ai](https://platform.kimi.ai)
|
| 20 |
2. Enter it in the settings panel
|
| 21 |
-
3.
|
| 22 |
|
| 23 |
## Links
|
| 24 |
|
|
|
|
| 12 |
|
| 13 |
A lightweight chat interface for [Moonshot AI's Kimi K3](https://huggingface.co/moonshotai/Kimi-K3) — a 2.8T-parameter native multimodal agentic model.
|
| 14 |
|
| 15 |
+
Built with vanilla HTML/CSS/JS. No backend required — connects directly to the Kimi API from your browser. Text only — this model does not support image input.
|
| 16 |
|
| 17 |
## Usage
|
| 18 |
|
| 19 |
1. Get an API key from [platform.kimi.ai](https://platform.kimi.ai)
|
| 20 |
2. Enter it in the settings panel
|
| 21 |
+
3. Start chatting
|
| 22 |
|
| 23 |
## Links
|
| 24 |
|
index.html
CHANGED
|
@@ -66,7 +66,6 @@
|
|
| 66 |
padding: 8px 12px; border-radius: 6px; margin: 8px 0; font-size: 0.88em; color: #9999cc;
|
| 67 |
}
|
| 68 |
.msg .thinking-label { font-weight: 600; color: var(--primary); margin-bottom: 4px; font-size: 0.85em; }
|
| 69 |
-
.msg img { max-width: 100%; max-height: 200px; border-radius: 8px; margin: 4px 0; }
|
| 70 |
.typing { display: flex; gap: 4px; padding: 4px 0; }
|
| 71 |
.typing span { width: 6px; height: 6px; background: var(--text-muted); border-radius: 50%; animation: bounce 1.4s infinite; }
|
| 72 |
.typing span:nth-child(2) { animation-delay: 0.2s; }
|
|
@@ -88,13 +87,6 @@
|
|
| 88 |
}
|
| 89 |
.input-row button:hover { background: var(--primary-hover); }
|
| 90 |
.input-row button:disabled { opacity: 0.5; cursor: not-allowed; }
|
| 91 |
-
.input-row .image-btn {
|
| 92 |
-
background: var(--surface-2); border: 1px solid var(--border); padding: 8px 10px;
|
| 93 |
-
border-radius: 8px; cursor: pointer; font-size: 1.1em; line-height: 1; transition: 0.2s;
|
| 94 |
-
}
|
| 95 |
-
.input-row .image-btn:hover { border-color: var(--primary); }
|
| 96 |
-
#imagePreview { display: none; max-width: 60px; max-height: 60px; border-radius: 6px; border: 1px solid var(--border); }
|
| 97 |
-
|
| 98 |
pre { background: #1a1a2e; border: 1px solid var(--border); border-radius: 6px; padding: 8px 12px; overflow-x: auto; margin: 8px 0; font-size: 0.85em; }
|
| 99 |
code { background: var(--surface-2); padding: 2px 4px; border-radius: 4px; font-size: 0.9em; }
|
| 100 |
pre code { background: none; padding: 0; }
|
|
@@ -137,12 +129,9 @@
|
|
| 137 |
<div class="chat">
|
| 138 |
<div class="messages" id="messages">
|
| 139 |
<div class="msg system">Welcome! Enter your Kimi API key above, then start chatting.</div>
|
| 140 |
-
<div class="msg system">
|
| 141 |
</div>
|
| 142 |
<div class="input-row">
|
| 143 |
-
<label class="image-btn" title="Attach image" id="imageLabel">📷</label>
|
| 144 |
-
<input type="file" id="imageInput" accept="image/*" style="display:none" />
|
| 145 |
-
<img id="imagePreview" />
|
| 146 |
<textarea id="messageInput" placeholder="Type a message..." rows="1"></textarea>
|
| 147 |
<button id="sendBtn">Send</button>
|
| 148 |
</div>
|
|
@@ -163,14 +152,9 @@ const reasoningSelect = document.getElementById("reasoningEffort");
|
|
| 163 |
const messagesEl = document.getElementById("messages");
|
| 164 |
const messageInput = document.getElementById("messageInput");
|
| 165 |
const sendBtn = document.getElementById("sendBtn");
|
| 166 |
-
const imageInput = document.getElementById("imageInput");
|
| 167 |
-
const imagePreview = document.getElementById("imagePreview");
|
| 168 |
-
const imageLabel = document.getElementById("imageLabel");
|
| 169 |
const statusDot = document.getElementById("statusDot");
|
| 170 |
const statusText = document.getElementById("statusText");
|
| 171 |
|
| 172 |
-
let selectedImage = null;
|
| 173 |
-
|
| 174 |
apiKeyInput.addEventListener("input", () => {
|
| 175 |
if (apiKeyInput.value.startsWith("sk-")) {
|
| 176 |
statusDot.className = "status-dot on";
|
|
@@ -184,35 +168,6 @@ apiKeyInput.addEventListener("input", () => {
|
|
| 184 |
}
|
| 185 |
});
|
| 186 |
|
| 187 |
-
imageLabel.addEventListener("click", () => imageInput.click());
|
| 188 |
-
imageInput.addEventListener("change", (e) => {
|
| 189 |
-
const file = e.target.files[0];
|
| 190 |
-
if (file) {
|
| 191 |
-
selectedImage = file;
|
| 192 |
-
const reader = new FileReader();
|
| 193 |
-
reader.onload = (ev) => { imagePreview.src = ev.target.result; imagePreview.style.display = "block"; };
|
| 194 |
-
reader.readAsDataURL(file);
|
| 195 |
-
imageLabel.textContent = "✕";
|
| 196 |
-
} else {
|
| 197 |
-
clearImage();
|
| 198 |
-
}
|
| 199 |
-
});
|
| 200 |
-
|
| 201 |
-
imageLabel.addEventListener("click", (e) => {
|
| 202 |
-
if (imageLabel.textContent === "✕") {
|
| 203 |
-
e.preventDefault();
|
| 204 |
-
clearImage();
|
| 205 |
-
}
|
| 206 |
-
});
|
| 207 |
-
|
| 208 |
-
function clearImage() {
|
| 209 |
-
selectedImage = null;
|
| 210 |
-
imageInput.value = "";
|
| 211 |
-
imagePreview.style.display = "none";
|
| 212 |
-
imagePreview.src = "";
|
| 213 |
-
imageLabel.textContent = "📷";
|
| 214 |
-
}
|
| 215 |
-
|
| 216 |
function addMessage(role, content, thinking) {
|
| 217 |
const div = document.createElement("div");
|
| 218 |
div.className = `msg ${role}`;
|
|
@@ -294,29 +249,19 @@ function markedParse(text) {
|
|
| 294 |
|
| 295 |
async function sendMessage() {
|
| 296 |
const text = messageInput.value.trim();
|
| 297 |
-
if (!text
|
| 298 |
const apiKey = apiKeyInput.value.trim();
|
| 299 |
if (!apiKey || !apiKey.startsWith("sk-")) {
|
| 300 |
addMessage("system", "Please enter a valid Kimi API key (sk-...) in the settings.");
|
| 301 |
return;
|
| 302 |
}
|
| 303 |
|
| 304 |
-
addMessage("user", text
|
| 305 |
messageInput.value = "";
|
| 306 |
-
const currentImage = selectedImage;
|
| 307 |
-
clearImage();
|
| 308 |
showTyping();
|
| 309 |
|
| 310 |
try {
|
| 311 |
-
const messages = [];
|
| 312 |
-
|
| 313 |
-
const userContent = [];
|
| 314 |
-
if (text) userContent.push({ type: "text", text });
|
| 315 |
-
if (currentImage) {
|
| 316 |
-
const b64 = await toBase64(currentImage);
|
| 317 |
-
userContent.push({ type: "image_url", image_url: { url: b64 } });
|
| 318 |
-
}
|
| 319 |
-
messages.push({ role: "user", content: userContent });
|
| 320 |
|
| 321 |
const response = await fetch(`${KIMI_API}/chat/completions`, {
|
| 322 |
method: "POST",
|
|
@@ -388,20 +333,13 @@ async function sendMessage() {
|
|
| 388 |
msg = "Invalid API key. Check your key at platform.kimi.ai.";
|
| 389 |
} else if (msg.includes("402")) {
|
| 390 |
msg = "Payment required. Add credits at platform.kimi.ai.";
|
|
|
|
|
|
|
| 391 |
}
|
| 392 |
addMessage("system", `Error: ${msg}`);
|
| 393 |
}
|
| 394 |
}
|
| 395 |
|
| 396 |
-
function toBase64(file) {
|
| 397 |
-
return new Promise((resolve, reject) => {
|
| 398 |
-
const reader = new FileReader();
|
| 399 |
-
reader.onload = () => resolve(reader.result);
|
| 400 |
-
reader.onerror = reject;
|
| 401 |
-
reader.readAsDataURL(file);
|
| 402 |
-
});
|
| 403 |
-
}
|
| 404 |
-
|
| 405 |
sendBtn.addEventListener("click", sendMessage);
|
| 406 |
messageInput.addEventListener("keydown", (e) => {
|
| 407 |
if (e.key === "Enter" && !e.shiftKey) {
|
|
|
|
| 66 |
padding: 8px 12px; border-radius: 6px; margin: 8px 0; font-size: 0.88em; color: #9999cc;
|
| 67 |
}
|
| 68 |
.msg .thinking-label { font-weight: 600; color: var(--primary); margin-bottom: 4px; font-size: 0.85em; }
|
|
|
|
| 69 |
.typing { display: flex; gap: 4px; padding: 4px 0; }
|
| 70 |
.typing span { width: 6px; height: 6px; background: var(--text-muted); border-radius: 50%; animation: bounce 1.4s infinite; }
|
| 71 |
.typing span:nth-child(2) { animation-delay: 0.2s; }
|
|
|
|
| 87 |
}
|
| 88 |
.input-row button:hover { background: var(--primary-hover); }
|
| 89 |
.input-row button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
pre { background: #1a1a2e; border: 1px solid var(--border); border-radius: 6px; padding: 8px 12px; overflow-x: auto; margin: 8px 0; font-size: 0.85em; }
|
| 91 |
code { background: var(--surface-2); padding: 2px 4px; border-radius: 4px; font-size: 0.9em; }
|
| 92 |
pre code { background: none; padding: 0; }
|
|
|
|
| 129 |
<div class="chat">
|
| 130 |
<div class="messages" id="messages">
|
| 131 |
<div class="msg system">Welcome! Enter your Kimi API key above, then start chatting.</div>
|
| 132 |
+
<div class="msg system">Text only. This model does not support image input.</div>
|
| 133 |
</div>
|
| 134 |
<div class="input-row">
|
|
|
|
|
|
|
|
|
|
| 135 |
<textarea id="messageInput" placeholder="Type a message..." rows="1"></textarea>
|
| 136 |
<button id="sendBtn">Send</button>
|
| 137 |
</div>
|
|
|
|
| 152 |
const messagesEl = document.getElementById("messages");
|
| 153 |
const messageInput = document.getElementById("messageInput");
|
| 154 |
const sendBtn = document.getElementById("sendBtn");
|
|
|
|
|
|
|
|
|
|
| 155 |
const statusDot = document.getElementById("statusDot");
|
| 156 |
const statusText = document.getElementById("statusText");
|
| 157 |
|
|
|
|
|
|
|
| 158 |
apiKeyInput.addEventListener("input", () => {
|
| 159 |
if (apiKeyInput.value.startsWith("sk-")) {
|
| 160 |
statusDot.className = "status-dot on";
|
|
|
|
| 168 |
}
|
| 169 |
});
|
| 170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
function addMessage(role, content, thinking) {
|
| 172 |
const div = document.createElement("div");
|
| 173 |
div.className = `msg ${role}`;
|
|
|
|
| 249 |
|
| 250 |
async function sendMessage() {
|
| 251 |
const text = messageInput.value.trim();
|
| 252 |
+
if (!text) return;
|
| 253 |
const apiKey = apiKeyInput.value.trim();
|
| 254 |
if (!apiKey || !apiKey.startsWith("sk-")) {
|
| 255 |
addMessage("system", "Please enter a valid Kimi API key (sk-...) in the settings.");
|
| 256 |
return;
|
| 257 |
}
|
| 258 |
|
| 259 |
+
addMessage("user", text);
|
| 260 |
messageInput.value = "";
|
|
|
|
|
|
|
| 261 |
showTyping();
|
| 262 |
|
| 263 |
try {
|
| 264 |
+
const messages = [{ role: "user", content: text }];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
const response = await fetch(`${KIMI_API}/chat/completions`, {
|
| 267 |
method: "POST",
|
|
|
|
| 333 |
msg = "Invalid API key. Check your key at platform.kimi.ai.";
|
| 334 |
} else if (msg.includes("402")) {
|
| 335 |
msg = "Payment required. Add credits at platform.kimi.ai.";
|
| 336 |
+
} else if (msg.includes("not support image") || msg.includes("image.png")) {
|
| 337 |
+
msg = "Cannot read image. This model does not support image input.";
|
| 338 |
}
|
| 339 |
addMessage("system", `Error: ${msg}`);
|
| 340 |
}
|
| 341 |
}
|
| 342 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 343 |
sendBtn.addEventListener("click", sendMessage);
|
| 344 |
messageInput.addEventListener("keydown", (e) => {
|
| 345 |
if (e.key === "Enter" && !e.shiftKey) {
|