Commit graph

1074 commits

Author SHA1 Message Date
Damien Elmes
ec96bfe315 Uyghur is RTL
https://forums.ankiweb.net/t/rlt-ui-bug-uyghur-language-gui-direction/46610
2024-07-02 19:03:29 +07:00
Damien Elmes
e4326ce801 Add Uyghur to language list 2024-06-24 14:55:16 +07:00
Abdo
06f7aa393d
Add a preference to toggle LaTeX generation (#3218)
* Add a preference to toggle LaTeX generation

* Fix test

* Remove LaTeX security restrictions

* Show existing LaTeX images regardless of preference

* Lift config check out of loop (dae)

* Shift option to review settings; display warning when disabled (dae)
2024-06-01 09:26:28 +01:00
Damien Elmes
17a20067f3 Fix set_due_date docstring
Closes #3207
2024-05-25 16:08:45 +07:00
Damien Elmes
7efae4d95d Mnemosyne importer now handles float values for due date and interval 2024-05-17 12:34:01 +07:00
Abdo
00248665d7
Avoid locale.getdefaultlocale() if possible (#3143)
* Avoid locale.getdefaultlocale() if possible

* Suppress warning

* Add comment

* Apply suggestions from code review
2024-04-19 19:52:02 +10:00
Jarrett Ye
10d567f937
Update to FSRS-rs v0.6.1 (#3106)
* update to FSRS-rs 0.6.0

* update to crates.io version

* format

* update to FSRS-rs v0.6.01

* ./ninja fix:minilints

* update python backend code
2024-04-05 13:04:50 +01:00
Damien Elmes
9f55cf26fc
Switch to SvelteKit (#3077)
* Update to latest Node LTS

* Add sveltekit

* Split tslib into separate @generated and @tslib components

SvelteKit's path aliases don't support multiple locations, so our old
approach of using @tslib to refer to both ts/lib and out/ts/lib will no
longer work. Instead, all generated sources and their includes are
placed in a separate out/ts/generated folder, and imported via @generated
instead. This also allows us to generate .ts files, instead of needing
to output separate .d.ts and .js files.

* Switch package.json to module type

* Avoid usage of baseUrl

Incompatible with SvelteKit

* Move sass into ts; use relative links

SvelteKit's default sass support doesn't allow overriding loadPaths

* jest->vitest, graphs example working with yarn dev

* most pages working in dev mode

* Some fixes after rebasing

* Fix/silence some svelte-check errors

* Get image-occlusion working with Fabric types

* Post-rebase lock changes

* Editor is now checked

* SvelteKit build integrated into ninja

* Use the new SvelteKit entrypoint for pages like congrats/deck options/etc

* Run eslint once for ts/**; fix some tests

* Fix a bunch of issues introduced when rebasing over latest main

* Run eslint fix

* Fix remaining eslint+pylint issues; tests now all pass

* Fix some issues with a clean build

* Latest bufbuild no longer requires @__PURE__ hack

* Add a few missed dependencies

* Add yarn.bat to fix Windows build

* Fix pages failing to show when ANKI_API_PORT not defined

* Fix svelte-check and vitest on Windows

* Set node path in ./yarn

* Move svelte-kit output to ts/.svelte-kit

Sadly, I couldn't figure out a way to store it in out/ if out/ is
a symlink, as it breaks module resolution when SvelteKit is run.

* Allow HMR inside Anki

* Skip SvelteKit build when HMR is defined

* Fix some post-rebase issues

I should have done a normal merge instead.
2024-03-31 09:16:31 +01:00
snowtimeglass
ddccfb805f
Use "importing-added" string in importing detail table (#3104)
Supplement to the task of commit 184ad80 (Clone "added" string for importing)
2024-03-31 07:33:31 +01:00
RumovZ
36287888cc
Reset original id when cloning notetypes (#3089) 2024-03-24 08:18:59 +00:00
Jarrett Ye
055b6e30f8
update FSRS-rs to 0.5.0 & export benchmark API (#3056)
* update fsrs to 0.4.5

* update to fsrs 0.4.6

* add benchmark API

* update fsrs to 0.5.0

* cargo fmt

* ./ninja fix:minilints

* ./ninja format

* Add a brief comment about the new method (dae)
2024-03-08 12:37:06 +00:00
Damien Elmes
4fc535ec88
Dependency updates (#3040)
* Update JS deps

* Update semver-compat Rust deps

* Update some semver-incompat Rust deps

- hyper/axum held back because reqwests is not ready
- rusqlite held back due to burn-rs incompat version
- wiremock held back due to compile issue

* pylint wants changes to our _rsbridge.pyi

* Update Python deps

Also solves a security warning in orjson

Reformat with latest black
2024-02-28 16:28:04 +07:00
Abdo
23291e7172
Fix circular import (#3007) 2024-02-14 20:23:14 +10:00
Han Yeong-woo
9ba527afa2
Improve typing deck_due_tree() (#3003)
* Improve typing `deck_due_tree()`

* Add me to CONTRIBUTORS
2024-02-12 15:56:29 +10:00
cav71
f4a8f7d9c7
Add support for python logging (#2969)
* adds log module

* enable logging in the app

* adds a getLogger method to AddonManager

* change log level depending on ANKIDEV

* fix undefined module variable

* - fix addons log file path
- remove a breakpoint leftover

set the addons log files under pm.addonFolder()/NNNNNN/user_files/logs/NNNNNN.log

* fix path bug

* move log closing handling into AddonManager deleteAddon/backupUserFiles methods

* logging module level import
fix undefined variable in backupUserFiles

* pretty format log records

* move MediaServer log into logging

* update CONTRIBUTORS

* documentation cleanup

* capture warnings into log messages
fix waitress verbosity

* remove record_factory function

* add get_logger method alias to getLogger in AddonManager
switch to TimedRotatingFileHandler handler
fix minor typo

* set main log level to DEBUG if ANKIDEV is not 0 (or unset)
added two new methods to AddonManager addon_get_logger/addon_toggle_log_level

* add new find_logger_output to AddonManager

* move logs under pm.base

* change log output

* update addonmanager getlogger

* Format imports

* Refactor logging set-up slightly and tweak docstring

* Remove obsolete log closing statements

As logs are no longer stored in user_files, we do not need to close their handlers

* Refactor and try to simplify log module

* Remove demo code

* Refactor and update add-on manager logging API

* Simplify writing unit tests for add-ons that use logging

Loggers are likely to be also employed in non UI code, so it seems like a good idea to decouple them from requiring a running Anki instance to work (thus freeing add-on authors from the need to mock Anki APIs in their tests).

* Fix arguments and drop obsolete inline instructions

Lets add a section on logging to the add-on docs instead

* Drop unnecessary import

* Supply logging basicConfig force option by default

Until we change the module import order and thus ensure that `log` is always evaluated before third-party dependencies have a chance to initialize the root logger, `force` is non-optional.

* Fix formatting and type errors

* Restore mediasrv type ignore comments

* Add note on prefix API stability

* Consistently use addon_from_module in new code

* Use logFolder rather than profileFolder

* Adjust method name for PEP8

* Change loggerDict access path, satisfying pylint

* Drop unused import and use lazy % formatting

* lint fix

* refactor .log_folder -> .addon_logs
store anki.log under logdir

* Fix method name (dae)

* Disable file-based logging in the backend (dae)

I have never found this useful, and it logs nothing by default, so
creating/opening the file is a waste. Removing it also ensures that
addon_logs() is solely used for add-ons.

---------

Co-authored-by: Glutanimate <5459332+glutanimate@users.noreply.github.com>
2024-02-11 16:41:50 +10:00
Damien Elmes
6aa51cc933 Fix Ukranian language name
https://github.com/ankitects/anki/pull/2979
2024-01-30 15:11:24 +10:00
Damien Elmes
6b3381afef Log long-running calls on the UI thread 2023-12-10 12:37:01 +10:00
user1823
884e8dcac5
Ignore rescheduled cards in old stats page (#2863)
* Ignore rescheduled cards in old stats page

Rescheduled cards are not actual reviews. So, including them in Cards Studied Today is inappropriate.

* Simplify SQL query

* Fix Answer Buttons graph

* Fix error

* Potentially fix error
2023-12-05 09:55:33 +10:00
antecrescent
42145e8312
Update licensing information (#2850)
* Add myself to CONTRIBUTORS
2023-11-27 11:35:51 +10:00
Abdo
1e477406e6
Expose ImportAnkiPackageOptions (#2835) 2023-11-20 13:17:53 +10:00
RumovZ
f200d6248e
Allow im-/exporting with or without deck configs (#2804)
* Allow im-/exporting with or without deck configs

Closes #2777.

* Enable webengine remote debugging in launch.json

* Reset deck limits and counts based on scheduling

Also:
- Fix `deck.common` not being reset.
- Apply all logic only depending on the source collection in the
gathering stage.
- Skip checking for scheduling and only act based on whether the call
wants scheduling. Preservation of filtered decks also depends on all
original decks being included.
- Fix check_ids() not covering revlog.

* Fix importing legacy filtered decks w/o scheduling

* Disable 'include deck options' by default, and fix tab order (dae)

* deck options > deck presets (dae)
2023-11-13 13:54:41 +10:00
Abdo
6a2d1f94d4
Move anki.utils.html_to_text_line() to backend (#2816) 2023-11-09 09:57:23 +10:00
Damien Elmes
9268dce707 Expose fuzz delta for FSRS add-on 2023-11-06 12:27:53 +10:00
Damien Elmes
e7436a2b23 Fix add-ons screen showing 'requires Anki >= 2.1.231000' 2023-11-05 11:16:09 +10:00
Damien Elmes
9bbc6c9405 Add image occlusion to stock notetypes 2023-10-22 11:00:39 +10:00
Damien Elmes
f9de92ee09 Expose skip_undo_entry
https://forums.ankiweb.net/t/porting-tips-for-anki-23-10/35916/4
2023-10-22 10:21:27 +10:00
Abdo
bba67fdab4
Fix misleading warning when changing notetype from/to cloze (#2744) 2023-10-18 16:50:32 +10:00
Abdo
b23f17df27
Drop support for checkpoints (#2742)
* Drop support for checkpoints

* Deprecate .flush()

* Remove .begin/.commit

* Remove rollback() and deprecate save/autosave/reset()

There's no need to commit anymore, as the Rust code is handling
transactions for us.

* Add safer transact() method

This will ensure add-on authors can't accidentally leave a transaction
open, leading to data loss.

---------

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2023-10-17 12:43:34 +10:00
Abdo
5cde4b6941
Remove v1/v2 support from the backend (#2727)
* Remove v1/v2 support from deck list

* Remove v1/v2 support from most routines and show error

* Remove scheduler_version from preferences

* Fix formatting

* Remove v1/v2 conditionals from Python code

* Fix legacy importer

* Remove legacy hooks

* Add missing scheduler checks

* Remove V2 logic from deck options screen

* Remove the review_did_undo hook

* Restore ability to open old options with shift (dae)
2023-10-14 10:50:59 +10:00
Abdo
c052be9e25
Add a backend method to extract static media references (#2716)
* Add a backend method to extract static media references

* Extract into Notetype.gather_media_names()
2023-10-11 14:10:02 +10:00
RumovZ
23f29a6ecc
Invalid sorting (#2709)
* Rollback if toggling state fails

Previously, if the search triggered by a state toggle failed, the switch
and the model would move to the new state, while the table would remain
in the previous state.

* Fix reversed sort orders of FSRS columns

* Add sep. default sort orders for notes and cards

* Add test for consistent default sort orders

* Add launch config for debugging in VSC

* Extend launch config for macOS and Linux
2023-10-07 06:36:15 +00:00
Damien Elmes
0ef28853fd Fix TypeError when deck list or overview refreshes during optimal retention
Caused by legacy code blocking the main thread.

https://forums.ankiweb.net/t/anki-23-10-beta/34912/79
2023-09-30 16:10:23 +10:00
Damien Elmes
b8390d096e Shift weight calculation to backend so it can be run in parallel 2023-09-28 09:10:54 +10:00
Damien Elmes
1f55ad1d44
Expose the ability to train weights from items (#2687) 2023-09-28 08:28:24 +10:00
Damien Elmes
e6aaeb85e9 Expose memory state computation to Python
Closes #2676
2023-09-25 11:05:47 +10:00
Damien Elmes
4dc9890845 Include v2 scheduler in "no longer supported" message 2023-09-25 09:39:59 +10:00
Damien Elmes
558c75493f Strip out v1/v2 code 2023-09-24 14:27:40 +10:00
Damien Elmes
03edb7bf9e Store desired retention in card data
If we want to be able to factor the desired retention into a sort based
on relative overdueness, having the values accessible on the card makes
things easier.
2023-09-23 15:42:42 +10:00
Abdo
c2b1ab5eb0
Skip template checks in Fields screen (#2670) 2023-09-20 16:09:54 +10:00
Damien Elmes
0301ae1d8a fsrs_memory_state -> memory_state 2023-09-17 11:50:38 +10:00
Damien Elmes
5004cd332b
Integrate FSRS into Anki (#2654)
* Pack FSRS data into card.data

* Update FSRS card data when preset or weights change

+ Show FSRS stats in card stats

* Show a warning when there's a limited review history

* Add some translations; tweak UI

* Fix default requested retention

* Add browser columns, fix calculation of R

* Property searches

eg prop:d>0.1

* Integrate FSRS into reviewer

* Warn about long learning steps

* Hide minimum interval when FSRS is on

* Don't apply interval multiplier to FSRS intervals

* Expose memory state to Python

* Don't set memory state on new cards

* Port Jarret's new tests; add some helpers to make tests more compact

https://github.com/open-spaced-repetition/fsrs-rs/pull/64

* Fix learning cards not being given memory state

* Require update to v3 scheduler

* Don't exclude single learning step when calculating memory state

* Use relearning step when learning steps unavailable

* Update docstring

* fix single_card_revlog_to_items (#2656)

* not need check the review_kind for unique_dates

* add email address to CONTRIBUTORS

* fix last first learn & keep early review

* cargo fmt

* cargo clippy --fix

* Add Jarrett to about screen

* Fix fsrs_memory_state being initialized to default in get_card()

* Set initial memory state on graduate

* Update to latest FSRS

* Fix experiment.log being empty

* Fix broken colpkg imports

Introduced by "Update FSRS card data when preset or weights change"

* Update memory state during (re)learning; use FSRS for graduating intervals

* Reset memory state when cards are manually rescheduled as new

* Add difficulty graph; hide eases when FSRS enabled

* Add retrievability graph

* Derive memory_state from revlog when it's missing and shouldn't be

---------

Co-authored-by: Jarrett Ye <jarrett.ye@outlook.com>
2023-09-16 16:09:26 +10:00
Abdo
6f0bf58d49
Add a backend method to add notes in bulk (#2659)
* Add a backend method to add notes in bulk

* i -> idx

* Remove duplicate assignment

* Allow add_notes to work with multiple deck IDs

* Rename note_deck_id to requests
2023-09-16 13:51:32 +10:00
Damien Elmes
bfef908c6c
Refactor media sync handling (#2647)
* Refactor media sync handling

- The media USN is now returned in sync/meta, which avoids an extra
round-trip.
- Media syncing is now automatically started by the syncing code at
the end of a normal or full sync, which avoids it competing for bandwidth
and resources, and avoids duplicate invalid login messages when the auth
token is invalid.
- Added a new media_sync_progress() method to both check if media is
syncing, and get access to the latest progress.
- Updated the sync log screen to only show the latest line, like AnkiMobile.
- Show media sync errors in a pop-up, so they don't get missed. Use a non-modal
pop-up to avoid potential conflicts with other modals.

* Remove print statement
2023-09-10 13:22:20 +10:00
RumovZ
14de8451dc
Merging Notetypes on Import (#2612)
* Remember original id when importing notetype

* Reuse notetypes with matching original id

* Add field and template ids

* Enable merging imported notetypes

* Fix test

Note should be updated if the incoming note's notetype is
remapped to the existing note's notetype.
On the other hand, it should be skipped if its notetype id is mapped
to some new notetype.

* Change field and template ids to i32

* Add merge notetypes flag to proto message

* Add dialog for apkg import

* Move HelpModal into components

* Generalize import dialog

* Move SettingTitle into components

* Add help modal to ImportAnkiPackagePage

* Move SwitchRow into components

* Fix backend method import

* Make testable in browser

* Fix broken modal

* Wrap in container and fix margins

* Update commented Anki version of new proto fields

* Check ids when comparing notetype schemas

* Add tooltip for merging notetypes.

* Allow updating notes regardless of mtime

* Gitignore yarn-error.log

* Allow updating notetypes regardless of mtime

* Fix apkg help carousel

* Use i64s for template and field ids

* Add option to omit importing scheduling info

* Restore last settings in apkg import dialog

* Display error when getting metadata in webview

* Update manual links for apkg importing

* Apply suggestions from code review

Co-authored-by: Damien Elmes <dae@users.noreply.github.com>

* Omit schduling -> Import all cards as new cards

* Tweak importing-update-notes-help

* UpdateCondition → ImportAnkiPackageUpdateCondition

* Load keyboard.ftl

* Skip updating dupes in 'update alwyas' case

* Explain more when merging notetypes is required

* "omit scheduling" → "with scheduling"

* Skip updating notetype dupes if 'update always'

* Merge duplicated notetypes from previous imports

* Fix rebase aftermath

* Fix panic when merging

* Clarify 'update notetypes' help

* Mention 'merge notetypes' in the log

* Add a test which covers the previously panicking path

* Use nested ftl messages to ensure consistency

* Make order of merged fields deterministic

* Rewrite test to trigger panic

* Update version comment on new fields
2023-09-09 09:00:55 +10:00
Damien Elmes
ffd392de21
Change Anki's version scheme; bump to 23.09 (#2640)
* Accept iterables as inputs to backend methods

* Shift add-on check to backend; use new endpoint

The new endpoint will return info on a suitable branch if found,
instead of returning all branches. This simplifies the frontend code,
and means that you can now drop support for certain versions without
it also remotely disabling the add-on for people who are running one of
the excluded versions, like in

https://forums.ankiweb.net/t/prevent-add-ons-from-being-disabled-remote-stealthily-surreptitiously/33427

* Bump version to 23.09

This changes Anki's version numbering system to year.month.patch, as
previously mentioned on https://forums.ankiweb.net/t/use-a-different-versioning-system-semver-perhaps/20046/5

This is shaping up to be a big release, with the introduction of FSRS and
image occlusion, and it seems like a good time to be finally updating the
version scheme as well. AnkiWeb has been updated to understand the new
format, and add-on authors will now specify version compatibility using
the full version number, as can be seen here:

https://ankiweb.net/shared/info/3918629684

* Shift update check to backend, and tidy up update.py

* Use the shared client for sync connections too
2023-09-07 12:37:15 +10:00
Damien Elmes
6d34d19808 Drop legacy schema option 2023-09-06 15:49:14 +10:00
Nil Admirari
9d3f01043b
Support AVIF (#2630) 2023-08-31 08:44:10 +10:00
Abdo
98715e593a
Improve presentation of importing results (#2568)
* Implement import log screen in Svelte

* Show filename in import log screen title

* Remove unused NoteRow property

* Show number of imported notes

* Use a single nid expression

* Use 'count' as variable name for consistency

* Import from @tslib/backend instead

* Fix summary_template typing

* Fix clippy warning

* Apply suggestions from code review

* Fix imports

* Contents -> Fields

* Increase max length of browser search bar

https://github.com/ankitects/anki/pull/2568/files#r1255227035

* Fix race condition in Bootstrap tooltip destruction

https://github.com/twbs/bootstrap/issues/37474

* summary_template -> summaryTemplate

* Make show link a button

* Run import ops on Svelte side

* Fix geometry not being restored in CSV Import page

* Make VirtualTable fill available height

* Keep CSV dialog modal

* Reword importing-existing-notes-skipped

* Avoid mentioning matching based on first field

* Change tick and cross icons

* List skipped notes last

* Pure CSS spinner

* Move set_wants_abort() call to relevant dialogs

* Show number of imported cards

* Remove bold from first sentence and indent summaries

* Update UI after import operations

* Add close button to import log page

Also make virtual table react to resize event.

* Fix typing

* Make CSV dialog non-modal again

Otherwise user can't interact with browser window.

* Update window modality after import

* Commit DB and update undo actions after import op

* Split frontend proto into separate file, so backend can ignore it

Currently the automatically-generated frontend RPC methods get placed in
'backend.js' with all the backend methods; we could optionally split them
into a separate 'frontend.js' file in the future.

* Migrate import_done from a bridgecmd to a HTTP request

* Update plural form of importing-notes-added

* Move import response handling to mediasrv

* Move task callback to script section

* Avoid unnecessary :global()

* .log cannot be missing if result exists

* Move import log search handling to mediasrv

* Type common params of ImportLogDialog

* Use else if

* Remove console.log()

* Add way to test apkg imports in new log screen

* Remove unused import

* Get actual card count for CSV imports

* Use import type

* Fix typing error

* Ignore import log when checking for changes in Python layer

* Apply suggestions from code review

* Remove imported card count for now

* Avoid non-null assertion in assignment

* Change showInBrowser to take an array of notes

* Use dataclasses for import log args

* Simplify ResultWithChanges in TS

* Only abort import when window is modal

* Fix ResultWithChanges typing

* Fix Rust warnings

* Only log one duplicate per incoming note

* Update wording about note updates

* Remove caveat about found_notes

* Reduce font size

* Remove redundant map

* Give credit to loading.io

* Remove unused line

---------

Co-authored-by: RumovZ <gp5glkw78@relay.firefox.com>
2023-08-02 20:29:44 +10:00
Mani
135de7f9ed
image occlusion button in note editor (#2485)
* setup mask editor in note editor
- add image on mask button click (only one time)
- show hide add button for io on notetype change
- hide field in io notetype
- icon for toggle
and replace image

* add update io notes

* Tidy up i/o notetype check and fix error

- Make it a method on editor
- Use .get(), because the setting doesn't exist on older notetypes
- Pass the bool value into the ts code, instead of the enum

* reset io page after adding

* remove adjust function & add target for mask editor

* handle browse mode & merged sidetoolbar and toptoolbar to toolbar

* fix: shape, button click in browse, dropdown menu

* add arrow to add button

* store for handling visiblity of maskeditor
- remove update  button in edit mode, implement autoupdate

* update var name

* simplify store
2023-07-27 22:45:49 +10:00
Damien Elmes
f504cc45f1 Add Filipino to language list 2023-07-17 14:07:11 +10:00
Damien Elmes
ffadbf577b deckconfig.proto -> deck_config.proto
Makes it consistent with our other proto files, and matches the service
name.
2023-07-03 13:44:54 +10:00
Damien Elmes
85c2769f80
Update Rust and Python deps (#2567)
* Update Python deps

* Update semver-compat Rust deps

* Update most crates to latest semver

* Update to latest axum-client-ip
2023-07-01 18:26:43 +10:00
BenYip
14bc02b431
Support more mime types for add_extension_based_on_mime (#2562)
* Support more mime types for add_extension_based_on_mime

* Add myself to CONTRIBUTORS
2023-06-29 15:22:04 +10:00
Damien Elmes
dc56a2ca7d Rework RenderCardOutput::question/answer
Instead of flattening the output (which was missing FrontSide), alter
the behaviour of render() instead. The non-partial output is now exposed
via Protobuf, so the non-Python clients can take advantage of it.
2023-06-27 00:37:41 +10:00
Damien Elmes
45f5709214
Migrate to protobuf-es (#2547)
* Fix .no-reduce-motion missing from graphs spinner, and not being honored

* Begin migration from protobuf.js -> protobuf-es

Motivation:

- Protobuf-es has a nicer API: messages are represented as classes, and
fields which should exist are not marked as nullable.
- As it uses modules, only the proto messages we actually use get included
in our bundle output. Protobuf.js put everything in a namespace, which
prevented tree-shaking, and made it awkward to access inner messages.
- ./run after touching a proto file drops from about 8s to 6s on my machine. The tradeoff
is slower decoding/encoding (#2043), but that was mainly a concern for the
graphs page, and was unblocked by
37151213cd

Approach/notes:

- We generate the new protobuf-es interface in addition to existing
protobuf.js interface, so we can migrate a module at a time, starting
with the graphs module.
- rslib:proto now generates RPC methods for TS in addition to the Python
interface. The input-arg-unrolling behaviour of the Python generation is
not required here, as we declare the input arg as a PlainMessage<T>, which
marks it as requiring all fields to be provided.
- i64 is represented as bigint in protobuf-es. We were using a patch to
protobuf.js to get it to output Javascript numbers instead of long.js
types, but now that our supported browser versions support bigint, it's
probably worth biting the bullet and migrating to bigint use. Our IDs
fit comfortably within MAX_SAFE_INTEGER, but that may not hold for future
fields we add.
- Oneofs are handled differently in protobuf-es, and are going to need
some refactoring.

Other notable changes:

- Added a --mkdir arg to our build runner, so we can create a dir easily
during the build on Windows.
- Simplified the preference handling code, by wrapping the preferences
in an outer store, instead of a separate store for each individual
preference. This means a change to one preference will trigger a redraw
of all components that depend on the preference store, but the redrawing
is cheap after moving the data processing to Rust, and it makes the code
easier to follow.
- Drop async(Reactive).ts in favour of more explicit handling with await
blocks/updating.
- Renamed add_inputs_to_group() -> add_dependency(), and fixed it not adding
dependencies to parent groups. Renamed add() -> add_action() for clarity.

* Remove a couple of unused proto imports

* Migrate card info

* Migrate congrats, image occlusion, and tag editor

+ Fix imports for multi-word proto files.

* Migrate change-notetype

* Migrate deck options

* Bump target to es2020; simplify ts lib list

Have used caniuse.com to confirm Chromium 77, iOS 14.5 and the Chrome
on Android support the full es2017-es2020 features.

* Migrate import-csv

* Migrate i18n and fix missing output types in .js

* Migrate custom scheduling, and remove protobuf.js

To mostly maintain our old API contract, we make use of protobuf-es's
ability to convert to JSON, which follows the same format as protobuf.js
did. It doesn't cover all case: users who were previously changing the
variant of a type will need to update their code, as assigning to a new
variant no longer automatically removes the old one, which will cause an
error when we try to convert back from JSON. But I suspect the large majority
of users are adjusting the current variant rather than creating a new one,
and this saves us having to write proxy wrappers, so it seems like a
reasonable compromise.

One other change I made at the same time was to rename value->kind for
the oneofs in our custom study protos, as 'value' was easily confused
with the 'case/value' output that protobuf-es has.

With protobuf.js codegen removed, touching a proto file and invoking
./run drops from about 8s to 6s.

This closes #2043.

* Allow tree-shaking on protobuf types

* Display backend error messages in our ts alert()

* Make sourcemap generation opt-in for ts-run

Considerably slows down build, and not used most of the time.
2023-06-14 22:47:37 +10:00
Damien Elmes
bac05039a7 Move protobuf generation into a separate crate; write .py interface in Rust
A couple of motivations for this:

- genbackend.py was somewhat messy, and difficult to change with the
lack of types. The mobile clients used it as a base for their generation,
so improving it will make life easier for them too, once they're ported.
- It will make it easier to write a .ts generator in the future
- We currently implement a bunch of helper methods on protobuf types
which don't allow us to compile the protobuf types until we compile
the Anki crate. If we change this in the future, we will be able to
do more of the compilation up-front.

We no longer need to record the services in the proto file, as we can
extract the service order from the compiled protos. Support for map types
has also been added.
2023-06-12 09:52:00 +10:00
Damien Elmes
894b7862e3
Improve import messaging when notetype has changed (#2483)
* Fix file extension not being appended on export

Regressed in #2427

* Improve import messaging when notetype has changed

- If the local notes are up to date, we don't need to warn about the
changed notetype, as no updates are required.
- Make it clearer that a changed notetype only affects updates.

Will update the docs as well.
2023-04-28 11:39:18 +10:00
Ren Tatsumoto
57d6e99262
Add a hook that allows to edit a note before it is added. (#2481)
* add note_will_be_added hook

* attempt to fix Tests.buildkite/linux/entrypoint

* attempt to fix Tests.buildkite/linux/entrypoint

* apply patch
2023-04-27 15:34:33 +10:00
Damien Elmes
f6486da233
Various tweaks to I/O code (#2478)
* Allow user to select I/O notetype instead of enforcing a specific name

* Display a clearer error when I/O note is missing an image

Opening the card layout screen from "manage notetypes" was showing an
error about the Anki version being too old.

Replacement error is not currently translatable.

* Preserve existing notetype when adding I/O notetype

* Add a 'from clipboard' string

The intention is to use this in the future to allow an image occlusion
to be created from an image on the clipboard.

* Tweak I/O init

- Use union type instead of multiple nullable values
- Pass the notetype id in to initialization

* Fix image insertion in I/O note

- The regex expected double quotes, and we were using single ones
- Image tags don't need to be closed

* Use more consistent naming in image_occlusion.proto

* Tweaks to default I/O notetype

- Show the header on the front side as well (I presume this is what
users expect; if not am happy to revert)
- Don't show comments on card (again, I presume users expect to use
this field to add notes that aren't displayed during review, as they
can use back extra for that)

* Fix sticky footer missing background

Caused by earlier CSS refactoring
2023-04-19 15:30:18 +10:00
Damien Elmes
dd13e78eca
Add ability to restore a notetype to its original configuration (#2472)
* Store the original stock notetype kind in the notetype

Will allow us to provide a command to restore a notetype to its default
settings/templates.

* Add a new action to restore a notetype to its original state
2023-04-18 14:07:51 +10:00
Damien Elmes
0a0d17ff98 Update Python deps
- Black's formatting has changed
- Pylint has introduced a new lint
2023-03-31 14:04:05 +10:00
Mani
2bf134dc72
Feature image occlusion (#2367)
* add note types with occlusions and image fields

* generate image occlusion cloze div data
- generate div element with data-* atrributes for canvas shape generate for reviewer

* getting image data & deck id and adding notes
the implementation added into backend
- added service index in backend.proto for image occlusion request
- created image_occlusion.proto with required message and service
- implementation in backend for getting image and adding notes, also during editing return imagecloze note and update notes
- add notes to selected deck, if no notetype then add image occlusion notetypes
- reuse notetype from stock notetypes when not exist

* script for generating shapes using canvas api in reviewer
- the flash issues fixed by loading image and using image size to draw canvas, also when image get resized, calculate scale using natural width and canvas width to draw shape at right position
- limit size of canvas for safari

* init image occlusion page in ts and build page
with
- fabricjs for editing shapes
- panzoom for drag and zoom
- pickr for color picker
- build page using web.rs

* implement top toolbar for canvas shapes
- undo & redo tools
- zoom in, zoom out and zoom fit
- group & ungroup
- copy & paste
- set transparency of shapes
- align tools

* implement side toolbar for drawing shapes
add top toolbar and the side toolbar contains following tools
- cursor for selecting shapes
- zoom for drag and zoom shapes in mask editor
- rectangle for creating it
- ellipse for creating it
- polygon for creating it using points
- shape fill color
- question mask color (currently only single color can be added for all shapes)

* add maskeditor page for editing mask
- add side toolbar and sidebar include toptoolbar
- load maskeditor in two mode
     - for adding note using path to image
     - for editing note using note id

* implement note editor page for adding notes
- the note editor page have simple button (B/I/U) and option to toggle html view
- option to select deck for adding notes into that deck
- option to generate to hide all, guess one & hide one, guess one notes

* add image occlusion page
add side toolbar, top toolbar, mask editor and note editor
- option to switch between mask editor and note editor

* implement generates notes and save notes
implemention to show toast components for messages

* removed pickr & implemented color picker component
- remove pickr
- implemented using html5 canvas
- range input for changing color
- another range input for opacity changes
- hex and rgba value support

* rename methods name & rust unwrap safety
- change plural names to singular
- create respone message in proto and return response with imagecloze note or error if not found with note id
- remove image_occlusion from post handler list
- rename service name in mediasrv.py
- rename methods name for image occlusion in backend and image_occlusion
- update frontend also for update functions' names
- handle error in frontend mask-editor.ts, when error getting notes then toast message shown to frontend

* extract to function & add comments & remove global
- extract function in mask-editor.ts to reduce duplicate
- remove unused global from css
- add comments to store.ts explaining usage
- changes id to noteId in lib.ts
- add comments for limitSize, becuase of duplicate implementation

* remove image_occlusion notetype
- remove from stock notetype, stdmodels
- add implementation for notetype to image occlusion
- add i18n for errors

* update smooth scroll, always show cursor tools
- change questionmask to qmask
- make selectable for shape true in all tools to simplify edits and draw shapes
- update image occlusion in reviewer ts to load image properly

* add and get notetype else return errors

* fix: not showing occlusion

* Use a oneof for ImageClozeNoteResponse

Makes it clearer that only one of them can be returned

* Don't crash if image filename not provided

The second unwrap should be ok, as the input is utf8

* Refactor get_image_cloze_note

- fixes crash when note doesn't exist - Ok(None) case was not covered
- decouples business logic from native error->proto error conversion
- no need for original copy
- field[x] is more idiomatic than field.get(x).unwrap()
- don't need mutable access to fields

* Fix crash if image file unreadable

+ Use our read_file helper for better error context

* Add metadata() helper

* Fix crash if file metadata can't be read

* remove color picker, qmask and shape color
- remove strings from ftl
- remove color picker component
- remove from cloze generation
- remove icons for two buttons
- use constant color for shapes

* update color in reviewer and ftl strings

* fix shape position in canvas & add border to shape
- rename mask to inactive shape and active shape color
- border witdth and border color
- change decimal point deserializing string and toFixed(2)
- add thin border in mask editor, may be image background was transparent

* fix shape position in canvas after modified
- do not draw fixed ratio shapes by turn of uniformScaling
- fix rectangle width,height
- fix ellipse rx,ry,width,height
- fix polygon postion and points
- draw outside of canvas also

* fix border width and color in reviewer canvas
- rename variable

* refactor cloze div generate and remove angle

* fix origin when drawn outside of canvas from right

* fix shape at boundry & not include rx,ry rectangle
- move shapes at boundry when pointer is outside of canvas
- include rx, ry for ellipse only
- include points for polygon only

* fix lint errors & update image size in editor canvas based on height and width

* remove unsupported layerX & layerX for touchscreen
- fix shapes at edges

* implemented undo redo with canvas state

- implemented undo redo using fabric canvas events
- polygon is special case and implemented only added and modified event
- rectangle and ellipse have object:added, object:modified and object:removed case
- change id to undo and redo

* remove background image from canvas and used css to put image tag below canvas editor

- set image width and height after adding image

* fix for polygon points, add br in cloze strings, & toogle masks button

- fix shapes at edges
- toggle masks button to show/hide masks
- hide clozes string, it contains <br>
- set height for div container (used 'relative' in css)

* refactor top toolbar, add space and border radius
- rename cursor tools
- add left and right border

* fix undo after undo happen, use transparent color in draw mode
2023-03-29 12:33:19 +10:00
RumovZ
bd88c6d352
Ensure state mutator runs after card is rendered (#2421)
* Ensure state mutator runs after card is rendered

* Ensure ease buttons only show when states are ready

* Pass context into states mutator

* Revert queuing of state mutator hook

Now that context data is exposed users shouldn't rely on the question
having been rendered anymore.

* Use callbacks instead of signals and timeout

... to track whether the states mutator ran or failed.

* Make mutator async

* Remove State enum

* Reduce requests and compute seed on backend
2023-03-16 16:31:00 +10:00
RumovZ
cdfb84f19a
Implement TTS using windows crate (#2371)
* Implement TTS using windows crate

* Use API calls instead of SSML

* Properly stop player in case of TTS error

* Add context to WindowsErrors

* Validate available voices

* Remove TTS text from synthesize error

* Limit maximum buffer size

* Make validation optional and list it in tts filter

* We no longer need the winrt module (dae)

* Use a separate request object so the meaning of the bool is clear (dae)

* Slightly shorten runtime error message (dae)

The default message appears to clip slightly.

* Alternate buffer implementation (dae)

* Use array instead of vec

* Drop the max buffer size to 128k (dae)
2023-02-17 12:26:07 +10:00
Damien Elmes
19bbcb4cc0 Use backend for extracting cloze text to type
Closes #2311
2023-01-18 23:05:28 +10:00
Damien Elmes
cf45cbf429
Rework syncing code, and replace local sync server (#2329)
This PR replaces the existing Python-driven sync server with a new one in Rust.
The new server supports both collection and media syncing, and is compatible
with both the new protocol mentioned below, and older clients. A setting has
been added to the preferences screen to point Anki to a local server, and a
similar setting is likely to come to AnkiMobile soon.

Documentation is available here: <https://docs.ankiweb.net/sync-server.html>

In addition to the new server and refactoring, this PR also makes changes to the
sync protocol. The existing sync protocol places payloads and metadata inside a
multipart POST body, which causes a few headaches:

- Legacy clients build the request in a non-deterministic order, meaning the
entire request needs to be scanned to extract the metadata.
- Reqwest's multipart API directly writes the multipart body, without exposing
the resulting stream to us, making it harder to track the progress of the
transfer. We've been relying on a patched version of reqwest for timeouts,
which is a pain to keep up to date.

To address these issues, the metadata is now sent in a HTTP header, with the
data payload sent directly in the body. Instead of the slower gzip, we now
use zstd. The old timeout handling code has been replaced with a new implementation
that wraps the request and response body streams to track progress, allowing us
to drop the git dependencies for reqwest, hyper-timeout and tokio-io-timeout.

The main other change to the protocol is that one-way syncs no longer need to
downgrade the collection to schema 11 prior to sending.
2023-01-18 12:43:46 +10:00
Damien Elmes
0eddb25287
Integrate AnkiDroid's backend patches into the repo (#2290)
* Relax chrono specification for AnkiDroid

https://github.com/ankidroid/Anki-Android-Backend/pull/251

* Add AnkiDroid service and AnkiDroid customizations

Most of the work here was done by David in the Backend repo; integrating
it into this repo for ease of future maintenance.

Based on 5d9f262f4c
with some tweaks:

- Protobuf imports have been fixed to match the recent refactor
- FatalError has been renamed to AnkidroidPanicError
- Tweaks to the desktop code to deal with the extra arg to open_collection,
and exclude AnkiDroid service methods from our Python code.

* Refactor AnkiDroid's DB code to avoid uses of unsafe
2023-01-03 13:11:23 +10:00
Damien Elmes
0570cfdf48 Migrate from slog to tracing
The Rust community appear to have converged on tracing - it's used by
the Rust compiler, and receives close to 10x the number of downloads
that slog does. Its API is more ergonomic, and it does a much nicer
job with async rust.

To make this change, we no longer pass around explicit loggers, and rely
on a globally-registered one. The log file location has been changed
from one in each profile folder to a single one in the base folder. This
will remain empty for most users, since only errors are logged by default,
but may be useful for debugging future changes.
2022-12-24 10:44:40 +10:00
Matthias Metelka
6481899454
Fix some more RTL issues (#2244)
* Swap flag and mark indicator position in RTL mode

* Make buttons of bottom toolbar align to edge of screen in RTL mode

* Use start instead of left and end instead of right
2022-12-08 22:29:56 +10:00
Damien Elmes
5e0a761b87
Move away from Bazel (#2202)
(for upgrading users, please see the notes at the bottom)

Bazel brought a lot of nice things to the table, such as rebuilds based on
content changes instead of modification times, caching of build products,
detection of incorrect build rules via a sandbox, and so on. Rewriting the build
in Bazel was also an opportunity to improve on the Makefile-based build we had
prior, which was pretty poor: most dependencies were external or not pinned, and
the build graph was poorly defined and mostly serialized. It was not uncommon
for fresh checkouts to fail due to floating dependencies, or for things to break
when trying to switch to an older commit.

For day-to-day development, I think Bazel served us reasonably well - we could
generally switch between branches while being confident that builds would be
correct and reasonably fast, and not require full rebuilds (except on Windows,
where the lack of a sandbox and the TS rules would cause build breakages when TS
files were renamed/removed).

Bazel achieves that reliability by defining rules for each programming language
that define how source files should be turned into outputs. For the rules to
work with Bazel's sandboxing approach, they often have to reimplement or
partially bypass the standard tools that each programming language provides. The
Rust rules call Rust's compiler directly for example, instead of using Cargo,
and the Python rules extract each PyPi package into a separate folder that gets
added to sys.path.

These separate language rules allow proper declaration of inputs and outputs,
and offer some advantages such as caching of build products and fine-grained
dependency installation. But they also bring some downsides:

- The rules don't always support use-cases/platforms that the standard language
tools do, meaning they need to be patched to be used. I've had to contribute a
number of patches to the Rust, Python and JS rules to unblock various issues.
- The dependencies we use with each language sometimes make assumptions that do
not hold in Bazel, meaning they either need to be pinned or patched, or the
language rules need to be adjusted to accommodate them.

I was hopeful that after the initial setup work, things would be relatively
smooth-sailing. Unfortunately, that has not proved to be the case. Things
frequently broke when dependencies or the language rules were updated, and I
began to get frustrated at the amount of Anki development time I was instead
spending on build system upkeep. It's now about 2 years since switching to
Bazel, and I think it's time to cut losses, and switch to something else that's
a better fit.

The new build system is based on a small build tool called Ninja, and some
custom Rust code in build/. This means that to build Anki, Bazel is no longer
required, but Ninja and Rust need to be installed on your system. Python and
Node toolchains are automatically downloaded like in Bazel.

This new build system should result in faster builds in some cases:

- Because we're using cargo to build now, Rust builds are able to take advantage
of pipelining and incremental debug builds, which we didn't have with Bazel.
It's also easier to override the default linker on Linux/macOS, which can
further improve speeds.
- External Rust crates are now built with opt=1, which improves performance
of debug builds.
- Esbuild is now used to transpile TypeScript, instead of invoking the TypeScript
compiler. This results in faster builds, by deferring typechecking to test/check
time, and by allowing more work to happen in parallel.

As an example of the differences, when testing with the mold linker on Linux,
adding a new message to tags.proto (which triggers a recompile of the bulk of
the Rust and TypeScript code) results in a compile that goes from about 22s on
Bazel to about 7s in the new system. With the standard linker, it's about 9s.

Some other changes of note:

- Our Rust workspace now uses cargo-hakari to ensure all packages agree on
available features, preventing unnecessary rebuilds.
- pylib/anki is now a PEP420 implicit namespace, avoiding the need to merge
source files and generated files into a single folder for running. By telling
VSCode about the extra search path, code completion now works with generated
files without needing to symlink them into the source folder.
- qt/aqt can't use PEP420 as it's difficult to get rid of aqt/__init__.py.
Instead, the generated files are now placed in a separate _aqt package that's
added to the path.
- ts/lib is now exposed as @tslib, so the source code and generated code can be
provided under the same namespace without a merging step.
- MyPy and PyLint are now invoked once for the entire codebase.
- dprint will be used to format TypeScript/json files in the future instead of
the slower prettier (currently turned off to avoid causing conflicts). It can
automatically defer to prettier when formatting Svelte files.
- svelte-check is now used for typechecking our Svelte code, which revealed a
few typing issues that went undetected with the old system.
- The Jest unit tests now work on Windows as well.

If you're upgrading from Bazel, updated usage instructions are in docs/development.md and docs/build.md. A summary of the changes:

- please remove node_modules and .bazel
- install rustup (https://rustup.rs/)
- install rsync if not already installed  (on windows, use pacman - see docs/windows.md)
- install Ninja (unzip from https://github.com/ninja-build/ninja/releases/tag/v1.11.1 and
  place on your path, or from your distro/homebrew if it's 1.10+)
- update .vscode/settings.json from .vscode.dist
2022-11-27 15:24:20 +10:00
Stefan Kangas
5551a37f03
Fix typos (#2210) 2022-11-24 20:18:57 +10:00
Damien Elmes
2504ad0b99 Fix mypy not picking up on missing attributes
Behaviour changed in recent releases:
https://github.com/python/mypy/issues/13319
2022-11-04 14:56:36 +10:00
RumovZ
c521753057
Refactor error handling (#2136)
* Add crate snafu

* Replace all inline structs in AnkiError

* Derive Snafu on AnkiError

* Use snafu for card type errors

* Use snafu whatever error for InvalidInput

* Use snafu for NotFoundError and improve message

* Use snafu for FileIoError to attach context

Remove IoError.
Add some context-attaching helpers to replace code returning bare
io::Errors.

* Add more context-attaching io helpers

* Add message, context and backtrace to new snafus

* Utilize error context and backtrace on frontend

* Rename LocalizedError -> BackendError.
* Remove DocumentedError.
* Have all backend exceptions inherit BackendError.

* Rename localized(_description) -> message

* Remove accidentally committed experimental trait

* invalid_input_context -> ok_or_invalid

* ensure_valid_input! -> require!

* Always return `Err` from `invalid_input!`

Instead of a Result to unwrap, the macro accepts a source error now.

* new_tempfile_in_parent -> new_tempfile_in_parent_of

* ok_or_not_found -> or_not_found

* ok_or_invalid -> or_invalid

* Add crate convert_case

* Use unqualified lowercase type name

* Remove uses of snafu::ensure

* Allow public construction of InvalidInputErrors (dae)

Needed to port the AnkiDroid changes.

* Make into_protobuf() public (dae)

Also required for AnkiDroid. Not sure why it worked previously - possible
bug in older Rust version?
2022-10-21 18:02:12 +10:00
Damien Elmes
9abc73360e Update Python deps
Addresses a protobuf CVE. Required some other patches due to changes
in latest mypy and pylint.
2022-09-24 09:46:43 +10:00
RumovZ
e39fb74e82
Enable state-dependent custom scheduling data (#2049)
* Enable state-dependent custom scheduling data

* Next(Card)States -> SchedulingStates

The fact that `current` was included in `next` always bothered me,
and custom data is part of the card state, so that was a bit confusing
too.

* Store custom_data in SchedulingState

* Make custom_data optional when answering

Avoids having to send it 4 extra times to the frontend, and avoids the
legacy answerCard() API clobbering the stored data.

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2022-09-05 16:48:01 +10:00
Damien Elmes
0bcb3a3564 Add non-legacy backend interface for adding new decks 2022-09-04 14:12:29 +10:00
RumovZ
31b7464c67
Add card meta for persisting custom scheduling state (#2040)
* Add card meta for persisting custom scheduling state

* Rename meta -> custom_data

* Enforce limits on size of custom data

Large values will slow down table scans of the cards table, and it's
easier to be strict now and possibly relax things in the future than
the opposite.

* Pack card states and customData into a single message

+ default customData to empty if it can't be parsed

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2022-09-02 11:22:49 +10:00
gnnoh
e4f1d9952b
Make dvipng use transparent background by default (#2036)
* make dvipng use transparent background by default

Note that if using all-lowercase 'transparent', it will give "a simple fully transparent background with non-transparent antialiased pixels", which is unnecessary.

* latex.py: format
2022-08-31 18:49:22 +10:00
Aristotelis
825c88b6e8
Make all Anki-native exceptions inherit from the same base class (#2028)
* Make all Anki-native exceptions inherit from same base class

Allows add-ons to easily catch all Anki-native exceptions without being coupled to the currently implemented exceptions.

* Satisfy pylint
2022-08-24 16:07:44 +10:00
phwoo
a338271d55
Narrow typehints within pylib (#2003)
* narrowed type hints

* narrowed type Any

* narrowed type Any

* narrow type Any
2022-08-05 14:48:07 +10:00
Damien Elmes
1e0be26b7e Partially migrate type answer to backend
Partially completes #1068, and will allow mobile clients to drop
their separate diff-match-patch imports. Does not yet try to handle
case folding or combining-char stripping, and leaves some of the outer
HTML wrapping up to the frontend for now.

The logic for rendering the provided string has changed: missing chars
are now only inserted if they follow a correct section, and the original
text is shown instead of hyphens. This is an experiment, and can be
changed if it's not well received.
2022-07-22 19:28:34 +10:00
RumovZ
cc929687ae
Deck-specific Limits (#1955)
* Add deck-specific limits to DeckNormal

* Add deck-specific limits to schema11

* Add DeckLimitsDialog

* deck_limits_qt6.py needs to be a symlink

* Clear duplicate deck setting keys on downgrade

* Export deck limits when exporting with scheduling

* Revert "deck_limits_qt6.py needs to be a symlink"

This reverts commit 4ee7be1e10.

* Revert "Add DeckLimitsDialog"

This reverts commit eb0e2a62d3.

* Add day limits to DeckNormal

* Add deck and day limits mock to deck options

* Revert "Add deck and day limits mock to deck options"

This reverts commit 0775814989.

* Add Tabs component for daily limits

* Add borders to tabs component

* Revert "Add borders to tabs component"

This reverts commit aaaf553893.

* Implement tabbed limits properly

* Add comment to translations

* Update rslib/src/decks/limits.rs

Co-authored-by: Damien Elmes <dae@users.noreply.github.com>

* Fix camel case in clear_other_duplicates()

* day_limit → current_limit

* Also import day limits

* Remember last used day limits

* Add day limits to schema 11

* Tweak comment (dae)

* Exclude day limit in export (dae)

* Tweak tab wording (dae)

* Update preset limits on preset change

* Explain tabs in tooltip (dae)

* Omit deck and today limits if v2 is enabled

* Preserve deck limit when switching to today limit
2022-07-19 18:27:25 +10:00
Sam Penny
9dcceff4af
Remember previous choices in reposition dialog (#1950)
* remember previous choices in reposition dialog

* remember previous choice for randomize option as well

* fix failing test
2022-07-08 11:28:38 +10:00
Damien Elmes
5ea78e1c8e Since DupeResolution is in CsvMetadata, we don't need to pass it separately
Follow-up to #1930
2022-06-27 17:15:54 +10:00
RumovZ
6da5e5b042
CSV import/export fixes and features (#1898)
* Fix footer moving upwards

* Fix column detection

Was broken because escaped line breaks were not considered.
Also removes delimiter detection on `#columns:` line. User must use tabs
or set delimiter beforehand.

* Add CSV preview

* Parse `#tags column:`

* Optionally export deck and notetype with CSV

* Avoid clones in CSV export

* Prevent bottom of page appearing under footer (dae)

* Increase padding to 1em (dae)

With 0.5em, when a vertical scrollbar is shown, it sits right next to
the right edge of the content, making it look like there's no right
margin.

* Experimental changes to make table fit+scroll (dae)

- limit individual cells to 15em, and show ellipses when truncated
- limit total table width to body width, so that inner table is shown
with scrollbar
- use class rather than id - ids are bad practice in Svelte components,
as more than one may be displayed on a single page

* Skip importing foreign notes with filtered decks

Were implicitly imported into the default deck before.
Also some refactoring to fetch deck ids and names beforehand.

* Hide spacer below hidden field mapping

* Fix guid being replaced when updating note

* Fix dupe identity check

Canonify tags before checking if dupe is identical, but only add update
tags later if appropriate.

* Fix deck export for notes with missing card 1

* Fix note lines starting with `#`

csv crate doesn't support escaping a leading comment char. :(

* Support import/export of guids

* Strip HTML from preview rows

* Fix initially set deck if current is filtered

* Make isHtml toggle reactive

* Fix `html_to_text_line()` stripping sound names

* Tweak export option labels

* Switch to patched rust-csv fork

Fixes writing lines starting with `#`, so revert 5ece10ad05.

* List column options with first column field

* Fix flag for exports with HTML stripped
2022-06-09 10:28:01 +10:00
Damien Elmes
53c768e61c Reload scheduler after full download
Fixes invalid state where schedVer=2 but v1 scheduler active
https://forums.ankiweb.net/t/not-able-to-fix-database-error/20483
2022-06-02 16:42:43 +10:00
RumovZ
42cbe42f06
Plaintext import/export (#1850)
* Add crate csv

* Add start of csv importing on backend

* Add Menomosyne serializer

* Add csv and json importing on backend

* Add plaintext importing on frontend

* Add csv metadata extraction on backend

* Add csv importing with GUI

* Fix missing dfa file in build

Added compile_data_attr, then re-ran cargo/update.py.

* Don't use doubly buffered reader in csv

* Escape HTML entities if CSV is not HTML

Also use name 'is_html' consistently.

* Use decimal number as foreign ease (like '2.5')

* ForeignCard.ivl → ForeignCard.interval

* Only allow fixed set of CSV delimiters

* Map timestamp of ForeignCard to native due time

* Don't trim CSV records

* Document use of empty strings for defaults

* Avoid creating CardGenContexts for every note

This requires CardGenContext to be generic, so it works both with an
owned and borrowed notetype.

* Show all accepted file types  in import file picker

* Add import_json_file()

* factor → ease_factor

* delimter_from_value → delimiter_from_value

* Map columns to fields, not the other way around

* Fallback to current config for csv metadata

* Add start of new import csv screen

* Temporary fix for compilation issue on Linux/Mac

* Disable jest bazel action for import-csv

Jest fails with an error code if no tests are available, but this would
not be noticable on Windows as Jest is not run there.

* Fix field mapping issue

* Revert "Temporary fix for compilation issue on Linux/Mac"

This reverts commit 21f8a26140.

* Add HtmlSwitch and move Switch to components

* Fix spacing and make selectors consistent

* Fix shortcut tooltip

* Place import button at the top with path

* Fix meta column indices

* Remove NotetypeForString

* Fix queue and type of foreign cards

* Support different dupe resolution strategies

* Allow dupe resolution selection when importing CSV

* Test import of unnormalized text

Close  #1863.

* Fix logging of foreign notes

* Implement CSV exports

* Use db_scalar() in notes_table_len()

* Rework CSV metadata

- Notetypes and decks are either defined by a global id or by a column.
- If a notetype id is provided, its field map must also be specified.
- If a notetype column is provided, fields are now mapped by index
instead of name at import time. So the first non-meta column is used for
the first field of every note, regardless of notetype. This makes
importing easier and should improve compatiblity with files without a
notetype column.
- Ensure first field can be mapped to a column.
- Meta columns must be defined as `#[meta name]:[column index]` instead
of in the `#columns` tag.
- Column labels contain the raw names defined by the file and must be
prettified by the frontend.

* Adjust frontend to new backend column mapping

* Add force flags for is_html and delimiter

* Detect if CSV is HTML by field content

* Update dupe resolution labels

* Simplify selectors

* Fix coalescence of oneofs in TS

* Disable meta columns from selection

Plus a lot of refactoring.

* Make import button stick to the bottom

* Write delimiter and html flag into csv

* Refetch field map after notetype change

* Fix log labels for csv import

* Log notes whose deck/notetype was missing

* Fix hiding of empty log queues

* Implement adding tags to all notes of a csv

* Fix dupe resolution not being set in log

* Implement adding tags to updated notes of a csv

* Check first note field is not empty

* Temporary fix for build on Linux/Mac

* Fix inverted html check (dae)

* Remove unused ftl string

* Delimiter → Separator

* Remove commented-out line

* Don't accept .json files

* Tweak tag ftl strings

* Remove redundant blur call

* Strip sound and add spaces in csv export

* Export HTML by default

* Fix unset deck in Mnemosyne import

Also accept both numbers and strings for notetypes and decks in JSON.

* Make DupeResolution::Update the default

* Fix missing dot in extension

* Make column indices 1-based

* Remove StickContainer from TagEditor

Fixes line breaking, border and z index on ImportCsvPage.

* Assign different key combos to tag editors

* Log all updated duplicates

Add a log field for the true number of found notes.

* Show identical notes as skipped

* Split tag-editor into separate ts module (dae)

* Add progress for CSV export

* Add progress for text import

* Tidy-ups after tag-editor split (dae)

- import-csv no longer depends on editor
- remove some commented lines
2022-06-01 20:26:16 +10:00
Abdo
45b12ca104
Store rendered side in TemplateRenderContext (#1852)
* Store rendered side in TemplateRenderContext

* Move _question_side up with the other simple properties (dae)
2022-05-10 11:44:44 +10:00
Damien Elmes
c8a4e5ea22 Add more progress + abort points to export code
The old `media_files_did_export` hook has been kept around for use with
the legacy apkg exporter (an add-on uses it), and a new 
`legacy_export_progress` hook has been added so we can get progress
from the new colpkg exporter until we move over fully to the new code.
2022-05-06 15:32:23 +10:00
RumovZ
5f9451f547
Add apkg import/export on backend (#1743)
* Add apkg export on backend

* Filter out missing media-paths at write time

* Make TagMatcher::new() infallible

* Gather export data instead of copying directly

* Revert changes to rslib/src/tags/

* Reuse filename_is_safe/check_filename_safe()

* Accept func to produce MediaIter in export_apkg()

* Only store file folder once in MediaIter

* Use temporary tables for gathering

export_apkg() now accepts a search instead of a deck id. Decks are
gathered according to the matched notes' cards.

* Use schedule_as_new() to reset cards

* ExportData → ExchangeData

* Ignore ascii case when filtering system tags

* search_notes_cards_into_table →

search_cards_of_notes_into_table

* Start on apkg importing on backend

* Fix due dates in days for apkg export

* Refactor import-export/package

- Move media and meta code into appropriate modules.
- Normalize/check for normalization when deserializing media entries.

* Add SafeMediaEntry for deserialized MediaEntries

* Prepare media based on checksums

- Ensure all existing media files are hashed.
- Hash incoming files during preparation to detect conflicts.
- Uniquify names of conflicting files with hash (not notetype id).
- Mark media files as used while importing notes.
- Finally copy used media.

* Handle encoding in `replace_media_refs()`

* Add trait to keep down cow boilerplate

* Add notetypes immediately instaed of preparing

* Move target_col into Context

* Add notes immediately instaed of preparing

* Note id, not guid of conflicting notes

* Add import_decks()

* decks_configs → deck_configs

* Add import_deck_configs()

* Add import_cards(), import_revlog()

* Use dyn instead of generic for media_fn

Otherwise, would have to pass None with type annotation in the default
case.

* Fix signature of import_apkg()

* Fix search_cards_of_notes_into_table()

* Test new functions in text.rs

* Add roundtrip test for apkg (stub)

* Keep source id of imported cards (or skip)

* Keep source ids of imported revlog (or skip)

* Try to keep source ids of imported notes

* Make adding notetype with id undoable

* Wrap apkg import in transaction

* Keep source ids of imported deck configs (or skip)

* Handle card due dates and original due/did

* Fix importing cards/revlog

Card ids are manually uniquified.

* Factor out card importing

* Refactor card and revlog importing

* Factor out card importing

Also handle missing parents .

* Factor out note importing

* Factor out media importing

* Maybe upgrade scheduler of apkg

* Fix parent deck gathering

* Unconditionally import static media

* Fix deck importing edge cases

Test those edge cases, and add some global test helpers.

* Test note importing

* Let import_apkg() take a progress func

* Expand roundtrip apkg test

* Use fat pointer to avoid propogating generics

* Fix progress_fn type

* Expose apkg export/import on backend

* Return note log when importing apkg

* Fix archived collection name on apkg import

* Add CollectionOpWithBackendProgress

* Fix wrong Interrupted Exception being checked

* Add ClosedCollectionOp

* Add note ids to log and strip HTML

* Update progress when checking incoming media too

* Conditionally enable new importing in GUI

* Fix all_checksums() for media import

Entries of deleted files are nulled, not removed.

* Make apkg exporting on backend abortable

* Return number of notes imported from apkg

* Fix exception printing for QueryOp as well

* Add QueryOpWithBackendProgress

Also support backend exporting progress.

* Expose new apkg and colpkg exporting

* Open transaction in insert_data()

Was slowing down exporting by several orders of magnitude.

* Handle zstd-compressed apkg

* Add legacy arg to ExportAnkiPackage

Currently not exposed on the frontend

* Remove unused import in proto file

* Add symlink for typechecking of import_export_pb2

* Avoid kwargs in pb message creation, so typechecking is not lost

Protobuf's behaviour is rather subtle and I had to dig through the docs
to figure it out: set a field on a submessage to automatically assign 
the submessage to the parent, or call SetInParent() to persist a default
version of the field you specified.

* Avoid re-exporting protobuf msgs we only use internally

* Stop after one test failure

mypy often fails much faster than pylint

* Avoid an extra allocation when extracting media checksums

* Update progress after prepare_media() finishes

Otherwise the bulk of the import ends up being shown as "Checked: 0"
in the progress window.

* Show progress of note imports

Note import is the slowest part, so showing progress here makes the UI
feel more responsive.

* Reset filtered decks at import time

Before this change, filtered decks exported with scheduling remained
filtered on import, and maybe_remove_from_filtered_deck() moved cards
into them as their home deck, leading to errors during review.

We may still want to provide a way to preserve filtered decks on import,
but to do that we'll need to ensure we don't rewrite the home decks of
cards, and we'll need to ensure the home decks are included as part of
the import (or give an error if they're not).

https://github.com/ankitects/anki/pull/1743/files#r839346423

* Fix a corner-case where due dates were shifted by a day

This issue existed in the old Python code as well. We need to include
the user's UTC offset in the exported file, or days_elapsed falls back
on the v1 cutoff calculation, which may be a day earlier or later than
the v2 calculation.

* Log conflicting note in remapped nt case

* take_fields() → into_fields()

* Alias `[u8; 20]` with `Sha1Hash`

* Truncate logged fields

* Rework apkg note import tests

- Use macros for more helpful errors.
- Split monolith into unit tests.
- Fix some unknown error with the previous test along the way.
(Was failing after 969484de4388d225c9f17d94534b3ba0094c3568.)

* Fix sorting of imported decks

Also adjust the test, so it fails without the patch. It was only passing
before, because the parent deck happened to come before the
inconsistently capitalised child alphabetically. But we want all parent
decks to be imported before their child decks, so their children can
adopt their capitalisation.

* target[_id]s → existing_card[_id]s

* export_collection_extracting_media() → ...

export_into_collection_file()

* target_already_exists→card_ordinal_already_exists

* Add search_cards_of_notes_into_table.sql

* Imrove type of apkg export selector/limit

* Remove redundant call to mod_schema()

* Parent tooltips to mw

* Fix a crash when truncating note text

String::truncate() is a bit of a footgun, and I've hit this before
too :-)

* Remove ExportLimit in favour of separate classes

* Remove OpWithBackendProgress and ClosedCollectionOp

Backend progress logic is now in ProgressManager. QueryOp can be used
for running on closed collection.

Also fix aborting of colpkg exports, which slipped through in #1817.

* Tidy up import log

* Avoid QDialog.exec()

* Default to excluding scheuling for deck list deck

* Use IncrementalProgress in whole import_export code

* Compare checksums when importing colpkgs

* Avoid registering changes if hashes are not needed

* ImportProgress::Collection → ImportProgress::File

* Make downgrading apkgs depend on meta version

* Generalise IncrementableProgress

And use it in entire import_export code instead.

* Fix type complexity lint

* Take count_map for IncrementableProgress::get_inner

* Replace import/export env with Shift click

* Accept all args from update() for backend progress

* Pass fields of ProgressUpdate explicitly

* Move update_interval into IncrementableProgress

* Outsource incrementing into Incrementor

* Mutate ProgressUpdate in progress_update callback

* Switch import/export legacy toggle to profile setting

Shift would have been nice, but the existing shortcuts complicate things.
If the user triggers an import with ctrl+shift+i, shift is unlikely to
have been released by the time our code runs, meaning the user accidentally
triggers the new code. We could potentially wait a while before bringing
up the dialog, but then we're forced to guess at how long it will take the
user to release the key.

One alternative would be to use alt instead of shift, but then we need to
trigger our shortcut when that key is pressed as well, and it could
potentially cause a conflict with an add-on that already uses that
combination.

* Show extension in export dialog

* Continue to provide separate options for schema 11+18 colpkg export

* Default to colpkg export when using File>Export

* Improve appearance of combo boxes when switching between apkg/colpkg

+ Deal with long deck names

* Convert newlines to spaces when showing fields from import

Ensures each imported note appears on a separate line

* Don't separate total note count from the other summary lines

This may come down to personal preference, but I feel the other counts
are equally as important, and separating them feels like it makes it
a bit easier to ignore them.

* Fix 'deck not normal' error when importing a filtered deck for the 2nd time

* Fix [Identical] being shown on first import

* Revert "Continue to provide separate options for schema 11+18 colpkg export"

This reverts commit 8f0b2c175f.

Will use a different approach

* Move legacy support into a separate exporter option; add to apkg export

* Adjust 'too new' message to also apply to .apkg import case

* Show a better message when attempting to import new apkg into old code

Previously the user could end seeing a message like:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb5 in position 1: invalid start byte

Unfortunately we can't retroactively fix this for older clients.

* Hide legacy support option in older exporting screen

* Reflect change from paths to fnames in type & name

* Make imported decks normal at once

Then skip special casing in update_deck(). Also skip updating
description if new one is empty.

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2022-05-02 21:12:46 +10:00
Damien Elmes
e6d990a44e Fix cwd changing on export
No longer required after #1656
2022-04-21 16:50:05 +10:00
Damien Elmes
a9769813ba Add back support for custom mountpoint in card stats
The move to separate .html files broke our legacy card stats routine.

Related: d1d71ffdbb
2022-04-15 15:30:05 +10:00
Damien Elmes
d90608198f Don't rely on frontend to cap time taken in v3 2022-04-02 20:06:23 +10:00
RumovZ
f1488b5983
Card type error (#1749)
* TemplateSaveError -> CardTypeError

* Don't show success tooltip if export fails

* Attach help page to error

Show help link if export fails due to card type error.

* Add type (dae)

* Add shared show_exception() (dae)

- Use a shared routine for printing standard backend errors, so that
we can take advantage of the help links in eg. the card layout screen
as well.
- The truthiness check on help in showInfo() would have ignored the
enum 0 value.
- Close the exporting dialog on a documented failure as well

* Fix local variable help_page
2022-03-28 22:17:50 +10:00
RumovZ
dd16890c11
Add Deleted error and disable all bad browser rows (#1742)
* Add Deleted error and disable all bad browser rows

* Avoid error when opening the browse screen to a card with a missing note (dae)

* In cards mode, a missing note is NotFound, not Deleted (dae)

So we distinguish between referential integrity error, and explicit
deletion.

* Remove redundant try block
2022-03-28 19:06:19 +10:00
Damien Elmes
4515c41d2c
Backup improvements (#1728)
* Collection needs to be closed prior to backup even when not downgrading

* Backups -> BackupLimits

* Some improvements to backup_task

- backup_inner now returns the error instead of logging it, so that
the frontend can discover the issue when they await a backup (or create
another one)
- start_backup() was acquiring backup_task twice, and if another thread
started a backup between the two locks, the task could have been accidentally
overwritten without awaiting it

* Backups no longer require a collection close

- Instead of closing the collection, we ensure there is no active
transaction, and flush the WAL to disk. This means the undo history
is no longer lost on backup, which will be particularly useful if we
add a periodic backup in the future.
- Because a close is no longer required, backups are now achieved with
a separate command, instead of being included in CloseCollection().
- Full sync no longer requires an extra close+reopen step, and we now
wait for the backup to complete before proceeding.
- Create a backup before 'check db'

* Add File>Create Backup

https://forums.ankiweb.net/t/anki-mac-os-no-backup-on-sync/6157

* Defer checkpoint until we know we need it

When running periodic backups on a timer, we don't want to be fsync()ing
unnecessarily.

* Skip backup if modification time has not changed

We don't want the user leaving Anki open overnight, and coming back
to lots of identical backups.

* Periodic backups

Creates an automatic backup every 30 minutes if the collection has been
modified.

If there's a legacy checkpoint active, tries again 5 minutes later.

* Switch to a user-configurable backup duration

CreateBackup() now uses a simple force argument to determine whether
the user's limits should be respected or not, and only potentially
destructive ops (full download, check DB) override the user's configured
limit.

I considered having a separate limit for collection close and automatic
backups (eg keeping the previous 5 minute limit for collection close),
but that had two downsides:

- When the user closes their collection at the end of the day, they'd
get a recent backup. When they open the collection the next day, it
would get backed up again within 5 minutes, even though not much had
changed.
- Multiple limits are harder to communicate to users in the UI

Some remaining decisions I wasn't 100% sure about:

- If force is true but the collection has not been modified, the backup
will be skipped. If the user manually deleted their backups without
closing Anki, they wouldn't get a new one if the mtime hadn't changed.
- Force takes preference over the configured backup interval - should
we be ignored the user here, or take no backups at all?

Did a sneaky edit of the existing ftl string, as it hasn't been live
long.

* Move maybe_backup() into Collection

* Use a single method for manual and periodic backups

When manually creating a backup via the File menu, we no longer make
the user wait until the backup completes. As we continue waiting for
the backup in the background, if any errors occur, the user will get
notified about it fairly quickly.

* Show message to user if backup was skipped due to no changes

+ Don't incorrectly assert a backup will be created on force

* Add "automatic" to description

* Ensure we backup prior to importing colpkg if collection open

The backup doesn't happen when invoked from 'open backup' in the profile
screen, which matches Anki's previous behaviour. The user could
potentially clobber up to 30 minutes of their work if they exited to
the profile screen and restored a backup, but the alternative is we
create backups every time a backup is restored, which may happen a number
of times if the user is trying various ones. Or we could go back to a
separate throttle amount for this case, at the cost of more complexity.

* Remove the 0 special case on backup interval; minimum of 5 minutes

https://github.com/ankitects/anki/pull/1728#discussion_r830876833
2022-03-21 19:40:42 +10:00
RumovZ
16fe18d033
Refactor export-import code and resolve fixmes (#1723)
* Write media files in chunks

* Test media file writing

* Add iter `ReadDirFiles`

* Remove ImportMediaError, fail fatally instead

Partially reverts commit f8ed4d89ba.

* Compare hashes of media files to be restored

* Improve `MediaCopier::copy()`

* Restore media files atomically with tempfile

* Make downgrade flag an enum

* Remove SchemaVersion::Latest in favour of Option

* Remove sha1 comparison again

* Remove unnecessary repr(u8) (dae)
2022-03-18 19:31:55 +10:00
Damien Elmes
f8ed4d89ba Add new error variant to frontend; ensure errors are mapped appropriately 2022-03-17 20:58:35 +10:00
Damien Elmes
c2e8d89fc6
Colpkg fixes (#1722)
* Fix legacy colpkg import; disable v3 import/export; add roundtrip test

The test has revealed we weren't decompressing the media files on v3
import. That's easy to fix, but means all files need decompressing
even when they already exist, which is not ideal - it would be better
to store size/checksum in the metadata instead.

* Switch media and meta to protobuf; re-enable v3 import/export

- Fixed media not being decompressed on import
- The uncompressed size and checksum is now included for each media
entry, so that we can quickly check if a given file needs to be extracted.
We're still just doing a naive size comparison on colpkg import at the
moment, but we may want to use a checksum in the future, and will need
a checksum for apkg imports.
- Checksums can't be efficiently encoded in JSON, so the media list
has been switched to protobuf to reduce the the space requirements.
- The meta file has been switched to protobuf as well, for consistency.
This will mean any colpkg files exported with beta7 will be
unreadable.

* Avoid integer version comparisons

* Re-enable v3 test

* Apply suggestions from code review

Co-authored-by: RumovZ <gp5glkw78@relay.firefox.com>

* Add export_colpkg() method to Collection

More discoverable, and easier to call from unit tests

* Split import/export code out into separate folders

Currently colpkg/*.rs contain some routines that will be useful for
apkg import/export as well; in the future we can refactor them into a
separate file in the parent module.

* Return a proper error when media import fails

This tripped me up when writing the earlier unit test - I had called
the equivalent of import_colpkg()?, and it was returning a string error
that I didn't notice. In practice this should result in the same text
being shown in the UI, but just skips the tooltip.

* Automatically create media folder on import

* Move roundtrip test into separate file; check collection too

* Remove zstd version suffix

Prevents a warning shown each time Rust Analyzer is used to check the
code.

Co-authored-by: RumovZ <gp5glkw78@relay.firefox.com>
2022-03-17 15:11:23 +10:00
RumovZ
e759885734
Backend colpkg exporting (#1719)
* Implement colpkg exporting on backend

* Use exporting logic in backup.rs

* Refactor exporting.rs

* Add backend function to export collection

* Refactor backend/collection.rs

* Use backend for colpkg exporting

* Don't use default zip compression for media

* Add exporting progress

* Refactor media file writing

* Write dummy collections

* Localize dummy collection note

* Minimize dummy db size

* Use `NamedTempFile::new()` instead of `new_in`

* Drop redundant v2 dummy collection

* COLLECTION_VERSION -> PACKAGE_VERSION

* Split `lock_collection()` into two to drop flag

* Expose new colpkg in GUI

* Improve dummy collection message

* Please type checker

* importing-colpkg-too-new -> exporting-...

* Compress the media map in the v3 package (dae)

On collections with lots of media, it can grow into megabytes.

Also return an error in extract_media_file_names(), instead of masking
it as an optional.

* Store media map as a vector in the v3 package (dae)

This compresses better (eg 280kb original, 100kb hashmap, 42kb vec)

In the colpkg import case we don't need random access. When importing
an apkg, we will need to be able to fetch file data for a given media
filename, but the existing map doesn't help us there, as we need
filename->index, not index->filename.

* Ensure folders in the media dir don't break the file mapping (dae)
2022-03-15 16:48:02 +10:00