31 décembre 2025

Important : Darktable rétablit automatiquement les raccourcis standard manquants au démarrage.

Pour éviter que ta configuration personnalisée ne soit perturbée, tu devrais désactiver cette fonction automatique.

  • Dans Darktable, allez dans Paramètres > Divers.
  • Désactivez l'option « Restaurer les raccourcis clavier au démarrage » (si elle est disponible/active).

Installation du fichier :

  • Quitter Darktable
  • Remplacez le fichier shortcutsrc dans ton dossier de configuration par la nouvelle version.
QWERTZ QWERTY AZERTY

Load your shortcutsrc file to begin editing. You can also drag & drop the file anywhere on this page.

Default By Key By Value
Shortcut Key Action (Value) Context Actions
const preloadedShortcutsrcContent = « ; // — CONFIGURATION — const KEYBOARD_LAYOUTS = { qwertz: { fkeys: [ [ [« Escape »], « SPACER », [« F1 », « F1 », « key-fkey »], [« F2 », « F2 », « key-fkey »], [« F3 », « F3 », « key-fkey »], [« F4 », « F4 », « key-fkey »], « SPACER », [« F5 », « F5 », « key-fkey »], [« F6 », « F6 », « key-fkey »], [« F7 », « F7 », « key-fkey »], [« F8 », « F8 », « key-fkey »], « SPACER », [« F9 », « F9 », « key-fkey »], [« F10 », « F10 », « key-fkey »], [« F11 », « F11 », « key-fkey »], [« F12 », « F12 », « key-fkey »], « SPACER », ], ], main: [ [ [« ^ », « asciicircum »], [« 1 »], [« 2 »], [« 3 »], [« 4 »], [« 5 »], [« 6 »], [« 7 »], [« 8 »], [« 9 »], [« 0 »], [« ß », « ssharp »], [« ´ », « acute »], [« BackSpace », « BackSpace », « key-backspace »], ], [ [« Tab », « Tab », « key-tab », « modifier »], [« q »], [« w »], [« e »], [« r »], [« t »], [« z »], [« u »], [« i »], [« o »], [« p »], [« Ü », « udiaeresis »], [« + », « plus »], [« Return », « Return », « key-return »], ], [ [« Caps », « Caps_Lock », « key-caps », « modifier »], [« a »], [« s »], [« d »], [« f »], [« g »], [« h »], [« j »], [« k »], [« l »], [« Ö », « odiaeresis »], [« Ä », « adiaeresis »], [« # », « numbersign »], ], [ [« Shift », « shift », « key-shift-l », « modifier »], [« <", "<"], ["y"], ["x"], ["c"], ["v"], ["b"], ["n"], ["m"], [",", "comma"], [".", "period"], ["-", "minus"], ["Shift", "shift", "key-shift-r", "modifier"], ], [ ["Ctrl", "control", "key-ctrl", "modifier"], ["Win", "super", "", "modifier"], ["Alt", "alt", "", "modifier"], ["Space", "space", "key-space"], ["AltGr", "alt", "", "modifier"], ["Ctrl", "control", "key-ctrl", "modifier"], ], ], special: [ [ ["Print", "Print", "key-special"], ["Scroll", "Scroll", "key-special"], ["Pause", "Pause", "key-special"], ["None", "None", "key-special"], ], [ ["Ins", "Insert", "key-special"], ["Home", "Home", "key-special"], ["P_Up", "Page_Up", "key-special"], ], [ ["Delete", "Delete", "key-special"], ["End", "End", "key-special"], ["P_Down", "Page_Down", "key-special"], ], ], arrows: [ [ ["Left", "Left", "key-special"], ["Up", "Up", "key-special"], ["Down", "Down", "key-special"], ["Right", "Right", "key-special"], ], ], }, qwerty: { fkeys: [ [ ["Escape"], "SPACER", ["F1", "F1", "key-fkey"], ["F2", "F2", "key-fkey"], ["F3", "F3", "key-fkey"], ["F4", "F4", "key-fkey"], "SPACER", ["F5", "F5", "key-fkey"], ["F6", "F6", "key-fkey"], ["F7", "F7", "key-fkey"], ["F8", "F8", "key-fkey"], "SPACER", ["F9", "F9", "key-fkey"], ["F10", "F10", "key-fkey"], ["F11", "F11", "key-fkey"], ["F12", "F12", "key-fkey"], "SPACER", ], ], main: [ [ ["~", "asciitilde"], ["1"], ["2"], ["3"], ["4"], ["5"], ["6"], ["7"], ["8"], ["9"], ["0"], ["-", "minus"], ["=", "equal"], ["BackSpace", "BackSpace", "key-backspace"], ], [ ["Tab", "Tab", "key-tab", "modifier"], ["q"], ["w"], ["e"], ["r"], ["t"], ["y"], ["u"], ["i"], ["o"], ["p"], ["[", "bracketleft"], ["]", "bracketright"], ["\\", "backslash"], ], [ ["Caps", "Caps_Lock", "key-caps", "modifier"], ["a"], ["s"], ["d"], ["f"], ["g"], ["h"], ["j"], ["k"], ["l"], [";", "semicolon"], ["'", "quote"], ["Enter", "Return", "key-return"], ], [ ["Shift", "shift", "key-shift-l", "modifier"], [",", "comma"], [".", "period"], ["/", "slash"], ["z"], ["x"], ["c"], ["v"], ["b"], ["n"], ["m"], ["Shift", "shift", "key-shift-r", "modifier"], ], [ ["Ctrl", "control", "key-ctrl", "modifier"], ["Win", "super", "", "modifier"], ["Alt", "alt", "", "modifier"], ["Space", "space", "key-space"], ["AltGr", "alt", "", "modifier"], ["Ctrl", "control", "key-ctrl", "modifier"], ], ], special: [ [ ["Print", "Print", "key-special"], ["Scroll", "Scroll", "key-special"], ["Pause", "Pause", "key-special"], ["None", "None", "key-special"], ], [ ["Insert", "Insert", "key-special"], ["Home", "Home", "key-special"], ["P_Up", "Page_Up", "key-special"], ], [ ["Del", "Delete", "key-special"], ["End", "End", "key-special"], ["P_Down", "Page_Down", "key-special"], ], ], arrows: [ [ ["Left", "Left", "key-special"], ["Up", "Up", "key-special"], ["Down", "Down", "key-special"], ["Right", "Right", "key-special"], ], ], }, azerty: { fkeys: [ [ ["Escape"], "SPACER", ["F1", "F1", "key-fkey"], ["F2", "F2", "key-fkey"], ["F3", "F3", "key-fkey"], ["F4", "F4", "key-fkey"], "SPACER", ["F5", "F5", "key-fkey"], ["F6", "F6", "key-fkey"], ["F7", "F7", "key-fkey"], ["F8", "F8", "key-fkey"], "SPACER", ["F9", "F9", "key-fkey"], ["F10", "F10", "key-fkey"], ["F11", "F11", "key-fkey"], ["F12", "F12", "key-fkey"], "SPACER", ], ], main: [ [ ["²", "asciicircum"], ["1"], ["2"], ["3"], ["4"], ["5"], ["6"], ["7"], ["8"], ["9"], ["0"], ["°", "degree"], ["+", "plus"], ["BackSpace", "BackSpace", "key-backspace"], ], [ ["Tab", "Tab", "key-tab", "modifier"], ["a"], ["z"], ["e"], ["r"], ["t"], ["y"], ["u"], ["i"], ["o"], ["p"], ["^", "asciicircum"], ["$", "dollar"], ["Return", "Return", "key-return"], ], [ ["Caps", "Caps_Lock", "key-caps", "modifier"], ["q"], ["s"], ["d"], ["f"], ["g"], ["h"], ["j"], ["k"], ["l"], ["m"], ["ù", "grave"], ["*", "asterisk"], ], [ ["Shift", "shift", "key-shift-l", "modifier"], [" { if (!str) return «  »; return str .replace(/&/g, « & ») .replace(//g, « > ») .replace(/ »/g, « " ») .replace(/’/g, « ' »); }; const escapeAttr = (str) => { if (!str) return «  »; return str.replace(/ »/g, « " »); }; const normalizeKey = (key) => { if (!key) return «  »; return key .trim() .replace(/^;+|;+$/g, «  ») .replace(/;{2,}/g, « ; »); }; const getContext = (value) => { if (!value || !value.includes(« / »)) { return « global »; } const [prefix, view] = value.split(« / »); if (prefix === « views ») { if (view && view.startsWith(« darkroom »)) return « darkroom »; if (view && view.startsWith(« lighttable »)) return « lighttable »; return « lighttable »; // Default for ‘views’ is lighttable } if ([« actions », « iop », « utility »].includes(prefix)) { return « darkroom »; } return « global »; }; const parseFileContent = (content) => { const lines = content.split(« \n »); const parsedShortcuts = []; nextId = 0; lines.forEach((line) => { line = line.trim(); if (line.startsWith(« # ») || !line) return; if (line.includes(« = »)) { const [key, value] = line.split(« = », 2); if (key) { const trimmedValue = value.trim(); parsedShortcuts.push({ id: nextId++, key: key.trim(), value: trimmedValue, context: getContext(trimmedValue), }); } } }); return parsedShortcuts; }; const processAndRenderContent = (content, sourceName) => { try { shortcuts = parseFileContent(content); fileInfo.textContent = `Loaded from « ${sourceName} ». Found ${shortcuts.length} shortcuts.`; mainContent.classList.remove(« d-none »); renderAll(); } catch (err) { console.error(« Error processing file content: », err); fileInfo.textContent = `Error: Could not process content from « ${sourceName} ». Check console for details.`; mainContent.classList.add(« d-none »); } }; const renderAll = () => { renderKeyboard(); renderTable(); renderFilterInfo(); }; const getKeyboardMap = () => { const keyMap = {}; let shortcutsToDisplay = shortcuts; if (currentKeyboardContext !== « all ») { shortcutsToDisplay = shortcuts.filter( (s) => s.context === currentKeyboardContext, ); } shortcutsToDisplay.forEach((entry) => { const parts = entry.key .toLowerCase() .split(« ; ») .map((k) => k.trim()); parts.forEach((part) => { if (!part) return; if (!keyMap[part]) keyMap[part] = []; keyMap[part].push( `[${entry.context}] ${escapeHTML(entry.key)} = ${escapeHTML(entry.value)}`, ); }); }); return keyMap; }; // — RENDER FUNCTIONS — const renderKeyboard = () => { const keyMap = getKeyboardMap(); keyboardContainer.innerHTML = «  »; const createKeyElement = (keyInfo) => { if (!keyInfo) return ‘
‘; if (keyInfo === « SPACER ») return ‘
‘; const [ displayText, dataKey = displayText.toLowerCase(), cssClass = «  », modifierClass = «  », ] = keyInfo; const keyId = dataKey.toLowerCase(); if (!keyId) return «  »; const isAssigned = keyMap[keyId]; const isSelected = currentFilterKeys.includes(keyId); const tooltip = isAssigned ? `${keyMap[keyId].join(« \n »)}` : «  »; return `
${displayText}${tooltip}
`; }; Object.entries(KEYBOARD_LAYOUTS[currentLayout]).forEach( ([blockName, layout]) => { const blockContainer = document.createElement(« div »); blockContainer.classList.add( « keyboard-block », `keyboard-${blockName}`, ); layout.forEach((row) => { const rowContainer = document.createElement(« div »); rowContainer.classList.add(« key-row »); row.forEach((keyInfo) => { rowContainer.innerHTML += createKeyElement(keyInfo); }); blockContainer.appendChild(rowContainer); }); keyboardContainer.appendChild(blockContainer); }, ); }; const renderTable = () => { let displayData = […shortcuts]; if (currentSearchTerm) { const lowerCaseSearchTerm = currentSearchTerm.toLowerCase(); displayData = displayData.filter( (entry) => entry.key .toLowerCase() .includes(lowerCaseSearchTerm) || entry.value .toLowerCase() .includes(lowerCaseSearchTerm) || entry.context .toLowerCase() .includes(lowerCaseSearchTerm), ); } if (currentFilterKeys.length > 0) { displayData = displayData.filter((entry) => { const entryKeys = entry.key .toLowerCase() .split(« ; ») .map((k) => k.trim()); return currentFilterKeys.every((filterKey) => entryKeys.includes(filterKey), ); }); } switch (currentSortBy) { case « key »: displayData.sort((a, b) => a.key.localeCompare(b.key)); break; case « value »: displayData.sort((a, b) => a.value.localeCompare(b.value), ); break; } tableBody.innerHTML = displayData.length === 0 && !document.querySelector(« .add-new-row ») ? ‘No shortcuts found for the current filter.‘ : displayData .map( (entry) => `${entry.context} `, ) .join(«  »); }; const renderFilterInfo = () => { if (currentFilterKeys.length > 0) { filterInfo.innerHTML = `Filter active: ${currentFilterKeys.join( » + « )} `; } else { filterInfo.innerHTML = «  »; } }; // — EVENT HANDLERS — fileInput.addEventListener(« change », (event) => { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { processAndRenderContent(e.target.result, file.name); }; reader.readAsText(file); }); sortSelect.addEventListener(« change », (e) => { currentSortBy = e.target.value; renderTable(); }); searchInput.addEventListener(« input », (e) => { currentSearchTerm = e.target.value; renderTable(); }); downloadButton.addEventListener(« click », () => { const fileContent = shortcuts .map((entry) => `${entry.key}=${entry.value}`) .join(« \n »); const blob = new Blob([fileContent], { type: « text/plain;charset=utf-8 », }); const a = document.createElement(« a »); a.href = URL.createObjectURL(blob); a.download = « shortcutsrc »; document.body.appendChild(a); a.click(); document.body.removeChild(a); }); addShortcutButton.addEventListener(« click », () => { if (document.querySelector(« .add-new-row »)) return; const newRow = document.createElement(« tr »); newRow.classList.add(« add-new-row »); newRow.innerHTML = ` `; tableBody.prepend(newRow); }); keyboardContainer.addEventListener(« click », (e) => { const keyElement = e.target.closest(« .filter-key »); if (!keyElement) return; const filterKey = keyElement.dataset.key; if (e.shiftKey) { const index = currentFilterKeys.indexOf(filterKey); if (index > -1) { currentFilterKeys.splice(index, 1); } else { currentFilterKeys.push(filterKey); } } else { currentFilterKeys = currentFilterKeys.length === 1 && currentFilterKeys[0] === filterKey ? [] : [filterKey]; } renderAll(); }); contextFilterButtons.addEventListener(« click », (e) => { const button = e.target.closest(« button »); if (!button) return; currentKeyboardContext = button.dataset.context; contextFilterButtons .querySelectorAll(« button ») .forEach((btn) => { btn.classList.remove( « btn-primary », « btn-outline-secondary », ); if (btn === button) { btn.classList.add(« btn-primary »); } else { btn.classList.add(« btn-outline-secondary »); } }); renderKeyboard(); }); filterInfo.addEventListener(« click », (e) => { if (e.target.id === « clear-filter-btn ») { currentFilterKeys = []; renderAll(); } }); tableBody.addEventListener(« click », (e) => { const row = e.target.closest(« tr »); if (!row) return; const handleUpdate = (isNew) => { const keyInput = row.querySelector(« td:nth-child(1) input »); const valueInput = row.querySelector( « td:nth-child(2) input », ); const newKey = normalizeKey(keyInput.value); const newValue = valueInput.value.trim(); if (!newKey || !newValue) { alert(« Key and Value cannot be empty. »); return; } const newContext = getContext(newValue); const entryId = isNew ? -1 : parseInt(row.dataset.id, 10); const conflict = shortcuts.find( (s) => s.id !== entryId && s.key === newKey && (s.context === newContext || s.context === « global » || newContext === « global »), ); if (conflict) { alert( `Conflict found! The key ‘${newKey}’ is already assigned for the context ‘${newContext}’ (Action: ${conflict.value}). Global shortcuts cannot be overwritten in a specific context and vice versa with the same key.`, ); return; } if (isNew) { shortcuts.push({ id: nextId++, key: newKey, value: newValue, context: newContext, }); } else { const entry = shortcuts.find((s) => s.id === entryId); if (entry) { entry.key = newKey; entry.value = newValue; entry.context = newContext; } } renderAll(); }; if (e.target.classList.contains(« save-new-btn »)) handleUpdate(true); if (e.target.classList.contains(« update-btn »)) handleUpdate(false); if (e.target.classList.contains(« cancel-new-btn »)) row.remove(); if (e.target.classList.contains(« delete-btn »)) { const entryId = parseInt(row.dataset.id, 10); const entryIndex = shortcuts.findIndex( (s) => s.id === entryId, ); if ( entryIndex > -1 && confirm( `Really delete shortcut « ${shortcuts[entryIndex].key} »?`, ) ) { shortcuts.splice(entryIndex, 1); renderAll(); } } }); const loadFromUrl = async () => { const urlParams = new URLSearchParams(window.location.search); const srcUrl = urlParams.get(« src »); if (srcUrl) { const fileInputCard = fileInput.closest(« .card »); if (fileInputCard) { fileInputCard.classList.add(« d-none »); } try { fileInfo.textContent = `Loading from URL: ${srcUrl}`; const response = await fetch(srcUrl); if (!response.ok) { throw new Error( `HTTP error! status: ${response.status}`, ); } const content = await response.text(); processAndRenderContent(content, srcUrl); } catch (err) { console.error( « Error fetching or processing file from URL: », err, ); fileInfo.textContent = `Error: Could not load file from URL. ${err.message}`; mainContent.classList.add(« d-none »); if (fileInputCard) { fileInputCard.classList.remove(« d-none »); } } } }; document.body.addEventListener(« dragover », (event) => { event.preventDefault(); document.body.classList.add(« drag-over »); }); document.body.addEventListener(« dragleave », () => { document.body.classList.remove(« drag-over »); }); document.body.addEventListener(« drop », (event) => { event.preventDefault(); document.body.classList.remove(« drag-over »); const file = event.dataTransfer.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { processAndRenderContent(e.target.result, file.name); }; reader.readAsText(file); }); // — INITIALIZATION — document.addEventListener(« DOMContentLoaded », () => { if ( preloadedShortcutsrcContent && preloadedShortcutsrcContent.trim() !== «  » ) { const fileInputCard = fileInput.closest(« .card »); if (fileInputCard) { fileInputCard.classList.add(« d-none »); } processAndRenderContent( preloadedShortcutsrcContent, « pre-loaded variable », ); } else { loadFromUrl(); } // Standard-Theme auf « dark » setzen document.body.classList.add(« dark-mode »); localStorage.setItem(« theme », « dark »); // QWERTZ als Standard-Tastaturbelegung keyboardLayoutSelect.value = « qwertz »; currentLayout = « qwertz »; renderAll(); }); // — THEME — document .getElementById(« theme-toggle ») .addEventListener(« click », () => { document.body.classList.toggle(« dark-mode »); localStorage.setItem( « theme », document.body.classList.contains(« dark-mode ») ? « dark » : « light », ); }); if (localStorage.getItem(« theme ») === « dark ») { document.body.classList.add(« dark-mode »); } // — KEYBOARD LAYOUT — keyboardLayoutSelect.addEventListener(« change », (e) => { currentLayout = e.target.value; renderAll(); });

Laisser un commentaire