feat(chat): Add Youtube URLs as context

This commit is contained in:
2026-01-18 14:13:39 +01:00
parent a06913ba63
commit 883c192034
+58 -29
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -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;
@@ -319,10 +333,12 @@
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.
- 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}`, {