diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 61cdc9b48..b1eb89893 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -408,7 +408,7 @@ class Browser(QMainWindow): def add_preview_button(editor: Editor) -> None: editor._links["preview"] = lambda _editor: self.onTogglePreview() editor.web.eval( - "noteEditorPromise.then(noteEditor => noteEditor.toolbar.then(toolbar => toolbar.notetypeButtons.appendButton({ component: editorToolbar.PreviewButton, id: 'preview' })));", + "noteEditorPromise.then(noteEditor => noteEditor.toolbar.notetypeButtons.appendButton({ component: editorToolbar.PreviewButton, id: 'preview' }));", ) gui_hooks.editor_did_init.append(add_preview_button) diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 9e4e21fdd..eb2e179c8 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -137,7 +137,7 @@ class Editor: gui_hooks.editor_did_init_left_buttons(lefttopbtns, self) lefttopbtns_defs = [ - f"noteEditorPromise.then((noteEditor) => noteEditor.toolbar.then((toolbar) => toolbar.notetypeButtons.appendButton({{ component: editorToolbar.Raw, props: {{ html: {json.dumps(button)} }} }}, -1)));" + f"noteEditorPromise.then((noteEditor) => noteEditor.toolbar.notetypeButtons.appendButton({{ component: editorToolbar.Raw, props: {{ html: {json.dumps(button)} }} }}, -1));" for button in lefttopbtns ] lefttopbtns_js = "\n".join(lefttopbtns_defs) @@ -150,7 +150,7 @@ class Editor: righttopbtns_defs = ", ".join([json.dumps(button) for button in righttopbtns]) righttopbtns_js = ( f""" -noteEditorPromise.then(noteEditor => noteEditor.toolbar.then((toolbar) => toolbar.toolbar.appendGroup({{ +noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{ component: editorToolbar.AddonButtons, id: "addons", props: {{ buttons: [ {righttopbtns_defs} ] }}, @@ -1323,11 +1323,11 @@ gui_hooks.editor_will_munge_html.append(reverse_url_quoting) def set_cloze_button(editor: Editor) -> None: if editor.note.note_type()["type"] == MODEL_CLOZE: editor.web.eval( - 'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.then((toolbar) => toolbar.templateButtons.showButton("cloze"))); ' + 'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.showButton("cloze")); ' ) else: editor.web.eval( - 'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.then(({ templateButtons }) => templateButtons.hideButton("cloze"))); ' + 'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.hideButton("cloze")); ' ) diff --git a/ts/components/ButtonGroup.svelte b/ts/components/ButtonGroup.svelte index cd66589a5..9dc7656b9 100644 --- a/ts/components/ButtonGroup.svelte +++ b/ts/components/ButtonGroup.svelte @@ -2,16 +2,27 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> + + diff --git a/ts/components/ButtonToolbar.svelte b/ts/components/ButtonToolbar.svelte index 4310abfc2..cd636e4d4 100644 --- a/ts/components/ButtonToolbar.svelte +++ b/ts/components/ButtonToolbar.svelte @@ -2,14 +2,26 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> + + diff --git a/ts/editor/CodeMirror.svelte b/ts/editor/CodeMirror.svelte index b244dd81c..7303ad4bc 100644 --- a/ts/editor/CodeMirror.svelte +++ b/ts/editor/CodeMirror.svelte @@ -44,14 +44,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // TODO passing in the tabindex option does not do anything: bug? codeMirror.getInputField().tabIndex = 0; + let ranges: CodeMirrorLib.Range[] | null = null; + codeMirror.on("change", () => dispatch("change", codeMirror.getValue())); + codeMirror.on("mousedown", () => { + ranges = null; + }); codeMirror.on("focus", () => { + if (ranges) { + codeMirror.setSelections(ranges); + } unsubscribe(); - dispatch("focus"); }); codeMirror.on("blur", () => { + ranges = codeMirror.listSelections(); subscribe(); - dispatch("blur"); }); subscribe(); diff --git a/ts/editor/EditingArea.svelte b/ts/editor/EditingArea.svelte index 14037b847..f0ce14631 100644 --- a/ts/editor/EditingArea.svelte +++ b/ts/editor/EditingArea.svelte @@ -10,6 +10,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html readonly name: string; focusable: boolean; focus(): void; + moveCaretToEnd(): void; refocus(): void; } @@ -48,7 +49,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let focusTrap: HTMLInputElement; const inputsStore = writable([]); - $: editingInputs = $inputsStore; function getAvailableInput(): EditingInputAPI | undefined { @@ -111,12 +111,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } - export const api = set({ - content, - editingInputs: inputsStore, - focus, - refocus, - }); + export let api: Partial = {}; + + Object.assign( + api, + set({ + content, + editingInputs: inputsStore, + focus, + refocus, + }), + ); onMount(() => { if (autofocus) { diff --git a/ts/editor/EditorField.svelte b/ts/editor/EditorField.svelte index 335f9b231..be5d7800d 100644 --- a/ts/editor/EditorField.svelte +++ b/ts/editor/EditorField.svelte @@ -5,6 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
api.editingArea?.focus()} + on:click={() => editingArea.focus?.()} > {field.name} @@ -79,7 +82,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {autofocus} fontFamily={field.fontFamily} fontSize={field.fontSize} - bind:api={api.editingArea} + api={editingArea} > diff --git a/ts/editor/EditorToolbar.svelte b/ts/editor/EditorToolbar.svelte index 835518c51..06202c9b4 100644 --- a/ts/editor/EditorToolbar.svelte +++ b/ts/editor/EditorToolbar.svelte @@ -6,6 +6,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import "./legacy.css"; import { updateAllState, resetAllState } from "../components/WithState.svelte"; + import type { ButtonGroupAPI } from "../components/ButtonGroup.svelte"; + import type { ButtonToolbarAPI } from "../components/ButtonToolbar.svelte"; + export function updateActiveButtons(event: Event) { updateAllState(event); } @@ -15,12 +18,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } export interface EditorToolbarAPI { - toolbar: any; - notetypeButtons: any; - formatInlineButtons: any; - formatBlockButtons: any; - colorButtons: any; - templateButtons: any; + toolbar: ButtonToolbarAPI; + notetypeButtons: ButtonGroupAPI; + formatInlineButtons: ButtonGroupAPI; + formatBlockButtons: ButtonGroupAPI; + colorButtons: ButtonGroupAPI; + templateButtons: ButtonGroupAPI; } /* Our dynamic components */ @@ -57,14 +60,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const colorButtons = {}; const templateButtons = {}; - export const api = { + export let api: Partial = {}; + + Object.assign(api, { toolbar, notetypeButtons, formatInlineButtons, formatBlockButtons, colorButtons, templateButtons, - }; + } as EditorToolbarAPI); diff --git a/ts/editor/OldEditorAdapter.svelte b/ts/editor/OldEditorAdapter.svelte index b7e1be554..aaac22cd9 100644 --- a/ts/editor/OldEditorAdapter.svelte +++ b/ts/editor/OldEditorAdapter.svelte @@ -8,11 +8,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { PlainTextInputAPI } from "./PlainTextInput.svelte"; import type { EditorToolbarAPI } from "./EditorToolbar.svelte"; + import { registerShortcut } from "../lib/shortcuts"; import contextProperty from "../sveltelib/context-property"; + import { writable, get } from "svelte/store"; export interface NoteEditorAPI { fields: EditorFieldAPI[]; - currentField: Writable; + currentField: Writable; activeInput: Writable; focusInRichText: Writable; toolbar: EditorToolbarAPI; @@ -22,6 +24,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const [set, getNoteEditor, hasNoteEditor] = contextProperty(key); export { getNoteEditor, hasNoteEditor }; + + const activeInput = writable(null); + const currentField = writable(null); + + function updateFocus() { + get(activeInput)?.moveCaretToEnd(); + } + + registerShortcut( + () => document.addEventListener("focusin", updateFocus, { once: true }), + "Shift?+Tab", + ); diff --git a/ts/editor/PlainTextInput.svelte b/ts/editor/PlainTextInput.svelte index ddba2c149..58ace8a64 100644 --- a/ts/editor/PlainTextInput.svelte +++ b/ts/editor/PlainTextInput.svelte @@ -148,7 +148,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {configuration} {code} bind:api={codeMirror} - on:focus={moveCaretToEnd} on:change={({ detail: html }) => code.set(parseAsHTML(html))} />
diff --git a/ts/editor/StickyBadge.svelte b/ts/editor/StickyBadge.svelte index 195695b8b..90858b1a4 100644 --- a/ts/editor/StickyBadge.svelte +++ b/ts/editor/StickyBadge.svelte @@ -20,16 +20,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let index: number; - function toggleSticky() { + function toggle() { bridgeCommand(`toggleSticky:${index}`, (value: boolean) => { active = value; }); } - onMount(() => registerShortcut(toggleSticky, keyCombination, editorField.element)); + function shortcut(element: HTMLElement): void { + registerShortcut(toggle, keyCombination, element); + } + + onMount(() => editorField.element.then(shortcut)); - + {@html icon}(): (T & Destroyable)[] { + const list: (T & Destroyable)[] = []; + + return new Proxy(list, { + get: function (target: (T & Destroyable)[], prop: string | symbol) { + if (!isNaN(Number(prop)) && !target[prop]) { + const item = {} as T & Destroyable; + + const destroy = (): void => { + const index = list.indexOf(item); + list.splice(index, 1); + }; + + target[prop] = new Proxy(item, { + get: function (target: T & Destroyable, prop: string | symbol) { + if (prop === "destroy") { + return destroy; + } + + return target[prop]; + }, + }); + } + + return target[prop]; + }, + }); +} diff --git a/ts/editor/index.ts b/ts/editor/index.ts index d53cc4d85..00510404c 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -75,8 +75,11 @@ async function setupNoteEditor(): Promise { await i18n; + const api: Partial = {}; + const noteEditor = new OldEditorAdapter({ target: document.body, + props: { api: api as NoteEditorAPI }, context, }); @@ -95,7 +98,7 @@ async function setupNoteEditor(): Promise { setNoteId: noteEditor.setNoteId, }); - return noteEditor.api; + return api as NoteEditorAPI; } export const noteEditorPromise = setupNoteEditor(); diff --git a/ts/editor/legacy.scss b/ts/editor/legacy.scss index a8c7bac9d..a7577cf88 100644 --- a/ts/editor/legacy.scss +++ b/ts/editor/legacy.scss @@ -6,6 +6,7 @@ background-color: white; border: 1px solid var(--medium-border); padding: 0 !important; + margin-left: -1px; /* !important cannot be used with @include */ border-top-left-radius: var(--border-left-radius) !important; @@ -13,8 +14,6 @@ border-top-right-radius: var(--border-right-radius) !important; border-bottom-right-radius: var(--border-right-radius) !important; - margin-left: -1px; - &:hover { background-color: #ebebeb; } @@ -27,10 +26,12 @@ } .nightMode & { + background: linear-gradient(0deg, #555555 0%, #656565 100%); + border-width: 0px; margin-left: 1px; &:hover { - background-color: #7a7a7a; + background: #7a7a7a; border-color: #7a7a7a; } @@ -45,7 +46,7 @@ .topbut { display: inline-block; - vertical-align: middle; + vertical-align: baseline; width: calc(var(--buttons-size) - 12px); height: calc(var(--buttons-size) - 12px); }