anki/rslib/backend.proto

1592 lines
34 KiB
Protocol Buffer
Raw Normal View History

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
syntax = "proto3";
package BackendProto;
2020-05-22 13:25:25 +02:00
// Generic containers
///////////////////////////////////////////////////////////
message Empty {}
message OptionalInt32 {
sint32 val = 1;
}
message OptionalUInt32 {
uint32 val = 1;
}
2020-05-22 13:25:25 +02:00
message Int32 {
sint32 val = 1;
2020-05-22 13:25:25 +02:00
}
2020-05-23 08:19:48 +02:00
message UInt32 {
uint32 val = 1;
2020-05-23 08:19:48 +02:00
}
2020-05-22 13:25:25 +02:00
message Int64 {
int64 val = 1;
2020-05-22 13:25:25 +02:00
}
message String {
string val = 1;
2020-05-22 13:25:25 +02:00
}
2020-05-23 08:56:05 +02:00
message Json {
bytes json = 1;
2020-05-23 04:58:13 +02:00
}
2020-05-23 07:09:16 +02:00
message Bool {
bool val = 1;
2020-05-23 07:09:16 +02:00
}
message StringList {
repeated string vals = 1;
}
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
message OpChangesWithCount {
uint32 count = 1;
OpChanges changes = 2;
}
message OpChangesWithId {
2021-03-22 14:17:07 +01:00
int64 id = 1;
OpChanges changes = 2;
}
2020-05-23 08:56:05 +02:00
// IDs used in RPC calls
///////////////////////////////////////////////////////////
message NotetypeId {
int64 ntid = 1;
2020-05-23 08:19:48 +02:00
}
message NoteId {
int64 nid = 1;
2020-05-23 08:19:48 +02:00
}
message CardId {
int64 cid = 1;
2020-05-23 08:19:48 +02:00
}
message CardIds {
repeated int64 cids = 1;
}
message DeckId {
int64 did = 1;
2020-05-23 08:56:05 +02:00
}
message DeckIds {
repeated int64 dids = 1;
}
message DeckConfigId {
int64 dcid = 1;
2020-05-23 08:56:05 +02:00
}
2020-05-23 08:19:48 +02:00
// Backend methods
2020-05-22 13:25:25 +02:00
///////////////////////////////////////////////////////////
/// while the protobuf descriptors expose the order services are defined in,
/// that information is not available in prost, so we define an enum to make
/// sure all clients agree on the service index
enum ServiceIndex {
SERVICE_INDEX_SCHEDULING = 0;
SERVICE_INDEX_DECKS = 1;
SERVICE_INDEX_NOTES = 2;
SERVICE_INDEX_SYNC = 3;
SERVICE_INDEX_NOTETYPES = 4;
SERVICE_INDEX_CONFIG = 5;
SERVICE_INDEX_CARD_RENDERING = 6;
SERVICE_INDEX_DECK_CONFIG = 7;
SERVICE_INDEX_TAGS = 8;
2021-03-11 07:16:40 +01:00
SERVICE_INDEX_SEARCH = 9;
SERVICE_INDEX_STATS = 10;
SERVICE_INDEX_MEDIA = 11;
SERVICE_INDEX_I18N = 12;
SERVICE_INDEX_COLLECTION = 13;
SERVICE_INDEX_CARDS = 14;
}
service SchedulingService {
rpc SchedTimingToday(Empty) returns (SchedTimingTodayOut);
rpc StudiedToday(Empty) returns (String);
rpc StudiedTodayMessage(StudiedTodayMessageIn) returns (String);
rpc UpdateStats(UpdateStatsIn) returns (Empty);
rpc ExtendLimits(ExtendLimitsIn) returns (Empty);
rpc CountsForDeckToday(DeckId) returns (CountsForDeckTodayOut);
rpc CongratsInfo(Empty) returns (CongratsInfoOut);
rpc RestoreBuriedAndSuspendedCards(CardIds) returns (OpChanges);
rpc UnburyDeck(UnburyDeckIn) returns (OpChanges);
rpc BuryOrSuspendCards(BuryOrSuspendCardsIn) returns (OpChangesWithCount);
rpc EmptyFilteredDeck(DeckId) returns (OpChanges);
rpc RebuildFilteredDeck(DeckId) returns (OpChangesWithCount);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc ScheduleCardsAsNew(ScheduleCardsAsNewIn) returns (OpChanges);
rpc SetDueDate(SetDueDateIn) returns (OpChanges);
2021-03-18 02:46:11 +01:00
rpc SortCards(SortCardsIn) returns (OpChangesWithCount);
rpc SortDeck(SortDeckIn) returns (OpChangesWithCount);
rpc GetNextCardStates(CardId) returns (NextCardStates);
rpc DescribeNextStates(NextCardStates) returns (StringList);
rpc StateIsLeech(SchedulingState) returns (Bool);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc AnswerCard(AnswerCardIn) returns (OpChanges);
rpc UpgradeScheduler(Empty) returns (Empty);
rpc GetQueuedCards(GetQueuedCardsIn) returns (GetQueuedCardsOut);
}
service DecksService {
rpc AddDeckLegacy(Json) returns (OpChangesWithId);
rpc AddOrUpdateDeckLegacy(AddOrUpdateDeckLegacyIn) returns (DeckId);
rpc DeckTree(DeckTreeIn) returns (DeckTreeNode);
rpc DeckTreeLegacy(Empty) returns (Json);
rpc GetAllDecksLegacy(Empty) returns (Json);
rpc GetDeckIdByName(String) returns (DeckId);
rpc GetDeck(DeckId) returns (Deck);
rpc UpdateDeck(Deck) returns (OpChanges);
rpc SetDeckCollapsed(SetDeckCollapsedIn) returns (OpChanges);
rpc GetDeckLegacy(DeckId) returns (Json);
rpc GetDeckNames(GetDeckNamesIn) returns (DeckNames);
rpc NewDeckLegacy(Bool) returns (Json);
rpc RemoveDecks(DeckIds) returns (OpChangesWithCount);
2021-03-22 09:23:56 +01:00
rpc ReparentDecks(ReparentDecksIn) returns (OpChangesWithCount);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc RenameDeck(RenameDeckIn) returns (OpChanges);
rpc GetOrCreateFilteredDeck(DeckId) returns (FilteredDeckForUpdate);
rpc AddOrUpdateFilteredDeck(FilteredDeckForUpdate) returns (OpChangesWithId);
rpc FilteredDeckOrderLabels(Empty) returns (StringList);
rpc SetCurrentDeck(DeckId) returns (OpChanges);
rpc GetCurrentDeck(Empty) returns (Deck);
}
service NotesService {
rpc NewNote(NotetypeId) returns (Note);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc AddNote(AddNoteIn) returns (AddNoteOut);
Simplify note adding and the deck/notetype choosers The existing code was really difficult to reason about: - The default notetype depended on the selected deck, and vice versa, and this logic was buried in the deck and notetype choosing screens, and models.py. - Changes to the notetype were not passed back directly, but were fired via a hook, which changed any screen in the app that had a notetype selector. It also wasn't great for performance, as the most recent deck and tags were embedded in the notetype, which can be expensive to save and sync for large notetypes. To address these points: - The current deck for a notetype, and notetype for a deck, are now stored in separate config variables, instead of directly in the deck or notetype. These are cheap to read and write, and we'll be able to sync them individually in the future once config syncing is updated in the future. I seem to recall some users not wanting the tag saving behaviour, so I've dropped that for now, but if people end up missing it, it would be simple to add as an extra auxiliary config variable. - The logic for getting the starting deck and notetype has been moved into the backend. It should be the same as the older Python code, with one exception: when "change deck depending on notetype" is enabled in the preferences, it will start with the current notetype ("curModel"), instead of first trying to get a deck-specific notetype. - ModelChooser has been duplicated into notetypechooser.py, and it has been updated to solely be concerned with keeping track of a selected notetype - it no longer alters global state.
2021-03-08 14:23:24 +01:00
rpc DefaultsForAdding(DefaultsForAddingIn) returns (DeckAndNotetype);
rpc DefaultDeckForNotetype(NotetypeId) returns (DeckId);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc UpdateNote(UpdateNoteIn) returns (OpChanges);
rpc GetNote(NoteId) returns (Note);
rpc RemoveNotes(RemoveNotesIn) returns (OpChangesWithCount);
rpc ClozeNumbersInNote(Note) returns (ClozeNumbersInNoteOut);
rpc AfterNoteUpdates(AfterNoteUpdatesIn) returns (OpChangesWithCount);
rpc FieldNamesForNotes(FieldNamesForNotesIn) returns (FieldNamesForNotesOut);
rpc NoteIsDuplicateOrEmpty(Note) returns (NoteIsDuplicateOrEmptyOut);
rpc CardsOfNote(NoteId) returns (CardIds);
}
service SyncService {
rpc SyncMedia(SyncAuth) returns (Empty);
rpc AbortSync(Empty) returns (Empty);
rpc AbortMediaSync(Empty) returns (Empty);
rpc BeforeUpload(Empty) returns (Empty);
rpc SyncLogin(SyncLoginIn) returns (SyncAuth);
rpc SyncStatus(SyncAuth) returns (SyncStatusOut);
rpc SyncCollection(SyncAuth) returns (SyncCollectionOut);
rpc FullUpload(SyncAuth) returns (Empty);
rpc FullDownload(SyncAuth) returns (Empty);
2021-01-11 05:11:18 +01:00
rpc SyncServerMethod(SyncServerMethodIn) returns (Json);
}
service ConfigService {
rpc GetConfigJson(String) returns (Json);
rpc SetConfigJson(SetConfigJsonIn) returns (Empty);
rpc RemoveConfig(String) returns (Empty);
rpc GetAllConfig(Empty) returns (Json);
rpc GetConfigBool(Config.Bool) returns (Bool);
rpc SetConfigBool(SetConfigBoolIn) returns (Empty);
rpc GetConfigString(Config.String) returns (String);
rpc SetConfigString(SetConfigStringIn) returns (Empty);
rpc GetPreferences(Empty) returns (Preferences);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc SetPreferences(Preferences) returns (OpChanges);
}
service NotetypesService {
rpc AddNotetype(Notetype) returns (OpChangesWithId);
rpc UpdateNotetype(Notetype) returns (OpChanges);
rpc AddNotetypeLegacy(Json) returns (OpChangesWithId);
rpc UpdateNotetypeLegacy(Json) returns (OpChanges);
rpc AddOrUpdateNotetype(AddOrUpdateNotetypeIn) returns (NotetypeId);
rpc GetStockNotetypeLegacy(StockNotetype) returns (Json);
rpc GetNotetype(NotetypeId) returns (Notetype);
rpc GetNotetypeLegacy(NotetypeId) returns (Json);
rpc GetNotetypeNames(Empty) returns (NotetypeNames);
rpc GetNotetypeNamesAndCounts(Empty) returns (NotetypeUseCounts);
rpc GetNotetypeIdByName(String) returns (NotetypeId);
rpc RemoveNotetype(NotetypeId) returns (OpChanges);
}
service CardRenderingService {
rpc ExtractAVTags(ExtractAVTagsIn) returns (ExtractAVTagsOut);
rpc ExtractLatex(ExtractLatexIn) returns (ExtractLatexOut);
rpc GetEmptyCards(Empty) returns (EmptyCardsReport);
rpc RenderExistingCard(RenderExistingCardIn) returns (RenderCardOut);
rpc RenderUncommittedCard(RenderUncommittedCardIn) returns (RenderCardOut);
rpc StripAVTags(String) returns (String);
rpc RenderMarkdown(RenderMarkdownIn) returns (String);
}
service DeckConfigService {
rpc AddOrUpdateDeckConfigLegacy(AddOrUpdateDeckConfigLegacyIn)
returns (DeckConfigId);
rpc GetDeckConfig(DeckConfigId) returns (DeckConfig);
rpc AllDeckConfigLegacy(Empty) returns (Json);
rpc GetDeckConfigLegacy(DeckConfigId) returns (Json);
rpc NewDeckConfigLegacy(Empty) returns (Json);
rpc RemoveDeckConfig(DeckConfigId) returns (Empty);
rpc GetDeckConfigsForUpdate(DeckId) returns (DeckConfigsForUpdate);
rpc UpdateDeckConfigs(UpdateDeckConfigsIn) returns (OpChanges);
}
service TagsService {
clear_unused_tags and browser redraw improvements - clear_unused_tags() is now undoable, and returns the number of removed notes - add a new mw.query_op() helper for immutable queries - decouple "freeze/unfreeze ui state" hooks from the "interface update required" hook, so that the former is fired even on error, and can be made re-entrant - use a 'block_updates' flag in Python, instead of setUpdatesEnabled(), as the latter has the side-effect of preventing child windows like tooltips from appearing, and forces a full redrawn when updates are enabled again. The new behaviour leads to the card list blanking out when a long-running op is running, but in the future if we cache the cell values we can just display them from the cache instead. - we were indiscriminately saving the note with saveNow(), due to the call to saveTags(). Changed so that it only saves when the tags field is focused. - drain the "on_done" queue on main before launching a new background task, to lower the chances of something in on_done making a small query to the DB and hanging until a long op finishes - the duplicate check in the editor was executed after the webview loads, leading to it hanging until the sidebar finishes loading. Run it at set_note() time instead, so that the editor loads first. - don't throw an error when a long-running op started with with_progress() finishes after the window it was launched from has closed - don't throw an error when the browser is closed before the sidebar has finished loading
2021-03-17 12:27:42 +01:00
rpc ClearUnusedTags(Empty) returns (OpChangesWithCount);
rpc AllTags(Empty) returns (StringList);
rpc RemoveTags(String) returns (OpChangesWithCount);
rpc SetTagCollapsed(SetTagCollapsedIn) returns (OpChanges);
rpc TagTree(Empty) returns (TagTreeNode);
rpc ReparentTags(ReparentTagsIn) returns (OpChangesWithCount);
rpc RenameTags(RenameTagsIn) returns (OpChangesWithCount);
rpc AddNoteTags(NoteIdsAndTagsIn) returns (OpChangesWithCount);
rpc RemoveNoteTags(NoteIdsAndTagsIn) returns (OpChangesWithCount);
rpc FindAndReplaceTag(FindAndReplaceTagIn) returns (OpChangesWithCount);
}
2021-03-11 07:16:40 +01:00
service SearchService {
rpc BuildSearchString(SearchNode) returns (String);
rpc SearchCards(SearchIn) returns (SearchOut);
rpc SearchNotes(SearchIn) returns (SearchOut);
rpc JoinSearchNodes(JoinSearchNodesIn) returns (String);
rpc ReplaceSearchNode(ReplaceSearchNodeIn) returns (String);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc FindAndReplace(FindAndReplaceIn) returns (OpChangesWithCount);
rpc AllBrowserColumns(Empty) returns (BrowserColumns);
rpc BrowserRowForId(Int64) returns (BrowserRow);
rpc SetActiveBrowserColumns(StringList) returns (Empty);
2021-03-11 07:16:40 +01:00
}
2021-03-11 07:16:40 +01:00
service StatsService {
rpc CardStats(CardId) returns (String);
rpc Graphs(GraphsIn) returns (GraphsOut);
rpc GetGraphPreferences(Empty) returns (GraphPreferences);
rpc SetGraphPreferences(GraphPreferences) returns (Empty);
2021-03-11 07:16:40 +01:00
}
2021-03-11 07:16:40 +01:00
service MediaService {
rpc CheckMedia(Empty) returns (CheckMediaOut);
rpc TrashMediaFiles(TrashMediaFilesIn) returns (Empty);
rpc AddMediaFile(AddMediaFileIn) returns (String);
rpc EmptyTrash(Empty) returns (Empty);
rpc RestoreTrash(Empty) returns (Empty);
2021-03-11 07:16:40 +01:00
}
service I18nService {
rpc TranslateString(TranslateStringIn) returns (String);
rpc FormatTimespan(FormatTimespanIn) returns (String);
rpc I18nResources(I18nResourcesIn) returns (Json);
}
service CollectionService {
rpc OpenCollection(OpenCollectionIn) returns (Empty);
rpc CloseCollection(CloseCollectionIn) returns (Empty);
rpc CheckDatabase(Empty) returns (CheckDatabaseOut);
rpc GetUndoStatus(Empty) returns (UndoStatus);
rpc Undo(Empty) returns (OpChangesAfterUndo);
rpc Redo(Empty) returns (OpChangesAfterUndo);
rpc AddCustomUndoEntry(String) returns (UInt32);
rpc MergeUndoEntries(UInt32) returns (OpChanges);
rpc LatestProgress(Empty) returns (Progress);
rpc SetWantsAbort(Empty) returns (Empty);
}
service CardsService {
rpc GetCard(CardId) returns (Card);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc UpdateCard(UpdateCardIn) returns (OpChanges);
rpc RemoveCards(RemoveCardsIn) returns (Empty);
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
rpc SetDeck(SetDeckIn) returns (OpChanges);
rpc SetFlag(SetFlagIn) returns (OpChanges);
}
2020-05-18 00:57:30 +02:00
// Protobuf stored in .anki2 files
// These should be moved to a separate file in the future
///////////////////////////////////////////////////////////
message DeckConfig {
message Config {
enum NewCardOrder {
NEW_CARD_ORDER_DUE = 0;
NEW_CARD_ORDER_RANDOM = 1;
}
enum ReviewCardOrder {
REVIEW_CARD_ORDER_SHUFFLED_BY_DAY = 0;
REVIEW_CARD_ORDER_SHUFFLED = 1;
REVIEW_CARD_ORDER_INTERVALS_ASCENDING = 2;
REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 3;
}
enum ReviewMix {
REVIEW_MIX_MIX_WITH_REVIEWS = 0;
REVIEW_MIX_AFTER_REVIEWS = 1;
REVIEW_MIX_BEFORE_REVIEWS = 2;
}
enum LeechAction {
LEECH_ACTION_SUSPEND = 0;
LEECH_ACTION_TAG_ONLY = 1;
}
2020-05-18 00:57:30 +02:00
repeated float learn_steps = 1;
repeated float relearn_steps = 2;
2020-05-18 00:57:30 +02:00
reserved 3 to 8;
2020-05-18 00:57:30 +02:00
uint32 new_per_day = 9;
uint32 reviews_per_day = 10;
uint32 new_per_day_minimum = 29;
2020-05-18 00:57:30 +02:00
float initial_ease = 11;
float easy_multiplier = 12;
float hard_multiplier = 13;
float lapse_multiplier = 14;
float interval_multiplier = 15;
2020-05-18 00:57:30 +02:00
uint32 maximum_review_interval = 16;
uint32 minimum_lapse_interval = 17;
2020-05-18 00:57:30 +02:00
uint32 graduating_interval_good = 18;
uint32 graduating_interval_easy = 19;
2020-05-18 00:57:30 +02:00
NewCardOrder new_card_order = 20;
ReviewCardOrder review_order = 32;
ReviewMix new_mix = 30;
ReviewMix interday_learning_mix = 31;
2020-05-18 00:57:30 +02:00
LeechAction leech_action = 21;
uint32 leech_threshold = 22;
2020-05-18 00:57:30 +02:00
bool disable_autoplay = 23;
uint32 cap_answer_time_to_secs = 24;
bool show_timer = 25;
bool skip_question_when_replaying_answer = 26;
2020-05-18 00:57:30 +02:00
bool bury_new = 27;
bool bury_reviews = 28;
2020-05-18 00:57:30 +02:00
bytes other = 255;
}
2020-05-18 00:57:30 +02:00
int64 id = 1;
string name = 2;
int64 mtime_secs = 3;
int32 usn = 4;
Config config = 5;
}
2020-05-18 00:57:30 +02:00
message Deck {
2021-04-04 13:15:30 +02:00
message Common {
bool study_collapsed = 1;
bool browser_collapsed = 2;
uint32 last_day_studied = 3;
int32 new_studied = 4;
int32 review_studied = 5;
int32 milliseconds_studied = 7;
// previously set in the v1 scheduler,
// but not currently used for anything
int32 learning_studied = 6;
reserved 8 to 13;
bytes other = 255;
}
message Normal {
int64 config_id = 1;
uint32 extend_new = 2;
uint32 extend_review = 3;
string description = 4;
bool markdown_description = 5;
reserved 6 to 11;
}
message Filtered {
message SearchTerm {
enum Order {
OLDEST_REVIEWED_FIRST = 0;
RANDOM = 1;
INTERVALS_ASCENDING = 2;
INTERVALS_DESCENDING = 3;
LAPSES = 4;
ADDED = 5;
DUE = 6;
REVERSE_ADDED = 7;
DUE_PRIORITY = 8;
}
string search = 1;
uint32 limit = 2;
Order order = 3;
}
bool reschedule = 1;
repeated SearchTerm search_terms = 2;
// v1 scheduler only
repeated float delays = 3;
// v2 scheduler only
uint32 preview_delay = 4;
}
// a container to store the deck specifics in the DB
// as a tagged enum
message KindContainer {
oneof kind {
Normal normal = 1;
Filtered filtered = 2;
}
}
int64 id = 1;
string name = 2;
2021-03-22 14:17:07 +01:00
int64 mtime_secs = 3;
int32 usn = 4;
2021-04-04 13:15:30 +02:00
Common common = 5;
// the specifics are inlined here when sending data to clients,
// as otherwise an extra level of indirection would be required
oneof kind {
2021-04-04 13:15:30 +02:00
Normal normal = 6;
Filtered filtered = 7;
}
2020-05-18 00:57:30 +02:00
}
message Notetype {
2021-04-04 13:57:17 +02:00
message Config {
enum Kind {
KIND_NORMAL = 0;
KIND_CLOZE = 1;
}
message CardRequirement {
enum Kind {
KIND_NONE = 0;
KIND_ANY = 1;
KIND_ALL = 2;
}
uint32 card_ord = 1;
Kind kind = 2;
repeated uint32 field_ords = 3;
}
2020-05-18 00:57:30 +02:00
2021-04-04 13:57:17 +02:00
Kind kind = 1;
uint32 sort_field_idx = 2;
string css = 3;
/// This is now stored separately; retrieve with DefaultsForAdding()
int64 target_deck_id_unused = 4;
2021-04-04 13:57:17 +02:00
string latex_pre = 5;
string latex_post = 6;
bool latex_svg = 7;
repeated CardRequirement reqs = 8;
2020-05-18 00:57:30 +02:00
2021-04-04 13:57:17 +02:00
bytes other = 255;
}
message Field {
message Config {
bool sticky = 1;
bool rtl = 2;
string font_name = 3;
uint32 font_size = 4;
bytes other = 255;
}
OptionalUInt32 ord = 1;
string name = 2;
Config config = 5;
}
message Template {
message Config {
string q_format = 1;
string a_format = 2;
string q_format_browser = 3;
string a_format_browser = 4;
int64 target_deck_id = 5;
string browser_font_name = 6;
uint32 browser_font_size = 7;
bytes other = 255;
}
OptionalUInt32 ord = 1;
string name = 2;
int64 mtime_secs = 3;
2021-04-04 13:57:17 +02:00
sint32 usn = 4;
Config config = 5;
}
int64 id = 1;
string name = 2;
int64 mtime_secs = 3;
sint32 usn = 4;
2021-04-04 13:57:17 +02:00
Config config = 7;
repeated Field fields = 8;
repeated Template templates = 9;
2020-05-18 00:57:30 +02:00
}
// Database objects
///////////////////////////////////////////////////////////
2020-05-18 00:57:30 +02:00
message Note {
int64 id = 1;
string guid = 2;
int64 notetype_id = 3;
uint32 mtime_secs = 4;
int32 usn = 5;
repeated string tags = 6;
repeated string fields = 7;
2020-05-18 00:57:30 +02:00
}
message Card {
int64 id = 1;
int64 note_id = 2;
int64 deck_id = 3;
uint32 template_idx = 4;
int64 mtime_secs = 5;
sint32 usn = 6;
uint32 ctype = 7;
sint32 queue = 8;
sint32 due = 9;
uint32 interval = 10;
uint32 ease_factor = 11;
uint32 reps = 12;
uint32 lapses = 13;
uint32 remaining_steps = 14;
sint32 original_due = 15;
int64 original_deck_id = 16;
uint32 flags = 17;
string data = 18;
2020-05-18 00:57:30 +02:00
}
// Backend
///////////////////////////////////////////////////////////
message BackendInit {
repeated string preferred_langs = 1;
string locale_folder_path = 2;
bool server = 3;
}
message I18nBackendInit {
repeated string preferred_langs = 4;
string locale_folder_path = 5;
}
2020-05-24 00:36:50 +02:00
// Errors
2020-05-22 13:25:25 +02:00
///////////////////////////////////////////////////////////
2019-12-24 23:59:33 +01:00
message BackendError {
enum Kind {
INVALID_INPUT = 0;
UNDO_EMPTY = 1;
INTERRUPTED = 2;
TEMPLATE_PARSE = 3;
IO_ERROR = 4;
DB_ERROR = 5;
NETWORK_ERROR = 6;
SYNC_AUTH_ERROR = 7;
SYNC_OTHER_ERROR = 8;
JSON_ERROR = 9;
PROTO_ERROR = 10;
NOT_FOUND_ERROR = 11;
EXISTS = 12;
FILTERED_DECK_ERROR = 13;
SEARCH_ERROR = 14;
}
// localized error description suitable for displaying to the user
string localized = 1;
// the error subtype
Kind kind = 2;
2020-02-04 10:39:31 +01:00
}
// Progress
///////////////////////////////////////////////////////////
message Progress {
message MediaSync {
string checked = 1;
string added = 2;
string removed = 3;
}
message FullSync {
uint32 transferred = 1;
uint32 total = 2;
}
message NormalSync {
string stage = 1;
string added = 2;
string removed = 3;
}
message DatabaseCheck {
string stage = 1;
uint32 stage_total = 2;
uint32 stage_current = 3;
}
oneof value {
Empty none = 1;
MediaSync media_sync = 2;
string media_check = 3;
FullSync full_sync = 4;
NormalSync normal_sync = 5;
DatabaseCheck database_check = 6;
}
2020-06-08 12:28:11 +02:00
}
2020-05-24 00:36:50 +02:00
// Messages
///////////////////////////////////////////////////////////
message SchedTimingTodayOut {
uint32 days_elapsed = 1;
int64 next_day_at = 2;
}
2020-01-06 03:18:20 +01:00
message DeckTreeIn {
// if non-zero, counts for the provided timestamp will be included
int64 now = 1;
int64 top_deck_id = 2;
2020-01-06 03:18:20 +01:00
}
message DeckTreeNode {
int64 deck_id = 1;
string name = 2;
repeated DeckTreeNode children = 3;
uint32 level = 4;
bool collapsed = 5;
uint32 review_count = 6;
uint32 learn_count = 7;
uint32 new_count = 8;
bool filtered = 16;
2020-01-06 03:18:20 +01:00
}
message RenderExistingCardIn {
int64 card_id = 1;
bool browser = 2;
}
message RenderUncommittedCardIn {
Note note = 1;
uint32 card_ord = 2;
bytes template = 3;
bool fill_empty = 4;
}
message RenderCardOut {
repeated RenderedTemplateNode question_nodes = 1;
repeated RenderedTemplateNode answer_nodes = 2;
}
message RenderedTemplateNode {
oneof value {
string text = 1;
RenderedTemplateReplacement replacement = 2;
}
}
message RenderedTemplateReplacement {
string field_name = 1;
string current_text = 2;
repeated string filters = 3;
}
message ExtractAVTagsIn {
string text = 1;
bool question_side = 2;
}
message ExtractAVTagsOut {
string text = 1;
repeated AVTag av_tags = 2;
}
message AVTag {
oneof value {
string sound_or_video = 1;
TTSTag tts = 2;
}
}
message TTSTag {
string field_text = 1;
string lang = 2;
repeated string voices = 3;
float speed = 4;
repeated string other_args = 5;
}
2020-01-28 12:45:26 +01:00
message ExtractLatexIn {
string text = 1;
bool svg = 2;
bool expand_clozes = 3;
}
message ExtractLatexOut {
string text = 1;
repeated ExtractedLatex latex = 2;
}
message ExtractedLatex {
string filename = 1;
string latex_body = 2;
}
2020-02-11 09:08:34 +01:00
message AddMediaFileIn {
string desired_name = 1;
bytes data = 2;
}
2020-05-22 13:25:25 +02:00
message CheckMediaOut {
repeated string unused = 1;
repeated string missing = 2;
string report = 3;
bool have_trash = 4;
}
message TrashMediaFilesIn {
repeated string fnames = 1;
2020-02-16 12:07:40 +01:00
}
message TranslateStringIn {
uint32 module_index = 1;
uint32 message_index = 2;
map<string, TranslateArgValue> args = 3;
2020-02-16 12:07:40 +01:00
}
message TranslateArgValue {
oneof value {
string str = 1;
double number = 2;
}
2020-02-16 12:07:40 +01:00
}
2020-05-24 00:36:50 +02:00
message FormatTimespanIn {
enum Context {
PRECISE = 0;
ANSWER_BUTTONS = 1;
INTERVALS = 2;
}
float seconds = 1;
Context context = 2;
}
message I18nResourcesIn {
repeated string modules = 1;
}
message StudiedTodayMessageIn {
uint32 cards = 1;
double seconds = 2;
}
message CongratsLearnMessageIn {
float next_due = 1;
uint32 remaining = 2;
2020-03-09 09:58:49 +01:00
}
message OpenCollectionIn {
string collection_path = 1;
string media_folder_path = 2;
string media_db_path = 3;
string log_path = 4;
}
message SearchIn {
string search = 1;
SortOrder order = 2;
}
message SearchOut {
repeated int64 ids = 1;
}
message SortOrder {
2021-01-31 08:08:22 +01:00
message Builtin {
string column = 1;
2021-01-31 08:08:22 +01:00
bool reverse = 2;
}
oneof value {
Empty none = 1;
string custom = 2;
Builtin builtin = 3;
}
}
2020-03-21 00:00:05 +01:00
message SearchNode {
message Dupe {
int64 notetype_id = 1;
string first_field = 2;
}
enum Flag {
FLAG_NONE = 0;
FLAG_ANY = 1;
FLAG_RED = 2;
FLAG_ORANGE = 3;
FLAG_GREEN = 4;
FLAG_BLUE = 5;
}
enum Rating {
RATING_ANY = 0;
RATING_AGAIN = 1;
RATING_HARD = 2;
RATING_GOOD = 3;
RATING_EASY = 4;
RATING_BY_RESCHEDULE = 5;
}
message Rated {
uint32 days = 1;
Rating rating = 2;
}
enum CardState {
CARD_STATE_NEW = 0;
CARD_STATE_LEARN = 1;
CARD_STATE_REVIEW = 2;
CARD_STATE_DUE = 3;
CARD_STATE_SUSPENDED = 4;
CARD_STATE_BURIED = 5;
}
message IdList {
repeated int64 ids = 1;
2021-01-13 11:57:41 +01:00
}
message Group {
enum Joiner {
AND = 0;
OR = 1;
}
repeated SearchNode nodes = 1;
Joiner joiner = 2;
}
oneof filter {
Group group = 1;
SearchNode negated = 2;
string parsable_text = 3;
uint32 template = 4;
int64 nid = 5;
Dupe dupe = 6;
string field_name = 7;
Rated rated = 8;
uint32 added_in_days = 9;
int32 due_in_days = 10;
Flag flag = 11;
CardState card_state = 12;
IdList nids = 13;
uint32 edited_in_days = 14;
string deck = 15;
2021-02-11 01:41:19 +01:00
int32 due_on_day = 16;
string tag = 17;
string note = 18;
uint32 introduced_in_days = 19;
}
}
message JoinSearchNodesIn {
SearchNode.Group.Joiner joiner = 1;
SearchNode existing_node = 2;
SearchNode additional_node = 3;
2021-01-06 14:03:43 +01:00
}
message ReplaceSearchNodeIn {
SearchNode existing_node = 1;
SearchNode replacement_node = 2;
2021-01-06 14:03:43 +01:00
}
message CloseCollectionIn {
bool downgrade_to_schema11 = 1;
}
2020-05-23 07:09:16 +02:00
message AddOrUpdateDeckConfigLegacyIn {
bytes config = 1;
bool preserve_usn_and_mtime = 2;
}
message DeckConfigsForUpdate {
message ConfigWithExtra {
DeckConfig config = 1;
uint32 use_count = 2;
}
message CurrentDeck {
string name = 1;
int64 config_id = 2;
repeated int64 parent_config_ids = 3;
}
repeated ConfigWithExtra all_config = 1;
CurrentDeck current_deck = 2;
DeckConfig defaults = 3;
bool schema_modified = 4;
}
message UpdateDeckConfigsIn {
int64 target_deck_id = 1;
/// Unchanged, non-selected configs can be omitted. Deck will
/// be set to whichever entry comes last.
repeated DeckConfig configs = 2;
repeated int64 removed_config_ids = 3;
bool apply_to_children = 4;
}
message SetTagCollapsedIn {
string name = 1;
bool collapsed = 2;
}
message SetDeckCollapsedIn {
enum Scope {
REVIEWER = 0;
BROWSER = 1;
}
int64 deck_id = 1;
bool collapsed = 2;
Scope scope = 3;
}
message GetChangedTagsOut {
repeated string tags = 1;
}
2021-01-06 15:04:03 +01:00
message TagTreeNode {
string name = 1;
repeated TagTreeNode children = 2;
uint32 level = 3;
bool collapsed = 4;
2021-01-06 15:04:03 +01:00
}
message ReparentTagsIn {
repeated string tags = 1;
string new_parent = 2;
2021-02-02 11:14:04 +01:00
}
message RenameTagsIn {
string current_prefix = 1;
string new_prefix = 2;
}
2020-05-24 00:36:50 +02:00
message SetConfigJsonIn {
string key = 1;
bytes value_json = 2;
}
message StockNotetype {
enum Kind {
BASIC = 0;
BASIC_AND_REVERSED = 1;
BASIC_OPTIONAL_REVERSED = 2;
BASIC_TYPING = 3;
CLOZE = 4;
}
2020-04-13 23:30:33 +02:00
Kind kind = 1;
2020-05-23 12:43:55 +02:00
}
message NotetypeNames {
repeated NotetypeNameId entries = 1;
}
message NotetypeUseCounts {
repeated NotetypeNameIdUseCount entries = 1;
}
message NotetypeNameId {
int64 id = 1;
string name = 2;
}
message NotetypeNameIdUseCount {
int64 id = 1;
string name = 2;
uint32 use_count = 3;
}
message AddOrUpdateNotetypeIn {
bytes json = 1;
bool preserve_usn_and_mtime = 2;
}
message AddNoteIn {
Note note = 1;
int64 deck_id = 2;
}
2020-04-25 10:25:56 +02:00
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
message AddNoteOut {
int64 note_id = 1;
OpChanges changes = 2;
}
message UpdateNoteIn {
Note note = 1;
bool skip_undo_entry = 2;
}
message UpdateCardIn {
Card card = 1;
bool skip_undo_entry = 2;
}
2020-04-25 10:25:56 +02:00
message EmptyCardsReport {
2021-02-08 10:11:16 +01:00
message NoteWithEmptyCards {
int64 note_id = 1;
repeated int64 card_ids = 2;
bool will_delete_note = 3;
}
string report = 1;
repeated NoteWithEmptyCards notes = 2;
2020-04-25 10:25:56 +02:00
}
message DeckNames {
repeated DeckNameId entries = 1;
}
message DeckNameId {
int64 id = 1;
string name = 2;
}
message AddOrUpdateDeckLegacyIn {
bytes deck = 1;
bool preserve_usn_and_mtime = 2;
}
2020-05-05 12:50:17 +02:00
message FieldNamesForNotesIn {
repeated int64 nids = 1;
2020-05-05 12:50:17 +02:00
}
message FieldNamesForNotesOut {
repeated string fields = 1;
2020-05-05 12:50:17 +02:00
}
message FindAndReplaceIn {
repeated int64 nids = 1;
string search = 2;
string replacement = 3;
bool regex = 4;
bool match_case = 5;
string field_name = 6;
2020-05-05 12:50:17 +02:00
}
2021-04-06 19:46:12 +02:00
message BrowserColumns {
2021-04-08 11:40:24 +02:00
enum Sorting {
SORTING_NONE = 0;
SORTING_NORMAL = 1;
SORTING_REVERSED = 2;
}
2021-04-08 11:28:29 +02:00
enum Alignment {
ALIGNMENT_START = 0;
ALIGNMENT_CENTER = 1;
}
message Column {
string key = 1;
2021-04-10 09:14:20 +02:00
string cards_mode_label = 2;
string notes_mode_label = 3;
Sorting sorting = 4;
bool uses_cell_font = 5;
2021-04-08 11:28:29 +02:00
Alignment alignment = 6;
}
repeated Column columns = 1;
2021-04-06 19:46:12 +02:00
}
2021-03-20 12:02:51 +01:00
message BrowserRow {
message Cell {
string text = 1;
bool is_rtl = 2;
}
enum Color {
COLOR_DEFAULT = 0;
COLOR_MARKED = 1;
COLOR_SUSPENDED = 2;
COLOR_FLAG_RED = 3;
COLOR_FLAG_ORANGE = 4;
COLOR_FLAG_GREEN = 5;
COLOR_FLAG_BLUE = 6;
}
repeated Cell cells = 1;
Color color = 2;
string font_name = 3;
uint32 font_size = 4;
}
message AfterNoteUpdatesIn {
repeated int64 nids = 1;
bool mark_notes_modified = 2;
bool generate_cards = 3;
}
message NoteIdsAndTagsIn {
repeated int64 note_ids = 1;
string tags = 2;
}
message FindAndReplaceTagIn {
repeated int64 note_ids = 1;
string search = 2;
string replacement = 3;
bool regex = 4;
bool match_case = 5;
}
message CheckDatabaseOut {
repeated string problems = 1;
}
message Preferences {
message Scheduling {
enum NewReviewMix {
DISTRIBUTE = 0;
REVIEWS_FIRST = 1;
NEW_FIRST = 2;
}
// read only
uint32 scheduler_version = 1;
uint32 rollover = 2;
uint32 learn_ahead_secs = 3;
NewReviewMix new_review_mix = 4;
// v2 only
bool new_timezone = 5;
bool day_learn_first = 6;
}
message Reviewing {
bool hide_audio_play_buttons = 1;
bool interrupt_audio_when_answering = 2;
bool show_remaining_due_counts = 3;
bool show_intervals_on_buttons = 4;
uint32 time_limit_secs = 5;
}
message Editing {
bool adding_defaults_to_current_deck = 1;
bool paste_images_as_png = 2;
bool paste_strips_formatting = 3;
}
Scheduling scheduling = 1;
Reviewing reviewing = 2;
Editing editing = 3;
}
message ClozeNumbersInNoteOut {
repeated uint32 numbers = 1;
}
message GetDeckNamesIn {
bool skip_empty_default = 1;
// if unset, implies skip_empty_default
bool include_filtered = 2;
}
2020-05-24 11:48:56 +02:00
2021-03-22 09:23:56 +01:00
message ReparentDecksIn {
repeated int64 deck_ids = 1;
int64 new_parent = 2;
2021-01-30 11:37:29 +01:00
}
2020-05-24 11:48:56 +02:00
message NoteIsDuplicateOrEmptyOut {
enum State {
NORMAL = 0;
EMPTY = 1;
DUPLICATE = 2;
}
State state = 1;
2020-05-24 11:48:56 +02:00
}
2020-05-28 11:49:44 +02:00
message SyncLoginIn {
string username = 1;
string password = 2;
2020-05-28 11:49:44 +02:00
}
message SyncStatusOut {
enum Required {
NO_CHANGES = 0;
NORMAL_SYNC = 1;
FULL_SYNC = 2;
}
Required required = 1;
}
2020-05-28 11:49:44 +02:00
message SyncCollectionOut {
enum ChangesRequired {
NO_CHANGES = 0;
NORMAL_SYNC = 1;
FULL_SYNC = 2;
// local collection has no cards; upload not an option
FULL_DOWNLOAD = 3;
// remote collection has no cards; download not an option
FULL_UPLOAD = 4;
}
2020-05-28 11:49:44 +02:00
uint32 host_number = 1;
string server_message = 2;
ChangesRequired required = 3;
2020-05-28 11:49:44 +02:00
}
message SyncAuth {
string hkey = 1;
uint32 host_number = 2;
}
2020-06-04 10:21:04 +02:00
2021-01-11 05:11:18 +01:00
message SyncServerMethodIn {
enum Method {
HOST_KEY = 0;
META = 1;
START = 2;
APPLY_GRAVES = 3;
APPLY_CHANGES = 4;
CHUNK = 5;
APPLY_CHUNK = 6;
SANITY_CHECK = 7;
FINISH = 8;
ABORT = 9;
// caller must reopen after these two are called
FULL_UPLOAD = 10;
FULL_DOWNLOAD = 11;
}
Method method = 1;
bytes data = 2;
}
2020-06-04 10:21:04 +02:00
message RemoveNotesIn {
repeated int64 note_ids = 1;
repeated int64 card_ids = 2;
2020-06-04 10:21:04 +02:00
}
message RemoveCardsIn {
repeated int64 card_ids = 1;
2020-06-04 10:21:04 +02:00
}
2020-06-05 11:49:53 +02:00
message UpdateStatsIn {
int64 deck_id = 1;
int32 new_delta = 2;
int32 review_delta = 4;
int32 millisecond_delta = 5;
2020-06-05 11:49:53 +02:00
}
message ExtendLimitsIn {
int64 deck_id = 1;
int32 new_delta = 2;
int32 review_delta = 3;
2020-06-05 11:49:53 +02:00
}
message CountsForDeckTodayOut {
int32 new = 1;
int32 review = 2;
2020-06-05 11:49:53 +02:00
}
message GraphsIn {
string search = 1;
uint32 days = 2;
}
message GraphsOut {
repeated Card cards = 1;
repeated RevlogEntry revlog = 2;
uint32 days_elapsed = 3;
// Based on rollover hour
uint32 next_day_at_secs = 4;
uint32 scheduler_version = 5;
/// Seconds to add to UTC timestamps to get local time.
int32 local_offset_secs = 7;
}
message GraphPreferences {
enum Weekday {
SUNDAY = 0;
MONDAY = 1;
FRIDAY = 5;
SATURDAY = 6;
}
Weekday calendar_first_day_of_week = 1;
bool card_counts_separate_inactive = 2;
bool browser_links_supported = 3;
bool future_due_show_backlog = 4;
}
message RevlogEntry {
enum ReviewKind {
LEARNING = 0;
REVIEW = 1;
RELEARNING = 2;
EARLY_REVIEW = 3;
MANUAL = 4;
}
int64 id = 1;
int64 cid = 2;
int32 usn = 3;
uint32 button_chosen = 4;
int32 interval = 5;
int32 last_interval = 6;
uint32 ease_factor = 7;
uint32 taken_millis = 8;
ReviewKind review_kind = 9;
}
message CongratsInfoOut {
uint32 learn_remaining = 1;
uint32 secs_until_next_learn = 2;
bool review_remaining = 3;
bool new_remaining = 4;
bool have_sched_buried = 5;
bool have_user_buried = 6;
bool is_filtered_deck = 7;
bool bridge_commands_supported = 8;
string deck_description = 9;
}
message UnburyDeckIn {
enum Mode {
ALL = 0;
SCHED_ONLY = 1;
USER_ONLY = 2;
}
int64 deck_id = 1;
Mode mode = 2;
}
2020-08-31 08:14:04 +02:00
message BuryOrSuspendCardsIn {
enum Mode {
SUSPEND = 0;
BURY_SCHED = 1;
BURY_USER = 2;
}
repeated int64 card_ids = 1;
repeated int64 note_ids = 2;
Mode mode = 3;
2020-08-31 08:14:04 +02:00
}
2020-09-02 06:41:46 +02:00
message ScheduleCardsAsNewIn {
repeated int64 card_ids = 1;
bool log = 2;
2020-09-02 06:41:46 +02:00
}
message SetDueDateIn {
repeated int64 card_ids = 1;
string days = 2;
2021-03-12 05:50:31 +01:00
Config.String config_key = 3;
}
message SortCardsIn {
repeated int64 card_ids = 1;
uint32 starting_from = 2;
uint32 step_size = 3;
bool randomize = 4;
bool shift_existing = 5;
}
message SortDeckIn {
int64 deck_id = 1;
bool randomize = 2;
}
2020-09-03 09:42:46 +02:00
message SetDeckIn {
repeated int64 card_ids = 1;
int64 deck_id = 2;
2020-09-03 09:42:46 +02:00
}
message Config {
message Bool {
enum Key {
BROWSER_TABLE_SHOW_NOTES_MODE = 0;
BROWSER_SORT_BACKWARDS = 1;
BROWSER_NOTE_SORT_BACKWARDS = 2;
PREVIEW_BOTH_SIDES = 3;
COLLAPSE_TAGS = 4;
COLLAPSE_NOTETYPES = 5;
COLLAPSE_DECKS = 6;
COLLAPSE_SAVED_SEARCHES = 7;
COLLAPSE_TODAY = 8;
COLLAPSE_CARD_STATE = 9;
COLLAPSE_FLAGS = 10;
SCHED_2021 = 11;
ADDING_DEFAULTS_TO_CURRENT_DECK = 12;
HIDE_AUDIO_PLAY_BUTTONS = 13;
INTERRUPT_AUDIO_WHEN_ANSWERING = 14;
PASTE_IMAGES_AS_PNG = 15;
PASTE_STRIPS_FORMATTING = 16;
NORMALIZE_NOTE_TEXT = 17;
}
Key key = 1;
}
message String {
enum Key {
SET_DUE_BROWSER = 0;
SET_DUE_REVIEWER = 1;
}
Key key = 1;
}
}
message SetConfigBoolIn {
Config.Bool.Key key = 1;
bool value = 2;
}
message SetConfigStringIn {
Config.String.Key key = 1;
string value = 2;
}
message RenderMarkdownIn {
string markdown = 1;
bool sanitize = 2;
}
message SchedulingState {
message New {
uint32 position = 1;
}
message Learning {
uint32 remaining_steps = 1;
uint32 scheduled_secs = 2;
}
message Review {
uint32 scheduled_days = 1;
uint32 elapsed_days = 2;
float ease_factor = 3;
uint32 lapses = 4;
bool leeched = 5;
}
message Relearning {
Review review = 1;
Learning learning = 2;
}
message Normal {
oneof value {
New new = 1;
Learning learning = 2;
Review review = 3;
Relearning relearning = 4;
}
}
message Preview {
uint32 scheduled_secs = 1;
bool finished = 2;
}
message ReschedulingFilter {
Normal original_state = 1;
}
message Filtered {
oneof value {
Preview preview = 1;
ReschedulingFilter rescheduling = 2;
}
}
oneof value {
Normal normal = 1;
Filtered filtered = 2;
}
}
message NextCardStates {
SchedulingState current = 1;
SchedulingState again = 2;
SchedulingState hard = 3;
SchedulingState good = 4;
SchedulingState easy = 5;
}
message AnswerCardIn {
enum Rating {
AGAIN = 0;
HARD = 1;
GOOD = 2;
EASY = 3;
}
int64 card_id = 1;
SchedulingState current_state = 2;
SchedulingState new_state = 3;
Rating rating = 4;
int64 answered_at_millis = 5;
uint32 milliseconds_taken = 6;
}
message GetQueuedCardsIn {
uint32 fetch_limit = 1;
bool intraday_learning_only = 2;
}
message GetQueuedCardsOut {
enum Queue {
New = 0;
Learning = 1;
Review = 2;
}
message QueuedCard {
Card card = 1;
2021-03-02 06:48:25 +01:00
Queue queue = 2;
NextCardStates next_states = 3;
}
message QueuedCards {
repeated QueuedCard cards = 1;
uint32 new_count = 2;
uint32 learning_count = 3;
uint32 review_count = 4;
}
oneof value {
QueuedCards queued_cards = 1;
CongratsInfoOut congrats_info = 2;
}
}
undoable ops now return changes directly; add new *_ops.py files - Introduced a new transact() method that wraps the return value in a separate struct that describes the changes that were made. - Changes are now gathered from the undo log, so we don't need to guess at what was changed - eg if update_note() is called with identical note contents, no changes are returned. Card changes will only be set if cards were actually generated by the update_note() call, and tag will only be set if a new tag was added. - mw.perform_op() has been updated to expect the op to return the changes, or a structure with the changes in it, and it will use them to fire the change hook, instead of fetching the changes from undo_status(), so there is no risk of race conditions. - the various calls to mw.perform_op() have been split into separate files like card_ops.py. Aside from making the code cleaner, this works around a rather annoying issue with mypy. Because we run it with no_strict_optional, mypy is happy to accept an operation that returns None, despite the type signature saying it requires changes to be returned. Turning no_strict_optional on for the whole codebase is not practical at the moment, but we can enable it for individual files. Still todo: - The cursor keeps moving back to the start of a field when typing - we need to ignore the refresh hook when we are the initiator. - The busy cursor icon should probably be delayed a few hundreds ms. - Still need to think about a nicer way of handling saveNow() - op_made_changes(), op_affects_study_queue() might be better embedded as properties in the object instead
2021-03-16 05:26:42 +01:00
message OpChanges {
bool card = 1;
bool note = 2;
bool deck = 3;
bool tag = 4;
bool notetype = 5;
bool config = 6;
bool deck_config = 11;
bool browser_table = 7;
bool browser_sidebar = 8;
bool editor = 9;
bool study_queues = 10;
}
message UndoStatus {
string undo = 1;
string redo = 2;
uint32 last_step = 3;
}
Simplify note adding and the deck/notetype choosers The existing code was really difficult to reason about: - The default notetype depended on the selected deck, and vice versa, and this logic was buried in the deck and notetype choosing screens, and models.py. - Changes to the notetype were not passed back directly, but were fired via a hook, which changed any screen in the app that had a notetype selector. It also wasn't great for performance, as the most recent deck and tags were embedded in the notetype, which can be expensive to save and sync for large notetypes. To address these points: - The current deck for a notetype, and notetype for a deck, are now stored in separate config variables, instead of directly in the deck or notetype. These are cheap to read and write, and we'll be able to sync them individually in the future once config syncing is updated in the future. I seem to recall some users not wanting the tag saving behaviour, so I've dropped that for now, but if people end up missing it, it would be simple to add as an extra auxiliary config variable. - The logic for getting the starting deck and notetype has been moved into the backend. It should be the same as the older Python code, with one exception: when "change deck depending on notetype" is enabled in the preferences, it will start with the current notetype ("curModel"), instead of first trying to get a deck-specific notetype. - ModelChooser has been duplicated into notetypechooser.py, and it has been updated to solely be concerned with keeping track of a selected notetype - it no longer alters global state.
2021-03-08 14:23:24 +01:00
message OpChangesAfterUndo {
OpChanges changes = 1;
string operation = 2;
int64 reverted_to_timestamp = 3;
UndoStatus new_status = 4;
uint32 counter = 5;
}
Simplify note adding and the deck/notetype choosers The existing code was really difficult to reason about: - The default notetype depended on the selected deck, and vice versa, and this logic was buried in the deck and notetype choosing screens, and models.py. - Changes to the notetype were not passed back directly, but were fired via a hook, which changed any screen in the app that had a notetype selector. It also wasn't great for performance, as the most recent deck and tags were embedded in the notetype, which can be expensive to save and sync for large notetypes. To address these points: - The current deck for a notetype, and notetype for a deck, are now stored in separate config variables, instead of directly in the deck or notetype. These are cheap to read and write, and we'll be able to sync them individually in the future once config syncing is updated in the future. I seem to recall some users not wanting the tag saving behaviour, so I've dropped that for now, but if people end up missing it, it would be simple to add as an extra auxiliary config variable. - The logic for getting the starting deck and notetype has been moved into the backend. It should be the same as the older Python code, with one exception: when "change deck depending on notetype" is enabled in the preferences, it will start with the current notetype ("curModel"), instead of first trying to get a deck-specific notetype. - ModelChooser has been duplicated into notetypechooser.py, and it has been updated to solely be concerned with keeping track of a selected notetype - it no longer alters global state.
2021-03-08 14:23:24 +01:00
message DefaultsForAddingIn {
int64 home_deck_of_current_review_card = 1;
}
message DeckAndNotetype {
int64 deck_id = 1;
int64 notetype_id = 2;
}
2021-03-11 09:54:30 +01:00
message RenameDeckIn {
int64 deck_id = 1;
string new_name = 2;
2021-03-14 10:54:15 +01:00
}
message FilteredDeckForUpdate {
int64 id = 1;
string name = 2;
2021-04-04 13:15:30 +02:00
Deck.Filtered config = 3;
}
2021-03-14 10:54:15 +01:00
message SetFlagIn {
repeated int64 card_ids = 1;
uint32 flag = 2;
}