consolidation & ux improvements
This commit is contained in:
@@ -62,26 +62,10 @@ export class ExFATFilesystem extends BaseFilesystem {
|
||||
|
||||
calculate(variantId) {
|
||||
if (variantId !== 'exfat') return;
|
||||
|
||||
const values = {};
|
||||
|
||||
const values = this.getInputValues(variantId);
|
||||
const results = {};
|
||||
|
||||
// Gather all input values for this variant
|
||||
const variant = this.variants.find(v => v.id === variantId);
|
||||
if (!variant) return;
|
||||
|
||||
[...variant.constants, ...variant.inputs].forEach(field => {
|
||||
const el = document.getElementById(field.id);
|
||||
if (el) {
|
||||
let val = el.value;
|
||||
if (val && val.startsWith('0x')) {
|
||||
values[field.id] = parseInt(val, 16);
|
||||
} else {
|
||||
values[field.id] = Number(val) || 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.calculateExFAT(values, results);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,22 +8,10 @@ import { checkDependencies, updateResultItem } from '../utils.js';
|
||||
export class FAT12_16Filesystem extends BaseFilesystem {
|
||||
calculate(variantId) {
|
||||
if (variantId !== 'fat1216') return;
|
||||
const values = {};
|
||||
|
||||
const values = this.getInputValues(variantId);
|
||||
const results = {};
|
||||
// Gather all input values for this variant
|
||||
const variant = this.variants.find(v => v.id === variantId);
|
||||
if (!variant) return;
|
||||
[...variant.constants, ...variant.inputs].forEach(field => {
|
||||
const el = document.getElementById(field.id);
|
||||
if (el) {
|
||||
let val = el.value;
|
||||
if (val && val.startsWith('0x')) {
|
||||
values[field.id] = parseInt(val, 16);
|
||||
} else {
|
||||
values[field.id] = Number(val) || 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.calculateFAT1216(values, results);
|
||||
}
|
||||
constructor() {
|
||||
@@ -211,22 +199,10 @@ export class FAT12_16Filesystem extends BaseFilesystem {
|
||||
export class FAT32Filesystem extends BaseFilesystem {
|
||||
calculate(variantId) {
|
||||
if (variantId !== 'fat32') return;
|
||||
const values = {};
|
||||
|
||||
const values = this.getInputValues(variantId);
|
||||
const results = {};
|
||||
// Gather all input values for this variant
|
||||
const variant = this.variants.find(v => v.id === variantId);
|
||||
if (!variant) return;
|
||||
[...variant.constants, ...variant.inputs].forEach(field => {
|
||||
const el = document.getElementById(field.id);
|
||||
if (el) {
|
||||
let val = el.value;
|
||||
if (val && val.startsWith('0x')) {
|
||||
values[field.id] = parseInt(val, 16);
|
||||
} else {
|
||||
values[field.id] = Number(val) || 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.calculateFAT32(values, results);
|
||||
}
|
||||
constructor() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Calculator } from './calculator.js';
|
||||
import { FAT12_16Filesystem, FAT32Filesystem } from './filesystems/fat.js';
|
||||
import { NTFSFilesystem } from './filesystems/ntfs.js';
|
||||
import { ExFATFilesystem } from './filesystems/exfat.js';
|
||||
import { initializeUXEnhancements } from './ux-enhancements.js';
|
||||
|
||||
class FilesystemCalculator {
|
||||
constructor() {
|
||||
@@ -87,6 +88,9 @@ class FilesystemCalculator {
|
||||
setupTooltips();
|
||||
setupCopyButtons();
|
||||
|
||||
// Initialize UX enhancements
|
||||
initializeUXEnhancements();
|
||||
|
||||
window.copyToClipboard = (elementId) => {
|
||||
import('./utils.js').then(module => {
|
||||
module.copyToClipboard(elementId);
|
||||
|
||||
340
webroot/js/ux-enhancements.js
Normal file
340
webroot/js/ux-enhancements.js
Normal 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!');
|
||||
}
|
||||
Reference in New Issue
Block a user