├── proto ├── .top_level ├── .clang-format ├── README.md └── anki │ ├── generic.proto │ └── ankihub.proto ├── qt ├── aqt │ ├── py.typed │ ├── import_export │ │ └── __init__.py │ ├── forms │ │ ├── main.py │ │ ├── about.py │ │ ├── addons.py │ │ ├── dconf.py │ │ ├── debug.py │ │ ├── fields.py │ │ ├── forget.py │ │ ├── models.py │ │ ├── stats.py │ │ ├── addcards.py │ │ ├── addfield.py │ │ ├── addmodel.py │ │ ├── addonconf.py │ │ ├── browser.py │ │ ├── changemap.py │ │ ├── edithtml.py │ │ ├── exporting.py │ │ ├── finddupes.py │ │ ├── getaddons.py │ │ ├── importing.py │ │ ├── modelopts.py │ │ ├── preview.py │ │ ├── profiles.py │ │ ├── progress.py │ │ ├── setgroup.py │ │ ├── setlang.py │ │ ├── studydeck.py │ │ ├── synclog.py │ │ ├── taglimit.py │ │ ├── template.py │ │ ├── widgets.py │ │ ├── browserdisp.py │ │ ├── browseropts.py │ │ ├── changemodel.py │ │ ├── clayout_top.py │ │ ├── customstudy.py │ │ ├── editcurrent.py │ │ ├── emptycards.py │ │ ├── findreplace.py │ │ ├── preferences.py │ │ ├── reposition.py │ │ └── filtered_deck.py │ ├── data │ │ ├── qt │ │ │ └── icons │ │ │ │ └── anki.png │ │ └── web │ │ │ ├── imgs │ │ │ ├── more.png │ │ │ ├── favicon.ico │ │ │ ├── text_sub.png │ │ │ ├── paperclip.png │ │ │ ├── text_bold.png │ │ │ ├── text_clear.png │ │ │ ├── text_cloze.png │ │ │ ├── text_super.png │ │ │ ├── text_under.png │ │ │ ├── media-record.png │ │ │ ├── text_italic.png │ │ │ └── anki-logo-thin.png │ │ │ ├── js │ │ │ ├── pycmd.d.ts │ │ │ ├── tsconfig.json │ │ │ └── webview.ts │ │ │ └── css │ │ │ ├── addonconf.scss │ │ │ └── toolbar-bottom.scss │ ├── colors.py │ ├── props.py │ ├── gui_hooks.py │ ├── _macos_helper.py │ ├── browser │ │ └── sidebar │ │ │ └── __init__.py │ └── qt │ │ └── qt6.py ├── tests │ └── __init__.py ├── mac │ ├── anki_mac_helper │ │ └── py.typed │ ├── README.md │ ├── ankihelper.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ ├── xcuserdata │ │ │ │ └── dae.xcuserdatad │ │ │ │ │ ├── UserInterfaceState.xcuserstate │ │ │ │ │ └── xcschemes │ │ │ │ │ └── xcschememanagement.plist │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── dae.xcuserdatad │ │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ ├── update-launcher-env │ ├── pyproject.toml │ ├── build.sh │ └── theme.swift ├── README.md ├── release │ └── .gitignore ├── launcher │ ├── mac │ │ ├── dmg │ │ │ ├── set-dmg-settings.app │ │ │ │ └── Contents │ │ │ │ │ ├── PkgInfo │ │ │ │ │ ├── MacOS │ │ │ │ │ └── applet │ │ │ │ │ └── Resources │ │ │ │ │ ├── applet.icns │ │ │ │ │ ├── applet.rsrc │ │ │ │ │ ├── description.rtfd │ │ │ │ │ └── TXT.rtf │ │ │ │ │ └── Scripts │ │ │ │ │ └── main.scpt │ │ │ ├── dmg_ds_store │ │ │ ├── anki-logo-bg.png │ │ │ └── set-dmg-settings.scpt │ │ ├── icon │ │ │ ├── Assets.car │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── round-1024-512.png │ │ │ └── build.sh │ │ ├── stub.c │ │ ├── notarize.sh │ │ └── entitlements.python.xml │ ├── lin │ │ ├── anki.png │ │ ├── uninstall.sh │ │ ├── anki.desktop │ │ ├── anki.xml │ │ └── README.md │ ├── win │ │ ├── anki-icon.ico │ │ ├── anki-manifest.rc │ │ └── build.bat │ ├── addon │ │ └── manifest.json │ ├── pyproject.toml │ └── build.rs ├── icons │ ├── README.md │ └── sidebar.afdesign ├── tools │ ├── runanki.system.in │ └── build_qrc.py └── runanki.py ├── .rustfmt-empty.toml ├── .version ├── pylib ├── anki │ ├── py.typed │ ├── storage.py │ ├── types.py │ ├── _rsbridge.pyi │ ├── scheduler │ │ └── __init__.py │ ├── statsbg.py │ ├── sync.py │ ├── rsbackend.py │ └── syncserver.py ├── tests │ ├── support │ │ ├── fake.png │ │ ├── text-update.txt │ │ ├── text-tags.txt │ │ ├── mnemo.db │ │ ├── media.apkg │ │ ├── anki12.anki │ │ ├── update1.apkg │ │ ├── update2.apkg │ │ ├── anki12-due.anki │ │ ├── anki12-broken.anki │ │ ├── anki2-alpha.anki2 │ │ ├── diffmodels1.anki │ │ ├── diffmodels2-1.apkg │ │ ├── diffmodels2-2.apkg │ │ ├── diffmodels2.anki │ │ ├── invalid-ords.anki │ │ ├── suspended12.anki │ │ ├── diffmodeltemplates-1.apkg │ │ ├── diffmodeltemplates-2.apkg │ │ └── text-2fields.txt │ ├── __init__.py │ ├── test_utils.py │ └── test_template.py ├── rsbridge │ ├── .gitignore │ └── Cargo.toml ├── README.md ├── .gitignore └── tools │ └── genbuildinfo.py ├── .python-version ├── rslib ├── README.md ├── .gitignore ├── src │ ├── storage │ │ ├── config │ │ │ ├── get.sql │ │ │ ├── get_entry.sql │ │ │ └── add.sql │ │ ├── dbcheck │ │ │ ├── invalid_ids_drop.sql │ │ │ ├── invalid_ids_count.sql │ │ │ └── invalid_ids_update.sql │ │ ├── tag │ │ │ ├── get.sql │ │ │ ├── add.sql │ │ │ ├── update.sql │ │ │ └── alloc_id.sql │ │ ├── graves │ │ │ ├── remove.sql │ │ │ └── add.sql │ │ ├── note │ │ │ ├── get_tags.sql │ │ │ ├── is_orphaned.sql │ │ │ ├── update_tags.sql │ │ │ ├── search_nids_setup.sql │ │ │ ├── get.sql │ │ │ ├── get_without_fields.sql │ │ │ ├── update.sql │ │ │ ├── notes_types_checksums_decks.sql │ │ │ ├── add_if_unique.sql │ │ │ ├── add_or_update.sql │ │ │ └── add.sql │ │ ├── notetype │ │ │ ├── get_notetype_names.sql │ │ │ ├── update_fields.sql │ │ │ ├── get_notetype.sql │ │ │ ├── get_fields.sql │ │ │ ├── update_notetype_config.sql │ │ │ ├── update_templates.sql │ │ │ ├── add_or_update.sql │ │ │ ├── field_names_for_notes.sql │ │ │ ├── get_templates.sql │ │ │ ├── highest_card_ord.sql │ │ │ ├── get_use_counts.sql │ │ │ ├── add_notetype.sql │ │ │ └── existing_cards.sql │ │ ├── card │ │ │ ├── fix_mod.sql │ │ │ ├── at_or_above_position.sql │ │ │ ├── search_cids_setup_ordered.sql │ │ │ ├── search_cids_setup.sql │ │ │ ├── get_card_entry.sql │ │ │ ├── fix_ordinal.sql │ │ │ ├── get_ignored_before_count.sql │ │ │ ├── new_cards.sql │ │ │ ├── fix_ivl.sql │ │ │ ├── search_cards_of_notes_into_table.sql │ │ │ ├── fix_low_ease.sql │ │ │ ├── active_new_cards.sql │ │ │ ├── intraday_due.sql │ │ │ ├── due_cards.sql │ │ │ ├── get_card.sql │ │ │ ├── deck_due_counts.sql │ │ │ ├── update_card.sql │ │ │ ├── fix_due_new.sql │ │ │ ├── siblings_for_bury.sql │ │ │ ├── fix_due_other.sql │ │ │ ├── add_or_update.sql │ │ │ ├── add_card_if_unique.sql │ │ │ ├── fix_odue.sql │ │ │ └── congrats.sql │ │ ├── deckconfig │ │ │ ├── get.sql │ │ │ ├── update.sql │ │ │ ├── add_if_unique.sql │ │ │ ├── add_or_update.sql │ │ │ └── add.sql │ │ ├── deck │ │ │ ├── get_deck.sql │ │ │ ├── cards_for_deck.sql │ │ │ ├── missing-decks.sql │ │ │ ├── add_or_update_deck.sql │ │ │ ├── update_deck.sql │ │ │ ├── active_deck_ids_sorted.sql │ │ │ ├── update_active.sql │ │ │ ├── all_decks_of_search_notes.sql │ │ │ ├── alloc_id.sql │ │ │ ├── all_decks_and_original_of_search_cards.sql │ │ │ └── due_counts.sql │ │ ├── revlog │ │ │ ├── v2_upgrade.sql │ │ │ ├── studied_today.sql │ │ │ ├── get.sql │ │ │ ├── time_of_last_review.sql │ │ │ ├── studied_today_by_deck.sql │ │ │ ├── add.sql │ │ │ └── fix_props.sql │ │ └── upgrades │ │ │ ├── schema17_upgrade.sql │ │ │ ├── schema11_downgrade.sql │ │ │ ├── schema18_downgrade.sql │ │ │ ├── schema18_upgrade.sql │ │ │ └── schema14_upgrade.sql │ ├── sync │ │ ├── media │ │ │ ├── database │ │ │ │ ├── server │ │ │ │ │ ├── meta │ │ │ │ │ │ ├── get_meta.sql │ │ │ │ │ │ └── set_meta.sql │ │ │ │ │ ├── entry │ │ │ │ │ │ ├── get_entry.sql │ │ │ │ │ │ ├── set_entry.sql │ │ │ │ │ │ └── changes.sql │ │ │ │ │ └── schema_v3.sql │ │ │ │ ├── mod.rs │ │ │ │ └── client │ │ │ │ │ └── schema.sql │ │ │ ├── sanity.rs │ │ │ └── progress.rs │ │ ├── mod.rs │ │ └── collection │ │ │ └── mod.rs │ ├── notetype │ │ ├── cloze_styling.css │ │ ├── styling.css │ │ └── header.tex │ ├── i18n │ │ └── mod.rs │ ├── ankihub │ │ └── mod.rs │ ├── ankidroid │ │ └── mod.rs │ ├── import_export │ │ ├── text │ │ │ └── csv │ │ │ │ └── mod.rs │ │ └── package │ │ │ ├── colpkg │ │ │ └── mod.rs │ │ │ └── apkg │ │ │ └── mod.rs │ ├── search │ │ ├── deck_order.sql │ │ ├── notetype_order.sql │ │ ├── card_mod_order.sql │ │ ├── note_cards_order.sql │ │ ├── note_lapses_order.sql │ │ ├── note_reps_order.sql │ │ ├── note_ease_order.sql │ │ ├── note_interval_order.sql │ │ ├── template_order.sql │ │ ├── note_due_order.sql │ │ ├── note_original_position_order.sql │ │ └── note_decks_order.sql │ ├── image_occlusion │ │ ├── mod.rs │ │ └── notetype.css │ ├── stats │ │ └── mod.rs │ ├── scheduler │ │ ├── fsrs │ │ │ └── mod.rs │ │ └── service │ │ │ └── states │ │ │ └── new.rs │ ├── services.rs │ ├── markdown.rs │ ├── backend │ │ └── adding.rs │ └── card_rendering │ │ └── tts │ │ ├── other.rs │ │ └── mod.rs ├── tests │ └── support │ │ └── mediacheck.anki2 ├── bench.sh ├── linkchecker │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── process │ └── Cargo.toml ├── io │ └── Cargo.toml ├── i18n │ └── src │ │ ├── generated.rs │ │ └── generated_launcher.rs ├── benches │ └── benchmark.rs ├── sync │ └── Cargo.toml ├── proto_gen │ └── Cargo.toml └── proto │ ├── build.rs │ └── Cargo.toml ├── .dockerignore ├── yarn.bat ├── ts ├── .gitignore ├── lib │ ├── tslib │ │ ├── image-import.d.ts │ │ ├── i18n │ │ │ └── index.ts │ │ ├── shadow-dom.d.ts │ │ ├── promise.ts │ │ ├── globals.ts │ │ ├── ui.ts │ │ ├── node.ts │ │ ├── functional.ts │ │ ├── context-keys.ts │ │ ├── typing.ts │ │ ├── parsing.ts │ │ ├── nightmode.ts │ │ └── helpers.ts │ ├── generated │ │ └── README.md │ ├── sveltelib │ │ ├── event-predicate.d.ts │ │ ├── composition.ts │ │ └── position │ │ │ └── position-algorithm.d.ts │ ├── components │ │ ├── RenderChildren.svelte │ │ ├── DropdownDivider.svelte │ │ ├── Spacer.svelte │ │ ├── ErrorPage.svelte │ │ ├── CheckBox.svelte │ │ ├── WithContext.svelte │ │ ├── SettingTitle.svelte │ │ ├── Icon.svelte │ │ ├── Row.svelte │ │ ├── types.ts │ │ ├── context-keys.ts │ │ └── FloatingArrow.svelte │ ├── tag-editor │ │ ├── index.ts │ │ ├── tag-options-button │ │ │ └── index.ts │ │ ├── TagSpacer.svelte │ │ └── TagDeleteBadge.svelte │ ├── domlib │ │ ├── index.ts │ │ ├── surround │ │ │ ├── tree │ │ │ │ ├── index.ts │ │ │ │ ├── block-node.ts │ │ │ │ └── element-node.ts │ │ │ └── index.ts │ │ └── content-editable.ts │ └── sass │ │ ├── bootstrap-forms.scss │ │ ├── night-mode.scss │ │ ├── bootstrap-dark.scss │ │ ├── card-counts.scss │ │ └── core.scss ├── reviewer │ ├── reviewer_extras.scss │ └── index_wrapper.ts ├── routes │ ├── graphs │ │ ├── graphs-base.scss │ │ ├── _true-retention-base.scss │ │ ├── HoverColumns.svelte │ │ ├── graph-styles.ts │ │ ├── InputBox.svelte │ │ └── AxisTicks.svelte │ ├── congrats │ │ ├── congrats-base.scss │ │ ├── +page.svelte │ │ └── +page.ts │ ├── image-occlusion │ │ ├── notes-toolbar │ │ │ ├── index.ts │ │ │ └── NotesToolbar.svelte │ │ ├── types.ts │ │ ├── fabric.d.ts │ │ ├── tools │ │ │ ├── index.ts │ │ │ └── tool-cursor.ts │ │ ├── [...imagePathOrNoteId] │ │ │ └── +page.svelte │ │ ├── shapes │ │ │ └── index.ts │ │ ├── lib.ts │ │ ├── image-occlusion-base.scss │ │ └── review.scss │ ├── +error.svelte │ ├── import-page │ │ ├── [...path] │ │ │ ├── +page.ts │ │ │ └── +page.svelte │ │ ├── import-page-base.scss │ │ ├── TableCell.svelte │ │ └── TableCellWithTooltip.svelte │ ├── tmp │ │ └── _page.ts │ ├── deck-options │ │ ├── HtmlAddon.svelte │ │ ├── ParamsSearchRow.svelte │ │ ├── [deckId] │ │ │ └── +page.ts │ │ ├── Warning.svelte │ │ ├── StepsInput.svelte │ │ ├── ParamsInputRow.svelte │ │ ├── GlobalLabel.svelte │ │ └── Addons.svelte │ ├── import-anki-package │ │ ├── Header.svelte │ │ ├── [...path] │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ │ └── import-anki-package-base.scss │ ├── import-csv │ │ ├── [...path] │ │ │ └── +page.svelte │ │ └── import-csv-base.scss │ ├── change-notetype │ │ ├── [...notetypeIds] │ │ │ └── +page.svelte │ │ └── change-notetype-base.scss │ ├── +layout.ts │ ├── +layout.svelte │ └── card-info │ │ ├── CardInfoPlaceholder.svelte │ │ └── [cardId] │ │ └── +page.ts ├── editor │ ├── LabelName.svelte │ ├── image-overlay │ │ └── index.ts │ ├── mathjax-overlay │ │ └── index.ts │ ├── index.ts │ ├── plain-text-input │ │ ├── index.ts │ │ └── transform.ts │ ├── editor-toolbar │ │ └── index.ts │ ├── rich-text-input │ │ ├── index.ts │ │ └── StyleLink.svelte │ ├── editor-base.scss │ ├── ReviewerEditor.svelte │ ├── Notification.svelte │ ├── HandleBackground.svelte │ └── DuplicateLink.svelte ├── src │ ├── hooks.client.js │ ├── app.html │ └── app.d.ts ├── README.md ├── editable │ └── index.ts ├── page.html ├── html-filter │ └── helpers.ts ├── yarn.sh ├── mathjax │ └── mathjax-types.d.ts ├── tsconfig.json ├── icons │ └── contain-plus.svg ├── transform_ts.mjs └── bundle_ts.mjs ├── .config └── nextest.toml ├── check ├── .yarnrc.yml ├── ftl ├── .gitignore ├── core │ ├── keyboard.ftl │ ├── findreplace.ftl │ ├── network.ftl │ ├── undo.ftl │ ├── help.ftl │ ├── media.ftl │ ├── empty-cards.ftl │ ├── change-notetype.ftl │ └── adding.ftl ├── ftl ├── usage │ └── no-deprecate.json ├── README.md ├── update-ankidroid-usage.sh ├── move-from-ankimobile ├── update-ankimobile-usage.sh ├── qt │ ├── profiles.ftl │ └── preferences.ftl ├── copy-core-string.sh └── Cargo.toml ├── tools ├── runopt ├── install-n2 ├── profile ├── unused-rust-deps ├── build ├── build.bat ├── dmypy ├── build-x64-mac ├── run-qt6.6 ├── run-qt6.7 ├── run-qt6.8 ├── ninja.bat ├── update-launcher-env.bat ├── run.py ├── clean ├── build-arm-lin ├── rebuild-web ├── update-launcher-env ├── minilints │ └── Cargo.toml └── publish ├── .gitattributes ├── cargo ├── format │ └── rust-toolchain.toml └── README.md ├── rust-toolchain.toml ├── yarn ├── .cursor └── rules │ └── building.md ├── .buildkite ├── linux │ ├── docker │ │ ├── common.inc │ │ ├── buildkite.cfg │ │ ├── environment │ │ ├── build.sh │ │ └── run.sh │ ├── release-entrypoint │ └── entrypoint ├── mac │ └── entrypoint └── windows │ └── entrypoint.bat ├── .prettierrc ├── python ├── mkempty.py ├── version.py └── sphinx │ ├── build.py │ └── index.rst ├── .gitmodules ├── .rustfmt.toml ├── .vscode.dist ├── extensions.json └── tasks.json ├── .gitignore ├── ninja ├── docs └── mac.md ├── run.bat ├── .github └── ISSUE_TEMPLATE │ └── config.yml ├── .idea.dist └── repo.iml └── .cargo └── config.toml /proto/.top_level: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /qt/aqt/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rustfmt-empty.toml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | 25.09.2 2 | -------------------------------------------------------------------------------- /pylib/anki/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /qt/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13.5 2 | -------------------------------------------------------------------------------- /qt/aqt/import_export/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /qt/mac/anki_mac_helper/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pylib/tests/support/fake.png: -------------------------------------------------------------------------------- 1 | abc 2 | -------------------------------------------------------------------------------- /rslib/README.md: -------------------------------------------------------------------------------- 1 | Anki's Rust code. 2 | -------------------------------------------------------------------------------- /pylib/tests/support/text-update.txt: -------------------------------------------------------------------------------- 1 | 1 x 2 | -------------------------------------------------------------------------------- /qt/README.md: -------------------------------------------------------------------------------- 1 | Python's Qt GUI is in aqt/ 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | target/ 3 | out/ -------------------------------------------------------------------------------- /proto/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: google 2 | -------------------------------------------------------------------------------- /yarn.bat: -------------------------------------------------------------------------------- 1 | call .\out\extracted\node\yarn %* 2 | -------------------------------------------------------------------------------- /pylib/rsbridge/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /rslib/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | .build 3 | target 4 | -------------------------------------------------------------------------------- /ts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn-error.log 3 | -------------------------------------------------------------------------------- /qt/aqt/forms/main.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.main_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/release/.gitignore: -------------------------------------------------------------------------------- 1 | pyproject.toml 2 | pyproject.toml.old -------------------------------------------------------------------------------- /ts/lib/tslib/image-import.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg"; 2 | -------------------------------------------------------------------------------- /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [store] 2 | dir = "out/tests/nextest" 3 | -------------------------------------------------------------------------------- /check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./ninja format && ./ninja check 4 | -------------------------------------------------------------------------------- /qt/aqt/forms/about.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.about_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/addons.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.addons_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/dconf.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.dconf_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/debug.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.debug_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/fields.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.fields_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/forget.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.forget_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/models.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.models_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/stats.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.stats_qt6 import * 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | enableScripts: false 3 | -------------------------------------------------------------------------------- /qt/aqt/forms/addcards.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.addcards_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/addfield.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.addfield_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/addmodel.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.addmodel_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/addonconf.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.addonconf_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/browser.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.browser_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/changemap.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.changemap_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/edithtml.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.edithtml_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/exporting.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.exporting_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/finddupes.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.finddupes_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/getaddons.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.getaddons_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/importing.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.importing_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/modelopts.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.modelopts_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/preview.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.preview_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/profiles.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.profiles_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/progress.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.progress_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/setgroup.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.setgroup_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/setlang.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.setlang_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/studydeck.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.studydeck_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/synclog.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.synclog_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/taglimit.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.taglimit_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/template.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.template_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/widgets.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.widgets_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.app/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPLaplt -------------------------------------------------------------------------------- /ftl/.gitignore: -------------------------------------------------------------------------------- 1 | usage/* 2 | !usage/no-deprecate.json 3 | mobile-repo 4 | -------------------------------------------------------------------------------- /ftl/core/keyboard.ftl: -------------------------------------------------------------------------------- 1 | keyboard-ctrl = Ctrl 2 | keyboard-shift = Shift 3 | -------------------------------------------------------------------------------- /pylib/tests/support/text-tags.txt: -------------------------------------------------------------------------------- 1 | foo bar baz,qux 2 | foo2 bar2 baz2 3 | -------------------------------------------------------------------------------- /qt/aqt/forms/browserdisp.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.browserdisp_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/browseropts.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.browseropts_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/changemodel.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.changemodel_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/clayout_top.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.clayout_top_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/customstudy.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.customstudy_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/editcurrent.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.editcurrent_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/emptycards.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.emptycards_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/findreplace.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.findreplace_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/preferences.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.preferences_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/reposition.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.reposition_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/mac/README.md: -------------------------------------------------------------------------------- 1 | Helper library for macOS-specific functionality. 2 | -------------------------------------------------------------------------------- /qt/aqt/forms/filtered_deck.py: -------------------------------------------------------------------------------- 1 | from _aqt.forms.filtered_deck_qt6 import * 2 | -------------------------------------------------------------------------------- /qt/icons/README.md: -------------------------------------------------------------------------------- 1 | Source files used to produce some of the svg/png files. 2 | -------------------------------------------------------------------------------- /rslib/src/storage/config/get.sql: -------------------------------------------------------------------------------- 1 | SELECT val 2 | FROM config 3 | WHERE KEY = ? -------------------------------------------------------------------------------- /ts/reviewer/reviewer_extras.scss: -------------------------------------------------------------------------------- 1 | @use "ts/routes/image-occlusion/review"; 2 | -------------------------------------------------------------------------------- /ftl/ftl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd $(dirname $0)/.. 4 | cargo run -p ftl -- $* 5 | -------------------------------------------------------------------------------- /rslib/src/storage/dbcheck/invalid_ids_drop.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS invalid_ids; -------------------------------------------------------------------------------- /rslib/src/storage/tag/get.sql: -------------------------------------------------------------------------------- 1 | SELECT tag, 2 | usn, 3 | collapsed 4 | FROM tags -------------------------------------------------------------------------------- /tools/runopt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | RELEASE=1 $(dirname $0)/../run $* 6 | -------------------------------------------------------------------------------- /rslib/src/storage/graves/remove.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM graves 2 | WHERE oid = ? 3 | AND type = ? -------------------------------------------------------------------------------- /rslib/src/storage/note/get_tags.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | mod, 3 | usn, 4 | tags 5 | FROM notes -------------------------------------------------------------------------------- /rslib/src/storage/note/is_orphaned.sql: -------------------------------------------------------------------------------- 1 | SELECT COUNT(id) = 0 2 | FROM cards 3 | WHERE nid = ?; -------------------------------------------------------------------------------- /rslib/src/storage/notetype/get_notetype_names.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | name 3 | FROM notetypes -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.ftl -linguist-detectable 3 | cargo/remote/* linguist-vendored 4 | -------------------------------------------------------------------------------- /rslib/src/storage/graves/add.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR IGNORE INTO graves (usn, oid, type) 3 | VALUES (?, ?, ?) -------------------------------------------------------------------------------- /rslib/src/storage/tag/add.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO tags (tag, usn, collapsed) 3 | VALUES (?, ?, ?) -------------------------------------------------------------------------------- /qt/icons/sidebar.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/icons/sidebar.afdesign -------------------------------------------------------------------------------- /qt/launcher/lin/anki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/lin/anki.png -------------------------------------------------------------------------------- /rslib/src/storage/config/get_entry.sql: -------------------------------------------------------------------------------- 1 | SELECT val, 2 | usn, 3 | mtime_secs 4 | FROM config 5 | WHERE KEY = ? -------------------------------------------------------------------------------- /rslib/src/storage/notetype/update_fields.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO fields (ntid, ord, name, config) 2 | VALUES (?, ?, ?, ?); -------------------------------------------------------------------------------- /ts/lib/generated/README.md: -------------------------------------------------------------------------------- 1 | Files in this folder get combined with generated files in out/ts/lib/generated/ 2 | -------------------------------------------------------------------------------- /ftl/usage/no-deprecate.json: -------------------------------------------------------------------------------- 1 | [ 2 | "scheduling-update-soon", 3 | "scheduling-update-later-button" 4 | ] 5 | -------------------------------------------------------------------------------- /pylib/tests/support/mnemo.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/mnemo.db -------------------------------------------------------------------------------- /rslib/src/storage/note/update_tags.sql: -------------------------------------------------------------------------------- 1 | UPDATE notes 2 | SET mod = ?, 3 | usn = ?, 4 | tags = ? 5 | WHERE id = ? -------------------------------------------------------------------------------- /rslib/src/storage/tag/update.sql: -------------------------------------------------------------------------------- 1 | UPDATE tags 2 | SET tag = ?1, 3 | usn = ?, 4 | collapsed = ? 5 | WHERE tag = ?1 -------------------------------------------------------------------------------- /ftl/README.md: -------------------------------------------------------------------------------- 1 | Files related to Anki's translations. 2 | 3 | Please see https://translating.ankiweb.net/anki/developers 4 | -------------------------------------------------------------------------------- /pylib/tests/support/media.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/media.apkg -------------------------------------------------------------------------------- /qt/aqt/data/qt/icons/anki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/qt/icons/anki.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/more.png -------------------------------------------------------------------------------- /qt/aqt/data/web/js/pycmd.d.ts: -------------------------------------------------------------------------------- 1 | declare function pycmd(cmd: string, result_callback?: (arg: unknown) => void): unknown; 2 | -------------------------------------------------------------------------------- /qt/launcher/win/anki-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/win/anki-icon.ico -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_mod.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET mod = cast(mod AS integer) 3 | WHERE mod != cast(mod AS integer) -------------------------------------------------------------------------------- /rslib/src/storage/config/add.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO config (KEY, usn, mtime_secs, val) 3 | VALUES (?, ?, ?, ?) -------------------------------------------------------------------------------- /rslib/src/storage/deckconfig/get.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | name, 3 | mtime_secs, 4 | usn, 5 | config 6 | FROM deck_config -------------------------------------------------------------------------------- /pylib/tests/support/anki12.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/anki12.anki -------------------------------------------------------------------------------- /pylib/tests/support/update1.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/update1.apkg -------------------------------------------------------------------------------- /pylib/tests/support/update2.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/update2.apkg -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/favicon.ico -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_sub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_sub.png -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/dmg_ds_store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/dmg_ds_store -------------------------------------------------------------------------------- /qt/launcher/mac/icon/Assets.car: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/icon/Assets.car -------------------------------------------------------------------------------- /rslib/src/storage/deck/get_deck.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | name, 3 | mtime_secs, 4 | usn, 5 | common, 6 | kind 7 | FROM decks -------------------------------------------------------------------------------- /rslib/src/storage/notetype/get_notetype.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | name, 3 | mtime_secs, 4 | usn, 5 | config 6 | FROM notetypes -------------------------------------------------------------------------------- /rslib/src/storage/revlog/v2_upgrade.sql: -------------------------------------------------------------------------------- 1 | UPDATE revlog 2 | SET ease = ease + 1 3 | WHERE ease IN (2, 3) 4 | AND type IN (0, 2); -------------------------------------------------------------------------------- /pylib/tests/support/anki12-due.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/anki12-due.anki -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/paperclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/paperclip.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_bold.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_clear.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_cloze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_cloze.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_super.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_super.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_under.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_under.png -------------------------------------------------------------------------------- /rslib/src/storage/notetype/get_fields.sql: -------------------------------------------------------------------------------- 1 | SELECT ord, 2 | name, 3 | config 4 | FROM fields 5 | WHERE ntid = ? 6 | ORDER BY ord -------------------------------------------------------------------------------- /rslib/src/storage/notetype/update_notetype_config.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO notetype_config (ntid, config) 3 | VALUES (?, ?) -------------------------------------------------------------------------------- /rslib/src/sync/media/database/server/meta/get_meta.sql: -------------------------------------------------------------------------------- 1 | SELECT last_usn, 2 | total_bytes, 3 | total_nonempty_files 4 | FROM meta; -------------------------------------------------------------------------------- /cargo/format/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-03-20" 3 | profile = "minimal" 4 | components = ["rustfmt"] 5 | -------------------------------------------------------------------------------- /ftl/update-ankidroid-usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo run --bin write_ftl_json ftl/usage/ankidroid.json ~/Local/droid/Anki-Android 4 | -------------------------------------------------------------------------------- /pylib/tests/support/anki12-broken.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/anki12-broken.anki -------------------------------------------------------------------------------- /pylib/tests/support/anki2-alpha.anki2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/anki2-alpha.anki2 -------------------------------------------------------------------------------- /pylib/tests/support/diffmodels1.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/diffmodels1.anki -------------------------------------------------------------------------------- /pylib/tests/support/diffmodels2-1.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/diffmodels2-1.apkg -------------------------------------------------------------------------------- /pylib/tests/support/diffmodels2-2.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/diffmodels2-2.apkg -------------------------------------------------------------------------------- /pylib/tests/support/diffmodels2.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/diffmodels2.anki -------------------------------------------------------------------------------- /pylib/tests/support/invalid-ords.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/invalid-ords.anki -------------------------------------------------------------------------------- /pylib/tests/support/suspended12.anki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/suspended12.anki -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/media-record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/media-record.png -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/text_italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/text_italic.png -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/anki-logo-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/anki-logo-bg.png -------------------------------------------------------------------------------- /rslib/src/storage/card/at_or_above_position.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO search_cids 2 | SELECT id 3 | FROM cards 4 | WHERE due >= ? 5 | AND type = ? -------------------------------------------------------------------------------- /rslib/tests/support/mediacheck.anki2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/rslib/tests/support/mediacheck.anki2 -------------------------------------------------------------------------------- /qt/aqt/data/web/imgs/anki-logo-thin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/aqt/data/web/imgs/anki-logo-thin.png -------------------------------------------------------------------------------- /rslib/bench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo install cargo-criterion --version 1.1.0 4 | cargo criterion --bench benchmark --features bench 5 | -------------------------------------------------------------------------------- /rslib/src/storage/deck/cards_for_deck.sql: -------------------------------------------------------------------------------- 1 | SELECT id 2 | FROM cards 3 | WHERE did = ?1 4 | OR ( 5 | odid != 0 6 | AND odid = ?1 7 | ) -------------------------------------------------------------------------------- /rslib/src/storage/deck/missing-decks.sql: -------------------------------------------------------------------------------- 1 | SELECT DISTINCT did 2 | FROM cards 3 | WHERE did NOT IN ( 4 | SELECT id 5 | FROM decks 6 | ); -------------------------------------------------------------------------------- /rslib/src/storage/notetype/update_templates.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO templates (ntid, ord, name, mtime_secs, usn, config) 2 | VALUES (?, ?, ?, ?, ?, ?) -------------------------------------------------------------------------------- /rslib/src/sync/media/database/server/meta/set_meta.sql: -------------------------------------------------------------------------------- 1 | UPDATE meta 2 | SET last_usn = ?, 3 | total_bytes = ?, 4 | total_nonempty_files = ?; -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # older versions may fail to compile; newer versions may fail the clippy tests 3 | channel = "1.89.0" 4 | -------------------------------------------------------------------------------- /tools/install-n2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo install --git https://github.com/evmar/n2.git --rev 53ec691df749277104d1d4201a344fe4243d6d0a 4 | -------------------------------------------------------------------------------- /ts/lib/sveltelib/event-predicate.d.ts: -------------------------------------------------------------------------------- 1 | export interface EventPredicateResult { 2 | reason: string; 3 | originalEvent: Event; 4 | } 5 | -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/set-dmg-settings.scpt -------------------------------------------------------------------------------- /qt/launcher/mac/icon/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /qt/launcher/win/anki-manifest.rc: -------------------------------------------------------------------------------- 1 | #define RT_MANIFEST 24 2 | 1 RT_MANIFEST "anki.exe.manifest" 3 | IDI_ICON1 ICON DISCARDABLE "anki-icon.ico" 4 | -------------------------------------------------------------------------------- /rslib/src/storage/card/search_cids_setup_ordered.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS search_cids; 2 | CREATE TEMPORARY TABLE search_cids (cid integer NOT NULL); -------------------------------------------------------------------------------- /rslib/src/storage/deckconfig/update.sql: -------------------------------------------------------------------------------- 1 | UPDATE deck_config 2 | SET name = ?, 3 | mtime_secs = ?, 4 | usn = ?, 5 | config = ? 6 | WHERE id = ?; -------------------------------------------------------------------------------- /rslib/src/storage/notetype/add_or_update.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO notetypes (id, name, mtime_secs, usn, config) 3 | VALUES (?, ?, ?, ?, ?); -------------------------------------------------------------------------------- /tools/profile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ANKI_PROFILE_CODE=1 ./run 4 | out/pyenv/bin/pip install snakeviz 5 | out/pyenv/bin/snakeviz out/anki.prof 6 | -------------------------------------------------------------------------------- /pylib/tests/support/diffmodeltemplates-1.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/diffmodeltemplates-1.apkg -------------------------------------------------------------------------------- /pylib/tests/support/diffmodeltemplates-2.apkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/pylib/tests/support/diffmodeltemplates-2.apkg -------------------------------------------------------------------------------- /rslib/src/storage/card/search_cids_setup.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS search_cids; 2 | CREATE TEMPORARY TABLE search_cids (cid integer PRIMARY KEY NOT NULL); -------------------------------------------------------------------------------- /rslib/src/storage/deck/add_or_update_deck.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO decks (id, name, mtime_secs, usn, common, kind) 3 | VALUES (?, ?, ?, ?, ?, ?) -------------------------------------------------------------------------------- /rslib/src/storage/deckconfig/add_if_unique.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR IGNORE INTO deck_config (id, name, mtime_secs, usn, config) 3 | VALUES (?, ?, ?, ?, ?); -------------------------------------------------------------------------------- /rslib/src/storage/deckconfig/add_or_update.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO deck_config (id, name, mtime_secs, usn, config) 3 | VALUES (?, ?, ?, ?, ?); -------------------------------------------------------------------------------- /rslib/src/storage/note/search_nids_setup.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS search_nids; 2 | CREATE TEMPORARY TABLE search_nids (nid integer PRIMARY KEY NOT NULL); -------------------------------------------------------------------------------- /rslib/src/sync/media/database/server/entry/get_entry.sql: -------------------------------------------------------------------------------- 1 | SELECT fname, 2 | csum, 3 | size, 4 | usn, 5 | mtime 6 | FROM media 7 | WHERE fname = ?; -------------------------------------------------------------------------------- /rslib/src/sync/media/database/server/entry/set_entry.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO media (fname, csum, size, usn, mtime) 3 | VALUES (?, ?, ?, ?, ?); -------------------------------------------------------------------------------- /rslib/src/storage/card/get_card_entry.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | CASE 4 | WHEN odid = 0 THEN did 5 | ELSE odid 6 | END AS did 7 | FROM cards; -------------------------------------------------------------------------------- /rslib/src/storage/deck/update_deck.sql: -------------------------------------------------------------------------------- 1 | UPDATE decks 2 | SET name = ?, 3 | mtime_secs = ?, 4 | usn = ?, 5 | common = ?, 6 | kind = ? 7 | WHERE id = ? -------------------------------------------------------------------------------- /tools/unused-rust-deps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo install cargo-udeps@0.1.40 4 | cargo +nightly-2023-01-24-x86_64-unknown-linux-gnu udeps --all-targets 5 | -------------------------------------------------------------------------------- /rslib/src/notetype/cloze_styling.css: -------------------------------------------------------------------------------- 1 | .cloze { 2 | font-weight: bold; 3 | color: blue; 4 | } 5 | .nightMode .cloze { 6 | color: lightblue; 7 | } 8 | -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_ordinal.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET ord = max(0, min(30000, ord)), 3 | mod = ?1, 4 | usn = ?2 5 | WHERE ord != max(0, min(30000, ord)) -------------------------------------------------------------------------------- /rslib/src/storage/card/get_ignored_before_count.sql: -------------------------------------------------------------------------------- 1 | SELECT COUNT(DISTINCT cid) 2 | FROM revlog 3 | WHERE id > ? 4 | AND type == 0 5 | AND cid IN search_cids -------------------------------------------------------------------------------- /rslib/src/storage/deck/active_deck_ids_sorted.sql: -------------------------------------------------------------------------------- 1 | SELECT id 2 | FROM decks 3 | WHERE id IN ( 4 | SELECT id 5 | FROM active_decks 6 | ) 7 | ORDER BY name -------------------------------------------------------------------------------- /pylib/README.md: -------------------------------------------------------------------------------- 1 | Anki's Python library code is in anki/. 2 | 3 | The Rust/Python extension module is in rsbridge/; it references the library defined in ../rslib. 4 | -------------------------------------------------------------------------------- /rslib/linkchecker/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | -------------------------------------------------------------------------------- /rslib/src/storage/notetype/field_names_for_notes.sql: -------------------------------------------------------------------------------- 1 | SELECT DISTINCT name 2 | FROM fields 3 | WHERE ntid IN ( 4 | SELECT mid 5 | FROM notes 6 | WHERE id IN -------------------------------------------------------------------------------- /rslib/src/storage/notetype/get_templates.sql: -------------------------------------------------------------------------------- 1 | SELECT ord, 2 | name, 3 | mtime_secs, 4 | usn, 5 | config 6 | FROM templates 7 | WHERE ntid = ? 8 | ORDER BY ord -------------------------------------------------------------------------------- /qt/tools/runanki.system.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | sys.path.append("@PREFIX@/share/anki") 6 | 7 | import aqt 8 | 9 | aqt.run() 10 | -------------------------------------------------------------------------------- /rslib/src/storage/card/new_cards.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | ord, 4 | cast(mod AS integer), 5 | did, 6 | odid 7 | FROM cards 8 | WHERE did = ? 9 | AND queue = 0 -------------------------------------------------------------------------------- /rslib/src/storage/deck/update_active.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO active_decks 2 | SELECT id 3 | FROM decks 4 | WHERE name = ? 5 | OR ( 6 | name >= ? 7 | AND name < ? 8 | ) -------------------------------------------------------------------------------- /rslib/src/storage/note/get.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | guid, 3 | mid, 4 | mod, 5 | usn, 6 | tags, 7 | flds, 8 | cast(sfld AS text), 9 | csum 10 | FROM notes -------------------------------------------------------------------------------- /rslib/src/storage/revlog/studied_today.sql: -------------------------------------------------------------------------------- 1 | SELECT COUNT(), 2 | coalesce(sum(time) / 1000.0, 0.0) 3 | FROM revlog 4 | WHERE id > ? 5 | AND type != ? 6 | AND type != ? -------------------------------------------------------------------------------- /rslib/src/storage/notetype/highest_card_ord.sql: -------------------------------------------------------------------------------- 1 | SELECT coalesce(max(ord), 0) 2 | FROM cards 3 | WHERE nid IN ( 4 | SELECT id 5 | FROM notes 6 | WHERE mid = ? 7 | ) -------------------------------------------------------------------------------- /yarn: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Execute subcommand (eg 'yarn ...') 3 | 4 | set -e 5 | 6 | export PATH="./out/extracted/node/bin:$PATH" 7 | ./out/extracted/node/bin/yarn $* 8 | -------------------------------------------------------------------------------- /.cursor/rules/building.md: -------------------------------------------------------------------------------- 1 | - To build and check the project, use ./check in the root folder (or check.bat on Windows) 2 | - This will format files, then run lints and unit tests. 3 | -------------------------------------------------------------------------------- /qt/launcher/addon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Anki Launcher", 3 | "package": "anki-launcher", 4 | "min_point_version": 50, 5 | "max_point_version": 250600 6 | } 7 | -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_ivl.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET ivl = min(max(round(ivl), 0), 2147483647), 3 | mod = ?1, 4 | usn = ?2 5 | WHERE ivl != min(max(round(ivl), 0), 2147483647) -------------------------------------------------------------------------------- /rslib/src/storage/card/search_cards_of_notes_into_table.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO search_cids 2 | SELECT id 3 | FROM cards 4 | WHERE nid IN ( 5 | SELECT nid 6 | FROM search_nids 7 | ) -------------------------------------------------------------------------------- /tools/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | rm -rf out/wheels/* 6 | RELEASE=2 ./ninja wheels 7 | (cd qt/release && ./build.sh) 8 | echo "wheels are in out/wheels" 9 | -------------------------------------------------------------------------------- /.buildkite/linux/docker/common.inc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ "$(uname -m)" == "x86_64" ]]; then 6 | platform="amd" 7 | else 8 | platform="arm" 9 | fi 10 | -------------------------------------------------------------------------------- /qt/aqt/colors.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from _aqt.colors import * 5 | -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/MacOS/applet -------------------------------------------------------------------------------- /rslib/src/i18n/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | pub(crate) mod service; 4 | -------------------------------------------------------------------------------- /rslib/src/storage/note/get_without_fields.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | guid, 3 | mid, 4 | mod, 5 | usn, 6 | tags, 7 | "", 8 | cast(sfld AS text), 9 | csum 10 | FROM notes -------------------------------------------------------------------------------- /cargo/README.md: -------------------------------------------------------------------------------- 1 | This folder contains: 2 | 3 | - a list of Rust crate licenses, which is checked/updated with ./ninja [check|fix]:minilints 4 | - a nightly toolchain definition for formatting 5 | -------------------------------------------------------------------------------- /pylib/.gitignore: -------------------------------------------------------------------------------- 1 | *.mo 2 | *.pyc 3 | *\# 4 | *~ 5 | .*.swp 6 | .build 7 | .coverage 8 | .DS_Store 9 | .mypy_cache 10 | .pytype 11 | __pycache__ 12 | anki.egg-info 13 | build 14 | dist 15 | -------------------------------------------------------------------------------- /rslib/src/storage/note/update.sql: -------------------------------------------------------------------------------- 1 | UPDATE notes 2 | SET guid = ?, 3 | mid = ?, 4 | mod = ?, 5 | usn = ?, 6 | tags = ?, 7 | flds = ?, 8 | sfld = ?, 9 | csum = ? 10 | WHERE id = ? -------------------------------------------------------------------------------- /tools/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd "%~dp0"\.. 3 | if exist out\wheels rmdir /s /q out\wheels 4 | set RELEASE=2 5 | tools\ninja wheels || exit /b 1 6 | echo wheels are in out/wheels 7 | popd 8 | -------------------------------------------------------------------------------- /tools/dmypy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run mypy in daemon mode for fast checking 4 | 5 | ./ninja pylib qt 6 | MYPY_CACHE_DIR=out/tests/mypy out/pyenv/bin/dmypy run pylib/anki qt/aqt pylib/tests 7 | -------------------------------------------------------------------------------- /rslib/src/storage/deck/all_decks_of_search_notes.sql: -------------------------------------------------------------------------------- 1 | SELECT nid, 2 | did 3 | FROM cards 4 | WHERE nid IN ( 5 | SELECT nid 6 | FROM search_nids 7 | ) 8 | GROUP BY nid 9 | HAVING ord = MIN(ord) -------------------------------------------------------------------------------- /rslib/src/storage/revlog/get.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | cid, 3 | usn, 4 | ease, 5 | cast(ivl AS integer), 6 | cast(lastIvl AS integer), 7 | factor, 8 | time, 9 | type 10 | FROM revlog -------------------------------------------------------------------------------- /ftl/core/findreplace.ftl: -------------------------------------------------------------------------------- 1 | findreplace-notes-updated = 2 | { $total -> 3 | [one] { $changed } of { $total } note updated 4 | *[other] { $changed } of { $total } notes updated 5 | } 6 | -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.icns -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/applet.rsrc -------------------------------------------------------------------------------- /qt/launcher/mac/stub.c: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | int main(void) { 5 | return 0; 6 | } -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_low_ease.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET factor = 2500, 3 | usn = ? 4 | WHERE factor != 0 5 | AND factor <= 2000 6 | AND ( 7 | did IN DECK_IDS 8 | OR odid IN DECK_IDS 9 | ) -------------------------------------------------------------------------------- /rslib/src/storage/notetype/get_use_counts.sql: -------------------------------------------------------------------------------- 1 | SELECT nt.id, 2 | nt.name, 3 | ( 4 | SELECT COUNT(*) 5 | FROM notes n 6 | WHERE nt.id = n.mid 7 | ) 8 | FROM notetypes nt 9 | ORDER BY nt.name -------------------------------------------------------------------------------- /ts/lib/components/RenderChildren.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ts/routes/graphs/graphs-base.scss: -------------------------------------------------------------------------------- 1 | @use "$lib/sass/root-vars"; 2 | @import "$lib/sass/base"; 3 | 4 | button { 5 | margin-bottom: 5px; 6 | } 7 | 8 | html { 9 | height: initial; 10 | } 11 | -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/description.rtfd/TXT.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1671 2 | {\fonttbl} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | } -------------------------------------------------------------------------------- /qt/launcher/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/icon/Assets.xcassets/AppIcon.appiconset/round-1024-512.png -------------------------------------------------------------------------------- /rslib/src/ankihub/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub mod http_client; 5 | pub mod login; 6 | -------------------------------------------------------------------------------- /rslib/src/notetype/styling.css: -------------------------------------------------------------------------------- 1 | .card { 2 | font-family: arial; 3 | font-size: 20px; 4 | line-height: 1.5; 5 | text-align: center; 6 | color: black; 7 | background-color: white; 8 | } 9 | -------------------------------------------------------------------------------- /tools/build-x64-mac: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rustup target add x86_64-apple-darwin 6 | 7 | export MAC_X86=1 8 | 9 | RELEASE=2 ./ninja wheels:anki 10 | echo "wheels are in out/wheels" 11 | -------------------------------------------------------------------------------- /ftl/move-from-ankimobile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Move a translation that previously only existed in AnkiMobile to the core translations. 4 | # 5 | 6 | ./ftl string move ftl/mobile-repo/mobile ftl/core-repo/core $* 7 | -------------------------------------------------------------------------------- /pylib/tests/support/text-2fields.txt: -------------------------------------------------------------------------------- 1 | # this is a test file 2 | 食べる to eat 3 | 飲む to drink 4 | テスト test 5 | to eat 食べる 6 | 飲む to drink 7 | 多すぎる too many fields 8 | not, enough, fields 9 | 遊ぶ 10 | to play 11 | -------------------------------------------------------------------------------- /qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/launcher/mac/dmg/set-dmg-settings.app/Contents/Resources/Scripts/main.scpt -------------------------------------------------------------------------------- /qt/launcher/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "anki-launcher" 3 | version = "1.0.0" 4 | description = "UV-based launcher for Anki." 5 | requires-python = ">=3.9" 6 | dependencies = [ 7 | "anki-release", 8 | ] 9 | -------------------------------------------------------------------------------- /rslib/src/sync/media/database/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub mod client; 5 | pub mod server; 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "printWidth": 88, 4 | "tabWidth": 4, 5 | "semi": true, 6 | "htmlWhitespaceSensitivity": "ignore", 7 | "plugins": ["prettier-plugin-svelte"] 8 | } 9 | -------------------------------------------------------------------------------- /rslib/src/storage/deck/alloc_id.sql: -------------------------------------------------------------------------------- 1 | SELECT CASE 2 | WHEN ?1 IN ( 3 | SELECT id 4 | FROM decks 5 | ) THEN ( 6 | SELECT max(id) + 1 7 | FROM decks 8 | ) 9 | ELSE ?1 10 | END; -------------------------------------------------------------------------------- /rslib/src/storage/tag/alloc_id.sql: -------------------------------------------------------------------------------- 1 | SELECT CASE 2 | WHEN ?1 IN ( 3 | SELECT id 4 | FROM tags 5 | ) THEN ( 6 | SELECT max(id) + 1 7 | FROM tags 8 | ) 9 | ELSE ?1 10 | END; -------------------------------------------------------------------------------- /tools/run-qt6.6: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./ninja extract:uv 6 | 7 | export PYENV=./out/pyenv66 8 | UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt66 9 | ./run $* 10 | -------------------------------------------------------------------------------- /tools/run-qt6.7: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./ninja extract:uv 6 | 7 | export PYENV=./out/pyenv67 8 | UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt67 9 | ./run $* 10 | -------------------------------------------------------------------------------- /tools/run-qt6.8: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./ninja extract:uv 6 | 7 | export PYENV=./out/pyenv68 8 | UV_PROJECT_ENVIRONMENT=$PYENV ./out/extracted/uv/uv sync --all-packages --extra qt68 9 | ./run $* 10 | -------------------------------------------------------------------------------- /.buildkite/linux/docker/buildkite.cfg: -------------------------------------------------------------------------------- 1 | name="lin-ci" 2 | tags="queue=lin-ci" 3 | build-path="/state/build" 4 | hooks-path="/etc/buildkite-agent/hooks" 5 | no-plugins=true 6 | no-local-hooks=true 7 | no-git-submodules=true 8 | -------------------------------------------------------------------------------- /pylib/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from anki.lang import set_lang 5 | 6 | set_lang("en_US") 7 | -------------------------------------------------------------------------------- /qt/launcher/win/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if "%NOCOMP%"=="1" ( 4 | set NO_COMPRESS=1 5 | set CODESIGN=0 6 | ) else ( 7 | set CODESIGN=1 8 | set NO_COMPRESS=0 9 | ) 10 | cargo run --bin build_win 11 | -------------------------------------------------------------------------------- /ts/lib/components/DropdownDivider.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /ftl/update-ankimobile-usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script can only be run by Damien, as it requires a copy of AnkiMobile's sources. 3 | 4 | cargo run --bin write_ftl_json ftl/usage/ankimobile.json ../../mobile/ankimobile/src 5 | -------------------------------------------------------------------------------- /python/mkempty.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import sys 5 | 6 | open(sys.argv[1], "w", encoding="utf8").close() 7 | -------------------------------------------------------------------------------- /rslib/src/ankidroid/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | pub(crate) mod db; 4 | pub(crate) mod error; 5 | pub mod service; 6 | -------------------------------------------------------------------------------- /ts/lib/tag-editor/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export { default as TagEditor } from "./TagEditor.svelte"; 5 | -------------------------------------------------------------------------------- /qt/mac/ankihelper.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /rslib/src/import_export/text/csv/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | mod export; 5 | mod import; 6 | pub mod metadata; 7 | -------------------------------------------------------------------------------- /rslib/src/notetype/header.tex: -------------------------------------------------------------------------------- 1 | \documentclass[12pt]{article} 2 | \special{papersize=3in,5in} 3 | \usepackage[utf8]{inputenc} 4 | \usepackage{amssymb,amsmath} 5 | \pagestyle{empty} 6 | \setlength{\parindent}{0in} 7 | \begin{document} 8 | -------------------------------------------------------------------------------- /rslib/src/storage/revlog/time_of_last_review.sql: -------------------------------------------------------------------------------- 1 | SELECT id / 1000 2 | FROM revlog 3 | WHERE cid = $1 4 | AND ease BETWEEN 1 AND 4 5 | AND ( 6 | type != 3 7 | OR factor != 0 8 | ) 9 | ORDER BY id DESC 10 | LIMIT 1 -------------------------------------------------------------------------------- /ts/editor/LabelName.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /qt/aqt/data/web/css/addonconf.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | body { 5 | margin: 5px; 6 | font-size: 13px; 7 | } 8 | -------------------------------------------------------------------------------- /rslib/src/storage/card/active_new_cards.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | ord, 4 | cast(mod AS integer), 5 | did, 6 | odid 7 | FROM cards 8 | WHERE did IN ( 9 | SELECT id 10 | FROM active_decks 11 | ) 12 | AND queue = 0 -------------------------------------------------------------------------------- /rslib/src/sync/media/database/server/entry/changes.sql: -------------------------------------------------------------------------------- 1 | SELECT fname, 2 | usn, 3 | ( 4 | CASE 5 | WHEN size > 0 THEN lower(hex(csum)) 6 | ELSE '' 7 | END 8 | ) 9 | FROM media 10 | WHERE usn > ? 11 | LIMIT 1000 -------------------------------------------------------------------------------- /qt/launcher/mac/icon/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | xcrun actool --app-icon AppIcon $(pwd)/Assets.xcassets --compile . --platform macosx --minimum-deployment-target 13.0 --target-device mac --output-partial-info-plist /dev/null 6 | -------------------------------------------------------------------------------- /rslib/src/storage/upgrades/schema17_upgrade.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE tags; 2 | CREATE TABLE tags ( 3 | tag text NOT NULL PRIMARY KEY COLLATE unicase, 4 | usn integer NOT NULL, 5 | collapsed boolean NOT NULL, 6 | config blob NULL 7 | ) without rowid; -------------------------------------------------------------------------------- /tools/ninja.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set CARGO_TARGET_DIR=%~dp0..\out\rust 3 | REM separate build+run steps so build env doesn't leak into subprocesses 4 | cargo build -p runner --release || exit /b 1 5 | out\rust\release\runner build %* || exit /b 1 6 | -------------------------------------------------------------------------------- /ts/routes/congrats/congrats-base.scss: -------------------------------------------------------------------------------- 1 | @use "../../lib/sass/root-vars"; 2 | @import "../../lib/sass/base"; 3 | 4 | @import "bootstrap/scss/containers"; 5 | 6 | body { 7 | margin-left: 0.5em; 8 | margin-right: 0.5em; 9 | } 10 | -------------------------------------------------------------------------------- /rslib/src/import_export/package/colpkg/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub(super) mod export; 5 | pub(super) mod import; 6 | mod tests; 7 | -------------------------------------------------------------------------------- /rslib/src/search/deck_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | did integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (did) 7 | SELECT id 8 | FROM decks 9 | ORDER BY name; -------------------------------------------------------------------------------- /proto/README.md: -------------------------------------------------------------------------------- 1 | Protobuf files defining the interface the frontend and backend components use to talk to each other, 2 | and how Anki stores some of the data inside its SQLite database. These files are used to generate Rust, 3 | Python and TypeScript bindings. 4 | -------------------------------------------------------------------------------- /rslib/src/search/notetype_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | ntid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (ntid) 7 | SELECT id 8 | FROM notetypes 9 | ORDER BY name; -------------------------------------------------------------------------------- /rslib/src/storage/note/notes_types_checksums_decks.sql: -------------------------------------------------------------------------------- 1 | SELECT DISTINCT notes.id, 2 | notes.mid, 3 | notes.csum, 4 | CASE 5 | WHEN cards.odid = 0 THEN cards.did 6 | ELSE cards.odid 7 | END AS did 8 | FROM notes 9 | JOIN cards ON notes.id = cards.nid -------------------------------------------------------------------------------- /ts/editor/image-overlay/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import ImageOverlay from "./ImageOverlay.svelte"; 5 | 6 | export default ImageOverlay; 7 | -------------------------------------------------------------------------------- /ts/lib/tag-editor/tag-options-button/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export { default as TagOptionsButton } from "./TagOptionsButton.svelte"; 5 | -------------------------------------------------------------------------------- /ts/lib/tslib/i18n/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export { ModuleName } from "@generated/ftl"; 5 | // eslint-disable 6 | export * from "./utils"; 7 | -------------------------------------------------------------------------------- /qt/mac/ankihelper.xcodeproj/project.xcworkspace/xcuserdata/dae.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankicommunity/anki-desktop/main/qt/mac/ankihelper.xcodeproj/project.xcworkspace/xcuserdata/dae.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /rslib/src/image_occlusion/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub mod imagedata; 5 | pub mod imageocclusion; 6 | pub(crate) mod notetype; 7 | mod service; 8 | -------------------------------------------------------------------------------- /rslib/src/storage/deck/all_decks_and_original_of_search_cards.sql: -------------------------------------------------------------------------------- 1 | WITH cids AS ( 2 | SELECT cid 3 | FROM search_cids 4 | ) 5 | SELECT did 6 | FROM cards 7 | WHERE id IN cids 8 | UNION 9 | SELECT odid 10 | FROM cards 11 | WHERE odid != 0 12 | AND id IN cids -------------------------------------------------------------------------------- /.buildkite/mac/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | STATE=$(pwd)/../state/anki-ci 6 | mkdir -p $STATE 7 | 8 | echo "+++ Building and testing" 9 | ln -sf out/node_modules . 10 | SKIP_RUNNER_BUILD=0 BUILD_ROOT=$STATE/build ./ninja pylib qt wheels check 11 | -------------------------------------------------------------------------------- /rslib/src/import_export/package/apkg/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | mod export; 5 | mod import; 6 | mod tests; 7 | 8 | pub(crate) use import::NoteMeta; 9 | -------------------------------------------------------------------------------- /rslib/src/stats/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | mod card; 5 | mod graphs; 6 | mod service; 7 | mod today; 8 | 9 | pub use today::studied_today; 10 | -------------------------------------------------------------------------------- /ts/editor/mathjax-overlay/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import MathjaxOverlay from "./MathjaxOverlay.svelte"; 5 | 6 | export default MathjaxOverlay; 7 | -------------------------------------------------------------------------------- /.buildkite/linux/docker/environment: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "${BUILDKITE_COMMAND}" != ".buildkite/linux/entrypoint" && 4 | "${BUILDKITE_COMMAND}" != ".buildkite/linux/release-entrypoint" ]]; then 5 | echo "Command not allowed: ${BUILDKITE_COMMAND}" 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ftl/core-repo"] 2 | path = ftl/core-repo 3 | url = https://github.com/ankitects/anki-core-i18n.git 4 | shallow = true 5 | [submodule "ftl/qt-repo"] 6 | path = ftl/qt-repo 7 | url = https://github.com/ankitects/anki-desktop-ftl.git 8 | shallow = true 9 | -------------------------------------------------------------------------------- /ts/editor/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { globalExport } from "@tslib/globals"; 5 | 6 | import * as base from "./base"; 7 | 8 | globalExport(base); 9 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/notes-toolbar/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import NotesToolbar from "./NotesToolbar.svelte"; 5 | 6 | export default NotesToolbar; 7 | -------------------------------------------------------------------------------- /rslib/src/storage/card/intraday_due.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | due, 4 | cast(mod AS integer), 5 | did, 6 | odid 7 | FROM cards 8 | WHERE did IN ( 9 | SELECT id 10 | FROM active_decks 11 | ) 12 | AND ( 13 | queue IN (1, 4) 14 | AND due <= ? 15 | ) -------------------------------------------------------------------------------- /rslib/src/search/card_mod_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | GROUP BY nid 10 | ORDER BY MAX(mod); -------------------------------------------------------------------------------- /ts/src/hooks.client.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@sveltejs/kit').HandleClientError} */ 2 | export async function handleError({ error, event, status, message }) { 3 | /** @type {any} */ 4 | const anyError = error; 5 | return { 6 | message: anyError.message, 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # These settings are not supported on stable Rust, and are ignored by the ninja 2 | # build script - to use them you need to run 'cargo +nightly fmt' 3 | group_imports = "StdExternalCrate" 4 | imports_granularity = "Item" 5 | imports_layout = "Vertical" 6 | wrap_comments = true 7 | -------------------------------------------------------------------------------- /rslib/src/search/note_cards_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | GROUP BY nid 10 | ORDER BY COUNT(*); -------------------------------------------------------------------------------- /rslib/src/search/note_lapses_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | GROUP BY nid 10 | ORDER BY SUM(lapses); -------------------------------------------------------------------------------- /rslib/src/search/note_reps_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | GROUP BY nid 10 | ORDER BY SUM(reps); -------------------------------------------------------------------------------- /ts/README.md: -------------------------------------------------------------------------------- 1 | Anki's TypeScript and Sass dependencies. Some TS/JS code is also 2 | stored separately in ../qt/aqt/data/web/. 3 | 4 | To update all dependencies: 5 | 6 | ./update.sh 7 | 8 | To add a new dev dependency, use something like: 9 | 10 | ./add.sh -D @rollup/plugin-alias 11 | -------------------------------------------------------------------------------- /ts/editable/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import "./editable-base.scss"; 5 | /* only imported for the CSS */ 6 | import "./ContentEditable.svelte"; 7 | import "./Mathjax.svelte"; 8 | -------------------------------------------------------------------------------- /ts/lib/tslib/shadow-dom.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface DocumentOrShadowRoot { 5 | getSelection(): Selection | null; 6 | } 7 | 8 | interface Node { 9 | getRootNode(options?: GetRootNodeOptions): Document | ShadowRoot; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pylib/anki/storage.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | # Legacy code expects to find Collection in this module. 5 | 6 | from anki.collection import Collection 7 | 8 | _Collection = Collection 9 | -------------------------------------------------------------------------------- /rslib/src/storage/card/due_cards.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | due, 4 | cast(ivl AS integer), 5 | cast(mod AS integer), 6 | did, 7 | odid 8 | FROM cards 9 | WHERE did IN ( 10 | SELECT id 11 | FROM active_decks 12 | ) 13 | AND ( 14 | queue = ? 15 | AND due <= ? 16 | ) -------------------------------------------------------------------------------- /qt/mac/ankihelper.xcodeproj/project.xcworkspace/xcuserdata/dae.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /rslib/src/storage/dbcheck/invalid_ids_count.sql: -------------------------------------------------------------------------------- 1 | SELECT ( 2 | SELECT COUNT(*) 3 | FROM notes 4 | WHERE id > :cutoff 5 | ) + ( 6 | SELECT COUNT(*) 7 | FROM cards 8 | WHERE id > :cutoff 9 | ) + ( 10 | SELECT COUNT(*) 11 | FROM revlog 12 | WHERE id > :cutoff 13 | ); -------------------------------------------------------------------------------- /tools/update-launcher-env.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem 3 | rem Install our latest anki/aqt code into the launcher venv 4 | 5 | rmdir /s /q out\wheels 2>nul 6 | call tools\ninja wheels 7 | set VIRTUAL_ENV=%LOCALAPPDATA%\AnkiProgramFiles\.venv 8 | for %%f in (out\wheels\*.whl) do out\extracted\uv\uv pip install "%%f" -------------------------------------------------------------------------------- /rslib/src/search/note_ease_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | WHERE type != 0 10 | GROUP BY nid 11 | ORDER BY AVG(factor); -------------------------------------------------------------------------------- /rslib/src/storage/card/get_card.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | did, 4 | ord, 5 | cast(mod AS integer), 6 | usn, 7 | type, 8 | queue, 9 | due, 10 | cast(ivl AS integer), 11 | factor, 12 | reps, 13 | lapses, 14 | left, 15 | odue, 16 | odid, 17 | flags, 18 | data 19 | FROM cards -------------------------------------------------------------------------------- /rslib/src/storage/upgrades/schema11_downgrade.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE config; 2 | DROP TABLE deck_config; 3 | DROP TABLE tags; 4 | DROP TABLE fields; 5 | DROP TABLE templates; 6 | DROP TABLE notetypes; 7 | DROP TABLE decks; 8 | DROP INDEX idx_cards_odid; 9 | DROP INDEX idx_notes_mid; 10 | UPDATE col 11 | SET ver = 11; -------------------------------------------------------------------------------- /rslib/src/search/note_interval_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | WHERE type IN (2, 3) 10 | GROUP BY nid 11 | ORDER BY AVG(ivl); -------------------------------------------------------------------------------- /ftl/qt/profiles.ftl: -------------------------------------------------------------------------------- 1 | profiles-folder-readme = 2 | This folder stores all of your Anki data in a single location, 3 | to make backups easy. To tell Anki to use a different location, 4 | please see: 5 | 6 | { $link } 7 | # will appear as 'Downgrade & Quit' 8 | profiles-downgrade-and-quit = Downgrade && Quit 9 | -------------------------------------------------------------------------------- /ts/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | {message} 12 | -------------------------------------------------------------------------------- /qt/aqt/data/web/css/toolbar-bottom.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | body { 5 | overflow: hidden; 6 | } 7 | 8 | #header { 9 | border-bottom: 0; 10 | margin-top: 0; 11 | padding: 9px; 12 | } 13 | -------------------------------------------------------------------------------- /ts/lib/domlib/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export * from "./content-editable"; 5 | export * from "./location"; 6 | export * from "./move-nodes"; 7 | export * from "./place-caret"; 8 | export * from "./surround"; 9 | -------------------------------------------------------------------------------- /rslib/src/scheduler/fsrs/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | mod error; 5 | pub mod memory_state; 6 | pub mod params; 7 | pub mod rescheduler; 8 | pub mod retention; 9 | pub mod simulator; 10 | pub mod try_collect; 11 | -------------------------------------------------------------------------------- /rslib/src/search/template_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | ntid integer NOT NULL, 5 | ord integer NOT NULL, 6 | UNIQUE(ntid, ord) 7 | ); 8 | INSERT INTO sort_order (ntid, ord) 9 | SELECT ntid, 10 | ord 11 | FROM templates 12 | ORDER BY name -------------------------------------------------------------------------------- /tools/run.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import os 5 | import sys 6 | 7 | sys.path.extend(["pylib", "qt", "out/pylib", "out/qt"]) 8 | 9 | import aqt 10 | 11 | if not os.environ.get("SKIP_RUN"): 12 | aqt.run() 13 | -------------------------------------------------------------------------------- /ts/routes/import-page/[...path]/+page.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | import type { PageLoad } from "./$types"; 4 | 5 | export const load = (async ({ params }) => { 6 | return { path: params.path }; 7 | }) satisfies PageLoad; 8 | -------------------------------------------------------------------------------- /ts/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /qt/aqt/props.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | """ 5 | See pylib/anki/hooks.py 6 | """ 7 | 8 | from __future__ import annotations 9 | 10 | # You can find the definitions in ../tools/genhooks_gui.py 11 | from _aqt.props import * 12 | -------------------------------------------------------------------------------- /rslib/src/storage/card/deck_due_counts.sql: -------------------------------------------------------------------------------- 1 | SELECT CASE 2 | WHEN odid == 0 THEN did 3 | ELSE odid 4 | END AS original_did, 5 | CASE 6 | WHEN odid == 0 THEN due 7 | ELSE odue 8 | END AS true_due, 9 | COUNT() AS COUNT 10 | FROM cards 11 | WHERE type = 2 12 | AND queue != -1 13 | GROUP BY original_did, 14 | true_due -------------------------------------------------------------------------------- /ts/routes/image-occlusion/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export interface Size { 5 | width: number; 6 | height: number; 7 | } 8 | 9 | export type ConstructorParams = { 10 | [P in keyof T]?: T[P]; 11 | }; 12 | -------------------------------------------------------------------------------- /qt/aqt/gui_hooks.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | """ 5 | See pylib/anki/hooks.py 6 | """ 7 | 8 | from __future__ import annotations 9 | 10 | # You can find the definitions in ../tools/genhooks_gui.py 11 | from _aqt.hooks import * 12 | -------------------------------------------------------------------------------- /qt/mac/ankihelper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /rslib/src/search/note_due_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | WHERE ( 10 | odid = 0 11 | AND type != 0 12 | AND queue > 0 13 | ) 14 | GROUP BY nid -------------------------------------------------------------------------------- /rslib/src/storage/card/update_card.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET nid = ?, 3 | did = ?, 4 | ord = ?, 5 | mod = ?, 6 | usn = ?, 7 | type = ?, 8 | queue = ?, 9 | due = ?, 10 | ivl = ?, 11 | factor = ?, 12 | reps = ?, 13 | lapses = ?, 14 | left = ?, 15 | odue = ?, 16 | odid = ?, 17 | flags = ?, 18 | data = ? 19 | WHERE id = ? -------------------------------------------------------------------------------- /ts/lib/components/Spacer.svelte: -------------------------------------------------------------------------------- 1 | 5 |
6 | 7 | 13 | -------------------------------------------------------------------------------- /qt/aqt/_macos_helper.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from __future__ import annotations 5 | 6 | import sys 7 | 8 | if sys.platform == "darwin": 9 | from anki_mac_helper import macos_helper 10 | else: 11 | macos_helper = None 12 | -------------------------------------------------------------------------------- /rslib/src/storage/dbcheck/invalid_ids_update.sql: -------------------------------------------------------------------------------- 1 | UPDATE "{target_table}" 2 | SET "{id_column}" = ( 3 | SELECT invalid_ids.new_id 4 | FROM invalid_ids 5 | WHERE invalid_ids.id = "{target_table}"."{id_column}" 6 | LIMIT 1 7 | ) 8 | WHERE "{target_table}"."{id_column}" IN ( 9 | SELECT invalid_ids.id 10 | FROM invalid_ids 11 | ); -------------------------------------------------------------------------------- /tools/clean: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Remove most things from the build folder, to test a clean build. Keeps 4 | # the download folder, and optionally node_modules/pyenv. 5 | 6 | set -e 7 | shopt -s extglob 8 | 9 | if [ "$1" == "keep-env" ]; then 10 | rm -rf out/!(node_modules|pyenv|download) 11 | else 12 | rm -rf out/!(download) 13 | fi 14 | -------------------------------------------------------------------------------- /.vscode.dist/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dprint.dprint", 4 | "ms-python.python", 5 | "charliermarsh.ruff", 6 | "rust-lang.rust-analyzer", 7 | "svelte.svelte-vscode", 8 | "zxh404.vscode-proto3", 9 | "usernamehw.errorlens", 10 | "eamodio.gitlens" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tools/build-arm-lin: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # sudo apt install libc6-dev-arm64-cross gcc-aarch64-linux-gnu 6 | rustup target add aarch64-unknown-linux-gnu 7 | 8 | export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc 9 | export LIN_ARM64=1 10 | 11 | RELEASE=2 ./ninja wheels:anki 12 | echo "wheels are in out/wheels" 13 | -------------------------------------------------------------------------------- /ftl/core/network.ftl: -------------------------------------------------------------------------------- 1 | network-offline = Please check your internet connection. 2 | network-timeout = Connection timed out. Please try again. If you see frequent timeouts, please try a different network connection. 3 | network-proxy-auth = Your proxy requires authentication. 4 | network-other = A network error occurred. 5 | network-details = Error details: { $details } 6 | -------------------------------------------------------------------------------- /rslib/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub mod collection; 5 | pub mod error; 6 | pub mod http_client; 7 | pub mod http_server; 8 | pub mod login; 9 | pub mod media; 10 | pub mod request; 11 | pub mod response; 12 | pub mod version; 13 | -------------------------------------------------------------------------------- /ts/lib/sass/bootstrap-forms.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | @import "bootstrap/scss/forms"; 5 | 6 | .form-control, 7 | .form-select { 8 | // the unprefixed version wasn't added until Chrome 81 9 | -webkit-appearance: none; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .mypy_cache 3 | .DS_Store 4 | anki.prof 5 | target 6 | /user.bazelrc 7 | .dmypy.json 8 | /.idea/ 9 | /.vscode 10 | /.bazel 11 | /windows.bazelrc 12 | /out 13 | node_modules 14 | .n2_db 15 | .ninja_log 16 | .ninja_deps 17 | /extra 18 | yarn-error.log 19 | ts/.svelte-kit 20 | .yarn 21 | .claude/settings.local.json 22 | .claude/user.md 23 | -------------------------------------------------------------------------------- /ts/lib/sass/night-mode.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | @mixin input { 5 | background-color: var(--canvas-inset); 6 | border-color: var(--border); 7 | 8 | &:focus { 9 | background-color: var(--canvas-inset); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /rslib/src/storage/revlog/studied_today_by_deck.sql: -------------------------------------------------------------------------------- 1 | SELECT CASE 2 | WHEN c.odid == 0 THEN c.did 3 | ELSE c.odid 4 | END AS original_did, 5 | COUNT(DISTINCT r.cid) AS cnt 6 | FROM revlog AS r 7 | JOIN cards AS c ON r.cid = c.id 8 | WHERE r.id > ? 9 | AND r.ease > 0 10 | AND ( 11 | r.type < 3 12 | OR r.factor != 0 13 | ) 14 | GROUP BY original_did -------------------------------------------------------------------------------- /ts/lib/domlib/surround/tree/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export { BlockNode } from "./block-node"; 5 | export { ElementNode } from "./element-node"; 6 | export { FormattingNode } from "./formatting-node"; 7 | export type { TreeNode } from "./tree-node"; 8 | -------------------------------------------------------------------------------- /ts/routes/tmp/_page.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | // this route pulls in code that's currently bundled separately, so that 4 | // errors in it get caught by svelte-check 5 | import * as _editor from "$lib/../editor"; 6 | import * as _reviewer from "$lib/../reviewer"; 7 | -------------------------------------------------------------------------------- /rslib/src/storage/upgrades/schema18_downgrade.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE graves 2 | RENAME TO graves_old; 3 | CREATE TABLE graves ( 4 | usn integer NOT NULL, 5 | oid integer NOT NULL, 6 | type integer NOT NULL 7 | ); 8 | INSERT INTO graves (usn, oid, type) 9 | SELECT usn, 10 | oid, 11 | type 12 | FROM graves_old; 13 | DROP TABLE graves_old; 14 | UPDATE col 15 | SET ver = 17; -------------------------------------------------------------------------------- /rslib/process/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anki_process" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | description = "Utils for better process error reporting" 10 | 11 | [dependencies] 12 | itertools.workspace = true 13 | snafu.workspace = true 14 | -------------------------------------------------------------------------------- /ts/editor/plain-text-input/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import PlainTextInput from "./PlainTextInput.svelte"; 5 | 6 | export type { PlainTextInputAPI } from "./PlainTextInput.svelte"; 7 | export default PlainTextInput; 8 | export * from "./PlainTextInput.svelte"; 9 | -------------------------------------------------------------------------------- /ts/editor/editor-toolbar/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import EditorToolbar from "./EditorToolbar.svelte"; 5 | 6 | export type { EditorToolbarAPI } from "./EditorToolbar.svelte"; 7 | export default EditorToolbar; 8 | export { editorToolbar } from "./EditorToolbar.svelte"; 9 | -------------------------------------------------------------------------------- /ts/editor/rich-text-input/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { default as RichTextInput } from "./RichTextInput.svelte"; 5 | 6 | export type { RichTextInputAPI } from "./RichTextInput.svelte"; 7 | export default RichTextInput; 8 | export * from "./RichTextInput.svelte"; 9 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/fabric.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | namespace fabric { 5 | interface Object { 6 | id: string; 7 | ordinal: number; 8 | /** a custom property set on groups in the ungrouping routine to avoid adding a spurious undo entry */ 9 | destroyed: boolean; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ftl/copy-core-string.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # - sync ftl 3 | # - ./copy-core-string.sh scheduling-review browsing-sidebar-card-state-review 4 | # - confirm changes in core-repo/ correct 5 | # - commit and push changes 6 | # - ensure string in template isn't in the 'no need to translate' section 7 | # - update submodule in main repo 8 | ./ftl string copy ftl/core-repo/core ftl/core-repo/core $1 $2 9 | -------------------------------------------------------------------------------- /rslib/src/storage/note/add_if_unique.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR IGNORE INTO notes ( 3 | id, 4 | guid, 5 | mid, 6 | mod, 7 | usn, 8 | tags, 9 | flds, 10 | sfld, 11 | csum, 12 | flags, 13 | data 14 | ) 15 | VALUES ( 16 | ?, 17 | ?, 18 | ?, 19 | ?, 20 | ?, 21 | ?, 22 | ?, 23 | ?, 24 | ?, 25 | 0, 26 | "" 27 | ) -------------------------------------------------------------------------------- /rslib/src/storage/note/add_or_update.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO notes ( 3 | id, 4 | guid, 5 | mid, 6 | mod, 7 | usn, 8 | tags, 9 | flds, 10 | sfld, 11 | csum, 12 | flags, 13 | data 14 | ) 15 | VALUES ( 16 | ?, 17 | ?, 18 | ?, 19 | ?, 20 | ?, 21 | ?, 22 | ?, 23 | ?, 24 | ?, 25 | 0, 26 | "" 27 | ) -------------------------------------------------------------------------------- /ts/reviewer/index_wrapper.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | // extend the global namespace with our exports - not sure if there's a better way with esbuild 5 | import * as globals from "./index"; 6 | 7 | for (const key in globals) { 8 | window[key] = globals[key]; 9 | } 10 | -------------------------------------------------------------------------------- /ts/routes/import-page/import-page-base.scss: -------------------------------------------------------------------------------- 1 | @use "../lib/sass/bootstrap-dark"; 2 | 3 | @import "../lib/sass/base"; 4 | 5 | @import "../lib/sass/bootstrap-tooltip"; 6 | @import "bootstrap/scss/buttons"; 7 | 8 | .night-mode { 9 | @include bootstrap-dark.night-mode; 10 | } 11 | 12 | body { 13 | padding: 0 1em 1em 1em; 14 | } 15 | 16 | html { 17 | height: initial; 18 | } 19 | -------------------------------------------------------------------------------- /rslib/io/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anki_io" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | description = "Utils for better I/O error reporting" 10 | 11 | [dependencies] 12 | camino.workspace = true 13 | snafu.workspace = true 14 | tempfile.workspace = true 15 | -------------------------------------------------------------------------------- /tools/rebuild-web: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright: Ankitects Pty Ltd and contributors 3 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 4 | 5 | # Manually trigger a rebuild and reload of Anki's web stack 6 | 7 | # NOTE: This script needs to be run from the project root 8 | 9 | set -e 10 | 11 | ./ninja qt 12 | ./out/pyenv/bin/python tools/reload_webviews.py 13 | -------------------------------------------------------------------------------- /rslib/src/services.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | #![allow(clippy::redundant_closure)] 5 | 6 | // Includes the automatically-generated *Service and Backend*Service traits, 7 | // and some impls on Backend and Collection. 8 | 9 | include!(concat!(env!("OUT_DIR"), "/backend.rs")); 10 | -------------------------------------------------------------------------------- /ts/lib/tslib/promise.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export function promiseWithResolver(): [Promise, (value: T) => void] { 5 | let resolve: (object: T) => void; 6 | const promise = new Promise((res) => (resolve = res)); 7 | 8 | return [promise, resolve!]; 9 | } 10 | -------------------------------------------------------------------------------- /ts/routes/deck-options/HtmlAddon.svelte: -------------------------------------------------------------------------------- 1 | 5 | 13 | 14 | {@html html} 15 | -------------------------------------------------------------------------------- /python/version.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | """Version helper for wheel builds.""" 5 | 6 | import pathlib 7 | 8 | # Read version from .version file in project root 9 | _version_file = pathlib.Path(__file__).parent.parent / ".version" 10 | __version__ = _version_file.read_text().strip() 11 | -------------------------------------------------------------------------------- /qt/mac/update-launcher-env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Build and install into the launcher venv 4 | 5 | set -e 6 | 7 | ./build.sh 8 | if [[ "$OSTYPE" == "darwin"* ]]; then 9 | export VIRTUAL_ENV=$HOME/Library/Application\ Support/AnkiProgramFiles/.venv 10 | else 11 | export VIRTUAL_ENV=$HOME/.local/share/AnkiProgramFiles/.venv 12 | fi 13 | ../../out/extracted/uv/uv pip install dist/*.whl 14 | 15 | -------------------------------------------------------------------------------- /ts/html-filter/helpers.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export function isHTMLElement(elem: Element): elem is HTMLElement { 5 | return elem instanceof HTMLElement; 6 | } 7 | 8 | export function isNightMode(): boolean { 9 | return document.body.classList.contains("nightMode"); 10 | } 11 | -------------------------------------------------------------------------------- /ts/yarn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Execute subcommand (eg 'yarn ...') and update licenses. 3 | 4 | set -e 5 | 6 | PATH=$(realpath ../out/extracted/node/bin) 7 | 8 | ../out/extracted/node/bin/yarn $* 9 | 10 | cd .. 11 | 12 | ./node_modules/.bin/license-checker-rseidelsohn --production --json \ 13 | --excludePackages anki --relativeLicensePath \ 14 | --relativeModulePath > ts/licenses.json 15 | -------------------------------------------------------------------------------- /ftl/core/undo.ftl: -------------------------------------------------------------------------------- 1 | ### The strings in this file are currently in development, 2 | ### and you may want to skip translating them for now. 3 | 4 | undo-undo = Undo 5 | undo-redo = Redo 6 | # eg "Undo Answer Card" 7 | undo-undo-action = Undo { $val } 8 | # eg "Answer Card Undone" 9 | undo-action-undone = { $action } undone 10 | undo-redo-action = Redo { $action } 11 | undo-action-redone = { $action } redone 12 | -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_due_new.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET due = ( 3 | CASE 4 | WHEN type = 0 5 | AND queue != 4 THEN 1000000 + due % 1000000 6 | ELSE due 7 | END 8 | ), 9 | mod = ?1, 10 | usn = ?2 11 | WHERE due != ( 12 | CASE 13 | WHEN type = 0 14 | AND queue != 4 THEN 1000000 + due % 1000000 15 | ELSE due 16 | END 17 | ) 18 | AND due >= 1000000; -------------------------------------------------------------------------------- /ts/routes/congrats/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ts/routes/congrats/+page.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | import { congratsInfo } from "@generated/backend"; 4 | 5 | import type { PageLoad } from "./$types"; 6 | 7 | export const load = (async () => { 8 | const info = await congratsInfo({}); 9 | return { info }; 10 | }) satisfies PageLoad; 11 | -------------------------------------------------------------------------------- /ts/routes/import-anki-package/Header.svelte: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 |

10 | {heading} 11 |

12 | 13 | 18 | -------------------------------------------------------------------------------- /ts/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /pylib/anki/types.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from typing import NoReturn 5 | 6 | 7 | def assert_exhaustive(arg: NoReturn) -> NoReturn: 8 | """The type definition will cause mypy to tell us if we've missed an enum case.""" 9 | raise Exception(f"unexpected arg received: {type(arg)} {arg}") 10 | -------------------------------------------------------------------------------- /pylib/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from anki.utils import int_version_to_str 5 | 6 | 7 | def test_int_version_to_str(): 8 | assert int_version_to_str(23) == "2.1.23" 9 | assert int_version_to_str(230900) == "23.09" 10 | assert int_version_to_str(230901) == "23.09.1" 11 | -------------------------------------------------------------------------------- /rslib/src/storage/deckconfig/add.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO deck_config (id, name, mtime_secs, usn, config) 2 | VALUES ( 3 | ( 4 | CASE 5 | WHEN ?1 IN ( 6 | SELECT id 7 | FROM deck_config 8 | ) THEN ( 9 | SELECT max(id) + 1 10 | FROM deck_config 11 | ) 12 | ELSE ?1 13 | END 14 | ), 15 | ?, 16 | ?, 17 | ?, 18 | ? 19 | ); -------------------------------------------------------------------------------- /ts/lib/tslib/globals.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export function globalExport(globals: Record): void { 5 | for (const key in globals) { 6 | window[key] = globals[key]; 7 | } 8 | 9 | // but also export as window.anki 10 | window["anki"] = globals; 11 | } 12 | -------------------------------------------------------------------------------- /rslib/src/storage/notetype/add_notetype.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO notetypes (id, name, mtime_secs, usn, config) 2 | VALUES ( 3 | ( 4 | CASE 5 | WHEN ?1 IN ( 6 | SELECT id 7 | FROM notetypes 8 | ) THEN ( 9 | SELECT max(id) + 1 10 | FROM notetypes 11 | ) 12 | ELSE ?1 13 | END 14 | ), 15 | ?, 16 | ?, 17 | ?, 18 | ? 19 | ); -------------------------------------------------------------------------------- /rslib/src/sync/media/database/client/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE media ( 2 | fname text NOT NULL PRIMARY KEY, 3 | -- null indicates deleted file 4 | csum text, 5 | -- zero if deleted 6 | mtime int NOT NULL, 7 | dirty int NOT NULL 8 | ) without rowid; 9 | CREATE INDEX idx_media_dirty ON media (dirty) 10 | WHERE dirty = 1; 11 | CREATE TABLE meta (dirMod int, lastUsn int); 12 | INSERT INTO meta 13 | VALUES (0, 0); -------------------------------------------------------------------------------- /ts/routes/graphs/_true-retention-base.scss: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | 4 | margin-left: auto; 5 | margin-right: auto; 6 | } 7 | 8 | tr { 9 | border-top: 1px solid var(--border); 10 | border-bottom: 1px solid var(--border); 11 | } 12 | 13 | td, 14 | th { 15 | padding-left: 0.5em; 16 | padding-right: 0.5em; 17 | } 18 | 19 | .row-header { 20 | color: var(--fg); 21 | } 22 | -------------------------------------------------------------------------------- /pylib/anki/_rsbridge.pyi: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | class Backend: 4 | @classmethod 5 | def command(cls, service: int, method: int, data: bytes) -> bytes: ... 6 | def db_command(self, data: bytes) -> bytes: ... 7 | 8 | def buildhash() -> str: ... 9 | def open_backend(data: bytes) -> Backend: ... 10 | def initialize_logging(log_file: Union[str, None]) -> Backend: ... 11 | def syncserver() -> None: ... 12 | -------------------------------------------------------------------------------- /rslib/src/search/note_original_position_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | GROUP BY nid 10 | ORDER BY COALESCE( 11 | extract_original_position(data), 12 | CASE 13 | WHEN type == 0 THEN due 14 | ELSE 0 15 | END 16 | ); -------------------------------------------------------------------------------- /rslib/src/storage/card/siblings_for_bury.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO search_cids 2 | SELECT id 3 | FROM cards 4 | WHERE id != :card_id 5 | AND nid = :note_id 6 | AND ( 7 | ( 8 | :include_new 9 | AND queue = :new_queue 10 | ) 11 | OR ( 12 | :include_reviews 13 | AND queue = :review_queue 14 | ) 15 | OR ( 16 | :include_day_learn 17 | AND queue = :daylearn_queue 18 | ) 19 | ); -------------------------------------------------------------------------------- /ts/lib/tslib/ui.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { promiseWithResolver } from "./promise"; 5 | import { registerPackage } from "./runtime-require"; 6 | 7 | const [loaded, uiResolve] = promiseWithResolver(); 8 | 9 | registerPackage("anki/ui", { 10 | loaded, 11 | }); 12 | 13 | export { uiResolve }; 14 | -------------------------------------------------------------------------------- /ts/routes/import-csv/[...path]/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ts/mathjax/mathjax-types.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | /* eslint 5 | @typescript-eslint/no-explicit-any: "off", 6 | */ 7 | 8 | export {}; 9 | 10 | declare global { 11 | interface Window { 12 | // Mathjax does not provide a full type 13 | MathJax: { [name: string]: any }; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ts/src/app.d.ts: -------------------------------------------------------------------------------- 1 | import "@poppanator/sveltekit-svg/dist/svg"; 2 | 3 | // See https://kit.svelte.dev/docs/types#app 4 | // for information about these interfaces 5 | declare global { 6 | namespace App { 7 | // interface Error {} 8 | // interface Locals {} 9 | // interface PageData {} 10 | // interface PageState {} 11 | // interface Platform {} 12 | } 13 | } 14 | 15 | export {}; 16 | -------------------------------------------------------------------------------- /.buildkite/linux/docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # builds an 'anki-[amd|arm]' image for the current platform 3 | # 4 | # for a cross-compile on recent Docker: 5 | # docker buildx create --use 6 | # docker run --privileged --rm tonistiigi/binfmt --install amd64 7 | # docker buildx build --platform linux/amd64 --tag anki-amd64 . --load 8 | 9 | . common.inc 10 | 11 | DOCKER_BUILDKIT=1 docker build --tag anki-${platform} . 12 | -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_due_other.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET due = ( 3 | CASE 4 | WHEN queue = 2 5 | AND due > 100000 THEN ?1 6 | ELSE min(max(round(due), -2147483648), 2147483647) 7 | END 8 | ), 9 | mod = ?2, 10 | usn = ?3 11 | WHERE due != ( 12 | CASE 13 | WHEN queue = 2 14 | AND due > 100000 THEN ?1 15 | ELSE min(max(round(due), -2147483648), 2147483647) 16 | END 17 | ); -------------------------------------------------------------------------------- /ts/lib/domlib/surround/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export type { MatchType } from "./match-type"; 5 | export { boolMatcher } from "./match-type"; 6 | export { reformat, surround, unsurround } from "./surround"; 7 | export type { SurroundFormat } from "./surround-format"; 8 | export type { FormattingNode } from "./tree"; 9 | -------------------------------------------------------------------------------- /ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["./.svelte-kit/tsconfig.json"], 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "noImplicitAny": false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.buildkite/windows/entrypoint.bat: -------------------------------------------------------------------------------- 1 | set PATH=c:\cargo\bin;%PATH% 2 | 3 | echo +++ Building and testing 4 | 5 | if exist \buildkite\state\out ( 6 | move \buildkite\state\out . 7 | ) 8 | if exist \buildkite\state\node_modules ( 9 | move \buildkite\state\node_modules . 10 | ) 11 | 12 | call tools\ninja build pylib qt check || exit /b 1 13 | 14 | echo --- Cleanup 15 | move out \buildkite\state\ 16 | move node_modules \buildkite\state\ 17 | -------------------------------------------------------------------------------- /tools/update-launcher-env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install our latest anki/aqt code into the launcher venv 4 | 5 | set -e 6 | 7 | rm -rf out/wheels 8 | ./ninja wheels 9 | if [[ "$OSTYPE" == "darwin"* ]]; then 10 | export VIRTUAL_ENV=$HOME/Library/Application\ Support/AnkiProgramFiles/.venv 11 | else 12 | export VIRTUAL_ENV=$HOME/.local/share/AnkiProgramFiles/.venv 13 | fi 14 | ./out/extracted/uv/uv pip install out/wheels/* 15 | 16 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/tools/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { drawEllipse } from "./tool-ellipse"; 5 | import { drawPolygon } from "./tool-polygon"; 6 | import { drawRectangle } from "./tool-rect"; 7 | import { drawText } from "./tool-text"; 8 | 9 | export { drawEllipse, drawPolygon, drawRectangle, drawText }; 10 | -------------------------------------------------------------------------------- /ninja: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ "$BUILD_ROOT" == "" ]; then 6 | out=$(pwd)/out 7 | else 8 | out="$BUILD_ROOT" 9 | fi 10 | export CARGO_TARGET_DIR=$out/rust 11 | export RECONFIGURE_KEY="${MAC_X86};${LIN_ARM64};${SOURCEMAP};${HMR}" 12 | 13 | if [ "$SKIP_RUNNER_BUILD" = "1" ]; then 14 | echo "Runner not rebuilt." 15 | else 16 | cargo build -p runner --release 17 | fi 18 | exec $out/rust/release/runner build -- $* 19 | -------------------------------------------------------------------------------- /python/sphinx/build.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | import os 4 | import subprocess 5 | 6 | os.environ["REPO_ROOT"] = os.path.abspath(".") 7 | subprocess.run(["out/pyenv/bin/sphinx-apidoc", "-o", "out/python/sphinx", "pylib", "qt"], check=True) 8 | subprocess.run(["out/pyenv/bin/sphinx-build", "out/python/sphinx", "out/python/sphinx/html"], check=True) 9 | -------------------------------------------------------------------------------- /qt/launcher/mac/notarize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Define output path 6 | OUTPUT_DIR="$1" 7 | APP_LAUNCHER="$OUTPUT_DIR/Anki.app" 8 | ZIP_FILE="$OUTPUT_DIR/Anki.zip" 9 | 10 | # Create zip for notarization 11 | (cd "$OUTPUT_DIR" && rm -rf Anki.zip && zip -r Anki.zip Anki.app) 12 | 13 | # Upload for notarization 14 | xcrun notarytool submit "$ZIP_FILE" -p default --wait 15 | 16 | # Staple the app 17 | xcrun stapler staple "$APP_LAUNCHER" -------------------------------------------------------------------------------- /ts/routes/change-notetype/[...notetypeIds]/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.buildkite/linux/release-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export PATH="$PATH:/state/rust/cargo/bin" 6 | export BUILD_ROOT=/state/build 7 | export RELEASE=2 8 | ln -sf out/node_modules . 9 | 10 | echo "--- Install n2" 11 | ./tools/install-n2 12 | 13 | echo "+++ Building" 14 | if [ $(uname -m) = "aarch64" ]; then 15 | export PYTHONPATH=/usr/lib/python3/dist-packages 16 | ./ninja wheels:anki 17 | else 18 | ./ninja bundle 19 | fi 20 | -------------------------------------------------------------------------------- /rslib/src/markdown.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use pulldown_cmark::html; 5 | use pulldown_cmark::Parser; 6 | 7 | pub(crate) fn render_markdown(markdown: &str) -> String { 8 | let mut buf = String::with_capacity(markdown.len()); 9 | let parser = Parser::new(markdown); 10 | html::push_html(&mut buf, parser); 11 | buf 12 | } 13 | -------------------------------------------------------------------------------- /ts/lib/components/ErrorPage.svelte: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 |
10 | {error.message} 11 |
12 | 13 | 19 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/[...imagePathOrNoteId]/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /rslib/src/sync/collection/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub mod changes; 5 | pub mod chunks; 6 | pub mod download; 7 | pub mod finish; 8 | pub mod graves; 9 | pub mod meta; 10 | pub mod normal; 11 | pub mod progress; 12 | pub mod protocol; 13 | pub mod sanity; 14 | pub mod start; 15 | pub mod status; 16 | pub mod tests; 17 | pub mod upload; 18 | -------------------------------------------------------------------------------- /rslib/src/sync/media/database/server/schema_v3.sql: -------------------------------------------------------------------------------- 1 | BEGIN exclusive; 2 | CREATE TABLE IF NOT EXISTS media ( 3 | fname text NOT NULL PRIMARY KEY, 4 | csum blob, 5 | sz int NOT NULL, 6 | usn int NOT NULL, 7 | deleted int NOT NULL 8 | ); 9 | CREATE INDEX IF NOT EXISTS ix_usn ON media (usn); 10 | CREATE TABLE IF NOT EXISTS meta (usn int NOT NULL, sz int NOT NULL); 11 | INSERT INTO meta (usn, sz) 12 | VALUES (0, 0); 13 | pragma user_version = 3; 14 | COMMIT; -------------------------------------------------------------------------------- /ts/lib/components/CheckBox.svelte: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /ts/lib/sass/bootstrap-dark.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | @use "vars"; 5 | 6 | @mixin night-mode { 7 | input, 8 | select { 9 | background-color: var(--canvas-inset); 10 | border-color: var(--border); 11 | 12 | &:focus { 13 | background-color: var(--canvas-inset); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/notes-toolbar/NotesToolbar.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tools/minilints/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minilints" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | 10 | [dependencies] 11 | anki_io.workspace = true 12 | anki_process.workspace = true 13 | anyhow.workspace = true 14 | camino.workspace = true 15 | serde_json.workspace = true 16 | walkdir.workspace = true 17 | which.workspace = true 18 | -------------------------------------------------------------------------------- /tools/publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | shopt -s extglob 5 | 6 | #export UV_PUBLISH_TOKEN=$(pass show w/pypi-api-test) 7 | #out/extracted/uv/uv publish --index testpypi out/wheels/* 8 | 9 | export UV_PUBLISH_TOKEN=$(pass show w/pypi-api) 10 | 11 | # Upload all wheels except anki_release*.whl first 12 | out/extracted/uv/uv publish out/wheels/!(anki_release*).whl 13 | # Then upload anki_release*.whl 14 | out/extracted/uv/uv publish out/wheels/anki_release*.whl 15 | -------------------------------------------------------------------------------- /ts/editor/editor-base.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | @import "../lib/sass/base"; 5 | 6 | $btn-disabled-opacity: 0.4; 7 | @import "bootstrap/scss/buttons"; 8 | @import "bootstrap/scss/button-group"; 9 | @import "../lib/sass/bootstrap-tooltip"; 10 | 11 | html, 12 | body { 13 | font-family: var(--bs-font-sans-serif); 14 | overflow: hidden; 15 | } 16 | -------------------------------------------------------------------------------- /ts/lib/sass/card-counts.scss: -------------------------------------------------------------------------------- 1 | .review-count { 2 | color: var(--state-review); 3 | } 4 | 5 | .new-count { 6 | color: var(--state-new); 7 | } 8 | 9 | .learn-count { 10 | color: var(--state-learn); 11 | } 12 | 13 | .zero-count { 14 | color: var(--fg-faint); 15 | } 16 | 17 | .bury-count { 18 | color: var(--fg-disabled); 19 | font-weight: bold; 20 | margin-inline-start: 2px; 21 | 22 | &:empty { 23 | display: none; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pylib/anki/scheduler/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from __future__ import annotations 5 | 6 | import anki.scheduler.base as _base 7 | 8 | UnburyDeck = _base.UnburyDeck 9 | CongratsInfo = _base.CongratsInfo 10 | BuryOrSuspend = _base.BuryOrSuspend 11 | FilteredDeckForUpdate = _base.FilteredDeckForUpdate 12 | CustomStudyRequest = _base.CustomStudyRequest 13 | -------------------------------------------------------------------------------- /qt/mac/ankihelper.xcodeproj/xcuserdata/dae.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ankihelper.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /rslib/src/search/note_decks_order.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sort_order; 2 | CREATE TEMPORARY TABLE sort_order ( 3 | pos integer PRIMARY KEY, 4 | nid integer NOT NULL UNIQUE 5 | ); 6 | INSERT INTO sort_order (nid) 7 | SELECT nid 8 | FROM cards 9 | JOIN ( 10 | SELECT id, 11 | row_number() OVER( 12 | ORDER BY name 13 | ) AS pos 14 | FROM decks 15 | ) decks ON cards.did = decks.id 16 | GROUP BY nid 17 | ORDER BY COUNT(DISTINCT did), 18 | decks.pos; -------------------------------------------------------------------------------- /ts/routes/import-anki-package/[...path]/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/mac.md: -------------------------------------------------------------------------------- 1 | # Mac-specific notes 2 | 3 | ## Requirements 4 | 5 | **Xcode**: 6 | 7 | Install the latest XCode from the App Store. Open it at least once 8 | so it installs the command line tools. 9 | 10 | **Git/rsync** 11 | 12 | Install via Homebrew or similar tool. 13 | 14 | ## Audio 15 | 16 | To play audio, use Homebrew to install mpv and lame. 17 | 18 | ## More 19 | 20 | For info on running tests, building wheels and so on, please see [Development](./development.md). 21 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd "%~dp0" 3 | 4 | set PYTHONWARNINGS=default 5 | set PYTHONPYCACHEPREFIX=out\pycache 6 | set ANKIDEV=1 7 | set QTWEBENGINE_REMOTE_DEBUGGING=8080 8 | set QTWEBENGINE_CHROMIUM_FLAGS=--remote-allow-origins=http://localhost:8080 9 | set ANKI_API_PORT=40000 10 | set ANKI_API_HOST=127.0.0.1 11 | 12 | @if not defined PYENV set PYENV=out\pyenv 13 | 14 | call tools\ninja pylib qt || exit /b 1 15 | %PYENV%\Scripts\python tools\run.py %* || exit /b 1 16 | popd 17 | -------------------------------------------------------------------------------- /ts/lib/tslib/node.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export function isOnlyChild(node: Node): boolean { 5 | return node.parentNode!.childNodes.length === 1; 6 | } 7 | 8 | export function hasOnlyChild(node: Node): boolean { 9 | return node.childNodes.length === 1; 10 | } 11 | 12 | export function ascend(node: Node): Node { 13 | return node.parentNode!; 14 | } 15 | -------------------------------------------------------------------------------- /rslib/src/storage/upgrades/schema18_upgrade.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE graves 2 | RENAME TO graves_old; 3 | CREATE TABLE graves ( 4 | oid integer NOT NULL, 5 | type integer NOT NULL, 6 | usn integer NOT NULL, 7 | PRIMARY KEY (oid, type) 8 | ) WITHOUT ROWID; 9 | INSERT 10 | OR IGNORE INTO graves (oid, type, usn) 11 | SELECT oid, 12 | type, 13 | usn 14 | FROM graves_old; 15 | DROP TABLE graves_old; 16 | CREATE INDEX idx_graves_pending ON graves (usn); 17 | UPDATE col 18 | SET ver = 18; -------------------------------------------------------------------------------- /ts/lib/components/WithContext.svelte: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ts/lib/tslib/functional.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export function noop(): void { 5 | /* noop */ 6 | } 7 | 8 | export async function asyncNoop(): Promise { 9 | /* noop */ 10 | } 11 | 12 | export function id(t: T): T { 13 | return t; 14 | } 15 | 16 | export function truthy(t: T | void | undefined | null): t is T { 17 | return Boolean(t); 18 | } 19 | -------------------------------------------------------------------------------- /ts/routes/import-anki-package/[...path]/+page.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | import { getImportAnkiPackagePresets } from "@generated/backend"; 4 | 5 | import type { PageLoad } from "./$types"; 6 | 7 | export const load = (async ({ params }) => { 8 | const options = await getImportAnkiPackagePresets({}); 9 | return { path: params.path, options }; 10 | }) satisfies PageLoad; 11 | -------------------------------------------------------------------------------- /rslib/src/image_occlusion/notetype.css: -------------------------------------------------------------------------------- 1 | #image-occlusion-canvas { 2 | --inactive-shape-color: #ffeba2; 3 | --active-shape-color: #ff8e8e; 4 | --inactive-shape-border: 1px #212121; 5 | --active-shape-border: 1px #212121; 6 | --highlight-shape-color: #ff8e8e00; 7 | --highlight-shape-border: 1px #ff8e8e; 8 | } 9 | 10 | .card { 11 | font-family: arial; 12 | font-size: 20px; 13 | text-align: center; 14 | color: black; 15 | background-color: white; 16 | } 17 | -------------------------------------------------------------------------------- /ts/editor/plain-text-input/transform.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { decoratedElements } from "../decorated-elements"; 5 | 6 | export function storedToUndecorated(html: string): string { 7 | return decoratedElements.toUndecorated(html); 8 | } 9 | 10 | export function undecoratedToStored(html: string): string { 11 | return decoratedElements.toStored(html); 12 | } 13 | -------------------------------------------------------------------------------- /ts/routes/deck-options/ParamsSearchRow.svelte: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/shapes/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export type { ShapeOrShapes } from "./base"; 5 | export { Shape } from "./base"; 6 | export { Ellipse } from "./ellipse"; 7 | export { extractShapesFromRenderedClozes } from "./from-cloze"; 8 | export { Polygon } from "./polygon"; 9 | export { Rectangle } from "./rectangle"; 10 | export { Text } from "./text"; 11 | -------------------------------------------------------------------------------- /ts/icons/contain-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.vscode.dist/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "ninja", 6 | "command": "ninja", 7 | "args": [ 8 | "pylib", 9 | "qt" 10 | ], 11 | "windows": { 12 | "command": "tools/ninja.bat", 13 | "args": [ 14 | "pylib", 15 | "qt" 16 | ] 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /qt/aqt/browser/sidebar/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | # ruff: noqa: F401 4 | from anki.utils import is_mac 5 | from aqt.theme import theme_manager 6 | 7 | from .item import SidebarItem, SidebarItemType 8 | from .model import SidebarModel 9 | from .searchbar import SidebarSearchBar 10 | from .toolbar import SidebarTool, SidebarToolbar 11 | from .tree import SidebarStage, SidebarTreeView 12 | -------------------------------------------------------------------------------- /qt/mac/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "anki-mac-helper" 7 | version = "0.1.1" 8 | description = "Small support library for Anki on Macs" 9 | requires-python = ">=3.9" 10 | license = { text = "AGPL-3.0-or-later" } 11 | authors = [ 12 | { name = "Anki Team" }, 13 | ] 14 | urls = { Homepage = "https://github.com/ankitects/anki" } 15 | 16 | [tool.hatch.build.targets.wheel] 17 | packages = ["anki_mac_helper"] 18 | -------------------------------------------------------------------------------- /rslib/src/storage/notetype/existing_cards.sql: -------------------------------------------------------------------------------- 1 | SELECT id, 2 | nid, 3 | ord, 4 | -- original deck 5 | ( 6 | CASE 7 | odid 8 | WHEN 0 THEN did 9 | ELSE odid 10 | END 11 | ), 12 | -- new position if card is empty 13 | ( 14 | CASE 15 | type 16 | WHEN 0 THEN ( 17 | CASE 18 | odue 19 | WHEN 0 THEN max(0, due) 20 | ELSE max(odue, 0) 21 | END 22 | ) 23 | ELSE NULL 24 | END 25 | ) 26 | FROM cards c -------------------------------------------------------------------------------- /ts/lib/tslib/context-keys.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export const fontFamilyKey = Symbol("fontFamily"); 5 | export const fontSizeKey = Symbol("fontSize"); 6 | export const directionKey = Symbol("direction"); 7 | export const descriptionKey = Symbol("description"); 8 | export const collapsedKey = Symbol("collapsed"); 9 | export const tagActionsShortcutsKey = Symbol("tagActionsShortcuts"); 10 | -------------------------------------------------------------------------------- /qt/launcher/lin/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ "$PREFIX" = "" ]; then 6 | PREFIX=/usr/local 7 | fi 8 | 9 | echo "Uninstalling Anki..." 10 | xdg-mime uninstall "$PREFIX"/share/anki/anki.xml || true 11 | 12 | rm -rf "$PREFIX"/share/anki 13 | rm -rf "$PREFIX"/bin/anki 14 | rm -rf "$PREFIX"/share/pixmaps/anki.xpm 15 | rm -rf "$PREFIX"/share/pixmaps/anki.png 16 | rm -rf "$PREFIX"/share/applications/anki.desktop 17 | rm -rf "$PREFIX"/share/man/man1/anki.1 18 | 19 | echo "Uninstall complete." 20 | -------------------------------------------------------------------------------- /qt/runanki.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright: Ankitects Pty Ltd and contributors 3 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 4 | 5 | import os 6 | import sys 7 | 8 | try: 9 | import bazelfixes 10 | 11 | bazelfixes.fix_pywin32_in_bazel() 12 | bazelfixes.fix_extraneous_path_in_bazel() 13 | bazelfixes.fix_run_on_macos() 14 | except ImportError: 15 | pass 16 | 17 | import aqt 18 | 19 | if not os.environ.get("ANKI_IMPORT_ONLY"): 20 | aqt.run() 21 | -------------------------------------------------------------------------------- /ts/lib/sveltelib/composition.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { writable } from "svelte/store"; 5 | 6 | /** 7 | * Indicates whether an IME composition session is currently active 8 | */ 9 | export const isComposing = writable(false); 10 | 11 | window.addEventListener("compositionstart", () => isComposing.set(true)); 12 | window.addEventListener("compositionend", () => isComposing.set(false)); 13 | -------------------------------------------------------------------------------- /ts/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { setupGlobalI18n } from "@tslib/i18n"; 5 | import { checkNightMode } from "@tslib/nightmode"; 6 | 7 | import type { LayoutLoad } from "./$types"; 8 | 9 | export const ssr = false; 10 | export const prerender = false; 11 | 12 | export const load: LayoutLoad = async () => { 13 | checkNightMode(); 14 | await setupGlobalI18n(); 15 | }; 16 | -------------------------------------------------------------------------------- /pylib/anki/statsbg.py: -------------------------------------------------------------------------------- 1 | # from subtlepatterns.com; CC BY 4.0. 2 | # by Daniel Beaton 3 | # https://www.toptal.com/designers/subtlepatterns/fancy-deboss/ 4 | bg = """\ 5 | iVBORw0KGgoAAAANSUhEUgAAABIAAAANCAMAAACTkM4rAAAAM1BMVEXy8vLz8/P5+fn19fXt7e329vb4+Pj09PTv7+/u7u739/fw8PD7+/vx8fHr6+v6+vrs7Oz2LjW2AAAAkUlEQVR42g3KyXHAQAwDQYAQj12ItvOP1qqZZwMMPVnd06XToQvz4L2HDQ2iRgkvA7yPPB+JD+OUPnfzZ0JNZh6kkQus5NUmR7g4Jpxv5XN6nYWNmtlq9o3zuK6w3XRsE1pQIEGPIsdtTP3m2cYwlPv6MbL8/QASsKppZefyDmJPbxvxa/NrX1TJ1yp20fhj9D+SiAWWLU8myQAAAABJRU5ErkJggg== 6 | """ 7 | -------------------------------------------------------------------------------- /rslib/i18n/src/generated.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | #![allow(clippy::all)] 5 | 6 | #[derive(Clone)] 7 | pub struct All; 8 | 9 | // Include auto-generated content 10 | include!(concat!(env!("OUT_DIR"), "/strings.rs")); 11 | 12 | impl Translations for All { 13 | const STRINGS: &phf::Map<&str, &phf::Map<&str, &str>> = &_STRINGS; 14 | const KEYS_BY_MODULE: &[&[&str]] = &_KEYS_BY_MODULE; 15 | } 16 | -------------------------------------------------------------------------------- /ts/lib/sveltelib/position/position-algorithm.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import type { FloatingElement, Placement, ReferenceElement } from "@floating-ui/dom"; 5 | 6 | /** 7 | * The interface of a function that calls `computePosition` of floating-ui. 8 | */ 9 | export type PositionAlgorithm = ( 10 | reference: ReferenceElement, 11 | floating: FloatingElement, 12 | ) => Promise; 13 | -------------------------------------------------------------------------------- /ts/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /python/sphinx/index.rst: -------------------------------------------------------------------------------- 1 | .. Anki documentation master file, created by 2 | sphinx-quickstart on Tue Sep 26 09:41:18 2023. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Anki's documentation! 7 | ================================ 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | -------------------------------------------------------------------------------- /rslib/src/backend/adding.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use anki_proto::notes::DeckAndNotetype as DeckAndNotetypeProto; 5 | 6 | use crate::adding::DeckAndNotetype; 7 | 8 | impl From for DeckAndNotetypeProto { 9 | fn from(s: DeckAndNotetype) -> Self { 10 | DeckAndNotetypeProto { 11 | deck_id: s.deck_id.0, 12 | notetype_id: s.notetype_id.0, 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rslib/i18n/src/generated_launcher.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | #![allow(clippy::all)] 5 | 6 | #[derive(Clone)] 7 | pub struct Launcher; 8 | 9 | // Include auto-generated content 10 | include!(concat!(env!("OUT_DIR"), "/strings_launcher.rs")); 11 | 12 | impl Translations for Launcher { 13 | const STRINGS: &phf::Map<&str, &phf::Map<&str, &str>> = &_STRINGS; 14 | const KEYS_BY_MODULE: &[&[&str]] = &_KEYS_BY_MODULE; 15 | } 16 | -------------------------------------------------------------------------------- /ts/transform_ts.mjs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { buildSync } from "esbuild"; 5 | import { argv } from "process"; 6 | 7 | const [_node, _script, entrypoint, js_out] = argv; 8 | 9 | // support Qt 5.14 10 | const target = ["es6", "chrome77"]; 11 | 12 | buildSync({ 13 | bundle: false, 14 | entryPoints: [entrypoint], 15 | outfile: js_out, 16 | minify: true, 17 | preserveSymlinks: true, 18 | target, 19 | }); 20 | -------------------------------------------------------------------------------- /ts/lib/sass/core.scss: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | @use "vars"; 5 | 6 | * { 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | color: var(--fg); 12 | background: var(--canvas); 13 | margin: 1em; 14 | &.fancy { 15 | transition: opacity var(--transition-medium) ease-out; 16 | } 17 | overscroll-behavior: none; 18 | } 19 | 20 | a { 21 | color: var(--fg-link); 22 | text-decoration: none; 23 | } 24 | -------------------------------------------------------------------------------- /ts/routes/graphs/HoverColumns.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /ftl/qt/preferences.ftl: -------------------------------------------------------------------------------- 1 | ## Video drivers/hardware acceleration. Please avoid translating 'OpenGL' and 'ANGLE'. 2 | 3 | preferences-video-driver = Video driver 4 | preferences-video-driver-opengl-mac = OpenGL (recommended on Macs) 5 | preferences-video-driver-software-mac = Software (not recommended) 6 | preferences-video-driver-opengl-other = OpenGL (faster, may cause issues) 7 | preferences-video-driver-software-other = Software (slower) 8 | preferences-video-driver-angle = ANGLE (may work better than OpenGL) 9 | preferences-video-driver-default = default 10 | -------------------------------------------------------------------------------- /rslib/benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use anki::card_rendering::anki_directive_benchmark; 5 | use criterion::criterion_group; 6 | use criterion::criterion_main; 7 | use criterion::Criterion; 8 | 9 | pub fn criterion_benchmark(c: &mut Criterion) { 10 | c.bench_function("anki_tag_parse", |b| b.iter(|| anki_directive_benchmark())); 11 | } 12 | 13 | criterion_group!(benches, criterion_benchmark); 14 | criterion_main!(benches); 15 | -------------------------------------------------------------------------------- /rslib/src/sync/media/sanity.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use serde::Deserialize; 5 | use serde::Serialize; 6 | 7 | #[derive(Serialize, Deserialize)] 8 | pub struct SanityCheckRequest { 9 | pub local: u32, 10 | } 11 | 12 | #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] 13 | pub enum MediaSanityCheckResponse { 14 | #[serde(rename = "OK")] 15 | Ok, 16 | #[serde(rename = "mediaSanity")] 17 | SanityCheckFailed, 18 | } 19 | -------------------------------------------------------------------------------- /pylib/rsbridge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rsbridge" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | description = "Anki's Rust library code Python bindings" 10 | 11 | [lib] 12 | name = "rsbridge" 13 | crate-type = ["cdylib"] 14 | path = "lib.rs" 15 | test = false 16 | 17 | [dependencies] 18 | anki.workspace = true 19 | pyo3.workspace = true 20 | 21 | [features] 22 | rustls = ["anki/rustls"] 23 | native-tls = ["anki/native-tls"] 24 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/lib.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export interface IOAddingMode { 5 | kind: "add"; 6 | notetypeId: number; 7 | imagePath: string; 8 | } 9 | 10 | export interface IOCloningMode { 11 | kind: "add"; 12 | clonedNoteId: number; 13 | } 14 | 15 | export interface IOEditingMode { 16 | kind: "edit"; 17 | noteId: number; 18 | } 19 | 20 | export type IOMode = IOAddingMode | IOEditingMode | IOCloningMode; 21 | -------------------------------------------------------------------------------- /pylib/anki/sync.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from anki import sync_pb2 5 | 6 | # public exports 7 | SyncAuth = sync_pb2.SyncAuth 8 | SyncOutput = sync_pb2.SyncCollectionResponse 9 | SyncStatus = sync_pb2.SyncStatusResponse 10 | 11 | 12 | # Legacy attributes some add-ons may be using 13 | 14 | from .httpclient import HttpClient 15 | 16 | AnkiRequestsClient = HttpClient 17 | 18 | 19 | class Syncer: 20 | def sync(self) -> str: 21 | pass 22 | -------------------------------------------------------------------------------- /pylib/tests/test_template.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | from tests.shared import getEmptyCol 5 | 6 | 7 | def test_deferred_frontside(): 8 | col = getEmptyCol() 9 | m = col.models.current() 10 | m["tmpls"][0]["qfmt"] = "{{custom:Front}}" 11 | col.models.save(m) 12 | 13 | note = col.newNote() 14 | note["Front"] = "xxtest" 15 | note["Back"] = "" 16 | col.addNote(note) 17 | 18 | assert "xxtest" in note.cards()[0].answer() 19 | -------------------------------------------------------------------------------- /qt/launcher/lin/anki.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Anki 3 | Comment=An intelligent spaced-repetition memory training program 4 | GenericName=Flashcards 5 | Exec=anki %f 6 | TryExec=anki 7 | Icon=anki 8 | Categories=Education;Languages;KDE;Qt; 9 | Terminal=false 10 | Type=Application 11 | Version=1.0 12 | MimeType=application/x-apkg;application/x-anki;application/x-ankiaddon; 13 | #should be removed eventually as it was upstreamed as to be an XDG specification called SingleMainWindow 14 | X-GNOME-SingleWindow=true 15 | SingleMainWindow=true 16 | StartupWMClass=anki 17 | -------------------------------------------------------------------------------- /ts/lib/tslib/typing.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export function assertUnreachable(x: never): never { 5 | throw new Error(`unreachable: ${x}`); 6 | } 7 | 8 | export type Callback = () => void; 9 | export type AsyncCallback = () => Promise; 10 | 11 | export function singleCallback(...callbacks: Callback[]): Callback { 12 | return () => { 13 | for (const cb of callbacks) { 14 | cb(); 15 | } 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /rslib/src/storage/card/add_or_update.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR REPLACE INTO cards ( 3 | id, 4 | nid, 5 | did, 6 | ord, 7 | mod, 8 | usn, 9 | type, 10 | queue, 11 | due, 12 | ivl, 13 | factor, 14 | reps, 15 | lapses, 16 | left, 17 | odue, 18 | odid, 19 | flags, 20 | data 21 | ) 22 | VALUES ( 23 | ?, 24 | ?, 25 | ?, 26 | ?, 27 | ?, 28 | ?, 29 | ?, 30 | ?, 31 | ?, 32 | ?, 33 | ?, 34 | ?, 35 | ?, 36 | ?, 37 | ?, 38 | ?, 39 | ?, 40 | ? 41 | ) -------------------------------------------------------------------------------- /ts/lib/tslib/parsing.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | /** 5 | * Parsing with or without this dummy structure changes the output 6 | * for both `DOMParser.parseAsString` and range.createContextualFragment`. 7 | * Parsing without means that comments or meaningless html elements are dropped, 8 | * which we want to avoid. 9 | */ 10 | export function createDummyDoc(html: string): string { 11 | return `${html}`; 12 | } 13 | -------------------------------------------------------------------------------- /qt/launcher/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | fn main() { 4 | #[cfg(windows)] 5 | { 6 | embed_resource::compile("win/anki-manifest.rc", embed_resource::NONE) 7 | .manifest_required() 8 | .unwrap(); 9 | } 10 | println!("cargo:rerun-if-changed=../../out/buildhash"); 11 | let buildhash = std::fs::read_to_string("../../out/buildhash").unwrap_or_default(); 12 | println!("cargo:rustc-env=BUILDHASH={buildhash}"); 13 | } 14 | -------------------------------------------------------------------------------- /rslib/src/storage/card/add_card_if_unique.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR IGNORE INTO cards ( 3 | id, 4 | nid, 5 | did, 6 | ord, 7 | mod, 8 | usn, 9 | type, 10 | queue, 11 | due, 12 | ivl, 13 | factor, 14 | reps, 15 | lapses, 16 | left, 17 | odue, 18 | odid, 19 | flags, 20 | data 21 | ) 22 | VALUES ( 23 | ?, 24 | ?, 25 | ?, 26 | ?, 27 | ?, 28 | ?, 29 | ?, 30 | ?, 31 | ?, 32 | ?, 33 | ?, 34 | ?, 35 | ?, 36 | ?, 37 | ?, 38 | ?, 39 | ?, 40 | ? 41 | ) -------------------------------------------------------------------------------- /ts/lib/tslib/nightmode.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | /** Add night-mode class to documentElement if hash location is #night, and 5 | return true if added. */ 6 | export function checkNightMode(): boolean { 7 | const nightMode = window.location.hash == "#night"; 8 | if (nightMode) { 9 | document.documentElement.className = "night-mode"; 10 | document.documentElement.dataset.bsTheme = "dark"; 11 | } 12 | return nightMode; 13 | } 14 | -------------------------------------------------------------------------------- /rslib/src/storage/revlog/add.sql: -------------------------------------------------------------------------------- 1 | INSERT 2 | OR IGNORE INTO revlog ( 3 | id, 4 | cid, 5 | usn, 6 | ease, 7 | ivl, 8 | lastIvl, 9 | factor, 10 | time, 11 | type 12 | ) 13 | VALUES ( 14 | ( 15 | CASE 16 | WHEN ?1 17 | AND ?2 IN ( 18 | SELECT id 19 | FROM revlog 20 | ) THEN ( 21 | SELECT max(id) + 1 22 | FROM revlog 23 | ) 24 | ELSE ?2 25 | END 26 | ), 27 | ?, 28 | ?, 29 | ?, 30 | ?, 31 | ?, 32 | ?, 33 | ?, 34 | ? 35 | ) -------------------------------------------------------------------------------- /rslib/src/storage/upgrades/schema14_upgrade.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE deck_config ( 2 | id integer PRIMARY KEY NOT NULL, 3 | name text NOT NULL COLLATE unicase, 4 | mtime_secs integer NOT NULL, 5 | usn integer NOT NULL, 6 | config blob NOT NULL 7 | ); 8 | CREATE TABLE config ( 9 | KEY text NOT NULL PRIMARY KEY, 10 | usn integer NOT NULL, 11 | mtime_secs integer NOT NULL, 12 | val blob NOT NULL 13 | ) without rowid; 14 | CREATE TABLE tags ( 15 | tag text NOT NULL PRIMARY KEY COLLATE unicase, 16 | usn integer NOT NULL 17 | ) without rowid; 18 | UPDATE col 19 | SET ver = 14; -------------------------------------------------------------------------------- /rslib/src/sync/media/progress.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | #[derive(Debug, Default, Clone, Copy)] 5 | pub struct MediaSyncProgress { 6 | pub checked: usize, 7 | pub downloaded_files: usize, 8 | pub downloaded_deletions: usize, 9 | pub uploaded_files: usize, 10 | pub uploaded_deletions: usize, 11 | } 12 | 13 | #[derive(Debug, Default, Clone, Copy)] 14 | #[repr(transparent)] 15 | pub struct MediaCheckProgress { 16 | pub checked: usize, 17 | } 18 | -------------------------------------------------------------------------------- /rslib/sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anki-sync-server" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | description = "Standalone sync server" 10 | 11 | [[bin]] 12 | path = "main.rs" 13 | name = "anki-sync-server" 14 | 15 | [dependencies] 16 | 17 | [target.'cfg(windows)'.dependencies] 18 | anki = { workspace = true, features = ["native-tls"] } 19 | 20 | [target.'cfg(not(windows))'.dependencies] 21 | anki = { workspace = true, features = ["rustls"] } 22 | -------------------------------------------------------------------------------- /ts/lib/domlib/surround/tree/block-node.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { TreeNode } from "./tree-node"; 5 | 6 | /** 7 | * Its purpose is to block adjacent FormattingNodes from merging, or single 8 | * FormattingNodes from trying to ascend. 9 | */ 10 | export class BlockNode extends TreeNode { 11 | private constructor() { 12 | super(false); 13 | } 14 | 15 | static make(): BlockNode { 16 | return new BlockNode(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rslib/src/card_rendering/tts/other.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use anki_proto::card_rendering::all_tts_voices_response::TtsVoice; 5 | 6 | use crate::prelude::*; 7 | 8 | pub(super) fn all_voices(_validate: bool) -> Result> { 9 | invalid_input!("not implemented for this OS"); 10 | } 11 | 12 | pub(super) fn write_stream(_path: &str, _voice_id: &str, _speed: f32, _text: &str) -> Result<()> { 13 | invalid_input!("not implemented for this OS"); 14 | } 15 | -------------------------------------------------------------------------------- /ts/lib/tag-editor/TagSpacer.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 |
8 |
9 |
10 | 11 | 19 | -------------------------------------------------------------------------------- /rslib/src/storage/note/add.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO notes ( 2 | id, 3 | guid, 4 | mid, 5 | mod, 6 | usn, 7 | tags, 8 | flds, 9 | sfld, 10 | csum, 11 | flags, 12 | data 13 | ) 14 | VALUES ( 15 | ( 16 | CASE 17 | WHEN ?1 IN ( 18 | SELECT id 19 | FROM notes 20 | ) THEN ( 21 | SELECT max(id) + 1 22 | FROM notes 23 | ) 24 | ELSE ?1 25 | END 26 | ), 27 | ?, 28 | ?, 29 | ?, 30 | ?, 31 | ?, 32 | ?, 33 | ?, 34 | ?, 35 | 0, 36 | "" 37 | ) -------------------------------------------------------------------------------- /ts/lib/components/SettingTitle.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 |
8 | 9 |
10 | 11 | 19 | -------------------------------------------------------------------------------- /ftl/core/help.ftl: -------------------------------------------------------------------------------- 1 | ### Text shown in Help pages 2 | 3 | ## Header/footer 4 | 5 | # Link to more detailed information in the manual 6 | help-for-more-info = For more information, see { $link } in the manual. 7 | 8 | # Tooltip for links to the manual 9 | help-open-manual-chapter = Open { $name } in the manual 10 | 11 | help-ok = OK 12 | 13 | ## Body 14 | 15 | # Newly introduced settings may not have an explanation yet 16 | help-no-explanation = 17 | Whoops! There doesn't seem to be an explanation for this setting yet. 18 | 19 | You can help us complete this help page on { $link }. 20 | -------------------------------------------------------------------------------- /ts/lib/components/Icon.svelte: -------------------------------------------------------------------------------- 1 | 5 | 16 | 17 | {#if component} 18 | 19 | {:else} 20 | {@html icon.url} 21 | {/if} 22 | -------------------------------------------------------------------------------- /rslib/proto_gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anki_proto_gen" 3 | publish = false 4 | description = "Helpers for interface code generation" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | license.workspace = true 10 | rust-version.workspace = true 11 | 12 | [dependencies] 13 | anki_io.workspace = true 14 | anyhow.workspace = true 15 | camino.workspace = true 16 | inflections.workspace = true 17 | itertools.workspace = true 18 | prost-reflect.workspace = true 19 | prost-types.workspace = true 20 | regex.workspace = true 21 | walkdir.workspace = true 22 | -------------------------------------------------------------------------------- /ts/lib/components/Row.svelte: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 |
11 | 12 |
13 | 14 | 23 | -------------------------------------------------------------------------------- /ts/lib/tslib/helpers.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { marked } from "marked"; 5 | 6 | export type Callback = () => void; 7 | 8 | export function removeItem(items: T[], item: T): void { 9 | const index = items.findIndex((i: T): boolean => i === item); 10 | 11 | if (index >= 0) { 12 | items.splice(index, 1); 13 | } 14 | } 15 | 16 | export function renderMarkdown(text: string): string { 17 | return marked(text, { mangle: false, headerIds: false }); 18 | } 19 | -------------------------------------------------------------------------------- /ts/routes/graphs/graph-styles.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | // Global css classes used by subcomponents 5 | 6 | // Graph.svelte 7 | export const oddTickClass = "tick-odd"; 8 | export const clickableClass = "graph-element-clickable"; 9 | 10 | // It would be nice to define these in the svelte file that declares them, 11 | // but currently this trips the tooling up: 12 | // https://github.com/sveltejs/svelte/issues/5817 13 | // export { oddTickClass, clickableClass } from "./Graph.svelte"; 14 | -------------------------------------------------------------------------------- /ts/routes/card-info/CardInfoPlaceholder.svelte: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 |
{tr.cardStatsNoCard()}
10 | 11 | 21 | -------------------------------------------------------------------------------- /rslib/src/storage/card/fix_odue.sql: -------------------------------------------------------------------------------- 1 | UPDATE cards 2 | SET odue = ( 3 | CASE 4 | WHEN odue > 0 5 | AND ( 6 | type = 1 7 | OR queue = 2 8 | ) 9 | AND NOT ?3 10 | AND NOT odid THEN 0 11 | ELSE min(max(round(odue), -2147483648), 2147483647) 12 | END 13 | ), 14 | mod = ?1, 15 | usn = ?2 16 | WHERE odue != ( 17 | CASE 18 | WHEN odue > 0 19 | AND ( 20 | type = 1 21 | OR queue = 2 22 | ) 23 | AND NOT ?3 24 | AND NOT odid THEN 0 25 | ELSE min(max(round(odue), -2147483648), 2147483647) 26 | END 27 | ); -------------------------------------------------------------------------------- /rslib/src/storage/deck/due_counts.sql: -------------------------------------------------------------------------------- 1 | SELECT did, 2 | -- new 3 | sum(queue = :new_queue), 4 | -- reviews 5 | sum( 6 | queue = :review_queue 7 | AND due <= :day_cutoff 8 | ), 9 | -- interday learning 10 | sum( 11 | queue = :daylearn_queue 12 | AND due <= :day_cutoff 13 | ), 14 | -- intraday learning 15 | sum( 16 | ( 17 | ( 18 | queue = :learn_queue 19 | AND due < :learn_cutoff 20 | ) 21 | OR ( 22 | queue = :preview_queue 23 | AND due <= :learn_cutoff 24 | ) 25 | ) 26 | ), 27 | -- total 28 | COUNT(1) 29 | FROM cards -------------------------------------------------------------------------------- /ts/lib/domlib/surround/tree/element-node.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { TreeNode } from "./tree-node"; 5 | 6 | export class ElementNode extends TreeNode { 7 | private constructor( 8 | public readonly element: Element, 9 | public readonly insideRange: boolean, 10 | ) { 11 | super(insideRange); 12 | } 13 | 14 | static make(element: Element, insideRange: boolean): ElementNode { 15 | return new ElementNode(element, insideRange); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ts/lib/tag-editor/TagDeleteBadge.svelte: -------------------------------------------------------------------------------- 1 | 5 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ftl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ftl" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | description = "Helpers for Anki's i18n system" 10 | 11 | [dependencies] 12 | anki_io.workspace = true 13 | anki_process.workspace = true 14 | anyhow.workspace = true 15 | camino.workspace = true 16 | clap.workspace = true 17 | fluent-syntax.workspace = true 18 | itertools.workspace = true 19 | regex.workspace = true 20 | serde_json.workspace = true 21 | snafu.workspace = true 22 | walkdir.workspace = true 23 | -------------------------------------------------------------------------------- /pylib/anki/rsbackend.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | # 4 | # The backend code has moved into _backend; this file exists only to avoid breaking 5 | # some add-ons. They should be updated to point to the correct location in the 6 | # future. 7 | 8 | # ruff: noqa: F401 9 | from anki.decks import DeckTreeNode 10 | from anki.errors import InvalidInput, NotFoundError 11 | from anki.lang import TR 12 | from anki.lang import FormatTimeSpan as FormatTimeSpanContext 13 | from anki.utils import from_json_bytes, to_json_bytes 14 | -------------------------------------------------------------------------------- /rslib/linkchecker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linkchecker" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | 10 | [dependencies] 11 | anki.workspace = true 12 | futures.workspace = true 13 | itertools.workspace = true 14 | linkcheck.workspace = true 15 | regex.workspace = true 16 | reqwest.workspace = true 17 | strum.workspace = true 18 | tokio.workspace = true 19 | 20 | [features] 21 | rustls = ["reqwest/rustls-tls", "reqwest/rustls-tls-native-roots"] 22 | native-tls = ["reqwest/native-tls"] 23 | -------------------------------------------------------------------------------- /pylib/anki/syncserver.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | 5 | def run_sync_server() -> None: 6 | import sys 7 | from os import environ as env 8 | 9 | from anki._backend import RustBackend 10 | 11 | env["RUST_LOG"] = env.get("RUST_LOG", "anki=info") 12 | 13 | try: 14 | RustBackend.syncserver() 15 | except Exception as exc: 16 | print("Sync server failed:", exc) 17 | sys.exit(1) 18 | sys.exit(0) 19 | 20 | 21 | if __name__ == "__main__": 22 | run_sync_server() 23 | -------------------------------------------------------------------------------- /qt/aqt/data/web/js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../../../ts/tsconfig_legacy.json", 3 | "include": ["*.ts"], 4 | "references": [], 5 | "compilerOptions": { 6 | "target": "es6", 7 | "module": "commonjs", 8 | "lib": ["es2019", "dom", "dom.iterable"], 9 | "types": ["jquery", "jqueryui"], 10 | "strict": true, 11 | "isolatedModules": false, 12 | "noImplicitAny": false, 13 | "strictNullChecks": false, 14 | "strictPropertyInitialization": false, 15 | "noImplicitThis": false, 16 | "esModuleInterop": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /qt/launcher/lin/anki.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Anki 2.1 collection package 6 | 7 | 8 | 9 | 10 | Anki 2.0 deck package 11 | 12 | 13 | 14 | 15 | Anki 2.1 add-on package 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ts/editor/ReviewerEditor.svelte: -------------------------------------------------------------------------------- 1 | 5 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ts/lib/components/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export type Size = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; 5 | export type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl" | "xxl"; 6 | 7 | export type HelpItem = { 8 | title: string; 9 | help?: string; 10 | url?: string; 11 | sched?: HelpItemScheduler; 12 | global?: boolean; 13 | }; 14 | 15 | export enum HelpItemScheduler { 16 | SM2 = 0, 17 | FSRS = 1, 18 | } 19 | 20 | export type IconData = { 21 | url: string; 22 | }; 23 | -------------------------------------------------------------------------------- /ts/routes/import-page/TableCell.svelte: -------------------------------------------------------------------------------- 1 | 5 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ts/routes/import-page/[...path]/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ts/bundle_ts.mjs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import { build } from "esbuild"; 5 | import { argv, env } from "process"; 6 | 7 | const [_node, _script, entrypoint, bundle_js] = argv; 8 | 9 | // support Qt 5.14 10 | const target = ["es6", "chrome77"]; 11 | 12 | build({ 13 | bundle: true, 14 | entryPoints: [entrypoint], 15 | outfile: bundle_js, 16 | minify: env.RELEASE && true, 17 | sourcemap: env.SOURCEMAP ? "inline" : false, 18 | preserveSymlinks: true, 19 | target, 20 | }).catch(() => process.exit(1)); 21 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/image-occlusion-base.scss: -------------------------------------------------------------------------------- 1 | @use "../lib/sass/vars"; 2 | @use "../lib/sass/bootstrap-dark"; 3 | 4 | @import "../lib/sass/base"; 5 | 6 | @import "bootstrap/scss/alert"; 7 | @import "bootstrap/scss/buttons"; 8 | @import "bootstrap/scss/button-group"; 9 | @import "bootstrap/scss/close"; 10 | @import "bootstrap/scss/grid"; 11 | @import "../lib/sass/bootstrap-forms"; 12 | 13 | .night-mode { 14 | @include bootstrap-dark.night-mode; 15 | } 16 | 17 | html { 18 | overflow: hidden; 19 | } 20 | 21 | /** consistent font size **/ 22 | :root { 23 | --font-size: 16px; 24 | } 25 | body { 26 | font-size: 16px; 27 | } 28 | -------------------------------------------------------------------------------- /ftl/core/media.ftl: -------------------------------------------------------------------------------- 1 | media-error-executing = Error executing { $val }. 2 | media-error-running = Error running { $val } 3 | media-for-security-reasons-is-not = For security reasons, '{ $val }' is not allowed on cards. You can still use it by placing the command in a different package, and importing that package in the LaTeX header instead. 4 | media-generated-file = Generated file: { $val } 5 | media-have-you-installed-latex-and-dvipngdvisvgm = Have you installed latex and dvipng/dvisvgm? 6 | media-recordingtime = Recording...
Time: { $secs } 7 | media-sound-and-video-on-cards-will = Sound and video on cards will not function until mpv or mplayer is installed. 8 | -------------------------------------------------------------------------------- /ts/editor/Notification.svelte: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 |
10 | 11 |
12 | 13 | 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Bug Reports 4 | url: https://forums.ankiweb.net 5 | about: This issue tracker is for developers. Please report any bugs you encounter over on the user forum, and they will be triaged there. 6 | 7 | - name: Questions/Support 8 | url: https://forums.ankiweb.net 9 | about: If you have a question or need support, please post on the user forum. 10 | 11 | - name: Feature Requests/Suggestions 12 | url: https://forums.ankiweb.net/c/suggestions/17 13 | about: Please post suggestions and feature requests on our user forum. 14 | -------------------------------------------------------------------------------- /ts/routes/change-notetype/change-notetype-base.scss: -------------------------------------------------------------------------------- 1 | @use "../lib/sass/bootstrap-dark"; 2 | 3 | @import "../lib/sass/base"; 4 | 5 | @import "bootstrap/scss/alert"; 6 | @import "bootstrap/scss/buttons"; 7 | @import "bootstrap/scss/button-group"; 8 | @import "bootstrap/scss/close"; 9 | @import "bootstrap/scss/grid"; 10 | @import "../lib/sass/bootstrap-forms"; 11 | 12 | .night-mode { 13 | @include bootstrap-dark.night-mode; 14 | } 15 | 16 | body { 17 | width: min(100vw, 70em); 18 | margin: 0 auto; 19 | } 20 | 21 | html { 22 | overflow-x: hidden; 23 | } 24 | 25 | #main { 26 | padding: 0.5em 0.5em 1em 0.5em; 27 | height: 100vh; 28 | } 29 | -------------------------------------------------------------------------------- /ts/routes/deck-options/[deckId]/+page.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | import { getDeckConfigsForUpdate } from "@generated/backend"; 4 | 5 | import { DeckOptionsState } from "../lib"; 6 | import type { PageLoad } from "./$types"; 7 | 8 | export const load = (async ({ params }) => { 9 | const deckId = Number(params.deckId); 10 | 11 | const did = BigInt(deckId); 12 | const info = await getDeckConfigsForUpdate({ did }); 13 | const state = new DeckOptionsState(BigInt(did), info); 14 | 15 | return { state }; 16 | }) satisfies PageLoad; 17 | -------------------------------------------------------------------------------- /.buildkite/linux/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export PATH="$PATH:/state/rust/cargo/bin" 6 | export BUILD_ROOT=/state/build 7 | export ONLINE_TESTS=1 8 | 9 | echo "--- Install n2" 10 | ./tools/install-n2 11 | 12 | echo "+++ Building and testing" 13 | ln -sf out/node_modules . 14 | 15 | if [ "$CLEAR_RUST" = "1" ]; then 16 | rm -rf $BUILD_ROOT/rust 17 | fi 18 | 19 | rm -f out/build.ninja 20 | ./ninja pylib qt check 21 | 22 | echo "--- Ensure libs importable" 23 | SKIP_RUN=1 ./run 24 | 25 | echo "--- Check Rust libs" 26 | cargo install cargo-deny --version 0.14.24 27 | cargo deny check 28 | 29 | echo "--- Cleanup" 30 | rm -rf /tmp/* || true 31 | -------------------------------------------------------------------------------- /qt/mac/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright: Ankitects Pty Ltd and contributors 3 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 4 | 5 | set -e 6 | 7 | # Get the project root directory 8 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | PROJ_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" 10 | 11 | # Build the dylib first 12 | echo "Building macOS helper dylib..." 13 | "$PROJ_ROOT/out/pyenv/bin/python" "$SCRIPT_DIR/helper_build.py" 14 | 15 | # Create the wheel using uv 16 | echo "Creating wheel..." 17 | cd "$SCRIPT_DIR" 18 | rm -rf dist 19 | "$PROJ_ROOT/out/extracted/uv/uv" build --wheel 20 | 21 | echo "Build complete!" 22 | -------------------------------------------------------------------------------- /rslib/proto/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | pub mod python; 5 | pub mod rust; 6 | pub mod typescript; 7 | 8 | use anki_proto_gen::descriptors_path; 9 | use anki_proto_gen::get_services; 10 | use anyhow::Result; 11 | 12 | fn main() -> Result<()> { 13 | let descriptors_path = descriptors_path(); 14 | 15 | let pool = rust::write_rust_protos(descriptors_path)?; 16 | let (_, services) = get_services(&pool); 17 | python::write_python_interface(&services)?; 18 | typescript::write_ts_interface(&services)?; 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /ts/lib/domlib/content-editable.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | /** 5 | * Trivial wrapper to silence Svelte deprecation warnings 6 | */ 7 | export function execCommand( 8 | command: string, 9 | showUI?: boolean | undefined, 10 | value?: string | undefined, 11 | ): void { 12 | document.execCommand(command, showUI, value); 13 | } 14 | 15 | /** 16 | * Trivial wrappers to silence Svelte deprecation warnings 17 | */ 18 | export function queryCommandState(command: string): boolean { 19 | return document.queryCommandState(command); 20 | } 21 | -------------------------------------------------------------------------------- /ts/routes/card-info/[cardId]/+page.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | import { cardStats } from "@generated/backend"; 4 | 5 | import type { PageLoad } from "./$types"; 6 | 7 | function optionalBigInt(x: any): bigint | null { 8 | try { 9 | return BigInt(x); 10 | } catch (e) { 11 | return null; 12 | } 13 | } 14 | 15 | export const load = (async ({ params }) => { 16 | const cid = optionalBigInt(params.cardId); 17 | const info = cid !== null ? await cardStats({ cid }) : null; 18 | return { info }; 19 | }) satisfies PageLoad; 20 | -------------------------------------------------------------------------------- /ts/routes/graphs/InputBox.svelte: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 |
9 | 10 |
11 | 12 | 27 | -------------------------------------------------------------------------------- /.idea.dist/repo.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | STRINGS_PY = { value = "out/pylib/anki/_fluent.py", relative = true } 3 | STRINGS_TS = { value = "out/ts/lib/generated/ftl.ts", relative = true } 4 | DESCRIPTORS_BIN = { value = "out/rslib/proto/descriptors.bin", relative = true } 5 | # build script will append .exe if necessary 6 | PROTOC = { value = "out/extracted/protoc/bin/protoc", relative = true } 7 | PYO3_NO_PYTHON = "1" 8 | MACOSX_DEPLOYMENT_TARGET = "11" 9 | PYTHONDONTWRITEBYTECODE = "1" # prevent junk files on Windows 10 | 11 | [term] 12 | color = "always" 13 | 14 | [target.'cfg(all(target_env = "msvc", target_os = "windows"))'] 15 | rustflags = ["-C", "target-feature=+crt-static"] 16 | -------------------------------------------------------------------------------- /ftl/core/empty-cards.ftl: -------------------------------------------------------------------------------- 1 | empty-cards-for-note-type = Empty cards for { $notetype }: 2 | empty-cards-count-line = { $empty_count } of { $existing_count } cards empty ({ $template_names }). 3 | empty-cards-window-title = Empty Cards 4 | empty-cards-preserve-notes-checkbox = Keep notes with no valid cards 5 | empty-cards-delete-button = Delete 6 | empty-cards-not-found = No empty cards. 7 | empty-cards-deleted-count = 8 | Deleted { $cards -> 9 | [one] { $cards } card. 10 | *[other] { $cards } cards. 11 | } 12 | empty-cards-delete-empty-cards = Delete Empty Cards 13 | empty-cards-delete-empty-notes = Delete Empty Notes 14 | empty-cards-deleting = Deleting... 15 | -------------------------------------------------------------------------------- /ts/lib/components/context-keys.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | export const touchDeviceKey = Symbol("touchDevice"); 5 | export const sectionKey = Symbol("section"); 6 | export const buttonGroupKey = Symbol("buttonGroup"); 7 | export const dropdownKey = Symbol("dropdown"); 8 | export const modalsKey = Symbol("modals"); 9 | export const floatingKey = Symbol("floating"); 10 | export const overlayKey = Symbol("overlay"); 11 | export const selectKey = Symbol("select"); 12 | export const showKey = Symbol("selectShow"); 13 | export const focusIdKey = Symbol("selectFocusId"); 14 | -------------------------------------------------------------------------------- /rslib/src/card_rendering/tts/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use anki_proto::card_rendering::all_tts_voices_response::TtsVoice; 5 | 6 | use crate::prelude::*; 7 | 8 | #[cfg(windows)] 9 | #[path = "windows.rs"] 10 | mod inner; 11 | #[cfg(not(windows))] 12 | #[path = "other.rs"] 13 | mod inner; 14 | 15 | pub fn all_voices(validate: bool) -> Result> { 16 | inner::all_voices(validate) 17 | } 18 | 19 | pub fn write_stream(path: &str, voice_id: &str, speed: f32, text: &str) -> Result<()> { 20 | inner::write_stream(path, voice_id, speed, text) 21 | } 22 | -------------------------------------------------------------------------------- /proto/anki/generic.proto: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | syntax = "proto3"; 5 | 6 | option java_multiple_files = true; 7 | 8 | package anki.generic; 9 | 10 | message Empty {} 11 | 12 | message Int32 { 13 | sint32 val = 1; 14 | } 15 | 16 | message UInt32 { 17 | uint32 val = 1; 18 | } 19 | 20 | message Int64 { 21 | int64 val = 1; 22 | } 23 | 24 | message String { 25 | string val = 1; 26 | } 27 | 28 | message Json { 29 | bytes json = 1; 30 | } 31 | 32 | message Bool { 33 | bool val = 1; 34 | } 35 | 36 | message StringList { 37 | repeated string vals = 1; 38 | } 39 | -------------------------------------------------------------------------------- /pylib/tools/genbuildinfo.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import sys 5 | 6 | version_file = sys.argv[1] 7 | buildhash_file = sys.argv[2] 8 | outpath = sys.argv[3] 9 | 10 | with open(version_file, "r", encoding="utf8") as f: 11 | version = f.read().strip() 12 | 13 | with open(buildhash_file, "r", encoding="utf8") as f: 14 | buildhash = f.read().strip() 15 | 16 | with open(outpath, "w", encoding="utf8") as f: 17 | # if we switch to uppercase we'll need to add legacy aliases 18 | f.write(f"version = '{version}'\n") 19 | f.write(f"buildhash = '{buildhash}'\n") 20 | -------------------------------------------------------------------------------- /qt/mac/theme.swift: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import AppKit 5 | import Foundation 6 | 7 | /// Force our app to be either light or dark mode. 8 | @_cdecl("set_darkmode_enabled") 9 | public func setDarkmodeEnabled(_ enabled: Bool) { 10 | NSApplication.shared.appearance = NSAppearance(named: enabled ? .darkAqua : .aqua) 11 | } 12 | 13 | /// True if the system is set to dark mode. 14 | @_cdecl("system_is_dark") 15 | public func systemIsDark() -> Bool { 16 | let styleSet = UserDefaults.standard.object(forKey: "AppleInterfaceStyle") != nil 17 | return styleSet 18 | } 19 | -------------------------------------------------------------------------------- /ts/routes/deck-options/Warning.svelte: -------------------------------------------------------------------------------- 1 | 5 | 14 | 15 | {#if warning} 16 | 17 |
18 | {withoutUnicodeIsolation(warning)} 19 |
20 |
21 | {/if} 22 | -------------------------------------------------------------------------------- /ts/routes/graphs/AxisTicks.svelte: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /ts/routes/import-csv/import-csv-base.scss: -------------------------------------------------------------------------------- 1 | @use "../lib/sass/bootstrap-dark"; 2 | 3 | @import "../lib/sass/base"; 4 | 5 | @import "bootstrap/scss/alert"; 6 | @import "bootstrap/scss/buttons"; 7 | @import "bootstrap/scss/button-group"; 8 | @import "bootstrap/scss/close"; 9 | @import "bootstrap/scss/grid"; 10 | @import "bootstrap/scss/transitions"; 11 | @import "bootstrap/scss/modal"; 12 | @import "bootstrap/scss/carousel"; 13 | @import "../lib/sass/bootstrap-forms"; 14 | @import "../lib/sass/bootstrap-tooltip"; 15 | 16 | .night-mode { 17 | @include bootstrap-dark.night-mode; 18 | } 19 | 20 | body { 21 | padding: 0 1em 1em 1em; 22 | } 23 | 24 | html { 25 | height: initial; 26 | } 27 | -------------------------------------------------------------------------------- /qt/launcher/mac/entitlements.python.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-executable-page-protection 6 | 7 | com.apple.security.device.audio-input 8 | 9 | com.apple.security.device.camera 10 | 11 | com.apple.security.cs.allow-dyld-environment-variables 12 | 13 | com.apple.security.cs.disable-library-validation 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ts/lib/components/FloatingArrow.svelte: -------------------------------------------------------------------------------- 1 | 5 |
6 | 7 | 24 | -------------------------------------------------------------------------------- /ts/routes/deck-options/StepsInput.svelte: -------------------------------------------------------------------------------- 1 | 5 | 17 | 18 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /rslib/proto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anki_proto" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | rust-version.workspace = true 9 | description = "Anki's Rust library protobuf code" 10 | 11 | [build-dependencies] 12 | anki_io.workspace = true 13 | anki_proto_gen.workspace = true 14 | anyhow.workspace = true 15 | inflections.workspace = true 16 | itertools.workspace = true 17 | prost-build.workspace = true 18 | prost-reflect.workspace = true 19 | prost-types.workspace = true 20 | 21 | [dependencies] 22 | prost.workspace = true 23 | serde.workspace = true 24 | snafu.workspace = true 25 | strum.workspace = true 26 | -------------------------------------------------------------------------------- /rslib/src/storage/revlog/fix_props.sql: -------------------------------------------------------------------------------- 1 | UPDATE revlog 2 | SET ivl = min(max(round(ivl), -2147483648), 2147483647), 3 | lastIvl = min(max(round(lastIvl), -2147483648), 2147483647), 4 | time = min(max(round(time), 0), 2147483647), 5 | type = ( 6 | CASE 7 | WHEN type = 0 8 | AND time = 0 9 | AND ease = 0 THEN 5 10 | ELSE type 11 | END 12 | ) 13 | WHERE ivl != min(max(round(ivl), -2147483648), 2147483647) 14 | OR lastIvl != min(max(round(lastIvl), -2147483648), 2147483647) 15 | OR time != min(max(round(time), 0), 2147483647) 16 | OR type != ( 17 | CASE 18 | WHEN type = 0 19 | AND time = 0 20 | AND ease = 0 THEN 5 21 | ELSE type 22 | END 23 | ) -------------------------------------------------------------------------------- /ts/routes/import-anki-package/import-anki-package-base.scss: -------------------------------------------------------------------------------- 1 | @use "../lib/sass/bootstrap-dark"; 2 | 3 | @import "../lib/sass/base"; 4 | 5 | @import "bootstrap/scss/alert"; 6 | @import "bootstrap/scss/buttons"; 7 | @import "bootstrap/scss/button-group"; 8 | @import "bootstrap/scss/close"; 9 | @import "bootstrap/scss/grid"; 10 | @import "bootstrap/scss/transitions"; 11 | @import "bootstrap/scss/modal"; 12 | @import "bootstrap/scss/carousel"; 13 | @import "../lib/sass/bootstrap-forms"; 14 | @import "../lib/sass/bootstrap-tooltip"; 15 | 16 | .night-mode { 17 | @include bootstrap-dark.night-mode; 18 | } 19 | 20 | body { 21 | padding: 0 1em 1em 1em; 22 | } 23 | 24 | html { 25 | height: initial; 26 | } 27 | -------------------------------------------------------------------------------- /ts/routes/import-page/TableCellWithTooltip.svelte: -------------------------------------------------------------------------------- 1 | 5 | 14 | 15 | 16 | createTooltip(event.detail.element)} 19 | > 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ftl/core/change-notetype.ftl: -------------------------------------------------------------------------------- 1 | change-notetype-current = Current 2 | change-notetype-new = New 3 | change-notetype-nothing = (Nothing) 4 | change-notetype-collapse = Collapse 5 | change-notetype-expand = Expand 6 | change-notetype-will-discard-content = Will discard content on the following fields: 7 | change-notetype-will-discard-cards = Will remove the following cards: 8 | change-notetype-fields = Fields 9 | change-notetype-templates = Templates 10 | change-notetype-to-from-cloze = 11 | When changing to or from a Cloze note type, card numbers remain unchanged. 12 | 13 | If changing to a regular note type, and there are more cloze deletions 14 | than available card templates, any extra cards will be removed. 15 | -------------------------------------------------------------------------------- /qt/aqt/data/web/js/webview.ts: -------------------------------------------------------------------------------- 1 | /* Copyright: Ankitects Pty Ltd and contributors 2 | * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ 3 | 4 | // prevent backspace key from going back a page 5 | document.addEventListener("keydown", function(evt: KeyboardEvent) { 6 | if (evt.keyCode !== 8) { 7 | return; 8 | } 9 | let isText = 0; 10 | const node = evt.target as Element; 11 | const nn = node.nodeName; 12 | if (nn === "INPUT" || nn === "TEXTAREA") { 13 | isText = 1; 14 | } else if (nn === "DIV" && (node as HTMLDivElement).contentEditable) { 15 | isText = 1; 16 | } 17 | if (!isText) { 18 | evt.preventDefault(); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /ts/editor/HandleBackground.svelte: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 |
17 | 18 | 27 | -------------------------------------------------------------------------------- /proto/anki/ankihub.proto: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | syntax = "proto3"; 5 | 6 | option java_multiple_files = true; 7 | 8 | import "anki/generic.proto"; 9 | 10 | package anki.ankihub; 11 | 12 | service AnkiHubService {} 13 | 14 | service BackendAnkiHubService { 15 | rpc AnkihubLogin(LoginRequest) returns (LoginResponse); 16 | rpc AnkihubLogout(LogoutRequest) returns (generic.Empty); 17 | } 18 | 19 | message LoginResponse { 20 | string token = 1; 21 | } 22 | 23 | message LoginRequest { 24 | string id = 1; 25 | string password = 2; 26 | } 27 | 28 | message LogoutRequest { 29 | string token = 1; 30 | } 31 | -------------------------------------------------------------------------------- /ts/routes/deck-options/ParamsInputRow.svelte: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /rslib/src/scheduler/service/states/new.rs: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | use crate::scheduler::states::NewState; 5 | 6 | impl From for NewState { 7 | fn from(state: anki_proto::scheduler::scheduling_state::New) -> Self { 8 | NewState { 9 | position: state.position, 10 | } 11 | } 12 | } 13 | 14 | impl From for anki_proto::scheduler::scheduling_state::New { 15 | fn from(state: NewState) -> Self { 16 | anki_proto::scheduler::scheduling_state::New { 17 | position: state.position, 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /qt/aqt/qt/qt6.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | # make sure not to optimize imports on this file 5 | # ruff: noqa: F401 6 | """ 7 | PyQt6 imports 8 | """ 9 | 10 | from PyQt6 import sip 11 | from PyQt6.QtCore import * 12 | 13 | # conflicting Qt and qFuzzyCompare definitions require an ignore 14 | from PyQt6.QtGui import * # type: ignore[no-redef,assignment] 15 | from PyQt6.QtNetwork import QLocalServer, QLocalSocket, QNetworkProxy 16 | from PyQt6.QtQuick import * 17 | from PyQt6.QtWebChannel import QWebChannel 18 | from PyQt6.QtWebEngineCore import * 19 | from PyQt6.QtWebEngineWidgets import * 20 | from PyQt6.QtWidgets import * 21 | -------------------------------------------------------------------------------- /qt/launcher/lin/README.md: -------------------------------------------------------------------------------- 1 | # Installing Anki 2 | 3 | ## Running directly 4 | 5 | To run without installing, change to this folder in a terminal, and run the 6 | following command: 7 | 8 | ./anki 9 | 10 | ## Installing 11 | 12 | To install system wide, run 'sudo ./install.sh' 13 | 14 | To remove in the future, run 'sudo /usr/local/share/anki/uninstall.sh'. You should 15 | do this before installing a newer version. 16 | 17 | ## Audio 18 | 19 | To play and record audio, mpv and lame must be installed. If mpv is not 20 | installed or too old, Anki will try to fall back on using mplayer. 21 | 22 | ## Problems 23 | 24 | If Anki fails to start, please run it from a terminal to see what errors it 25 | outputs, and then post on our support site. 26 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/review.scss: -------------------------------------------------------------------------------- 1 | #image-occlusion-container { 2 | position: relative; 3 | // if height-constrained, ensure container is centered 4 | margin: 0 auto; 5 | // allow for 20px margin on html element, or short windows can truncate 6 | // image 7 | max-height: calc(95vh - 40px); 8 | } 9 | 10 | #image-occlusion-container img { 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | width: 100%; 15 | height: 100%; 16 | // remove the default image limits, as we rely on container 17 | max-width: unset; 18 | max-height: unset; 19 | } 20 | 21 | #image-occlusion-canvas { 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | width: 100%; 26 | height: 100%; 27 | } 28 | -------------------------------------------------------------------------------- /ts/routes/image-occlusion/tools/tool-cursor.ts: -------------------------------------------------------------------------------- 1 | // Copyright: Ankitects Pty Ltd and contributors 2 | // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import type { fabric } from "fabric"; 5 | 6 | import { stopDraw } from "./lib"; 7 | import { onPinchZoom } from "./tool-zoom"; 8 | 9 | export const drawCursor = (canvas: fabric.Canvas): void => { 10 | canvas.selectionColor = "rgba(100, 100, 255, 0.3)"; 11 | stopDraw(canvas); 12 | 13 | canvas.on("mouse:down", function(o) { 14 | if (o.target) { 15 | return; 16 | } 17 | }); 18 | 19 | canvas.on("mouse:move", function(o) { 20 | if (onPinchZoom(o)) { 21 | return; 22 | } 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /.buildkite/linux/docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # - use './run.sh' to run in the foreground 3 | # - use './run.sh serve' to daemonize. 4 | 5 | set -e 6 | 7 | . common.inc 8 | 9 | if [ "$1" = "serve" ]; then 10 | extra_args="-d --restart always" 11 | else 12 | extra_args="-it" 13 | fi 14 | 15 | name=anki-${platform} 16 | 17 | # Stop and remove the existing container if it exists. 18 | # This doesn't delete the associated volume. 19 | if docker container inspect $name > /dev/null 2>&1; then 20 | docker stop $name || true 21 | docker container rm $name 22 | fi 23 | 24 | docker run $extra_args \ 25 | --name $name \ 26 | -v ${name}-state:/state \ 27 | -e BUILDKITE_AGENT_TOKEN \ 28 | -e BUILDKITE_AGENT_TAGS \ 29 | $name 30 | -------------------------------------------------------------------------------- /rslib/src/storage/card/congrats.sql: -------------------------------------------------------------------------------- 1 | SELECT coalesce( 2 | sum( 3 | queue IN (:review_queue, :day_learn_queue) 4 | AND due <= :today 5 | ), 6 | 0 7 | ) AS review_count, 8 | coalesce(sum(queue = :new_queue), 0) AS new_count, 9 | coalesce(sum(queue = :sched_buried_queue), 0) AS sched_buried, 10 | coalesce(sum(queue = :user_buried_queue), 0) AS user_buried, 11 | coalesce(sum(queue = :learn_queue), 0) AS learn_count, 12 | max( 13 | 0, 14 | coalesce( 15 | min( 16 | CASE 17 | WHEN queue = :learn_queue THEN due 18 | ELSE NULL 19 | END 20 | ), 21 | 0 22 | ) 23 | ) AS first_learn_due 24 | FROM cards 25 | WHERE did IN ( 26 | SELECT id 27 | FROM active_decks 28 | ) -------------------------------------------------------------------------------- /ts/editor/DuplicateLink.svelte: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | 11 | bridgeCommand("dupes")}> 12 | {tr.editingShowDuplicates()} 13 | 14 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /ts/routes/deck-options/GlobalLabel.svelte: -------------------------------------------------------------------------------- 1 | 5 | 12 | 13 | {title} 14 |
15 | 16 |
17 | 18 | 26 | -------------------------------------------------------------------------------- /ts/editor/rich-text-input/StyleLink.svelte: -------------------------------------------------------------------------------- 1 | 5 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ftl/core/adding.ftl: -------------------------------------------------------------------------------- 1 | adding-add-shortcut-ctrlandenter = Add (shortcut: ctrl+enter) 2 | adding-added = Added 3 | adding-discard-current-input = Discard current input? 4 | adding-keep-editing = Keep Editing 5 | adding-edit = Edit "{ $val }" 6 | adding-history = History 7 | adding-note-deleted = (Note deleted) 8 | adding-shortcut = Shortcut: { $val } 9 | adding-the-first-field-is-empty = The first field is empty. 10 | adding-you-have-a-cloze-deletion-note = You have a cloze note type but have not made any cloze deletions. Proceed? 11 | adding-cloze-outside-cloze-notetype = Cloze deletion can only be used on cloze note types. 12 | adding-cloze-outside-cloze-field = Cloze deletion can only be used in fields which use the 'cloze:' filter. This is typically the first field. 13 | -------------------------------------------------------------------------------- /qt/tools/build_qrc.py: -------------------------------------------------------------------------------- 1 | # Copyright: Ankitects Pty Ltd and contributors 2 | # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 3 | 4 | import os 5 | import sys 6 | 7 | qrc_file = os.path.abspath(sys.argv[1]) 8 | icons = sys.argv[2:] 9 | 10 | file_skeleton = """ 11 | 12 | 13 | FILES 14 | 15 | 16 | """.strip() 17 | 18 | indent = " " * 8 19 | lines = [] 20 | for icon in icons: 21 | base = os.path.basename(icon) 22 | path = os.path.relpath(icon, start=os.path.dirname(qrc_file)) 23 | line = f'{indent}{path}' 24 | lines.append(line) 25 | 26 | with open(qrc_file, "w") as file: 27 | file.write(file_skeleton.replace("FILES", "\n".join(lines))) 28 | -------------------------------------------------------------------------------- /ts/routes/deck-options/Addons.svelte: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | {#if $components.length} 17 | 18 | {#each $components as addon} 19 | 20 | {/each} 21 | 22 | {/if} 23 | --------------------------------------------------------------------------------