Skip to content

Instantly share code, notes, and snippets.

@23maverick23
Created September 25, 2025 19:55
Show Gist options
  • Save 23maverick23/77fa747e12e44d0d6624fc8d96cbf4c5 to your computer and use it in GitHub Desktop.
Save 23maverick23/77fa747e12e44d0d6624fc8d96cbf4c5 to your computer and use it in GitHub Desktop.
NS: User Script: Claude Chat Table of Contents
// ==UserScript==
// @name Claude Chat TOC Navigator
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Table of Contents for Claude chat with on-demand updates to prevent UI slowdown during streaming
// @author rymoio
// @match https://*.anthropic.com/*
// @match https://claude.ai/*
// @icon https://claude.ai/favicon.ico
// @grant none
// ==/UserScript==
(function() {
'use strict';
// --- Performance tracking ---
let activeObservers = [];
let tocVisible = false;
let tocNeedsUpdate = false;
let lastUpdateTime = 0;
let globalEventListeners = []; // Track global event listeners for cleanup
// --- Inject Share Button-like Styles ---
const style = document.createElement('style');
style.textContent = `
#claude-toc-share-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
margin-left: 8px;
border-radius: 6px;
border: 1px solid #E5E7EB;
background: #FFF;
color: #23272F;
font-size: 18px;
cursor: pointer;
transition: background 0.15s, color 0.15s, border 0.15s;
position: relative;
z-index: 1001;
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
}
#claude-toc-share-btn:hover, #claude-toc-share-btn:focus {
background: #23272F;
color: #FFF;
border-color: #23272F;
outline: none;
}
#claude-toc-share-btn.needs-update {
position: relative;
}
#claude-toc-share-btn.needs-update::after {
content: '';
position: absolute;
top: 2px;
right: 2px;
width: 6px;
height: 6px;
background: #FF6B47;
border-radius: 50%;
border: 1px solid #FFF;
}
@media (prefers-color-scheme: dark) {
#claude-toc-share-btn {
background: #23272F;
color: #F7F7F8;
border-color: #363B47;
}
#claude-toc-share-btn:hover, #claude-toc-share-btn:focus {
background: #FFF;
color: #23272F;
border-color: #FFF;
}
#claude-toc-share-btn.needs-update::after {
border-color: #23272F;
}
}
#claude-toc-container {
position: fixed !important;
top: 60px !important;
right: 20px !important;
width: 320px;
max-height: 70vh;
background: #F7F7F8;
border: 1px solid #E5E7EB;
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
z-index: 9999 !important;
padding: 1.25rem 1rem 1rem 1rem;
font-family: 'Inter', 'system-ui', sans-serif;
overflow-y: auto;
border-radius: 10px;
color: #23272F;
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 240px;
min-height: 40px;
transition: opacity 0.2s;
}
#claude-toc-container.closed {
display: none !important;
}
#claude-toc-container h3 {
font-size: 1.1rem;
font-weight: 500;
margin-bottom: 1rem;
color: #23272F;
border-bottom: 1px solid #E5E7EB;
padding-bottom: 0.5rem;
letter-spacing: -0.01em;
}
.toc-loading {
padding: 1rem;
text-align: center;
color: #6B7280;
font-style: italic;
}
.toc-entry {
padding: 0.5rem 0.75rem;
border-radius: 8px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, color 0.15s;
font-size: 15px;
margin-bottom: 6px;
background: #F7F7F8;
border: 1px solid #E5E7EB;
border-left-width: 6px;
color: #23272F;
font-family: inherit;
display: flex;
align-items: center;
gap: 0.5em;
}
.toc-entry.user {
border-left-color: #D1D5DB;
}
.toc-entry.claude {
border-left-color: #FFB25B;
}
.toc-entry.artifact {
border-left-color: #FF8A00;
background: #FFF5E6;
border-color: #FFB25B;
font-weight: 500;
padding-left: 0.5rem;
margin-left: 1.5rem;
}
.toc-entry:hover, .toc-entry:focus {
background: #ECECEC;
color: #23272F;
border-color: #D1D5DB;
outline: none;
}
.toc-entry.artifact:hover, .toc-entry.artifact:focus {
background: #FFEECC;
border-color: #FF8A00;
color: #23272F;
}
.toc-entry.active {
background: #FFF3E6;
border-left-color: #FFB25B;
border-color: #FFB25B;
color: #23272F;
}
.toc-entry.artifact.active {
background: #FFEECC;
border-left-color: #FF8A00;
border-color: #FF8A00;
color: #23272F;
}
.artifact-icon {
flex-shrink: 0;
width: 16px;
height: 16px;
color: #FF8A00;
}
@media (prefers-color-scheme: dark) {
#claude-toc-container {
background: #23272F;
border-color: #363B47;
color: #F7F7F8;
}
#claude-toc-container h3 {
color: #F7F7F8;
border-bottom: 1px solid #363B47;
}
.toc-loading {
color: #9CA3AF;
}
.toc-entry {
background: #23272F;
border: 1px solid #363B47;
border-left-width: 6px;
color: #F7F7F8;
}
.toc-entry.user {
border-left-color: #363B47;
}
.toc-entry.claude {
border-left-color: #FFB25B;
}
.toc-entry.artifact {
border-left-color: #FF8A00;
background: #2A2416;
border-color: #FFB25B;
margin-left: 1.5rem;
}
.toc-entry:hover, .toc-entry:focus {
background: #181A20;
color: #F7F7F8;
border-color: #363B47;
}
.toc-entry.artifact:hover, .toc-entry.artifact:focus {
background: #332A1A;
border-color: #FF8A00;
color: #F7F7F8;
}
.toc-entry.active {
background: #2A2E39;
border-left-color: #FFB25B;
border-color: #FFB25B;
color: #F7F7F8;
}
.toc-entry.artifact.active {
background: #332A1A;
border-left-color: #FF8A00;
border-color: #FF8A00;
color: #F7F7F8;
}
.artifact-icon {
color: #FFB25B;
}
}
`;
document.head.appendChild(style);
// --- Cleanup function to disconnect all observers and event listeners ---
function cleanupObservers() {
activeObservers.forEach(observer => observer.disconnect());
activeObservers = [];
// Remove global event listeners
globalEventListeners.forEach(({ element, event, handler }) => {
element.removeEventListener(event, handler);
});
globalEventListeners = [];
}
// --- SPA Navigation Support: Watch for URL changes AND artifact panel changes ---
let lastUrl = location.href;
let lastArtifactState = document.querySelector('[data-testid="artifact-panel"]') ? 'open' : 'closed';
setInterval(() => {
const currentUrl = location.href;
const currentArtifactState = document.querySelector('[data-testid="artifact-panel"]') ? 'open' : 'closed';
if (currentUrl !== lastUrl || currentArtifactState !== lastArtifactState) {
lastUrl = currentUrl;
lastArtifactState = currentArtifactState;
cleanupObservers();
removeTOC();
tocNeedsUpdate = true;
waitForHeaderAndChatContainer((headerBar, chatContainer) => {
// Check if we're in artifact mode
const artifactPanel = document.querySelector('[data-testid="artifact-panel"]');
if (artifactPanel) {
waitForArtifactHeaderAndInsertTOC(artifactPanel, chatContainer);
} else {
waitForShareButtonAndInsertTOC(headerBar, chatContainer);
}
});
}
}, 1000); // Reduced frequency for better performance
// --- Lightweight change detection (only when TOC is closed) ---
function setupLightweightChangeDetection(chatContainer) {
if (!chatContainer) return;
// Only observe when TOC is closed to detect when updates are needed
const lightObserver = new MutationObserver((mutations) => {
if (!tocVisible) {
// Only check if significant changes occurred
const significantChange = mutations.some(mutation => {
return mutation.addedNodes.length > 0 ||
mutation.removedNodes.length > 0 ||
(mutation.type === 'childList' && mutation.target.matches &&
(mutation.target.matches('[data-message-author]') ||
mutation.target.closest('[data-message-author]')));
});
if (significantChange) {
tocNeedsUpdate = true;
updateTOCButtonState();
}
}
});
// Use less aggressive observation settings
lightObserver.observe(chatContainer, {
childList: true,
subtree: false // Only watch direct children, not deep subtree
});
activeObservers.push(lightObserver);
}
// --- Update TOC button visual state ---
function updateTOCButtonState() {
const tocBtn = document.getElementById('claude-toc-share-btn');
if (tocBtn) {
tocBtn.classList.toggle('needs-update', tocNeedsUpdate && !tocVisible);
}
}
// --- Wait for artifact header and insert TOC there ---
function waitForArtifactHeaderAndInsertTOC(artifactPanel, chatContainer) {
function check() {
const artifactHeader =
artifactPanel.querySelector('[data-testid="artifact-version-trigger"]')?.closest('.flex.items-center') ||
artifactPanel.querySelector('[data-testid="artifact-version-trigger"]')?.parentNode ||
artifactPanel.querySelector('.group\\/segmented-control') ||
artifactPanel.querySelector('[role="group"]') ||
artifactPanel.querySelector('.pr-2.pl-3.flex.items-center.justify-between') ||
artifactPanel.querySelector('.flex.items-center.justify-between') ||
artifactPanel.querySelector('.flex.items-center.flex-1.gap-2') ||
artifactPanel.querySelector('header') ||
artifactPanel.querySelector('[class*="header"]');
if (artifactHeader) {
insertTOCButtonInArtifactHeader(artifactHeader, chatContainer);
} else {
setTimeout(check, 200);
}
}
check();
}
// --- Insert TOC button in artifact header ---
function insertTOCButtonInArtifactHeader(artifactHeader, chatContainer) {
removeTOC();
const tocBtn = document.createElement('button');
tocBtn.id = 'claude-toc-share-btn';
tocBtn.setAttribute('aria-label', 'Table of Contents');
tocBtn.title = 'Table of Contents';
tocBtn.innerHTML = getListSVG();
if (artifactHeader.parentNode) {
artifactHeader.parentNode.insertBefore(tocBtn, artifactHeader.nextSibling);
} else {
artifactHeader.appendChild(tocBtn);
}
const tocContainer = document.createElement('div');
tocContainer.id = 'claude-toc-container';
tocContainer.className = 'closed';
tocContainer.innerHTML = `
<h3>Table of Contents</h3>
<div id="claude-toc-content"></div>
`;
tocContainer.style.position = 'fixed';
tocContainer.style.right = '20px';
tocContainer.style.top = '60px';
tocContainer.style.minWidth = '240px';
tocContainer.style.minHeight = '40px';
document.body.appendChild(tocContainer);
setupTOCToggle(tocBtn, tocContainer, chatContainer);
setupLightweightChangeDetection(chatContainer);
tocNeedsUpdate = true;
updateTOCButtonState();
}
// --- Remove TOC button and container if present ---
function removeTOC() {
document.getElementById('claude-toc-share-btn')?.remove();
document.getElementById('claude-toc-container')?.remove();
cleanupObservers();
}
// --- Wait for both header and chat container ---
function waitForHeaderAndChatContainer(callback) {
function check() {
let headerBar = null;
const allHeaders = document.querySelectorAll('header, [class*="Header"]');
for (const header of allHeaders) {
const hasShareBtn = Array.from(header.querySelectorAll('button, [role="button"]'))
.some(btn => btn.textContent?.trim().toLowerCase() === 'share' || btn.getAttribute('aria-label')?.toLowerCase() === 'share');
if (hasShareBtn) {
headerBar = header;
break;
}
}
if (!headerBar) {
headerBar = document.querySelector('header') || document.querySelector('[class*="Header"]');
}
const chatContainer =
document.querySelector('.flex-1.flex.flex-col.gap-3') ||
document.querySelector('.conversation-container') ||
document.querySelector('.message-container');
if (headerBar && chatContainer) {
callback(headerBar, chatContainer);
} else {
setTimeout(check, 200);
}
}
check();
}
// --- Main: Wait for Share button, then insert TOC ---
function waitForShareButtonAndInsertTOC(headerBar, chatContainer) {
let shareBtn = Array.from(headerBar.querySelectorAll('button, [role="button"]'))
.find(btn => btn.textContent?.trim().toLowerCase() === 'share' || btn.getAttribute('aria-label')?.toLowerCase() === 'share');
const buttonContainer = headerBar.querySelector('[data-testid="chat-actions"]') ||
headerBar.querySelector('.flex.items-center.gap-1') ||
headerBar.querySelector('.right-3.flex.gap-2') ||
shareBtn?.parentNode;
if (shareBtn && buttonContainer) {
insertTOCButton(shareBtn, headerBar, chatContainer, buttonContainer);
return;
} else if (buttonContainer) {
insertTOCButton(null, headerBar, chatContainer, buttonContainer);
return;
}
const observer = new MutationObserver(() => {
let shareBtn = Array.from(headerBar.querySelectorAll('button, [role="button"]'))
.find(btn => btn.textContent?.trim().toLowerCase() === 'share' || btn.getAttribute('aria-label')?.toLowerCase() === 'share');
const buttonContainer = headerBar.querySelector('[data-testid="chat-actions"]') ||
headerBar.querySelector('.flex.items-center.gap-1') ||
headerBar.querySelector('.right-3.flex.gap-2') ||
shareBtn?.parentNode;
if (buttonContainer) {
observer.disconnect();
insertTOCButton(shareBtn, headerBar, chatContainer, buttonContainer);
}
});
observer.observe(headerBar, { childList: true, subtree: true });
activeObservers.push(observer);
}
// --- Insert TOC button and panel ---
function insertTOCButton(shareBtn, headerBar, chatContainer, buttonContainer) {
removeTOC();
const tocBtn = document.createElement('button');
tocBtn.id = 'claude-toc-share-btn';
tocBtn.setAttribute('aria-label', 'Table of Contents');
tocBtn.title = 'Table of Contents';
tocBtn.innerHTML = getListSVG();
if (shareBtn && shareBtn.parentNode) {
shareBtn.parentNode.insertBefore(tocBtn, shareBtn.nextSibling);
} else if (buttonContainer) {
buttonContainer.appendChild(tocBtn);
} else {
headerBar.appendChild(tocBtn);
}
const tocContainer = document.createElement('div');
tocContainer.id = 'claude-toc-container';
tocContainer.className = 'closed';
tocContainer.innerHTML = `
<h3>Table of Contents</h3>
<div id="claude-toc-content"></div>
`;
tocContainer.style.position = 'fixed';
tocContainer.style.right = '20px';
tocContainer.style.top = '60px';
tocContainer.style.minWidth = '240px';
tocContainer.style.minHeight = '40px';
document.body.appendChild(tocContainer);
setupTOCToggle(tocBtn, tocContainer, chatContainer);
setupLightweightChangeDetection(chatContainer);
tocNeedsUpdate = true;
updateTOCButtonState();
}
// --- Setup TOC toggle functionality ---
function setupTOCToggle(tocBtn, tocContainer, chatContainer) {
let dismissHandlersActive = false;
let outsideClickHandler = null;
let escapeKeyHandler = null;
function closeTOC() {
tocVisible = false;
tocContainer.classList.add('closed');
tocBtn.innerHTML = getListSVG();
updateTOCButtonState();
// Remove dismiss handlers when closing
if (dismissHandlersActive) {
if (outsideClickHandler) {
document.removeEventListener('mousedown', outsideClickHandler, true);
}
if (escapeKeyHandler) {
document.removeEventListener('keydown', escapeKeyHandler, true);
}
dismissHandlersActive = false;
}
}
function openTOC() {
tocVisible = true;
tocContainer.classList.remove('closed');
tocBtn.innerHTML = getCloseSVG();
// Update TOC content when opening (on-demand)
if (tocNeedsUpdate) {
updateTOC();
tocNeedsUpdate = false;
}
updateTOCButtonState();
// Add dismiss handlers only after opening
if (!dismissHandlersActive) {
// Outside click handler
outsideClickHandler = (e) => {
if (
tocVisible &&
tocContainer &&
tocBtn &&
!tocContainer.contains(e.target) &&
!tocBtn.contains(e.target)
) {
closeTOC();
}
};
// Escape key handler
escapeKeyHandler = (e) => {
if (e.key === 'Escape' && tocVisible) {
e.preventDefault();
closeTOC();
}
};
// Add the handlers
document.addEventListener('mousedown', outsideClickHandler, true);
document.addEventListener('keydown', escapeKeyHandler, true);
dismissHandlersActive = true;
}
}
// Button click handler - this is the only handler we attach immediately
const buttonClickHandler = (e) => {
e.stopPropagation();
e.preventDefault();
if (tocVisible) {
closeTOC();
} else {
openTOC();
}
};
tocBtn.addEventListener('click', buttonClickHandler);
}
// --- Update TOC entries (now called on-demand only) ---
function updateTOC() {
const tocContent = document.getElementById('claude-toc-content');
if (!tocContent) return;
// Show loading state
tocContent.innerHTML = '<div class="toc-loading">Updating...</div>';
// Use requestAnimationFrame to avoid blocking the UI
requestAnimationFrame(() => {
try {
const allElements = collectTOCElements();
renderTOCEntries(tocContent, allElements);
lastUpdateTime = Date.now();
} catch (error) {
console.warn('TOC update failed:', error);
tocContent.innerHTML = '<div class="toc-loading">Failed to load</div>';
}
});
}
// --- Collect all TOC elements ---
function collectTOCElements() {
let allElements = [];
// User messages
const userSelectors = [
'.group.relative.inline-flex.gap-2.bg-bg-300.rounded-xl',
'.user-message',
'[data-message-author="user"]',
'.message.user'
];
let userMessages = [];
for (const selector of userSelectors) {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
userMessages = Array.from(elements);
break;
}
}
userMessages.forEach((msg, index) => {
let textElement = msg.querySelector('[data-testid="user-message"]') ||
msg.querySelector('.message-content') ||
msg;
const textContent = textElement?.textContent?.trim();
if (textContent) {
allElements.push({
type: 'user',
content: truncateText(textContent, 50),
element: msg,
position: getElementPosition(msg)
});
}
});
// Claude responses
const claudeSelectors = [
'.group.relative.-tracking-\\[0\\.015em\\]',
'.claude-message',
'[data-message-author="assistant"]',
'.message.assistant'
];
let claudeMessages = [];
for (const selector of claudeSelectors) {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
claudeMessages = Array.from(elements);
break;
}
}
claudeMessages.forEach((msg, index) => {
let textElement = msg.querySelector('.whitespace-pre-wrap.break-words') ||
msg.querySelector('.message-content') ||
msg.querySelector('p');
const textContent = textElement?.textContent?.trim();
if (textContent) {
allElements.push({
type: 'claude',
content: truncateText(textContent, 50),
element: msg,
position: getElementPosition(msg)
});
}
});
// Artifacts
const artifactSelectors = [
'.artifact-block-cell',
'.group\\/artifact-block',
'[class*="artifact-block"]',
'[class*="artifact"]'
];
let artifacts = [];
for (const selector of artifactSelectors) {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
artifacts = Array.from(elements);
break;
}
}
if (artifacts.length === 0) {
const potentialArtifacts = document.querySelectorAll('div');
artifacts = Array.from(potentialArtifacts).filter(el => {
const classes = el.className;
return classes.includes('artifact') ||
el.querySelector('[class*="artifact"]') ||
el.querySelector('.leading-tight.text-sm.line-clamp-1');
});
}
artifacts.forEach((artifact, index) => {
let titleElement = artifact.querySelector('.leading-tight.text-sm.line-clamp-1') ||
artifact.querySelector('[class*="title"]') ||
artifact.querySelector('h1, h2, h3, h4, h5, h6') ||
artifact.querySelector('.text-sm');
let artifactTitle = titleElement?.textContent?.trim();
if (!artifactTitle || artifactTitle.length < 3) {
artifactTitle = 'Artifact';
const classes = artifact.className.toLowerCase();
if (classes.includes('react') || artifact.querySelector('[class*="react"]')) {
artifactTitle = 'React Component';
} else if (classes.includes('html') || artifact.querySelector('iframe')) {
artifactTitle = 'HTML Document';
} else if (classes.includes('code') || artifact.querySelector('code, pre')) {
artifactTitle = 'Code Snippet';
} else if (classes.includes('text') || classes.includes('markdown')) {
artifactTitle = 'Text Document';
}
}
allElements.push({
type: 'artifact',
content: truncateText(artifactTitle, 40),
element: artifact,
position: getElementPosition(artifact)
});
});
// Sort by DOM position
allElements.sort((a, b) => a.position - b.position);
return allElements;
}
// --- Render TOC entries ---
function renderTOCEntries(tocContent, allElements) {
tocContent.innerHTML = '';
if (allElements.length === 0) {
tocContent.innerHTML = '<div class="toc-loading">No messages found</div>';
return;
}
allElements.forEach((item, idx) => {
const entry = document.createElement('div');
entry.className = `toc-entry ${item.type}`;
entry.tabIndex = 0;
entry.dataset.index = idx;
if (item.type === 'artifact') {
entry.innerHTML = `
<div class="artifact-icon">${getArtifactSVG()}</div>
<span style="font-style: italic;">${item.content}</span>
`;
} else if (item.type === 'user') {
entry.innerHTML = `
<div class="artifact-icon">${getUserMessageSVG()}</div>
<span>${item.content}</span>
`;
} else {
entry.innerHTML = `
<div class="artifact-icon">${getClaudeMessageSVG()}</div>
<span>${item.content}</span>
`;
}
entry.addEventListener('click', () => {
item.element.scrollIntoView({ behavior: 'smooth', block: 'start' });
highlightElement(item.element);
setActiveEntry(idx);
});
entry.addEventListener('mouseenter', () => {
entry.classList.add('active');
});
entry.addEventListener('mouseleave', () => {
entry.classList.remove('active');
});
entry.addEventListener('focus', () => {
entry.classList.add('active');
});
entry.addEventListener('blur', () => {
entry.classList.remove('active');
});
tocContent.appendChild(entry);
});
}
// --- Helper functions (unchanged) ---
function getElementPosition(element) {
return element.getBoundingClientRect().top + window.scrollY;
}
function truncateText(text, maxLength) {
if (text.length <= maxLength) return text;
return text.substr(0, maxLength) + '...';
}
function highlightElement(element) {
const originalBackground = element.style.background;
const originalTransition = element.style.transition;
element.style.transition = 'background-color 0.5s ease';
element.style.backgroundColor = isDarkMode()
? 'rgba(255, 178, 91, 0.3)'
: 'rgba(255, 178, 91, 0.15)';
setTimeout(() => {
element.style.backgroundColor = originalBackground;
setTimeout(() => {
element.style.transition = originalTransition;
}, 500);
}, 1000);
}
function isDarkMode() {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
function setActiveEntry(idx) {
document.querySelectorAll('.toc-entry').forEach((el, i) => {
el.classList.toggle('active', i === idx);
});
}
function getListSVG() {
return `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<rect x="4" y="7" width="16" height="2" rx="1" fill="currentColor"/>
<rect x="4" y="11" width="16" height="2" rx="1" fill="currentColor"/>
<rect x="4" y="15" width="16" height="2" rx="1" fill="currentColor"/>
</svg>
`;
}
function getCloseSVG() {
return `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<line x1="6" y1="6" x2="18" y2="18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<line x1="18" y1="6" x2="6" y2="18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
`;
}
function getArtifactSVG() {
return `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<polyline points="14,2 14,8 20,8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<line x1="16" y1="13" x2="8" y2="13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<line x1="16" y1="17" x2="8" y2="17" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<line x1="10" y1="9" x2="8" y2="9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
`;
}
function getUserMessageSVG() {
return `
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
</svg>
`;
}
function getClaudeMessageSVG() {
return `
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 3C6.48 3 2 6.58 2 11c0 2.03.78 3.87 2.06 5.28L3 21l5.28-1.06C9.69 20.22 10.82 20.5 12 20.5c5.52 0 10-3.58 10-8S17.52 3 12 3z"/>
</svg>
`;
}
// --- Start ---
waitForHeaderAndChatContainer((headerBar, chatContainer) => {
waitForShareButtonAndInsertTOC(headerBar, chatContainer);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment