From a981e560088db76d9f07a8f5436786c0dca61d47 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 3 Feb 2022 05:52:11 +0100 Subject: [PATCH] Improved add-on extension API (#1626) * Add componentHook functionality * Register package NoteEditor * Rename OldEditorAdapter to NoteEditor * Expose instances in component-hook as well * Rename NoteTypeButtons to NotetypeButtons * Move PreviewButton initialization to BrowserEditor.svelte * Remove focusInRichText - Same thing can be done by inspecting activeInput * Satisfy formatter * Fix remaining rebase issues * Add .bazel to .prettierignore * Rename currentField and activeInput to focused{Field,Input} * Move identifier to lib and registration to sveltelib * Fix Dynamic component insertion * Simplify editingInputIsRichText * Give extra warning in svelte/svelte.ts - This was caused by doing a rename of a files, that only differed in case: NoteTypeButtons.svelte to NotetypeButtons.svelte - It was quite tough to figure out, and this console.log might make it easier if it ever happens again * Change signature of contextProperty * Add ts/typings for add-on definition files * Add Anki types in typings/common/index.d.ts * Export without .svelte suffix It conflicts with how Svelte types its packages * Fix left over .svelte import from editor.py * Rename NoteTypeButtons to unrelated to ensure case-only rename * Rename back to NotetypeButtons.svelte * Remove unused component-hook.ts, Fix typing in lifecycle-hooks * Merge runtime-require and register-package into one file + Give some preliminary types to require * Rename uiDidLoad to loaded * Fix eslint / svelte-check * Rename context imports to noteEditorContext * Fix import name mismatch - I wonder why these issues are not caught by svelte-check? * Rename two missed usages of uiDidLoad * Fix ButtonDropdown from having wrong border-radius * Uniformly rename libraries to packages - I don't have a strong opinion on whether to name them libraries or packages, I just think we should have a uniform name. - JS/TS only uses the terms "module" and "namespace", however `package` is a reserved keyword for future use, whereas `library` is not. * Refactor registration.ts into dynamic-slotting - This is part of an effort to refactor the dynamic slotting (extending buttons) functionality out of components like ButtonGroup. * Remove dynamically-slottable logic from ButtonToolbar * Use DynamicallySlottable in editor-toolbar * Fix no border radius on indentation button dropdown * Fix AddonButtons * Remove Item/ButtonGroupItem in deck-options, where it's not necessary * Remove unnecessary uses of Item and ButtonGroupItem * Fix remaining tests * Fix relative imports * Revert change return value of remapBinToSrcDir to ./bazel/out... * Remove typings directory * Adjust comments for dynamic-slottings --- .prettierignore | 3 +- qt/aqt/browser/browser.py | 7 +- qt/aqt/editor.py | 28 +- ts/change-notetype/NotetypeSelector.svelte | 57 +-- ts/change-notetype/SaveButton.svelte | 17 +- ts/components/BUILD.bazel | 1 + ts/components/ButtonDropdown.svelte | 9 +- ts/components/ButtonGroup.svelte | 101 +---- ts/components/ButtonGroupItem.svelte | 114 ++++-- ts/components/ButtonToolbar.svelte | 77 +--- ts/components/Container.svelte | 6 +- ts/components/Detachable.svelte | 11 - ts/components/DynamicallySlottable.svelte | 64 ++++ ts/components/Item.svelte | 29 +- ts/components/Row.svelte | 11 +- ts/components/Section.svelte | 69 ---- ts/components/StickyContainer.svelte | 3 +- ts/components/WithDropdown.svelte | 7 +- ts/components/buttons.ts | 15 - ts/components/identifier.ts | 87 ----- ts/components/registration.ts | 126 ------ ts/deck-options/Addons.svelte | 3 +- ts/deck-options/AdvancedOptions.svelte | 132 ++++--- ts/deck-options/AudioOptions.svelte | 36 +- ts/deck-options/BUILD.bazel | 9 +- ts/deck-options/BuryOptions.svelte | 38 +- ts/deck-options/ConfigSelector.svelte | 46 +-- ts/deck-options/DailyLimits.svelte | 49 ++- ts/deck-options/DeckOptionsPage.svelte | 82 ++-- ts/deck-options/DisplayOrder.svelte | 108 +++--- ts/deck-options/LapseOptions.svelte | 83 ++-- ts/deck-options/NewOptions.svelte | 87 +++-- ts/deck-options/SaveButton.svelte | 68 ++-- ts/deck-options/TimerOptions.svelte | 45 ++- ts/deck-options/TitledContainer.svelte | 3 +- ts/domlib/location/index.ts | 2 +- ts/domlib/surround/index.ts | 2 +- ts/editor/BrowserEditor.svelte | 16 +- ts/editor/DecoratedElements.svelte | 6 +- ts/editor/EditingArea.svelte | 24 +- ts/editor/EditorField.svelte | 15 +- ts/editor/MathjaxElement.svelte | 4 +- ts/editor/NoteCreator.svelte | 10 +- ts/editor/NoteEditor.svelte | 361 +++++++++++++++++- ts/editor/OldEditorAdapter.svelte | 361 ------------------ ts/editor/PlainTextBadge.svelte | 4 +- .../{editor-toolbar => }/PreviewButton.svelte | 17 +- ts/editor/ReviewerEditor.svelte | 8 +- ts/editor/StickyBadge.svelte | 4 +- ts/editor/base.ts | 2 +- ts/editor/editor-toolbar/AddonButtons.svelte | 26 +- ts/editor/editor-toolbar/BoldButton.svelte | 11 +- ts/editor/editor-toolbar/ClozeButton.svelte | 12 +- ts/editor/editor-toolbar/ColorButtons.svelte | 162 ++++---- .../editor-toolbar/CommandIconButton.svelte | 7 +- ts/editor/editor-toolbar/EditorToolbar.svelte | 57 +-- .../editor-toolbar/FormatBlockButtons.svelte | 110 +++--- .../editor-toolbar/FormatInlineButtons.svelte | 80 ++-- ts/editor/editor-toolbar/ItalicButton.svelte | 13 +- ts/editor/editor-toolbar/LatexButton.svelte | 9 +- .../editor-toolbar/NoteTypeButtons.svelte | 41 -- .../editor-toolbar/NotetypeButtons.svelte | 56 +++ .../editor-toolbar/TemplateButtons.svelte | 101 +++-- .../editor-toolbar/UnderlineButton.svelte | 15 +- ts/editor/image-overlay/FloatButtons.svelte | 69 ++-- ts/editor/image-overlay/ImageHandle.svelte | 19 +- ts/editor/image-overlay/SizeSelect.svelte | 17 +- ts/editor/index_browser.ts | 11 +- ts/editor/index_creator.ts | 11 +- ts/editor/index_reviewer.ts | 11 +- .../mathjax-overlay/MathjaxButtons.svelte | 68 ++-- .../mathjax-overlay/MathjaxHandle.svelte | 4 +- .../plain-text-input/PlainTextInput.svelte | 8 +- .../rich-text-input/RichTextInput.svelte | 26 +- ts/editor/rich-text-input/index.ts | 6 +- ts/lib/bridgecommand.ts | 2 +- ts/lib/children-access.ts | 109 ++++++ ts/lib/helpers.ts | 12 + ts/lib/register-package.ts | 57 --- ts/lib/runtime-require.ts | 100 ++++- ts/lib/shortcuts.ts | 2 +- ts/lib/ui.ts | 13 + ts/svelte/svelte.ts | 40 +- ts/sveltelib/context-property.ts | 51 ++- ts/sveltelib/dynamic-slotting.ts | 299 +++++++++++++++ ts/sveltelib/export-runtime.ts | 4 +- ts/sveltelib/lifecycle-hooks.ts | 76 ++++ ts/sveltelib/theme.ts | 2 +- ts/sveltelib/types.ts | 0 89 files changed, 2227 insertions(+), 1957 deletions(-) delete mode 100644 ts/components/Detachable.svelte create mode 100644 ts/components/DynamicallySlottable.svelte delete mode 100644 ts/components/Section.svelte delete mode 100644 ts/components/buttons.ts delete mode 100644 ts/components/identifier.ts delete mode 100644 ts/components/registration.ts delete mode 100644 ts/editor/OldEditorAdapter.svelte rename ts/editor/{editor-toolbar => }/PreviewButton.svelte (60%) delete mode 100644 ts/editor/editor-toolbar/NoteTypeButtons.svelte create mode 100644 ts/editor/editor-toolbar/NotetypeButtons.svelte create mode 100644 ts/lib/children-access.ts create mode 100644 ts/lib/helpers.ts delete mode 100644 ts/lib/register-package.ts create mode 100644 ts/lib/ui.ts create mode 100644 ts/sveltelib/dynamic-slotting.ts create mode 100644 ts/sveltelib/lifecycle-hooks.ts create mode 100644 ts/sveltelib/types.ts diff --git a/.prettierignore b/.prettierignore index efcaaca13..0815202a2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,5 +2,6 @@ licenses.json vendor node_modules bazel-* +.bazel ftl/usage -.mypy_cache \ No newline at end of file +.mypy_cache diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 904a8b922..d3cfd0fc2 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -413,9 +413,6 @@ 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.notetypeButtons.appendButton({ component: editorToolbar.PreviewButton, id: 'preview' }));", - ) gui_hooks.editor_did_init.append(add_preview_button) self.editor = aqt.editor.Editor( @@ -633,9 +630,7 @@ class Browser(QMainWindow): def toggle_preview_button_state(self, active: bool) -> None: if self.editor.web: - self.editor.web.eval( - f"editorToolbar.togglePreviewButtonState({json.dumps(active)});" - ) + self.editor.web.eval(f"togglePreviewButtonState({json.dumps(active)});") def _cleanup_preview(self) -> None: if self._previewer: diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 9860049c4..41e9e38c9 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -174,7 +174,7 @@ class Editor: righttopbtns_defs = ", ".join([json.dumps(button) for button in righttopbtns]) righttopbtns_js = ( f""" -uiPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{ +require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].toolbar.toolbar.append({{ component: editorToolbar.AddonButtons, id: "addons", props: {{ buttons: [ {righttopbtns_defs} ] }}, @@ -525,7 +525,9 @@ uiPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{ js += " setSticky(%s);" % json.dumps(sticky) js = gui_hooks.editor_will_load_note(js, self.note, self) - self.web.evalWithCallback(f"uiPromise.then(() => {{ {js} }})", oncallback) + self.web.evalWithCallback( + f'require("anki/ui").loaded.then(() => {{ {js} }})', oncallback + ) def _save_current_note(self) -> None: "Call after note is updated with data from webview." @@ -579,8 +581,12 @@ uiPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{ elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE: cloze_hint = tr.adding_cloze_outside_cloze_field() - self.web.eval(f"uiPromise.then(() => setBackgrounds({json.dumps(cols)}));") - self.web.eval(f"uiPromise.then(() => setClozeHint({json.dumps(cloze_hint)}));") + self.web.eval( + 'require("anki/ui").loaded.then(() => {' + f"setBackgrounds({json.dumps(cols)});\n" + f"setClozeHint({json.dumps(cloze_hint)});\n" + "}); " + ) def showDupes(self) -> None: aqt.dialogs.open( @@ -1353,14 +1359,12 @@ 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( - 'uiPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.showButton("cloze")); ' - ) - else: - editor.web.eval( - 'uiPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.hideButton("cloze")); ' - ) + action = "show" if editor.note.note_type()["type"] == MODEL_CLOZE else "hide" + editor.web.eval( + 'require("anki/ui").loaded.then(() =>' + f'require("anki/NoteEditor").instances[0].toolbar.templateButtons.{action}("cloze")' + "); " + ) gui_hooks.editor_did_load_note.append(set_cloze_button) diff --git a/ts/change-notetype/NotetypeSelector.svelte b/ts/change-notetype/NotetypeSelector.svelte index 7b2bc5301..0d769c639 100644 --- a/ts/change-notetype/NotetypeSelector.svelte +++ b/ts/change-notetype/NotetypeSelector.svelte @@ -6,9 +6,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { ChangeNotetypeState } from "./lib"; import StickyContainer from "../components/StickyContainer.svelte"; import ButtonToolbar from "../components/ButtonToolbar.svelte"; - import Item from "../components/Item.svelte"; import ButtonGroup from "../components/ButtonGroup.svelte"; - import ButtonGroupItem from "../components/ButtonGroupItem.svelte"; import LabelButton from "../components/LabelButton.svelte"; import Badge from "../components/Badge.svelte"; import { arrowRightIcon, arrowLeftIcon } from "./icons"; @@ -33,41 +31,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --sticky-borders="0 0 1px" > - - - - {$info.oldNotetypeName} - - - - - - {#if window.getComputedStyle(document.body).direction == "rtl"} - {@html arrowLeftIcon} - {:else} - {@html arrowRightIcon} - {/if} - - - - - - - {#each $notetypes as entry} - - {entry.name} - - {/each} - - - - + + {$info.oldNotetypeName} + + + {#if window.getComputedStyle(document.body).direction == "rtl"} + {@html arrowLeftIcon} + {:else} + {@html arrowRightIcon} + {/if} + + + + {#each $notetypes as entry} + + {entry.name} + + {/each} + + - - - + diff --git a/ts/change-notetype/SaveButton.svelte b/ts/change-notetype/SaveButton.svelte index e5f79007b..d93c94ddf 100644 --- a/ts/change-notetype/SaveButton.svelte +++ b/ts/change-notetype/SaveButton.svelte @@ -8,7 +8,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { getPlatformString } from "../lib/shortcuts"; import ButtonGroup from "../components/ButtonGroup.svelte"; - import ButtonGroupItem from "../components/ButtonGroupItem.svelte"; import LabelButton from "../components/LabelButton.svelte"; import Shortcut from "../components/Shortcut.svelte"; @@ -25,12 +24,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - {tr.actionsSave()} - - + {tr.actionsSave()} + diff --git a/ts/components/BUILD.bazel b/ts/components/BUILD.bazel index a86e250e1..f75360f0a 100644 --- a/ts/components/BUILD.bazel +++ b/ts/components/BUILD.bazel @@ -42,6 +42,7 @@ svelte_check( "//sass:breakpoints_lib", "//sass/bootstrap", "@npm//@types/bootstrap", + "//ts/lib:lib_pkg", "//ts/sveltelib:sveltelib_pkg", ], ) diff --git a/ts/components/ButtonDropdown.svelte b/ts/components/ButtonDropdown.svelte index f5de681d7..a6657b47a 100644 --- a/ts/components/ButtonDropdown.svelte +++ b/ts/components/ButtonDropdown.svelte @@ -12,17 +12,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let className = ""; export { className as class }; - export let api: Record | undefined = undefined; - setContext(dropdownKey, null); - +
diff --git a/ts/components/ButtonGroup.svelte b/ts/components/ButtonGroup.svelte index bc538766e..28254d366 100644 --- a/ts/components/ButtonGroup.svelte +++ b/ts/components/ButtonGroup.svelte @@ -2,35 +2,12 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - -
+
- {#each $dynamicItems as item (item[0].id)} - - - - {/each}
diff --git a/ts/components/Item.svelte b/ts/components/Item.svelte index 352542b33..81a4c955b 100644 --- a/ts/components/Item.svelte +++ b/ts/components/Item.svelte @@ -3,35 +3,24 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -->
- + {#if !$detach} - + {/if}
diff --git a/ts/components/StickyContainer.svelte b/ts/components/StickyContainer.svelte index 27abc83fb..f64507d41 100644 --- a/ts/components/StickyContainer.svelte +++ b/ts/components/StickyContainer.svelte @@ -12,11 +12,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let height: number = 0; export let breakpoint: Breakpoint | "fluid" = "fluid"; - export let api: Record | undefined = undefined;
- +
diff --git a/ts/components/WithDropdown.svelte b/ts/components/WithDropdown.svelte index 2b69c9796..41dc1cbf3 100644 --- a/ts/components/WithDropdown.svelte +++ b/ts/components/WithDropdown.svelte @@ -85,9 +85,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html api = { show: dropdown.show.bind(dropdown), - // TODO this is quite confusing, but commenting this fixes Bootstrap + // TODO this is quite confusing, but having a noop function fixes Bootstrap // in the deck-options when not including Bootstrap via -
+
diff --git a/ts/components/buttons.ts b/ts/components/buttons.ts deleted file mode 100644 index 9c57fc08f..000000000 --- a/ts/components/buttons.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import type { Writable } from "svelte/store"; -import type { Registration } from "./registration"; - -export enum ButtonPosition { - Standalone, - InlineStart, - Center, - InlineEnd, -} - -export interface ButtonRegistration extends Registration { - position: Writable; -} diff --git a/ts/components/identifier.ts b/ts/components/identifier.ts deleted file mode 100644 index 8e4b74cc3..000000000 --- a/ts/components/identifier.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -export type Identifier = string | number; - -export function findElement( - collection: HTMLCollection, - idOrIndex: Identifier, -): [number, Element] | null { - let result: [number, Element] | null = null; - - if (typeof idOrIndex === "string") { - const element = collection.namedItem(idOrIndex); - - if (element) { - const index = Array.prototype.indexOf.call(collection, element); - result = [index, element]; - } - } else if (idOrIndex < 0) { - const index = collection.length + idOrIndex; - const element = collection.item(index); - - if (element) { - result = [index, element]; - } - } else { - const index = idOrIndex; - const element = collection.item(index); - - if (element) { - result = [index, element]; - } - } - - return result; -} - -export function insertElement( - element: Element, - collection: Element, - idOrIndex: Identifier, -): number { - const match = findElement(collection.children, idOrIndex); - - if (match) { - const [index, reference] = match; - collection.insertBefore(element, reference[0]); - - return index; - } - - return -1; -} - -export function appendElement( - element: Element, - collection: Element, - idOrIndex: Identifier, -): number { - const match = findElement(collection.children, idOrIndex); - - if (match) { - const [index, before] = match; - const reference = before.nextElementSibling ?? null; - collection.insertBefore(element, reference); - - return index + 1; - } - - return -1; -} - -export function updateElement( - f: (element: Element) => void, - collection: Element, - idOrIndex: Identifier, -): number { - const match = findElement(collection.children, idOrIndex); - - if (match) { - const [index, element] = match; - f(element[0]); - - return index; - } - - return -1; -} diff --git a/ts/components/registration.ts b/ts/components/registration.ts deleted file mode 100644 index 9999734f1..000000000 --- a/ts/components/registration.ts +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright: Ankitects Pty Ltd and contributors -// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import type { SvelteComponentTyped } from "svelte/internal"; -import type { Writable, Readable } from "svelte/store"; -import { writable } from "svelte/store"; -import type { Identifier } from "./identifier"; -import { findElement } from "./identifier"; - -export interface SvelteComponent { - component: SvelteComponentTyped; - id: string; - props: Record | undefined; -} - -export interface Registration { - detach: Writable; -} - -export type Register = (index?: number, registration?: T) => T; - -export interface RegistrationAPI { - registerComponent: Register; - items: Readable; - dynamicItems: Readable<[SvelteComponent, T][]>; - getDynamicInterface: (elementRef: HTMLElement) => DynamicRegistrationAPI; -} - -export interface DynamicRegistrationAPI { - addComponent: ( - component: SvelteComponent, - add: (added: Element, parent: Element) => number, - ) => void; - updateRegistration: ( - update: (registration: T) => void, - position: Identifier, - ) => void; -} - -export function nodeIsElement(node: Node): node is Element { - return node.nodeType === Node.ELEMENT_NODE; -} - -export function makeInterface( - makeRegistration: () => T, -): RegistrationAPI { - const registrations: T[] = []; - const items = writable(registrations); - - function registerComponent( - index: number = registrations.length, - registration = makeRegistration(), - ): T { - items.update((registrations) => { - registrations.splice(index, 0, registration); - return registrations; - }); - - return registration; - } - - const dynamicRegistrations: [SvelteComponent, T][] = []; - const dynamicItems = writable(dynamicRegistrations); - - function getDynamicInterface(elementRef: HTMLElement): DynamicRegistrationAPI { - function addComponent( - component: SvelteComponent, - add: (added: Element, parent: Element) => number, - ): void { - const registration = makeRegistration(); - - const callback = ( - mutations: MutationRecord[], - observer: MutationObserver, - ): void => { - for (const mutation of mutations) { - for (const addedNode of mutation.addedNodes) { - if ( - nodeIsElement(addedNode) && - (!component.id || addedNode.id === component.id) - ) { - const index = add(addedNode, elementRef); - - if (index >= 0) { - registerComponent(index, registration); - } - - return observer.disconnect(); - } - } - } - }; - - const observer = new MutationObserver(callback); - observer.observe(elementRef, { childList: true }); - - dynamicRegistrations.push([component, registration]); - dynamicItems.set(dynamicRegistrations); - } - - function updateRegistration( - update: (registration: T) => void, - position: Identifier, - ): void { - const match = findElement(elementRef.children, position); - - if (match) { - const [index] = match; - const registration = registrations[index]; - update(registration); - items.set(registrations); - } - } - - return { - addComponent, - updateRegistration, - }; - } - - return { - registerComponent, - items, - dynamicItems, - getDynamicInterface, - }; -} diff --git a/ts/deck-options/Addons.svelte b/ts/deck-options/Addons.svelte index 871748d2f..40eb3decb 100644 --- a/ts/deck-options/Addons.svelte +++ b/ts/deck-options/Addons.svelte @@ -7,14 +7,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { DeckOptionsState } from "./lib"; export let state: DeckOptionsState; - export let api: Record; let components = state.addonComponents; const auxData = state.currentAuxData; {#if $components.length || state.haveAddons} - +

If you're using an add-on that hasn't been updated to use this new screen yet, you can access the old deck options screen by holding down the shift diff --git a/ts/deck-options/AdvancedOptions.svelte b/ts/deck-options/AdvancedOptions.svelte index 3678180c4..133b512cd 100644 --- a/ts/deck-options/AdvancedOptions.svelte +++ b/ts/deck-options/AdvancedOptions.svelte @@ -9,6 +9,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import SpinBoxFloatRow from "./SpinBoxFloatRow.svelte"; import type { DeckOptionsState } from "./lib"; import CardStateCustomizer from "./CardStateCustomizer.svelte"; + import DynamicallySlottable from "../components/DynamicallySlottable.svelte"; + import Item from "../components/Item.svelte"; export let state: DeckOptionsState; export let api: Record; @@ -18,67 +20,83 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let cardStateCustomizer = state.cardStateCustomizer; - - - {tr.schedulingMaximumInterval()} - + + + + + {tr.schedulingMaximumInterval()} + + - - {tr.schedulingStartingEase()} - + + + {tr.schedulingStartingEase()} + + - - {tr.schedulingEasyBonus()} - + + + {tr.schedulingEasyBonus()} + + - - {tr.schedulingIntervalModifier()} - + + + {tr.schedulingIntervalModifier()} + + - - {tr.schedulingHardInterval()} - + + + {tr.schedulingHardInterval()} + + - - {tr.schedulingNewInterval()} - + + + {tr.schedulingNewInterval()} + + - {#if state.v3Scheduler} - - {/if} + {#if state.v3Scheduler} + + + + {/if} + diff --git a/ts/deck-options/AudioOptions.svelte b/ts/deck-options/AudioOptions.svelte index 4ca842576..a25864146 100644 --- a/ts/deck-options/AudioOptions.svelte +++ b/ts/deck-options/AudioOptions.svelte @@ -7,6 +7,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import TitledContainer from "./TitledContainer.svelte"; import SwitchRow from "./SwitchRow.svelte"; import type { DeckOptionsState } from "./lib"; + import DynamicallySlottable from "../components/DynamicallySlottable.svelte"; + import Item from "../components/Item.svelte"; export let state: DeckOptionsState; export let api: Record; @@ -15,19 +17,25 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let defaults = state.defaults; - - - {tr.deckConfigDisableAutoplay()} - + + + + + {tr.deckConfigDisableAutoplay()} + + - - {tr.deckConfigSkipQuestionWhenReplaying()} - + + + {tr.deckConfigSkipQuestionWhenReplaying()} + + + diff --git a/ts/deck-options/BUILD.bazel b/ts/deck-options/BUILD.bazel index 0476c2452..d795b8e1d 100644 --- a/ts/deck-options/BUILD.bazel +++ b/ts/deck-options/BUILD.bazel @@ -17,6 +17,7 @@ compile_sass( "//sass:base_lib", "//sass:breakpoints_lib", "//sass:scrollbar_lib", + "//sass:night_mode_lib", "//sass/bootstrap", ], ) @@ -37,6 +38,10 @@ _ts_deps = [ compile_svelte( deps = _ts_deps + [ + "//sass:base_lib", + "//sass:breakpoints_lib", + "//sass:scrollbar_lib", + "//sass:night_mode_lib", "//sass/bootstrap", ], ) @@ -77,9 +82,11 @@ svelte_check( "*.ts", "*.svelte", ]) + [ + "//sass:base_lib", "//sass:button_mixins_lib", - "//sass:night_mode_lib", + "//sass:scrollbar_lib", "//sass:breakpoints_lib", + "//sass:night_mode_lib", "//sass/bootstrap", "//ts/components", "//ts/sveltelib:sveltelib_pkg", diff --git a/ts/deck-options/BuryOptions.svelte b/ts/deck-options/BuryOptions.svelte index 1b812fea3..3048e186b 100644 --- a/ts/deck-options/BuryOptions.svelte +++ b/ts/deck-options/BuryOptions.svelte @@ -7,6 +7,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import SwitchRow from "./SwitchRow.svelte"; import * as tr from "../lib/ftl"; import type { DeckOptionsState } from "./lib"; + import DynamicallySlottable from "../components/DynamicallySlottable.svelte"; + import Item from "../components/Item.svelte"; export let state: DeckOptionsState; export let api: Record; @@ -15,20 +17,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let defaults = state.defaults; - - - {tr.deckConfigBuryNewSiblings()} - + + + + + {tr.deckConfigBuryNewSiblings()} + + - - {tr.deckConfigBuryReviewSiblings()} - + + + {tr.deckConfigBuryReviewSiblings()} + + + diff --git a/ts/deck-options/ConfigSelector.svelte b/ts/deck-options/ConfigSelector.svelte index e7d3dab26..8871919da 100644 --- a/ts/deck-options/ConfigSelector.svelte +++ b/ts/deck-options/ConfigSelector.svelte @@ -12,9 +12,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import TextInputModal from "./TextInputModal.svelte"; import StickyContainer from "../components/StickyContainer.svelte"; import ButtonToolbar from "../components/ButtonToolbar.svelte"; - import Item from "../components/Item.svelte"; import ButtonGroup from "../components/ButtonGroup.svelte"; - import ButtonGroupItem from "../components/ButtonGroupItem.svelte"; import SelectButton from "../components/SelectButton.svelte"; import SelectOption from "../components/SelectOption.svelte"; @@ -89,30 +87,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - - - - {#each $configList as entry} - - {configLabel(entry)} - - {/each} - - - - + + + {#each $configList as entry} + + {configLabel(entry)} + + {/each} + + - - - + diff --git a/ts/deck-options/DailyLimits.svelte b/ts/deck-options/DailyLimits.svelte index 9ad746e3f..b60ffbe69 100644 --- a/ts/deck-options/DailyLimits.svelte +++ b/ts/deck-options/DailyLimits.svelte @@ -5,6 +5,7 @@ - - - {tr.schedulingNewCardsday()} - + + + + + {tr.schedulingNewCardsday()} + + - - - + + + - - {tr.schedulingMaximumReviewsday()} - + + + {tr.schedulingMaximumReviewsday()} + + - - - + + + + diff --git a/ts/deck-options/DeckOptionsPage.svelte b/ts/deck-options/DeckOptionsPage.svelte index 195a446a8..0306e2361 100644 --- a/ts/deck-options/DeckOptionsPage.svelte +++ b/ts/deck-options/DeckOptionsPage.svelte @@ -15,6 +15,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import TimerOptions from "./TimerOptions.svelte"; import AudioOptions from "./AudioOptions.svelte"; import Addons from "./Addons.svelte"; + import DynamicallySlottable from "../components/DynamicallySlottable.svelte"; + import Item from "../components/Item.svelte"; import type { DeckOptionsState } from "./lib"; import type { Writable } from "svelte/store"; @@ -51,7 +53,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export const displayOrder = {}; export const timerOptions = {}; export const audioOptions = {}; - export const addonOptions = {}; export const advancedOptions = {}; @@ -63,45 +64,64 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --gutter-inline="0.25rem" --gutter-block="0.5rem" class="container-columns" - api={options} > - - - + + + + + + - - - + + + + + - - - + + + + + - {#if state.v3Scheduler} - - - - {/if} + {#if state.v3Scheduler} + + + + + + {/if} - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + +

diff --git a/ts/deck-options/DisplayOrder.svelte b/ts/deck-options/DisplayOrder.svelte index 3461817e2..426f3a7b4 100644 --- a/ts/deck-options/DisplayOrder.svelte +++ b/ts/deck-options/DisplayOrder.svelte @@ -5,8 +5,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - - - {tr.deckConfigNewGatherPriority()} - - + + + + + {tr.deckConfigNewGatherPriority()} + + - - - {tr.deckConfigNewCardSortOrder()} - - + + + {tr.deckConfigNewCardSortOrder()} + + - - - {tr.deckConfigNewReviewPriority()} - - + + + {tr.deckConfigNewReviewPriority()} + + - - - {tr.deckConfigInterdayStepPriority()} - - + + + {tr.deckConfigInterdayStepPriority()} + + - - - {tr.deckConfigReviewSortOrder()} - - + + + {tr.deckConfigReviewSortOrder()} + + + diff --git a/ts/deck-options/LapseOptions.svelte b/ts/deck-options/LapseOptions.svelte index 423876958..bca8ffb86 100644 --- a/ts/deck-options/LapseOptions.svelte +++ b/ts/deck-options/LapseOptions.svelte @@ -5,6 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - - {tr.deckConfigRelearningSteps()} - + + + + + {tr.deckConfigRelearningSteps()} + + - - {tr.schedulingMinimumInterval()} - + + + {tr.schedulingMinimumInterval()} + + - - - + + + - - {tr.schedulingLeechThreshold()} - + + + {tr.schedulingLeechThreshold()} + + - - {tr.schedulingLeechAction()} - + + + {tr.schedulingLeechAction()} + + + diff --git a/ts/deck-options/NewOptions.svelte b/ts/deck-options/NewOptions.svelte index 35883e788..450efff5d 100644 --- a/ts/deck-options/NewOptions.svelte +++ b/ts/deck-options/NewOptions.svelte @@ -7,8 +7,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import StepsInputRow from "./StepsInputRow.svelte"; import SpinBoxRow from "./SpinBoxRow.svelte"; import EnumSelectorRow from "./EnumSelectorRow.svelte"; - import Item from "../components/Item.svelte"; import Warning from "./Warning.svelte"; + import DynamicallySlottable from "../components/DynamicallySlottable.svelte"; + import Item from "../components/Item.svelte"; import type { DeckOptionsState } from "./lib"; import * as tr from "../lib/ftl"; @@ -41,46 +42,56 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html : ""; - - - {tr.deckConfigLearningSteps()} - + + + + + {tr.deckConfigLearningSteps()} + + - - {tr.schedulingGraduatingInterval()} - + + + {tr.schedulingGraduatingInterval()} + + - - - + + + - - {tr.schedulingEasyInterval()} - + + + {tr.schedulingEasyInterval()} + + - - - + + + - - {tr.deckConfigNewInsertionOrder()} - + + + {tr.deckConfigNewInsertionOrder()} + + + diff --git a/ts/deck-options/SaveButton.svelte b/ts/deck-options/SaveButton.svelte index 790ab50db..322da8d12 100644 --- a/ts/deck-options/SaveButton.svelte +++ b/ts/deck-options/SaveButton.svelte @@ -11,8 +11,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { withCollapsedWhitespace } from "../lib/i18n"; import ButtonGroup from "../components/ButtonGroup.svelte"; - import ButtonGroupItem from "../components/ButtonGroupItem.svelte"; - import LabelButton from "../components/LabelButton.svelte"; import DropdownMenu from "../components/DropdownMenu.svelte"; import DropdownItem from "../components/DropdownItem.svelte"; @@ -62,40 +60,36 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - save(false)} - tooltip={getPlatformString(saveKeyCombination)} - >{tr.deckConfigSaveButton()} - save(false)} /> - + save(false)} + tooltip={getPlatformString(saveKeyCombination)} + --border-left-radius="5px">{tr.deckConfigSaveButton()} + save(false)} /> - - - (dropdown = createDropdown(event.detail.button))} - on:click={() => dropdown.toggle()} - /> - - dispatch("add")} - >{tr.deckConfigAddGroup()} - dispatch("clone")} - >{tr.deckConfigCloneGroup()} - dispatch("rename")}> - {tr.deckConfigRenameGroup()} - - {tr.deckConfigRemoveGroup()} - - save(true)}> - {tr.deckConfigSaveToAllSubdecks()} - - - - + + dropdown.toggle()} + on:mount={(event) => (dropdown = createDropdown(event.detail.button))} + /> + + dispatch("add")} + >{tr.deckConfigAddGroup()} + dispatch("clone")} + >{tr.deckConfigCloneGroup()} + dispatch("rename")}> + {tr.deckConfigRenameGroup()} + + {tr.deckConfigRemoveGroup()} + + save(true)}> + {tr.deckConfigSaveToAllSubdecks()} + + + diff --git a/ts/deck-options/TimerOptions.svelte b/ts/deck-options/TimerOptions.svelte index 8516a0b9e..ddfb3119f 100644 --- a/ts/deck-options/TimerOptions.svelte +++ b/ts/deck-options/TimerOptions.svelte @@ -4,6 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - - {tr.deckConfigMaximumAnswerSecs()} - - + + + + + {tr.deckConfigMaximumAnswerSecs()} + + - - - {tr.schedulingShowAnswerTimer()} - - + + + {tr.schedulingShowAnswerTimer()} + + + diff --git a/ts/deck-options/TitledContainer.svelte b/ts/deck-options/TitledContainer.svelte index 384300cd3..7fb1e4ed0 100644 --- a/ts/deck-options/TitledContainer.svelte +++ b/ts/deck-options/TitledContainer.svelte @@ -6,10 +6,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import Container from "../components/Container.svelte"; export let title: string; - export let api: Record | undefined = undefined; - +

{title}

diff --git a/ts/domlib/location/index.ts b/ts/domlib/location/index.ts index 80b88cf20..1e7f748f8 100644 --- a/ts/domlib/location/index.ts +++ b/ts/domlib/location/index.ts @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import { registerPackage } from "../../lib/register-package"; +import { registerPackage } from "../../lib/runtime-require"; import { saveSelection, restoreSelection } from "./document"; import { Position } from "./location"; diff --git a/ts/domlib/surround/index.ts b/ts/domlib/surround/index.ts index a4434caa2..f56c93381 100644 --- a/ts/domlib/surround/index.ts +++ b/ts/domlib/surround/index.ts @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import { registerPackage } from "../../lib/register-package"; +import { registerPackage } from "../../lib/runtime-require"; import { surroundNoSplitting } from "./no-splitting"; import { unsurround } from "./unsurround"; diff --git a/ts/editor/BrowserEditor.svelte b/ts/editor/BrowserEditor.svelte index fad9e991e..26fd9ae14 100644 --- a/ts/editor/BrowserEditor.svelte +++ b/ts/editor/BrowserEditor.svelte @@ -3,11 +3,13 @@ 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/DecoratedElements.svelte b/ts/editor/DecoratedElements.svelte index fa1b3c476..d42f9e69e 100644 --- a/ts/editor/DecoratedElements.svelte +++ b/ts/editor/DecoratedElements.svelte @@ -12,14 +12,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const decoratedElements = new CustomElementArray(); const key = Symbol("decoratedElements"); - const [set, getDecoratedElements, hasDecoratedElements] = + const [context, setContextProperty] = contextProperty>(key); - export { getDecoratedElements, hasDecoratedElements }; + export { context }; diff --git a/ts/editor/EditingArea.svelte b/ts/editor/EditingArea.svelte index 168fe06ac..5064d22a0 100644 --- a/ts/editor/EditingArea.svelte +++ b/ts/editor/EditingArea.svelte @@ -22,9 +22,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } const key = Symbol("editingArea"); - const [set, getEditingArea, hasEditingArea] = contextProperty(key); + const [context, setContextProperty] = contextProperty(key); - export { getEditingArea, hasEditingArea }; + export { context }; diff --git a/ts/editor/MathjaxElement.svelte b/ts/editor/MathjaxElement.svelte index 774f42222..710db7414 100644 --- a/ts/editor/MathjaxElement.svelte +++ b/ts/editor/MathjaxElement.svelte @@ -3,10 +3,10 @@ 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/NoteEditor.svelte b/ts/editor/NoteEditor.svelte index 229a81e29..70b22142a 100644 --- a/ts/editor/NoteEditor.svelte +++ b/ts/editor/NoteEditor.svelte @@ -2,14 +2,371 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> + + + +
- + + + + + + {#if hint} + + + {@html alertIcon} + {@html hint} + + + {/if} + + + + {#each fieldsData as field, index} + { + $focusedField = fields[index]; + bridgeCommand(`focus:${index}`); + }} + on:focusout={() => { + $focusedField = null; + bridgeCommand( + `blur:${index}:${getNoteId()}:${get( + fieldStores[index], + )}`, + ); + }} + --label-color={cols[index] === "dupe" + ? "var(--flag1-bg)" + : "transparent"} + > + + {#if cols[index] === "dupe"} + + {/if} + + + + + + + + + + + + {/each} + + + + + + + +
diff --git a/ts/editor/editor-toolbar/BoldButton.svelte b/ts/editor/editor-toolbar/BoldButton.svelte index 4b83a70f3..037382196 100644 --- a/ts/editor/editor-toolbar/BoldButton.svelte +++ b/ts/editor/editor-toolbar/BoldButton.svelte @@ -10,8 +10,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { MatchResult } from "../../domlib/surround"; import { getPlatformString } from "../../lib/shortcuts"; import { getSurrounder } from "../surround"; - import { getNoteEditor } from "../OldEditorAdapter.svelte"; + import { context as noteEditorContext } from "../NoteEditor.svelte"; import type { RichTextInputAPI } from "../rich-text-input"; + import { editingInputIsRichText } from "../rich-text-input"; import { boldIcon } from "./icons"; function matchBold(element: Element): Exclude { @@ -42,11 +43,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html return !htmlElement.hasAttribute("style") && element.className.length === 0; } - const { focusInRichText, activeInput } = getNoteEditor(); + const { focusedInput } = noteEditorContext.get(); - $: input = $activeInput; - $: disabled = !$focusInRichText; - $: surrounder = disabled ? null : getSurrounder(input as RichTextInputAPI); + $: input = $focusedInput as RichTextInputAPI; + $: disabled = !editingInputIsRichText($focusedInput); + $: surrounder = disabled ? null : getSurrounder(input); function updateStateFromActiveInput(): Promise { return disabled ? Promise.resolve(false) : surrounder!.isSurrounded(matchBold); diff --git a/ts/editor/editor-toolbar/ClozeButton.svelte b/ts/editor/editor-toolbar/ClozeButton.svelte index 3f79c1191..702cf25c9 100644 --- a/ts/editor/editor-toolbar/ClozeButton.svelte +++ b/ts/editor/editor-toolbar/ClozeButton.svelte @@ -10,18 +10,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { wrapInternal } from "../../lib/wrap"; import { getPlatformString } from "../../lib/shortcuts"; import { get } from "svelte/store"; - import { getNoteEditor } from "../OldEditorAdapter.svelte"; + import { context as noteEditorContext } from "../NoteEditor.svelte"; import type { RichTextInputAPI } from "../rich-text-input"; + import { editingInputIsRichText } from "../rich-text-input"; import { ellipseIcon } from "./icons"; - const noteEditor = getNoteEditor(); - const { focusInRichText, activeInput } = noteEditor; + const { focusedInput, fields } = noteEditorContext.get(); const clozePattern = /\{\{c(\d+)::/gu; function getCurrentHighestCloze(increment: boolean): number { let highest = 0; - for (const field of noteEditor.fields) { + for (const field of fields) { const content = field.editingArea?.content; const fieldHTML = content ? get(content) : ""; @@ -42,7 +42,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html return Math.max(1, highest); } - $: richTextAPI = $activeInput as RichTextInputAPI; + $: richTextAPI = $focusedInput as RichTextInputAPI; async function onCloze(event: KeyboardEvent | MouseEvent): Promise { const highestCloze = getCurrentHighestCloze(!event.getModifierState("Alt")); @@ -50,7 +50,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html wrapInternal(richText, `{{c${highestCloze}::`, "}}", false); } - $: disabled = !$focusInRichText; + $: disabled = !editingInputIsRichText($focusedInput); const keyCombination = "Control+Alt?+Shift+C"; diff --git a/ts/editor/editor-toolbar/ColorButtons.svelte b/ts/editor/editor-toolbar/ColorButtons.svelte index c061e0250..1c99f55a0 100644 --- a/ts/editor/editor-toolbar/ColorButtons.svelte +++ b/ts/editor/editor-toolbar/ColorButtons.svelte @@ -4,17 +4,23 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - - - {@html textColorIcon} - {@html colorHelperIcon} - - - + + + + + + {@html textColorIcon} + {@html colorHelperIcon} + + + - - - {@html arrowIcon} - { + + + {@html arrowIcon} + { + const textColor = setColor(event); + bridgeCommand(`lastTextColor:${textColor}`); + forecolorWrap = wrapWithForecolor(setColor(event)); + forecolorWrap(); + }} + /> + + { const textColor = setColor(event); bridgeCommand(`lastTextColor:${textColor}`); forecolorWrap = wrapWithForecolor(setColor(event)); forecolorWrap(); }} /> - - { - const textColor = setColor(event); - bridgeCommand(`lastTextColor:${textColor}`); - forecolorWrap = wrapWithForecolor(setColor(event)); - forecolorWrap(); - }} - /> - - + + - - - - {@html highlightColorIcon} - {@html colorHelperIcon} - - + + + + {@html highlightColorIcon} + {@html colorHelperIcon} + + - - - {@html arrowIcon} - { - const highlightColor = setColor(event); - bridgeCommand(`lastHighlightColor:${highlightColor}`); - backcolorWrap = wrapWithBackcolor(highlightColor); - backcolorWrap(); - }} - /> - - - + + + {@html arrowIcon} + { + const highlightColor = setColor(event); + bridgeCommand(`lastHighlightColor:${highlightColor}`); + backcolorWrap = wrapWithBackcolor(highlightColor); + backcolorWrap(); + }} + /> + + + + diff --git a/ts/editor/editor-toolbar/CommandIconButton.svelte b/ts/editor/editor-toolbar/CommandIconButton.svelte index 595fec729..72d08ed25 100644 --- a/ts/editor/editor-toolbar/CommandIconButton.svelte +++ b/ts/editor/editor-toolbar/CommandIconButton.svelte @@ -8,7 +8,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import WithState from "../../components/WithState.svelte"; import { execCommand, queryCommandState } from "../helpers"; - import { getNoteEditor } from "../OldEditorAdapter.svelte"; + import { context as noteEditorContext } from "../NoteEditor.svelte"; + import { editingInputIsRichText } from "../rich-text-input"; export let key: string; export let tooltip: string; @@ -17,13 +18,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let withoutShortcut = false; export let withoutState = false; - const { focusInRichText } = getNoteEditor(); + const { focusedInput } = noteEditorContext.get(); function action() { execCommand(key); } - $: disabled = !$focusInRichText; + $: disabled = !editingInputIsRichText($focusedInput); {#if withoutState} diff --git a/ts/editor/editor-toolbar/EditorToolbar.svelte b/ts/editor/editor-toolbar/EditorToolbar.svelte index fa3e0b990..11348315a 100644 --- a/ts/editor/editor-toolbar/EditorToolbar.svelte +++ b/ts/editor/editor-toolbar/EditorToolbar.svelte @@ -4,8 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + + diff --git a/ts/editor/editor-toolbar/FormatBlockButtons.svelte b/ts/editor/editor-toolbar/FormatBlockButtons.svelte index 79e74b183..a607de243 100644 --- a/ts/editor/editor-toolbar/FormatBlockButtons.svelte +++ b/ts/editor/editor-toolbar/FormatBlockButtons.svelte @@ -4,13 +4,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - {@html ulIcon} - - - - {@html olIcon} - - - - - createDropdown(event.detail.button)} + + + + {@html ulIcon} - {@html listOptionsIcon} - + - - - - + + {@html olIcon} + + + + + createDropdown(event.detail.button)} + > + {@html listOptionsIcon} + + + + + {@html justifyLeftIcon} - - {@html justifyCenterIcon} - - {@html justifyRightIcon} - - {@html justifyFullIcon} - - - + + - - - + + {@html outdentIcon} @@ -137,15 +144,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html keyCombination={outdentKeyCombination} on:action={outdentListItem} /> - - {@html indentIcon} @@ -154,10 +160,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html keyCombination={indentKeyCombination} on:action={indentListItem} /> - - - - - - + + + + + + diff --git a/ts/editor/editor-toolbar/FormatInlineButtons.svelte b/ts/editor/editor-toolbar/FormatInlineButtons.svelte index 56c41b5a1..6a50a9689 100644 --- a/ts/editor/editor-toolbar/FormatInlineButtons.svelte +++ b/ts/editor/editor-toolbar/FormatInlineButtons.svelte @@ -4,11 +4,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - - + + + + + - - - + + + - - - + + + - - {@html superscriptIcon} - + + {@html superscriptIcon} + - - {@html subscriptIcon} - + + {@html subscriptIcon} + - - {@html eraserIcon} - + + {@html eraserIcon} + + diff --git a/ts/editor/editor-toolbar/ItalicButton.svelte b/ts/editor/editor-toolbar/ItalicButton.svelte index f615c01b0..6a344e0e9 100644 --- a/ts/editor/editor-toolbar/ItalicButton.svelte +++ b/ts/editor/editor-toolbar/ItalicButton.svelte @@ -11,8 +11,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { getPlatformString } from "../../lib/shortcuts"; import { getSurrounder } from "../surround"; import { italicIcon } from "./icons"; - import { getNoteEditor } from "../OldEditorAdapter.svelte"; + import { context as noteEditorContext } from "../NoteEditor.svelte"; import type { RichTextInputAPI } from "../rich-text-input"; + import { editingInputIsRichText } from "../rich-text-input"; function matchItalic(element: Element): Exclude { if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { @@ -41,14 +42,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html return !htmlElement.hasAttribute("style") && element.className.length === 0; } - const { focusInRichText, activeInput } = getNoteEditor(); + const { focusedInput } = noteEditorContext.get(); - $: input = $activeInput; - $: disabled = !$focusInRichText; - $: surrounder = disabled ? null : getSurrounder(input as RichTextInputAPI); + $: input = $focusedInput as RichTextInputAPI; + $: disabled = !editingInputIsRichText($focusedInput); + $: surrounder = disabled ? null : getSurrounder(input); function updateStateFromActiveInput(): Promise { - return !input || input.name === "plain-text" + return disabled ? Promise.resolve(false) : surrounder!.isSurrounded(matchItalic); } diff --git a/ts/editor/editor-toolbar/LatexButton.svelte b/ts/editor/editor-toolbar/LatexButton.svelte index 9c43a474b..ae3d52f99 100644 --- a/ts/editor/editor-toolbar/LatexButton.svelte +++ b/ts/editor/editor-toolbar/LatexButton.svelte @@ -13,11 +13,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { getPlatformString } from "../../lib/shortcuts"; import { wrapInternal } from "../../lib/wrap"; import { functionIcon } from "./icons"; - import { getNoteEditor } from "../OldEditorAdapter.svelte"; + import { context as noteEditorContext } from "../NoteEditor.svelte"; import type { RichTextInputAPI } from "../rich-text-input"; + import { editingInputIsRichText } from "../rich-text-input"; - const { activeInput, focusInRichText } = getNoteEditor(); - $: richTextAPI = $activeInput as RichTextInputAPI; + const { focusedInput } = noteEditorContext.get(); + $: richTextAPI = $focusedInput as RichTextInputAPI; async function surround(front: string, back: string): Promise { const element = await richTextAPI.element; @@ -59,7 +60,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html [onLatexMathEnv, "Control+T, M", tr.editingLatexMathEnv()], ]; - $: disabled = !$focusInRichText; + $: disabled = !editingInputIsRichText($focusedInput); diff --git a/ts/editor/editor-toolbar/NoteTypeButtons.svelte b/ts/editor/editor-toolbar/NoteTypeButtons.svelte deleted file mode 100644 index 4f211ce71..000000000 --- a/ts/editor/editor-toolbar/NoteTypeButtons.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - bridgeCommand("fields")} - > - {tr.editingFields()}... - - - - - bridgeCommand("cards")} - > - {tr.editingCards()}... - - bridgeCommand("cards")} /> - - diff --git a/ts/editor/editor-toolbar/NotetypeButtons.svelte b/ts/editor/editor-toolbar/NotetypeButtons.svelte new file mode 100644 index 000000000..fe87a3268 --- /dev/null +++ b/ts/editor/editor-toolbar/NotetypeButtons.svelte @@ -0,0 +1,56 @@ + + + + + + + bridgeCommand("fields")} + > + {tr.editingFields()}... + + + + + bridgeCommand("cards")} + > + {tr.editingCards()}... + + bridgeCommand("cards")} /> + + + + + diff --git a/ts/editor/editor-toolbar/TemplateButtons.svelte b/ts/editor/editor-toolbar/TemplateButtons.svelte index fafff458b..8707fff35 100644 --- a/ts/editor/editor-toolbar/TemplateButtons.svelte +++ b/ts/editor/editor-toolbar/TemplateButtons.svelte @@ -3,68 +3,87 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - - {@html paperclipIcon} - - - + + + + + {@html paperclipIcon} + + + - - - {@html micIcon} - - - + + + {@html micIcon} + + + - - - + + + - - - + + + + diff --git a/ts/editor/editor-toolbar/UnderlineButton.svelte b/ts/editor/editor-toolbar/UnderlineButton.svelte index 6346205ef..581b3657e 100644 --- a/ts/editor/editor-toolbar/UnderlineButton.svelte +++ b/ts/editor/editor-toolbar/UnderlineButton.svelte @@ -10,9 +10,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { MatchResult } from "../../domlib/surround"; import { getPlatformString } from "../../lib/shortcuts"; import { getSurrounder } from "../surround"; - import { getNoteEditor } from "../OldEditorAdapter.svelte"; - import type { RichTextInputAPI } from "../rich-text-input"; import { underlineIcon } from "./icons"; + import { context } from "../NoteEditor.svelte"; + import type { RichTextInputAPI } from "../rich-text-input"; + import { editingInputIsRichText } from "../rich-text-input"; function matchUnderline(element: Element): Exclude { if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { @@ -26,14 +27,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html return MatchResult.NO_MATCH; } - const { focusInRichText, activeInput } = getNoteEditor(); + const { focusedInput } = context.get(); - $: input = $activeInput; - $: disabled = !$focusInRichText; - $: surrounder = disabled ? null : getSurrounder(input as RichTextInputAPI); + $: input = $focusedInput as RichTextInputAPI; + $: disabled = !editingInputIsRichText($focusedInput); + $: surrounder = disabled ? null : getSurrounder(input); function updateStateFromActiveInput(): Promise { - return !input || input.name === "plain-text" + return disabled ? Promise.resolve(false) : surrounder!.isSurrounded(matchUnderline); } diff --git a/ts/editor/image-overlay/FloatButtons.svelte b/ts/editor/image-overlay/FloatButtons.svelte index bcd306be8..bbd8d11eb 100644 --- a/ts/editor/image-overlay/FloatButtons.svelte +++ b/ts/editor/image-overlay/FloatButtons.svelte @@ -9,7 +9,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { directionKey } from "../../lib/context-keys"; import ButtonGroup from "../../components/ButtonGroup.svelte"; - import ButtonGroupItem from "../../components/ButtonGroupItem.svelte"; import IconButton from "../../components/IconButton.svelte"; import { createEventDispatcher } from "svelte"; @@ -27,44 +26,40 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - { - image.style.float = "left"; - setTimeout(() => dispatch("update")); - }}>{@html inlineStartIcon} - + { + image.style.float = "left"; + setTimeout(() => dispatch("update")); + }} + --border-left-radius="5px">{@html inlineStartIcon} - - { - image.style.removeProperty("float"); + { + image.style.removeProperty("float"); - if (image.getAttribute("style")?.length === 0) { - image.removeAttribute("style"); - } + if (image.getAttribute("style")?.length === 0) { + image.removeAttribute("style"); + } - setTimeout(() => dispatch("update")); - }}>{@html floatNoneIcon} - + setTimeout(() => dispatch("update")); + }}>{@html floatNoneIcon} - - { - image.style.float = "right"; - setTimeout(() => dispatch("update")); - }}>{@html inlineEndIcon} - + { + image.style.float = "right"; + setTimeout(() => dispatch("update")); + }} + --border-right-radius="5px">{@html inlineEndIcon} diff --git a/ts/editor/image-overlay/ImageHandle.svelte b/ts/editor/image-overlay/ImageHandle.svelte index 5ed76bec3..804803e3c 100644 --- a/ts/editor/image-overlay/ImageHandle.svelte +++ b/ts/editor/image-overlay/ImageHandle.svelte @@ -6,19 +6,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { tick, onDestroy } from "svelte"; import WithDropdown from "../../components/WithDropdown.svelte"; import ButtonDropdown from "../../components/ButtonDropdown.svelte"; - import Item from "../../components/Item.svelte"; import HandleBackground from "../HandleBackground.svelte"; import HandleSelection from "../HandleSelection.svelte"; import HandleControl from "../HandleControl.svelte"; import HandleLabel from "../HandleLabel.svelte"; - import { getRichTextInput } from "../rich-text-input"; + import { context } from "../rich-text-input"; import WithImageConstrained from "./WithImageConstrained.svelte"; import FloatButtons from "./FloatButtons.svelte"; import SizeSelect from "./SizeSelect.svelte"; - const { container, styles } = getRichTextInput(); + const { container, styles } = context.get(); const sheetPromise = styles .addStyleTag("imageOverlay") @@ -233,15 +232,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html /> - - - - - - + + {/if} diff --git a/ts/editor/image-overlay/SizeSelect.svelte b/ts/editor/image-overlay/SizeSelect.svelte index 7babcafc2..87d190715 100644 --- a/ts/editor/image-overlay/SizeSelect.svelte +++ b/ts/editor/image-overlay/SizeSelect.svelte @@ -9,7 +9,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { directionKey } from "../../lib/context-keys"; import ButtonGroup from "../../components/ButtonGroup.svelte"; - import ButtonGroupItem from "../../components/ButtonGroupItem.svelte"; import IconButton from "../../components/IconButton.svelte"; import { sizeActual, sizeMinimized } from "./icons"; @@ -22,12 +21,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - {@html icon} - + {@html icon} diff --git a/ts/editor/index_browser.ts b/ts/editor/index_browser.ts index dde5bb5e9..1c24ec17c 100644 --- a/ts/editor/index_browser.ts +++ b/ts/editor/index_browser.ts @@ -2,11 +2,9 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import BrowserEditor from "./BrowserEditor.svelte"; import { editorModules } from "./base"; -import { promiseWithResolver } from "../lib/promise"; import { globalExport } from "../lib/globals"; import { setupI18n } from "../lib/i18n"; - -const [uiPromise, uiResolve] = promiseWithResolver(); +import { uiResolve } from "../lib/ui"; async function setupBrowserEditor(): Promise { await setupI18n({ modules: editorModules }); @@ -20,9 +18,4 @@ async function setupBrowserEditor(): Promise { setupBrowserEditor(); import * as base from "./base"; - -globalExport({ - ...base, - uiPromise, - noteEditorPromise: uiPromise, -}); +globalExport(base); diff --git a/ts/editor/index_creator.ts b/ts/editor/index_creator.ts index 76e481efb..67c2fef7b 100644 --- a/ts/editor/index_creator.ts +++ b/ts/editor/index_creator.ts @@ -2,11 +2,9 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import NoteCreator from "./NoteCreator.svelte"; import { editorModules } from "./base"; -import { promiseWithResolver } from "../lib/promise"; import { globalExport } from "../lib/globals"; import { setupI18n } from "../lib/i18n"; - -const [uiPromise, uiResolve] = promiseWithResolver(); +import { uiResolve } from "../lib/ui"; async function setupNoteCreator(): Promise { await setupI18n({ modules: editorModules }); @@ -20,9 +18,4 @@ async function setupNoteCreator(): Promise { setupNoteCreator(); import * as base from "./base"; - -globalExport({ - ...base, - uiPromise, - noteEditorPromise: uiPromise, -}); +globalExport(base); diff --git a/ts/editor/index_reviewer.ts b/ts/editor/index_reviewer.ts index 7a91807c7..d9eef6697 100644 --- a/ts/editor/index_reviewer.ts +++ b/ts/editor/index_reviewer.ts @@ -2,11 +2,9 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import ReviewerEditor from "./ReviewerEditor.svelte"; import { editorModules } from "./base"; -import { promiseWithResolver } from "../lib/promise"; import { globalExport } from "../lib/globals"; import { setupI18n } from "../lib/i18n"; - -const [uiPromise, uiResolve] = promiseWithResolver(); +import { uiResolve } from "../lib/ui"; async function setupReviewerEditor(): Promise { await setupI18n({ modules: editorModules }); @@ -20,9 +18,4 @@ async function setupReviewerEditor(): Promise { setupReviewerEditor(); import * as base from "./base"; - -globalExport({ - ...base, - uiPromise, - noteEditorPromise: uiPromise, -}); +globalExport(base); diff --git a/ts/editor/mathjax-overlay/MathjaxButtons.svelte b/ts/editor/mathjax-overlay/MathjaxButtons.svelte index edd31fdcd..b42a2bad4 100644 --- a/ts/editor/mathjax-overlay/MathjaxButtons.svelte +++ b/ts/editor/mathjax-overlay/MathjaxButtons.svelte @@ -4,9 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - - - { - isBlock = false; - updateBlock(); - }} - on:click>{@html inlineIcon} - + + { + isBlock = false; + updateBlock(); + }} + on:click + --border-left-radius="5px">{@html inlineIcon} - - { - isBlock = true; - updateBlock(); - }} - on:click>{@html blockIcon} - - - + { + isBlock = true; + updateBlock(); + }} + on:click + --border-right-radius="5px">{@html blockIcon} + - - - - dispatch("delete")}>{@html deleteIcon} - - - + + dispatch("delete")} + --border-left-radius="5px" + --border-right-radius="5px">{@html deleteIcon} + diff --git a/ts/editor/mathjax-overlay/MathjaxHandle.svelte b/ts/editor/mathjax-overlay/MathjaxHandle.svelte index 1683a6687..4567b755e 100644 --- a/ts/editor/mathjax-overlay/MathjaxHandle.svelte +++ b/ts/editor/mathjax-overlay/MathjaxHandle.svelte @@ -12,10 +12,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import HandleSelection from "../HandleSelection.svelte"; import HandleBackground from "../HandleBackground.svelte"; import HandleControl from "../HandleControl.svelte"; - import { getRichTextInput } from "../rich-text-input"; + import { context } from "../rich-text-input"; import MathjaxMenu from "./MathjaxMenu.svelte"; - const { container, api } = getRichTextInput(); + const { container, api } = context.get(); const { flushCaret, preventResubscription } = api; const code = writable(""); diff --git a/ts/editor/plain-text-input/PlainTextInput.svelte b/ts/editor/plain-text-input/PlainTextInput.svelte index 3cf4703e7..e21dd4b25 100644 --- a/ts/editor/plain-text-input/PlainTextInput.svelte +++ b/ts/editor/plain-text-input/PlainTextInput.svelte @@ -20,8 +20,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { tick, onMount } from "svelte"; import { writable } from "svelte/store"; import { pageTheme } from "../../sveltelib/theme"; - import { getDecoratedElements } from "../DecoratedElements.svelte"; - import { getEditingArea } from "../EditingArea.svelte"; + import { context as editingAreaContext } from "../EditingArea.svelte"; + import { context as decoratedElementsContext } from "../DecoratedElements.svelte"; import CodeMirror from "../CodeMirror.svelte"; import type { CodeMirrorAPI } from "../CodeMirror.svelte"; import { htmlanki, baseOptions, gutterOptions } from "../code-mirror"; @@ -34,8 +34,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html ...gutterOptions, }; - const { editingInputs, content } = getEditingArea(); - const decoratedElements = getDecoratedElements(); + const { editingInputs, content } = editingAreaContext.get(); + const decoratedElements = decoratedElementsContext.get(); const code = writable($content); function adjustInputHTML(html: string): string { diff --git a/ts/editor/rich-text-input/RichTextInput.svelte b/ts/editor/rich-text-input/RichTextInput.svelte index 404109ef8..0c231b71c 100644 --- a/ts/editor/rich-text-input/RichTextInput.svelte +++ b/ts/editor/rich-text-input/RichTextInput.svelte @@ -27,6 +27,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html getTriggerAfterInput(): Trigger; } + export function editingInputIsRichText( + editingInput: EditingInputAPI | null, + ): editingInput is RichTextInputAPI { + return editingInput?.name === "rich-text"; + } + export interface RichTextInputContextAPI { styles: CustomStyles; container: HTMLElement; @@ -34,10 +40,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } const key = Symbol("richText"); - const [set, getRichTextInput, hasRichTextInput] = - contextProperty(key); - - export { getRichTextInput, hasRichTextInput }; + const [context, setContextProperty] = contextProperty(key); import getDOMMirror from "../../sveltelib/mirror-dom"; import getInputManager from "../../sveltelib/input-manager"; @@ -49,7 +52,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html getTriggerOnNextInsert, } = getInputManager(); - export { getTriggerAfterInput, getTriggerOnInput, getTriggerOnNextInsert }; + export { context, getTriggerAfterInput, getTriggerOnInput, getTriggerOnNextInsert };