mirror of
https://github.com/zadam/trilium.git
synced 2025-11-08 06:15:48 +01:00
use electron 8 spellchecker instead of electron-spellchecker, WIP
This commit is contained in:
@@ -66,6 +66,7 @@ import RenderTypeWidget from "./widgets/type_widgets/render.js";
|
||||
import RelationMapTypeWidget from "./widgets/type_widgets/relation_map.js";
|
||||
import ProtectedSessionTypeWidget from "./widgets/type_widgets/protected_session.js";
|
||||
import BookTypeWidget from "./widgets/type_widgets/book.js";
|
||||
import contextMenuService from "./services/context_menu.js";
|
||||
|
||||
if (utils.isElectron()) {
|
||||
require('electron').ipcRenderer.on('globalShortcut', async function(event, actionName) {
|
||||
@@ -83,4 +84,44 @@ appContext.start();
|
||||
|
||||
noteTooltipService.setupGlobalTooltip();
|
||||
|
||||
noteAutocompleteService.init();
|
||||
noteAutocompleteService.init();
|
||||
|
||||
if (utils.isElectron()) {
|
||||
const {webContents} = require('electron').remote.getCurrentWindow();
|
||||
|
||||
webContents.on('context-menu', (event, params) => {
|
||||
const items = [
|
||||
{title: "Hello", cmd: "openNoteInNewTab", uiIcon: "arrow-up-right"}
|
||||
];
|
||||
|
||||
if (params.misspelledWord) {
|
||||
items.push({
|
||||
title: `Misspelled "<strong>${params.misspelledWord}</strong>"`,
|
||||
cmd: "openNoteInNewTab",
|
||||
uiIcon: ""
|
||||
});
|
||||
|
||||
for (const suggestion of params.dictionarySuggestions) {
|
||||
items.push({
|
||||
title: suggestion,
|
||||
command: "replaceMisspelling",
|
||||
spellingSuggestion: suggestion,
|
||||
uiIcon: ""
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
contextMenuService.initContextMenu({
|
||||
x: params.x,
|
||||
y: params.y,
|
||||
items,
|
||||
selectContextMenuItem: (e, {command, spellingSuggestion}) => {
|
||||
if (command === 'replaceMisspelling') {
|
||||
console.log("Replacing missspeling", spellingSuggestion);
|
||||
|
||||
require('electron').remote.getCurrentWindow().webContents.insertText(spellingSuggestion);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -16,11 +16,11 @@ const TPL = `
|
||||
<br/>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="spell-check-language-code">Language code</label>
|
||||
<label for="spell-check-language-code">Language code(s)</label>
|
||||
<input type="text" class="form-control" id="spell-check-language-code" placeholder="for example "en-US", "de-AT"">
|
||||
</div>
|
||||
|
||||
<p>Changes to the spell check options will take effect after application restart.</p>
|
||||
<p>Multiple languages can be separated by comman. Changes to the spell check options will take effect after application restart.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -21,9 +21,7 @@ function initAttributeNameAutocomplete({ $el, attributeType, open }) {
|
||||
const type = typeof attributeType === "function" ? attributeType() : attributeType;
|
||||
|
||||
const names = await server.get(`attributes/names/?type=${type}&query=${encodeURIComponent(term)}`);
|
||||
const result = names.map(name => {
|
||||
return {name};
|
||||
});
|
||||
const result = names.map(name => ({name}));
|
||||
|
||||
cb(result);
|
||||
}
|
||||
@@ -44,7 +42,7 @@ async function initLabelValueAutocomplete({ $el, open }) {
|
||||
}
|
||||
|
||||
const attributeValues = (await server.get('attributes/values/' + encodeURIComponent(attributeName)))
|
||||
.map(attribute => { return { value: attribute }; });
|
||||
.map(attribute => ({ value: attribute }));
|
||||
|
||||
if (attributeValues.length === 0) {
|
||||
return;
|
||||
|
||||
@@ -3,15 +3,7 @@ const $contextMenuContainer = $("#context-menu-container");
|
||||
|
||||
let dateContextMenuOpenedMs = 0;
|
||||
|
||||
/**
|
||||
* @param event - originating click event (used to get coordinates to display menu at position)
|
||||
* @param {object} contextMenu - needs to have getContextMenuItems() and selectContextMenuItem(e, cmd)
|
||||
*/
|
||||
async function initContextMenu(event, contextMenu) {
|
||||
event.stopPropagation();
|
||||
|
||||
$contextMenuContainer.empty();
|
||||
|
||||
async function initContextMenu(options) {
|
||||
function addItems($parent, items) {
|
||||
for (const item of items) {
|
||||
if (item.title === '----') {
|
||||
@@ -33,15 +25,14 @@ async function initContextMenu(event, contextMenu) {
|
||||
const $item = $("<li>")
|
||||
.addClass("dropdown-item")
|
||||
.append($link)
|
||||
.attr("data-cmd", item.cmd)
|
||||
.on('click', function (e) {
|
||||
const cmd = $(e.target).closest(".dropdown-item").attr("data-cmd");
|
||||
.on('mousedown', function (e) {
|
||||
e.stopPropagation();
|
||||
|
||||
hideContextMenu();
|
||||
|
||||
e.originalTarget = event.target;
|
||||
|
||||
contextMenu.selectContextMenuItem(e, cmd);
|
||||
|
||||
hideContextMenu();
|
||||
options.selectContextMenuItem(e, item);
|
||||
|
||||
// it's important to stop the propagation especially for sub-menus, otherwise the event
|
||||
// might be handled again by top-level menu
|
||||
@@ -68,21 +59,22 @@ async function initContextMenu(event, contextMenu) {
|
||||
}
|
||||
}
|
||||
|
||||
addItems($contextMenuContainer, await contextMenu.getContextMenuItems());
|
||||
$contextMenuContainer.empty();
|
||||
|
||||
addItems($contextMenuContainer, options.items);
|
||||
|
||||
keyboardActionService.updateDisplayedShortcuts($contextMenuContainer);
|
||||
|
||||
// code below tries to detect when dropdown would overflow from page
|
||||
// in such case we'll position it above click coordinates so it will fit into client
|
||||
const clickPosition = event.pageY;
|
||||
const clientHeight = document.documentElement.clientHeight;
|
||||
const contextMenuHeight = $contextMenuContainer.outerHeight() + 30;
|
||||
let top;
|
||||
|
||||
if (clickPosition + contextMenuHeight > clientHeight) {
|
||||
if (options.y + contextMenuHeight > clientHeight) {
|
||||
top = clientHeight - contextMenuHeight - 10;
|
||||
} else {
|
||||
top = event.pageY - 10;
|
||||
top = options.y - 10;
|
||||
}
|
||||
|
||||
dateContextMenuOpenedMs = Date.now();
|
||||
@@ -90,7 +82,7 @@ async function initContextMenu(event, contextMenu) {
|
||||
$contextMenuContainer.css({
|
||||
display: "block",
|
||||
top: top,
|
||||
left: event.pageX - 20
|
||||
left: options.x - 20
|
||||
}).addClass("show");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import options from "./options.js";
|
||||
|
||||
export async function initSpellCheck() {
|
||||
export async function initSpellCheck() {return;
|
||||
const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
|
||||
const {remote, shell} = require('electron');
|
||||
|
||||
|
||||
@@ -120,10 +120,10 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
|
||||
node.setSelected(true);
|
||||
|
||||
const notes = this.getSelectedNodes().map(node => { return {
|
||||
const notes = this.getSelectedNodes().map(node => ({
|
||||
noteId: node.data.noteId,
|
||||
title: node.title
|
||||
}});
|
||||
}));
|
||||
|
||||
data.dataTransfer.setData("text", JSON.stringify(notes));
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
attributeValues = attributeValues.map(attribute => { return { value: attribute }; });
|
||||
attributeValues = attributeValues.map(attribute => ({ value: attribute }));
|
||||
|
||||
$input.autocomplete({
|
||||
appendTo: document.querySelector('body'),
|
||||
|
||||
@@ -114,12 +114,10 @@ export default class TextTypeWidget extends TypeWidget {
|
||||
const codeBlockLanguages =
|
||||
(await mimeTypesService.getMimeTypes())
|
||||
.filter(mt => mt.enabled)
|
||||
.map(mt => {
|
||||
return {
|
||||
.map(mt => ({
|
||||
language: mt.mime.toLowerCase().replace(/[\W_]+/g,"-"),
|
||||
label: mt.title
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
// CKEditor since version 12 needs the element to be visible before initialization. At the same time
|
||||
// we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
|
||||
|
||||
@@ -43,6 +43,7 @@ body {
|
||||
min-height: 0;
|
||||
padding-left: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
|
||||
@@ -133,12 +133,12 @@ async function getRelationMap(req) {
|
||||
|
||||
resp.relations = resp.relations.concat((await note.getRelations())
|
||||
.filter(relation => noteIds.includes(relation.value))
|
||||
.map(relation => { return {
|
||||
.map(relation => ({
|
||||
attributeId: relation.attributeId,
|
||||
sourceNoteId: relation.noteId,
|
||||
targetNoteId: relation.value,
|
||||
name: relation.name
|
||||
}; }));
|
||||
})));
|
||||
|
||||
for (const relationDefinition of await note.getRelationDefinitions()) {
|
||||
if (relationDefinition.value.inverseRelation) {
|
||||
|
||||
@@ -121,15 +121,13 @@ async function exportToTar(taskContext, branch, format, res) {
|
||||
type: note.type,
|
||||
mime: note.mime,
|
||||
// we don't export utcDateCreated and utcDateModified of any entity since that would be a bit misleading
|
||||
attributes: (await note.getOwnedAttributes()).map(attribute => {
|
||||
return {
|
||||
attributes: (await note.getOwnedAttributes()).map(attribute => ({
|
||||
type: attribute.type,
|
||||
name: attribute.name,
|
||||
value: attribute.value,
|
||||
isInheritable: attribute.isInheritable,
|
||||
position: attribute.position
|
||||
};
|
||||
})
|
||||
}))
|
||||
};
|
||||
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
@@ -102,13 +102,11 @@ async function initStartupOptions() {
|
||||
function getKeyboardDefaultOptions() {
|
||||
return keyboardActions.DEFAULT_KEYBOARD_ACTIONS
|
||||
.filter(ka => !!ka.actionName)
|
||||
.map(ka => {
|
||||
return {
|
||||
name: "keyboardShortcuts" + ka.actionName.charAt(0).toUpperCase() + ka.actionName.slice(1),
|
||||
value: JSON.stringify(ka.defaultShortcuts),
|
||||
isSynced: false
|
||||
};
|
||||
});
|
||||
.map(ka => ({
|
||||
name: "keyboardShortcuts" + ka.actionName.charAt(0).toUpperCase() + ka.actionName.slice(1),
|
||||
value: JSON.stringify(ka.defaultShortcuts),
|
||||
isSynced: false
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -23,6 +23,8 @@ async function createMainWindow() {
|
||||
defaultHeight: 800
|
||||
});
|
||||
|
||||
const spellcheckEnabled = await optionService.getOptionBool('spellCheckEnabled');
|
||||
|
||||
const {BrowserWindow} = require('electron'); // should not be statically imported
|
||||
mainWindow = new BrowserWindow({
|
||||
x: mainWindowState.x,
|
||||
@@ -31,7 +33,8 @@ async function createMainWindow() {
|
||||
height: mainWindowState.height,
|
||||
title: 'Trilium Notes',
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
nodeIntegration: true,
|
||||
spellcheck: spellcheckEnabled
|
||||
},
|
||||
frame: await optionService.getOptionBool('nativeTitleBarVisible'),
|
||||
icon: getIcon()
|
||||
@@ -43,15 +46,17 @@ async function createMainWindow() {
|
||||
mainWindow.loadURL('http://127.0.0.1:' + await port);
|
||||
mainWindow.on('closed', () => mainWindow = null);
|
||||
|
||||
mainWindow.webContents.on('new-window', (e, url) => {
|
||||
if (url !== mainWindow.webContents.getURL()) {
|
||||
const {webContents} = mainWindow;
|
||||
|
||||
webContents.on('new-window', (e, url) => {
|
||||
if (url !== webContents.getURL()) {
|
||||
e.preventDefault();
|
||||
require('electron').shell.openExternal(url);
|
||||
}
|
||||
});
|
||||
|
||||
// prevent drag & drop to navigate away from trilium
|
||||
mainWindow.webContents.on('will-navigate', (ev, targetUrl) => {
|
||||
webContents.on('will-navigate', (ev, targetUrl) => {
|
||||
const parsedUrl = url.parse(targetUrl);
|
||||
|
||||
// we still need to allow internal redirects from setup and migration pages
|
||||
@@ -59,6 +64,14 @@ async function createMainWindow() {
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
if (spellcheckEnabled) {
|
||||
const languageCodes = (await optionService.getOption('spellCheckLanguageCode'))
|
||||
.split('/')
|
||||
.map(code => code.trim());
|
||||
|
||||
webContents.session.setSpellCheckerLanguages(languageCodes);
|
||||
}
|
||||
}
|
||||
|
||||
function getIcon() {
|
||||
|
||||
Reference in New Issue
Block a user