diff --git a/chat.html b/chat.html index ac282e4..bfdba2d 100644 --- a/chat.html +++ b/chat.html @@ -1,4 +1,4 @@ - +
@@ -45,12 +45,20 @@ font-size: 14px; background: var(--bg-primary); color: var(--text-primary); - transition: background-color 0.2s, color 0.2s; + transition: + background-color 0.2s, + color 0.2s; } input { line-height: inherit; font-family: inherit; } + + td + td, + th + td { + padding-left: 0.5rem; + } + .chat-container { height: 100vh; display: grid; @@ -131,7 +139,9 @@ font-size: inherit; background: var(--bg-input); color: var(--text-primary); - transition: background-color 0.2s, color 0.2s; + transition: + background-color 0.2s, + color 0.2s; } .prompt::placeholder { color: var(--text-secondary); @@ -145,7 +155,9 @@ color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 0.4rem; - transition: background-color 0.2s, color 0.2s; + transition: + background-color 0.2s, + color 0.2s; } .submit:hover { background: var(--hover-bg); @@ -173,7 +185,9 @@ background-color: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border-color); - transition: opacity 0.2s, background-color 0.2s; + transition: + opacity 0.2s, + background-color 0.2s; } :is(#model-selector, #history-panel):hover { opacity: 0.7; @@ -318,12 +332,14 @@ window.env ??= {}; const systemPrompt = ` - You are a helpful AI assistant. - Do not preach about implications, do not praise excessively. - Keep a critical mindset, do not assume blindly. - When I ask for code, keep in mind that I am a senior backend developer (but do not mention that explicitly), and do not explain excessively, but and challenge my statements with better alternatives if I don't sound right, especially in coding. - Answer in valid markdown only, no exceptions. - ` + You are a helpful AI assistant. + - Do not preach about implications, do not praise excessively. + - Keep a critical mindset, do not assume blindly. + - Answer in valid markdown only, no exceptions. + + - When I ask something about code, or software development, keep in mind that I am a senior backend developer (but do not mention that explicitly), and do not explain excessively, but and challenge my statements with better alternatives if I don't sound right, especially in coding. Do not bring it up otherwise. + - Do not give me code if the topic is not about programming or software development. + ` .replace(/^\s+/gm, "") .trim(); @@ -367,7 +383,7 @@ }, get inputRows() { - return Math.min(10, Math.max(2, this.promptText.split("\n").length)); + return Math.min(10, Math.max(2, this.promptText.split("\n").length)) + 1; }, handleScroll() { @@ -383,17 +399,6 @@ } }, - attachCopyButtons($el) { - $el.querySelectorAll("pre").forEach(($pre) => { - if (!$pre.querySelector(".copy-btn")) { - const $btn = document.createElement("button"); - $btn.className = "copy-btn"; - $btn.textContent = "Copy"; - $pre.appendChild($btn); - } - }); - }, - formatTime: (iso) => new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), getExcerpt: (msgs) => msgs.find((m) => m.role === "user")?.content.slice(0, 30) || "Empty chat", @@ -409,6 +414,7 @@ // Streaming setup const target = this.$refs.incomingMessage; + this.$refs.incomingMessage.innerHTML = ""; let fullResponse = ""; this.scrollToBottom(); @@ -423,13 +429,11 @@ onContent: (chunk) => { fullResponse += chunk; target.innerHTML = renderMarkdown(fullResponse); - this.attachCopyButtons(target); this.scrollToBottom(); }, onError: (e) => { fullResponse += `\n\n[Error: ${e.message}]`; target.innerHTML = renderMarkdown(fullResponse); - this.attachCopyButtons(target); this.scrollToBottom(); }, }); @@ -460,11 +464,22 @@ }, }); - const md = markdownit({ html: false }); + function attachCopyButtons($el) { + $el.querySelectorAll("pre").forEach(($pre) => { + if (!$pre.querySelector(".copy-btn")) { + const $btn = document.createElement("button"); + $btn.className = "copy-btn"; + $btn.textContent = "Copy"; + $pre.appendChild($btn); + } + }); + } + const md = markdownit({ html: false }); function renderMarkdown(markdown) { - const $placeholder = document.createElement("template"); + const $placeholder = document.createElement("div"); $placeholder.innerHTML = md.render(markdown); + attachCopyButtons($placeholder); return $placeholder.innerHTML; } @@ -514,10 +529,24 @@ } async function chatWithGemini({ onContent, onError, messages, signal, apiKey = window.env.GEMINI_API_KEY, model, ...rest }) { - const contents = messages.map((msg) => ({ - role: msg.role === "assistant" ? "model" : "user", - parts: [{ text: msg.content }], - })); + const contents = messages.map((msg, i, arr) => { + const role = msg.role === "assistant" ? "model" : "user"; + let parts = [{ text: msg.content }]; + + // Add YouTube URLs as file data for the final user message + const isLastMessage = i === arr.length - 1; + if (isLastMessage && role === "user") { + const youtubeUrlPattern = /https?:\/\/(www\.)?youtube\.com\/watch\?v=[\w-]+|https?:\/\/youtu\.be\/[\w-]+/g; + const youtubeUrls = msg.content.match(youtubeUrlPattern) || []; + const fileParts = youtubeUrls.map((url) => ({ file_data: { file_uri: url } })); + parts = [...parts, ...fileParts]; + } + + return { + role, + parts, + }; + }); try { const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?key=${apiKey}`, {