yanki-connect
Version:
A fully-typed Anki-Connect API client.
1,725 lines • 81.2 kB
TypeScript
//#region src/types/deck.d.ts
type DeckStats = {
deck_id: number;
learn_count: number;
name: string;
new_count: number;
review_count: number;
total_in_deck: number;
};
type DeckConfig = {
autoplay: boolean;
dyn: 1 | false;
id: number;
lapse: {
delays: number[];
leechAction: number;
leechFails: number;
minInt: number;
mult: number;
};
maxTaken: number;
mod: number;
name: string;
new: {
bury: boolean;
delays: number[];
initialFactor: number;
ints: number[];
order: number;
perDay: number;
separate: boolean;
};
replayq: boolean;
rev: {
bury: boolean;
ease4: number;
fuzz: number;
ivlFct: number;
maxIvl: number;
minSpace: number;
perDay: number;
};
timer: number;
usn: number;
};
type DeckRequests = Request<'changeDeck', 6, {
cards: number[];
deck: string;
}> | Request<'cloneDeckConfigId', 6, {
cloneFrom: number;
name: string;
}, false | number> | Request<'createDeck', 6, {
deck: string;
}, number> | Request<'deckNames', 6, never, string[]> | Request<'deckNamesAndIds', 6, never, Record<string, number>> | Request<'deleteDecks', 6, {
cardsToo: true;
decks: string[];
}> | Request<'getDeckConfig', 6, {
deck: string;
}, DeckConfig> | Request<'getDecks', 6, Record<'cards', number[]>, Record<string, number[]>> | Request<'getDeckStats', 6, {
decks: string[];
}, Record<string, DeckStats>> | Request<'removeDeckConfigId', 6, {
configId: number;
}, boolean> | Request<'saveDeckConfig', 6, {
config: DeckConfig;
}, boolean> | Request<'setDeckConfigId', 6, {
configId: number;
decks: string[];
}, boolean>;
//#endregion
//#region src/types/note.d.ts
type NoteModel = 'Basic' | 'Basic (and reversed card)' | 'Basic (type in the answer)' | 'Cloze' | (string & {});
type NoteMedia = {
data?: string;
fields?: string[];
filename: string;
path?: string;
skipHash?: string;
url?: string;
};
type Note = {
audio?: NoteMedia[];
deckName: string;
fields: Record<string, string>;
modelName: NoteModel;
picture?: NoteMedia[];
tags?: string[];
video?: NoteMedia[];
};
type NoteWithCreationOptions = Note & {
options?: {
allowDuplicate?: boolean;
duplicateScope?: 'deck' | (string & {});
duplicateScopeOptions?: {
checkAllModels?: boolean;
checkChildren?: boolean;
deckName?: null | string;
};
};
};
type NoteRequests = Request<'addNote', 6, {
note: NoteWithCreationOptions;
}, null | number> | Request<'addNotes', 6, {
notes: NoteWithCreationOptions[];
}, Array<null | string> | null> | Request<'addTags', 6, {
notes: number[];
tags: string;
}> | Request<'canAddNotes', 6, {
notes: NoteWithCreationOptions[];
}, boolean[]> | Request<'canAddNotesWithErrorDetail', 6, {
notes: NoteWithCreationOptions[];
}, Array<{
canAdd: false;
error: string;
} | {
canAdd: true;
}>> | Request<'clearUnusedTags', 6, never, string[]> | Request<'deleteNotes', 6, {
notes: number[];
}> | Request<'findNotes', 6, {
query: string;
}, number[]> | Request<'getNoteTags', 6, {
note: number;
}, string[]> | Request<'getTags', 6, never, string[]> | Request<'notesInfo', 6, {
notes: number[];
}, Array<{
cards: number[];
fields: Record<string, {
order: number;
value: string;
}>;
mod: number;
modelName: string;
noteId: number;
profile: string;
tags: string[];
}>> | Request<'notesModTime', 6, {
notes: number[];
}, Array<{
mod: number;
noteId: number;
}>> | Request<'removeEmptyNotes', 6> | Request<'removeTags', 6, {
notes: number[];
tags: string;
}> | Request<'replaceTags', 6, {
notes: number[];
replace_with_tag: string;
tag_to_replace: string;
}> | Request<'replaceTagsInAllNotes', 6, {
replace_with_tag: string;
tag_to_replace: string;
}> | Request<'updateNote', 6, {
note: {
audio?: NoteMedia[];
fields: Record<string, string>;
id: number;
picture?: NoteMedia[];
tags?: string[];
video?: NoteMedia[];
} | {
fields?: Record<string, string>;
id: number;
tags: string[];
};
}> | Request<'updateNoteFields', 6, {
note: {
audio?: NoteMedia[];
fields: Record<string, string>;
id: number;
picture?: NoteMedia[];
video?: NoteMedia[];
};
}> | Request<'updateNoteModel', 6, {
note: {
fields: Record<string, string>;
id: number;
modelName: string;
tags: string[];
};
}> | Request<'updateNoteTags', 6, {
note: number;
tags: string[];
}>;
//#endregion
//#region src/types/graphical.d.ts
type GraphicalRequests = Request<'guiAddCards', 6, {
note: Note;
}, number> | Request<'guiAnswerCard', 6, {
ease: number;
}, boolean> | Request<'guiBrowse', 6, {
query: string;
reorderCards?: {
columnId: CardBrowserColumns;
order: 'ascending' | 'descending';
};
}, number[]> | Request<'guiCheckDatabase', 6, never, true> | Request<'guiCurrentCard', 6, never, CardInfo | null> | Request<'guiDeckBrowser', 6> | Request<'guiDeckOverview', 6, {
name: string;
}, boolean> | Request<'guiDeckReview', 6, {
name: string;
}, boolean> | Request<'guiEditNote', 6, {
note: number;
}> | Request<'guiExitAnki', 6> | Request<'guiImportFile', 6, {
path: string;
}> | Request<'guiPlayAudio', 6, never, true> | Request<'guiSelectCard', 6, {
card: number;
}, boolean> | Request<'guiSelectedNotes', 6, never, number[]> | Request<'guiSelectNote', 6, {
note: number;
}, boolean> | Request<'guiShowAnswer', 6, never, boolean> | Request<'guiShowQuestion', 6, never, boolean> | Request<'guiStartCardTimer', 6, never, true> | Request<'guiUndo', 6, never, boolean>;
//#endregion
//#region src/types/media.d.ts
type MediaRequests = Request<'deleteMediaFile', 6, {
filename: string;
}> | Request<'getMediaDirPath', 6, never, string> | Request<'getMediaFilesNames', 6, {
pattern: string;
}, string[]> | Request<'retrieveMediaFile', 6, {
filename: string;
}, false | string> | Request<'storeMediaFile', 6, {
data?: string;
deleteExisting?: boolean;
filename: string;
path?: string;
url?: string;
}, string>;
//#endregion
//#region src/types/miscellaneous.d.ts
type MiscellaneousRequests = Request<'apiReflect', 6, {
actions: null | string[];
scopes: Array<'actions'>;
}, {
actions: string[];
scopes: string[];
}> | Request<'exportPackage', 6, {
deck: string;
includeSched?: boolean;
path: string;
}, boolean> | Request<'getActiveProfile', 6, never, string> | Request<'getProfiles', 6, never, string[]> | Request<'importPackage', 6, {
path: string;
}, boolean> | Request<'loadProfile', 6, {
name: string;
}, true> | Request<'multi', 6,
// Crazy, have to call this experimental
{
actions: Array<{
action: Requests['action'];
params?: Requests['params'];
version?: number;
}>;
}, Array<Requests['response'] | {
error: null | string;
result: Requests['response'];
}>> | Request<'reloadCollection', 6> | Request<'requestPermission', 6, never, {
permission: 'denied';
} | {
permission: 'granted';
requireApiKey: boolean;
version: boolean;
}> | Request<'sync', 6> | Request<'version', 6, never, number>;
//#endregion
//#region src/types/model.d.ts
type ModelField = {
collapsed: boolean;
description: string;
excludeFromSearch: boolean;
font: string;
id: number;
name: string;
ord: number;
plainText: boolean;
preventDeletion: boolean;
rtl: boolean;
size: number;
sticky: boolean;
tag: null;
};
type ModelTemplate = {
afmt: string;
bafmt: string;
bfont: string;
bqfmt: string;
bsize: number;
did: null;
id: number;
name: string;
ord: number;
qfmt: string;
};
type Model = {
css: string;
did: null;
flds: ModelField[];
id: number;
latexPost: string;
latexPre: string;
latexsvg: boolean;
mod: number;
name: string;
originalStockKind: number;
req: Array<[number, string, number[]]>;
sortf: number;
tmpls: ModelTemplate[];
type: number;
usn: number;
};
type ModelToCreate = {
cardTemplates: Array<{
[key: string]: string;
Back: string;
Front: string;
}>;
css?: string;
inOrderFields: string[];
isCloze?: boolean;
modelName: string;
};
type ModelRequests = Request<'createModel', 6, ModelToCreate, Model> | Request<'findAndReplaceInModels', 6, {
model: {
back: boolean;
css: boolean;
fieldText: string;
front: boolean;
modelName: string;
replaceText: string;
};
}, number> | Request<'findModelsById', 6, {
modelIds: number[];
}, Model[]> | Request<'findModelsByName', 6, {
modelNames: string[];
}, Model[]> | Request<'modelFieldAdd', 6, {
fieldName: string;
index: number;
modelName: string;
}> | Request<'modelFieldDescriptions', 6, {
description: string;
fieldName: string;
modelName: string;
}, boolean> | Request<'modelFieldFonts', 6, {
modelName: string;
}, Record<string, {
font: string;
size: number;
}>> | Request<'modelFieldNames', 6, {
modelName: string;
}, string[]> | Request<'modelFieldRemove', 6, {
fieldName: string;
modelName: string;
}> | Request<'modelFieldRename', 6, {
modelName: string;
newFieldName: string;
oldFieldName: string;
}> | Request<'modelFieldReposition', 6, {
fieldName: string;
index: number;
modelName: string;
}> | Request<'modelFieldSetDescription', 6, {
fieldName: string;
index: number;
modelName: string;
}> | Request<'modelFieldSetFont', 6, {
fieldName: string;
font: string;
modelName: string;
}> | Request<'modelFieldSetFontSize', 6, {
fieldName: string;
fontSize: number;
modelName: string;
}> | Request<'modelFieldsOnTemplates', 6, {
modelName: string;
}, Record<string, [string[], string[]]>> | Request<'modelNames', 6, never, string[]> | Request<'modelNamesAndIds', 6, never, Record<string, number>> | Request<'modelStyling', 6, {
modelName: string;
}, {
css: string;
}> | Request<'modelTemplateAdd', 6, {
modelName: string;
template: {
[key: string]: string;
Back: string;
Front: string;
};
}> | Request<'modelTemplateRemove', 6, {
modelName: string;
templateName: string;
}> | Request<'modelTemplateRename', 6, {
modelName: string;
newTemplateName: string;
oldTemplateName: string;
}> | Request<'modelTemplateReposition', 6, {
index: number;
modelName: string;
templateName: string;
}> | Request<'modelTemplates', 6, {
modelName: string;
}, Record<string, {
[key: string]: string;
Back: string;
Front: string;
}>> | Request<'updateModelStyling', 6, {
model: {
css: string;
name: string;
};
}> | Request<'updateModelTemplates', 6, {
model: {
name: string;
templates: Record<string, {
Back?: string;
Front?: string;
}>;
};
}>;
//#endregion
//#region src/types/statistic.d.ts
type ReviewStatisticTuple = [reviewTime: number, cardID: number, usn: number, buttonPressed: number, newInterval: number, previousInterval: number, newFactor: number, reviewDuration: number, reviewType: number];
type StatisticRequests = Request<'cardReviews', 6, {
deck: string;
startID: number;
}, ReviewStatisticTuple[]> | Request<'getCollectionStatsHTML', 6, {
wholeCollection: boolean;
}, string> | Request<'getLatestReviewID', 6, {
deck: string;
}, number> | Request<'getNumCardsReviewedByDay', 6, never, Array<[string, number]>> | Request<'getNumCardsReviewedToday', 6, never, number> | Request<'getReviewsOfCards', 6, {
cards: string[];
}, Record<string, Array<{
/** ButtonPressed */
ease: number;
/** NewFactor */
factor: number;
/** ReviewTime */
id: number;
/** NewInterval */
ivl: number;
/** PreviousInterval */
lastIvl: number;
/** ReviewDuration */
time: number;
/** ReviewType */
type: number;
/** Usn */
usn: number;
}>>> | Request<'insertReviews', 6, {
reviews: ReviewStatisticTuple[];
}>;
//#endregion
//#region src/types/shared.d.ts
/**
* Abstract wrapper over an Anki Connect action / response
*/
type Request<Action extends string, Version extends AnkiConnectVersion, Params = never, Result = null> = {
action: Action;
params: Params;
response: {
error: null | string;
result: Result;
};
version: Version;
};
/**
* Requests
*/
type Requests = CardRequests | DeckRequests | GraphicalRequests | MediaRequests | MiscellaneousRequests | ModelRequests | NoteRequests | StatisticRequests;
type AnkiConnectVersion = 6;
type Actions = Requests['action'];
type ActionsWithParams = { [K in Actions]: ParamsForAction<K> extends never ? never : K }[Actions];
type ActionsWithoutParams = { [K in Actions]: ParamsForAction<K> extends never ? K : never }[Actions];
type ParamsForAction<T extends Requests['action']> = Extract<Requests, {
action: T;
}>['params'];
type ResponseForAction<T extends Requests['action']> = Extract<Requests, {
action: T;
}>['response'];
//#endregion
//#region src/types/card.d.ts
type CardBrowserColumns = 'answer' | 'cardDue' | 'cardEase' | 'cardIvl' | 'cardLapses' | 'cardMod' | 'cardReps' | 'deck' | 'note' | 'noteCrt' | 'noteFld' | 'noteMod' | 'noteTags' | 'question' | 'template' | (string & {});
type CardValueKeys = 'data' | 'did' | 'due' | 'factor' | 'flags' | 'id' | 'ivl' | 'lapses' | 'left' | 'mod' | 'odid' | 'odue' | 'ord' | 'queue' | 'reps' | 'type' | 'usn';
type CardInfo = {
answer: string;
buttons?: number[];
cardId: number;
css: string;
deckName: string;
due: number;
fieldOrder: number;
fields: Record<string, {
order: number;
value: string;
}>;
interval: number;
lapses: number;
left: number;
mod: number;
modelName: string;
nextReviews: string[];
note: number;
ord: number;
question: string;
queue: number;
reps: number;
template: string;
type: number;
};
type CardRequests = Request<'answerCards', 6, {
answers: Array<{
cardId: number;
ease: number;
}>;
}, boolean[]> | Request<'areDue', 6, {
cards: number[];
}, boolean[]> | Request<'areSuspended', 6, {
cards: number[];
}, Array<boolean | null>> | Request<'cardsInfo', 6, {
cards: number[];
}, CardInfo[]> | Request<'cardsModTime', 6, {
cards: number[];
}, {
cardId: number;
mod: number;
}> | Request<'cardsToNotes', 6, {
cards: number[];
}, number[]> | Request<'findCards', 6, {
query: string;
}, number[]> | Request<'forgetCards', 6, {
cards: number[];
}> | Request<'getEaseFactors', 6, {
cards: number[];
}, number[]> | Request<'getIntervals', 6, {
cards: number[];
complete?: boolean;
}, number[] | number[][]> | Request<'relearnCards', 6, {
cards: number[];
}> | Request<'setDueDate', 6, {
cards: number[];
days: string;
}, boolean> | Request<'setEaseFactors', 6, {
cards: number[];
easeFactors: number[];
}, boolean[]> | Request<'setSpecificValueOfCard', 6, {
card: number;
keys: CardValueKeys[];
newValues: string[];
}, boolean[]> | Request<'suspend', 6, {
cards: number[];
}, boolean> | Request<'suspended', 6, {
card: number;
}, boolean> | Request<'unsuspend', 6, {
cards: number[];
}, boolean>;
//#endregion
//#region src/client.d.ts
/**
* Subset of built-in Fetch interface that's actually used by Anki, for ease of
* external re-implementation when passing a custom fetch function to
* YankiClient.
*/
type YankiFetchAdapter = (input: string, init?: {
body?: string;
headers?: Record<string, string>;
method?: string;
mode?: RequestMode;
}) => Promise<undefined | {
headers: Headers | Record<string, string>;
json(): Promise<any>;
status: number;
}>;
/** Optional options to pass when instantiating a new YankiConnect instance. */
type YankiConnectOptions = {
/**
* Attempt to open the desktop Anki.app if it's not already running.
*
* - `true` will always attempt to open Anki _when a request is made_. This
* might introduce significant latency on the first launch.
* - `false` will never attempt to open Anki. Requests will fail until
* something or someone else opens the Anki app.
* - `immediately` is a special option that will open Anki when the client is
* instantiated.
*
* The Anki desktop app must be running for the client and the underlying
* Anki-Connect service to work.
*
* Currently supported on macOS only.
*
* The client does not attempt to close the app.
* @default false
*/
autoLaunch: 'immediately' | boolean;
/**
* Advanced option to customize the resource fetch implementation used to make requests to Anki-Connect.
*
* Note that the signature reflects the subset of the built-in Fetch interface that's actually used by yanki-connect.
*
* The exact signature of this option is subject to change in the future.
* @default fetch
*/
fetchAdapter: undefined | YankiFetchAdapter;
/**
* Host where the Anki-Connect service is running.
* @default 'http://127.0.0.1'
*/
host: string;
/**
* Anki-Connect security key (optional)
* @default undefined
*/
key: string | undefined;
/**
* Port where the Anki-Connect service is running.
* @default 8765
*/
port: number;
/**
* Anki-Connect API version.
*
* Only API version 6 is supported for now.
* @default 6
*/
version: AnkiConnectVersion;
};
declare const defaultYankiConnectOptions: YankiConnectOptions;
/**
* __YankiConnect is a client for the [Anki-Connect
* API](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md)__.
*
* It implements every endpoint from Anki-Connect version 25.11.9.0, released 2025-11-09.
*
* Inline documentation is by the Anki-Connect authors, generated from [the
* readme.md](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md)
*/
declare class YankiConnect {
/**
* __Card Actions__
*
* [Documentation](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md#card-actions)
*/
readonly card: {
/**
* Answer cards. Ease is between 1 (Again) and 4 (Easy). Will start the
* timer immediately before answering. Returns true if card exists, `false`
* otherwise.
*/
answerCards: (params: {
answers: Array<{
cardId: number;
ease: number;
}>;
}) => Promise<boolean[]>;
/**
* Returns an array indicating whether each of the given cards is due (in
* the same order). Note: cards in the learning queue with a large interval
* (over 20 minutes) are treated as not due until the time of their interval
* has passed, to match the way Anki treats them when reviewing.
*/
areDue: (params: {
cards: number[];
}) => Promise<boolean[]>;
/**
* Returns an array indicating whether each of the given cards is suspended
* (in the same order). If card doesn’t exist returns `null`.
*/
areSuspended: (params: {
cards: number[];
}) => Promise<(boolean | null)[]>;
/**
* Returns a list of objects containing for each card ID the card fields,
* front and back sides including CSS, note type, the note that the card
* belongs to, and deck name, last modification timestamp as well as ease
* and interval.
*/
cardsInfo: (params: {
cards: number[];
}) => Promise<CardInfo[]>;
/**
* Returns a list of objects containing for each card ID the modification
* time. This function is about 15 times faster than executing `cardsInfo`.
*/
cardsModTime: (params: {
cards: number[];
}) => Promise<{
cardId: number;
mod: number;
}>;
/**
* Returns an unordered array of note IDs for the given card IDs. For cards
* with the same note, the ID is only given once in the array.
*/
cardsToNotes: (params: {
cards: number[];
}) => Promise<number[]>;
/**
* Returns an array of card IDs for a given query. Functionally identical to
* `guiBrowse` but doesn’t use the GUI for better performance.
*/
findCards: (params: {
query: string;
}) => Promise<number[]>;
/**
* Forget cards, making the cards new again.
*/
forgetCards: (params: {
cards: number[];
}) => Promise<null>;
/**
* Returns an array with the ease factor for each of the given cards (in the
* same order).
*/
getEaseFactors: (params: {
cards: number[];
}) => Promise<number[]>;
/**
* Returns an array of the most recent intervals for each given card ID, or
* a 2-dimensional array of all the intervals for each given card ID when
* complete is `true`. Negative intervals are in seconds and positive
* intervals in days.
*/
getIntervals: (params: {
cards: number[];
complete?: boolean;
}) => Promise<number[] | number[][]>;
/**
* Make cards be “relearning”.
*/
relearnCards: (params: {
cards: number[];
}) => Promise<null>;
/**
* Set Due Date. Turns cards into review cards if they are new, and makes
* them due on a certain date.
*
* - 0 = today
* - 1! = tomorrow + change interval to 1
* - 3-7 = random choice of 3-7 days
*/
setDueDate: (params: {
cards: number[];
days: string;
}) => Promise<boolean>;
/**
* Sets ease factor of cards by card ID; returns `true` if successful (all
* cards existed) or `false` otherwise.
*/
setEaseFactors: (params: {
cards: number[];
easeFactors: number[];
}) => Promise<boolean[]>;
/**
* Sets specific value of a single card. Given the risk of wreaking havoc in
* the database when changing some of the values of a card, some of the keys
* require the argument “warning_check” set to True. This can be used to set
* a card’s flag, change it’s ease factor, change the review order in a
* filtered deck and change the column “data” (not currently used by anki
* apparently), and many other values. A list of values and explanation of
* their respective utility can be found at [AnkiDroid’s
* wiki](https://github.com/ankidroid/Anki-Android/wiki/Database-Structure).
*/
setSpecificValueOfCard: (params: {
card: number;
keys: ("data" | "did" | "due" | "factor" | "flags" | "id" | "ivl" | "lapses" | "left" | "mod" | "odid" | "odue" | "ord" | "queue" | "reps" | "type" | "usn")[];
newValues: string[];
}) => Promise<boolean[]>;
/**
* Suspend cards by card ID; returns `true` if successful (at least one card
* wasn’t already suspended) or `false` otherwise.
*/
suspend: (params: {
cards: number[];
}) => Promise<boolean>;
/**
* Check if card is suspended by its ID. Returns `true` if suspended,
* `false` otherwise.
*/
suspended: (params: {
card: number;
}) => Promise<boolean>;
/**
* Unsuspend cards by card ID; returns `true` if successful (at least one
* card was previously suspended) or `false` otherwise.
*/
unsuspend: (params: {
cards: number[];
}) => Promise<boolean>;
};
/**
* __Deck Actions__
*
* [Documentation](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md#deck-actions)
*/
readonly deck: {
/**
* Moves cards with the given IDs to a different deck, creating the deck if
* it doesn’t exist yet.
*/
changeDeck: (params: {
cards: number[];
deck: string;
}) => Promise<null>;
/**
* Creates a new configuration group with the given name, cloning from the
* group with the given ID, or from the default group if this is
* unspecified. Returns the ID of the new configuration group, or `false` if
* the specified group to clone from does not exist.
*/
cloneDeckConfigId: (params: {
cloneFrom: number;
name: string;
}) => Promise<number | false>;
/**
* Create a new empty deck. Will not overwrite a deck that exists with the
* same name.
*/
createDeck: (params: {
deck: string;
}) => Promise<number>;
/**
* Gets the complete list of deck names for the current user.
*/
deckNames: () => Promise<string[]>;
/**
* Gets the complete list of deck names and their respective IDs for the
* current user.
*/
deckNamesAndIds: () => Promise<Record<string, number>>;
/**
* Deletes decks with the given names. The argument `cardsToo` must be
* specified and set to `true`.
*/
deleteDecks: (params: {
cardsToo: true;
decks: string[];
}) => Promise<null>;
/**
* Gets the configuration group object for the given deck.
*/
getDeckConfig: (params: {
deck: string;
}) => Promise<{
autoplay: boolean;
dyn: 1 | false;
id: number;
lapse: {
delays: number[];
leechAction: number;
leechFails: number;
minInt: number;
mult: number;
};
maxTaken: number;
mod: number;
name: string;
new: {
bury: boolean;
delays: number[];
initialFactor: number;
ints: number[];
order: number;
perDay: number;
separate: boolean;
};
replayq: boolean;
rev: {
bury: boolean;
ease4: number;
fuzz: number;
ivlFct: number;
maxIvl: number;
minSpace: number;
perDay: number;
};
timer: number;
usn: number;
}>;
/**
* Accepts an array of card IDs and returns an object with each deck name as
* a key, and its value an array of the given cards which belong to it.
*/
getDecks: (params: Record<"cards", number[]>) => Promise<Record<string, number[]>>;
/**
Gets statistics such as total cards and cards due for the given decks.
*/
getDeckStats: (params: {
decks: string[];
}) => Promise<Record<string, {
deck_id: number;
learn_count: number;
name: string;
new_count: number;
review_count: number;
total_in_deck: number;
}>>;
/**
* Removes the configuration group with the given ID, returning `true` if
* successful, or `false` if attempting to remove either the default
* configuration group (ID = 1) or a configuration group that does not
* exist.
*/
removeDeckConfigId: (params: {
configId: number;
}) => Promise<boolean>;
/**
* Saves the given configuration group, returning `true` on success or
* `false` if the ID of the configuration group is invalid (such as when it
* does not exist).
*/
saveDeckConfig: (params: {
config: {
autoplay: boolean;
dyn: 1 | false;
id: number;
lapse: {
delays: number[];
leechAction: number;
leechFails: number;
minInt: number;
mult: number;
};
maxTaken: number;
mod: number;
name: string;
new: {
bury: boolean;
delays: number[];
initialFactor: number;
ints: number[];
order: number;
perDay: number;
separate: boolean;
};
replayq: boolean;
rev: {
bury: boolean;
ease4: number;
fuzz: number;
ivlFct: number;
maxIvl: number;
minSpace: number;
perDay: number;
};
timer: number;
usn: number;
};
}) => Promise<boolean>;
/**
* Changes the configuration group for the given decks to the one with the
* given ID. Returns `true` on success or `false` if the given configuration
* group or any of the given decks do not exist.
*/
setDeckConfigId: (params: {
configId: number;
decks: string[];
}) => Promise<boolean>;
};
/**
* __Graphical Actions__
*
* [Documentation](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md#graphical-actions)
*/
readonly graphical: {
/**
* Invokes the _Add Cards_ dialog, presets the note using the given deck and
* model, with the provided field values and tags. Invoking it multiple
* times closes the old window and _reopen the window_ with the new provided
* values.
*
* Audio, video, and picture files can be embedded into the fields via the
* `audio`, `video`, and `picture` keys, respectively. Refer to the
* documentation of `addNote` and `storeMediaFile` for an explanation of
* these fields.
*
* The result is the ID of the note which would be added, if the user chose
* to confirm the _Add Cards_ dialogue.
*/
guiAddCards: (params: {
note: Note;
}) => Promise<number>;
/**
* Answers the current card; returns `true` if succeeded or `false`
* otherwise. Note that the answer for the current card must be displayed
* before before any answer can be accepted by Anki.
*/
guiAnswerCard: (params: {
ease: number;
}) => Promise<boolean>;
/**
* Invokes the _Card Browser_ dialog and searches for a given query. Returns
* an array of identifiers of the cards that were found. Query syntax is
* [documented here](https://docs.ankiweb.net/searching.html).
*
* Optionally, the `reorderCards` property can be provided to reorder the
* cards shown in the _Card Browser_. This is an array including the `order`
* and `columnId` objects. `order` can be either `ascending` or `descending`
* while `columnId` can be one of several column identifiers (as documented
* in the [Anki source
* code](https://github.com/ankitects/anki/blob/main/rslib/src/browser_table.rs)).
* The specified column needs to be visible in the _Card Browser_.
*/
guiBrowse: (params: {
query: string;
reorderCards?: {
columnId: CardBrowserColumns;
order: "ascending" | "descending";
};
}) => Promise<number[]>;
/**
* Requests a database check, but returns immediately without waiting for
* the check to complete. Therefore, the action will always return `true`
* even if errors are detected during the database check.
*/
guiCheckDatabase: () => Promise<true>;
/**
* Returns information about the current card or `null` if not in review
* mode.
*/
guiCurrentCard: () => Promise<CardInfo | null>;
/**
* Opens the _Deck Browser_ dialog.
*/
guiDeckBrowser: () => Promise<null>;
/**
* Opens the _Deck Overview_ dialog for the deck with the given name;
* returns `true` if succeeded or `false` otherwise.
*/
guiDeckOverview: (params: {
name: string;
}) => Promise<boolean>;
/**
* Starts review for the deck with the given name; returns `true` if
* succeeded or `false` otherwise.
*/
guiDeckReview: (params: {
name: string;
}) => Promise<boolean>;
/**
* Opens the _Edit_ dialog with a note corresponding to given note ID. The
* dialog is similar to the _Edit Current_ dialog, but:
*
* - has a Preview button to preview the cards for the note
* - has a Browse button to open the browser with these cards
* - has Previous/Back buttons to navigate the history of the dialog
* - has no bar with the Close button
*/
guiEditNote: (params: {
note: number;
}) => Promise<null>;
/**
* Schedules a request to gracefully close Anki. This operation is
* asynchronous, so it will return immediately and won’t wait until the Anki
* process actually terminates.
*/
guiExitAnki: () => Promise<null>;
/**
* Invokes the _Import… (Ctrl+Shift+I)_ dialog with an optional file path.
* Brings up the dialog for user to review the import. Supports all file
* types that Anki supports. Brings open file dialog if no path is provided.
* Forward slashes must be used in the path on Windows. Only supported for
* Anki 2.1.52+.
*/
guiImportFile: (params: {
path: string;
}) => Promise<null>;
/**
* Plays any Audio for the current side of the current card; returns true if
* succeeded or false otherwise.
*/
guiPlayAudio: () => Promise<true>;
/**
* Finds the open instance of the Card Browser dialog and selects a card
* given a card identifier. Returns true if the Card Browser is open, false
* otherwise.
*/
guiSelectCard: (params: {
card: number;
}) => Promise<boolean>;
/**
* Finds the open instance of the _Card Browser_ dialog and returns an array
* of identifiers of the notes that are selected. Returns an empty list if
* the browser is not open.
*/
guiSelectedNotes: () => Promise<number[]>;
/**
* @deprecated Actually selects card IDs. Use `guiSelectCard` instead.'
*/
guiSelectNote: (params: {
note: number;
}) => Promise<boolean>;
/**
* Shows answer text for the current card; returns `true` if in review mode
* or `false` otherwise.
*/
guiShowAnswer: () => Promise<boolean>;
/**
* Shows question text for the current card; returns `true` if in review
* mode or `false` otherwise.
*/
guiShowQuestion: () => Promise<boolean>;
/**
* Starts or resets the `timerStarted` value for the current card. This is
* useful for deferring the start time to when it is displayed via the API,
* allowing the recorded time taken to answer the card to be more accurate
* when calling `guiAnswerCard`.
*/
guiStartCardTimer: () => Promise<true>;
/**
* Undo the last action / card; returns `true` if succeeded or `false`
* otherwise.
*/
guiUndo: () => Promise<boolean>;
};
/**
* __Media Actions__
*
* [Documentation](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md#media-actions)
*/
readonly media: {
/**
* Deletes the specified file inside the media folder.
*/
deleteMediaFile: (params: {
filename: string;
}) => Promise<null>;
/**
* Gets the full path to the `collection.media` folder of the currently
* opened profile.
*/
getMediaDirPath: () => Promise<string>;
/**
* Gets the names of media files matched the pattern. Returning all names by
* default.
*/
getMediaFilesNames: (params: {
pattern: string;
}) => Promise<string[]>;
/**
* Retrieves the base64-encoded contents of the specified file, returning
* `false` if the file does not exist.
*/
retrieveMediaFile: (params: {
filename: string;
}) => Promise<string | false>;
/**
* Stores a file with the specified base64-encoded contents inside the media
* folder. Alternatively you can specify a absolute file path, or a url from
* where the file shell be downloaded. If more than one of `data`, `path`
* and `url` are provided, the `data` field will be used first, then `path`,
* and finally `url`. To prevent Anki from removing files not used by any
* cards (e.g. for configuration files), prefix the filename with an
* underscore. These files are still synchronized to AnkiWeb. Any existing
* file with the same name is deleted by default. Set `deleteExisting` to
* `false` to prevent that by [letting Anki give the new file a
* non-conflicting
* name](https://github.com/ankitects/anki/blob/aeba725d3ea9628c73300648f748140db3fdd5ed/rslib/src/media/files.rs#L194).
*/
storeMediaFile: (params: {
data?: string;
deleteExisting?: boolean;
filename: string;
path?: string;
url?: string;
}) => Promise<string>;
};
/**
* __Miscellaneous Actions__
*
* [Documentation](https://git.sr.ht/~foosoft/anki-connect/tree/25.11.9.0/item/README.md#miscellaneous-actions)
*/
readonly miscellaneous: {
/**
* Gets information about the AnkiConnect APIs available. The request
* supports the following params:
*
* - `scopes` - An array of scopes to get reflection information about. The
* only currently supported value is `"actions"`.
* - `actions` - Either `null` or an array of API method names to check for.
* If the value is `null`, the result will list all of the available API
* actions. If the value is an array of strings, the result will only
* contain actions which were in this array.
*/
apiReflect: (params: {
actions: null | string[];
scopes: Array<"actions">;
}) => Promise<{
actions: string[];
scopes: string[];
}>;
/**
* Exports a given deck in `.apkg` format. Returns `true` if successful or
* `false` otherwise. The optional property `includeSched` (default is
* `false`) can be specified to include the cards’ scheduling data.
*/
exportPackage: (params: {
deck: string;
includeSched?: boolean;
path: string;
}) => Promise<boolean>;
/**
* Retrieve the active profile.
*/
getActiveProfile: () => Promise<string>;
/**
* Retrieve the list of profiles.
*/
getProfiles: () => Promise<string[]>;
/**
* Imports a file in `.apkg` format into the collection. Returns `true` if
* successful or `false` otherwise. Note that the file path is relative to
* Anki’s collection.media folder, not to the client.
*/
importPackage: (params: {
path: string;
}) => Promise<boolean>;
/**
* Selects the profile specified in request.
*/
loadProfile: (params: {
name: string;
}) => Promise<true>;
/**
* Performs multiple actions in one request, returning an array with the
* response of each action (in the given order).
*/
multi: (params: {
actions: Array<{
action: Requests["action"];
params?: Requests["params"];
version?: number;
}>;
}) => Promise<({
error: null | string;
result: boolean[];
} | {
error: null | string;
result: (boolean | null)[];
} | {
error: null | string;
result: CardInfo[];
} | {
error: null | string;
result: {
cardId: number;
mod: number;
};
} | {
error: null | string;
result: number[];
} | {
error: null | string;
result: null;
} | {
error: null | string;
result: number[] | number[][];
} | {
error: null | string;
result: boolean;
} | {
error: null | string;
result: number | false;
} | {
error: null | string;
result: number;
} | {
error: null | string;
result: string[];
} | {
error: null | string;
result: Record<string, number>;
} | {
error: null | string;
result: {
autoplay: boolean;
dyn: 1 | false;
id: number;
lapse: {
delays: number[];
leechAction: number;
leechFails: number;
minInt: number;
mult: number;
};
maxTaken: number;
mod: number;
name: string;
new: {
bury: boolean;
delays: number[];
initialFactor: number;
ints: number[];
order: number;
perDay: number;
separate: boolean;
};
replayq: boolean;
rev: {
bury: boolean;
ease4: number;
fuzz: number;
ivlFct: number;
maxIvl: number;
minSpace: number;
perDay: number;
};
timer: number;
usn: number;
};
} | {
error: null | string;
result: Record<string, number[]>;
} | {
error: null | string;
result: Record<string, {
deck_id: number;
learn_count: number;
name: string;
new_count: number;
review_count: number;
total_in_deck: number;
}>;
} | {
error: null | string;
result: true;
} | {
error: null | string;
result: CardInfo | null;
} | {
error: null | string;
result: string;
} | {
error: null | string;
result: string | false;
} | {
error: null | string;
result: {
actions: string[];
scopes: string[];
};
} | {
error: null | string;
result: ({
error: null | string;
result: boolean[];
} | {
error: null | string;
result: (boolean | null)[];
} | {
error: null | string;
result: CardInfo[];
} | {
error: null | string;
result: {
cardId: number;
mod: number;
};
} | {
error: null | string;
result: number[];
} | {
error: null | string;
result: null;
} | {
error: null | string;
result: number[] | number[][];
} | {
error: null | string;
result: boolean;
} | {
error: null | string;
result: number | false;
} | {
error: null | string;
result: number;
} | {
error: null | string;
result: string[];
} | {
error: null | string;
result: Record<string, number>;
} | {
error: null | string;
result: {
autoplay: boolean;
dyn: 1 | false;
id: number;
lapse: {
delays: number[];
leechAction: number;
leechFails: number;
minInt: number;
mult: number;
};
maxTaken: number;
mod: number;
name: string;
new: {
bury: boolean;
delays: number[];
initialFactor: number;
ints: number[];
order: number;
perDay: number;
separate: boolean;
};
replayq: boolean;
rev: {
bury: boolean;
ease4: number;
fuzz: number;
ivlFct: number;
maxIvl: number;
minSpace: number;
perDay: number;
};
timer: number;
usn: number;
};
} | {
error: null | string;
result: Record<string, number[]>;
} | {
error: null | string;
result: Record<string, {
deck_id: number;
learn_count: number;
name: string;
new_count: number;
review_count: number;
total_in_deck: number;
}>;
} | {
error: null | string;
result: true;
} | {
error: null | string;
result: CardInfo | null;
} | {
error: null | string;
result: string;
} | {
error: null | string;
result: string | false;
} | {
error: null | string;
result: {
actions: string[];
scopes: string[];
};
} | /*elided*/any | {
error: null | string;
result: {
permission: "denied";
} | {
permission: "granted";
requireApiKey: boolean;
version: boolean;
};
} | {
error: null | string;
result: {
css: string;
did: null;
flds: {
collapsed: boolean;
description: string;
excludeFromSearch: boolean;
font: string;
id: number;
name: string;
ord: number;
plainText: boolean;
preventDeletion: boolean;
rtl: boolean;
size: number;
sticky: boolean;
tag: null;
}[];
id: number;
latexPost: string;
latexPre: string;
latexsvg: boolean;
mod: number;
name: string;
originalStockKind: number;
req: Array<[number, string, number[]]>;
sortf: number;
tmpls: {
afmt: string;
bafmt: string;
bfont: string;
bqfmt: string;
bsize: number;
did: null;
id: number;
name: string;
ord: number;
qfmt: string;
}[];
type: number;
usn: number;
};
} | {
error: null | string;
result: {
css: string;
did: null;
flds: {
collapsed: boolean;
description: string;
excludeFromSearch: boolean;
font: string;
id: number;
name: string;
ord: number;
plainText: boolean;
preventDeletion: boolean;
rtl: boolean;
size: number;
sticky: boolean;
tag: null;
}[];
id: number;
latexPost: string;
latexPre: string;
latexsvg: boolean;
mod: number;
name: string;
originalStockKind: number;
req: Array<[number, string, number[]]>;
sortf: number;
tmpls: {
afmt: string;
bafmt: string;
bfont: string;
bqfmt: string;
bsize: number;
did: null;
id: number;
name: string;
ord: number;
qfmt: string;
}[];
type: number;
usn: number;
}[];
} | {
error: null | string;
result: {
css: string;
did: null;
flds: {
collapsed: boolean;
description: string;
excludeFromSearch: boolean;
font: string;
id: number;
name: string;
ord: number;
plainText: boolean;
preventDeletion: boolean;
rtl: boolean;
size: number;
sticky: boolean;
tag: null;
}[];
id: number;
latexPost: string;
latexPre: string;
latexsvg: boolean;
mod: number;
name: string;
originalStockKind: number;
req: Array<[number, string, number[]]>;
sortf: number;
tmpls: {
afmt: string;
bafmt: string;
bfont: string;
bqfmt: string;
bsize: number;
did: null;
id: number;
name: string;
ord: number;
qfmt: string;
}[];
type: number;
usn: number;
}[];
} | {
error: null | string;
result: Record<string, {
font: string;
size: number;
}>;
} | {
error: null | string;
result: Record<string, [string[], string[]]>;
} | {
error: null | string;
result: {
css: string;
};
} | {
error: null | string;
result: Record<string, {
[key: string]: string;
Back: string;
Front: string;
}>;
} | {
error: null | string;
result: number | null;
} | {
error: null | string;
result: (string | null)[] | null;
} | {
error: null | string;
result: ({
canAdd: false;
error: string;
} | {
canAdd: true;
})[];
} | {
error: null | string;
result: {
cards: number[];
fields: Record<string, {
order: number;
value: string;
}>;
mod: number;
modelName: string;
noteId: number;
profile: string;
tags: string[];
}[];
} | {
error: null | string;
result: {
mod: number;
noteId: number;
}[];
} | {
error: null | string;
result: [reviewTime: number, cardID: number, usn: number, buttonPressed: number, newInterval: number, previousInterval: number, newFactor: number, reviewDuration: number, reviewType: number][];
} | {
error: null | string;
result: [string, number][];
} | {
error: null | string;
result: Record<string, {
ease: number;
factor: number;
id: number;
ivl: number;
lastIvl: number;
time: number;
type: number;
usn: number;
}[]>;
} | {
error: null | string;
result: Requests["response"];
})[];
} | {
error: null | string;
result: {
permission: "denied";
} | {
permission: "granted";
requireApiKey: boolean;
version: boolean;
};
} | {
error: null | string;
result: {
css: string;
did: null;
flds: {
collapsed: boolean;
description: string;
excludeFromSearch: boolean;
font: string;
id: number;
name: string;
ord: number;
plainText: boolean;
preventDeletion: boolean;
rtl: boolean;
size: number;
sticky: boolean;
tag: null;
}[];
id: number;
latexPost: string;
latexPre: string;
latexsvg: boolean;
mod: number;
name: string;
originalStockKind: number;
req: Array<[number, string, number[]]>;
sortf: number;
tmpls: {
afmt: string;
bafmt: string;
bfont: string;
bqfmt: string;
bsize: number;
did: null;
id: number;
name: string;
ord: number;
qfmt: string;
}[];
type: number;
usn: number;
};
} | {
error: null | string;
result: {
css: string;
did: null;
flds: {
collapsed: boolean;
description: string;
excludeFromSearch: boolean;
font: string;
id: number;
name: string;
ord: number;
plainText: boolean;
preventDeletion: boolean;
rtl: boolean;
size: numbe