From 239c206659d02f5e89b3f54aa050aff6a71dcdd5 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Sun, 16 Aug 2026 19:07:08 +0900 Subject: fix(ui): コピー失敗時のエラーハンドリングを追加 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clipboard API が利用できない非セキュアコンテキストや、ブラウザの権限拒否で writeText が失敗した場合に、未処理例外となってコピーボタンが無反応に見える問題を修正。 navigator.clipboard.writeText の存在を事前に確認し、非同期処理を try/catch で囲むことで同期例外と Promise の拒否をまとめて処理する。成功時はチェックマーク、失敗時は警告マークを一時表示し、詳細はコンソールへ記録する。アラートは表示しない。 --- src/ollama_web/static/app.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'src/ollama_web') diff --git a/src/ollama_web/static/app.js b/src/ollama_web/static/app.js index 54fb2e0..65aa17b 100644 --- a/src/ollama_web/static/app.js +++ b/src/ollama_web/static/app.js @@ -189,14 +189,24 @@ function addMessage(role, rawText, showCopy = false) { copyBtn.setAttribute("aria-label", t("common.copy", "Copy")); copyBtn.textContent = "📋"; copyBtn.hidden = !showCopy; - copyBtn.addEventListener("click", () => { + copyBtn.addEventListener("click", async () => { const raw = div.dataset.rawText || ""; - navigator.clipboard.writeText(raw).then(() => { - copyBtn.textContent = "✓"; - setTimeout(() => { - copyBtn.textContent = "📋"; - }, 1200); - }); + let feedback = "✓"; + + try { + if (!navigator.clipboard?.writeText) { + throw new Error("Clipboard API is unavailable"); + } + await navigator.clipboard.writeText(raw); + } catch (err) { + console.error("failed to copy message", err); + feedback = "⚠"; + } + + copyBtn.textContent = feedback; + setTimeout(() => { + copyBtn.textContent = "📋"; + }, 1200); }); div.appendChild(copyBtn); chatEl.appendChild(div); @@ -959,4 +969,4 @@ function updateTokenGaugeFromSession() { } loadSessions(); -loadModelCapabilities(); \ No newline at end of file +loadModelCapabilities(); -- cgit v1.2.3