mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-23 18:53:36 +02:00
parent
11c8bf7e6e
commit
33e1214ef6
@ -21,7 +21,7 @@ import { Command } from './types';
|
||||
* and look at the `execute()` command.
|
||||
*/
|
||||
export default class JoplinCommands {
|
||||
/**
|
||||
/**
|
||||
* <span class="platform-desktop">desktop</span> Executes the given
|
||||
* command.
|
||||
*
|
||||
@ -40,8 +40,8 @@ export default class JoplinCommands {
|
||||
* await joplin.commands.execute('newFolder', "SOME_FOLDER_ID");
|
||||
* ```
|
||||
*/
|
||||
execute(commandName: string, ...args: any[]): Promise<any | void>;
|
||||
/**
|
||||
execute(commandName: string, ...args: any[]): Promise<any | void>;
|
||||
/**
|
||||
* <span class="platform-desktop">desktop</span> Registers a new command.
|
||||
*
|
||||
* ```typescript
|
||||
@ -57,5 +57,5 @@ export default class JoplinCommands {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
register(command: Command): Promise<void>;
|
||||
register(command: Command): Promise<void>;
|
||||
}
|
||||
|
@ -5,6 +5,6 @@
|
||||
* so for now disable filters.
|
||||
*/
|
||||
export default class JoplinFilters {
|
||||
on(name: string, callback: Function): Promise<void>;
|
||||
off(name: string, callback: Function): Promise<void>;
|
||||
on(name: string, callback: Function): Promise<void>;
|
||||
off(name: string, callback: Function): Promise<void>;
|
||||
}
|
||||
|
@ -12,6 +12,6 @@ import { ExportModule, ImportModule } from './types';
|
||||
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/api/references/rest_api/
|
||||
*/
|
||||
export default class JoplinInterop {
|
||||
registerExportModule(module: ExportModule): Promise<void>;
|
||||
registerImportModule(module: ImportModule): Promise<void>;
|
||||
registerExportModule(module: ExportModule): Promise<void>;
|
||||
registerImportModule(module: ImportModule): Promise<void>;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export interface ChangeEvent {
|
||||
*/
|
||||
keys: string[];
|
||||
}
|
||||
export declare type ChangeHandler = (event: ChangeEvent) => void;
|
||||
export declare type ChangeHandler = (event: ChangeEvent)=> void;
|
||||
/**
|
||||
* This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user.
|
||||
*
|
||||
|
@ -3,7 +3,7 @@ import { Disposable } from './types';
|
||||
declare enum ItemChangeEventType {
|
||||
Create = 1,
|
||||
Update = 2,
|
||||
Delete = 3
|
||||
Delete = 3,
|
||||
}
|
||||
interface ItemChangeEvent {
|
||||
id: string;
|
||||
@ -12,8 +12,8 @@ interface ItemChangeEvent {
|
||||
interface SyncStartEvent {
|
||||
withErrors: boolean;
|
||||
}
|
||||
declare type ItemChangeHandler = (event: ItemChangeEvent) => void;
|
||||
declare type SyncStartHandler = (event: SyncStartEvent) => void;
|
||||
declare type ItemChangeHandler = (event: ItemChangeEvent)=> void;
|
||||
declare type SyncStartHandler = (event: SyncStartEvent)=> void;
|
||||
/**
|
||||
* The workspace service provides access to all the parts of Joplin that
|
||||
* are being worked on - i.e. the currently selected notes or notebooks as
|
||||
|
@ -330,57 +330,16 @@ export enum SettingItemType {
|
||||
export interface SettingItem {
|
||||
value: any;
|
||||
type: SettingItemType;
|
||||
|
||||
label: string;
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* A public setting will appear in the Configuration screen and will be
|
||||
* modifiable by the user. A private setting however will not appear there,
|
||||
* and can only be changed programmatically. You may use this to store some
|
||||
* values that you do not want to directly expose.
|
||||
*/
|
||||
public: boolean;
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* You would usually set this to a section you would have created
|
||||
* specifically for the plugin.
|
||||
*/
|
||||
section?: string;
|
||||
|
||||
/**
|
||||
* To create a setting with multiple options, set this property to `true`.
|
||||
* That setting will render as a dropdown list in the configuration screen.
|
||||
*/
|
||||
description?: string;
|
||||
isEnum?: boolean;
|
||||
|
||||
/**
|
||||
* This property is required when `isEnum` is `true`. In which case, it
|
||||
* should contain a map of value => label.
|
||||
*/
|
||||
options?: Record<any, any>;
|
||||
|
||||
/**
|
||||
* Reserved property. Not used at the moment.
|
||||
*/
|
||||
section?: string;
|
||||
options?: any;
|
||||
appTypes?: string[];
|
||||
|
||||
/**
|
||||
* Set this to `true` to store secure data, such as passwords. Any such
|
||||
* setting will be stored in the system keychain if one is available.
|
||||
*/
|
||||
secure?: boolean;
|
||||
|
||||
/**
|
||||
* An advanced setting will be moved under the "Advanced" button in the
|
||||
* config screen.
|
||||
*/
|
||||
advanced?: boolean;
|
||||
|
||||
/**
|
||||
* Set the min, max and step values if you want to restrict an int setting
|
||||
* to a particular range.
|
||||
*/
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
step?: number;
|
||||
|
@ -12,13 +12,16 @@ joplin.plugins.register({
|
||||
);
|
||||
|
||||
await joplin.commands.register({
|
||||
name: 'editor.printSomething',
|
||||
name: 'printSomething',
|
||||
label: 'Print some random string',
|
||||
execute: async () => {
|
||||
alert('mathMode.printSomething not implemented by Editor yet');
|
||||
await joplin.commands.execute('editor.execCommand', {
|
||||
name: 'printSomething',
|
||||
args: ['Anything']
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await joplin.views.menuItems.create('printSomethingButton', 'editor.printSomething', MenuItemLocation.Tools, { accelerator: 'Ctrl+Alt+Shift+U' });
|
||||
await joplin.views.menuItems.create('printSomethingButton', 'printSomething', MenuItemLocation.Tools, { accelerator: 'Ctrl+Alt+Shift+U' });
|
||||
},
|
||||
});
|
||||
|
@ -6,9 +6,9 @@ function plugin(CodeMirror) {
|
||||
// Once created here it can be called by any other codemirror command
|
||||
// using cm.execCommand(stringName) or register a joplin command called 'editor.printSomething'
|
||||
// through the joplin.commands api
|
||||
CodeMirror.commands.printSomething = function(cm) {
|
||||
console.log("Something");
|
||||
}
|
||||
CodeMirror.defineExtension('printSomething', function(something) {
|
||||
console.log(something);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -4,7 +4,7 @@
|
||||
"name": "joplin-match-highlighter",
|
||||
"description": "Adds support for the Codemirror match highlighter to the Joplin CodeView editor.",
|
||||
"version": "1.0.0",
|
||||
"author": "CalebJohn",
|
||||
"app_min_version": "1.4",
|
||||
"author": "JoplinTeam",
|
||||
"app_min_version": "1.7",
|
||||
"homepage_url": "joplinapp.org"
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHand
|
||||
import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
||||
import { commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
|
||||
import { ScrollOptions, ScrollOptionTypes } from '../../utils/types';
|
||||
import { CommandValue } from '../../utils/types';
|
||||
import { useScrollHandler, usePrevious, cursorPositionToTextOffset, useRootSize } from './utils';
|
||||
import Toolbar from './Toolbar';
|
||||
import styles_ from './styles';
|
||||
@ -218,6 +219,15 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
textCheckbox: () => addListItem('- [ ] ', _('List item')),
|
||||
textHeading: () => addListItem('## ', ''),
|
||||
textHorizontalRule: () => addListItem('* * *'),
|
||||
'editor.execCommand': (value: CommandValue) => {
|
||||
if (editorRef.current[value.name]) {
|
||||
if (!('args' in value)) value.args = [];
|
||||
|
||||
editorRef.current[value.name](...value.args);
|
||||
} else {
|
||||
reg.logger().warn('CodeMirror execCommand: unsupported command: ', value.name);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (commands[cmd.name]) {
|
||||
|
@ -25,6 +25,9 @@ export default function useKeymap(CodeMirror: any) {
|
||||
CodeMirror.Vim.defineAction('insertListElement', CodeMirror.commands.vimInsertListElement);
|
||||
CodeMirror.Vim.mapCommand('o', 'action', 'insertListElement', { after: true }, { context: 'normal', isEdit: true, interlaceInsertRepeat: true });
|
||||
}
|
||||
function isEditorCommand(command: string) {
|
||||
return command.startsWith('editor.');
|
||||
}
|
||||
|
||||
// Converts a command of the form editor.command to just command
|
||||
function editorCommandToCodeMirror(command: String) {
|
||||
@ -91,7 +94,7 @@ export default function useKeymap(CodeMirror: any) {
|
||||
|
||||
|
||||
CodeMirror.defineExtension('supportsCommand', function(cmd: EditorCommand) {
|
||||
return CommandService.isEditorCommand(cmd.name) && editorCommandToCodeMirror(cmd.name) in CodeMirror.commands;
|
||||
return isEditorCommand(cmd.name) && editorCommandToCodeMirror(cmd.name) in CodeMirror.commands;
|
||||
});
|
||||
|
||||
// Used when an editor command is executed using the CommandService.instance().execute
|
||||
|
@ -249,6 +249,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
editor.insertContent(result.html);
|
||||
} else if (cmd.name === 'editor.focus') {
|
||||
editor.focus();
|
||||
} else if (cmd.name === 'editor.execCommand') {
|
||||
if (!('ui' in cmd.value)) cmd.value.ui = false;
|
||||
if (!('value' in cmd.value)) cmd.value.value = null;
|
||||
if (!('args' in cmd.value)) cmd.value.args = {};
|
||||
|
||||
editor.execCommand(cmd.value.name, cmd.value.ui, cmd.value.value, cmd.value.args);
|
||||
} else if (cmd.name === 'dropItems') {
|
||||
if (cmd.value.type === 'notes') {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, cmd.value.markdownTags.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||
|
@ -233,7 +233,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
useWindowCommandHandler({
|
||||
dispatch: props.dispatch,
|
||||
plugins: props.plugins,
|
||||
formNote,
|
||||
setShowLocalSearch,
|
||||
noteSearchBarRef,
|
||||
|
@ -131,6 +131,9 @@ const declarations: CommandDeclaration[] = [
|
||||
{
|
||||
name: 'editor.focus',
|
||||
},
|
||||
{
|
||||
name: 'editor.execCommand',
|
||||
},
|
||||
];
|
||||
|
||||
export default declarations;
|
||||
|
@ -159,3 +159,10 @@ export interface EditorCommand {
|
||||
name: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export interface CommandValue {
|
||||
name: string;
|
||||
args?: any; // Should be an array for CodeMirror or an object for TinyMCE
|
||||
ui?: boolean; // For TinyMCE only
|
||||
value?: any; // For TinyMCE only
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { FormNote, ScrollOptionTypes } from './types';
|
||||
import editorCommandDeclarations from '../commands/editorCommandDeclarations';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import time from '@joplin/lib/time';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
@ -19,7 +20,6 @@ interface HookDependencies {
|
||||
titleInputRef: any;
|
||||
saveNoteAndWait: Function;
|
||||
setFormNote: Function;
|
||||
plugins: any;
|
||||
}
|
||||
|
||||
function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, setFormNote: Function): CommandRuntime {
|
||||
@ -61,10 +61,10 @@ function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, s
|
||||
}
|
||||
|
||||
export default function useWindowCommandHandler(dependencies: HookDependencies) {
|
||||
const { setShowLocalSearch, noteSearchBarRef, editorRef, titleInputRef, setFormNote, plugins } = dependencies;
|
||||
const { setShowLocalSearch, noteSearchBarRef, editorRef, titleInputRef, setFormNote } = dependencies;
|
||||
|
||||
useEffect(() => {
|
||||
for (const declaration of CommandService.instance().editorCommandDeclarations()) {
|
||||
for (const declaration of editorCommandDeclarations) {
|
||||
CommandService.instance().registerRuntime(declaration.name, editorCommandRuntime(declaration, editorRef, setFormNote));
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
|
||||
}
|
||||
|
||||
return () => {
|
||||
for (const declaration of CommandService.instance().editorCommandDeclarations()) {
|
||||
for (const declaration of editorCommandDeclarations) {
|
||||
CommandService.instance().unregisterRuntime(declaration.name);
|
||||
}
|
||||
|
||||
@ -88,5 +88,5 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
|
||||
CommandService.instance().unregisterRuntime(command.declaration.name);
|
||||
}
|
||||
};
|
||||
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef, plugins]);
|
||||
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef]);
|
||||
}
|
||||
|
@ -309,40 +309,4 @@ export default class CommandService extends BaseService {
|
||||
const command = this.commandByName(commandName, { mustExist: false });
|
||||
return !!command;
|
||||
}
|
||||
|
||||
public static isEditorCommand(commandName: string) {
|
||||
return (commandName.indexOf('editor.') === 0 ||
|
||||
// These commands are grandfathered in, but in the future
|
||||
// all editor commands should start with "editor."
|
||||
commandName === 'insertText' ||
|
||||
commandName === 'scrollToHash' ||
|
||||
commandName === 'textCopy' ||
|
||||
commandName === 'textCut' ||
|
||||
commandName === 'textPaste' ||
|
||||
commandName === 'textSelectAll' ||
|
||||
commandName === 'textBold' ||
|
||||
commandName === 'textItalic' ||
|
||||
commandName === 'textLink' ||
|
||||
commandName === 'textCode' ||
|
||||
commandName === 'attachFile' ||
|
||||
commandName === 'textNumberedList' ||
|
||||
commandName === 'textBulletedList' ||
|
||||
commandName === 'textCheckbox' ||
|
||||
commandName === 'textHeading' ||
|
||||
commandName === 'textHorizontalRule' ||
|
||||
commandName === 'insertDateTime' ||
|
||||
commandName === 'selectedText' ||
|
||||
commandName === 'replaceSelection'
|
||||
);
|
||||
}
|
||||
|
||||
public editorCommandDeclarations(): CommandDeclaration[] {
|
||||
const output = [];
|
||||
|
||||
for (const name in this.commands_) {
|
||||
if (CommandService.isEditorCommand(name)) { output.push(this.commands_[name].declaration); }
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
@ -33,16 +33,27 @@ export default class ToolbarButtonUtils {
|
||||
return this.service_;
|
||||
}
|
||||
|
||||
// Editor commands will focus the editor after they're executed
|
||||
private isEditorCommand(commandName: string) {
|
||||
return CommandService.isEditorCommand(commandName) && !(
|
||||
// These commands are attached to the editor runtime,
|
||||
// but they either handle focus themselves or don't need
|
||||
// to focus the editor
|
||||
commandName === 'textLink' ||
|
||||
commandName === 'insertText' ||
|
||||
commandName === 'scrollToHash' ||
|
||||
commandName === 'selectedText' ||
|
||||
commandName === 'replaceSelection'
|
||||
return (commandName.indexOf('editor.') === 0 ||
|
||||
// These commands are grandfathered in, but in the future
|
||||
// all editor commands should start with "editor."
|
||||
// WARNING: Some commands such as textLink are not defined here
|
||||
// because they are more complex and handle focus manually
|
||||
commandName === 'textCopy' ||
|
||||
commandName === 'textCut' ||
|
||||
commandName === 'textPaste' ||
|
||||
commandName === 'textSelectAll' ||
|
||||
commandName === 'textBold' ||
|
||||
commandName === 'textItalic' ||
|
||||
commandName === 'textCode' ||
|
||||
commandName === 'attachFile' ||
|
||||
commandName === 'textNumberedList' ||
|
||||
commandName === 'textBulletedList' ||
|
||||
commandName === 'textCheckbox' ||
|
||||
commandName === 'textHeading' ||
|
||||
commandName === 'textHorizontalRule' ||
|
||||
commandName === 'insertDateTime'
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user