consolidation & ux improvements

This commit is contained in:
overcuriousity
2025-11-12 21:46:24 +01:00
parent 02e3700828
commit 2261f735d7
6 changed files with 538 additions and 49 deletions

View File

@@ -0,0 +1,340 @@
// 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!');
}