mirror of
https://github.com/ankitects/anki.git
synced 2025-11-06 12:47:11 -05:00
Merge branch 'main' into launcher-gui
This commit is contained in:
commit
e7a2763e21
13 changed files with 93 additions and 36 deletions
13
.idea.dist/repo.iml
Normal file
13
.idea.dist/repo.iml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/out/pylib" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/pylib" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/qt" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/extra" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/out/pyenv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
@ -12,8 +12,7 @@
|
||||||
"command": "tools/ninja.bat",
|
"command": "tools/ninja.bat",
|
||||||
"args": [
|
"args": [
|
||||||
"pylib",
|
"pylib",
|
||||||
"qt",
|
"qt"
|
||||||
"extract:win_amd64_audio"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ Christian Donat <https://github.com/cdonat2>
|
||||||
Asuka Minato <https://asukaminato.eu.org>
|
Asuka Minato <https://asukaminato.eu.org>
|
||||||
Dillon Baldwin <https://github.com/DillBal>
|
Dillon Baldwin <https://github.com/DillBal>
|
||||||
Voczi <https://github.com/voczi>
|
Voczi <https://github.com/voczi>
|
||||||
Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com>
|
Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com>
|
||||||
Themis Demetriades <themis100@outlook.com>
|
Themis Demetriades <themis100@outlook.com>
|
||||||
Luke Bartholomew <lukesbart@icloud.com>
|
Luke Bartholomew <lukesbart@icloud.com>
|
||||||
Gregory Abrasaldo <degeemon@gmail.com>
|
Gregory Abrasaldo <degeemon@gmail.com>
|
||||||
|
|
@ -251,6 +251,9 @@ Matbe766 <matildabergstrom01@gmail.com>
|
||||||
Amanda Sternberg <mandis.sternberg@gmail.com>
|
Amanda Sternberg <mandis.sternberg@gmail.com>
|
||||||
arold0 <arold0@icloud.com>
|
arold0 <arold0@icloud.com>
|
||||||
nav1s <nav1s@proton.me>
|
nav1s <nav1s@proton.me>
|
||||||
|
Ranjit Odedra <ranjitodedra.dev@gmail.com>
|
||||||
|
Eltaurus <https://github.com/Eltaurus-Lt>
|
||||||
|
jariji
|
||||||
|
|
||||||
********************
|
********************
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,14 @@ see and install a number of recommended extensions.
|
||||||
|
|
||||||
## PyCharm/IntelliJ
|
## PyCharm/IntelliJ
|
||||||
|
|
||||||
If you decide to use PyCharm instead of VS Code, there are somethings to be
|
### Setting up Python environment
|
||||||
aware of.
|
|
||||||
|
|
||||||
### Pylib References
|
To make PyCharm recognize `anki` and `aqt` imports, you need to add source paths to _Settings > Project Structure_.
|
||||||
|
You can copy the provided .idea.dist directory to set up the paths automatically:
|
||||||
|
|
||||||
You'll need to use File>Project Structure to tell IntelliJ that pylib/ is a
|
```
|
||||||
sources root, so it knows references to 'anki' in aqt are valid.
|
mkdir .idea && cd .idea
|
||||||
|
ln -sf ../.idea.dist/* .
|
||||||
|
```
|
||||||
|
|
||||||
|
You also need to add a new Python interpreter under _Settings > Python > Interpreter_ pointing to the Python executable under `out/pyenv` (available after building Anki).
|
||||||
|
|
|
||||||
|
|
@ -85,11 +85,11 @@
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QSpinBox" name="limit">
|
<widget class="QSpinBox" name="limit">
|
||||||
<property name="maximumSize">
|
<property name="sizePolicy">
|
||||||
<size>
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
<width>60</width>
|
<horstretch>0</horstretch>
|
||||||
<height>16777215</height>
|
<verstretch>0</verstretch>
|
||||||
</size>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
|
|
@ -168,11 +168,11 @@
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QSpinBox" name="limit_2">
|
<widget class="QSpinBox" name="limit_2">
|
||||||
<property name="maximumSize">
|
<property name="sizePolicy">
|
||||||
<size>
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
<width>60</width>
|
<horstretch>0</horstretch>
|
||||||
<height>16777215</height>
|
<verstretch>0</verstretch>
|
||||||
</size>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,7 @@ class Preferences(QDialog):
|
||||||
self.update_login_status()
|
self.update_login_status()
|
||||||
self.confirm_sync_after_login()
|
self.confirm_sync_after_login()
|
||||||
|
|
||||||
|
self.update_network()
|
||||||
sync_login(self.mw, on_success)
|
sync_login(self.mw, on_success)
|
||||||
|
|
||||||
def sync_logout(self) -> None:
|
def sync_logout(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,7 @@ fn main() {
|
||||||
.manifest_required()
|
.manifest_required()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
println!("cargo:rerun-if-changed=../../out/buildhash");
|
||||||
|
let buildhash = std::fs::read_to_string("../../out/buildhash").unwrap_or_default();
|
||||||
|
println!("cargo:rustc-env=BUILDHASH={buildhash}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,9 @@ fn run() -> Result<()> {
|
||||||
let sync_time = file_timestamp_secs(&state.sync_complete_marker);
|
let sync_time = file_timestamp_secs(&state.sync_complete_marker);
|
||||||
state.pyproject_modified_by_user = pyproject_time > sync_time;
|
state.pyproject_modified_by_user = pyproject_time > sync_time;
|
||||||
let pyproject_has_changed = state.pyproject_modified_by_user;
|
let pyproject_has_changed = state.pyproject_modified_by_user;
|
||||||
if !launcher_requested && !pyproject_has_changed {
|
let different_launcher = diff_launcher_was_installed(&state)?;
|
||||||
|
|
||||||
|
if !launcher_requested && !pyproject_has_changed && !different_launcher {
|
||||||
// If no launcher request and venv is already up to date, launch Anki normally
|
// If no launcher request and venv is already up to date, launch Anki normally
|
||||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||||
let cmd = build_python_command(&state, &args)?;
|
let cmd = build_python_command(&state, &args)?;
|
||||||
|
|
@ -325,7 +327,6 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
|
||||||
}
|
}
|
||||||
|
|
||||||
command
|
command
|
||||||
.env("UV_CACHE_DIR", &state.uv_cache_dir)
|
|
||||||
.env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir)
|
.env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir)
|
||||||
.env(
|
.env(
|
||||||
"UV_HTTP_TIMEOUT",
|
"UV_HTTP_TIMEOUT",
|
||||||
|
|
@ -344,10 +345,6 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.no_cache_marker.exists() {
|
|
||||||
command.env("UV_NO_CACHE", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
match command.ensure_success() {
|
match command.ensure_success() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Sync succeeded
|
// Sync succeeded
|
||||||
|
|
@ -603,18 +600,27 @@ fn get_version_kind(state: &State) -> Result<Option<VersionKind>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_only_latest_patch(versions: &[String]) -> Vec<String> {
|
fn with_only_latest_patch(versions: &[String]) -> Vec<String> {
|
||||||
// Only show the latest patch release for a given (major, minor)
|
// Assumes versions are sorted in descending order (newest first)
|
||||||
|
// Only show the latest patch release for a given (major, minor),
|
||||||
|
// and exclude pre-releases if a newer major_minor exists
|
||||||
let mut seen_major_minor = std::collections::HashSet::new();
|
let mut seen_major_minor = std::collections::HashSet::new();
|
||||||
versions
|
versions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|v| {
|
.filter(|v| {
|
||||||
let (major, minor, _, _) = parse_version_for_filtering(v);
|
let (major, minor, _, is_prerelease) = parse_version_for_filtering(v);
|
||||||
if major == 2 {
|
if major == 2 {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let major_minor = (major, minor);
|
let major_minor = (major, minor);
|
||||||
if seen_major_minor.contains(&major_minor) {
|
if seen_major_minor.contains(&major_minor) {
|
||||||
false
|
false
|
||||||
|
} else if is_prerelease
|
||||||
|
&& seen_major_minor
|
||||||
|
.iter()
|
||||||
|
.any(|&(seen_major, seen_minor)| (seen_major, seen_minor) > (major, minor))
|
||||||
|
{
|
||||||
|
// Exclude pre-release if a newer major_minor exists
|
||||||
|
false
|
||||||
} else {
|
} else {
|
||||||
seen_major_minor.insert(major_minor);
|
seen_major_minor.insert(major_minor);
|
||||||
true
|
true
|
||||||
|
|
@ -1013,6 +1019,15 @@ fn uv_command(state: &State) -> Result<Command> {
|
||||||
.env("UV_DEFAULT_INDEX", &pypi_mirror);
|
.env("UV_DEFAULT_INDEX", &pypi_mirror);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state.no_cache_marker.exists() {
|
||||||
|
command.env("UV_NO_CACHE", "1");
|
||||||
|
} else {
|
||||||
|
command.env("UV_CACHE_DIR", &state.uv_cache_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// have uv use the system certstore instead of webpki-roots'
|
||||||
|
command.env("UV_NATIVE_TLS", "1");
|
||||||
|
|
||||||
Ok(command)
|
Ok(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1107,6 +1122,20 @@ fn show_mirror_submenu(state: &State) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn diff_launcher_was_installed(state: &State) -> Result<bool> {
|
||||||
|
let launcher_version = option_env!("BUILDHASH").unwrap_or("dev").trim();
|
||||||
|
let launcher_version_path = state.uv_install_root.join("launcher-version");
|
||||||
|
if let Ok(content) = read_file(&launcher_version_path) {
|
||||||
|
if let Ok(version_str) = String::from_utf8(content) {
|
||||||
|
if version_str.trim() == launcher_version {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_file(launcher_version_path, launcher_version)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub(crate) fn write_python_interface(services: &[BackendService]) -> Result<()>
|
||||||
write_header(&mut out)?;
|
write_header(&mut out)?;
|
||||||
|
|
||||||
for service in services {
|
for service in services {
|
||||||
if service.name == "BackendAnkidroidService" {
|
if ["BackendAnkidroidService", "BackendFrontendService"].contains(&service.name.as_str()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for method in service.all_methods() {
|
for method in service.all_methods() {
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,6 @@ fn reveal_cloze(
|
||||||
image_occlusion_text,
|
image_occlusion_text,
|
||||||
question,
|
question,
|
||||||
active,
|
active,
|
||||||
cloze_ord,
|
|
||||||
&cloze.ordinals,
|
&cloze.ordinals,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
|
|
@ -332,10 +331,9 @@ fn render_image_occlusion(
|
||||||
text: &str,
|
text: &str,
|
||||||
question_side: bool,
|
question_side: bool,
|
||||||
active: bool,
|
active: bool,
|
||||||
ordinal: u16,
|
|
||||||
ordinals: &[u16],
|
ordinals: &[u16],
|
||||||
) -> String {
|
) -> String {
|
||||||
if (question_side && active) || ordinal == 0 {
|
if (question_side && active) || ordinals.contains(&0) {
|
||||||
format!(
|
format!(
|
||||||
r#"<div class="cloze" data-ordinal="{}" {}></div>"#,
|
r#"<div class="cloze" data-ordinal="{}" {}></div>"#,
|
||||||
ordinals_str(ordinals),
|
ordinals_str(ordinals),
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ trait DiffTrait {
|
||||||
if self.get_typed() == self.get_expected() {
|
if self.get_typed() == self.get_expected() {
|
||||||
format_typeans!(format!(
|
format_typeans!(format!(
|
||||||
"<span class=typeGood>{}</span>",
|
"<span class=typeGood>{}</span>",
|
||||||
self.get_expected_original()
|
htmlescape::encode_minimal(&self.get_expected_original())
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let output = self.to_tokens();
|
let output = self.to_tokens();
|
||||||
|
|
@ -391,6 +391,15 @@ mod test {
|
||||||
assert_eq!(ctx, "<code id=typeans>123</code>");
|
assert_eq!(ctx, "<code id=typeans>123</code>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correct_input_is_escaped() {
|
||||||
|
let ctx = Diff::new("source <dir>/bin/activate", "source <dir>/bin/activate");
|
||||||
|
assert_eq!(
|
||||||
|
ctx.to_html(),
|
||||||
|
"<code id=typeans><span class=typeGood>source <dir>/bin/activate</span></code>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn correct_input_is_collapsed() {
|
fn correct_input_is_collapsed() {
|
||||||
let ctx = Diff::new("123", "123");
|
let ctx = Diff::new("123", "123");
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.extend(["pylib", "qt", "out/pylib", "out/qt"])
|
sys.path.extend(["pylib", "qt", "out/pylib", "out/qt"])
|
||||||
if sys.platform == "win32":
|
|
||||||
os.environ["PATH"] += ";out\\extracted\\win_amd64_audio"
|
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6975,8 +6975,8 @@ __metadata:
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vite@npm:6":
|
"vite@npm:6":
|
||||||
version: 6.3.6
|
version: 6.4.1
|
||||||
resolution: "vite@npm:6.3.6"
|
resolution: "vite@npm:6.4.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: "npm:^0.25.0"
|
esbuild: "npm:^0.25.0"
|
||||||
fdir: "npm:^6.4.4"
|
fdir: "npm:^6.4.4"
|
||||||
|
|
@ -7025,7 +7025,7 @@ __metadata:
|
||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
vite: bin/vite.js
|
vite: bin/vite.js
|
||||||
checksum: 10c0/add701f1e72596c002275782e38d0389ab400c1be330c93a3009804d62db68097a936ca1c53c3301df3aaacfe5e328eab547060f31ef9c49a277ae50df6ad4fb
|
checksum: 10c0/77bb4c5b10f2a185e7859cc9a81c789021bc18009b02900347d1583b453b58e4b19ff07a5e5a5b522b68fc88728460bb45a63b104d969e8c6a6152aea3b849f7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue