Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add apkg import/export on backend #1743

Merged
merged 140 commits into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
140 commits
Select commit Hold shift + click to select a range
5669731
Add apkg export on backend
RumovZ Mar 27, 2022
6269a88
Filter out missing media-paths at write time
RumovZ Mar 28, 2022
7d1686e
Make TagMatcher::new() infallible
RumovZ Mar 29, 2022
84c5ac8
Gather export data instead of copying directly
RumovZ Mar 29, 2022
aab518d
Merge remote-tracking branch 'upstream/main' into apkg
RumovZ Mar 29, 2022
3c17d37
Revert changes to rslib/src/tags/
RumovZ Mar 30, 2022
112ad11
Reuse filename_is_safe/check_filename_safe()
RumovZ Mar 30, 2022
a46026f
Accept func to produce MediaIter in export_apkg()
RumovZ Mar 30, 2022
d1dd058
Only store file folder once in MediaIter
RumovZ Mar 30, 2022
97c9dd4
Use temporary tables for gathering
RumovZ Mar 31, 2022
bd14ccf
Merge remote-tracking branch 'upstream/HEAD' into apkg
RumovZ Mar 31, 2022
71670a0
Use schedule_as_new() to reset cards
RumovZ Mar 31, 2022
fbc21da
ExportData → ExchangeData
RumovZ Mar 31, 2022
4aa5ee5
Ignore ascii case when filtering system tags
RumovZ Mar 31, 2022
fce797c
search_notes_cards_into_table →
RumovZ Mar 31, 2022
6836da0
Start on apkg importing on backend
RumovZ Apr 5, 2022
80dc3ae
Fix due dates in days for apkg export
RumovZ Apr 6, 2022
efde7c7
Refactor import-export/package
RumovZ Apr 6, 2022
a0085e7
Add SafeMediaEntry for deserialized MediaEntries
RumovZ Apr 6, 2022
7583a7e
Prepare media based on checksums
RumovZ Apr 7, 2022
cd4865d
Handle encoding in `replace_media_refs()`
RumovZ Apr 7, 2022
5bfd47e
Add trait to keep down cow boilerplate
RumovZ Apr 7, 2022
9527322
Add notetypes immediately instaed of preparing
RumovZ Apr 7, 2022
b9c1927
Move target_col into Context
RumovZ Apr 7, 2022
94a6cdd
Add notes immediately instaed of preparing
RumovZ Apr 7, 2022
48bc662
Note id, not guid of conflicting notes
RumovZ Apr 7, 2022
5cf1705
Add import_decks()
RumovZ Apr 8, 2022
7520afd
decks_configs → deck_configs
RumovZ Apr 8, 2022
ffdd8b7
Add import_deck_configs()
RumovZ Apr 8, 2022
08791e2
Add import_cards(), import_revlog()
RumovZ Apr 8, 2022
468c028
Use dyn instead of generic for media_fn
RumovZ Apr 8, 2022
d613474
Fix signature of import_apkg()
RumovZ Apr 8, 2022
44d727a
Fix search_cards_of_notes_into_table()
RumovZ Apr 8, 2022
fef4a6d
Test new functions in text.rs
RumovZ Apr 8, 2022
726438d
Add roundtrip test for apkg (stub)
RumovZ Apr 8, 2022
a0604a2
Keep source id of imported cards (or skip)
RumovZ Apr 9, 2022
5a76d22
Keep source ids of imported revlog (or skip)
RumovZ Apr 9, 2022
2a15ba5
Try to keep source ids of imported notes
RumovZ Apr 9, 2022
1ba5444
Make adding notetype with id undoable
RumovZ Apr 9, 2022
7798b78
Wrap apkg import in transaction
RumovZ Apr 9, 2022
a528106
Keep source ids of imported deck configs (or skip)
RumovZ Apr 9, 2022
126a92c
Handle card due dates and original due/did
RumovZ Apr 9, 2022
e189915
Fix importing cards/revlog
RumovZ Apr 10, 2022
8348240
Factor out card importing
RumovZ Apr 10, 2022
a402879
Refactor card and revlog importing
RumovZ Apr 10, 2022
a727883
Factor out card importing
RumovZ Apr 10, 2022
fe878dc
Factor out note importing
RumovZ Apr 11, 2022
251bfc4
Factor out media importing
RumovZ Apr 11, 2022
71e6292
Maybe upgrade scheduler of apkg
RumovZ Apr 11, 2022
4c79a1d
Merge branch 'main' into apkg
RumovZ Apr 13, 2022
f0cc027
Merge remote-tracking branch 'upstream/HEAD' into apkg
RumovZ Apr 15, 2022
7af0b08
Fix parent deck gathering
RumovZ Apr 15, 2022
42f55fc
Unconditionally import static media
RumovZ Apr 15, 2022
1e22432
Fix deck importing edge cases
RumovZ Apr 15, 2022
a78d101
Test note importing
RumovZ Apr 16, 2022
bb27eb3
Let import_apkg() take a progress func
RumovZ Apr 16, 2022
ed24fd1
Expand roundtrip apkg test
RumovZ Apr 16, 2022
e65b79b
Use fat pointer to avoid propogating generics
RumovZ Apr 16, 2022
16f8c98
Fix progress_fn type
RumovZ Apr 17, 2022
6b85b59
Expose apkg export/import on backend
RumovZ Apr 20, 2022
63ad662
Return note log when importing apkg
RumovZ Apr 20, 2022
bf5254e
Merge remote-tracking branch 'upstream/HEAD' into apkg
RumovZ Apr 20, 2022
592e7e2
Fix archived collection name on apkg import
RumovZ Apr 21, 2022
4837e52
Merge branch 'main' into apkg
RumovZ Apr 22, 2022
2acad45
Add CollectionOpWithBackendProgress
RumovZ Apr 22, 2022
2491b96
Fix wrong Interrupted Exception being checked
RumovZ Apr 22, 2022
50c8267
Add ClosedCollectionOp
RumovZ Apr 22, 2022
6eb8aa0
Add note ids to log and strip HTML
RumovZ Apr 22, 2022
8c1a4d0
Update progress when checking incoming media too
RumovZ Apr 22, 2022
c316996
Conditionally enable new importing in GUI
RumovZ Apr 22, 2022
e6dbe5b
Fix all_checksums() for media import
RumovZ Apr 25, 2022
e4d36e7
Make apkg exporting on backend abortable
RumovZ Apr 25, 2022
266362b
Return number of notes imported from apkg
RumovZ Apr 25, 2022
4137b1e
Fix exception printing for QueryOp as well
RumovZ Apr 25, 2022
c237d4c
Add QueryOpWithBackendProgress
RumovZ Apr 25, 2022
98d87fd
Expose new apkg and colpkg exporting
RumovZ Apr 25, 2022
7663968
Open transaction in insert_data()
RumovZ Apr 25, 2022
ede9698
Handle zstd-compressed apkg
dae Apr 26, 2022
055b404
Add legacy arg to ExportAnkiPackage
dae Apr 26, 2022
8204cee
Remove unused import in proto file
dae Apr 26, 2022
070d304
Add symlink for typechecking of import_export_pb2
dae Apr 26, 2022
0e9a8e1
Avoid kwargs in pb message creation, so typechecking is not lost
dae Apr 26, 2022
284805f
Avoid re-exporting protobuf msgs we only use internally
dae Apr 26, 2022
c7c1cfa
Stop after one test failure
dae Apr 26, 2022
c4ebbd7
Avoid an extra allocation when extracting media checksums
dae Apr 27, 2022
d645824
Update progress after prepare_media() finishes
dae Apr 27, 2022
23eda19
Show progress of note imports
dae Apr 27, 2022
308c663
Reset filtered decks at import time
dae Apr 27, 2022
a4dfad5
Fix a corner-case where due dates were shifted by a day
dae Apr 27, 2022
969484d
Log conflicting note in remapped nt case
RumovZ Apr 27, 2022
f2988f8
take_fields() → into_fields()
RumovZ Apr 27, 2022
382d741
Alias `[u8; 20]` with `Sha1Hash`
RumovZ Apr 27, 2022
b0d9745
Truncate logged fields
RumovZ Apr 27, 2022
4938a30
Rework apkg note import tests
RumovZ Apr 27, 2022
07ff01f
Fix sorting of imported decks
RumovZ Apr 27, 2022
60c6869
target[_id]s → existing_card[_id]s
RumovZ Apr 27, 2022
274afc7
export_collection_extracting_media() → ...
RumovZ Apr 27, 2022
f03df4a
target_already_exists→card_ordinal_already_exists
RumovZ Apr 27, 2022
20322fb
Add search_cards_of_notes_into_table.sql
RumovZ Apr 27, 2022
7c184cf
Imrove type of apkg export selector/limit
RumovZ Apr 27, 2022
c01b500
Remove redundant call to mod_schema()
RumovZ Apr 27, 2022
2423121
Parent tooltips to mw
RumovZ Apr 27, 2022
cf9d490
Fix a crash when truncating note text
dae Apr 28, 2022
702f47c
Remove ExportLimit in favour of separate classes
RumovZ Apr 28, 2022
19664a0
Remove OpWithBackendProgress and ClosedCollectionOp
RumovZ Apr 28, 2022
5e1b5d3
Tidy up import log
RumovZ Apr 28, 2022
50fb2a1
Avoid QDialog.exec()
RumovZ Apr 28, 2022
87fbefb
Default to excluding scheuling for deck list deck
RumovZ Apr 28, 2022
930f421
Use IncrementalProgress in whole import_export code
RumovZ Apr 28, 2022
146fd2a
Compare checksums when importing colpkgs
RumovZ Apr 28, 2022
76f4bf2
Avoid registering changes if hashes are not needed
RumovZ Apr 29, 2022
c65d181
ImportProgress::Collection → ImportProgress::File
RumovZ Apr 29, 2022
06cc44a
Make downgrading apkgs depend on meta version
RumovZ Apr 29, 2022
aba7b08
Generalise IncrementableProgress
RumovZ Apr 29, 2022
400fc3b
Fix type complexity lint
RumovZ Apr 29, 2022
e622521
Take count_map for IncrementableProgress::get_inner
RumovZ Apr 29, 2022
fda691f
Replace import/export env with Shift click
RumovZ Apr 29, 2022
1ffcc53
Accept all args from update() for backend progress
RumovZ Apr 29, 2022
b002c45
Pass fields of ProgressUpdate explicitly
RumovZ Apr 30, 2022
58235d3
Move update_interval into IncrementableProgress
RumovZ Apr 30, 2022
3864857
Outsource incrementing into Incrementor
RumovZ Apr 30, 2022
9231911
Merge remote-tracking branch 'upstream' into apkg
RumovZ Apr 30, 2022
8c5311e
Mutate ProgressUpdate in progress_update callback
RumovZ May 1, 2022
82e056b
Switch import/export legacy toggle to profile setting
dae May 2, 2022
8e4b1c6
Show extension in export dialog
dae May 2, 2022
8f0b2c1
Continue to provide separate options for schema 11+18 colpkg export
dae May 2, 2022
b600366
Default to colpkg export when using File>Export
dae May 2, 2022
9c7d7b9
Improve appearance of combo boxes when switching between apkg/colpkg
dae May 2, 2022
87b904a
Convert newlines to spaces when showing fields from import
dae May 2, 2022
911854a
Don't separate total note count from the other summary lines
dae May 2, 2022
40ba291
Fix 'deck not normal' error when importing a filtered deck for the 2n…
dae May 2, 2022
38cb6ef
Fix [Identical] being shown on first import
dae May 2, 2022
a296ea7
Revert "Continue to provide separate options for schema 11+18 colpkg …
dae May 2, 2022
f3be2ed
Move legacy support into a separate exporter option; add to apkg export
dae May 2, 2022
d7cbcf1
Adjust 'too new' message to also apply to .apkg import case
dae May 2, 2022
4c3ab6b
Show a better message when attempting to import new apkg into old code
dae May 2, 2022
c98f9ae
Hide legacy support option in older exporting screen
dae May 2, 2022
bc4ff14
Reflect change from paths to fnames in type & name
RumovZ May 2, 2022
6b3e59e
Make imported decks normal at once
RumovZ May 2, 2022
fd21273
Merge branch 'main' into apkg
dae May 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ test --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect --output_groups=+cl
# print output when test fails
test --test_output=errors

# stop after one test failure
test --notest_keep_going

# don't add empty __init__.py files
build --incompatible_default_to_explicit_init_py

Expand Down
3 changes: 2 additions & 1 deletion ftl/core/exporting.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exporting-anki-deck-package = Anki Deck Package
exporting-cards-in-plain-text = Cards in Plain Text
exporting-collection = collection
exporting-collection-exported = Collection exported.
exporting-colpkg-too-new = Please update to the latest Anki version, then import the .colpkg file again.
exporting-colpkg-too-new = Please update to the latest Anki version, then import the .colpkg/.apkg file again.
exporting-couldnt-save-file = Couldn't save file: { $val }
exporting-export = Export...
exporting-export-format = <b>Export format</b>:
Expand All @@ -14,6 +14,7 @@ exporting-include-html-and-media-references = Include HTML and media references
exporting-include-media = Include media
exporting-include-scheduling-information = Include scheduling information
exporting-include-tags = Include tags
exporting-support-older-anki-versions = Support older Anki versions (slower/larger files)
exporting-notes-in-plain-text = Notes in Plain Text
exporting-selected-notes = Selected Notes
exporting-card-exported =
Expand Down
6 changes: 6 additions & 0 deletions ftl/core/importing.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,10 @@ importing-processed-media-file =
*[other] Imported { $count } media files
}
importing-importing-collection = Importing collection...
importing-importing-file = Importing file...
importing-failed-to-import-media-file = Failed to import media file: { $debugInfo }
importing-processed-notes =
{ $count ->
[one] Processed { $count } note...
*[other] Processed { $count } notes...
}
37 changes: 37 additions & 0 deletions proto/anki/import_export.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ syntax = "proto3";

package anki.import_export;

import "anki/collection.proto";
import "anki/notes.proto";
import "anki/generic.proto";

service ImportExportService {
rpc ImportCollectionPackage(ImportCollectionPackageRequest)
returns (generic.Empty);
rpc ExportCollectionPackage(ExportCollectionPackageRequest)
returns (generic.Empty);
rpc ImportAnkiPackage(ImportAnkiPackageRequest)
returns (ImportAnkiPackageResponse);
rpc ExportAnkiPackage(ExportAnkiPackageRequest) returns (generic.UInt32);
}

message ImportCollectionPackageRequest {
string col_path = 1;
string backup_path = 2;
string media_folder = 3;
string media_db = 4;
}

message ExportCollectionPackageRequest {
Expand All @@ -26,6 +32,37 @@ message ExportCollectionPackageRequest {
bool legacy = 3;
}

message ImportAnkiPackageRequest {
string package_path = 1;
}

message ImportAnkiPackageResponse {
message Note {
notes.NoteId id = 1;
repeated string fields = 2;
}
message Log {
repeated Note new = 1;
repeated Note updated = 2;
repeated Note duplicate = 3;
repeated Note conflicting = 4;
}
collection.OpChanges changes = 1;
Log log = 2;
}

message ExportAnkiPackageRequest {
string out_path = 1;
bool with_scheduling = 2;
bool with_media = 3;
bool legacy = 4;
oneof selector {
generic.Empty whole_collection = 5;
int64 deck_id = 6;
notes.NoteIds note_ids = 7;
}
}

message PackageMetadata {
enum Version {
VERSION_UNKNOWN = 0;
Expand Down
1 change: 1 addition & 0 deletions pylib/.pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ignored-classes=
CustomStudyRequest,
Cram,
ScheduleCardsAsNewRequest,
ExportAnkiPackageRequest,

[REPORTS]
output-format=colorized
Expand Down
68 changes: 56 additions & 12 deletions pylib/anki/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
collection_pb2,
config_pb2,
generic_pb2,
import_export_pb2,
links_pb2,
search_pb2,
stats_pb2,
Expand All @@ -32,6 +33,7 @@
BrowserRow = search_pb2.BrowserRow
BrowserColumns = search_pb2.BrowserColumns
StripHtmlMode = card_rendering_pb2.StripHtmlRequest
ImportLogWithChanges = import_export_pb2.ImportAnkiPackageResponse

import copy
import os
Expand Down Expand Up @@ -90,6 +92,19 @@ class LegacyCheckpoint:
LegacyUndoResult = Union[None, LegacyCheckpoint, LegacyReviewUndo]


@dataclass
class DeckIdLimit:
deck_id: DeckId


@dataclass
class NoteIdsLimit:
note_ids: Sequence[NoteId]


ExportLimit = Union[DeckIdLimit, NoteIdsLimit, None]


class Collection(DeprecatedNamesMixin):
sched: V1Scheduler | V2Scheduler | V3Scheduler

Expand Down Expand Up @@ -259,14 +274,6 @@ def close_for_full_sync(self) -> None:
self._clear_caches()
self.db = None

def export_collection(
self, out_path: str, include_media: bool, legacy: bool
) -> None:
self.close_for_full_sync()
self._backend.export_collection_package(
out_path=out_path, include_media=include_media, legacy=legacy
)

def rollback(self) -> None:
self._clear_caches()
self.db.rollback()
Expand Down Expand Up @@ -321,6 +328,15 @@ def usn(self) -> int:
else:
return -1

def legacy_checkpoint_pending(self) -> bool:
return (
self._have_outstanding_checkpoint()
and time.time() - self._last_checkpoint_at < 300
)

# Import/export
##########################################################################

def create_backup(
self,
*,
Expand Down Expand Up @@ -353,12 +369,40 @@ def await_backup_completion(self) -> None:
"Throws if backup creation failed."
self._backend.await_backup_completion()

def legacy_checkpoint_pending(self) -> bool:
return (
self._have_outstanding_checkpoint()
and time.time() - self._last_checkpoint_at < 300
def export_collection_package(
self, out_path: str, include_media: bool, legacy: bool
) -> None:
self.close_for_full_sync()
self._backend.export_collection_package(
out_path=out_path, include_media=include_media, legacy=legacy
)

def import_anki_package(self, path: str) -> ImportLogWithChanges:
return self._backend.import_anki_package(package_path=path)

def export_anki_package(
self,
*,
out_path: str,
limit: ExportLimit,
with_scheduling: bool,
with_media: bool,
legacy_support: bool,
) -> int:
request = import_export_pb2.ExportAnkiPackageRequest(
out_path=out_path,
with_scheduling=with_scheduling,
with_media=with_media,
legacy=legacy_support,
)
if isinstance(limit, DeckIdLimit):
request.deck_id = limit.deck_id
elif isinstance(limit, NoteIdsLimit):
request.note_ids.note_ids.extend(limit.note_ids)
else:
request.whole_collection.SetInParent()
return self._backend.export_anki_package(request)

# Object helpers
##########################################################################

Expand Down
2 changes: 1 addition & 1 deletion pylib/anki/exporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ def progress() -> None:
time.sleep(0.1)

threading.Thread(target=progress).start()
self.col.export_collection(path, self.includeMedia, self.LEGACY)
self.col.export_collection_package(path, self.includeMedia, self.LEGACY)


class AnkiCollectionPackage21bExporter(AnkiCollectionPackageExporter):
Expand Down
1 change: 1 addition & 0 deletions pylib/anki/import_export_pb2.pyi
4 changes: 4 additions & 0 deletions pylib/anki/importing/anki2.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class V2ImportIntoV1(Exception):
pass


class MediaMapInvalid(Exception):
pass


class Anki2Importer(Importer):

needMapper = False
Expand Down
8 changes: 6 additions & 2 deletions pylib/anki/importing/apkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import zipfile
from typing import Any, Optional

from anki.importing.anki2 import Anki2Importer
from anki.importing.anki2 import Anki2Importer, MediaMapInvalid
from anki.utils import tmpfile


Expand All @@ -36,7 +36,11 @@ def run(self) -> None: # type: ignore
# number to use during the import
self.nameToNum = {}
dir = self.col.media.dir()
for k, v in list(json.loads(z.read("media").decode("utf8")).items()):
try:
media_dict = json.loads(z.read("media").decode("utf8"))
except Exception as exc:
raise MediaMapInvalid() from exc
for k, v in list(media_dict.items()):
path = os.path.abspath(os.path.join(dir, v))
if os.path.commonprefix([path, dir]) != dir:
raise Exception("Invalid file")
Expand Down
11 changes: 8 additions & 3 deletions qt/aqt/browser/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
from anki.utils import is_mac
from aqt import AnkiQt, gui_hooks
from aqt.editor import Editor
from aqt.exporting import ExportDialog
from aqt.exporting import ExportDialog as LegacyExportDialog
from aqt.import_export.exporting import ExportDialog
from aqt.operations.card import set_card_deck, set_card_flag
from aqt.operations.collection import redo, undo
from aqt.operations.note import remove_notes
Expand Down Expand Up @@ -792,8 +793,12 @@ def suspend_selected_cards(self, checked: bool) -> None:
@no_arg_trigger
@skip_if_selection_is_empty
def _on_export_notes(self) -> None:
cids = self.selectedNotesAsCards()
ExportDialog(self.mw, cids=list(cids))
if self.mw.pm.new_import_export():
nids = self.selected_notes()
ExportDialog(self.mw, nids=nids)
else:
cids = self.selectedNotesAsCards()
LegacyExportDialog(self.mw, cids=list(cids))

# Flags & Marking
######################################################################
Expand Down
4 changes: 2 additions & 2 deletions qt/aqt/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from markdown import markdown

import aqt
from anki.errors import DocumentedError, LocalizedError
from anki.errors import DocumentedError, Interrupted, LocalizedError
from aqt.qt import *
from aqt.utils import showText, showWarning, supportText, tr

Expand All @@ -22,7 +22,7 @@

def show_exception(*, parent: QWidget, exception: Exception) -> None:
"Present a caught exception to the user using a pop-up."
if isinstance(exception, InterruptedError):
if isinstance(exception, Interrupted):
RumovZ marked this conversation as resolved.
Show resolved Hide resolved
# nothing to do
return
help_page = exception.help_page if isinstance(exception, DocumentedError) else None
Expand Down
1 change: 1 addition & 0 deletions qt/aqt/exporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(
self.col = mw.col.weakref()
self.frm = aqt.forms.exporting.Ui_ExportDialog()
self.frm.setupUi(self)
self.frm.legacy_support.setVisible(False)
self.exporter: Exporter | None = None
self.cids = cids
disable_help_button(self)
Expand Down
29 changes: 25 additions & 4 deletions qt/aqt/forms/exporting.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>295</width>
<height>223</height>
<width>563</width>
<height>245</height>
</rect>
</property>
<property name="windowTitle">
Expand All @@ -30,7 +30,14 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="format"/>
<widget class="QComboBox" name="format">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
Expand All @@ -40,7 +47,11 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="deck"/>
<widget class="QComboBox" name="deck">
<property name="minimumContentsLength">
<number>50</number>
</property>
</widget>
</item>
</layout>
</item>
Expand Down Expand Up @@ -83,6 +94,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="legacy_support">
<property name="text">
<string>exporting_support_older_anki_versions</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
Expand Down
Empty file.