参考了该大佬分享的脚本贴,与下面大佬开发的桌面程序结合使用
1.新增站点https://demo.fuclaude.com
2.复制以下js代码添加到该站点的js文本框,保存即可
JS注入版
// Claude Session Key Switch - JS注入版
// 功能:使用sessionKey自动登录claude(可拖拽圆形图标,双击显示选项面板,支持夜间模式,支持前端管理密钥)
(function() {
'use strict';
// 使用localStorage替代GM_getValue和GM_setValue
const setValue = (key, value) => {
localStorage.setItem(`claude_key_switch_${key}`, JSON.stringify(value));
};
const getValue = (key, defaultValue) => {
const storedValue = localStorage.getItem(`claude_key_switch_${key}`);
return storedValue ? JSON.parse(storedValue) : defaultValue;
};
// 获取保存的令牌或使用空数组
const tokens = getValue('tokens', [
{name: 'Default', key: 'key'}
]);
// 获取保存的主题偏好或使用系统偏好
const isDarkMode = getValue('darkMode', window.matchMedia('(prefers-color-scheme: dark)').matches);
// 主题配置
const themes = {
light: {
background: '#faf9f5',
cardBackground: '#fff',
textPrimary: '#333',
textSecondary: '#666',
border: '#e0e0e0',
highlight: '#f0f7ff',
buttonBackground: '#faf9f5',
buttonColor: '#007bff',
shadow: '0 4px 20px rgba(0,0,0,0.15)'
},
dark: {
background: '#222',
cardBackground: '#333',
textPrimary: '#eee',
textSecondary: '#aaa',
border: '#444',
highlight: '#2a3b4d',
buttonBackground: '#333',
buttonColor: '#5aa9ff',
shadow: '0 4px 20px rgba(0,0,0,0.3)'
}
};
// 根据暗模式设置使用当前主题
const theme = isDarkMode ? themes.dark : themes.light;
const createElem = (tag, styles) => {
const elem = document.createElement(tag);
Object.assign(elem.style, styles);
return elem;
};
// 获取保存的位置或使用默认值
const savedPosition = {
left: getValue('buttonLeft', 10),
bottom: getValue('buttonBottom', 10)
};
// 创建圆形切换按钮
const toggleButton = createElem('button', {
width: '30px',
height: '30px',
borderRadius: '50%',
backgroundColor: theme.buttonBackground,
color: theme.buttonColor,
cursor: 'move',
position: 'fixed',
bottom: `${savedPosition.bottom}px`,
left: `${savedPosition.left}px`,
zIndex: '10000',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 2px 5px rgba(0,0,0,0.2)',
transition: 'background-color 0.3s ease',
outline: 'none',
padding: '0',
userSelect: 'none',
touchAction: 'none',
border: `1px solid ${theme.border}`
});
toggleButton.innerHTML = '🔑';
const dropdownContainer = createElem('div', {
position: 'fixed',
backgroundColor: theme.background,
padding: '20px',
borderRadius: '12px',
boxShadow: theme.shadow,
display: 'none',
flexDirection: 'column',
gap: '15px',
width: '500px',
maxHeight: '80vh',
overflowY: 'auto',
zIndex: '9999',
border: `1px solid ${theme.border}`,
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)'
});
// 添加标题和页眉控件
const headerContainer = createElem('div', {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '15px',
borderBottom: `2px solid ${theme.border}`,
paddingBottom: '10px'
});
const titleElement = createElem('h2', {
margin: '0',
color: theme.textPrimary,
fontSize: '18px'
});
titleElement.textContent = 'Claude Session Key Switch';
headerContainer.appendChild(titleElement);
// 页眉控件容器
const headerControlsContainer = createElem('div', {
display: 'flex',
alignItems: 'center',
gap: '15px'
});
// 管理密钥按钮(小图标)
const manageKeysBtn = createElem('button', {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
color: theme.textSecondary,
border: 'none',
cursor: 'pointer',
padding: '5px',
borderRadius: '50%',
fontSize: '18px',
transition: 'color 0.3s, transform 0.2s'
});
manageKeysBtn.innerHTML = '🔧';
manageKeysBtn.title = '管理密钥';
manageKeysBtn.addEventListener('mouseover', () => {
manageKeysBtn.style.color = theme.buttonColor;
manageKeysBtn.style.transform = 'rotate(30deg)';
});
manageKeysBtn.addEventListener('mouseout', () => {
manageKeysBtn.style.color = theme.textSecondary;
manageKeysBtn.style.transform = 'rotate(0deg)';
});
// 主题切换容器
const themeToggleContainer = createElem('div', {
display: 'flex',
alignItems: 'center',
cursor: 'pointer'
});
const themeIcon = createElem('span', {
color: theme.textSecondary,
fontSize: '16px',
transition: 'color 0.3s'
});
themeIcon.textContent = isDarkMode ? '🌙' : '☀️';
themeIcon.title = isDarkMode ? '切换到亮色模式' : '切换到暗色模式';
themeToggleContainer.appendChild(themeIcon);
themeToggleContainer.addEventListener('mouseover', () => {
themeIcon.style.color = theme.buttonColor;
});
themeToggleContainer.addEventListener('mouseout', () => {
themeIcon.style.color = theme.textSecondary;
});
themeToggleContainer.addEventListener('click', () => {
const newDarkMode = !isDarkMode;
setValue('darkMode', newDarkMode);
window.location.reload();
});
headerControlsContainer.appendChild(manageKeysBtn);
headerControlsContainer.appendChild(themeToggleContainer);
headerContainer.appendChild(headerControlsContainer);
dropdownContainer.appendChild(headerContainer);
// 为令牌创建网格容器
const gridContainer = createElem('div', {
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: '10px',
margin: '10px 0'
});
// 获取保存的切换时间
const switchTimes = getValue('switchTimes', {});
function refreshTokensGrid() {
// 清除现有卡片
gridContainer.innerHTML = '';
if (tokens.length === 0) {
const emptyMessage = createElem('div', {
padding: '15px',
textAlign: 'center',
color: theme.textSecondary,
backgroundColor: isDarkMode ? '#1a1a1a' : '#f8f9fa',
borderRadius: '6px',
gridColumn: '1 / span 2' // 跨越两列
});
emptyMessage.textContent = '没有保存的密钥,请点击右上角 🔧 添加';
gridContainer.appendChild(emptyMessage);
return;
}
tokens.forEach(token => {
const tokenCard = createElem('div', {
padding: '15px',
borderRadius: '8px',
backgroundColor: theme.cardBackground,
border: `1px solid ${theme.border}`,
cursor: 'pointer',
transition: 'all 0.3s ease'
});
const lastSwitchTime = switchTimes[token.name] || '未使用';
// 计算时间差
let color = theme.textSecondary; // 默认颜色
if(lastSwitchTime !== '未使用') {
const fiveHoursInMs = 5 * 60 * 60 * 1000; // 5小时转换为毫秒
const switchTimestamp = new Date(lastSwitchTime).getTime();
const currentTime = new Date().getTime();
// 如果时间差大于5小时显示绿色,否则显示红色
color = currentTime - switchTimestamp > fiveHoursInMs ? 'green' : 'red';
}
const nameElement = createElem('div', {
fontWeight: 'bold',
color: theme.textPrimary,
marginBottom: '5px'
});
nameElement.textContent = token.name;
const timeContainer = createElem('div', {
fontSize: '12px',
color: theme.textSecondary
});
const timeLabel = document.createTextNode('上次切换: ');
const timeValue = createElem('span', {
color: color
});
timeValue.textContent = lastSwitchTime;
timeContainer.appendChild(timeLabel);
timeContainer.appendChild(timeValue);
tokenCard.appendChild(nameElement);
tokenCard.appendChild(timeContainer);
tokenCard.addEventListener('mouseover', () => {
tokenCard.style.backgroundColor = theme.highlight;
tokenCard.style.borderColor = theme.buttonColor;
});
tokenCard.addEventListener('mouseout', () => {
tokenCard.style.backgroundColor = theme.cardBackground;
tokenCard.style.borderColor = theme.border;
});
tokenCard.addEventListener('click', () => {
// 更新切换时间
const now = new Date().toLocaleString('zh-CN');
switchTimes[token.name] = now;
setValue('switchTimes', switchTimes);
// 更新显示
timeValue.textContent = now;
timeValue.style.color = 'red'; // 刚切换,所以是红色
// 存储选择并触发登录
localStorage.setItem('claudeSelectedToken', token.key);
handleTokenSelection(token.key);
});
gridContainer.appendChild(tokenCard);
});
}
// 令牌网格的初始渲染
refreshTokensGrid();
dropdownContainer.appendChild(gridContainer);
let isDragging = false;
let startX, startY;
let buttonLeft = savedPosition.left;
let buttonBottom = savedPosition.bottom;
function onMouseDown(e) {
if (e.target === toggleButton) {
isDragging = true;
const buttonRect = toggleButton.getBoundingClientRect();
startX = e.clientX - buttonRect.left;
startY = e.clientY - buttonRect.top;
toggleButton.style.cursor = 'grabbing';
}
}
function onMouseMove(e) {
if (!isDragging) return;
e.preventDefault();
const newLeft = e.clientX - startX;
const newTop = e.clientY - startY;
// 从顶部计算底部位置
const bottom = window.innerHeight - newTop - toggleButton.offsetHeight;
// 确保按钮保持在窗口边界内
const maxLeft = window.innerWidth - toggleButton.offsetWidth;
const maxBottom = window.innerHeight - toggleButton.offsetHeight;
buttonLeft = Math.min(Math.max(newLeft, 0), maxLeft);
buttonBottom = Math.min(Math.max(bottom, 0), maxBottom);
// 更新按钮位置
toggleButton.style.left = `${buttonLeft}px`;
toggleButton.style.bottom = `${buttonBottom}px`;
toggleButton.style.top = 'auto';
}
function onMouseUp() {
if (isDragging) {
isDragging = false;
toggleButton.style.cursor = 'move';
// 将位置保存到localStorage
setValue('buttonLeft', buttonLeft);
setValue('buttonBottom', buttonBottom);
}
}
// 双击检测
let lastClickTime = 0;
toggleButton.addEventListener('click', (e) => {
const clickTime = new Date().getTime();
const timeDiff = clickTime - lastClickTime;
if (timeDiff < 300) { // 双击阈值
dropdownContainer.style.display = dropdownContainer.style.display === 'none' ? 'flex' : 'none';
e.stopPropagation();
}
lastClickTime = clickTime;
});
// 密钥管理面板(与主面板大小相同)
const keyManagementPanel = createElem('div', {
position: 'fixed',
backgroundColor: theme.background,
padding: '20px',
borderRadius: '12px',
boxShadow: theme.shadow,
display: 'none',
flexDirection: 'column',
gap: '15px',
width: '500px',
maxHeight: '80vh',
overflowY: 'auto',
zIndex: '10001', // 高于下拉菜单
border: `1px solid ${theme.border}`,
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)'
});
// 管理面板标题
const managementHeader = createElem('div', {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '15px',
borderBottom: `2px solid ${theme.border}`,
paddingBottom: '10px'
});
const managementTitle = createElem('h3', {
margin: '0',
color: theme.textPrimary,
fontSize: '16px'
});
managementTitle.textContent = '管理密钥';
const headerActions = createElem('div', {
display: 'flex',
gap: '15px',
alignItems: 'center'
});
// 返回按钮
const backBtn = createElem('button', {
backgroundColor: 'transparent',
border: 'none',
color: theme.textSecondary,
fontSize: '14px',
cursor: 'pointer',
padding: '0 5px',
display: 'flex',
alignItems: 'center',
gap: '3px'
});
backBtn.innerHTML = '← 返回';
backBtn.addEventListener('mouseenter', () => {
backBtn.style.color = theme.buttonColor;
});
backBtn.addEventListener('mouseleave', () => {
backBtn.style.color = theme.textSecondary;
});
backBtn.addEventListener('click', (e) => {
e.stopPropagation(); // 防止点击事件传播
keyManagementPanel.style.display = 'none';
dropdownContainer.style.display = 'flex';
});
const closeManagementBtn = createElem('button', {
backgroundColor: 'transparent',
border: 'none',
color: theme.textSecondary,
fontSize: '18px',
cursor: 'pointer',
padding: '0 5px'
});
closeManagementBtn.textContent = '×';
closeManagementBtn.addEventListener('click', (e) => {
e.stopPropagation(); // 防止事件传播
keyManagementPanel.style.display = 'none';
});
headerActions.appendChild(backBtn);
headerActions.appendChild(closeManagementBtn);
managementHeader.appendChild(managementTitle);
managementHeader.appendChild(headerActions);
keyManagementPanel.appendChild(managementHeader);
// 令牌列表(更紧凑)
const tokensList = createElem('div', {
display: 'flex',
flexDirection: 'column',
gap: '8px',
maxHeight: '200px',
overflowY: 'auto',
padding: '3px',
marginBottom: '12px'
});
function renderTokensList() {
// 清除列表
tokensList.innerHTML = '';
if (tokens.length === 0) {
const emptyMessage = createElem('div', {
padding: '15px',
textAlign: 'center',
color: theme.textSecondary,
backgroundColor: isDarkMode ? '#1a1a1a' : '#f8f9fa',
borderRadius: '6px'
});
emptyMessage.textContent = '没有保存的密钥';
tokensList.appendChild(emptyMessage);
} else {
tokens.forEach((token, index) => {
const tokenItem = createElem('div', {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px 12px',
backgroundColor: theme.cardBackground,
borderRadius: '6px',
border: `1px solid ${theme.border}`
});
const tokenInfo = createElem('div', {
display: 'flex',
flexDirection: 'column',
gap: '5px',
flex: '1'
});
const tokenName = createElem('div', {
fontWeight: 'bold',
color: theme.textPrimary
});
tokenName.textContent = token.name;
const tokenKey = createElem('div', {
fontSize: '12px',
color: theme.textSecondary
});
// 为隐私掩盖密钥
const maskedKey = token.key.length > 8
? token.key.substring(0, 4) + '•••••' + token.key.substring(token.key.length - 4)
: '•••••••••';
tokenKey.textContent = maskedKey;
tokenInfo.appendChild(tokenName);
tokenInfo.appendChild(tokenKey);
const actionsContainer = createElem('div', {
display: 'flex',
gap: '8px'
});
// 编辑按钮
const editBtn = createElem('button', {
backgroundColor: 'transparent',
border: 'none',
color: theme.buttonColor,
cursor: 'pointer',
fontSize: '14px',
padding: '5px'
});
editBtn.innerHTML = '✏️';
editBtn.title = '编辑';
editBtn.dataset.index = index;
editBtn.addEventListener('click', (e) => {
const idx = parseInt(e.currentTarget.dataset.index);
nameInput.value = tokens[idx].name;
keyInput.value = tokens[idx].key;
editingIndex = idx;
addUpdateBtn.textContent = '更新密钥';
addForm.style.borderColor = theme.buttonColor;
});
// 删除按钮
const deleteBtn = createElem('button', {
backgroundColor: 'transparent',
border: 'none',
color: '#ff5555',
cursor: 'pointer',
fontSize: '14px',
padding: '5px'
});
deleteBtn.innerHTML = '🗑️';
deleteBtn.title = '删除';
deleteBtn.dataset.index = index;
deleteBtn.addEventListener('click', (e) => {
const idx = parseInt(e.currentTarget.dataset.index);
if (confirm(`确定要删除密钥 "${tokens[idx].name}" 吗?`)) {
tokens.splice(idx, 1);
setValue('tokens', tokens);
renderTokensList();
refreshTokensGrid();
}
});
actionsContainer.appendChild(editBtn);
actionsContainer.appendChild(deleteBtn);
tokenItem.appendChild(tokenInfo);
tokenItem.appendChild(actionsContainer);
tokensList.appendChild(tokenItem);
});
}
}
keyManagementPanel.appendChild(tokensList);
// 添加/编辑令牌表单(更紧凑)
const addForm = createElem('div', {
display: 'flex',
flexDirection: 'column',
gap: '8px',
padding: '12px',
backgroundColor: theme.cardBackground,
borderRadius: '8px',
border: `1px solid ${theme.border}`
});
const formTitle = createElem('div', {
fontWeight: 'bold',
color: theme.textPrimary,
marginBottom: '5px'
});
formTitle.textContent = '添加新密钥';
const nameInputContainer = createElem('div', {
display: 'flex',
flexDirection: 'column',
gap: '5px'
});
const nameLabel = createElem('label', {
color: theme.textSecondary,
fontSize: '12px'
});
nameLabel.textContent = '名称';
const nameInput = createElem('input', {
padding: '6px 8px',
borderRadius: '4px',
border: `1px solid ${theme.border}`,
backgroundColor: isDarkMode ? '#444' : '#fff',
color: theme.textPrimary,
fontSize: '14px',
width: '100%',
boxSizing: 'border-box'
});
nameInputContainer.appendChild(nameLabel);
nameInputContainer.appendChild(nameInput);
const keyInputContainer = createElem('div', {
display: 'flex',
flexDirection: 'column',
gap: '5px'
});
const keyLabel = createElem('label', {
color: theme.textSecondary,
fontSize: '12px'
});
keyLabel.textContent = '密钥值';
const keyInput = createElem('input', {
padding: '6px 8px',
borderRadius: '4px',
border: `1px solid ${theme.border}`,
backgroundColor: isDarkMode ? '#444' : '#fff',
color: theme.textPrimary,
fontSize: '14px',
width: '100%',
boxSizing: 'border-box'
});
keyInputContainer.appendChild(keyLabel);
keyInputContainer.appendChild(keyInput);
let editingIndex = -1; // -1表示添加新的,>=0表示编辑现有的
const addUpdateBtn = createElem('button', {
padding: '5px',
backgroundColor: theme.buttonColor,
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
marginTop: '8px',
fontWeight: 'bold',
fontSize: '13px'
});
addUpdateBtn.textContent = '添加密钥';
addUpdateBtn.addEventListener('click', () => {
const name = nameInput.value.trim();
const key = keyInput.value.trim();
if (!name || !key) {
alert('名称和密钥都不能为空');
return;
}
if (editingIndex >= 0) {
// 更新现有的
tokens[editingIndex] = { name, key };
} else {
// 添加新的
tokens.push({ name, key });
}
setValue('tokens', tokens);
nameInput.value = '';
keyInput.value = '';
editingIndex = -1;
addUpdateBtn.textContent = '添加密钥';
addForm.style.borderColor = theme.border;
renderTokensList();
refreshTokensGrid();
});
const resetBtn = createElem('button', {
padding: '4px',
backgroundColor: 'transparent',
color: theme.textSecondary,
border: `1px solid ${theme.border}`,
borderRadius: '4px',
cursor: 'pointer',
marginTop: '4px',
fontSize: '12px'
});
resetBtn.textContent = '重置';
resetBtn.addEventListener('click', () => {
nameInput.value = '';
keyInput.value = '';
editingIndex = -1;
addUpdateBtn.textContent = '添加密钥';
addForm.style.borderColor = theme.border;
});
// 使表单中的按钮更小更紧凑
const formButtons = createElem('div', {
display: 'flex',
gap: '8px'
});
addUpdateBtn.style.flex = '1';
resetBtn.style.flex = '1';
formButtons.appendChild(addUpdateBtn);
formButtons.appendChild(resetBtn);
addForm.appendChild(formTitle);
addForm.appendChild(nameInputContainer);
addForm.appendChild(keyInputContainer);
addForm.appendChild(formButtons);
keyManagementPanel.appendChild(addForm);
document.body.appendChild(keyManagementPanel);
// 点击按钮时打开管理面板
manageKeysBtn.addEventListener('click', () => {
renderTokensList();
keyManagementPanel.style.display = 'flex';
dropdownContainer.style.display = 'none';
// 打开管理面板时重置表单
nameInput.value = '';
keyInput.value = '';
editingIndex = -1;
addUpdateBtn.textContent = '添加密钥';
addForm.style.borderColor = theme.border;
});
// 点击外部时关闭管理面板
document.addEventListener('click', (e) => {
if (!keyManagementPanel.contains(e.target) && e.target !== manageKeysBtn) {
keyManagementPanel.style.display = 'none';
}
});
// ESC键关闭面板
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (keyManagementPanel.style.display === 'flex') {
keyManagementPanel.style.display = 'none';
} else if (dropdownContainer.style.display === 'flex') {
dropdownContainer.style.display = 'none';
}
}
});
// 添加拖动事件
toggleButton.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
// 点击外部时关闭下拉菜单
document.addEventListener('click', (e) => {
if (!dropdownContainer.contains(e.target) && e.target !== toggleButton) {
dropdownContainer.style.display = 'none';
}
});
function handleTokenSelection(token) {
if (token === '') {
console.log('选择了空令牌。未采取任何操作。');
} else {
autoLogin(token);
}
}
function autoLogin(token) {
const currentURL = window.location.href;
let loginUrl;
if (currentURL.startsWith('https://demo.fuclaude.com/')) {
loginUrl = `https://demo.fuclaude.com/login_token?session_key=${token}`;
} else if (currentURL.startsWith('https://claude.asia/')) {
loginUrl = `https://claude.asia/login_token?session_key=${token}`;
} else {
loginUrl = `https://demo.fuclaude.com/login_token?session_key=${token}`;
}
window.location.href = loginUrl;
}
// 初始化UI
document.body.appendChild(dropdownContainer);
document.body.appendChild(toggleButton);
})();