feat: add character name indicators to assistant messages
- 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 <noreply@anthropic.com>
This commit is contained in:
127
src/main.js
127
src/main.js
@@ -120,6 +120,33 @@ function autoResize(textarea) {
|
|||||||
textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
|
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
|
// Add message to chat
|
||||||
function addMessage(content, isUser = false, skipActions = false, timestamp = null) {
|
function addMessage(content, isUser = false, skipActions = false, timestamp = null) {
|
||||||
const messageDiv = document.createElement('div');
|
const messageDiv = document.createElement('div');
|
||||||
@@ -156,10 +183,20 @@ function addMessage(content, isUser = false, skipActions = false, timestamp = nu
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Assistant messages: render as markdown
|
// 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
|
// Apply syntax highlighting to code blocks
|
||||||
contentDiv.querySelectorAll('pre code').forEach((block) => {
|
messageContent.querySelectorAll('pre code').forEach((block) => {
|
||||||
hljs.highlightElement(block);
|
hljs.highlightElement(block);
|
||||||
|
|
||||||
// Add copy button to code blocks
|
// Add copy button to code blocks
|
||||||
@@ -326,40 +363,7 @@ async function handleSwipeNavigation(messageDiv, direction) {
|
|||||||
const contentDiv = messageDiv.querySelector('.message-content');
|
const contentDiv = messageDiv.querySelector('.message-content');
|
||||||
console.log('Found contentDiv:', contentDiv);
|
console.log('Found contentDiv:', contentDiv);
|
||||||
console.log('Setting content to:', swipeInfo.content);
|
console.log('Setting content to:', swipeInfo.content);
|
||||||
contentDiv.innerHTML = marked.parse(swipeInfo.content);
|
renderAssistantContent(contentDiv, 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 = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
||||||
<rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
|
|
||||||
<path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
|
||||||
</svg>`;
|
|
||||||
copyBtn.title = 'Copy code';
|
|
||||||
copyBtn.addEventListener('click', () => {
|
|
||||||
navigator.clipboard.writeText(block.textContent);
|
|
||||||
copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
||||||
<path d="M3 8l3 3 7-7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>`;
|
|
||||||
copyBtn.classList.add('copied');
|
|
||||||
setTimeout(() => {
|
|
||||||
copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
||||||
<rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
|
|
||||||
<path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
|
||||||
</svg>`;
|
|
||||||
copyBtn.classList.remove('copied');
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
pre.style.position = 'relative';
|
|
||||||
pre.appendChild(copyBtn);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update swipe controls
|
// Update swipe controls
|
||||||
updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);
|
updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);
|
||||||
@@ -509,13 +513,7 @@ async function generateSwipeNonStream(messageDiv, userMessage) {
|
|||||||
|
|
||||||
// Update the message content
|
// Update the message content
|
||||||
const contentDiv = messageDiv.querySelector('.message-content');
|
const contentDiv = messageDiv.querySelector('.message-content');
|
||||||
contentDiv.innerHTML = marked.parse(swipeInfo.content);
|
renderAssistantContent(contentDiv, swipeInfo.content);
|
||||||
|
|
||||||
// Apply syntax highlighting
|
|
||||||
contentDiv.querySelectorAll('pre code').forEach((block) => {
|
|
||||||
hljs.highlightElement(block);
|
|
||||||
addCopyButtonToCode(block);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update swipe controls
|
// Update swipe controls
|
||||||
updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);
|
updateSwipeControls(messageDiv, swipeInfo.current, swipeInfo.total);
|
||||||
@@ -547,13 +545,7 @@ async function generateSwipeStream(messageDiv, userMessage) {
|
|||||||
fullContent += token;
|
fullContent += token;
|
||||||
|
|
||||||
// Update content with markdown rendering
|
// Update content with markdown rendering
|
||||||
contentDiv.innerHTML = marked.parse(fullContent);
|
renderAssistantContent(contentDiv, fullContent);
|
||||||
|
|
||||||
// Apply syntax highlighting
|
|
||||||
contentDiv.querySelectorAll('pre code').forEach((block) => {
|
|
||||||
hljs.highlightElement(block);
|
|
||||||
addCopyButtonToCode(block);
|
|
||||||
});
|
|
||||||
|
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
});
|
});
|
||||||
@@ -692,40 +684,7 @@ async function sendMessage(message, isRegenerate = false) {
|
|||||||
fullContent += token;
|
fullContent += token;
|
||||||
|
|
||||||
// Update content with markdown rendering
|
// Update content with markdown rendering
|
||||||
streamingContentDiv.innerHTML = marked.parse(fullContent);
|
renderAssistantContent(streamingContentDiv, 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 = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
||||||
<rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
|
|
||||||
<path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
|
||||||
</svg>`;
|
|
||||||
copyBtn.title = 'Copy code';
|
|
||||||
copyBtn.addEventListener('click', () => {
|
|
||||||
navigator.clipboard.writeText(block.textContent);
|
|
||||||
copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
||||||
<path d="M3 8l3 3 7-7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>`;
|
|
||||||
copyBtn.classList.add('copied');
|
|
||||||
setTimeout(() => {
|
|
||||||
copyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
||||||
<rect x="5" y="5" width="9" height="9" stroke="currentColor" stroke-width="1.5" fill="none" rx="1"/>
|
|
||||||
<path d="M3 11V3a1 1 0 0 1 1-1h8" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
|
||||||
</svg>`;
|
|
||||||
copyBtn.classList.remove('copied');
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
pre.style.position = 'relative';
|
|
||||||
pre.appendChild(copyBtn);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -306,6 +306,16 @@ body {
|
|||||||
margin-left: 4px;
|
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 action buttons */
|
||||||
.message-actions {
|
.message-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
Reference in New Issue
Block a user