Claude Style Selector v1.0.0
This is version 1.0.0 of the style-selector user script.
Script Content
// ==UserScript==
// @name Claude Style Selector
// @namespace https://github.com/tjhleeds/user-scripts/
// @version 1.0.0
// @description Display and select Claude conversation styles from a sidebar
// @author tjhleeds using Claude
// @match https://claude.ai/
// @match https://claude.ai/new
// @match https://claude.ai/chat/*
// @match https://claude.ai/project/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const MAX_RETRIES = 10;
const RETRY_DELAY_MS = 2000;
let styles = [];
let sidebarElement = null;
function getCurrentlySelectedStyle() {
// Check which style button is currently pressed
const buttons = document.querySelectorAll('button[aria-pressed="true"]');
for (const button of buttons) {
const svgPath = button.querySelector('svg path[d*="M15.5117"]');
if (svgPath) {
// This is a pressed style button - find the style name
const p = button.querySelector('p');
return p ? p.textContent.trim() : null;
}
}
return null;
}
function selectStyle(styleName) {
return new Promise((resolve, reject) => {
// Open the tools menu
const openToolsMenuButton = document.querySelector('[aria-label="Open tools menu"]');
if (!openToolsMenuButton) {
reject('Tools menu button not found');
return;
}
openToolsMenuButton.click();
// Wait for menu to open
setTimeout(() => {
// Find the style button (feather icon)
const styleButtons = document.querySelectorAll('button');
let styleButton;
for (const button of styleButtons) {
const svgPath = button.querySelector('svg path[d*="M15.5117"]');
if (svgPath) {
styleButton = button;
break;
}
}
if (!styleButton) {
openToolsMenuButton.click(); // Close menu
reject('Style button not found');
return;
}
styleButton.click();
// Wait for styles submenu to appear
setTimeout(() => {
// Find the style to click
const paragraphs = document.querySelectorAll('p');
let foundButton = null;
for (const p of paragraphs) {
if (p.textContent.trim() === styleName) {
foundButton = p.closest('button');
if (foundButton) break;
}
}
if (foundButton) {
foundButton.click();
console.log('Claude Style Selector: Selected style:', styleName);
// Click the prompt textarea to return focus
setTimeout(() => {
const promptTextarea = document.querySelector('[aria-label="Write your prompt to Claude"]');
if (promptTextarea) {
promptTextarea.click();
}
resolve();
}, 100);
} else {
openToolsMenuButton.click(); // Close menu
reject('Style not found: ' + styleName);
}
}, 500);
}, 500);
});
}
function updateSidebarSelection() {
if (!sidebarElement) return;
const currentStyle = getCurrentlySelectedStyle();
const listItems = sidebarElement.querySelectorAll('li');
for (const item of listItems) {
const styleName = item.getAttribute('data-style-name');
if (styleName === currentStyle) {
item.style.color = '#fff';
item.style.fontWeight = '600';
} else {
item.style.color = '#ccc';
item.style.fontWeight = '400';
}
}
}
function discoverStyles() {
return new Promise((resolve, reject) => {
// Open the tools menu
const openToolsMenuButton = document.querySelector('[aria-label="Open tools menu"]');
if (!openToolsMenuButton) {
reject('Tools menu button not found');
return;
}
openToolsMenuButton.click();
// Wait for menu to open
setTimeout(() => {
// Find the style button (feather icon)
const styleButtons = document.querySelectorAll('button');
let styleButton;
for (const button of styleButtons) {
const svgPath = button.querySelector('svg path[d*="M15.5117"]');
if (svgPath) {
styleButton = button;
break;
}
}
if (!styleButton) {
openToolsMenuButton.click(); // Close menu
reject('Style button not found');
return;
}
styleButton.click();
// Wait for styles submenu to appear
setTimeout(() => {
// Extract all style names from elements in buttons
const paragraphs = document.querySelectorAll('p');
const discoveredStyles = [];
for (const p of paragraphs) {
const text = p.textContent.trim();
const button = p.closest('button');
// Only include if it's in a button (likely a style option)
if (button && text && !discoveredStyles.includes(text)) {
// Check if this looks like a style name (not other UI text)
// We'll be permissive and include anything in the submenu
discoveredStyles.push(text);
}
}
// Close the menu by clicking the tools button again
openToolsMenuButton.click();
console.log('Claude Style Selector: Discovered styles:', discoveredStyles);
resolve(discoveredStyles);
}, 500);
}, 500);
});
}
function createSidebar() {
const sidebar = document.createElement('div');
sidebar.id = 'claude-style-selector-sidebar';
sidebar.style.cssText = `
position: fixed;
top: 0;
right: 0;
width: 200px;
height: 100vh;
background: #1a1a1a;
border-left: 1px solid #333;
padding: 20px;
box-sizing: border-box;
z-index: 1000;
overflow-y: auto;
font-family: system-ui, -apple-system, sans-serif;
`;
const title = document.createElement('h3');
title.textContent = 'Styles';
title.style.cssText = `
color: #fff;
font-size: 16px;
margin: 0 0 15px 0;
font-weight: 600;
`;
sidebar.appendChild(title);
const styleList = document.createElement('ul');
styleList.style.cssText = `
list-style: none;
padding: 0;
margin: 0;
`;
for (const styleName of styles) {
const listItem = document.createElement('li');
listItem.setAttribute('data-style-name', styleName);
listItem.style.cssText = `
color: #ccc;
padding: 8px 0;
font-size: 14px;
cursor: pointer;
transition: color 0.2s;
`;
listItem.textContent = styleName;
// Add click handler
listItem.addEventListener('click', async () => {
try {
await selectStyle(styleName);
updateSidebarSelection();
} catch (error) {
console.error('Claude Style Selector: Failed to select style:', error);
}
});
// Add hover effect
listItem.addEventListener('mouseenter', () => {
if (listItem.style.color === 'rgb(255, 255, 255)') return; // Don't change if selected
listItem.style.color = '#fff';
});
listItem.addEventListener('mouseleave', () => {
if (listItem.style.fontWeight === '600') return; // Don't change if selected
listItem.style.color = '#ccc';
});
styleList.appendChild(listItem);
}
sidebar.appendChild(styleList);
document.body.appendChild(sidebar);
sidebarElement = sidebar;
// Push main content left to make room for sidebar
const mainContent = document.querySelector('body > div:first-child');
if (mainContent) {
mainContent.style.marginRight = '200px';
}
// Set initial selection state
updateSidebarSelection();
console.log('Claude Style Selector: Sidebar created with', styles.length, 'styles');
}
async function initialize() {
try {
styles = await discoverStyles();
createSidebar();
} catch (error) {
console.error('Claude Style Selector: Failed to initialize:', error);
}
}
function waitForChatInterface(retryCount = 0) {
const sendMessageButton = document.querySelector('[aria-label="Send message"]');
if (sendMessageButton) {
console.log('Claude Style Selector: Chat interface loaded.');
initialize();
} else if (retryCount < MAX_RETRIES) {
setTimeout(() => waitForChatInterface(retryCount + 1), RETRY_DELAY_MS);
} else {
console.log('Claude Style Selector: Chat interface not found after multiple retries.');
}
}
// Start checking for the chat interface
waitForChatInterface();
})();