This commit is contained in:
2026-01-14 10:36:55 +01:00
parent 2566b70a98
commit 483ac1bf5a

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="ja">
<html lang="en">
<head>
<meta charset="UTF-8">
@ -14,7 +14,7 @@
scroll-behavior: smooth;
}
/* カスタムスクロールバー */
/* Custom scrollbar */
::-webkit-scrollbar {
width: 6px;
}
@ -24,12 +24,12 @@
}
::-webkit-scrollbar-thumb {
background: #e2e8f0;
background: #4b5563;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: #cbd5e1;
background: #6b7280;
}
.message-animation {
@ -50,40 +50,41 @@
</style>
</head>
<body class="bg-gray-50 text-gray-900 font-sans h-screen flex flex-col overflow-hidden">
<body class="bg-gray-900 text-gray-100 font-sans h-screen flex flex-col overflow-hidden">
<!-- ヘッダー -->
<header class="flex items-center justify-between px-6 py-4 bg-white border-b shadow-sm z-10">
<!-- Header -->
<header class="flex items-center justify-between px-6 py-4 bg-gray-800 border-b border-gray-700 shadow-sm z-10">
<div class="flex items-center gap-2">
<div class="p-2 bg-indigo-600 rounded-lg shadow-blue-200 shadow-lg">
<i data-lucide="bot" class="text-white w-6 h-6"></i>
</div>
<div>
<h1 class="text-lg font-bold tracking-tight text-gray-800 leading-none">AI Assistant</h1>
<h1 class="text-lg font-bold tracking-tight text-gray-100 leading-none">AI Assistant</h1>
<span class="text-[10px] text-green-500 font-medium uppercase tracking-tighter">● Online</span>
</div>
</div>
<button id="clear-btn" class="p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-full transition-all"
title="チャットをクリア">
<button id="clear-btn"
class="p-2 text-gray-400 hover:text-red-400 hover:bg-gray-700 rounded-full transition-all"
title="Clear chat">
<i data-lucide="trash-2" class="w-5 h-5"></i>
</button>
</header>
<!-- メッセージ表示エリア -->
<!-- Message display area -->
<main id="chat-window" class="flex-1 overflow-y-auto px-4 py-8 scroll-smooth">
<div id="messages-container" class="max-w-3xl mx-auto space-y-6">
<!-- メッセージがここに挿入されます -->
<div id="messages-container" class="w-full space-y-6">
<!-- Messages will be inserted here -->
</div>
<!-- ローディング表示(初期状態は非表示) -->
<div id="loading-indicator" class="hidden max-w-3xl mx-auto mt-6">
<!-- Loading indicator (initially hidden) -->
<div id="loading-indicator" class="hidden w-full mt-6">
<div class="flex gap-4">
<div
class="flex-shrink-0 w-8 h-8 rounded-full bg-white border flex items-center justify-center shadow-sm">
class="flex-shrink-0 w-8 h-8 rounded-full bg-gray-700 border border-gray-600 flex items-center justify-center shadow-sm">
<i data-lucide="bot" class="text-indigo-400 w-4 h-4"></i>
</div>
<div
class="bg-white border border-gray-100 px-4 py-3 rounded-2xl rounded-tl-none shadow-sm flex items-center gap-3">
class="bg-gray-800 border border-gray-700 px-4 py-3 rounded-2xl rounded-tl-none shadow-sm flex items-center gap-3">
<div class="flex gap-1">
<span
class="w-1.5 h-1.5 bg-indigo-400 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
@ -91,24 +92,24 @@
class="w-1.5 h-1.5 bg-indigo-400 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
<span class="w-1.5 h-1.5 bg-indigo-400 rounded-full animate-bounce"></span>
</div>
<span class="text-gray-400 text-xs font-medium">AI応答生成中...</span>
<span class="text-gray-400 text-xs font-medium">AI is thinking...</span>
</div>
</div>
</div>
<!-- エラー表示エリア -->
<div id="error-container" class="hidden max-w-2xl mx-auto mt-6">
<!-- エラー内容がここに挿入されます -->
<!-- Error display area -->
<div id="error-container" class="hidden w-full mt-6">
<!-- Error content will be inserted here -->
</div>
</main>
<!-- 入力エリア -->
<footer class="bg-white border-t p-4">
<div class="max-w-3xl mx-auto">
<!-- Input area -->
<footer class="bg-gray-800 border-t border-gray-700 p-4">
<div class="w-full px-2">
<div
class="relative flex items-end gap-2 bg-gray-50 border border-gray-200 rounded-2xl p-1.5 focus-within:ring-2 focus-within:ring-indigo-500 focus-within:bg-white transition-all shadow-inner">
<textarea id="user-input" placeholder="質問を入力してください..." rows="1"
class="flex-1 bg-transparent border-none focus:ring-0 text-sm py-2 px-3 resize-none max-h-40 min-h-[40px] outline-none"></textarea>
class="relative flex items-end gap-2 bg-gray-700 border border-gray-600 rounded-2xl p-1.5 focus-within:ring-2 focus-within:ring-indigo-500 focus-within:bg-gray-700 transition-all shadow-inner">
<textarea id="user-input" placeholder="Type your message..." rows="1"
class="flex-1 bg-transparent border-none focus:ring-0 text-sm py-2 px-3 resize-none max-h-40 min-h-[40px] outline-none text-gray-100 placeholder-gray-400"></textarea>
<button id="send-btn" class="p-2.5 rounded-xl transition-all text-gray-300 disabled:cursor-not-allowed"
disabled>
<i data-lucide="send" id="send-icon" class="w-5 h-5"></i>
@ -128,7 +129,7 @@
const MODEL = 'openai/gpt-oss-20b';
let messages = [
{ role: 'assistant', content: 'こんにちは!何かお手伝いできることはありますか?' }
{ role: 'assistant', content: 'Hello! How can I help you today?' }
];
let isLoading = false;
@ -155,11 +156,11 @@
const isUser = msg.role === 'user';
const messageHtml = `
<div class="flex gap-4 ${isUser ? 'flex-row-reverse' : 'flex-row'} message-animation">
<div class="flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center shadow-sm ${isUser ? 'bg-indigo-600' : 'bg-white border'}">
<i data-lucide="${isUser ? 'user' : 'bot'}" class="${isUser ? 'text-white' : 'text-indigo-600'} w-4 h-4"></i>
<div class="flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center shadow-sm ${isUser ? 'bg-indigo-600' : 'bg-gray-700 border border-gray-600'}">
<i data-lucide="${isUser ? 'user' : 'bot'}" class="${isUser ? 'text-white' : 'text-indigo-400'} w-4 h-4"></i>
</div>
<div class="flex flex-col max-w-[85%] ${isUser ? 'items-end' : 'items-start'}">
<div class="px-4 py-2.5 rounded-2xl shadow-sm text-sm ${isUser ? 'bg-indigo-600 text-white rounded-tr-none' : 'bg-white text-gray-800 border border-gray-100 rounded-tl-none'}">
<div class="px-4 py-2.5 rounded-2xl shadow-sm text-sm ${isUser ? 'bg-indigo-600 text-white rounded-tr-none' : 'bg-gray-800 text-gray-100 border border-gray-700 rounded-tl-none'}">
<p class="whitespace-pre-wrap break-words">${msg.content}</p>
</div>
</div>
@ -177,16 +178,16 @@
function showError(message) {
errorContainer.innerHTML = `
<div class="flex flex-col gap-2 p-4 bg-red-50 border border-red-100 rounded-xl text-red-700">
<div class="flex flex-col gap-2 p-4 bg-red-900 bg-opacity-30 border border-red-800 rounded-xl text-red-400">
<div class="flex items-center gap-2 font-bold text-sm">
<i data-lucide="alert-circle" class="w-4 h-4"></i>
<span>エラーが発生しました</span>
<span>An error occurred</span>
</div>
<p class="text-xs opacity-90 leading-relaxed">${message}</p>
${message.includes('Failed to fetch') ? `
<div class="mt-2 text-[10px] bg-red-100 p-2 rounded flex gap-2 items-start">
<div class="mt-2 text-[10px] bg-red-950 bg-opacity-50 p-2 rounded flex gap-2 items-start">
<i data-lucide="info" class="w-3 h-3 mt-0.5 flex-shrink-0"></i>
<span>ヒント: 外部ドメインからのAPI呼び出しがブラウザにブロックされています。サーバー側で Access-Control-Allow-Origin ヘッダーを設定するか、開発中は CORS 解除用のブラウザ拡張機能を試してください。</span>
<span>Hint: API calls from external domains are being blocked by the browser. Set the Access-Control-Allow-Origin header on the server side, or try using a CORS-disabling browser extension during development.</span>
</div>
` : ''}
</div>
@ -229,18 +230,18 @@
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error?.message || `APIエラー (ステータス: ${response.status})`);
throw new Error(errorData.error?.message || `API error (status: ${response.status})`);
}
const data = await response.json();
if (data.choices && data.choices[0]?.message) {
messages.push(data.choices[0].message);
} else {
throw new Error('レスポンスの形式が正しくありません。');
throw new Error('Invalid response format.');
}
} catch (err) {
console.error(err);
showError(err.message === 'Failed to fetch' ? 'APIへの接続に失敗しました。CORS制限を確認してください。' : err.message);
showError(err.message === 'Failed to fetch' ? 'Failed to connect to API. Please check CORS restrictions.' : err.message);
} finally {
isLoading = false;
updateUIState();
@ -280,12 +281,12 @@
sendBtn.addEventListener('click', handleSend);
clearBtn.addEventListener('click', () => {
messages = [{ role: 'assistant', content: 'チャットをリセットしました。何かお手伝いしましょうか?' }];
messages = [{ role: 'assistant', content: 'Chat has been reset. How can I help you?' }];
errorContainer.classList.add('hidden');
renderMessages();
});
// 初期表示
// Initial render
renderMessages();
initIcons();
</script>