From 634ad6cd8eb776caf964e6c2205b53ac2a04bffe Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 25 Aug 2024 15:21:45 +0200 Subject: [PATCH] feat: ai-generation - stream results --- index.css | 17 ++++++++++++++++ index.html | 2 +- modules/ui/ai-generator.js | 41 ++++++++++++++++++++++++++++++++------ versioning.js | 2 +- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/index.css b/index.css index da6acf9e..203fbf61 100644 --- a/index.css +++ b/index.css @@ -356,6 +356,14 @@ text.drag { font-weight: bold; } +button.ui-button:disabled { + filter: brightness(0.95); +} + +button.ui-button:disabled:hover { + cursor: default; +} + .ui-dialog, #optionsContainer { user-select: none; @@ -2378,6 +2386,15 @@ svg.button { margin-left: 0.25em; } +@keyframes clockwiseBorderPulse { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + @media print { div, canvas { diff --git a/index.html b/index.html index 4c70d664..b2c55863 100644 --- a/index.html +++ b/index.html @@ -8065,7 +8065,7 @@ - + diff --git a/modules/ui/ai-generator.js b/modules/ui/ai-generator.js index a0dbc0a6..c28efc8a 100644 --- a/modules/ui/ai-generator.js +++ b/modules/ui/ai-generator.js @@ -54,18 +54,24 @@ function geneateWithAi(defaultPrompt, onApply) { try { button.disabled = true; - byId("aiGeneratorResult").disabled = true; + const resultArea = byId("aiGeneratorResult"); + resultArea.value = ""; + resultArea.disabled = true; const response = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", - headers: {"Content-Type": "application/json", Authorization: `Bearer ${key}`}, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${key}` + }, body: JSON.stringify({ model, messages: [ {role: "system", content: SYSTEM_MESSAGE}, {role: "user", content: prompt} ], - temperature: 1.2 + temperature: 1.2, + stream: true // Enable streaming }) }); @@ -74,9 +80,32 @@ function geneateWithAi(defaultPrompt, onApply) { throw new Error(json?.error?.message || "Failed to generate"); } - const {choices} = await response.json(); - const result = choices[0].message.content; - byId("aiGeneratorResult").value = result; + const reader = response.body.getReader(); + const decoder = new TextDecoder("utf-8"); + let buffer = ""; + + while (true) { + const {done, value} = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, {stream: true}); + const lines = buffer.split("\n"); + + for (let i = 0; i < lines.length - 1; i++) { + const line = lines[i].trim(); + if (line.startsWith("data: ") && line !== "data: [DONE]") { + try { + const jsonData = JSON.parse(line.slice(6)); + const content = jsonData.choices[0].delta.content; + if (content) resultArea.value += content; + } catch (jsonError) { + console.warn("Failed to parse JSON:", jsonError, "Line:", line); + } + } + } + + buffer = lines[lines.length - 1]; + } } catch (error) { return tip(error.message, true, "error", 4000); } finally { diff --git a/versioning.js b/versioning.js index 71c28183..da0012fb 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.99.08"; // generator version, update each time +const version = "1.99.09"; // generator version, update each time { document.title += " v" + version;