feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
这个提交包含在:
@@ -0,0 +1,39 @@
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
const commandService = CommandService.instance();
|
||||
|
||||
const getLabel = (commandName: string): string => {
|
||||
if (commandService.exists(commandName)) return commandService.label(commandName, true);
|
||||
|
||||
// Some commands are not registered in CommandService at the moment
|
||||
// Following hard-coded labels are used as a workaround
|
||||
|
||||
switch (commandName) {
|
||||
case 'quit':
|
||||
return _('Quit');
|
||||
case 'zoomActualSize':
|
||||
return _('Actual Size');
|
||||
case 'help':
|
||||
return _('Website and documentation');
|
||||
case 'hideApp':
|
||||
return _('Hide Joplin');
|
||||
case 'minimizeWindow':
|
||||
return _('Minimise');
|
||||
case 'closeWindow':
|
||||
return _('Close Window');
|
||||
case 'config':
|
||||
return shim.isMac() ? _('Preferences') : _('Options');
|
||||
}
|
||||
|
||||
// We don't throw an error if a command is not found because if for
|
||||
// example a command is removed from one version to the next, or a
|
||||
// command is renamed, we still want the keymap editor to work. So in
|
||||
// that case, we simply display the command name and it is up to the
|
||||
// user to fix the shortcut if needed.
|
||||
return `${commandName} (${_('Invalid')})`;
|
||||
};
|
||||
|
||||
export default getLabel;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useState } from 'react';
|
||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
interface CommandStatus {
|
||||
[commandName: string]: boolean;
|
||||
}
|
||||
|
||||
const useCommandStatus = (): [CommandStatus, (commandName: string)=> void, (commandName: string)=> void] => {
|
||||
const [status, setStatus] = useState<CommandStatus>(() =>
|
||||
keymapService.getCommandNames().reduce((accumulator: CommandStatus, command: string) => {
|
||||
accumulator[command] = false;
|
||||
return accumulator;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
const disableStatus = (commandName: string) => setStatus(prevStatus => ({ ...prevStatus, [commandName]: false }));
|
||||
const enableStatus = (commandName: string) => setStatus(prevStatus => {
|
||||
// Falsify all the commands; Only one command should be truthy at any given time
|
||||
const newStatus = Object.keys(prevStatus).reduce((accumulator: CommandStatus, command: string) => {
|
||||
accumulator[command] = false;
|
||||
return accumulator;
|
||||
}, {});
|
||||
|
||||
// Make the appropriate command truthful
|
||||
newStatus[commandName] = true;
|
||||
return newStatus;
|
||||
});
|
||||
|
||||
return [status, enableStatus, disableStatus];
|
||||
};
|
||||
|
||||
export default useCommandStatus;
|
||||
@@ -0,0 +1,96 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import KeymapService, { KeymapItem } from '@joplin/lib/services/KeymapService';
|
||||
import getLabel from './getLabel';
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
// This custom hook provides a synchronized snapshot of the keymap residing at KeymapService
|
||||
// All the logic regarding altering and interacting with the keymap is isolated from the components
|
||||
|
||||
function allKeymapItems() {
|
||||
const output = keymapService.getKeymapItems().slice();
|
||||
|
||||
output.sort((a: KeymapItem, b: KeymapItem) => {
|
||||
return getLabel(a.command).toLocaleLowerCase() < getLabel(b.command).toLocaleLowerCase() ? -1 : +1;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
const useKeymap = (): [
|
||||
KeymapItem[],
|
||||
Error,
|
||||
(keymapItems: KeymapItem[])=> void,
|
||||
(commandName: string, accelerator: string)=> void,
|
||||
(commandName: string)=> void,
|
||||
] => {
|
||||
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => allKeymapItems());
|
||||
const [keymapError, setKeymapError] = useState<Error>(null);
|
||||
const [mustSave, setMustSave] = useState(false);
|
||||
|
||||
const setAccelerator = (commandName: string, accelerator: string) => {
|
||||
setKeymapItems(prevKeymap => {
|
||||
const newKeymap = [...prevKeymap];
|
||||
|
||||
newKeymap.find(item => item.command === commandName).accelerator = accelerator || null /* Disabled */;
|
||||
return newKeymap;
|
||||
});
|
||||
|
||||
setMustSave(true);
|
||||
};
|
||||
|
||||
const resetAccelerator = (commandName: string) => {
|
||||
const defaultAccelerator = keymapService.getDefaultAccelerator(commandName);
|
||||
setKeymapItems(prevKeymap => {
|
||||
const newKeymap = [...prevKeymap];
|
||||
|
||||
newKeymap.find(item => item.command === commandName).accelerator = defaultAccelerator;
|
||||
return newKeymap;
|
||||
});
|
||||
|
||||
setMustSave(true);
|
||||
};
|
||||
|
||||
const overrideKeymapItems = (customKeymapItems: KeymapItem[]) => {
|
||||
const oldKeymapItems = [...customKeymapItems];
|
||||
keymapService.resetKeymap(); // Start with a fresh keymap
|
||||
|
||||
try {
|
||||
// First, try to update the in-memory keymap of KeymapService
|
||||
// This function will throw if there are any issues with the new custom keymap
|
||||
keymapService.overrideKeymap(customKeymapItems);
|
||||
// Then, update the state with the data from KeymapService
|
||||
// Side-effect: Changes will also be saved to the disk
|
||||
setKeymapItems(keymapService.getKeymapItems());
|
||||
setMustSave(true);
|
||||
} catch (error) {
|
||||
// oldKeymapItems includes even the unchanged keymap items
|
||||
// However, it is not an issue because the logic accounts for such scenarios
|
||||
keymapService.overrideKeymap(oldKeymapItems);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!mustSave) return;
|
||||
|
||||
setMustSave(false);
|
||||
|
||||
async function saveKeymap() {
|
||||
try {
|
||||
keymapService.overrideKeymap(keymapItems);
|
||||
await keymapService.saveCustomKeymap();
|
||||
setKeymapError(null);
|
||||
} catch (error) {
|
||||
error.message = `Could not save file: ${error.message}`;
|
||||
setKeymapError(error);
|
||||
}
|
||||
}
|
||||
|
||||
void saveKeymap();
|
||||
}, [keymapItems, mustSave]);
|
||||
|
||||
return [keymapItems, keymapError, overrideKeymapItems, setAccelerator, resetAccelerator];
|
||||
};
|
||||
|
||||
export default useKeymap;
|
||||
在新工单中引用
屏蔽一个用户