From e0239acedab6b41217ac845b40d5a408a27b887f Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 14 Oct 2025 12:08:39 -0700 Subject: [PATCH] feat: add character name indicators to assistant messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Character names display in accent color above all assistant messages - Created renderAssistantContent() helper for consistent rendering - Character names show on new messages, history, swipes, and regenerations - Styled with small, bold text for clear attribution - Enhances multi-character roleplay clarity 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/main.js | 127 +++++++++++++++++-------------------------------- src/styles.css | 10 ++++ 2 files changed, 53 insertions(+), 84 deletions(-) diff --git a/src/main.js b/src/main.js index a8fc5e5..efd50b5 100644 --- a/src/main.js +++ b/src/main.js @@ -120,6 +120,33 @@ function autoResize(textarea) { textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px'; } +// Helper function to render assistant message content with character name +function renderAssistantContent(contentDiv, messageText) { + // Clear existing content + contentDiv.innerHTML = ''; + + // Add character name indicator + if (currentCharacter && currentCharacter.name) { + const nameIndicator = document.createElement('div'); + nameIndicator.className = 'character-name-indicator'; + nameIndicator.textContent = currentCharacter.name; + contentDiv.appendChild(nameIndicator); + } + + // Add message content + const messageContent = document.createElement('div'); + messageContent.innerHTML = marked.parse(messageText); + contentDiv.appendChild(messageContent); + + // Apply syntax highlighting to code blocks + messageContent.querySelectorAll('pre code').forEach((block) => { + hljs.highlightElement(block); + addCopyButtonToCode(block); + }); + + return messageContent; +} + // Add message to chat function addMessage(content, isUser = false, skipActions = false, timestamp = null) { const messageDiv = document.createElement('div'); @@ -156,10 +183,20 @@ function addMessage(content, isUser = false, skipActions = false, timestamp = nu } } else { // Assistant messages: render as markdown - contentDiv.innerHTML = marked.parse(content); + // Add character name indicator if character exists + if (currentCharacter && currentCharacter.name) { + const nameIndicator = document.createElement('div'); + nameIndicator.className = 'character-name-indicator'; + nameIndicator.textContent = currentCharacter.name; + contentDiv.appendChild(nameIndicator); + } + + const messageContent = document.createElement('div'); + messageContent.innerHTML = marked.parse(content); + contentDiv.appendChild(messageContent); // Apply syntax highlighting to code blocks - contentDiv.querySelectorAll('pre code').forEach((block) => { + messageContent.querySelectorAll('pre code').forEach((block) => { hljs.highlightElement(block); // Add copy button to code blocks @@ -326,40 +363,7 @@ async function handleSwipeNavigation(messageDiv, direction) { const contentDiv = messageDiv.querySelector('.message-content'); console.log('Found contentDiv:', contentDiv); console.log('Setting content to:', swipeInfo.content); - contentDiv.innerHTML = marked.parse(swipeInfo.content); - - // Apply syntax highlighting to code blocks - contentDiv.querySelectorAll('pre code').forEach((block) => { - hljs.highlightElement(block); - - // Add copy button - const pre = block.parentElement; - if (!pre.querySelector('.copy-btn')) { - const copyBtn = document.createElement('button'); - copyBtn.className = 'copy-btn'; - copyBtn.innerHTML = ` - - - `; - copyBtn.title = 'Copy code'; - copyBtn.addEventListener('click', () => { - navigator.clipboard.writeText(block.textContent); - copyBtn.innerHTML = ` - - `; - copyBtn.classList.add('copied'); - setTimeout(() => { - copyBtn.innerHTML = ` - - - `; - copyBtn.classList.remove('copied'); - }, 2000); - }); - pre.style.position = 'relative'; - pre.appendChild(copyBtn); - } - }); + renderAssistantContent(contentDiv, swipeInfo.content); // Update swipe controls updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total); @@ -509,13 +513,7 @@ async function generateSwipeNonStream(messageDiv, userMessage) { // Update the message content const contentDiv = messageDiv.querySelector('.message-content'); - contentDiv.innerHTML = marked.parse(swipeInfo.content); - - // Apply syntax highlighting - contentDiv.querySelectorAll('pre code').forEach((block) => { - hljs.highlightElement(block); - addCopyButtonToCode(block); - }); + renderAssistantContent(contentDiv, swipeInfo.content); // Update swipe controls updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total); @@ -547,13 +545,7 @@ async function generateSwipeStream(messageDiv, userMessage) { fullContent += token; // Update content with markdown rendering - contentDiv.innerHTML = marked.parse(fullContent); - - // Apply syntax highlighting - contentDiv.querySelectorAll('pre code').forEach((block) => { - hljs.highlightElement(block); - addCopyButtonToCode(block); - }); + renderAssistantContent(contentDiv, fullContent); messagesContainer.scrollTop = messagesContainer.scrollHeight; }); @@ -692,40 +684,7 @@ async function sendMessage(message, isRegenerate = false) { fullContent += token; // Update content with markdown rendering - streamingContentDiv.innerHTML = marked.parse(fullContent); - - // Apply syntax highlighting - streamingContentDiv.querySelectorAll('pre code').forEach((block) => { - hljs.highlightElement(block); - - // Add copy button - const pre = block.parentElement; - if (!pre.querySelector('.copy-btn')) { - const copyBtn = document.createElement('button'); - copyBtn.className = 'copy-btn'; - copyBtn.innerHTML = ` - - - `; - copyBtn.title = 'Copy code'; - copyBtn.addEventListener('click', () => { - navigator.clipboard.writeText(block.textContent); - copyBtn.innerHTML = ` - - `; - copyBtn.classList.add('copied'); - setTimeout(() => { - copyBtn.innerHTML = ` - - - `; - copyBtn.classList.remove('copied'); - }, 2000); - }); - pre.style.position = 'relative'; - pre.appendChild(copyBtn); - } - }); + renderAssistantContent(streamingContentDiv, fullContent); messagesContainer.scrollTop = messagesContainer.scrollHeight; }); diff --git a/src/styles.css b/src/styles.css index 44b8c4b..766517b 100644 --- a/src/styles.css +++ b/src/styles.css @@ -306,6 +306,16 @@ body { margin-left: 4px; } +/* Character name indicator */ +.character-name-indicator { + font-size: 11px; + font-weight: 600; + color: var(--accent); + margin-bottom: 4px; + opacity: 0.9; + user-select: none; +} + /* Message action buttons */ .message-actions { position: absolute;