diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py
index ae9249a07..d1ad135e1 100644
--- a/qt/aqt/addcards.py
+++ b/qt/aqt/addcards.py
@@ -71,7 +71,12 @@ class AddCards(QMainWindow):
self.setAndFocusNote(new_note)
def setupEditor(self) -> None:
- self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self, True)
+ self.editor = aqt.editor.Editor(
+ self.mw,
+ self.form.fieldsArea,
+ self,
+ editorMode=aqt.editor.EditorMode.ADD_CARDS,
+ )
self.editor.web.eval("noteEditorPromise.then(() => activateStickyShortcuts());")
def setup_choosers(self) -> None:
diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py
index 6eca263b3..6926e74b3 100644
--- a/qt/aqt/browser/browser.py
+++ b/qt/aqt/browser/browser.py
@@ -418,7 +418,12 @@ class Browser(QMainWindow):
)
gui_hooks.editor_did_init.append(add_preview_button)
- self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self)
+ self.editor = aqt.editor.Editor(
+ self.mw,
+ self.form.fieldsArea,
+ self,
+ editorMode=aqt.editor.EditorMode.BROWSER,
+ )
gui_hooks.editor_did_init.remove(add_preview_button)
@ensure_editor_saved
diff --git a/qt/aqt/data/web/css/BUILD.bazel b/qt/aqt/data/web/css/BUILD.bazel
index 2bdcca54c..8d677a20e 100644
--- a/qt/aqt/data/web/css/BUILD.bazel
+++ b/qt/aqt/data/web/css/BUILD.bazel
@@ -15,14 +15,6 @@ compile_sass(
],
)
-copy_files_into_group(
- name = "editor",
- srcs = [
- "editor.css",
- ],
- package = "//ts/editor",
-)
-
copy_files_into_group(
name = "editable",
srcs = [
@@ -31,6 +23,16 @@ copy_files_into_group(
package = "//ts/editable",
)
+copy_files_into_group(
+ name = "editor",
+ srcs = [
+ "browser_editor.css",
+ "reviewer_editor.css",
+ "note_creator.css",
+ ],
+ package = "//ts/editor",
+)
+
copy_files_into_group(
name = "reviewer",
srcs = [
diff --git a/qt/aqt/data/web/js/BUILD.bazel b/qt/aqt/data/web/js/BUILD.bazel
index b8fac74a4..a28fb5562 100644
--- a/qt/aqt/data/web/js/BUILD.bazel
+++ b/qt/aqt/data/web/js/BUILD.bazel
@@ -26,7 +26,9 @@ typescript(
copy_files_into_group(
name = "editor",
srcs = [
- "editor.js",
+ "browser_editor.js",
+ "reviewer_editor.js",
+ "note_creator.js",
],
package = "//ts/editor",
)
@@ -43,9 +45,9 @@ filegroup(
name = "js",
srcs = [
"aqt",
- "editor",
"mathjax.js",
"reviewer",
+ "editor",
"//qt/aqt/data/web/js/vendor",
],
visibility = ["//qt:__subpackages__"],
diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py
index 4d841b5bc..cdee9388b 100644
--- a/qt/aqt/editcurrent.py
+++ b/qt/aqt/editcurrent.py
@@ -24,7 +24,12 @@ class EditCurrent(QDialog):
self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close).setShortcut(
QKeySequence("Ctrl+Return")
)
- self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self)
+ self.editor = aqt.editor.Editor(
+ self.mw,
+ self.form.fieldsArea,
+ self,
+ editorMode=aqt.editor.EditorMode.EDIT_CURRENT,
+ )
self.editor.card = self.mw.reviewer.card
self.editor.set_note(self.mw.reviewer.card.note(), focusTo=0)
restoreGeom(self, "editcurrent")
diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py
index 65c36134a..7a312dd2a 100644
--- a/qt/aqt/editor.py
+++ b/qt/aqt/editor.py
@@ -13,6 +13,7 @@ import urllib.error
import urllib.parse
import urllib.request
import warnings
+from enum import Enum
from random import randrange
from typing import Any, Callable, Match, cast
@@ -80,6 +81,12 @@ audio = (
)
+class EditorMode(Enum):
+ ADD_CARDS = 0
+ EDIT_CURRENT = 1
+ BROWSER = 2
+
+
class Editor:
"""The screen that embeds an editing widget should listen for changes via
the `operation_did_execute` hook, and call set_note() when the editor needs
@@ -91,13 +98,23 @@ class Editor:
"""
def __init__(
- self, mw: AnkiQt, widget: QWidget, parentWindow: QWidget, addMode: bool = False
+ self,
+ mw: AnkiQt,
+ widget: QWidget,
+ parentWindow: QWidget,
+ addMode: bool | None = None,
+ *,
+ editorMode: EditorMode = EditorMode.EDIT_CURRENT,
) -> None:
self.mw = mw
self.widget = widget
self.parentWindow = parentWindow
self.note: Note | None = None
- self.addMode = addMode
+ # legacy argument provided?
+ if addMode is not None:
+ editorMode = EditorMode.ADD_CARDS if addMode else EditorMode.EDIT_CURRENT
+ self.addMode = editorMode is EditorMode.ADD_CARDS
+ self.editorMode = editorMode
self.currentField: int | None = None
# Similar to currentField, but not set to None on a blur. May be
# outside the bounds of the current notetype.
@@ -124,11 +141,18 @@ class Editor:
self.web.set_bridge_command(self.onBridgeCmd, self)
self.outerLayout.addWidget(self.web, 1)
+ if self.editorMode == EditorMode.ADD_CARDS:
+ file = "note_creator"
+ elif self.editorMode == EditorMode.BROWSER:
+ file = "browser_editor"
+ else:
+ file = "reviewer_editor"
+
# then load page
self.web.stdHtml(
"",
- css=["css/editor.css"],
- js=["js/editor.js"],
+ css=[f"css/{file}.css"],
+ js=[f"js/{file}.js"],
context=self,
default_css=False,
)
@@ -137,7 +161,7 @@ class Editor:
gui_hooks.editor_did_init_left_buttons(lefttopbtns, self)
lefttopbtns_defs = [
- f"noteEditorPromise.then((noteEditor) => noteEditor.toolbar.notetypeButtons.appendButton({{ component: editorToolbar.Raw, props: {{ html: {json.dumps(button)} }} }}, -1));"
+ f"uiPromise.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 +174,7 @@ class Editor:
righttopbtns_defs = ", ".join([json.dumps(button) for button in righttopbtns])
righttopbtns_js = (
f"""
-noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
+uiPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
component: editorToolbar.AddonButtons,
id: "addons",
props: {{ buttons: [ {righttopbtns_defs} ] }},
@@ -501,9 +525,7 @@ noteEditorPromise.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"noteEditorPromise.then(() => {{ {js} }})", oncallback
- )
+ self.web.evalWithCallback(f"uiPromise.then(() => {{ {js} }})", oncallback)
def _save_current_note(self) -> None:
"Call after note is updated with data from webview."
@@ -557,8 +579,8 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE:
cloze_hint = tr.adding_cloze_outside_cloze_field()
- self.web.eval(f"setBackgrounds({json.dumps(cols)});")
- self.web.eval(f"setClozeHint({json.dumps(cloze_hint)});")
+ self.web.eval(f"uiPromise.then(() => setBackgrounds({json.dumps(cols)}));")
+ self.web.eval(f"uiPromise.then(() => setClozeHint({json.dumps(cloze_hint)}));")
def showDupes(self) -> None:
aqt.dialogs.open(
@@ -1333,11 +1355,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.templateButtons.showButton("cloze")); '
+ 'uiPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.showButton("cloze")); '
)
else:
editor.web.eval(
- 'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.hideButton("cloze")); '
+ 'uiPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.hideButton("cloze")); '
)
diff --git a/ts/editor/BUILD.bazel b/ts/editor/BUILD.bazel
index e668721fa..389b7b61b 100644
--- a/ts/editor/BUILD.bazel
+++ b/ts/editor/BUILD.bazel
@@ -35,28 +35,52 @@ _ts_deps = [
compile_svelte(deps = _ts_deps)
typescript(
- name = "editor_ts",
+ name = "editor",
deps = _ts_deps + [
":svelte",
],
)
+_esbuild_deps = [
+ ":editor",
+ ":editor_css",
+ "//sass:button_mixins_lib",
+ "@npm//@mdi",
+ "@npm//bootstrap-icons",
+ "@npm//protobufjs",
+]
+
esbuild(
- name = "editor",
+ name = "browser_editor",
args = {
"loader": {".svg": "text"},
},
- entry_point = "index_wrapper.ts",
- output_css = "editor.css",
+ entry_point = "index_browser.ts",
+ output_css = "browser_editor.css",
visibility = ["//visibility:public"],
- deps = [
- ":editor_css",
- ":editor_ts",
- "//sass:button_mixins_lib",
- "@npm//@mdi",
- "@npm//bootstrap-icons",
- "@npm//protobufjs",
- ],
+ deps = _esbuild_deps,
+)
+
+esbuild(
+ name = "reviewer_editor",
+ args = {
+ "loader": {".svg": "text"},
+ },
+ entry_point = "index_reviewer.ts",
+ output_css = "reviewer_editor.css",
+ visibility = ["//visibility:public"],
+ deps = _esbuild_deps,
+)
+
+esbuild(
+ name = "note_creator",
+ args = {
+ "loader": {".svg": "text"},
+ },
+ entry_point = "index_creator.ts",
+ output_css = "note_creator.css",
+ visibility = ["//visibility:public"],
+ deps = _esbuild_deps,
)
# Tests
diff --git a/ts/editor/BrowserEditor.svelte b/ts/editor/BrowserEditor.svelte
new file mode 100644
index 000000000..fad9e991e
--- /dev/null
+++ b/ts/editor/BrowserEditor.svelte
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/ts/editor/NoteCreator.svelte b/ts/editor/NoteCreator.svelte
new file mode 100644
index 000000000..fad9e991e
--- /dev/null
+++ b/ts/editor/NoteCreator.svelte
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/ts/editor/OldEditorAdapter.svelte b/ts/editor/OldEditorAdapter.svelte
index dae07472e..b18e6d8f1 100644
--- a/ts/editor/OldEditorAdapter.svelte
+++ b/ts/editor/OldEditorAdapter.svelte
@@ -248,7 +248,38 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}),
);
+ import { wrapInternal } from "../lib/wrap";
+
onMount(() => {
+ function wrap(before: string, after: string): void {
+ if (!get(focusInRichText)) {
+ return;
+ }
+
+ const input = get(activeInput!) as RichTextInputAPI;
+
+ input.element.then((element) => {
+ wrapInternal(element, before, after, false);
+ });
+ }
+
+ Object.assign(globalThis, {
+ setFields,
+ setDescriptions,
+ setFonts,
+ focusField,
+ setColorButtons,
+ setTags,
+ setSticky,
+ setBackgrounds,
+ setClozeHint,
+ saveNow: saveFieldNow,
+ activateStickyShortcuts,
+ focusIfField,
+ setNoteId,
+ wrap,
+ });
+
document.addEventListener("visibilitychange", saveOnPageHide);
return () => document.removeEventListener("visibilitychange", saveOnPageHide);
});
diff --git a/ts/editor/PlainTextInput.svelte b/ts/editor/PlainTextInput.svelte
index 852bb6ada..bddacdf02 100644
--- a/ts/editor/PlainTextInput.svelte
+++ b/ts/editor/PlainTextInput.svelte
@@ -4,13 +4,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
+
+
diff --git a/ts/editor/index.ts b/ts/editor/index.ts
index 647320cb4..6577f4c56 100644
--- a/ts/editor/index.ts
+++ b/ts/editor/index.ts
@@ -60,53 +60,4 @@ export const i18n = setupI18n({
],
});
-import OldEditorAdapter from "./OldEditorAdapter.svelte";
-import type { NoteEditorAPI } from "./OldEditorAdapter.svelte";
-
-async function setupNoteEditor(): Promise {
- await i18n;
-
- const api: Partial = {};
-
- const noteEditor = new OldEditorAdapter({
- target: document.body,
- props: { api: api as NoteEditorAPI },
- });
-
- Object.assign(globalThis, {
- setFields: noteEditor.setFields,
- setDescriptions: noteEditor.setDescriptions,
- setFonts: noteEditor.setFonts,
- focusField: noteEditor.focusField,
- setColorButtons: noteEditor.setColorButtons,
- setTags: noteEditor.setTags,
- setSticky: noteEditor.setSticky,
- setBackgrounds: noteEditor.setBackgrounds,
- setClozeHint: noteEditor.setClozeHint,
- saveNow: noteEditor.saveFieldNow,
- activateStickyShortcuts: noteEditor.activateStickyShortcuts,
- focusIfField: noteEditor.focusIfField,
- setNoteId: noteEditor.setNoteId,
- });
-
- return api as NoteEditorAPI;
-}
-
-import { get } from "svelte/store";
-import { wrapInternal } from "../lib/wrap";
-import type { RichTextInputAPI } from "./RichTextInput.svelte";
-
-export async function wrap(before: string, after: string): Promise {
- const noteEditor = await noteEditorPromise;
-
- if (!get(noteEditor.focusInRichText)) {
- return;
- }
-
- const activeInput = get(noteEditor.activeInput) as RichTextInputAPI;
- const element = await activeInput.element;
- wrapInternal(element, before, after, false);
-}
-
-export const noteEditorPromise = setupNoteEditor();
export { editorToolbar } from "./EditorToolbar.svelte";
diff --git a/ts/editor/index_browser.ts b/ts/editor/index_browser.ts
new file mode 100644
index 000000000..25ebaa18d
--- /dev/null
+++ b/ts/editor/index_browser.ts
@@ -0,0 +1,27 @@
+// Copyright: Ankitects Pty Ltd and contributors
+// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+import { i18n } from ".";
+import BrowserEditor from "./BrowserEditor.svelte";
+import { promiseWithResolver } from "../lib/promise";
+import { globalExport } from "../lib/globals";
+
+const [uiPromise, uiResolve] = promiseWithResolver();
+
+async function setupBrowserEditor(): Promise {
+ await i18n;
+
+ new BrowserEditor({
+ target: document.body,
+ props: { uiResolve },
+ });
+}
+
+setupBrowserEditor();
+
+import * as editor from ".";
+
+globalExport({
+ ...editor,
+ uiPromise,
+ noteEditorPromise: uiPromise,
+});
diff --git a/ts/editor/index_creator.ts b/ts/editor/index_creator.ts
new file mode 100644
index 000000000..c28be2256
--- /dev/null
+++ b/ts/editor/index_creator.ts
@@ -0,0 +1,27 @@
+// Copyright: Ankitects Pty Ltd and contributors
+// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+import { i18n } from ".";
+import NoteCreator from "./NoteCreator.svelte";
+import { promiseWithResolver } from "../lib/promise";
+import { globalExport } from "../lib/globals";
+
+const [uiPromise, uiResolve] = promiseWithResolver();
+
+async function setupNoteCreator(): Promise {
+ await i18n;
+
+ new NoteCreator({
+ target: document.body,
+ props: { uiResolve },
+ });
+}
+
+setupNoteCreator();
+
+import * as editor from ".";
+
+globalExport({
+ ...editor,
+ uiPromise,
+ noteEditorPromise: uiPromise,
+});
diff --git a/ts/editor/index_reviewer.ts b/ts/editor/index_reviewer.ts
new file mode 100644
index 000000000..eab11b5ae
--- /dev/null
+++ b/ts/editor/index_reviewer.ts
@@ -0,0 +1,27 @@
+// Copyright: Ankitects Pty Ltd and contributors
+// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+import { i18n } from "../editor";
+import ReviewerEditor from "./ReviewerEditor.svelte";
+import { promiseWithResolver } from "../lib/promise";
+import { globalExport } from "../lib/globals";
+
+const [uiPromise, uiResolve] = promiseWithResolver();
+
+async function setupReviewerEditor(): Promise {
+ await i18n;
+
+ new ReviewerEditor({
+ target: document.body,
+ props: { uiResolve },
+ });
+}
+
+setupReviewerEditor();
+
+import * as editor from "../editor";
+
+globalExport({
+ ...editor,
+ uiPromise,
+ noteEditorPromise: uiPromise,
+});
diff --git a/ts/editor/index_wrapper.ts b/ts/editor/index_wrapper.ts
deleted file mode 100644
index 6c0ec80af..000000000
--- a/ts/editor/index_wrapper.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright: Ankitects Pty Ltd and contributors
-// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-
-// extend the global namespace with our exports - not sure if there's a better way with esbuild
-import * as globals from "./index";
-for (const key in globals) {
- window[key] = globals[key];
-}
-
-// but also export as window.anki
-window["anki"] = globals;
diff --git a/ts/lib/globals.ts b/ts/lib/globals.ts
new file mode 100644
index 000000000..3107a4fa4
--- /dev/null
+++ b/ts/lib/globals.ts
@@ -0,0 +1,11 @@
+// Copyright: Ankitects Pty Ltd and contributors
+// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+
+export function globalExport(globals: Record): void {
+ for (const key in globals) {
+ window[key] = globals[key];
+ }
+
+ // but also export as window.anki
+ window["anki"] = globals;
+}