diff --git a/webroot/css/ux-enhancements.css b/webroot/css/ux-enhancements.css
new file mode 100644
index 0000000..c9bdd03
--- /dev/null
+++ b/webroot/css/ux-enhancements.css
@@ -0,0 +1,184 @@
+/* ============================================
+ UX ENHANCEMENTS STYLES - Optimized
+ ============================================ */
+
+/* Keyboard Shortcuts Hint */
+.keyboard-hint {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ z-index: 1000;
+ background: #2a2a2a;
+ border: 1px solid #4a4a4a;
+ border-radius: 8px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+}
+
+.hint-toggle {
+ display: block;
+ padding: 10px 15px;
+ cursor: pointer;
+ font-size: 24px;
+ background: none;
+ border: none;
+ transition: transform 0.2s;
+}
+
+.hint-toggle:hover { transform: scale(1.1); }
+
+.hint-content {
+ padding: 15px;
+ max-width: 300px;
+}
+
+.hint-content h4 {
+ margin: 0 0 10px 0;
+ color: #4CAF50;
+ font-size: 14px;
+}
+
+.hint-content ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.hint-content li {
+ padding: 5px 0;
+ font-size: 12px;
+ color: #cccccc;
+}
+
+.hint-content kbd {
+ background: #1a1a1a;
+ border: 1px solid #555;
+ border-radius: 3px;
+ padding: 2px 6px;
+ font-family: 'Consolas', monospace;
+ font-size: 11px;
+ color: #ffffff;
+ margin-right: 8px;
+}
+
+/* Compact Mode Toggle Button */
+.compact-toggle {
+ background: #2a2a2a;
+ border: 1px solid #4a4a4a;
+ border-radius: 4px;
+ color: #cccccc;
+ padding: 8px 15px;
+ cursor: pointer;
+ font-size: 12px;
+ transition: all 0.2s;
+}
+
+.compact-toggle:hover {
+ background: #3a3a3a;
+ border-color: #666666;
+ color: #ffffff;
+}
+
+/* Compact Mode Styles - Condensed */
+body.compact-mode .section { padding: 10px; }
+body.compact-mode .section h2 { font-size: 14px; margin-bottom: 8px; }
+body.compact-mode .input-group { margin-bottom: 8px; }
+body.compact-mode .input-group label { font-size: 11px; }
+body.compact-mode .input-group input { padding: 4px 8px; font-size: 12px; }
+body.compact-mode .result-item { padding: 6px 10px; }
+body.compact-mode .result-label,
+body.compact-mode .result-value { font-size: 11px; }
+body.compact-mode .hex-calculator { display: none; }
+body.compact-mode .tab { padding: 6px 12px; font-size: 12px; }
+body.compact-mode .formula-item { padding: 4px 8px; font-size: 11px; }
+
+/* Context Menu - Condensed */
+.context-menu {
+ position: fixed;
+ background: #2a2a2a;
+ border: 1px solid #4a4a4a;
+ border-radius: 6px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+ z-index: 10000;
+ min-width: 250px;
+ padding: 4px 0;
+}
+
+.context-menu-header {
+ padding: 8px 12px;
+ font-size: 11px;
+ color: #888888;
+ font-weight: bold;
+ border-bottom: 1px solid #3a3a3a;
+ text-transform: uppercase;
+}
+
+.context-menu-item {
+ display: flex;
+ justify-content: space-between;
+ padding: 8px 12px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ font-size: 12px;
+}
+
+.context-menu-item:hover { background: #3a3a3a; }
+
+.format-label { color: #888888; font-weight: bold; margin-right: 15px; }
+.format-value { color: #4CAF50; font-family: 'Consolas', monospace; font-weight: bold; }
+
+/* Notification System - Condensed */
+.notification {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ padding: 12px 20px;
+ background: #2a2a2a;
+ border: 1px solid #4a4a4a;
+ border-radius: 6px;
+ color: #ffffff;
+ font-size: 13px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+ z-index: 10001;
+ opacity: 0;
+ transform: translateX(400px);
+ transition: all 0.3s ease;
+}
+
+.notification.show { opacity: 1; transform: translateX(0); }
+.notification-success { border-left: 4px solid #4CAF50; }
+.notification-info { border-left: 4px solid #2196F3; }
+.notification-warning { border-left: 4px solid #FF9800; }
+.notification-error { border-left: 4px solid #F44336; }
+
+/* Hover effects & Responsive - Condensed */
+.result-value, .conv-value { cursor: pointer; transition: background-color 0.3s; }
+.result-value:hover, .conv-value:hover { background-color: rgba(76, 175, 80, 0.1); }
+
+button:focus-visible,
+input:focus-visible,
+.tab:focus-visible { outline: 2px solid #4CAF50; outline-offset: 2px; }
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .keyboard-hint, .notification { bottom: 10px; right: 10px; }
+ .notification { top: 10px; max-width: calc(100% - 20px); }
+}
+
+@media print {
+ .keyboard-hint,
+ .compact-toggle,
+ .context-menu,
+ .notification { display: none !important; }
+}
diff --git a/webroot/index.html b/webroot/index.html
index aabaf29..a0d31fc 100644
--- a/webroot/index.html
+++ b/webroot/index.html
@@ -7,6 +7,7 @@
Dateisystem-Offset-Rechner
+
diff --git a/webroot/js/filesystems/exfat.js b/webroot/js/filesystems/exfat.js
index 9a425e1..549e998 100644
--- a/webroot/js/filesystems/exfat.js
+++ b/webroot/js/filesystems/exfat.js
@@ -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);
}
diff --git a/webroot/js/filesystems/fat.js b/webroot/js/filesystems/fat.js
index d9ad53c..b0e6db8 100644
--- a/webroot/js/filesystems/fat.js
+++ b/webroot/js/filesystems/fat.js
@@ -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() {
diff --git a/webroot/js/main.js b/webroot/js/main.js
index 17ce81a..4fb4e98 100644
--- a/webroot/js/main.js
+++ b/webroot/js/main.js
@@ -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);
diff --git a/webroot/js/ux-enhancements.js b/webroot/js/ux-enhancements.js
new file mode 100644
index 0000000..fe7dc22
--- /dev/null
+++ b/webroot/js/ux-enhancements.js
@@ -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 = `
+ ⌨️
+
+
Tastenkombinationen
+
+ - Ctrl+1-9 Tabs wechseln
+ - Ctrl+K Rechner fokussieren
+ - Ctrl+L Erste Eingabe fokussieren
+ - Alt+C Eingaben löschen
+ - Ctrl+Shift+C Alle Ergebnisse kopieren
+ - Doppelklick Wert kopieren
+
+
+ `;
+ 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 = `
+
+
+
+
+
+
+ `;
+
+ 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!');
+}