diff --git a/proto/backend.proto b/proto/backend.proto index fe56065f6..7a69e697e 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -723,7 +723,7 @@ message GetChangedTagsOut { message SetConfigJsonIn { string key = 1; - bytes val = 2; + bytes value_json = 2; } enum StockNoteType { diff --git a/pylib/Makefile b/pylib/Makefile index 91c28c1de..c02b2ff0c 100644 --- a/pylib/Makefile +++ b/pylib/Makefile @@ -46,12 +46,12 @@ PROTODEPS := ../proto/backend.proto ../proto/fluent.proto perl -i'' -pe 's/from fluent_pb2/from anki.fluent_pb2/' anki/backend_pb2.pyi perl -i'' -pe 's/import fluent_pb2/import anki.fluent_pb2/' anki/backend_pb2.py python tools/genbackend.py - python -m black anki/rsbackend.py + python -m black -t py36 anki/rsbackend.py @touch $@ .build/hooks: tools/genhooks.py tools/hookslib.py python tools/genhooks.py - python -m black anki/hooks.py + python -m black -t py36 anki/hooks.py @touch $@ BUILD_STEPS := .build/vernum .build/run-deps .build/dev-deps anki/buildinfo.py .build/py-proto .build/hooks diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 2a1b1da33..0acecaafc 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -26,13 +26,7 @@ from anki.lang import _ from anki.media import MediaManager, media_paths_from_col_path from anki.models import ModelManager from anki.notes import Note -from anki.rsbackend import ( - TR, - DBError, - FormatTimeSpanContext, - RustBackend, - pb, -) +from anki.rsbackend import TR, DBError, FormatTimeSpanContext, RustBackend, pb from anki.sched import Scheduler as V1Scheduler from anki.schedv2 import Scheduler as V2Scheduler from anki.tags import TagManager @@ -86,7 +80,7 @@ class Collection: seconds: float, context: FormatTimeSpanContext = FormatTimeSpanContext.INTERVALS, ) -> str: - return self.backend.format_timespan(seconds, context) + return self.backend.format_timespan(seconds=seconds, context=context) # Scheduler ########################################################################## @@ -246,7 +240,12 @@ class Collection: log_path = self.path.replace(".anki2", "2.log") # connect - self.backend.open_collection(self.path, media_dir, media_db, log_path) + self.backend.open_collection( + collection_path=self.path, + media_folder_path=media_dir, + media_db_path=media_db, + log_path=log_path, + ) self.db = DBProxy(weakref.proxy(self.backend)) self.db.begin() @@ -317,7 +316,7 @@ class Collection: return Note(self, self.models.current(forDeck)) def add_note(self, note: Note, deck_id: int) -> None: - note.id = self.backend.add_note(note.to_backend_note(), deck_id) + note.id = self.backend.add_note(note=note.to_backend_note(), deck_id=deck_id) def addNote(self, note: Note) -> int: self.add_note(note, note.model()["did"]) @@ -423,7 +422,7 @@ select id from notes where id in %s and id not in (select nid from cards)""" mode = pb.SortOrder( builtin=pb.BuiltinSearchOrder(kind=kind, reverse=reverse) ) - return self.backend.search_cards(query, mode) + return self.backend.search_cards(search=query, order=mode) def find_notes(self, query: str) -> Sequence[int]: return self.backend.search_notes(query) diff --git a/pylib/anki/config.py b/pylib/anki/config.py index 0c853c629..7ae6a98ab 100644 --- a/pylib/anki/config.py +++ b/pylib/anki/config.py @@ -37,7 +37,7 @@ class ConfigManager: raise KeyError def set(self, key: str, val: Any) -> None: - self.col.backend.set_config_json(key, to_json_bytes(val)) + self.col.backend.set_config_json(key=key, value_json=to_json_bytes(val)) def remove(self, key: str) -> None: self.col.backend.remove_config(key) diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index b6d3320ca..29ce7fe5d 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -205,7 +205,7 @@ class DeckManager: "Add or update an existing deck. Used for syncing and merging." try: g["id"] = self.col.backend.add_or_update_deck_legacy( - to_json_bytes(g), preserve_usn + deck=to_json_bytes(g), preserve_usn_and_mtime=preserve_usn ) except anki.rsbackend.DeckIsFilteredError: raise DeckRenameError("deck was filtered") @@ -284,7 +284,7 @@ class DeckManager: def update_config(self, conf: Dict[str, Any], preserve_usn=False) -> None: conf["id"] = self.col.backend.add_or_update_deck_config_legacy( - to_json_bytes(conf), preserve_usn + config=to_json_bytes(conf), preserve_usn_and_mtime=preserve_usn ) def add_config( diff --git a/pylib/anki/find.py b/pylib/anki/find.py index 11cfc2e30..33c3ca838 100644 --- a/pylib/anki/find.py +++ b/pylib/anki/find.py @@ -38,7 +38,14 @@ def findReplace( fold: bool = True, ) -> int: "Find and replace fields in a note. Returns changed note count." - return col.backend.find_and_replace(nids, src, dst, regex, not fold, field) + return col.backend.find_and_replace( + nids=nids, + search=src, + replacement=dst, + regex=regex, + match_case=not fold, + field_name=field, + ) def fieldNamesForNotes(col: Collection, nids: List[int]) -> List[str]: diff --git a/pylib/anki/latex.py b/pylib/anki/latex.py index ba6aaff21..4f8f3d8a2 100644 --- a/pylib/anki/latex.py +++ b/pylib/anki/latex.py @@ -86,7 +86,7 @@ def render_latex_returning_errors( header = model["latexPre"] footer = model["latexPost"] - proto = col.backend.extract_latex(html, svg, expand_clozes) + proto = col.backend.extract_latex(text=html, svg=svg, expand_clozes=expand_clozes) out = ExtractedLatexOutput.from_proto(proto) errors = [] html = out.html diff --git a/pylib/anki/media.py b/pylib/anki/media.py index 4dc7eaf07..6070599d5 100644 --- a/pylib/anki/media.py +++ b/pylib/anki/media.py @@ -96,7 +96,7 @@ class MediaManager: """Write the file to the media folder, renaming if not unique. Returns possibly-renamed filename.""" - return self.col.backend.add_media_file(desired_fname, data) + return self.col.backend.add_media_file(desired_name=desired_fname, data=data) def add_extension_based_on_mime(self, fname: str, content_type: str) -> str: "If jpg or png mime, add .png/.jpg if missing extension." diff --git a/pylib/anki/models.py b/pylib/anki/models.py index 6cbc2eec3..04ac5061c 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -223,7 +223,7 @@ class ModelManager: self._remove_from_cache(m["id"]) self.ensureNameUnique(m) m["id"] = self.col.backend.add_or_update_notetype( - to_json_bytes(m), preserve_usn_and_mtime=preserve_usn + json=to_json_bytes(m), preserve_usn_and_mtime=preserve_usn ) self.setCurrent(m) self._mutate_after_write(m) diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 06f11024b..fe7cd2fa8 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -15,19 +15,7 @@ import enum import json import os from dataclasses import dataclass -from typing import ( - Any, - Callable, - Dict, - Iterable, - List, - NewType, - NoReturn, - Optional, - Sequence, - Tuple, - Union, -) +from typing import Any, Dict, List, Optional, Sequence, Union import ankirspy # pytype: disable=import-error @@ -37,9 +25,7 @@ from anki import hooks from anki.dbproxy import Row as DBRow from anki.dbproxy import ValueForDB from anki.fluent_pb2 import FluentString as TR -from anki.sound import AVTag, SoundOrVideoTag, TTSTag from anki.types import assert_impossible_literal -from anki.utils import intTime assert ankirspy.buildhash() == anki.buildinfo.buildhash @@ -54,19 +40,13 @@ StockNoteType = pb.StockNoteType try: import orjson + + to_json_bytes = orjson.dumps + from_json_bytes = orjson.loads except: # add compat layer for 32 bit builds that can't use orjson - print("reverting to stock json") - - class orjson: # type: ignore - def dumps(obj: Any) -> bytes: - return json.dumps(obj).encode("utf8") - - loads = json.loads - - -to_json_bytes = orjson.dumps -from_json_bytes = orjson.loads + to_json_bytes = lambda obj: json.dumps(obj).encode("utf8") # type: ignore + from_json_bytes = json.loads class Interrupted(Exception): @@ -223,7 +203,7 @@ class RustBackend: return self._db_command(dict(kind="rollback")) def _db_command(self, input: Dict[str, Any]) -> Any: - return orjson.loads(self._backend.db_command(orjson.dumps(input))) + return from_json_bytes(self._backend.db_command(to_json_bytes(input))) def translate(self, key: TR, **kwargs: Union[str, int, float]) -> str: return self.translate_string(translate_string_in(key, **kwargs)) @@ -236,7 +216,7 @@ class RustBackend: print( "please use col.format_timespan() instead of col.backend.format_time_span()" ) - return self.format_timespan(seconds, context) + return self.format_timespan(seconds=seconds, context=context) def _run_command(self, method: int, input: Any) -> bytes: input_bytes = input.SerializeToString() @@ -253,14 +233,14 @@ class RustBackend: # @@AUTOGEN@@ - def extract_av_tags(self, text: str, question_side: bool) -> pb.ExtractAVTagsOut: + def extract_av_tags(self, *, text: str, question_side: bool) -> pb.ExtractAVTagsOut: input = pb.ExtractAVTagsIn(text=text, question_side=question_side) output = pb.ExtractAVTagsOut() output.ParseFromString(self._run_command(1, input)) return output def extract_latex( - self, text: str, svg: bool, expand_clozes: bool + self, *, text: str, svg: bool, expand_clozes: bool ) -> pb.ExtractLatexOut: input = pb.ExtractLatexIn(text=text, svg=svg, expand_clozes=expand_clozes) output = pb.ExtractLatexOut() @@ -273,14 +253,14 @@ class RustBackend: output.ParseFromString(self._run_command(3, input)) return output - def render_existing_card(self, card_id: int, browser: bool) -> pb.RenderCardOut: + def render_existing_card(self, *, card_id: int, browser: bool) -> pb.RenderCardOut: input = pb.RenderExistingCardIn(card_id=card_id, browser=browser) output = pb.RenderCardOut() output.ParseFromString(self._run_command(4, input)) return output def render_uncommitted_card( - self, note: pb.Note, card_ord: int, template: bytes, fill_empty: bool + self, *, note: pb.Note, card_ord: int, template: bytes, fill_empty: bool ) -> pb.RenderCardOut: input = pb.RenderUncommittedCardIn( note=note, card_ord=card_ord, template=template, fill_empty=fill_empty @@ -295,7 +275,7 @@ class RustBackend: output.ParseFromString(self._run_command(6, input)) return output.val - def search_cards(self, search: str, order: pb.SortOrder) -> Sequence[int]: + def search_cards(self, *, search: str, order: pb.SortOrder) -> Sequence[int]: input = pb.SearchCardsIn(search=search, order=order) output = pb.SearchCardsOut() output.ParseFromString(self._run_command(7, input)) @@ -309,6 +289,7 @@ class RustBackend: def find_and_replace( self, + *, nids: Sequence[int], search: str, replacement: str, @@ -346,13 +327,13 @@ class RustBackend: output.ParseFromString(self._run_command(12, input)) return output - def studied_today(self, cards: int, seconds: float) -> str: + def studied_today(self, *, cards: int, seconds: float) -> str: input = pb.StudiedTodayIn(cards=cards, seconds=seconds) output = pb.String() output.ParseFromString(self._run_command(13, input)) return output.val - def congrats_learn_message(self, next_due: float, remaining: int) -> str: + def congrats_learn_message(self, *, next_due: float, remaining: int) -> str: input = pb.CongratsLearnMessageIn(next_due=next_due, remaining=remaining) output = pb.String() output.ParseFromString(self._run_command(14, input)) @@ -370,7 +351,7 @@ class RustBackend: output.ParseFromString(self._run_command(16, input)) return output - def add_media_file(self, desired_name: str, data: bytes) -> str: + def add_media_file(self, *, desired_name: str, data: bytes) -> str: input = pb.AddMediaFileIn(desired_name=desired_name, data=data) output = pb.String() output.ParseFromString(self._run_command(17, input)) @@ -389,7 +370,7 @@ class RustBackend: return output def add_or_update_deck_legacy( - self, deck: bytes, preserve_usn_and_mtime: bool + self, *, deck: bytes, preserve_usn_and_mtime: bool ) -> int: input = pb.AddOrUpdateDeckLegacyIn( deck=deck, preserve_usn_and_mtime=preserve_usn_and_mtime @@ -398,7 +379,7 @@ class RustBackend: output.ParseFromString(self._run_command(20, input)) return output.did - def deck_tree(self, include_counts: bool, top_deck_id: int) -> pb.DeckTreeNode: + def deck_tree(self, *, include_counts: bool, top_deck_id: int) -> pb.DeckTreeNode: input = pb.DeckTreeIn(include_counts=include_counts, top_deck_id=top_deck_id) output = pb.DeckTreeNode() output.ParseFromString(self._run_command(21, input)) @@ -429,7 +410,7 @@ class RustBackend: return output.json def get_deck_names( - self, skip_empty_default: bool, include_filtered: bool + self, *, skip_empty_default: bool, include_filtered: bool ) -> Sequence[pb.DeckNameID]: input = pb.GetDeckNamesIn( skip_empty_default=skip_empty_default, include_filtered=include_filtered @@ -451,7 +432,7 @@ class RustBackend: return output def add_or_update_deck_config_legacy( - self, config: bytes, preserve_usn_and_mtime: bool + self, *, config: bytes, preserve_usn_and_mtime: bool ) -> int: input = pb.AddOrUpdateDeckConfigLegacyIn( config=config, preserve_usn_and_mtime=preserve_usn_and_mtime @@ -506,7 +487,7 @@ class RustBackend: output.ParseFromString(self._run_command(37, input)) return output - def add_note(self, note: pb.Note, deck_id: int) -> int: + def add_note(self, *, note: pb.Note, deck_id: int) -> int: input = pb.AddNoteIn(note=note, deck_id=deck_id) output = pb.NoteID() output.ParseFromString(self._run_command(38, input)) @@ -523,14 +504,14 @@ class RustBackend: output.ParseFromString(self._run_command(40, input)) return output - def add_note_tags(self, nids: Sequence[int], tags: str) -> int: + def add_note_tags(self, *, nids: Sequence[int], tags: str) -> int: input = pb.AddNoteTagsIn(nids=nids, tags=tags) output = pb.UInt32() output.ParseFromString(self._run_command(41, input)) return output.val def update_note_tags( - self, nids: Sequence[int], tags: str, replacement: str, regex: bool + self, *, nids: Sequence[int], tags: str, replacement: str, regex: bool ) -> int: input = pb.UpdateNoteTagsIn( nids=nids, tags=tags, replacement=replacement, regex=regex @@ -545,7 +526,7 @@ class RustBackend: return output.numbers def after_note_updates( - self, nids: Sequence[int], mark_notes_modified: bool, generate_cards: bool + self, *, nids: Sequence[int], mark_notes_modified: bool, generate_cards: bool ) -> pb.Empty: input = pb.AfterNoteUpdatesIn( nids=nids, @@ -562,7 +543,9 @@ class RustBackend: output.ParseFromString(self._run_command(45, input)) return output.fields - def add_or_update_notetype(self, json: bytes, preserve_usn_and_mtime: bool) -> int: + def add_or_update_notetype( + self, *, json: bytes, preserve_usn_and_mtime: bool + ) -> int: input = pb.AddOrUpdateNotetypeIn( json=json, preserve_usn_and_mtime=preserve_usn_and_mtime ) @@ -608,6 +591,7 @@ class RustBackend: def open_collection( self, + *, collection_path: str, media_folder_path: str, media_db_path: str, @@ -635,7 +619,7 @@ class RustBackend: output.ParseFromString(self._run_command(55, input)) return output.problems - def sync_media(self, hkey: str, endpoint: str) -> pb.Empty: + def sync_media(self, *, hkey: str, endpoint: str) -> pb.Empty: input = pb.SyncMediaIn(hkey=hkey, endpoint=endpoint) output = pb.Empty() output.ParseFromString(self._run_command(56, input)) @@ -659,7 +643,7 @@ class RustBackend: return output.val def format_timespan( - self, seconds: float, context: pb.FormatTimespanIn.Context + self, *, seconds: float, context: pb.FormatTimespanIn.Context ) -> str: input = pb.FormatTimespanIn(seconds=seconds, context=context) output = pb.String() @@ -667,7 +651,7 @@ class RustBackend: return output.val def register_tags( - self, tags: str, preserve_usn: bool, usn: int, clear_first: bool + self, *, tags: str, preserve_usn: bool, usn: int, clear_first: bool ) -> bool: input = pb.RegisterTagsIn( tags=tags, preserve_usn=preserve_usn, usn=usn, clear_first=clear_first @@ -694,8 +678,8 @@ class RustBackend: output.ParseFromString(self._run_command(64, input)) return output.json - def set_config_json(self, key: str, val: bytes) -> pb.Empty: - input = pb.SetConfigJsonIn(key=key, val=val) + def set_config_json(self, *, key: str, value_json: bytes) -> pb.Empty: + input = pb.SetConfigJsonIn(key=key, value_json=value_json) output = pb.Empty() output.ParseFromString(self._run_command(65, input)) return output diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index 63ce56e13..cab024770 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -1270,7 +1270,9 @@ from cards where did in {dids} and queue = {QUEUE_TYPE_LRN} remaining = remaining or 0 if next and next < self.dayCutoff: next -= intTime() - self.col.conf["collapseTime"] - return self.col.backend.congrats_learn_message(abs(next), remaining) + return self.col.backend.congrats_learn_message( + next_due=abs(next), remaining=remaining + ) else: return "" diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index d6e330854..1748bc303 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -177,7 +177,7 @@ from revlog where id > ? """ return "" + str(s) + "" if cards: - b += self.col.backend.studied_today(cards, float(thetime)) + b += self.col.backend.studied_today(cards=cards, seconds=float(thetime)) # again/pass count b += "
" + _("Again count: %s") % bold(failed) if cards: diff --git a/pylib/anki/tags.py b/pylib/anki/tags.py index c6aa7d910..e34863ea8 100644 --- a/pylib/anki/tags.py +++ b/pylib/anki/tags.py @@ -83,14 +83,16 @@ class TagManager: def bulk_add(self, nids: List[int], tags: str) -> int: """Add space-separate tags to provided notes, returning changed count.""" - return self.col.backend.add_note_tags(nids, tags) + return self.col.backend.add_note_tags(nids=nids, tags=tags) def bulk_update( self, nids: List[int], tags: str, replacement: str, regex: bool ) -> int: """Replace space-separated tags, returning changed count. Tags replaced with an empty string will be removed.""" - return self.col.backend.update_note_tags(nids, tags, replacement, regex) + return self.col.backend.update_note_tags( + nids=nids, tags=tags, replacement=replacement, regex=regex + ) # legacy routines diff --git a/pylib/anki/template.py b/pylib/anki/template.py index 4d54208ed..8be0846da 100644 --- a/pylib/anki/template.py +++ b/pylib/anki/template.py @@ -215,10 +215,10 @@ class TemplateRenderContext: ) qtext = apply_custom_filters(partial.qnodes, self, front_side=None) - qout = self.col().backend.extract_av_tags(qtext, True) + qout = self.col().backend.extract_av_tags(text=qtext, question_side=True) atext = apply_custom_filters(partial.anodes, self, front_side=qtext) - aout = self.col().backend.extract_av_tags(atext, False) + aout = self.col().backend.extract_av_tags(text=atext, question_side=False) output = TemplateRenderOutput( question_text=qout.text, @@ -237,14 +237,16 @@ class TemplateRenderContext: if self._template: # card layout screen out = self._col.backend.render_uncommitted_card( - self._note.to_backend_note(), - self._card.ord, - to_json_bytes(self._template), - self._fill_empty, + note=self._note.to_backend_note(), + card_ord=self._card.ord, + template=to_json_bytes(self._template), + fill_empty=self._fill_empty, ) else: # existing card (eg study mode) - out = self._col.backend.render_existing_card(self._card.id, self._browser) + out = self._col.backend.render_existing_card( + card_id=self._card.id, browser=self._browser + ) return PartiallyRenderedCard.from_proto(out) diff --git a/pylib/tools/genbackend.py b/pylib/tools/genbackend.py index 862595966..476098aad 100755 --- a/pylib/tools/genbackend.py +++ b/pylib/tools/genbackend.py @@ -80,7 +80,10 @@ def fix_snakecase(name): def get_input_args(msg): fields = sorted(msg.fields, key=lambda x: x.number) - return ", ".join(["self"] + [f"{f.name}: {python_type(f)}" for f in fields]) + self_star = ["self"] + if len(fields) >= 2: + self_star.append("*") + return ", ".join(self_star + [f"{f.name}: {python_type(f)}" for f in fields]) def get_input_assign(msg): diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py index 7eed73c2c..29f3c6496 100644 --- a/qt/aqt/clayout.py +++ b/qt/aqt/clayout.py @@ -304,7 +304,7 @@ class CardLayout(QDialog): self.preview_web.set_bridge_command(self._on_bridge_cmd, self) if self._isCloze(): - nums = self.note.cloze_numbers_in_fields() + nums = list(self.note.cloze_numbers_in_fields()) if self.ord + 1 not in nums: # current card is empty nums.append(self.ord + 1) diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index a14b27f89..ce0d5c486 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -145,7 +145,7 @@ where id > ?""", ) cards = cards or 0 thetime = thetime or 0 - buf = self.mw.col.backend.studied_today(cards, float(thetime)) + buf = self.mw.col.backend.studied_today(cards=cards, seconds=float(thetime)) return buf def _renderDeckTree(self, top: DeckTreeNode) -> str: diff --git a/qt/aqt/legacy.py b/qt/aqt/legacy.py index 96d458079..a2403685f 100644 --- a/qt/aqt/legacy.py +++ b/qt/aqt/legacy.py @@ -25,7 +25,7 @@ def bodyClass(col, card) -> str: def allSounds(text) -> List: print("allSounds() deprecated") - out = aqt.mw.col.backend.extract_av_tags(text, True) + out = aqt.mw.col.backend.extract_av_tags(text=text, question_side=True) return [ x.filename for x in av_tags_to_native(out.av_tags) diff --git a/qt/aqt/mediasync.py b/qt/aqt/mediasync.py index 4b77a8559..7a81b6923 100644 --- a/qt/aqt/mediasync.py +++ b/qt/aqt/mediasync.py @@ -74,7 +74,7 @@ class MediaSyncer: gui_hooks.media_sync_did_start_or_stop(True) def run() -> None: - self.mw.col.backend.sync_media(hkey, self._endpoint()) + self.mw.col.backend.sync_media(hkey=hkey, endpoint=self._endpoint()) self.mw.taskman.run_in_background(run, self._on_finished) diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 1688c341e..f390d33de 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -89,7 +89,7 @@ class Preferences(QDialog): f.useCurrent.setCurrentIndex(int(not qc.get("addToCur", True))) - s = self.prefs.sched + s = self.prefs f.lrnCutoff.setValue(s.learn_ahead_secs / 60.0) f.timeLimit.setValue(s.time_limit_secs / 60.0) f.showEstimates.setChecked(s.show_intervals_on_buttons) @@ -122,7 +122,7 @@ class Preferences(QDialog): qc = d.conf qc["addToCur"] = not f.useCurrent.currentIndex() - s = self.prefs.sched + s = self.prefs s.show_remaining_due_counts = f.showProgress.isChecked() s.show_intervals_on_buttons = f.showEstimates.isChecked() s.new_review_mix = f.newSpread.currentIndex() diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 29705c034..3d5276f07 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -1028,7 +1028,7 @@ impl BackendService for Backend { self.with_col(|col| { col.transact(None, |col| { // ensure it's a well-formed object - let val: JsonValue = serde_json::from_slice(&input.val)?; + let val: JsonValue = serde_json::from_slice(&input.value_json)?; col.set_config(input.key.as_str(), &val) }) })