diff --git a/dist-extension/toolbar.html b/dist-extension/toolbar.html index e8685ed2..cc713f45 100644 --- a/dist-extension/toolbar.html +++ b/dist-extension/toolbar.html @@ -1,396 +1,67 @@ - + - - Dev Tools Toolbar + + DevTools toolbar -
-
-
- - - - - - - - - - - - - - - - - - -
- + + diff --git a/dist-extension/toolbar.js b/dist-extension/toolbar.js index 857c2d45..448fa939 100644 --- a/dist-extension/toolbar.js +++ b/dist-extension/toolbar.js @@ -1,446 +1,87 @@ -// Dev Tools Toolbar JavaScript -// Floating toolbar similar to Vercel Toolbar - +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; const toolbar = document.getElementById('toolbar'); const menuBtn = document.getElementById('menuBtn'); const menuPanel = document.getElementById('menuPanel'); const menuSearch = document.getElementById('menuSearch'); +const toolSections = document.getElementById('toolSections'); const statusIndicator = document.getElementById('statusIndicator'); const dragHandle = document.getElementById('dragHandle'); - -let isSleeping = true; +const tools = [ + { category: 'Quick actions', items: [ + { id: 'search', label: 'Search all tools', description: 'Find a utility by name', icon: 'βŒ•', shortcut: '/' }, + { id: 'inspect', label: 'Inspect element', description: 'Pick an element on this page', icon: 'β†—' }, + { id: 'screenshot', label: 'Capture screenshot', description: 'Save the current page view', icon: 'β–£' }, + ]}, + { category: 'Popular tools', items: [ + { label: 'JSON Viewer', description: 'Format and explore JSON', icon: '{}' , path: 'json-viewer' }, + { label: 'Base64 Converter', description: 'Encode and decode Base64', icon: '64', path: 'base64-string-converter' }, + { label: 'Regex Tester', description: 'Build and test regular expressions', icon: '.*', path: 'regex-tester' }, + { label: 'Color Converter', description: 'Convert colors between formats', icon: 'β—‰', path: 'color-converter' }, + { label: 'JWT Decoder', description: 'Inspect JSON Web Tokens', icon: 'jwt', path: 'jwt-decoder' }, + ]}, + { category: 'All tools', items: [ + { label: 'Open DevTools', description: 'Browse the complete tool library', icon: 'β†—', path: '' }, + ]}, + { category: 'Settings', items: [ + { id: 'preferences', label: 'Preferences', description: 'Configure toolbar behavior', icon: 'βš™' }, + { id: 'hide', label: 'Hide toolbar', description: 'Show it again from the extension menu', icon: 'βˆ’', shortcut: 'Esc' }, + ]}, +]; +let isSleeping = false; let isMenuOpen = false; let isDragging = false; let dragOffset = { x: 0, y: 0 }; -// Default shortcuts -const defaultShortcuts = { - menu: 'Control', - search: '/', - inspect: '', - screenshot: '', - hide: 'Escape', - json: '', - base64: '', -}; - -// Current shortcuts -let shortcuts = { ...defaultShortcuts }; - -// Initialize toolbar -function initToolbar() { - loadPosition(); - loadState(); - loadShortcuts(); - setupEventListeners(); - setupKeyboardShortcuts(); -} - -// Load shortcuts from storage -function loadShortcuts() { - if (typeof browser !== 'undefined') { - browser.storage.local.get(['shortcuts'], (result) => { - shortcuts = { ...defaultShortcuts, ...(result.shortcuts || {}) }; - updateMenuShortcuts(); - }); - } else if (typeof chrome !== 'undefined') { - chrome.storage.local.get(['shortcuts'], (result) => { - shortcuts = { ...defaultShortcuts, ...(result.shortcuts || {}) }; - updateMenuShortcuts(); - }); - } -} - -// Update menu shortcuts display -function updateMenuShortcuts() { - const shortcutMap = { - search: 'search', - inspect: 'inspect', - screenshot: 'screenshot', - hide: 'hide', - }; - - Object.keys(shortcutMap).forEach(action => { - const shortcut = shortcuts[action]; - const items = menuPanel.querySelectorAll(`[data-action="${action}"]`); - items.forEach(item => { - let shortcutEl = item.querySelector('.menu-item-shortcut'); - if (!shortcutEl) { - shortcutEl = document.createElement('span'); - shortcutEl.className = 'menu-item-shortcut'; - item.appendChild(shortcutEl); - } - shortcutEl.textContent = shortcut || ''; - shortcutEl.style.display = shortcut ? 'block' : 'none'; - }); - }); -} - -// Load saved position -function loadPosition() { - const position = localStorage.getItem('it-toolbar-position'); - if (position) { - const { x, y } = JSON.parse(position); - toolbar.style.right = 'auto'; - toolbar.style.left = `${x}px`; - toolbar.style.top = `${y}px`; - toolbar.style.transform = 'none'; - } -} - -// Save position -function savePosition() { - const rect = toolbar.getBoundingClientRect(); - localStorage.setItem('it-toolbar-position', JSON.stringify({ - x: rect.left, - y: rect.top, - })); -} - -// Load state (sleeping/active) -function loadState() { - const state = localStorage.getItem('it-toolbar-state'); - if (state === 'active') { - wakeUp(); - } else { - sleep(); - } -} - -// Setup event listeners -function setupEventListeners() { - // Menu toggle - menuBtn.addEventListener('click', toggleMenu); - - // Close menu when clicking outside - document.addEventListener('click', (e) => { - if (!toolbar.contains(e.target)) { - closeMenu(); - } - }); - - // Menu item clicks - menuPanel.querySelectorAll('.menu-item').forEach(item => { - item.addEventListener('click', (e) => { - const action = item.dataset.action; - if (action) { - e.preventDefault(); - handleMenuAction(action); - } - }); - }); - - // Search - menuSearch.addEventListener('input', (e) => { - filterMenuItems(e.target.value); - }); - - // Dragging - dragHandle.addEventListener('mousedown', startDrag); - document.addEventListener('mousemove', onDrag); - document.addEventListener('mouseup', stopDrag); - - // Toolbar buttons - document.getElementById('searchBtn').addEventListener('click', () => { - menuSearch.focus(); - openMenu(); - }); - - document.getElementById('inspectBtn').addEventListener('click', () => { - sendMessage('inspectElement'); - }); - - document.getElementById('screenshotBtn').addEventListener('click', () => { - sendMessage('takeScreenshot'); - }); - - document.getElementById('hideBtn').addEventListener('click', () => { - hideToolbar(); - }); - - // Click on toolbar to wake up - toolbar.addEventListener('click', () => { - if (isSleeping) { - wakeUp(); - } - }); -} - -// Setup keyboard shortcuts -function setupKeyboardShortcuts() { - document.addEventListener('keydown', (e) => { - const shortcut = buildShortcutString(e); - - // Check each shortcut - if (shortcut === shortcuts.menu) { - e.preventDefault(); - toggleMenu(); - } - - if (shortcut === shortcuts.search) { - e.preventDefault(); - menuSearch.focus(); - openMenu(); - } - - if (shortcut === shortcuts.inspect) { - e.preventDefault(); - sendMessage('inspectElement'); - } - - if (shortcut === shortcuts.screenshot) { - e.preventDefault(); - sendMessage('takeScreenshot'); - } - - if (shortcut === shortcuts.hide) { - e.preventDefault(); - if (isMenuOpen) { - closeMenu(); - } else { - hideToolbar(); - } - } - - if (shortcut === shortcuts.json) { - e.preventDefault(); - window.open('https://devtools.khulnasoft.com/json-viewer', '_blank'); - } - - if (shortcut === shortcuts.base64) { - e.preventDefault(); - window.open('https://devtools.khulnasoft.com/base64-string-converter', '_blank'); - } - }); -} - -// Build shortcut string from keyboard event -function buildShortcutString(e) { - const parts = []; - - if (e.ctrlKey) parts.push('Ctrl'); - if (e.altKey) parts.push('Alt'); - if (e.shiftKey) parts.push('Shift'); - if (e.metaKey) parts.push('Cmd'); - - const key = e.key; - if (!parts.includes(key) && key !== 'Control' && key !== 'Alt' && key !== 'Shift' && key !== 'Meta') { - parts.push(key); - } - - return parts.join('+'); -} - -// Toggle menu -function toggleMenu() { - if (isMenuOpen) { - closeMenu(); - } else { - openMenu(); - } -} - -// Open menu -function openMenu() { - isMenuOpen = true; - menuPanel.classList.add('open'); - menuBtn.classList.add('active'); - if (isSleeping) { - wakeUp(); - } -} - -// Close menu -function closeMenu() { - isMenuOpen = false; - menuPanel.classList.remove('open'); - menuBtn.classList.remove('active'); - menuSearch.value = ''; - filterMenuItems(''); -} - -// Filter menu items -function filterMenuItems(query) { - const items = menuPanel.querySelectorAll('.menu-item'); - const lowerQuery = query.toLowerCase(); - - items.forEach(item => { - const text = item.querySelector('.menu-item-text').textContent.toLowerCase(); - if (text.includes(lowerQuery)) { - item.style.display = 'flex'; - } else { - item.style.display = 'none'; - } - }); -} - -// Handle menu action -function handleMenuAction(action) { - closeMenu(); - - switch (action) { - case 'search': - menuSearch.focus(); - openMenu(); - break; - case 'inspect': - sendMessage('inspectElement'); - break; - case 'screenshot': - sendMessage('takeScreenshot'); - break; - case 'preferences': - sendMessage('openPreferences'); - break; - case 'hide': - hideToolbar(); - break; - } -} - -// Wake up toolbar -function wakeUp() { - isSleeping = false; - toolbar.classList.remove('sleeping'); - statusIndicator.classList.remove('sleeping'); - localStorage.setItem('it-toolbar-state', 'active'); -} - -// Sleep toolbar -function sleep() { - isSleeping = true; - toolbar.classList.add('sleeping'); - statusIndicator.classList.add('sleeping'); - localStorage.setItem('it-toolbar-state', 'sleeping'); -} - -// Hide toolbar -function hideToolbar() { - toolbar.style.display = 'none'; - // Create a small indicator to show toolbar again - showRestoreIndicator(); -} - -// Show restore indicator -function showRestoreIndicator() { - const indicator = document.createElement('button'); - indicator.id = 'it-toolbar-restore'; - indicator.innerHTML = 'πŸ› οΈ'; - indicator.style.cssText = ` - position: fixed; - bottom: 20px; - right: 20px; - width: 50px; - height: 50px; - border-radius: 50%; - background: #18a058; - color: white; - border: none; - cursor: pointer; - z-index: 999999; - font-size: 24px; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - transition: transform 0.2s; - `; - - indicator.addEventListener('mouseenter', () => { - indicator.style.transform = 'scale(1.1)'; - }); - - indicator.addEventListener('mouseleave', () => { - indicator.style.transform = 'scale(1)'; - }); - - indicator.addEventListener('click', () => { - toolbar.style.display = 'flex'; - indicator.remove(); - wakeUp(); - }); - - document.body.appendChild(indicator); -} - -// Dragging -function startDrag(e) { - isDragging = true; - const rect = toolbar.getBoundingClientRect(); - dragOffset.x = e.clientX - rect.left; - dragOffset.y = e.clientY - rect.top; - toolbar.style.transition = 'none'; -} - -function onDrag(e) { - if (!isDragging) return; - - const x = e.clientX - dragOffset.x; - const y = e.clientY - dragOffset.y; - - // Constrain to viewport - const maxX = window.innerWidth - toolbar.offsetWidth; - const maxY = window.innerHeight - toolbar.offsetHeight; - - toolbar.style.left = `${Math.max(0, Math.min(x, maxX))}px`; - toolbar.style.top = `${Math.max(0, Math.min(y, maxY))}px`; - toolbar.style.right = 'auto'; - toolbar.style.transform = 'none'; -} - -function stopDrag() { - if (isDragging) { - isDragging = false; - toolbar.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; - savePosition(); - } -} - -// Send message to extension -function sendMessage(action) { - if (typeof browser !== 'undefined') { - browser.runtime.sendMessage({ action }); - } else if (typeof chrome !== 'undefined') { - chrome.runtime.sendMessage({ action }); - } -} - -// Listen for messages from extension -if (typeof browser !== 'undefined') { - browser.runtime.onMessage.addListener(handleMessage); -} else if (typeof chrome !== 'undefined') { - chrome.runtime.onMessage.addListener(handleMessage); -} - -function handleMessage(request, sender, sendResponse) { - if (request.action === 'showToolbar') { - toolbar.style.display = 'flex'; - const indicator = document.getElementById('it-toolbar-restore'); - if (indicator) indicator.remove(); - wakeUp(); - } - - if (request.action === 'hideToolbar') { - hideToolbar(); - } - - if (request.action === 'wakeUp') { - wakeUp(); - } - - if (request.action === 'sleep') { - sleep(); - } - - if (request.action === 'updateShortcuts') { - shortcuts = { ...defaultShortcuts, ...(request.shortcuts || {}) }; - updateMenuShortcuts(); - } - - sendResponse({ success: true }); - return true; -} - -// Initialize on load -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initToolbar); -} else { - initToolbar(); -} +function renderTools(query = '') { + const normalized = query.trim().toLowerCase(); + const html = tools.map((section) => { + const items = section.items.filter((item) => !normalized || `${item.label} ${item.description}`.toLowerCase().includes(normalized)); + if (!items.length) return ''; + return ``; + }).join(''); + toolSections.innerHTML = html || '
No tools match your search.
'; +} + +function sendMessage(action) { browserAPI.runtime.sendMessage({ action }).catch(() => {}); } +function openMenu(focus = false) { isMenuOpen = true; menuPanel.classList.add('open'); menuBtn.classList.add('active'); menuBtn.setAttribute('aria-expanded', 'true'); if (focus) menuSearch.focus(); } +function closeMenu() { isMenuOpen = false; menuPanel.classList.remove('open'); menuBtn.classList.remove('active'); menuBtn.setAttribute('aria-expanded', 'false'); menuSearch.value = ''; renderTools(); } +function hideToolbar() { toolbar.style.display = 'none'; browserAPI.storage.local.set({ toolbarHidden: true }); } +function wakeUp() { isSleeping = false; toolbar.classList.remove('sleeping'); statusIndicator.classList.remove('sleeping'); } +function sleep() { isSleeping = true; toolbar.classList.add('sleeping'); statusIndicator.classList.add('sleeping'); } +function openTool(path) { window.open(`https://devtools.khulnasoft.com/${path}`, '_blank', 'noopener'); } + +renderTools(); +menuBtn.addEventListener('click', () => (isMenuOpen ? closeMenu() : openMenu())); +document.getElementById('searchBtn').addEventListener('click', () => openMenu(true)); +document.getElementById('inspectBtn').addEventListener('click', () => sendMessage('inspectElement')); +document.getElementById('screenshotBtn').addEventListener('click', () => sendMessage('takeScreenshot')); +document.getElementById('hideBtn').addEventListener('click', hideToolbar); +menuSearch.addEventListener('input', (event) => renderTools(event.target.value)); +toolSections.addEventListener('click', (event) => { + const item = event.target.closest('.menu-item'); + if (!item) return; + const id = item.dataset.id; + if (id === 'search') return openMenu(true); + if (id === 'inspect') return sendMessage('inspectElement'); + if (id === 'screenshot') return sendMessage('takeScreenshot'); + if (id === 'hide') return hideToolbar(); + if (id === 'preferences') return browserAPI.runtime.sendMessage({ action: 'openOptions' }); + if (item.dataset.path !== '') openTool(item.dataset.path); + else openTool(''); +}); +document.addEventListener('click', (event) => { if (!toolbar.contains(event.target)) closeMenu(); }); +document.addEventListener('keydown', (event) => { + const key = event.key.toLowerCase(); + if ((event.ctrlKey || event.metaKey) && key === 'k') { event.preventDefault(); openMenu(true); } + else if (key === '/' && document.activeElement !== menuSearch) { event.preventDefault(); openMenu(true); } + else if (key === 'escape') { if (isMenuOpen) closeMenu(); else hideToolbar(); } +}); + +dragHandle.addEventListener('mousedown', (event) => { isDragging = true; const rect = toolbar.getBoundingClientRect(); dragOffset = { x: event.clientX - rect.left, y: event.clientY - rect.top }; toolbar.style.right = 'auto'; toolbar.style.transform = 'none'; }); +document.addEventListener('mousemove', (event) => { if (!isDragging) return; toolbar.style.left = `${Math.max(8, Math.min(window.innerWidth - toolbar.offsetWidth - 8, event.clientX - dragOffset.x))}px`; toolbar.style.top = `${Math.max(8, Math.min(window.innerHeight - toolbar.offsetHeight - 8, event.clientY - dragOffset.y))}px`; }); +document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; const rect = toolbar.getBoundingClientRect(); browserAPI.storage.local.set({ toolbarPosition: { x: rect.left, y: rect.top } }); }); + +browserAPI.storage.local.get(['toolbarPosition', 'toolbarHidden'], (result) => { + if (result.toolbarPosition) { toolbar.style.right = 'auto'; toolbar.style.transform = 'none'; toolbar.style.left = `${result.toolbarPosition.x}px`; toolbar.style.top = `${result.toolbarPosition.y}px`; } + if (result.toolbarHidden) toolbar.style.display = 'none'; +}); diff --git a/extension/toolbar.html b/extension/toolbar.html index e8685ed2..cc713f45 100644 --- a/extension/toolbar.html +++ b/extension/toolbar.html @@ -1,396 +1,67 @@ - + - - Dev Tools Toolbar + + DevTools toolbar -
-
-
- - - - - - - - - - - - - - - - - - -
- + + diff --git a/extension/toolbar.js b/extension/toolbar.js index 857c2d45..448fa939 100644 --- a/extension/toolbar.js +++ b/extension/toolbar.js @@ -1,446 +1,87 @@ -// Dev Tools Toolbar JavaScript -// Floating toolbar similar to Vercel Toolbar - +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; const toolbar = document.getElementById('toolbar'); const menuBtn = document.getElementById('menuBtn'); const menuPanel = document.getElementById('menuPanel'); const menuSearch = document.getElementById('menuSearch'); +const toolSections = document.getElementById('toolSections'); const statusIndicator = document.getElementById('statusIndicator'); const dragHandle = document.getElementById('dragHandle'); - -let isSleeping = true; +const tools = [ + { category: 'Quick actions', items: [ + { id: 'search', label: 'Search all tools', description: 'Find a utility by name', icon: 'βŒ•', shortcut: '/' }, + { id: 'inspect', label: 'Inspect element', description: 'Pick an element on this page', icon: 'β†—' }, + { id: 'screenshot', label: 'Capture screenshot', description: 'Save the current page view', icon: 'β–£' }, + ]}, + { category: 'Popular tools', items: [ + { label: 'JSON Viewer', description: 'Format and explore JSON', icon: '{}' , path: 'json-viewer' }, + { label: 'Base64 Converter', description: 'Encode and decode Base64', icon: '64', path: 'base64-string-converter' }, + { label: 'Regex Tester', description: 'Build and test regular expressions', icon: '.*', path: 'regex-tester' }, + { label: 'Color Converter', description: 'Convert colors between formats', icon: 'β—‰', path: 'color-converter' }, + { label: 'JWT Decoder', description: 'Inspect JSON Web Tokens', icon: 'jwt', path: 'jwt-decoder' }, + ]}, + { category: 'All tools', items: [ + { label: 'Open DevTools', description: 'Browse the complete tool library', icon: 'β†—', path: '' }, + ]}, + { category: 'Settings', items: [ + { id: 'preferences', label: 'Preferences', description: 'Configure toolbar behavior', icon: 'βš™' }, + { id: 'hide', label: 'Hide toolbar', description: 'Show it again from the extension menu', icon: 'βˆ’', shortcut: 'Esc' }, + ]}, +]; +let isSleeping = false; let isMenuOpen = false; let isDragging = false; let dragOffset = { x: 0, y: 0 }; -// Default shortcuts -const defaultShortcuts = { - menu: 'Control', - search: '/', - inspect: '', - screenshot: '', - hide: 'Escape', - json: '', - base64: '', -}; - -// Current shortcuts -let shortcuts = { ...defaultShortcuts }; - -// Initialize toolbar -function initToolbar() { - loadPosition(); - loadState(); - loadShortcuts(); - setupEventListeners(); - setupKeyboardShortcuts(); -} - -// Load shortcuts from storage -function loadShortcuts() { - if (typeof browser !== 'undefined') { - browser.storage.local.get(['shortcuts'], (result) => { - shortcuts = { ...defaultShortcuts, ...(result.shortcuts || {}) }; - updateMenuShortcuts(); - }); - } else if (typeof chrome !== 'undefined') { - chrome.storage.local.get(['shortcuts'], (result) => { - shortcuts = { ...defaultShortcuts, ...(result.shortcuts || {}) }; - updateMenuShortcuts(); - }); - } -} - -// Update menu shortcuts display -function updateMenuShortcuts() { - const shortcutMap = { - search: 'search', - inspect: 'inspect', - screenshot: 'screenshot', - hide: 'hide', - }; - - Object.keys(shortcutMap).forEach(action => { - const shortcut = shortcuts[action]; - const items = menuPanel.querySelectorAll(`[data-action="${action}"]`); - items.forEach(item => { - let shortcutEl = item.querySelector('.menu-item-shortcut'); - if (!shortcutEl) { - shortcutEl = document.createElement('span'); - shortcutEl.className = 'menu-item-shortcut'; - item.appendChild(shortcutEl); - } - shortcutEl.textContent = shortcut || ''; - shortcutEl.style.display = shortcut ? 'block' : 'none'; - }); - }); -} - -// Load saved position -function loadPosition() { - const position = localStorage.getItem('it-toolbar-position'); - if (position) { - const { x, y } = JSON.parse(position); - toolbar.style.right = 'auto'; - toolbar.style.left = `${x}px`; - toolbar.style.top = `${y}px`; - toolbar.style.transform = 'none'; - } -} - -// Save position -function savePosition() { - const rect = toolbar.getBoundingClientRect(); - localStorage.setItem('it-toolbar-position', JSON.stringify({ - x: rect.left, - y: rect.top, - })); -} - -// Load state (sleeping/active) -function loadState() { - const state = localStorage.getItem('it-toolbar-state'); - if (state === 'active') { - wakeUp(); - } else { - sleep(); - } -} - -// Setup event listeners -function setupEventListeners() { - // Menu toggle - menuBtn.addEventListener('click', toggleMenu); - - // Close menu when clicking outside - document.addEventListener('click', (e) => { - if (!toolbar.contains(e.target)) { - closeMenu(); - } - }); - - // Menu item clicks - menuPanel.querySelectorAll('.menu-item').forEach(item => { - item.addEventListener('click', (e) => { - const action = item.dataset.action; - if (action) { - e.preventDefault(); - handleMenuAction(action); - } - }); - }); - - // Search - menuSearch.addEventListener('input', (e) => { - filterMenuItems(e.target.value); - }); - - // Dragging - dragHandle.addEventListener('mousedown', startDrag); - document.addEventListener('mousemove', onDrag); - document.addEventListener('mouseup', stopDrag); - - // Toolbar buttons - document.getElementById('searchBtn').addEventListener('click', () => { - menuSearch.focus(); - openMenu(); - }); - - document.getElementById('inspectBtn').addEventListener('click', () => { - sendMessage('inspectElement'); - }); - - document.getElementById('screenshotBtn').addEventListener('click', () => { - sendMessage('takeScreenshot'); - }); - - document.getElementById('hideBtn').addEventListener('click', () => { - hideToolbar(); - }); - - // Click on toolbar to wake up - toolbar.addEventListener('click', () => { - if (isSleeping) { - wakeUp(); - } - }); -} - -// Setup keyboard shortcuts -function setupKeyboardShortcuts() { - document.addEventListener('keydown', (e) => { - const shortcut = buildShortcutString(e); - - // Check each shortcut - if (shortcut === shortcuts.menu) { - e.preventDefault(); - toggleMenu(); - } - - if (shortcut === shortcuts.search) { - e.preventDefault(); - menuSearch.focus(); - openMenu(); - } - - if (shortcut === shortcuts.inspect) { - e.preventDefault(); - sendMessage('inspectElement'); - } - - if (shortcut === shortcuts.screenshot) { - e.preventDefault(); - sendMessage('takeScreenshot'); - } - - if (shortcut === shortcuts.hide) { - e.preventDefault(); - if (isMenuOpen) { - closeMenu(); - } else { - hideToolbar(); - } - } - - if (shortcut === shortcuts.json) { - e.preventDefault(); - window.open('https://devtools.khulnasoft.com/json-viewer', '_blank'); - } - - if (shortcut === shortcuts.base64) { - e.preventDefault(); - window.open('https://devtools.khulnasoft.com/base64-string-converter', '_blank'); - } - }); -} - -// Build shortcut string from keyboard event -function buildShortcutString(e) { - const parts = []; - - if (e.ctrlKey) parts.push('Ctrl'); - if (e.altKey) parts.push('Alt'); - if (e.shiftKey) parts.push('Shift'); - if (e.metaKey) parts.push('Cmd'); - - const key = e.key; - if (!parts.includes(key) && key !== 'Control' && key !== 'Alt' && key !== 'Shift' && key !== 'Meta') { - parts.push(key); - } - - return parts.join('+'); -} - -// Toggle menu -function toggleMenu() { - if (isMenuOpen) { - closeMenu(); - } else { - openMenu(); - } -} - -// Open menu -function openMenu() { - isMenuOpen = true; - menuPanel.classList.add('open'); - menuBtn.classList.add('active'); - if (isSleeping) { - wakeUp(); - } -} - -// Close menu -function closeMenu() { - isMenuOpen = false; - menuPanel.classList.remove('open'); - menuBtn.classList.remove('active'); - menuSearch.value = ''; - filterMenuItems(''); -} - -// Filter menu items -function filterMenuItems(query) { - const items = menuPanel.querySelectorAll('.menu-item'); - const lowerQuery = query.toLowerCase(); - - items.forEach(item => { - const text = item.querySelector('.menu-item-text').textContent.toLowerCase(); - if (text.includes(lowerQuery)) { - item.style.display = 'flex'; - } else { - item.style.display = 'none'; - } - }); -} - -// Handle menu action -function handleMenuAction(action) { - closeMenu(); - - switch (action) { - case 'search': - menuSearch.focus(); - openMenu(); - break; - case 'inspect': - sendMessage('inspectElement'); - break; - case 'screenshot': - sendMessage('takeScreenshot'); - break; - case 'preferences': - sendMessage('openPreferences'); - break; - case 'hide': - hideToolbar(); - break; - } -} - -// Wake up toolbar -function wakeUp() { - isSleeping = false; - toolbar.classList.remove('sleeping'); - statusIndicator.classList.remove('sleeping'); - localStorage.setItem('it-toolbar-state', 'active'); -} - -// Sleep toolbar -function sleep() { - isSleeping = true; - toolbar.classList.add('sleeping'); - statusIndicator.classList.add('sleeping'); - localStorage.setItem('it-toolbar-state', 'sleeping'); -} - -// Hide toolbar -function hideToolbar() { - toolbar.style.display = 'none'; - // Create a small indicator to show toolbar again - showRestoreIndicator(); -} - -// Show restore indicator -function showRestoreIndicator() { - const indicator = document.createElement('button'); - indicator.id = 'it-toolbar-restore'; - indicator.innerHTML = 'πŸ› οΈ'; - indicator.style.cssText = ` - position: fixed; - bottom: 20px; - right: 20px; - width: 50px; - height: 50px; - border-radius: 50%; - background: #18a058; - color: white; - border: none; - cursor: pointer; - z-index: 999999; - font-size: 24px; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - transition: transform 0.2s; - `; - - indicator.addEventListener('mouseenter', () => { - indicator.style.transform = 'scale(1.1)'; - }); - - indicator.addEventListener('mouseleave', () => { - indicator.style.transform = 'scale(1)'; - }); - - indicator.addEventListener('click', () => { - toolbar.style.display = 'flex'; - indicator.remove(); - wakeUp(); - }); - - document.body.appendChild(indicator); -} - -// Dragging -function startDrag(e) { - isDragging = true; - const rect = toolbar.getBoundingClientRect(); - dragOffset.x = e.clientX - rect.left; - dragOffset.y = e.clientY - rect.top; - toolbar.style.transition = 'none'; -} - -function onDrag(e) { - if (!isDragging) return; - - const x = e.clientX - dragOffset.x; - const y = e.clientY - dragOffset.y; - - // Constrain to viewport - const maxX = window.innerWidth - toolbar.offsetWidth; - const maxY = window.innerHeight - toolbar.offsetHeight; - - toolbar.style.left = `${Math.max(0, Math.min(x, maxX))}px`; - toolbar.style.top = `${Math.max(0, Math.min(y, maxY))}px`; - toolbar.style.right = 'auto'; - toolbar.style.transform = 'none'; -} - -function stopDrag() { - if (isDragging) { - isDragging = false; - toolbar.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; - savePosition(); - } -} - -// Send message to extension -function sendMessage(action) { - if (typeof browser !== 'undefined') { - browser.runtime.sendMessage({ action }); - } else if (typeof chrome !== 'undefined') { - chrome.runtime.sendMessage({ action }); - } -} - -// Listen for messages from extension -if (typeof browser !== 'undefined') { - browser.runtime.onMessage.addListener(handleMessage); -} else if (typeof chrome !== 'undefined') { - chrome.runtime.onMessage.addListener(handleMessage); -} - -function handleMessage(request, sender, sendResponse) { - if (request.action === 'showToolbar') { - toolbar.style.display = 'flex'; - const indicator = document.getElementById('it-toolbar-restore'); - if (indicator) indicator.remove(); - wakeUp(); - } - - if (request.action === 'hideToolbar') { - hideToolbar(); - } - - if (request.action === 'wakeUp') { - wakeUp(); - } - - if (request.action === 'sleep') { - sleep(); - } - - if (request.action === 'updateShortcuts') { - shortcuts = { ...defaultShortcuts, ...(request.shortcuts || {}) }; - updateMenuShortcuts(); - } - - sendResponse({ success: true }); - return true; -} - -// Initialize on load -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initToolbar); -} else { - initToolbar(); -} +function renderTools(query = '') { + const normalized = query.trim().toLowerCase(); + const html = tools.map((section) => { + const items = section.items.filter((item) => !normalized || `${item.label} ${item.description}`.toLowerCase().includes(normalized)); + if (!items.length) return ''; + return ``; + }).join(''); + toolSections.innerHTML = html || '
No tools match your search.
'; +} + +function sendMessage(action) { browserAPI.runtime.sendMessage({ action }).catch(() => {}); } +function openMenu(focus = false) { isMenuOpen = true; menuPanel.classList.add('open'); menuBtn.classList.add('active'); menuBtn.setAttribute('aria-expanded', 'true'); if (focus) menuSearch.focus(); } +function closeMenu() { isMenuOpen = false; menuPanel.classList.remove('open'); menuBtn.classList.remove('active'); menuBtn.setAttribute('aria-expanded', 'false'); menuSearch.value = ''; renderTools(); } +function hideToolbar() { toolbar.style.display = 'none'; browserAPI.storage.local.set({ toolbarHidden: true }); } +function wakeUp() { isSleeping = false; toolbar.classList.remove('sleeping'); statusIndicator.classList.remove('sleeping'); } +function sleep() { isSleeping = true; toolbar.classList.add('sleeping'); statusIndicator.classList.add('sleeping'); } +function openTool(path) { window.open(`https://devtools.khulnasoft.com/${path}`, '_blank', 'noopener'); } + +renderTools(); +menuBtn.addEventListener('click', () => (isMenuOpen ? closeMenu() : openMenu())); +document.getElementById('searchBtn').addEventListener('click', () => openMenu(true)); +document.getElementById('inspectBtn').addEventListener('click', () => sendMessage('inspectElement')); +document.getElementById('screenshotBtn').addEventListener('click', () => sendMessage('takeScreenshot')); +document.getElementById('hideBtn').addEventListener('click', hideToolbar); +menuSearch.addEventListener('input', (event) => renderTools(event.target.value)); +toolSections.addEventListener('click', (event) => { + const item = event.target.closest('.menu-item'); + if (!item) return; + const id = item.dataset.id; + if (id === 'search') return openMenu(true); + if (id === 'inspect') return sendMessage('inspectElement'); + if (id === 'screenshot') return sendMessage('takeScreenshot'); + if (id === 'hide') return hideToolbar(); + if (id === 'preferences') return browserAPI.runtime.sendMessage({ action: 'openOptions' }); + if (item.dataset.path !== '') openTool(item.dataset.path); + else openTool(''); +}); +document.addEventListener('click', (event) => { if (!toolbar.contains(event.target)) closeMenu(); }); +document.addEventListener('keydown', (event) => { + const key = event.key.toLowerCase(); + if ((event.ctrlKey || event.metaKey) && key === 'k') { event.preventDefault(); openMenu(true); } + else if (key === '/' && document.activeElement !== menuSearch) { event.preventDefault(); openMenu(true); } + else if (key === 'escape') { if (isMenuOpen) closeMenu(); else hideToolbar(); } +}); + +dragHandle.addEventListener('mousedown', (event) => { isDragging = true; const rect = toolbar.getBoundingClientRect(); dragOffset = { x: event.clientX - rect.left, y: event.clientY - rect.top }; toolbar.style.right = 'auto'; toolbar.style.transform = 'none'; }); +document.addEventListener('mousemove', (event) => { if (!isDragging) return; toolbar.style.left = `${Math.max(8, Math.min(window.innerWidth - toolbar.offsetWidth - 8, event.clientX - dragOffset.x))}px`; toolbar.style.top = `${Math.max(8, Math.min(window.innerHeight - toolbar.offsetHeight - 8, event.clientY - dragOffset.y))}px`; }); +document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; const rect = toolbar.getBoundingClientRect(); browserAPI.storage.local.set({ toolbarPosition: { x: rect.left, y: rect.top } }); }); + +browserAPI.storage.local.get(['toolbarPosition', 'toolbarHidden'], (result) => { + if (result.toolbarPosition) { toolbar.style.right = 'auto'; toolbar.style.transform = 'none'; toolbar.style.left = `${result.toolbarPosition.x}px`; toolbar.style.top = `${result.toolbarPosition.y}px`; } + if (result.toolbarHidden) toolbar.style.display = 'none'; +});