save updates
This commit is contained in:
135
index.html
135
index.html
@ -9,6 +9,11 @@
|
|||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<!-- Lucide Icons -->
|
<!-- Lucide Icons -->
|
||||||
<script src="https://unpkg.com/lucide@latest"></script>
|
<script src="https://unpkg.com/lucide@latest"></script>
|
||||||
|
<!-- Marked.js for Markdown parsing -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
<!-- Highlight.js for syntax highlighting -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.scroll-smooth {
|
.scroll-smooth {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
@ -47,6 +52,70 @@
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Message content styling */
|
||||||
|
.message-content {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #1f2937;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content code {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content pre code {
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content ul,
|
||||||
|
.message-content ol {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content li {
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content blockquote {
|
||||||
|
border-left: 3px solid #4b5563;
|
||||||
|
padding-left: 1rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
.message-content :not(pre)>code {
|
||||||
|
background-color: #374151;
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -109,7 +178,7 @@
|
|||||||
<div
|
<div
|
||||||
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">
|
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"
|
<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>
|
class="flex-1 bg-transparent border-none focus:ring-0 text-sm py-2 px-3 resize-none max-h-60 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"
|
<button id="send-btn" class="p-2.5 rounded-xl transition-all text-gray-300 disabled:cursor-not-allowed"
|
||||||
disabled>
|
disabled>
|
||||||
<i data-lucide="send" id="send-icon" class="w-5 h-5"></i>
|
<i data-lucide="send" id="send-icon" class="w-5 h-5"></i>
|
||||||
@ -144,30 +213,74 @@
|
|||||||
|
|
||||||
modelLabel.textContent = `Model: ${MODEL}`;
|
modelLabel.textContent = `Model: ${MODEL}`;
|
||||||
|
|
||||||
|
// Configure marked.js
|
||||||
|
marked.setOptions({
|
||||||
|
breaks: true,
|
||||||
|
gfm: true,
|
||||||
|
headerIds: false,
|
||||||
|
mangle: false
|
||||||
|
});
|
||||||
|
|
||||||
// アイコンの初期化
|
// アイコンの初期化
|
||||||
function initIcons() {
|
function initIcons() {
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format message content
|
||||||
|
function formatMessage(content, isUser) {
|
||||||
|
if (isUser) {
|
||||||
|
// User messages: preserve formatting but allow line breaks
|
||||||
|
return `<div class="message-content whitespace-pre-wrap">${escapeHtml(content)}</div>`;
|
||||||
|
} else {
|
||||||
|
// AI messages: parse as Markdown
|
||||||
|
try {
|
||||||
|
const html = marked.parse(content);
|
||||||
|
return `<div class="message-content">${html}</div>`;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Markdown parsing error:', e);
|
||||||
|
return `<div class="message-content whitespace-pre-wrap">${escapeHtml(content)}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape HTML to prevent XSS
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const map = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
"'": '''
|
||||||
|
};
|
||||||
|
return text.replace(/[&<>"']/g, m => map[m]);
|
||||||
|
}
|
||||||
|
|
||||||
// メッセージのレンダリング
|
// メッセージのレンダリング
|
||||||
function renderMessages() {
|
function renderMessages() {
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
messages.forEach((msg) => {
|
messages.forEach((msg) => {
|
||||||
const isUser = msg.role === 'user';
|
const isUser = msg.role === 'user';
|
||||||
const messageHtml = `
|
const formattedContent = formatMessage(msg.content, isUser);
|
||||||
<div class="flex gap-4 ${isUser ? 'flex-row-reverse' : 'flex-row'} message-animation">
|
const messageDiv = document.createElement('div');
|
||||||
<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'}">
|
messageDiv.className = `flex gap-4 ${isUser ? 'flex-row-reverse' : 'flex-row'} message-animation`;
|
||||||
<i data-lucide="${isUser ? 'user' : 'bot'}" class="${isUser ? 'text-white' : 'text-indigo-400'} w-4 h-4"></i>
|
messageDiv.innerHTML = `
|
||||||
</div>
|
<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'}">
|
||||||
<div class="flex flex-col max-w-[85%] ${isUser ? 'items-end' : 'items-start'}">
|
<i data-lucide="${isUser ? 'user' : 'bot'}" class="${isUser ? 'text-white' : 'text-indigo-400'} w-4 h-4"></i>
|
||||||
<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'}">
|
</div>
|
||||||
<p class="whitespace-pre-wrap break-words">${msg.content}</p>
|
<div class="flex flex-col flex-1 ${isUser ? 'items-end' : 'items-start'}">
|
||||||
</div>
|
<div class="px-4 py-2.5 rounded-2xl shadow-sm text-sm max-w-full overflow-hidden ${isUser ? 'bg-indigo-600 text-white rounded-tr-none' : 'bg-gray-800 text-gray-100 border border-gray-700 rounded-tl-none'}">
|
||||||
|
${formattedContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
container.insertAdjacentHTML('beforeend', messageHtml);
|
container.appendChild(messageDiv);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Apply syntax highlighting to code blocks
|
||||||
|
document.querySelectorAll('pre code').forEach((block) => {
|
||||||
|
hljs.highlightElement(block);
|
||||||
|
});
|
||||||
|
|
||||||
initIcons();
|
initIcons();
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user