Files
webref/frontend/src/lib/canvas/keyboard.ts
Danilo Reyes e5abcced74
All checks were successful
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 8s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 7s
CI/CD Pipeline / VM Test - performance (push) Successful in 7s
CI/CD Pipeline / VM Test - security (push) Successful in 7s
CI/CD Pipeline / Backend Linting (push) Successful in 2s
CI/CD Pipeline / Frontend Linting (push) Successful in 16s
CI/CD Pipeline / Nix Flake Check (push) Successful in 38s
CI/CD Pipeline / CI Summary (push) Successful in 0s
phase 12
2025-11-02 14:34:55 -06:00

226 lines
4.7 KiB
TypeScript

/**
* Keyboard shortcuts for canvas operations
* Handles Ctrl+A (select all), Escape (deselect), and other shortcuts
*/
import { selection } from '$lib/stores/selection';
export interface KeyboardShortcutHandlers {
onSelectAll?: (allImageIds: string[]) => void;
onDeselectAll?: () => void;
onDelete?: () => void;
onCopy?: () => void;
onCut?: () => void;
onPaste?: () => void;
onUndo?: () => void;
onRedo?: () => void;
onBringToFront?: () => void;
onSendToBack?: () => void;
onBringForward?: () => void;
onSendBackward?: () => void;
}
/**
* Setup keyboard shortcuts for canvas
*/
export function setupKeyboardShortcuts(
getAllImageIds: () => string[],
handlers: KeyboardShortcutHandlers = {}
): () => void {
/**
* Handle keyboard shortcuts
*/
function handleKeyDown(e: KeyboardEvent) {
// Ignore if typing in input/textarea
if (
document.activeElement?.tagName === 'INPUT' ||
document.activeElement?.tagName === 'TEXTAREA'
) {
return;
}
const isCtrlOrCmd = e.ctrlKey || e.metaKey;
// Ctrl+A / Cmd+A - Select all
if (isCtrlOrCmd && e.key === 'a') {
e.preventDefault();
const allIds = getAllImageIds();
selection.selectAll(allIds);
if (handlers.onSelectAll) {
handlers.onSelectAll(allIds);
}
return;
}
// Escape - Deselect all
if (e.key === 'Escape') {
e.preventDefault();
selection.clearSelection();
if (handlers.onDeselectAll) {
handlers.onDeselectAll();
}
return;
}
// Delete / Backspace - Delete selected
if (e.key === 'Delete' || e.key === 'Backspace') {
e.preventDefault();
if (handlers.onDelete) {
handlers.onDelete();
}
return;
}
// Ctrl+C / Cmd+C - Copy
if (isCtrlOrCmd && e.key === 'c') {
e.preventDefault();
if (handlers.onCopy) {
handlers.onCopy();
}
return;
}
// Ctrl+X / Cmd+X - Cut
if (isCtrlOrCmd && e.key === 'x') {
e.preventDefault();
if (handlers.onCut) {
handlers.onCut();
}
return;
}
// Ctrl+V / Cmd+V - Paste
if (isCtrlOrCmd && e.key === 'v') {
e.preventDefault();
if (handlers.onPaste) {
handlers.onPaste();
}
return;
}
// Ctrl+Z / Cmd+Z - Undo
if (isCtrlOrCmd && e.key === 'z' && !e.shiftKey) {
e.preventDefault();
if (handlers.onUndo) {
handlers.onUndo();
}
return;
}
// Ctrl+Shift+Z / Cmd+Shift+Z - Redo
if (isCtrlOrCmd && e.key === 'z' && e.shiftKey) {
e.preventDefault();
if (handlers.onRedo) {
handlers.onRedo();
}
return;
}
// Ctrl+Y / Cmd+Y - Alternative Redo
if (isCtrlOrCmd && e.key === 'y') {
e.preventDefault();
if (handlers.onRedo) {
handlers.onRedo();
}
return;
}
// Ctrl+] - Bring to front
if (isCtrlOrCmd && e.key === ']') {
e.preventDefault();
if (handlers.onBringToFront) {
handlers.onBringToFront();
}
return;
}
// Ctrl+[ - Send to back
if (isCtrlOrCmd && e.key === '[') {
e.preventDefault();
if (handlers.onSendToBack) {
handlers.onSendToBack();
}
return;
}
// PageUp - Bring forward
if (e.key === 'PageUp') {
e.preventDefault();
if (handlers.onBringForward) {
handlers.onBringForward();
}
return;
}
// PageDown - Send backward
if (e.key === 'PageDown') {
e.preventDefault();
if (handlers.onSendBackward) {
handlers.onSendBackward();
}
return;
}
}
// Attach event listener
window.addEventListener('keydown', handleKeyDown);
// Return cleanup function
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}
/**
* Select all images programmatically
*/
export function selectAllImages(allImageIds: string[]): void {
selection.selectAll(allImageIds);
}
/**
* Deselect all images programmatically
*/
export function deselectAllImages(): void {
selection.clearSelection();
}
/**
* Check if modifier key is pressed
*/
export function isModifierPressed(e: KeyboardEvent): boolean {
return e.ctrlKey || e.metaKey;
}
/**
* Check if shift key is pressed
*/
export function isShiftPressed(e: KeyboardEvent): boolean {
return e.shiftKey;
}
/**
* Get keyboard shortcut display string
*/
export function getShortcutDisplay(shortcut: string): string {
const isMac = typeof navigator !== 'undefined' && /Mac/.test(navigator.platform);
return shortcut
.replace('Ctrl', isMac ? '⌘' : 'Ctrl')
.replace('Alt', isMac ? '⌥' : 'Alt')
.replace('Shift', isMac ? '⇧' : 'Shift');
}