mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 22:42:25 -04:00
Split/Merge editor.py for its three use cases (#1581)
* Forbid inserting object and iframe tags via PlainTextInput * Add optional browserMode parameter to Editor * Create new ts modules for three editor instances - note-creator for AddCards - browser-editor for the editor in the Browser - reviewer-editor for the EditCurrent * Revert "Forbid inserting object and iframe tags via PlainTextInput" This reverts commit ab90ae8194494d883a1863126496e2d8f332509e. * Refactor browserMode to editorMode * Move new editor variants inside /ts/editor directory * Fix typo
This commit is contained in:
parent
489eadb352
commit
3beea5e1e4
18 changed files with 286 additions and 100 deletions
|
@ -71,7 +71,12 @@ class AddCards(QMainWindow):
|
||||||
self.setAndFocusNote(new_note)
|
self.setAndFocusNote(new_note)
|
||||||
|
|
||||||
def setupEditor(self) -> None:
|
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());")
|
self.editor.web.eval("noteEditorPromise.then(() => activateStickyShortcuts());")
|
||||||
|
|
||||||
def setup_choosers(self) -> None:
|
def setup_choosers(self) -> None:
|
||||||
|
|
|
@ -418,7 +418,12 @@ class Browser(QMainWindow):
|
||||||
)
|
)
|
||||||
|
|
||||||
gui_hooks.editor_did_init.append(add_preview_button)
|
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)
|
gui_hooks.editor_did_init.remove(add_preview_button)
|
||||||
|
|
||||||
@ensure_editor_saved
|
@ensure_editor_saved
|
||||||
|
|
|
@ -15,14 +15,6 @@ compile_sass(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
copy_files_into_group(
|
|
||||||
name = "editor",
|
|
||||||
srcs = [
|
|
||||||
"editor.css",
|
|
||||||
],
|
|
||||||
package = "//ts/editor",
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_files_into_group(
|
copy_files_into_group(
|
||||||
name = "editable",
|
name = "editable",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
@ -31,6 +23,16 @@ copy_files_into_group(
|
||||||
package = "//ts/editable",
|
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(
|
copy_files_into_group(
|
||||||
name = "reviewer",
|
name = "reviewer",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
|
|
@ -26,7 +26,9 @@ typescript(
|
||||||
copy_files_into_group(
|
copy_files_into_group(
|
||||||
name = "editor",
|
name = "editor",
|
||||||
srcs = [
|
srcs = [
|
||||||
"editor.js",
|
"browser_editor.js",
|
||||||
|
"reviewer_editor.js",
|
||||||
|
"note_creator.js",
|
||||||
],
|
],
|
||||||
package = "//ts/editor",
|
package = "//ts/editor",
|
||||||
)
|
)
|
||||||
|
@ -43,9 +45,9 @@ filegroup(
|
||||||
name = "js",
|
name = "js",
|
||||||
srcs = [
|
srcs = [
|
||||||
"aqt",
|
"aqt",
|
||||||
"editor",
|
|
||||||
"mathjax.js",
|
"mathjax.js",
|
||||||
"reviewer",
|
"reviewer",
|
||||||
|
"editor",
|
||||||
"//qt/aqt/data/web/js/vendor",
|
"//qt/aqt/data/web/js/vendor",
|
||||||
],
|
],
|
||||||
visibility = ["//qt:__subpackages__"],
|
visibility = ["//qt:__subpackages__"],
|
||||||
|
|
|
@ -24,7 +24,12 @@ class EditCurrent(QDialog):
|
||||||
self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close).setShortcut(
|
self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close).setShortcut(
|
||||||
QKeySequence("Ctrl+Return")
|
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.card = self.mw.reviewer.card
|
||||||
self.editor.set_note(self.mw.reviewer.card.note(), focusTo=0)
|
self.editor.set_note(self.mw.reviewer.card.note(), focusTo=0)
|
||||||
restoreGeom(self, "editcurrent")
|
restoreGeom(self, "editcurrent")
|
||||||
|
|
|
@ -13,6 +13,7 @@ import urllib.error
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import warnings
|
import warnings
|
||||||
|
from enum import Enum
|
||||||
from random import randrange
|
from random import randrange
|
||||||
from typing import Any, Callable, Match, cast
|
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:
|
class Editor:
|
||||||
"""The screen that embeds an editing widget should listen for changes via
|
"""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
|
the `operation_did_execute` hook, and call set_note() when the editor needs
|
||||||
|
@ -91,13 +98,23 @@ class Editor:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
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:
|
) -> None:
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.widget = widget
|
self.widget = widget
|
||||||
self.parentWindow = parentWindow
|
self.parentWindow = parentWindow
|
||||||
self.note: Note | None = None
|
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
|
self.currentField: int | None = None
|
||||||
# Similar to currentField, but not set to None on a blur. May be
|
# Similar to currentField, but not set to None on a blur. May be
|
||||||
# outside the bounds of the current notetype.
|
# outside the bounds of the current notetype.
|
||||||
|
@ -124,11 +141,18 @@ class Editor:
|
||||||
self.web.set_bridge_command(self.onBridgeCmd, self)
|
self.web.set_bridge_command(self.onBridgeCmd, self)
|
||||||
self.outerLayout.addWidget(self.web, 1)
|
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
|
# then load page
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
"",
|
"",
|
||||||
css=["css/editor.css"],
|
css=[f"css/{file}.css"],
|
||||||
js=["js/editor.js"],
|
js=[f"js/{file}.js"],
|
||||||
context=self,
|
context=self,
|
||||||
default_css=False,
|
default_css=False,
|
||||||
)
|
)
|
||||||
|
@ -137,7 +161,7 @@ class Editor:
|
||||||
gui_hooks.editor_did_init_left_buttons(lefttopbtns, self)
|
gui_hooks.editor_did_init_left_buttons(lefttopbtns, self)
|
||||||
|
|
||||||
lefttopbtns_defs = [
|
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
|
for button in lefttopbtns
|
||||||
]
|
]
|
||||||
lefttopbtns_js = "\n".join(lefttopbtns_defs)
|
lefttopbtns_js = "\n".join(lefttopbtns_defs)
|
||||||
|
@ -150,7 +174,7 @@ class Editor:
|
||||||
righttopbtns_defs = ", ".join([json.dumps(button) for button in righttopbtns])
|
righttopbtns_defs = ", ".join([json.dumps(button) for button in righttopbtns])
|
||||||
righttopbtns_js = (
|
righttopbtns_js = (
|
||||||
f"""
|
f"""
|
||||||
noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
|
uiPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
|
||||||
component: editorToolbar.AddonButtons,
|
component: editorToolbar.AddonButtons,
|
||||||
id: "addons",
|
id: "addons",
|
||||||
props: {{ buttons: [ {righttopbtns_defs} ] }},
|
props: {{ buttons: [ {righttopbtns_defs} ] }},
|
||||||
|
@ -501,9 +525,7 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
|
||||||
js += " setSticky(%s);" % json.dumps(sticky)
|
js += " setSticky(%s);" % json.dumps(sticky)
|
||||||
|
|
||||||
js = gui_hooks.editor_will_load_note(js, self.note, self)
|
js = gui_hooks.editor_will_load_note(js, self.note, self)
|
||||||
self.web.evalWithCallback(
|
self.web.evalWithCallback(f"uiPromise.then(() => {{ {js} }})", oncallback)
|
||||||
f"noteEditorPromise.then(() => {{ {js} }})", oncallback
|
|
||||||
)
|
|
||||||
|
|
||||||
def _save_current_note(self) -> None:
|
def _save_current_note(self) -> None:
|
||||||
"Call after note is updated with data from webview."
|
"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:
|
elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE:
|
||||||
cloze_hint = tr.adding_cloze_outside_cloze_field()
|
cloze_hint = tr.adding_cloze_outside_cloze_field()
|
||||||
|
|
||||||
self.web.eval(f"setBackgrounds({json.dumps(cols)});")
|
self.web.eval(f"uiPromise.then(() => setBackgrounds({json.dumps(cols)}));")
|
||||||
self.web.eval(f"setClozeHint({json.dumps(cloze_hint)});")
|
self.web.eval(f"uiPromise.then(() => setClozeHint({json.dumps(cloze_hint)}));")
|
||||||
|
|
||||||
def showDupes(self) -> None:
|
def showDupes(self) -> None:
|
||||||
aqt.dialogs.open(
|
aqt.dialogs.open(
|
||||||
|
@ -1333,11 +1355,11 @@ gui_hooks.editor_will_munge_html.append(reverse_url_quoting)
|
||||||
def set_cloze_button(editor: Editor) -> None:
|
def set_cloze_button(editor: Editor) -> None:
|
||||||
if editor.note.note_type()["type"] == MODEL_CLOZE:
|
if editor.note.note_type()["type"] == MODEL_CLOZE:
|
||||||
editor.web.eval(
|
editor.web.eval(
|
||||||
'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.showButton("cloze")); '
|
'uiPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.showButton("cloze")); '
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
editor.web.eval(
|
editor.web.eval(
|
||||||
'noteEditorPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.hideButton("cloze")); '
|
'uiPromise.then((noteEditor) => noteEditor.toolbar.templateButtons.hideButton("cloze")); '
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,28 +35,52 @@ _ts_deps = [
|
||||||
compile_svelte(deps = _ts_deps)
|
compile_svelte(deps = _ts_deps)
|
||||||
|
|
||||||
typescript(
|
typescript(
|
||||||
name = "editor_ts",
|
name = "editor",
|
||||||
deps = _ts_deps + [
|
deps = _ts_deps + [
|
||||||
":svelte",
|
":svelte",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
esbuild(
|
_esbuild_deps = [
|
||||||
name = "editor",
|
":editor",
|
||||||
args = {
|
|
||||||
"loader": {".svg": "text"},
|
|
||||||
},
|
|
||||||
entry_point = "index_wrapper.ts",
|
|
||||||
output_css = "editor.css",
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
deps = [
|
|
||||||
":editor_css",
|
":editor_css",
|
||||||
":editor_ts",
|
|
||||||
"//sass:button_mixins_lib",
|
"//sass:button_mixins_lib",
|
||||||
"@npm//@mdi",
|
"@npm//@mdi",
|
||||||
"@npm//bootstrap-icons",
|
"@npm//bootstrap-icons",
|
||||||
"@npm//protobufjs",
|
"@npm//protobufjs",
|
||||||
],
|
]
|
||||||
|
|
||||||
|
esbuild(
|
||||||
|
name = "browser_editor",
|
||||||
|
args = {
|
||||||
|
"loader": {".svg": "text"},
|
||||||
|
},
|
||||||
|
entry_point = "index_browser.ts",
|
||||||
|
output_css = "browser_editor.css",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
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
|
# Tests
|
||||||
|
|
19
ts/editor/BrowserEditor.svelte
Normal file
19
ts/editor/BrowserEditor.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!--
|
||||||
|
Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import OldEditorAdapter from "../editor/OldEditorAdapter.svelte";
|
||||||
|
import type { NoteEditorAPI } from "../editor/OldEditorAdapter.svelte";
|
||||||
|
|
||||||
|
const api: Partial<NoteEditorAPI> = {};
|
||||||
|
let noteEditor: OldEditorAdapter;
|
||||||
|
|
||||||
|
export let uiResolve: (api: NoteEditorAPI) => void;
|
||||||
|
|
||||||
|
$: if (noteEditor) {
|
||||||
|
uiResolve(api as NoteEditorAPI);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<OldEditorAdapter bind:this={noteEditor} {api} />
|
19
ts/editor/NoteCreator.svelte
Normal file
19
ts/editor/NoteCreator.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!--
|
||||||
|
Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import OldEditorAdapter from "../editor/OldEditorAdapter.svelte";
|
||||||
|
import type { NoteEditorAPI } from "../editor/OldEditorAdapter.svelte";
|
||||||
|
|
||||||
|
const api: Partial<NoteEditorAPI> = {};
|
||||||
|
let noteEditor: OldEditorAdapter;
|
||||||
|
|
||||||
|
export let uiResolve: (api: NoteEditorAPI) => void;
|
||||||
|
|
||||||
|
$: if (noteEditor) {
|
||||||
|
uiResolve(api as NoteEditorAPI);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<OldEditorAdapter bind:this={noteEditor} {api} />
|
|
@ -248,7 +248,38 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
import { wrapInternal } from "../lib/wrap";
|
||||||
|
|
||||||
onMount(() => {
|
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);
|
document.addEventListener("visibilitychange", saveOnPageHide);
|
||||||
return () => document.removeEventListener("visibilitychange", saveOnPageHide);
|
return () => document.removeEventListener("visibilitychange", saveOnPageHide);
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,13 +4,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script context="module" lang="ts">
|
<script context="module" lang="ts">
|
||||||
import type { EditingInputAPI } from "./EditingArea.svelte";
|
import type { EditingInputAPI } from "./EditingArea.svelte";
|
||||||
|
import type { CodeMirror as CodeMirrorType } from "./code-mirror";
|
||||||
import CodeMirror from "./CodeMirror.svelte";
|
import CodeMirror from "./CodeMirror.svelte";
|
||||||
|
|
||||||
export interface PlainTextInputAPI extends EditingInputAPI {
|
export interface PlainTextInputAPI extends EditingInputAPI {
|
||||||
name: "plain-text";
|
name: "plain-text";
|
||||||
moveCaretToEnd(): void;
|
moveCaretToEnd(): void;
|
||||||
toggle(): boolean;
|
toggle(): boolean;
|
||||||
getEditor(): CodeMirror.Editor;
|
getEditor(): CodeMirrorType.Editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parsingInstructions: string[] = [];
|
export const parsingInstructions: string[] = [];
|
||||||
|
@ -95,7 +96,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
return hidden;
|
return hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEditor(): CodeMirror.Editor {
|
function getEditor(): CodeMirrorType.Editor {
|
||||||
return codeMirror?.editor;
|
return codeMirror?.editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
ts/editor/ReviewerEditor.svelte
Normal file
19
ts/editor/ReviewerEditor.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!--
|
||||||
|
Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import OldEditorAdapter from "../editor/OldEditorAdapter.svelte";
|
||||||
|
import type { NoteEditorAPI } from "../editor/OldEditorAdapter.svelte";
|
||||||
|
|
||||||
|
const api: Partial<NoteEditorAPI> = {};
|
||||||
|
let noteEditor: OldEditorAdapter;
|
||||||
|
|
||||||
|
export let uiResolve: (api: NoteEditorAPI) => void;
|
||||||
|
|
||||||
|
$: if (noteEditor) {
|
||||||
|
uiResolve(api as NoteEditorAPI);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<OldEditorAdapter bind:this={noteEditor} {api} />
|
|
@ -60,53 +60,4 @@ export const i18n = setupI18n({
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
import OldEditorAdapter from "./OldEditorAdapter.svelte";
|
|
||||||
import type { NoteEditorAPI } from "./OldEditorAdapter.svelte";
|
|
||||||
|
|
||||||
async function setupNoteEditor(): Promise<NoteEditorAPI> {
|
|
||||||
await i18n;
|
|
||||||
|
|
||||||
const api: Partial<NoteEditorAPI> = {};
|
|
||||||
|
|
||||||
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<void> {
|
|
||||||
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";
|
export { editorToolbar } from "./EditorToolbar.svelte";
|
||||||
|
|
27
ts/editor/index_browser.ts
Normal file
27
ts/editor/index_browser.ts
Normal file
|
@ -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<void> {
|
||||||
|
await i18n;
|
||||||
|
|
||||||
|
new BrowserEditor({
|
||||||
|
target: document.body,
|
||||||
|
props: { uiResolve },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupBrowserEditor();
|
||||||
|
|
||||||
|
import * as editor from ".";
|
||||||
|
|
||||||
|
globalExport({
|
||||||
|
...editor,
|
||||||
|
uiPromise,
|
||||||
|
noteEditorPromise: uiPromise,
|
||||||
|
});
|
27
ts/editor/index_creator.ts
Normal file
27
ts/editor/index_creator.ts
Normal file
|
@ -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<void> {
|
||||||
|
await i18n;
|
||||||
|
|
||||||
|
new NoteCreator({
|
||||||
|
target: document.body,
|
||||||
|
props: { uiResolve },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupNoteCreator();
|
||||||
|
|
||||||
|
import * as editor from ".";
|
||||||
|
|
||||||
|
globalExport({
|
||||||
|
...editor,
|
||||||
|
uiPromise,
|
||||||
|
noteEditorPromise: uiPromise,
|
||||||
|
});
|
27
ts/editor/index_reviewer.ts
Normal file
27
ts/editor/index_reviewer.ts
Normal file
|
@ -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<void> {
|
||||||
|
await i18n;
|
||||||
|
|
||||||
|
new ReviewerEditor({
|
||||||
|
target: document.body,
|
||||||
|
props: { uiResolve },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupReviewerEditor();
|
||||||
|
|
||||||
|
import * as editor from "../editor";
|
||||||
|
|
||||||
|
globalExport({
|
||||||
|
...editor,
|
||||||
|
uiPromise,
|
||||||
|
noteEditorPromise: uiPromise,
|
||||||
|
});
|
|
@ -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;
|
|
11
ts/lib/globals.ts
Normal file
11
ts/lib/globals.ts
Normal file
|
@ -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<string, unknown>): void {
|
||||||
|
for (const key in globals) {
|
||||||
|
window[key] = globals[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// but also export as window.anki
|
||||||
|
window["anki"] = globals;
|
||||||
|
}
|
Loading…
Reference in a new issue