From 9516f287c87421d59477212e3efb8e0f29469f61 Mon Sep 17 00:00:00 2001 From: Pacharapol Withayasakpunt Date: Wed, 27 Apr 2022 01:41:52 +0700 Subject: [PATCH] strongly typed AnkiConnect --- src/anki.ts | 37 ++++++++++ src/ankiconnect.ts | 178 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/anki.ts diff --git a/src/anki.ts b/src/anki.ts new file mode 100644 index 0000000..144a82d --- /dev/null +++ b/src/anki.ts @@ -0,0 +1,37 @@ +export interface IAnkiCard { + sortf: number; + did: number; + latexPre: string; + latexPost: string; + mod: number; + usn: number; + vers: number[]; + type: number; + css: string; + name: string; + flds: IAnkiField[]; + tmpls: IAnkiTemplate[]; + tags: string[]; + id: number | string; + req: unknown[]; +} + +export interface IAnkiField { + name: string; + ord: number; + sticky: boolean; + rtl: boolean; + font: string; + size: number; + media: unknown[]; +} + +export interface IAnkiTemplate { + name: string; + ord: number; + qfmt: string; + afmt: string; + did: number | null; + bqfmt: string; + bamt: string; +} diff --git a/src/ankiconnect.ts b/src/ankiconnect.ts index 0c6c6f6..11e3e5d 100644 --- a/src/ankiconnect.ts +++ b/src/ankiconnect.ts @@ -1,5 +1,12 @@ import axios, { AxiosInstance } from 'axios'; +import { IAnkiCard } from './anki'; + +export interface IAnkiConnectResponse { + result: IAnkiConnectActions[K]; + error: string | null; +} + export interface IAnkiConnectActions extends Record< string, @@ -81,6 +88,152 @@ export interface IAnkiConnectActions }; result: null; }; + + // Miscellaneous Actions + exportPackage: { + /** + * Exports a given deck in .apkg format. + * Returns true if successful or false otherwise. + */ + params: { + deck: string; + path: string; + /** + * The optional property includeSched (default is false) can be specified to include the cards’ scheduling data. + */ + includeSched?: boolean; + }; + result: boolean; + }; + importPackage: { + /** + * Imports a file in .apkg format into the collection. + * Returns true if successful or false otherwise. + */ + params: { + /** + * Note that the file path is relative to Anki’s collection.media folder, not to the client. + */ + path: string; + }; + result: boolean; + }; + reloadCollection: { + result: null; + }; + + // Model Actions + modelNames: { + result: string[]; + }; + modelNamesAndIds: { + result: { + [modelName: string]: number; + }; + }; + modelFieldNames: { + params: { + modelName: string; + }; + result: string[]; + }; + modelFieldsOnTemplates: { + params: { + modelName: string; + }; + result: { + [side: string]: [string[], string[]]; + }; + }; + createModel: { + params: { + modelName: string; + inOrderFields: string[]; + /** + * Default to built-in CSS + */ + css?: string; + isCloze?: boolean; + cardTemplates: IAnkiConnectCardTemplate[]; + }; + result: IAnkiCard; + }; + modelTemplates: { + params: { + modelName: string; + }; + result: { + [side: string]: { + Front: string; + Back: string; + }; + }; + }; + modelStyling: { + params: { + modelName: string; + }; + result: { + css: string; + }; + }; + updateModelTemplates: { + /** + * Modify the templates of an existing model by name. + * Only specifies cards and specified sides will be modified. + * + * If an existing card or side is not included in the request, it will be left unchanged. + */ + params: { + model: { + name: string; + templates: { + [side: string]: { + Front?: string; + Back?: string; + }; + }; + }; + }; + result: null; + }; + updateModelStyling: { + params: { + model: { + name: string; + css: string; + }; + }; + result: null; + }; + findAndReplaceInModels: { + params: { + model: { + modelName: string; + findText: string; + replaceText: string; + front?: boolean; + back?: boolean; + css?: boolean; + }; + }; + result: number; + }; + + // Note Actions + addNote: { + params: {}; + result: number; + }; +} + +export interface IAnkiConnectCardTemplate { + /** + * By default the card names will be Card 1, Card 2, and so on. + */ + Name?: string; + Front: string; + Back: string; } export class AnkiConnect { @@ -95,8 +248,31 @@ export class AnkiConnect { async api( action: A, params: IAnkiConnectActions[A]['params'], + version?: number, + ): Promise; + + async api[]>( + action: 'multi', + params: { + actions: Array< + { + [A in keyof IAnkiConnectActions]: { + action: A; + params: IAnkiConnectActions[A]['params']; + }; + }[keyof IAnkiConnectActions] + >; + }, + version?: number, + ): Promise<{ + result: Results; + }>; + + async api( + action: A, + params: unknown, version = this.version, - ): Promise { + ) { const { data: response } = await this.$axios.post<{ result: any; error: string | null;