Advanced GUI cookie viewer and manager for current domain
// ==UserScript==
// @name Cookie Manager GUI
// @namespace gl4_manu
// @version 1.0.0
// @description Advanced GUI cookie viewer and manager for current domain
// @author gl4_manu
// @match *://*/*
// @grant GM_setClipboard
// @grant GM_deleteCookie
// @grant GM_setCookie
// @grant GM_listValues
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
'use strict';
class CookieGUI {
constructor() {
this.cookies = {};
this.isDragging = false;
this.dragOffset = { x: 0, y: 0 };
this.currentSort = 'name';
this.sortDirection = 'asc';
this.createUI();
this.loadCookies();
this.setupKeyboardShortcuts();
}
parseCookies() {
return document.cookie
.split(';')
.map(cookie => cookie.trim())
.filter(Boolean)
.reduce((acc, cookie) => {
const [name, ...rest] = cookie.split('=');
const decodedName = decodeURIComponent(name);
const decodedValue = decodeURIComponent(rest.join('='));
acc[decodedName] = {
value: decodedValue,
length: decodedValue.length,
hasSpecialChars: /[^a-zA-Z0-9\s]/.test(decodedValue)
};
return acc;
}, {});
}
loadCookies() {
this.cookies = this.parseCookies();
this.updateStats();
this.renderCookies();
}
updateStats() {
const stats = document.getElementById('tm-stats');
if (stats) {
const totalCookies = Object.keys(this.cookies).length;
const totalSize = Object.values(this.cookies).reduce((sum, cookie) => sum + cookie.length, 0);
stats.innerHTML = `📊 ${totalCookies} cookies | ${(totalSize / 1024).toFixed(1)} KB`;
}
}
createUI() {
this.container = document.createElement('div');
this.container.id = 'tm-cookie-panel';
this.container.innerHTML = `
<div class="tm-header" id="tm-drag-handle">
<div class="tm-title">
<span>🍪 Cookie Manager</span>
<span class="tm-badge">v1.0</span>
</div>
<div class="tm-header-actions">
<button id="tm-minimize" class="tm-icon-btn" title="Minimize">−</button>
<button id="tm-close" class="tm-icon-btn" title="Close">×</button>
</div>
</div>
<div class="tm-content">
<div class="tm-stats-bar">
<span id="tm-stats">Loading...</span>
<button id="tm-clear-filter" class="tm-icon-btn-small" title="Clear filter">✕</button>
</div>
<div class="tm-controls">
<div class="tm-control-group">
<button id="tm-refresh" class="tm-btn tm-btn-primary" title="Refresh cookies">
🔄 Refresh
</button>
<button id="tm-copy" class="tm-btn tm-btn-secondary" title="Copy as JSON">
📋 Copy
</button>
<button id="tm-export" class="tm-btn tm-btn-secondary" title="Export to file">
💾 Export
</button>
<button id="tm-delete-all" class="tm-btn tm-btn-danger" title="Delete all cookies">
🗑️ Delete All
</button>
</div>
</div>
<div class="tm-search-section">
<div class="tm-search-wrapper">
<span class="tm-search-icon">🔍</span>
<input
type="text"
id="tm-search"
placeholder="Search cookies by name..."
autocomplete="off"
/>
<span id="tm-search-count" class="tm-search-count"></span>
</div>
</div>
<div class="tm-sort-bar">
<span class="tm-sort-label">Sort by:</span>
<button class="tm-sort-btn" data-sort="name">Name</button>
<button class="tm-sort-btn" data-sort="length">Size</button>
<button class="tm-sort-btn tm-sort-active" data-sort="name">↑↓</button>
</div>
<div id="tm-cookie-list" class="tm-cookie-list"></div>
</div>
`;
document.body.appendChild(this.container);
this.injectStyles();
this.cookieList = document.getElementById('tm-cookie-list');
this.bindEvents();
this.setupDragAndDrop();
}
injectStyles() {
const style = document.createElement('style');
style.textContent = `
#tm-cookie-panel {
position: fixed;
top: 20px;
right: 20px;
width: 500px;
max-height: 650px;
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
color: #e2e8f0;
z-index: 999999;
border-radius: 16px;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
border: 1px solid rgba(71, 85, 105, 0.5);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
#tm-cookie-panel.tm-minimized .tm-content {
display: none;
}
#tm-cookie-panel.tm-minimized {
max-height: 50px;
}
.tm-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: rgba(30, 41, 59, 0.95);
backdrop-filter: blur(10px);
cursor: move;
border-bottom: 1px solid #334155;
user-select: none;
}
.tm-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 14px;
}
.tm-badge {
background: #3b82f6;
padding: 2px 6px;
border-radius: 12px;
font-size: 10px;
font-weight: 500;
}
.tm-header-actions {
display: flex;
gap: 8px;
}
.tm-icon-btn {
background: transparent;
border: none;
color: #94a3b8;
font-size: 20px;
cursor: pointer;
width: 28px;
height: 28px;
border-radius: 6px;
transition: all 0.2s;
}
.tm-icon-btn:hover {
background: #334155;
color: #e2e8f0;
}
.tm-content {
padding: 16px;
}
.tm-stats-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
font-size: 11px;
color: #94a3b8;
}
.tm-controls {
margin-bottom: 12px;
}
.tm-control-group {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.tm-btn {
padding: 6px 12px;
border: none;
border-radius: 8px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
.tm-btn-primary {
background: #3b82f6;
color: white;
}
.tm-btn-primary:hover {
background: #2563eb;
transform: translateY(-1px);
}
.tm-btn-secondary {
background: #334155;
color: #e2e8f0;
}
.tm-btn-secondary:hover {
background: #475569;
transform: translateY(-1px);
}
.tm-btn-danger {
background: #dc2626;
color: white;
}
.tm-btn-danger:hover {
background: #b91c1c;
transform: translateY(-1px);
}
.tm-search-section {
margin-bottom: 12px;
}
.tm-search-wrapper {
position: relative;
}
.tm-search-icon {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 14px;
opacity: 0.5;
}
#tm-search {
width: 100%;
padding: 8px 8px 8px 32px;
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
color: #e2e8f0;
font-size: 12px;
transition: all 0.2s;
}
#tm-search:focus {
outline: none;
border-color: #3b82f6;
background: #0f172a;
}
.tm-search-count {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 11px;
color: #94a3b8;
}
.tm-sort-bar {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #334155;
font-size: 11px;
}
.tm-sort-label {
color: #94a3b8;
}
.tm-sort-btn {
background: transparent;
border: none;
color: #94a3b8;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
transition: all 0.2s;
}
.tm-sort-btn:hover {
background: #334155;
color: #e2e8f0;
}
.tm-sort-active {
color: #3b82f6;
font-weight: 600;
}
.tm-cookie-list {
overflow-y: auto;
max-height: 380px;
}
.tm-cookie-item {
background: rgba(30, 41, 59, 0.6);
margin-bottom: 8px;
padding: 12px;
border-radius: 10px;
transition: all 0.2s;
border-left: 3px solid #3b82f6;
position: relative;
}
.tm-cookie-item:hover {
background: rgba(51, 65, 85, 0.8);
transform: translateX(-2px);
}
.tm-cookie-name {
font-weight: 600;
color: #60a5fa;
font-size: 13px;
margin-bottom: 6px;
word-break: break-word;
padding-right: 60px;
}
.tm-cookie-value {
font-size: 11px;
color: #94a3b8;
word-break: break-all;
font-family: 'Courier New', monospace;
margin-bottom: 8px;
}
.tm-cookie-actions {
position: absolute;
top: 12px;
right: 12px;
display: flex;
gap: 6px;
}
.tm-cookie-action {
background: transparent;
border: none;
cursor: pointer;
font-size: 14px;
padding: 2px 4px;
border-radius: 4px;
transition: all 0.2s;
}
.tm-cookie-action:hover {
background: #475569;
}
.tm-cookie-size {
font-size: 9px;
color: #64748b;
display: inline-block;
margin-top: 4px;
}
.tm-empty-state {
text-align: center;
padding: 40px;
color: #64748b;
}
.tm-toast {
position: fixed;
bottom: 20px;
right: 540px;
background: #10b981;
color: white;
padding: 10px 16px;
border-radius: 8px;
font-size: 12px;
z-index: 1000000;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: #1e293b;
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: #475569;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #64748b;
}
`;
document.head.appendChild(style);
}
setupDragAndDrop() {
const header = document.getElementById('tm-drag-handle');
header.addEventListener('mousedown', (e) => {
if (e.target.tagName === 'BUTTON') return;
this.isDragging = true;
const rect = this.container.getBoundingClientRect();
this.dragOffset.x = e.clientX - rect.left;
this.dragOffset.y = e.clientY - rect.top;
this.container.style.transition = 'none';
});
document.addEventListener('mousemove', (e) => {
if (!this.isDragging) return;
const x = e.clientX - this.dragOffset.x;
const y = e.clientY - this.dragOffset.y;
this.container.style.left = x + 'px';
this.container.style.top = y + 'px';
this.container.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
this.isDragging = false;
this.container.style.transition = '';
});
}
setupKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'c' && document.activeElement.id === 'tm-search') {
e.preventDefault();
this.copyCookies();
}
if (e.key === 'Escape') {
this.container.remove();
}
});
}
sortCookies(cookies, sortBy, direction) {
return Object.entries(cookies).sort((a, b) => {
let comparison = 0;
if (sortBy === 'name') {
comparison = a[0].localeCompare(b[0]);
} else if (sortBy === 'length') {
comparison = a[1].length - b[1].length;
}
return direction === 'asc' ? comparison : -comparison;
});
}
renderCookies(filter = '') {
this.cookieList.innerHTML = '';
let filteredCookies = Object.entries(this.cookies);
if (filter) {
filteredCookies = filteredCookies.filter(([name]) =>
name.toLowerCase().includes(filter.toLowerCase())
);
const searchCount = document.getElementById('tm-search-count');
if (searchCount) {
searchCount.textContent = `${filteredCookies.length} found`;
}
} else {
const searchCount = document.getElementById('tm-search-count');
if (searchCount) {
searchCount.textContent = '';
}
}
const sortedCookies = this.sortCookies(Object.fromEntries(filteredCookies), this.currentSort, this.sortDirection);
if (sortedCookies.length === 0) {
this.cookieList.innerHTML = '<div class="tm-empty-state">🍪 No cookies found</div>';
return;
}
for (const [name, data] of sortedCookies) {
const item = document.createElement('div');
item.className = 'tm-cookie-item';
const truncatedValue = data.value.length > 100 ?
data.value.substring(0, 100) + '...' :
data.value;
item.innerHTML = `
<div class="tm-cookie-name">${this.escapeHtml(name)}</div>
<div class="tm-cookie-value">${this.escapeHtml(truncatedValue)}</div>
<div class="tm-cookie-size">📏 ${data.length} chars</div>
<div class="tm-cookie-actions">
<button class="tm-cookie-action" data-action="copy" data-name="${this.escapeHtml(name)}" title="Copy value">📋</button>
<button class="tm-cookie-action" data-action="delete" data-name="${this.escapeHtml(name)}" title="Delete cookie">🗑️</button>
</div>
`;
const copyBtn = item.querySelector('[data-action="copy"]');
const deleteBtn = item.querySelector('[data-action="delete"]');
copyBtn.addEventListener('click', () => {
this.copySingleCookie(name);
});
deleteBtn.addEventListener('click', () => {
this.deleteCookie(name);
});
this.cookieList.appendChild(item);
}
}
escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
showToast(message, isError = false) {
const toast = document.createElement('div');
toast.className = 'tm-toast';
toast.style.background = isError ? '#ef4444' : '#10b981';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
deleteCookie(name) {
if (confirm(`Delete cookie "${name}"?`)) {
document.cookie = `${encodeURIComponent(name)}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
document.cookie = `${encodeURIComponent(name)}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${window.location.hostname}`;
this.loadCookies();
this.showToast(`✅ Deleted "${name}"`);
}
}
deleteAllCookies() {
if (confirm('⚠️ Delete ALL cookies for this domain? This action cannot be undone!')) {
const cookies = Object.keys(this.cookies);
cookies.forEach(name => {
document.cookie = `${encodeURIComponent(name)}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
document.cookie = `${encodeURIComponent(name)}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${window.location.hostname}`;
});
this.loadCookies();
this.showToast(`🗑️ Deleted ${cookies.length} cookies`);
}
}
copySingleCookie(name) {
const value = this.cookies[name].value;
GM_setClipboard(value);
this.showToast(`📋 Copied "${name}" to clipboard`);
}
exportCookies() {
const exportData = {};
Object.entries(this.cookies).forEach(([name, data]) => {
exportData[name] = data.value;
});
const blob = new Blob(
[JSON.stringify(exportData, null, 2)],
{ type: 'application/json' }
);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `cookies_${window.location.hostname}_${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
this.showToast('💾 Cookies exported successfully');
}
copyCookies() {
const exportData = {};
Object.entries(this.cookies).forEach(([name, data]) => {
exportData[name] = data.value;
});
GM_setClipboard(JSON.stringify(exportData, null, 2));
this.showToast('📋 Cookies copied to clipboard');
}
bindEvents() {
document.getElementById('tm-refresh').addEventListener('click', () => {
this.loadCookies();
this.showToast('🔄 Cookies refreshed');
});
document.getElementById('tm-copy').addEventListener('click', () => this.copyCookies());
document.getElementById('tm-export').addEventListener('click', () => this.exportCookies());
document.getElementById('tm-delete-all').addEventListener('click', () => this.deleteAllCookies());
document.getElementById('tm-close').addEventListener('click', () => this.container.remove());
document.getElementById('tm-minimize').addEventListener('click', () => {
this.container.classList.toggle('tm-minimized');
const minimizeBtn = document.getElementById('tm-minimize');
minimizeBtn.textContent = this.container.classList.contains('tm-minimized') ? '□' : '−';
});
document.getElementById('tm-clear-filter').addEventListener('click', () => {
const searchInput = document.getElementById('tm-search');
searchInput.value = '';
this.renderCookies('');
});
document.getElementById('tm-search').addEventListener('input', (event) => {
this.renderCookies(event.target.value);
});
document.querySelectorAll('.tm-sort-btn[data-sort]').forEach(btn => {
btn.addEventListener('click', () => {
const sortBy = btn.getAttribute('data-sort');
if (this.currentSort === sortBy) {
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
} else {
this.currentSort = sortBy;
this.sortDirection = 'asc';
}
document.querySelectorAll('.tm-sort-btn').forEach(b => b.classList.remove('tm-sort-active'));
btn.classList.add('tm-sort-active');
this.renderCookies(document.getElementById('tm-search').value);
});
});
}
}
new CookieGUI();
})();