341 lines
12 KiB
JavaScript
341 lines
12 KiB
JavaScript
// Enhanced UX utilities for hex editor companion workflow
|
|
|
|
import { copyToClipboard as originalCopyToClipboard, formatHex } from './utils.js';
|
|
|
|
// ============================================
|
|
// 1. KEYBOARD SHORTCUTS
|
|
// ============================================
|
|
|
|
export function setupKeyboardShortcuts() {
|
|
document.addEventListener('keydown', (e) => {
|
|
// Ctrl+1-9: Switch tabs
|
|
if (e.ctrlKey && !e.shiftKey && !e.altKey) {
|
|
const num = parseInt(e.key);
|
|
if (num >= 1 && num <= 9) {
|
|
const tabs = document.querySelectorAll('.tab');
|
|
if (tabs[num - 1]) {
|
|
e.preventDefault();
|
|
tabs[num - 1].click();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ctrl+K: Focus calculator
|
|
if (e.ctrlKey && e.key === 'k') {
|
|
e.preventDefault();
|
|
const calcInput = document.querySelector('.tab-content.active .calc-input-field');
|
|
if (calcInput) calcInput.focus();
|
|
}
|
|
|
|
// Ctrl+L: Focus first input field
|
|
if (e.ctrlKey && e.key === 'l') {
|
|
e.preventDefault();
|
|
const firstInput = document.querySelector('.tab-content.active .input-group input');
|
|
if (firstInput) firstInput.focus();
|
|
}
|
|
|
|
// Alt+C: Clear all inputs in active tab
|
|
if (e.altKey && e.key === 'c') {
|
|
e.preventDefault();
|
|
const inputs = document.querySelectorAll('.tab-content.active .input-group input');
|
|
inputs.forEach(input => {
|
|
if (!input.id.toLowerCase().includes('size') && !input.id.toLowerCase().includes('offset')) {
|
|
input.value = '';
|
|
}
|
|
});
|
|
// Trigger recalculation
|
|
if (inputs.length > 0) {
|
|
inputs[0].dispatchEvent(new Event('input', { bubbles: true }));
|
|
}
|
|
}
|
|
|
|
// Ctrl+Shift+C: Copy all visible results
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
|
|
e.preventDefault();
|
|
copyAllResults();
|
|
}
|
|
});
|
|
|
|
// Show keyboard shortcuts hint
|
|
showShortcutsHint();
|
|
}
|
|
|
|
function showShortcutsHint() {
|
|
// Create hint element if it doesn't exist
|
|
if (document.getElementById('keyboard-hint')) return;
|
|
|
|
const hint = document.createElement('div');
|
|
hint.id = 'keyboard-hint';
|
|
hint.className = 'keyboard-hint';
|
|
hint.innerHTML = `
|
|
<span class="hint-toggle" title="Tastenkombinationen anzeigen">⌨️</span>
|
|
<div class="hint-content" style="display: none;">
|
|
<h4>Tastenkombinationen</h4>
|
|
<ul>
|
|
<li><kbd>Ctrl+1-9</kbd> Tabs wechseln</li>
|
|
<li><kbd>Ctrl+K</kbd> Rechner fokussieren</li>
|
|
<li><kbd>Ctrl+L</kbd> Erste Eingabe fokussieren</li>
|
|
<li><kbd>Alt+C</kbd> Eingaben löschen</li>
|
|
<li><kbd>Ctrl+Shift+C</kbd> Alle Ergebnisse kopieren</li>
|
|
<li><kbd>Doppelklick</kbd> Wert kopieren</li>
|
|
</ul>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(hint);
|
|
|
|
// Toggle hint visibility
|
|
hint.querySelector('.hint-toggle').addEventListener('click', () => {
|
|
const content = hint.querySelector('.hint-content');
|
|
content.style.display = content.style.display === 'none' ? 'block' : 'none';
|
|
});
|
|
}
|
|
|
|
function copyAllResults() {
|
|
const results = [];
|
|
const activeTab = document.querySelector('.tab-content.active');
|
|
if (!activeTab) return;
|
|
|
|
const resultItems = activeTab.querySelectorAll('.result-item.available');
|
|
resultItems.forEach(item => {
|
|
const label = item.querySelector('.result-label')?.textContent.trim();
|
|
const value = item.querySelector('.result-value')?.textContent.trim();
|
|
if (label && value && value !== '-') {
|
|
results.push(`${label}: ${value}`);
|
|
}
|
|
});
|
|
|
|
if (results.length > 0) {
|
|
const text = results.join('\n');
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
showNotification('Alle Ergebnisse kopiert!', 'success');
|
|
});
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// 2. DOUBLE-CLICK TO COPY
|
|
// ============================================
|
|
|
|
export function setupDoubleClickCopy() {
|
|
document.addEventListener('dblclick', (e) => {
|
|
// Check if double-clicked on a result value or any hex value
|
|
const target = e.target;
|
|
|
|
if (target.classList.contains('result-value') ||
|
|
target.classList.contains('conv-value') ||
|
|
target.closest('.result-value')) {
|
|
|
|
const element = target.classList.contains('result-value') ? target : target.closest('.result-value');
|
|
const text = element.textContent.trim();
|
|
|
|
// Extract hex value
|
|
const hexMatch = text.match(/0x[0-9a-fA-F]+/);
|
|
if (hexMatch) {
|
|
navigator.clipboard.writeText(hexMatch[0]).then(() => {
|
|
highlightElement(element);
|
|
showNotification('Kopiert: ' + hexMatch[0], 'success');
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function highlightElement(element) {
|
|
element.style.transition = 'background-color 0.3s';
|
|
element.style.backgroundColor = '#4CAF50';
|
|
setTimeout(() => {
|
|
element.style.backgroundColor = '';
|
|
}, 300);
|
|
}
|
|
|
|
// ============================================
|
|
// 3. COMPACT MODE TOGGLE
|
|
// ============================================
|
|
|
|
export function setupCompactMode() {
|
|
const toggle = document.createElement('button');
|
|
toggle.id = 'compact-mode-toggle';
|
|
toggle.className = 'compact-toggle';
|
|
toggle.innerHTML = '⚡ Kompakt';
|
|
toggle.title = 'Kompaktansicht umschalten';
|
|
|
|
// Insert toggle button in header
|
|
const header = document.querySelector('.header-content .nav');
|
|
if (header) {
|
|
const toggleContainer = document.createElement('div');
|
|
toggleContainer.appendChild(toggle);
|
|
header.appendChild(toggleContainer);
|
|
}
|
|
|
|
// Load saved preference
|
|
const isCompact = localStorage.getItem('compactMode') === 'true';
|
|
if (isCompact) {
|
|
document.body.classList.add('compact-mode');
|
|
toggle.innerHTML = '🔍 Normal';
|
|
}
|
|
|
|
toggle.addEventListener('click', () => {
|
|
document.body.classList.toggle('compact-mode');
|
|
const isNowCompact = document.body.classList.contains('compact-mode');
|
|
toggle.innerHTML = isNowCompact ? '🔍 Normal' : '⚡ Kompakt';
|
|
localStorage.setItem('compactMode', isNowCompact);
|
|
showNotification(isNowCompact ? 'Kompaktmodus aktiviert' : 'Normalmodus aktiviert', 'info');
|
|
});
|
|
}
|
|
|
|
// ============================================
|
|
// 4. AUTO-CALCULATE ON PASTE
|
|
// ============================================
|
|
|
|
export function setupAutoCalculateOnPaste() {
|
|
document.addEventListener('paste', (e) => {
|
|
const target = e.target;
|
|
|
|
// Only handle paste in input fields
|
|
if (target.tagName !== 'INPUT' || !target.closest('.input-group')) {
|
|
return;
|
|
}
|
|
|
|
setTimeout(() => {
|
|
// Trigger calculation after paste
|
|
target.dispatchEvent(new Event('input', { bubbles: true }));
|
|
showNotification('Wert eingefügt und berechnet', 'info');
|
|
}, 10);
|
|
});
|
|
}
|
|
|
|
// ============================================
|
|
// 5. ENHANCED COPY WITH FORMAT OPTIONS
|
|
// ============================================
|
|
|
|
export function setupContextMenu() {
|
|
let contextMenu = null;
|
|
|
|
document.addEventListener('contextmenu', (e) => {
|
|
const target = e.target;
|
|
|
|
if (target.classList.contains('result-value') || target.classList.contains('conv-value')) {
|
|
e.preventDefault();
|
|
|
|
const text = target.textContent.trim();
|
|
const hexMatch = text.match(/0x[0-9a-fA-F]+/);
|
|
|
|
if (hexMatch) {
|
|
const hexValue = hexMatch[0];
|
|
const decValue = parseInt(hexValue, 16);
|
|
|
|
showContextMenu(e.pageX, e.pageY, hexValue, decValue);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Close menu on click outside
|
|
document.addEventListener('click', () => {
|
|
if (contextMenu) {
|
|
contextMenu.remove();
|
|
contextMenu = null;
|
|
}
|
|
});
|
|
|
|
function showContextMenu(x, y, hexValue, decValue) {
|
|
// Remove existing menu
|
|
if (contextMenu) contextMenu.remove();
|
|
|
|
contextMenu = document.createElement('div');
|
|
contextMenu.className = 'context-menu';
|
|
contextMenu.style.left = x + 'px';
|
|
contextMenu.style.top = y + 'px';
|
|
|
|
const hexNoPrefix = hexValue.substring(2);
|
|
const binary = '0b' + decValue.toString(2);
|
|
const littleEndian = toLittleEndianBytes(decValue);
|
|
|
|
contextMenu.innerHTML = `
|
|
<div class="context-menu-header">Kopieren als:</div>
|
|
<div class="context-menu-item" data-value="${hexValue}">
|
|
<span class="format-label">Hex (0x)</span>
|
|
<span class="format-value">${hexValue}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-value="${hexNoPrefix}">
|
|
<span class="format-label">Hex (roh)</span>
|
|
<span class="format-value">${hexNoPrefix}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-value="${decValue}">
|
|
<span class="format-label">Dezimal</span>
|
|
<span class="format-value">${decValue}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-value="${binary}">
|
|
<span class="format-label">Binär</span>
|
|
<span class="format-value">${binary}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-value="${littleEndian}">
|
|
<span class="format-label">Little-Endian</span>
|
|
<span class="format-value">${littleEndian}</span>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(contextMenu);
|
|
|
|
// Handle menu item clicks
|
|
contextMenu.querySelectorAll('.context-menu-item').forEach(item => {
|
|
item.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
const value = item.getAttribute('data-value');
|
|
navigator.clipboard.writeText(value).then(() => {
|
|
showNotification('Kopiert: ' + value, 'success');
|
|
contextMenu.remove();
|
|
contextMenu = null;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
function toLittleEndianBytes(value) {
|
|
const bytes = [];
|
|
let num = value;
|
|
|
|
// Convert to 4-byte little-endian
|
|
for (let i = 0; i < 4; i++) {
|
|
bytes.push((num & 0xFF).toString(16).padStart(2, '0').toUpperCase());
|
|
num = num >> 8;
|
|
}
|
|
|
|
return bytes.join(' ');
|
|
}
|
|
|
|
// ============================================
|
|
// 6. NOTIFICATION SYSTEM
|
|
// ============================================
|
|
|
|
function showNotification(message, type = 'info') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification notification-${type}`;
|
|
notification.textContent = message;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Trigger animation
|
|
setTimeout(() => notification.classList.add('show'), 10);
|
|
|
|
// Remove after 2 seconds
|
|
setTimeout(() => {
|
|
notification.classList.remove('show');
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 2000);
|
|
}
|
|
|
|
// ============================================
|
|
// INITIALIZATION
|
|
// ============================================
|
|
|
|
export function initializeUXEnhancements() {
|
|
setupKeyboardShortcuts();
|
|
setupDoubleClickCopy();
|
|
setupCompactMode();
|
|
setupAutoCalculateOnPaste();
|
|
setupContextMenu();
|
|
|
|
console.log('✨ UX Enhancements activated!');
|
|
}
|