Adds starred and recent chat buttons to Claude's minimized sidebar
// ==UserScript==
// @name Claude Sidebar Quick Access
// @namespace https://greasyfork.org/en/scripts/575025-claude-sidebar-quick-access
// @version 3.6
// @description Adds starred and recent chat buttons to Claude's minimized sidebar
// @author Wolf68k
// @match https://claude.ai/*
// @grant none
// @license CC BY-SA 4.0
// ==/UserScript==
(function () {
'use strict';
let starButton = null;
let recentButton = null;
let popup = null;
let observer = null;
function getStarredChats() {
const headings = document.querySelectorAll('h2');
for (const h2 of headings) {
if (h2.textContent.trim().toLowerCase().startsWith('starred')) {
const ul = h2.nextElementSibling;
if (ul && ul.tagName === 'UL') {
const links = ul.querySelectorAll('a[data-dd-action-name="sidebar-chat-item"]');
const results = [];
links.forEach(link => {
const span = link.querySelector('span');
const label = span ? span.textContent.trim() : link.textContent.trim();
const href = link.getAttribute('href');
if (label && href && !results.find(r => r.href === href)) {
results.push({ label, href });
}
});
return results;
}
}
}
return [];
}
function getRecentChats() {
const headings = document.querySelectorAll('h2');
for (const h2 of headings) {
if (h2.textContent.trim().toLowerCase().startsWith('recents')) {
const ul = h2.parentElement && h2.parentElement.nextElementSibling;
if (ul && ul.tagName === 'UL') {
const links = ul.querySelectorAll('a[data-dd-action-name="sidebar-chat-item"]');
const results = [];
links.forEach(link => {
const span = link.querySelector('span');
const label = span ? span.textContent.trim() : link.textContent.trim();
const href = link.getAttribute('href');
if (label && href && !results.find(r => r.href === href)) {
results.push({ label, href });
}
});
return results;
}
}
}
return [];
}
function showPopup(type, anchorButton) {
if (popup) {
popup.remove();
popup = null;
return;
}
const items = type === 'starred' ? getStarredChats() : getRecentChats();
const btnRect = anchorButton.getBoundingClientRect();
const label = type === 'starred' ? 'Starred Chats' : 'Recent Chats';
const emptyMsg = type === 'starred' ? 'No starred chats found' : 'No recent chats found';
popup = document.createElement('div');
popup.id = 'claude-chat-popup';
popup.style.cssText = `
position: fixed;
top: ${btnRect.top}px;
left: ${btnRect.right + 8}px;
background: #1e1e2e;
border: 1px solid #444;
border-radius: 8px;
padding: 8px 0;
z-index: 99999;
min-width: 220px;
max-width: 300px;
max-height: 400px;
overflow-y: auto;
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
font-family: sans-serif;
font-size: 13px;
`;
if (items.length === 0) {
const empty = document.createElement('div');
empty.textContent = emptyMsg;
empty.style.cssText = 'padding: 10px 16px; color: #888;';
popup.appendChild(empty);
} else {
const title = document.createElement('div');
title.textContent = label;
title.style.cssText = 'padding: 6px 16px 8px; color: #ddd; font-size: 15px; font-weight: 700; border-bottom: 1px solid #333; margin-bottom: 4px;';
popup.appendChild(title);
items.forEach(chat => {
const item = document.createElement('a');
item.href = chat.href;
item.textContent = chat.label;
item.style.cssText = `
display: block;
padding: 8px 16px;
color: #ddd;
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
`;
item.addEventListener('mouseenter', () => item.style.background = '#2a2a3e');
item.addEventListener('mouseleave', () => item.style.background = 'transparent');
item.addEventListener('click', () => {
popup.remove();
popup = null;
});
popup.appendChild(item);
});
}
document.body.appendChild(popup);
setTimeout(() => {
document.addEventListener('click', function closePopup(e) {
if (!popup) return;
if (!popup.contains(e.target) && e.target !== starButton && e.target !== recentButton) {
popup.remove();
popup = null;
document.removeEventListener('click', closePopup);
}
});
}, 0);
}
function findInsertionPoint() {
const navLinks = document.querySelectorAll('a[data-dd-action-name="sidebar-nav-item"]');
if (!navLinks.length) return null;
// Find the last nav link inside the gap-px container
// Some items are 2 levels deep (a -> relative group -> gap-px)
// Others are 3 levels deep (a -> relative group -> relative -> gap-px)
for (let i = navLinks.length - 1; i >= 0; i--) {
const link = navLinks[i];
let el = link;
for (let depth = 0; depth < 5; depth++) {
el = el.parentElement;
if (!el) break;
if (el.classList.contains('gap-px')) {
// lastGroup is the direct child of container that wraps this nav item
const directChild = el.children[Array.from(el.children).findIndex(c => c.contains(link))];
return { container: el, lastGroup: directChild || el.lastElementChild };
}
}
}
return null;
}
function makeButton(id, symbol, tooltipText, type) {
const btn = document.createElement('button');
btn.id = id;
btn.title = tooltipText;
btn.innerHTML = symbol;
btn.style.cssText = `
display: flex;
flex-direction: row;
align-items: center;
justify-content: start;
gap: 12px;
width: 100%;
padding: 6px 8px;
background: transparent;
border: none;
border-radius: 6px;
color: #ffffff;
font-size: 15px;
cursor: pointer;
text-align: left;
transition: background 0.15s;
`;
btn.addEventListener('mouseenter', () => btn.style.background = 'rgba(255,255,255,0.06)');
btn.addEventListener('mouseleave', () => btn.style.background = 'transparent');
btn.addEventListener('click', (e) => {
e.stopPropagation();
showPopup(type, btn);
});
return btn;
}
function addButtons() {
if (document.getElementById('claude-starred-wrapper')) return;
const result = findInsertionPoint();
if (!result) return;
const { container, lastGroup } = result;
const starWrapper = document.createElement('div');
starWrapper.id = 'claude-starred-wrapper';
starWrapper.title = '';
starWrapper.style.cssText = 'display: block; width: 100%;';
starButton = makeButton('claude-starred-btn', '<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" xmlns="http://www.w3.org/2000/svg" style="flex-shrink: 0;" aria-hidden="true"><path d="M10 2l2.39 4.84 5.34.78-3.87 3.77.91 5.32L10 14.27l-4.77 2.44.91-5.32L2.27 7.62l5.34-.78z" stroke-linejoin="round"/></svg>', 'Starred Chats', 'starred');
starWrapper.appendChild(starButton);
const recentWrapper = document.createElement('div');
recentWrapper.id = 'claude-recent-wrapper';
recentWrapper.title = '';
recentWrapper.style.cssText = 'display: block; width: 100%;';
recentButton = makeButton('claude-recent-btn', '<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" style="flex-shrink: 0;" aria-hidden="true"><path d="M4.5 4.5 A6.5 6.5 0 1 1 2.8 12.5" fill="none"/><polygon points="1.5,4.5 6,4.5 3.8,8.5" fill="currentColor" stroke="none" transform="rotate(25, 3.8, 6)"/><line x1="8.5" y1="10" x2="8.5" y2="6.5"/><line x1="8.5" y1="10" x2="10.8" y2="11.5"/></svg>', 'Recent Chats', 'recent');
recentWrapper.appendChild(recentButton);
lastGroup.after(starWrapper);
starWrapper.after(recentWrapper);
}
function init() {
addButtons();
if (!observer) {
observer = new MutationObserver(() => {
if (!document.getElementById('claude-starred-wrapper')) {
addButtons();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
setTimeout(init, 1000);
}
})();