Add keyboard controls

This commit is contained in:
2025-12-11 13:34:49 +01:00
parent 0b1b7dd985
commit 0f6e840807
3 changed files with 61 additions and 2 deletions

View File

@@ -27,6 +27,8 @@ spring:
driver-class-name: org.sqlite.JDBC driver-class-name: org.sqlite.JDBC
hikari: hikari:
connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;" connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;"
maximum-pool-size: 1
minimum-idle: 1
jpa: jpa:
hibernate: hibernate:
ddl-auto: update ddl-auto: update

View File

@@ -1247,7 +1247,6 @@ body {
.asset-item.pending { .asset-item.pending {
cursor: default; cursor: default;
border-style: dashed;
opacity: 0.92; opacity: 0.92;
} }
@@ -1682,7 +1681,7 @@ body {
.toast-container { .toast-container {
position: fixed; position: fixed;
top: 16px; bottom: 16px;
right: 16px; right: 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -26,6 +26,8 @@ const MAX_VOLUME = 2;
const VOLUME_SLIDER_MAX = 200; const VOLUME_SLIDER_MAX = 200;
const VOLUME_CURVE_STRENGTH = -0.6; const VOLUME_CURVE_STRENGTH = -0.6;
const pendingTransformSaves = new Map(); const pendingTransformSaves = new Map();
const KEYBOARD_NUDGE_STEP = 5;
const KEYBOARD_NUDGE_FAST_STEP = 20;
const controlsPanel = document.getElementById('asset-controls'); const controlsPanel = document.getElementById('asset-controls');
@@ -84,6 +86,13 @@ function debounce(fn, wait = 150) {
}; };
} }
function isFormInputElement(element) {
if (!element) return false;
if (element.isContentEditable) return true;
const tag = element.tagName ? element.tagName.toLowerCase() : '';
return ['input', 'textarea', 'select', 'button', 'option'].includes(tag);
}
function schedulePersistTransform(asset, silent = false, delay = 200) { function schedulePersistTransform(asset, silent = false, delay = 200) {
if (!asset?.id) return; if (!asset?.id) return;
cancelPendingTransform(asset.id); cancelPendingTransform(asset.id);
@@ -349,6 +358,55 @@ if (selectedDeleteBtn) {
deleteAsset(asset); deleteAsset(asset);
}); });
} }
window.addEventListener('keydown', (event) => {
if (isFormInputElement(event.target)) {
return;
}
const asset = getSelectedAsset();
if ((event.key === 'Delete' || event.key === 'Backspace') && asset) {
event.preventDefault();
deleteAsset(asset);
return;
}
if (!asset || isAudioAsset(asset)) {
return;
}
const step = event.shiftKey ? KEYBOARD_NUDGE_FAST_STEP : KEYBOARD_NUDGE_STEP;
let moved = false;
switch (event.key) {
case 'ArrowUp':
asset.y -= step;
moved = true;
break;
case 'ArrowDown':
asset.y += step;
moved = true;
break;
case 'ArrowLeft':
asset.x -= step;
moved = true;
break;
case 'ArrowRight':
asset.x += step;
moved = true;
break;
default:
break;
}
if (moved) {
event.preventDefault();
updateRenderState(asset);
schedulePersistTransform(asset);
drawAndList();
}
});
function connect() { function connect() {
const socket = new SockJS('/ws'); const socket = new SockJS('/ws');
stompClient = Stomp.over(socket); stompClient = Stomp.over(socket);