diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py
index 47470c0f3..b03925ce7 100644
--- a/qt/aqt/mediasrv.py
+++ b/qt/aqt/mediasrv.py
@@ -923,6 +923,7 @@ exposed_backend_list = [
"get_custom_colours",
# DeckService
"get_deck_names",
+ "get_deck",
# I18nService
"i18n_resources",
# ImportExportService
@@ -1003,11 +1004,7 @@ def raw_backend_request(endpoint: str) -> Callable[[], bytes]:
response.ParseFromString(output)
def handle_on_main() -> None:
- from aqt.editor import NewEditor
-
- handler = aqt.mw.app.activeModalWidget()
- if handler and isinstance(getattr(handler, "editor", None), NewEditor):
- handler = handler.editor # type: ignore
+ handler = aqt.mw.app.activeWindow()
on_op_finished(aqt.mw, response, handler)
aqt.mw.taskman.run_on_main(handle_on_main)
diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py
index 95d84c00e..d48743d16 100644
--- a/qt/aqt/webview.py
+++ b/qt/aqt/webview.py
@@ -12,14 +12,16 @@ from collections.abc import Callable, Sequence
from enum import Enum
from typing import TYPE_CHECKING, Any, Type, cast
+from google.protobuf.json_format import MessageToDict
from typing_extensions import TypedDict, Unpack
import anki
import anki.lang
from anki._legacy import deprecated
from anki.lang import is_rtl
-from anki.utils import hmr_mode, is_lin, is_mac, is_win
+from anki.utils import hmr_mode, is_lin, is_mac, is_win, to_json_bytes
from aqt import colors, gui_hooks
+from aqt.operations import OpChanges
from aqt.qt import *
from aqt.qt import sip
from aqt.theme import theme_manager
@@ -382,6 +384,7 @@ class AnkiWebView(QWebEngineView):
self._filterSet = False
gui_hooks.theme_did_change.append(self.on_theme_did_change)
gui_hooks.body_classes_need_update.append(self.on_body_classes_need_update)
+ gui_hooks.operation_did_execute.append(self.on_operation_did_execute)
qconnect(self.loadFinished, self._on_load_finished)
@@ -911,6 +914,7 @@ html {{ {font} }}
gui_hooks.theme_did_change.remove(self.on_theme_did_change)
gui_hooks.body_classes_need_update.remove(self.on_body_classes_need_update)
+ gui_hooks.operation_did_execute.remove(self.on_operation_did_execute)
# defer page cleanup so that in-flight requests have a chance to complete first
# https://forums.ankiweb.net/t/error-when-exiting-browsing-when-the-software-is-installed-in-the-path-c-program-files-anki/38363
mw.progress.single_shot(5000, lambda: mw.mediaServer.clear_page_html(id(self)))
@@ -960,6 +964,17 @@ html {{ {font} }}
f"""document.body.classList.toggle("reduce-motion", {json.dumps(mw.pm.reduce_motion())}); """
)
+ def on_operation_did_execute(
+ self, changes: OpChanges, handler: object | None
+ ) -> None:
+ if handler is self.parentWidget():
+ return
+
+ changes_json = to_json_bytes(MessageToDict(changes)).decode()
+ self.eval(
+ f"if(globalThis.anki && globalThis.anki.onOperationDidExecute) globalThis.anki.onOperationDidExecute({changes_json})"
+ )
+
@deprecated(info="use theme_manager.qcolor() instead")
def get_window_bg_color(self, night_mode: bool | None = None) -> QColor:
return theme_manager.qcolor(colors.CANVAS)
diff --git a/ts/lib/components/DeckChooser.svelte b/ts/lib/components/DeckChooser.svelte
index 59ca750a4..f9c6ba541 100644
--- a/ts/lib/components/DeckChooser.svelte
+++ b/ts/lib/components/DeckChooser.svelte
@@ -4,29 +4,50 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
diff --git a/ts/lib/components/ItemChooser.svelte b/ts/lib/components/ItemChooser.svelte
index 233b1145b..4637a9be9 100644
--- a/ts/lib/components/ItemChooser.svelte
+++ b/ts/lib/components/ItemChooser.svelte
@@ -60,8 +60,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
export function select(itemId: bigint) {
+ if (selectedItem?.id === itemId) {
+ return;
+ }
const item = items.find((item) => item.id === itemId);
- selectedItem = item ? item : null;
+ if (item) {
+ selectedItem = item;
+ onChange?.(item);
+ }
}
$effect(() => {
diff --git a/ts/lib/components/NotetypeChooser.svelte b/ts/lib/components/NotetypeChooser.svelte
index 4820bdd92..0952c5029 100644
--- a/ts/lib/components/NotetypeChooser.svelte
+++ b/ts/lib/components/NotetypeChooser.svelte
@@ -6,27 +6,49 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { NotetypeNameId } from "@generated/anki/notetypes_pb";
import { mdiNewspaper } from "./icons";
- import { getNotetypeNames } from "@generated/backend";
+ import { getNotetype, getNotetypeNames } from "@generated/backend";
import ItemChooser from "./ItemChooser.svelte";
import * as tr from "@generated/ftl";
+ import { registerOperationHandler } from "@tslib/operations";
+ import { onMount } from "svelte";
interface Props {
- selectedNotetype: NotetypeNameId | null;
onChange?: (notetype: NotetypeNameId) => void;
}
- let { selectedNotetype = $bindable(null), onChange }: Props = $props();
+ let { onChange }: Props = $props();
+ let selectedNotetype: NotetypeNameId | null = $state(null);
let notetypes: NotetypeNameId[] = $state([]);
let itemChooser: ItemChooser | null = $state(null);
+ async function fetchNotetypes() {
+ notetypes = (await getNotetypeNames({})).entries;
+ }
+
export function select(notetypeId: bigint) {
itemChooser?.select(notetypeId);
}
- $effect(() => {
- getNotetypeNames({}).then((response) => {
- notetypes = response.entries;
+ export async function getSelected(): Promise {
+ await fetchNotetypes();
+ try {
+ await getNotetype({ ntid: selectedNotetype!.id }, { alertOnError: false });
+ } catch (error) {
+ select(notetypes[0].id);
+ }
+ return selectedNotetype!;
+ }
+
+ onMount(() => {
+ registerOperationHandler((changes) => {
+ if (changes.notetype) {
+ getSelected();
+ }
});
});
+
+ $effect(() => {
+ fetchNotetypes();
+ });
): void {
}
// but also export as window.anki
- window["anki"] = globals;
+ window["anki"] = window["anki"] || {};
+ window["anki"] = { ...window["anki"], ...globals };
}
diff --git a/ts/lib/tslib/operations.ts b/ts/lib/tslib/operations.ts
new file mode 100644
index 000000000..f7244e8a9
--- /dev/null
+++ b/ts/lib/tslib/operations.ts
@@ -0,0 +1,20 @@
+// Copyright: Ankitects Pty Ltd and contributors
+// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+
+import type { OpChanges } from "@generated/anki/collection_pb";
+
+type OperationHandler = (changes: Partial) => void;
+const handlers: OperationHandler[] = [];
+
+export function registerOperationHandler(handler: (changes: Partial) => void): void {
+ handlers.push(handler);
+}
+
+function onOperationDidExecute(changes: Partial): void {
+ for (const handler of handlers) {
+ handler(changes);
+ }
+}
+
+globalThis.anki = globalThis.anki || {};
+globalThis.anki.onOperationDidExecute = onOperationDidExecute;
diff --git a/ts/routes/editor/NoteEditor.svelte b/ts/routes/editor/NoteEditor.svelte
index d7e9021ce..94c74a0b0 100644
--- a/ts/routes/editor/NoteEditor.svelte
+++ b/ts/routes/editor/NoteEditor.svelte
@@ -15,8 +15,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import LabelName from "./LabelName.svelte";
import { EditorState, type EditorMode } from "./types";
import { ContextMenu, Item } from "$lib/context-menu";
- import type { NotetypeNameId } from "@generated/anki/notetypes_pb";
- import type { DeckNameId } from "@generated/anki/decks_pb";
export interface NoteEditorAPI {
fields: EditorFieldAPI[];
@@ -312,20 +310,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let reviewerCard: Card | null = null;
let notetypeChooser: NotetypeChooser;
- let selectedNotetype: NotetypeNameId | null = null;
let deckChooser: DeckChooser;
- let selectedDeck: DeckNameId | null = null;
- async function onNotetypeChange(notetype: NotetypeNameId) {
- loadNote({ notetypeId: notetype.id, copyFromNote: note });
+ async function onNotetypeChange(notetypeId: bigint, updateDeck: boolean = true) {
+ loadNote({ notetypeId, copyFromNote: note });
if (
+ updateDeck &&
!(
await getConfigBool({
key: ConfigKey_Bool.ADDING_DEFAULTS_TO_CURRENT_DECK,
})
).val
) {
- const deckId = await defaultDeckForNotetype({ ntid: notetype.id });
+ const deckId = await defaultDeckForNotetype({ ntid: notetypeId });
deckChooser.select(deckId.did);
}
lastAddedNote = null;
@@ -487,7 +484,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
async function onAdd() {
- await addCurrentNote(selectedDeck!.id);
+ await addCurrentNote((await deckChooser.getSelected()).id);
}
let historyModal: Modal;
@@ -742,6 +739,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { wrapInternal } from "@tslib/wrap";
import { getProfileConfig, getMeta, setMeta, getColConfig } from "@tslib/profile";
import Shortcut from "$lib/components/Shortcut.svelte";
+ import { registerOperationHandler } from "@tslib/operations";
import { mathjaxConfig } from "$lib/editable/mathjax-element.svelte";
import ImageOcclusionPage from "../image-occlusion/ImageOcclusionPage.svelte";
@@ -1236,6 +1234,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
onMount(() => {
+ registerOperationHandler(async (changes) => {
+ if (mode === "add" && (changes.notetype || changes.deck)) {
+ let homeDeckId = 0n;
+ if (reviewerCard) {
+ homeDeckId = reviewerCard.originalDeckId || reviewerCard.deckId;
+ }
+ const chooserDefaults = await defaultsForAdding({
+ homeDeckOfCurrentReviewCard: homeDeckId,
+ });
+ onNotetypeChange(chooserDefaults.notetypeId, false);
+ }
+ });
+
if (mode === "add") {
deregisterSticky = registerShortcut(toggleStickyAll, "Shift+F9");
}
@@ -1371,9 +1382,7 @@ components and functionality for general note editing.
onNotetypeChange(notetype.id)}
/>
{/if}
diff --git a/ts/routes/editor/editor-toolbar/EditorChoosers.svelte b/ts/routes/editor/editor-toolbar/EditorChoosers.svelte
index c706990a8..10267a9e9 100644
--- a/ts/routes/editor/editor-toolbar/EditorChoosers.svelte
+++ b/ts/routes/editor/editor-toolbar/EditorChoosers.svelte
@@ -11,16 +11,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import * as tr from "@generated/ftl";
interface Props {
- selectedNotetype: NotetypeNameId | null;
- selectedDeck?: DeckNameId | null;
notetypeChooser?: NotetypeChooser;
deckChooser?: DeckChooser;
onNotetypeChange?: (notetype: NotetypeNameId) => void;
onDeckChange?: (deck: DeckNameId) => void;
}
let {
- selectedNotetype = $bindable(null),
- selectedDeck = $bindable(null),
notetypeChooser = $bindable(),
deckChooser = $bindable(),
onNotetypeChange,
@@ -31,19 +27,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{tr.notetypesType()}
-
+
{tr.decksDeck()}
-
+