// ==UserScript== // @name Claude Floating Control Panel // @name:en Claude Floating Control Panel // @namespace usage-and-quick-settings-of-claude // @author Yalums // @version 1.2 // @description 浮动面板显示Claude的Usage和功能开关 // @description:en Floating panel to display Claude usage and feature toggles // @match https://claude.ai/* // @grant none // @run-at document-idle // @license GNU General Public License v3.0 // @downloadURL https://update.greasyfork.icu/scripts/552139/Claude%20Floating%20Control%20Panel.user.js // @updateURL https://update.greasyfork.icu/scripts/552139/Claude%20Floating%20Control%20Panel.meta.js // ==/UserScript== (function() { 'use strict'; const FEATURES = [ { key: 'enabled_monkeys_in_a_barrel', name: 'Code execution', desc: 'Virtual code environment (supports archives)', exclusive: 'enabled_artifacts_attachments' }, { key: 'enabled_artifacts_attachments', name: 'Repl Tool', desc: 'Additional features for Artifacts', exclusive: 'enabled_monkeys_in_a_barrel' }, { key: 'enabled_saffron', name: 'Memory', desc: 'Cross-window memory' }, { key: 'enabled_saffron_search', name: 'Search chats', desc: 'Chat search' }, { key: 'enabled_sourdough', name: 'Projects', desc: 'Project memory' }, ]; let panelState = { isExpanded: localStorage.getItem('claudePanel_expanded') !== 'false', position: JSON.parse(localStorage.getItem('claudePanel_position') || '{"right":"20px","bottom":"20px"}') }; async function getUserSettings() { try { const response = await fetch('/api/account', { credentials: 'include' }); const data = await response.json(); return data.settings; } catch (err) { console.error('[Claude Panel] Failed to fetch settings:', err); return null; } } async function toggleFeature(key, currentValue, exclusiveKey = null) { try { const body = { [key]: !currentValue }; if (exclusiveKey && !currentValue) { body[exclusiveKey] = false; } const response = await fetch('/api/account/settings', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(body) }); if (!response.ok) { console.error('[Claude Panel] HTTP error:', response.status); return { success: false }; } const result = await response.json(); return { success: true, data: result }; } catch (err) { console.error('[Claude Panel] Toggle failed:', err); return { success: false }; } } async function getUsageData() { try { const orgsResponse = await fetch('/api/organizations', { credentials: 'include' }); const orgs = await orgsResponse.json(); const orgId = orgs[0]?.uuid; if (!orgId) return null; const usageResponse = await fetch(`/api/organizations/${orgId}/usage`, { credentials: 'include' }); return await usageResponse.json(); } catch (err) { console.error('[Claude Panel] Failed to fetch usage:', err); return null; } } function formatResetTime(isoTime) { if (!isoTime) return 'N/A'; const date = new Date(isoTime); const now = new Date(); const diff = date - now; const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (minutes < 1) return 'Resetting soon'; if (minutes < 60) return `In ${minutes} min`; if (hours < 24) return `In ${hours} hr`; return `In ${days} days`; } function injectStyles() { const style = document.createElement('style'); style.textContent = ` #claude-control-panel { position: fixed; z-index: 9999; background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); border: 1px solid #404040; border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; color: #ffffff; max-width: 400px; max-height: 90vh; overflow: hidden; transition: all 0.3s ease; } #claude-control-panel.collapsed { width: 60px !important; height: 60px !important; } .panel-header { padding: 16px; background: rgba(255, 255, 255, 0.05); border-bottom: 1px solid #404040; display: flex; align-items: center; justify-content: space-between; cursor: move; user-select: none; } .panel-header h3 { margin: 0; font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; } .panel-controls { display: flex; gap: 8px; } .panel-btn { background: rgba(255, 255, 255, 0.1); border: none; border-radius: 6px; width: 28px; height: 28px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } .panel-btn:hover { background: rgba(255, 255, 255, 0.2); } .panel-content { max-height: calc(90vh - 60px); overflow-y: auto; padding: 16px; } .collapsed .panel-content, .collapsed .panel-header h3 span { display: none; } .usage-section { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 16px; margin-bottom: 16px; } .usage-item { margin-bottom: 16px; } .usage-item:last-child { margin-bottom: 0; } .usage-label { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 13px; } .usage-label-text { font-weight: 500; color: #e0e0e0; } .usage-label-time { color: #909090; font-size: 12px; } .usage-bar { height: 8px; background: rgba(255, 255, 255, 0.1); border-radius: 4px; overflow: hidden; margin-bottom: 4px; } .usage-bar-fill { height: 100%; background: linear-gradient(90deg, #4ade80 0%, #22c55e 100%); transition: width 0.3s ease; } .usage-bar-fill.warning { background: linear-gradient(90deg, #fbbf24 0%, #f59e0b 100%); } .usage-bar-fill.danger { background: linear-gradient(90deg, #f87171 0%, #ef4444 100%); } .usage-percent { text-align: right; font-size: 12px; color: #b0b0b0; } .features-section { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 16px; } .section-title { font-size: 14px; font-weight: 600; margin-bottom: 12px; color: #e0e0e0; } .feature-item { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.1); gap: 24px; } .feature-item:last-child { border-bottom: none; } .feature-info { flex: 1; min-width: 0; } .feature-name { font-size: 13px; font-weight: 500; color: #e0e0e0; margin-bottom: 4px; } .feature-desc { font-size: 11px; color: #909090; line-height: 1.3; } .feature-toggle { padding: 6px 12px; border-radius: 6px; border: none; font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; min-width: 50px; flex-shrink: 0; } .feature-toggle.on { background: #22c55e; color: white; } .feature-toggle.off { background: rgba(255, 255, 255, 0.1); color: #909090; } .feature-toggle:hover:not(:disabled) { transform: scale(1.05); } .feature-toggle:disabled { opacity: 0.5; cursor: not-allowed; } .refresh-btn { width: 100%; padding: 10px; background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; color: #e0e0e0; font-size: 13px; cursor: pointer; margin-top: 16px; transition: all 0.2s; } .refresh-btn:hover { background: rgba(255, 255, 255, 0.15); } .loading { text-align: center; padding: 20px; color: #909090; font-size: 13px; } .panel-content::-webkit-scrollbar { width: 6px; } .panel-content::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.05); } .panel-content::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 3px; } .panel-content::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.3); } `; document.head.appendChild(style); } function createPanel() { const panel = document.createElement('div'); panel.id = 'claude-control-panel'; panel.style.right = panelState.position.right || '20px'; panel.style.bottom = panelState.position.bottom || '20px'; if (!panelState.isExpanded) { panel.classList.add('collapsed'); } panel.innerHTML = `