Move note saving to TS

This commit is contained in:
Abdo 2025-05-25 19:20:11 +03:00
parent 75bc67608a
commit 1809e793e6
4 changed files with 64 additions and 55 deletions

View file

@ -10,6 +10,7 @@ package anki.frontend;
import "anki/scheduler.proto";
import "anki/generic.proto";
import "anki/search.proto";
import "anki/notes.proto";
service FrontendService {
// Returns values from the reviewer
@ -28,7 +29,9 @@ service FrontendService {
// Warns python that the deck option web view is ready to receive requests.
rpc deckOptionsReady(generic.Empty) returns (generic.Empty);
// Editor
rpc editorReady(generic.Empty) returns (generic.Empty);
rpc editorUpdateNote(notes.UpdateNotesRequest) returns (generic.Empty);
}
service BackendFrontendService {}

View file

@ -402,24 +402,8 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
# focus lost or key/button pressed?
if cmd.startswith("blur") or cmd.startswith("key"):
(type, ord_str, nid_str, txt) = cmd.split(":", 3)
(type, ord_str) = cmd.split(":", 1)
ord = int(ord_str)
try:
nid = int(nid_str)
except ValueError:
nid = 0
if nid != self.note.id:
print("ignored late blur")
return
try:
self.note.fields[ord] = self.mungeHTML(txt)
except IndexError:
print("ignored late blur after notetype change")
return
if not self.addMode:
self._save_current_note()
if type == "blur":
self.currentField = None
# run any filters

View file

@ -608,13 +608,30 @@ def editor_ready() -> bytes:
def handle_on_main() -> None:
window = aqt.mw.app.activeWindow()
if isinstance(getattr(window, "editor"), Editor):
if window and isinstance(getattr(window, "editor"), Editor):
window.editor._set_ready() # type: ignore
aqt.mw.taskman.run_on_main(handle_on_main)
return b""
def editor_update_note() -> bytes:
from aqt.editor import Editor
output = raw_backend_request("update_notes")()
response = OpChanges()
response.ParseFromString(output)
def handle_on_main() -> None:
window = aqt.mw.app.activeWindow()
if window and isinstance(getattr(window, "editor"), Editor):
on_op_finished(aqt.mw, response, window.editor) # type: ignore
aqt.mw.taskman.run_on_main(handle_on_main)
return output
post_handler_list = [
congrats_info,
get_deck_configs_for_update,
@ -631,6 +648,7 @@ post_handler_list = [
deck_options_require_close,
deck_options_ready,
editor_ready,
editor_update_note,
]
@ -647,6 +665,7 @@ exposed_backend_list = [
# NotesService
"get_field_names",
"get_note",
"new_note",
# NotetypesService
"get_notetype",
"get_notetype_names",

View file

@ -224,14 +224,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
bridgeCommand(`setTagsCollapsed:${$tagsCollapsed}`);
}
let noteId: bigint | null = null;
export function setNoteId(ntid: bigint | null): void {
let note: Note | null = null;
export function setNote(n: Note): void {
note = n;
// TODO this is a hack, because it requires the NoteEditor to know implementation details of the PlainTextInput.
// It should be refactored once we work on our own Undo stack
for (const pi of plainTextInputs) {
pi.api.codeMirror.editor.then((editor) => editor.clearHistory());
}
noteId = ntid;
}
let notetypeMeta: NotetypeIdAndModTime;
@ -251,10 +251,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
}
function getNoteId(): string | null {
return noteId?.toString() ?? null;
}
let isImageOcclusion = false;
function setIsImageOcclusion(val: boolean) {
isImageOcclusion = val;
@ -294,18 +290,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function transformContentBeforeSave(content: string): string {
return content.replace(/ data-editor-shrink="(true|false)"/g, "");
// TODO: mungeHTML()
}
async function updateCurrentNote() {
if (mode !== "add") {
await editorUpdateNote({
notes: [note!],
skipUndoEntry: false,
});
}
}
function updateField(index: number, content: string): void {
fieldSave.schedule(
() =>
bridgeCommand(
`key:${index}:${getNoteId()}:${transformContentBeforeSave(
content,
)}`,
),
600,
);
fieldSave.schedule(() => {
bridgeCommand(`key:${index}`);
note!.fields[index] = transformContentBeforeSave(content);
updateCurrentNote();
}, 600);
}
function saveFieldNow(): void {
@ -422,6 +424,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
getNote,
getNotetype,
encodeIriPaths,
newNote,
editorUpdateNote,
} from "@generated/backend";
import { wrapInternal } from "@tslib/wrap";
@ -442,6 +446,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import StickyBadge from "./StickyBadge.svelte";
import ButtonGroupItem from "$lib/components/ButtonGroupItem.svelte";
import PreviewButton from "./PreviewButton.svelte";
import type { Note } from "@generated/anki/notes_pb";
$: isIOImageLoaded = false;
$: ioImageLoadedStore.set(isIOImageLoaded);
@ -562,11 +567,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
});
}
async function loadNote(nid: bigint | null, notetypeId: bigint, focusTo: number) {
async function loadNote(nid: bigint, notetypeId: bigint, focusTo: number) {
const notetype = await getNotetype({
ntid: notetypeId,
});
let fieldValues: string[] = notetype.fields.map(() => "");
const fieldNames = (
await getFieldNames({
ntid: notetype.id,
@ -576,18 +580,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const clozeFields = fieldNames.map((name, index) =>
clozeFieldOrds.includes(index),
);
let tags: string[] = [];
if (nid) {
const note = await getNote({
if (mode === "add") {
setNote(await newNote({ ntid: notetype.id }));
} else {
setNote(
await getNote({
nid,
});
fieldValues = (
}),
);
}
const fieldValues = (
await Promise.all(
note.fields.map((field) => encodeIriPaths({ val: field })),
note!.fields.map((field) => encodeIriPaths({ val: field })),
)
).map((field) => field.val);
tags = note.tags;
}
const tags = note!.tags;
saveSession();
setFields(fieldNames, fieldValues);
setIsImageOcclusion(
@ -610,7 +618,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
]),
);
focusField(focusTo);
setNoteId(nid);
// TODO: lastTextColor/lastHighlightColor profile config
// setColorButtons(["#0000ff", "#0000ff"]);
setTags(tags);
@ -666,8 +673,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setClozeHint,
saveNow,
focusIfField,
getNoteId,
setNoteId,
setNotetypeMeta,
wrap,
setMathjaxEnabled,
@ -786,11 +791,9 @@ components and functionality for general note editing.
on:focusout={() => {
$focusedField = null;
setAddonButtonsDisabled(true);
bridgeCommand(
`blur:${index}:${getNoteId()}:${transformContentBeforeSave(
get(content),
)}`,
);
bridgeCommand(`blur:${index}`);
note!.fields[index] = transformContentBeforeSave(get(content));
updateCurrentNote();
}}
on:mouseenter={() => {
$hoveredField = fields[index];