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.
This commit is contained in:
Damien Elmes 2024-03-31 09:16:31 +01:00 committed by GitHub
parent 429bc9e14c
commit 9f55cf26fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
527 changed files with 2933 additions and 3676 deletions

View File

@ -1,7 +1,6 @@
[env]
STRINGS_PY = { value = "out/pylib/anki/_fluent.py", relative = true }
STRINGS_JS = { value = "out/ts/lib/ftl.js", relative = true }
STRINGS_DTS = { value = "out/ts/lib/ftl.d.ts", relative = true }
STRINGS_TS = { value = "out/ts/lib/generated/ftl.ts", relative = true }
DESCRIPTORS_BIN = { value = "out/rslib/proto/descriptors.bin", relative = true }
# build script will append .exe if necessary
PROTOC = { value = "out/extracted/protoc/bin/protoc", relative = true }

View File

@ -30,7 +30,8 @@
"qt/bundle/PyOxidizer",
"target",
".mypy_cache",
"extra"
"extra",
"ts/.svelte-kit"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.85.1.wasm",

View File

@ -36,6 +36,7 @@ module.exports = {
],
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-explicit-any": "off",
},
},
{
@ -47,11 +48,12 @@ module.exports = {
rules: {
"svelte/no-at-html-tags": "off",
"svelte/valid-compile": ["error", { "ignoreWarnings": true }],
"@typescript-eslint/no-explicit-any": "off",
},
},
],
env: { browser: true, es2020: true },
ignorePatterns: ["backend_proto.d.ts", "*.svelte.d.ts", "vendor", "extra/*"],
ignorePatterns: ["backend_proto.d.ts", "*.svelte.d.ts", "vendor", "extra/*", "vite.config.ts"],
globals: {
globalThis: false,
NodeListOf: false,

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ node_modules
.ninja_deps
/extra
yarn-error.log
ts/.svelte-kit

View File

@ -14,6 +14,7 @@ use ninja_gen::node::EsbuildScript;
use ninja_gen::node::TypescriptCheck;
use ninja_gen::python::python_format;
use ninja_gen::python::PythonTest;
use ninja_gen::rsync::RsyncFiles;
use ninja_gen::Build;
use ninja_gen::Utf8Path;
use ninja_gen::Utf8PathBuf;
@ -21,7 +22,6 @@ use ninja_gen::Utf8PathBuf;
use crate::anki_version;
use crate::python::BuildWheel;
use crate::web::copy_mathjax;
use crate::web::eslint;
pub fn build_and_check_aqt(build: &mut Build) -> Result<()> {
build_forms(build)?;
@ -114,9 +114,22 @@ fn build_data_folder(build: &mut Build) -> Result<()> {
build_js(build)?;
build_pages(build)?;
build_icons(build)?;
copy_sveltekit(build)?;
Ok(())
}
fn copy_sveltekit(build: &mut Build) -> Result<()> {
build.add_action(
"qt:aqt:data:web:sveltekit",
RsyncFiles {
inputs: inputs![":sveltekit:folder"],
target_folder: "qt/_aqt/data/web/",
strip_prefix: "$builddir/",
extra_args: "-a",
},
)
}
fn build_css(build: &mut Build) -> Result<()> {
let scss_files = build.expand_inputs(inputs![glob!["qt/aqt/data/web/css/*.scss"]]);
let out_dir = Utf8Path::new("qt/_aqt/data/web/css");
@ -172,7 +185,6 @@ fn build_js(build: &mut Build) -> Result<()> {
)?;
}
let files = inputs![glob!["qt/aqt/data/web/js/*"]];
eslint(build, "aqt", "qt/aqt/data/web/js", files.clone())?;
build.add_action(
"check:typescript:aqt",
TypescriptCheck {

View File

@ -56,8 +56,7 @@ fn prepare_translations(build: &mut Build) -> Result<()> {
],
outputs: &[
RustOutput::Data("py", "pylib/anki/_fluent.py"),
RustOutput::Data("ts", "ts/lib/ftl.d.ts"),
RustOutput::Data("ts", "ts/lib/ftl.js"),
RustOutput::Data("ts", "ts/lib/generated/ftl.ts"),
],
target: None,
extra_args: "-p anki_i18n",
@ -119,8 +118,7 @@ fn build_proto_descriptors_and_interfaces(build: &mut Build) -> Result<()> {
let outputs = vec![
RustOutput::Data("descriptors.bin", "rslib/proto/descriptors.bin"),
RustOutput::Data("py", "pylib/anki/_backend_generated.py"),
RustOutput::Data("ts", "ts/lib/backend.d.ts"),
RustOutput::Data("ts", "ts/lib/backend.js"),
RustOutput::Data("ts", "ts/lib/generated/backend.ts"),
];
build.add_action(
"rslib:proto",
@ -249,7 +247,7 @@ pub fn check_minilints(build: &mut Build) -> Result<()> {
let files = inputs![
glob![
"**/*.{py,rs,ts,svelte,mjs}",
"{node_modules,qt/bundle/PyOxidizer}/**"
"{node_modules,qt/bundle/PyOxidizer,ts/.svelte-kit}/**"
],
"Cargo.lock"
];

View File

@ -3,21 +3,21 @@
use anyhow::Result;
use ninja_gen::action::BuildAction;
use ninja_gen::copy::CopyFiles;
use ninja_gen::glob;
use ninja_gen::hashmap;
use ninja_gen::input::BuildInput;
use ninja_gen::inputs;
use ninja_gen::node::node_archive;
use ninja_gen::node::CompileSass;
use ninja_gen::node::CompileTypescript;
use ninja_gen::node::DPrint;
use ninja_gen::node::EsbuildScript;
use ninja_gen::node::Eslint;
use ninja_gen::node::GenTypescriptProto;
use ninja_gen::node::JestTest;
use ninja_gen::node::SqlFormat;
use ninja_gen::node::SvelteCheck;
use ninja_gen::node::TypescriptCheck;
use ninja_gen::node::SveltekitBuild;
use ninja_gen::node::ViteTest;
use ninja_gen::rsync::RsyncFiles;
use ninja_gen::Build;
@ -25,6 +25,7 @@ pub fn build_and_check_web(build: &mut Build) -> Result<()> {
setup_node(build)?;
build_sass(build)?;
build_and_check_tslib(build)?;
build_sveltekit(build)?;
declare_and_check_other_libraries(build)?;
build_and_check_pages(build)?;
build_and_check_editor(build)?;
@ -35,6 +36,20 @@ pub fn build_and_check_web(build: &mut Build) -> Result<()> {
Ok(())
}
fn build_sveltekit(build: &mut Build) -> Result<()> {
build.add_action(
"sveltekit",
SveltekitBuild {
output_folder: inputs!["sveltekit"],
deps: inputs![
"ts/tsconfig.json",
glob!["ts/**", "ts/.svelte-kit/**"],
":ts:lib"
],
},
)
}
fn setup_node(build: &mut Build) -> Result<()> {
ninja_gen::node::setup_node(
build,
@ -46,7 +61,8 @@ fn setup_node(build: &mut Build) -> Result<()> {
"sass",
"tsc",
"tsx",
"jest",
"vite",
"vitest",
"protoc-gen-es",
],
hashmap! {
@ -111,55 +127,39 @@ fn setup_node(build: &mut Build) -> Result<()> {
}
fn build_and_check_tslib(build: &mut Build) -> Result<()> {
build.add_dependency("ts:lib:i18n", ":rslib:i18n:ts".into());
build.add_dependency("ts:generated:i18n", ":rslib:i18n:ts".into());
build.add_action(
"ts:lib:proto",
"ts:generated:proto",
GenTypescriptProto {
protos: inputs![glob!["proto/**/*.proto"]],
include_dirs: &["proto"],
out_dir: "out/ts/lib",
out_dir: "out/ts/lib/generated",
out_path_transform: |path| {
path.replace("proto/", "ts/lib/")
.replace("proto\\", "ts/lib\\")
path.replace("proto/", "ts/lib/generated/")
.replace("proto\\", "ts/lib/generated\\")
},
ts_transform_script: "ts/tools/markpure.ts",
},
)?;
// ensure _service files are generated by rslib
build.add_dependency("ts:lib:proto", inputs![":rslib:proto:ts"]);
// the generated _service.js files import @tslib/post, and esbuild won't be able
// to import the .ts file, so we need to generate a .js file for it
build.add_dependency("ts:generated:proto", inputs![":rslib:proto:ts"]);
// copy source files from ts/lib/generated
build.add_action(
"ts:lib:proto",
CompileTypescript {
ts_files: "ts/lib/post.ts".into(),
out_dir: "out/ts/lib",
out_path_transform: |path| path.into(),
"ts:generated:src",
CopyFiles {
inputs: inputs![glob!["ts/lib/generated/*.ts"]],
output_folder: "ts/lib/generated",
},
)?;
let src_files = inputs![glob!["ts/lib/**"]];
eslint(build, "lib", "ts/lib", inputs![":ts:lib", &src_files])?;
build.add_action(
"check:jest:lib",
jest_test("ts/lib", inputs![":ts:lib", &src_files], true),
)?;
build.add_dependency("ts:lib", inputs![":ts:generated"]);
build.add_dependency("ts:lib", src_files);
Ok(())
}
fn jest_test(folder: &str, deps: BuildInput, jsdom: bool) -> impl BuildAction + '_ {
JestTest {
folder,
deps,
jest_rc: "ts/jest.config.js".into(),
jsdom,
}
}
fn declare_and_check_other_libraries(build: &mut Build) -> Result<()> {
for (library, inputs) in [
("sveltelib", inputs![":ts:lib", glob!("ts/sveltelib/**")]),
@ -171,49 +171,13 @@ fn declare_and_check_other_libraries(build: &mut Build) -> Result<()> {
("html-filter", inputs![glob!("ts/html-filter/**")]),
] {
let library_with_ts = format!("ts:{library}");
let folder = library_with_ts.replace(':', "/");
build.add_dependency(&library_with_ts, inputs.clone());
eslint(build, library, &folder, inputs.clone())?;
if matches!(library, "domlib" | "html-filter") {
build.add_action(
&format!("check:jest:{library}"),
jest_test(&folder, inputs, true),
)?;
}
}
eslint(build, "scripts", "ts/tools", inputs![glob!("ts/tools/*")])?;
Ok(())
}
pub fn eslint(build: &mut Build, name: &str, folder: &str, deps: BuildInput) -> Result<()> {
let eslint_rc = inputs![".eslintrc.js"];
build.add_action(
format!("check:eslint:{name}"),
Eslint {
folder,
inputs: deps.clone(),
eslint_rc: eslint_rc.clone(),
fix: false,
},
)?;
build.add_action(
format!("fix:eslint:{name}"),
Eslint {
folder,
inputs: deps,
eslint_rc,
fix: true,
},
)?;
Ok(())
}
fn build_and_check_pages(build: &mut Build) -> Result<()> {
build.add_dependency("ts:tag-editor", inputs![glob!["ts/tag-editor/**"]]);
let mut build_page = |name: &str, html: bool, deps: BuildInput| -> Result<()> {
let group = format!("ts:{name}");
let deps = inputs![deps, glob!(format!("ts/{name}/**"))];
@ -229,99 +193,9 @@ fn build_and_check_pages(build: &mut Build) -> Result<()> {
},
)?;
build.add_dependency("ts:pages", inputs![format!(":{group}")]);
build.add_action(
format!("check:svelte:{name}"),
SvelteCheck {
tsconfig: inputs![format!("ts/{name}/tsconfig.json")],
inputs: deps.clone(),
},
)?;
let folder = format!("ts/{name}");
eslint(build, name, &folder, deps.clone())?;
if matches!(name, "deck-options" | "change-notetype") {
build.add_action(
&format!("check:jest:{name}"),
jest_test(&folder, deps, false),
)?;
}
Ok(())
};
build_page(
"congrats",
true,
inputs![
//
":ts:lib",
":ts:components",
":sass",
],
)?;
build_page(
"deck-options",
true,
inputs![
//
":ts:lib",
":ts:components",
":ts:sveltelib",
":sass",
],
)?;
build_page(
"graphs",
true,
inputs![
//
":ts:lib",
":ts:components",
":sass",
],
)?;
build_page(
"card-info",
true,
inputs![
//
":ts:lib",
":ts:components",
":sass",
],
)?;
build_page(
"change-notetype",
true,
inputs![
//
":ts:lib",
":ts:components",
":ts:sveltelib",
":sass",
],
)?;
build_page(
"import-csv",
true,
inputs![
//
":ts:lib",
":ts:components",
":ts:sveltelib",
":ts:tag-editor",
":sass"
],
)?;
build_page(
"import-anki-package",
true,
inputs![
//
":ts:lib",
":ts:components",
":ts:sveltelib",
":sass"
],
)?;
// we use the generated .css file separately
build_page(
"editable",
@ -332,30 +206,8 @@ fn build_and_check_pages(build: &mut Build) -> Result<()> {
":ts:components",
":ts:domlib",
":ts:sveltelib",
":sass"
],
)?;
build_page(
"image-occlusion",
true,
inputs![
//
":ts:lib",
":ts:components",
":ts:sveltelib",
":ts:tag-editor",
":sass"
],
)?;
build_page(
"import-page",
true,
inputs![
//
":ts:lib",
":ts:components",
":ts:sveltelib",
":sass"
":sass",
":sveltekit",
],
)?;
@ -369,11 +221,10 @@ fn build_and_check_editor(build: &mut Build) -> Result<()> {
":ts:components",
":ts:domlib",
":ts:sveltelib",
":ts:tag-editor",
":ts:html-filter",
":ts:image-occlusion",
":sass",
glob!("ts/{editable,editor}/**")
":sveltekit",
glob!("ts/{editable,editor,routes/image-occlusion}/**")
];
build.add_action(
@ -387,20 +238,15 @@ fn build_and_check_editor(build: &mut Build) -> Result<()> {
},
)?;
let group = "ts/editor";
build.add_action(
"check:svelte:editor",
SvelteCheck {
tsconfig: inputs![format!("{group}/tsconfig.json")],
inputs: editor_deps.clone(),
},
)?;
eslint(build, "editor", group, editor_deps)?;
Ok(())
}
fn build_and_check_reviewer(build: &mut Build) -> Result<()> {
let reviewer_deps = inputs![":ts:lib", glob!("ts/{reviewer,image-occlusion}/**"),];
let reviewer_deps = inputs![
":ts:lib",
glob!("ts/{reviewer,image-occlusion}/**"),
":sveltekit"
];
build.add_action(
"ts:reviewer:reviewer.js",
EsbuildScript {
@ -416,7 +262,7 @@ fn build_and_check_reviewer(build: &mut Build) -> Result<()> {
CompileSass {
input: inputs!["ts/reviewer/reviewer.scss"],
output: "ts/reviewer/reviewer.css",
deps: inputs![":sass", "ts/image-occlusion/review.scss"],
deps: inputs![":sass", "ts/routes/image-occlusion/review.scss"],
load_paths: vec!["."],
},
)?;
@ -435,30 +281,18 @@ fn build_and_check_reviewer(build: &mut Build) -> Result<()> {
CompileSass {
input: inputs!["ts/reviewer/reviewer_extras.scss"],
output: "ts/reviewer/reviewer_extras.css",
deps: inputs!["ts/image-occlusion/review.scss"],
deps: inputs!["ts/routes/image-occlusion/review.scss"],
load_paths: vec!["."],
},
)?;
build.add_action(
"check:typescript:reviewer",
TypescriptCheck {
tsconfig: inputs!["ts/reviewer/tsconfig.json"],
inputs: reviewer_deps.clone(),
},
)?;
eslint(build, "reviewer", "ts/reviewer", reviewer_deps)?;
build.add_action(
"check:jest:reviewer",
jest_test("ts/reviewer", inputs![":ts:reviewer"], false),
)?;
Ok(())
}
fn check_web(build: &mut Build) -> Result<()> {
let dprint_files = inputs![glob![
"**/*.{ts,mjs,js,md,json,toml,svelte,scss}",
"target/**"
"{target,ts/.svelte-kit}/**"
]];
build.add_action(
"check:format:dprint",
@ -474,6 +308,52 @@ fn check_web(build: &mut Build) -> Result<()> {
check_only: false,
},
)?;
build.add_action(
"check:vitest",
ViteTest {
deps: inputs![
"yarn",
":node_modules",
":ts:generated",
glob!["ts/{svelte.config.js,vite.config.ts,tsconfig.json}"],
glob!["ts/{lib,deck-options,html-filter,domlib,reviewer,change-notetype}/**/*"],
],
},
)?;
build.add_action(
"check:svelte",
SvelteCheck {
tsconfig: inputs!["ts/tsconfig.json"],
inputs: inputs![
"yarn",
":node_modules",
":ts:generated",
glob!["ts/**/*", "ts/.svelte-kit/**"],
],
},
)?;
let eslint_rc = inputs![".eslintrc.cjs"];
for folder in ["ts", "qt/aqt/data/web/js"] {
let inputs = inputs![glob![format!("{folder}/**"), "ts/.svelte-kit/**"]];
build.add_action(
"check:eslint",
Eslint {
folder,
inputs: inputs.clone(),
eslint_rc: eslint_rc.clone(),
fix: false,
},
)?;
build.add_action(
"fix:eslint",
Eslint {
folder,
inputs,
eslint_rc: eslint_rc.clone(),
fix: true,
},
)?;
}
Ok(())
}
@ -497,7 +377,7 @@ pub fn check_sql(build: &mut Build) -> Result<()> {
}
fn build_and_check_mathjax(build: &mut Build) -> Result<()> {
let files = inputs![glob!["ts/mathjax/*"]];
let files = inputs![glob!["ts/mathjax/*"], ":sveltekit"];
build.add_action(
"ts:mathjax",
EsbuildScript {
@ -507,14 +387,6 @@ fn build_and_check_mathjax(build: &mut Build) -> Result<()> {
output_stem: "ts/mathjax/mathjax",
extra_exts: &[],
},
)?;
eslint(build, "mathjax", "ts/mathjax", files.clone())?;
build.add_action(
"check:typescript:mathjax",
TypescriptCheck {
tsconfig: "ts/mathjax/tsconfig.json".into(),
inputs: files,
},
)
}
@ -566,14 +438,14 @@ pub fn copy_mathjax() -> impl BuildAction {
}
fn build_sass(build: &mut Build) -> Result<()> {
build.add_dependency("sass", inputs![glob!("sass/**")]);
build.add_dependency("sass", inputs![glob!("ts/lib/sass/**")]);
build.add_action(
"css:_root-vars",
CompileSass {
input: inputs!["sass/_root-vars.scss"],
output: "sass/_root-vars.css",
deps: inputs![glob!["sass/*"]],
input: inputs!["ts/lib/sass/_root-vars.scss"],
output: "ts/lib/sass/_root-vars.css",
deps: inputs![glob!["ts/lib/sass/*"]],
load_paths: vec![],
},
)?;

View File

@ -19,24 +19,24 @@ use crate::input::BuildInput;
pub fn node_archive(platform: Platform) -> OnlineArchive {
match platform {
Platform::LinuxX64 => OnlineArchive {
url: "https://nodejs.org/dist/v18.12.1/node-v18.12.1-linux-x64.tar.xz",
sha256: "4481a34bf32ddb9a9ff9540338539401320e8c3628af39929b4211ea3552a19e",
url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz",
sha256: "822780369d0ea309e7d218e41debbd1a03f8cdf354ebf8a4420e89f39cc2e612",
},
Platform::LinuxArm => OnlineArchive {
url: "https://nodejs.org/dist/v18.12.1/node-v18.12.1-linux-arm64.tar.xz",
sha256: "3904869935b7ecc51130b4b86486d2356539a174d11c9181180cab649f32cd2a",
url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-arm64.tar.xz",
sha256: "f943abd348d2b8ff8754ca912c118a20301eb6a0014cc4cdea86cff021fde8e6",
},
Platform::MacX64 => OnlineArchive {
url: "https://nodejs.org/dist/v18.12.1/node-v18.12.1-darwin-x64.tar.xz",
sha256: "6c88d462550a024661e74e9377371d7e023321a652eafb3d14d58a866e6ac002",
url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-x64.tar.xz",
sha256: "d4b4ab81ebf1f7aab09714f834992f27270ad0079600da00c8110f8950ca6c5a",
},
Platform::MacArm => OnlineArchive {
url: "https://nodejs.org/dist/v18.12.1/node-v18.12.1-darwin-arm64.tar.xz",
sha256: "17f2e25d207d36d6b0964845062160d9ed16207c08d09af33b9a2fd046c5896f",
url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-arm64.tar.xz",
sha256: "f18a7438723d48417f5e9be211a2f3c0520ffbf8e02703469e5153137ca0f328",
},
Platform::WindowsX64 => OnlineArchive {
url: "https://nodejs.org/dist/v18.12.1/node-v18.12.1-win-x64.zip",
sha256: "5478a5a2dce2803ae22327a9f8ae8494c1dec4a4beca5bbf897027380aecf4c7",
url: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-win-x64.zip",
sha256: "893115cd92ad27bf178802f15247115e93c0ef0c753b93dca96439240d64feb5",
},
}
}
@ -214,9 +214,11 @@ pub struct SvelteCheck {
impl BuildAction for SvelteCheck {
fn command(&self) -> &str {
"$svelte-check --tsconfig $tsconfig $
--fail-on-warnings --threshold warning $
--compiler-warnings $compiler_warnings"
if cfg!(windows) {
"cmd /c yarn svelte-check:once"
} else {
"./yarn svelte-check:once"
}
}
fn files(&mut self, build: &mut impl build::FilesHandle) {
@ -290,30 +292,23 @@ impl BuildAction for Eslint<'_> {
}
}
pub struct JestTest<'a> {
pub folder: &'a str,
pub struct ViteTest {
pub deps: BuildInput,
pub jest_rc: BuildInput,
pub jsdom: bool,
}
impl BuildAction for JestTest<'_> {
impl BuildAction for ViteTest {
fn command(&self) -> &str {
"$jest --config $config $env $folder"
if cfg!(windows) {
"cmd /c yarn vitest:once"
} else {
"./yarn vitest:once"
}
}
fn files(&mut self, build: &mut impl build::FilesHandle) {
build.add_inputs("jest", inputs![":node_modules:jest"]);
build.add_inputs("vitest", inputs![":node_modules:vitest"]);
build.add_inputs("", &self.deps);
build.add_inputs("config", &self.jest_rc);
build.add_variable("env", if self.jsdom { "--env=jsdom" } else { "" });
build.add_variable("folder", self.folder);
let hash = simple_hash(self.folder);
build.add_output_stamp(format!("tests/jest.{hash}"));
}
fn hide_last_line(&self) -> bool {
true
build.add_output_stamp("tests/vitest");
}
}
@ -451,3 +446,32 @@ impl BuildAction for CompileTypescript<'_> {
build.add_outputs("", output_files);
}
}
/// The output_folder will be declared as a build output, but each file inside
/// it is not declared, as the files will vary.
pub struct SveltekitBuild {
pub output_folder: BuildInput,
pub deps: BuildInput,
}
impl BuildAction for SveltekitBuild {
fn command(&self) -> &str {
if std::env::var("HMR").is_err() {
if cfg!(windows) {
"cmd /c yarn build"
} else {
"./yarn build"
}
} else {
"echo"
}
}
fn files(&mut self, build: &mut impl build::FilesHandle) {
build.add_inputs("node_modules", inputs![":node_modules"]);
build.add_inputs("", &self.deps);
build.add_inputs("", inputs!["yarn.lock"]);
build.add_output_stamp("sveltekit.marker");
build.add_outputs_ext("folder", vec!["sveltekit"], true);
}
}

View File

@ -69,11 +69,7 @@ pub fn run_build(args: BuildArgs) {
// commands will not show colors by default, as we do not provide a tty
.env("FORCE_COLOR", "1")
.env("MYPY_FORCE_COLOR", "1")
.env("TERM", std::env::var("TERM").unwrap_or_default())
// Prevents 'Warn: You must provide the URL of lib/mappings.wasm'.
// Updating svelte-check or its deps will likely remove the need for it.
.env("NODE_OPTIONS", "--no-experimental-fetch");
.env("TERM", std::env::var("TERM").unwrap_or_default());
if env::var("NINJA_STATUS").is_err() {
command.env("NINJA_STATUS", "[%f/%t; %r active; %es] ");
}

2
ninja
View File

@ -8,7 +8,7 @@ else
out="$BUILD_ROOT"
fi
export CARGO_TARGET_DIR=$out/rust
export RECONFIGURE_KEY="${MAC_X86};${SOURCEMAP}"
export RECONFIGURE_KEY="${MAC_X86};${SOURCEMAP};${HMR}"
if [ "$SKIP_RUNNER_BUILD" = "1" ]; then
echo "Runner not rebuilt."

View File

@ -5,14 +5,26 @@
"author": "Ankitects Pty Ltd and contributors",
"license": "AGPL-3.0-or-later",
"description": "Anki JS support files",
"scripts": {
"dev": "cd ts && vite dev",
"build": "cd ts && vite build",
"preview": "cd ts && vite preview",
"svelte-check:once": "cd ts && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --fail-on-warnings --threshold warning --compiler-warnings a11y-click-events-have-key-events:ignore,a11y-no-noninteractive-tabindex:ignore,a11y-no-static-element-interactions:ignore",
"svelte-check": "cd ts && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch --compiler-warnings a11y-click-events-have-key-events:ignore,a11y-no-noninteractive-tabindex:ignore,a11y-no-static-element-interactions:ignore",
"vitest:once": "cd ts && vitest run",
"vitest": "cd ts && vitest"
},
"devDependencies": {
"@bufbuild/protoc-gen-es": "^1.2.1",
"@bufbuild/protoc-gen-es": "^1.8.0",
"@sqltools/formatter": "^1.2.2",
"@sveltejs/adapter-static": "^3.0.0",
"@sveltejs/kit": "^2.4.1",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/bootstrap": "^5.0.12",
"@types/codemirror": "^5.60.0",
"@types/d3": "^7.0.0",
"@types/diff": "^5.0.0",
"@types/jest": "^27.0.2",
"@types/fabric": "^5.3.7",
"@types/jquery": "^3.5.0",
"@types/jqueryui": "^1.12.13",
"@types/lodash-es": "^4.17.4",
@ -32,8 +44,6 @@
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-svelte": "^2",
"jest-cli": "^28.0.0-alpha.5",
"jest-environment-jsdom": "^28.0.0-alpha.5",
"license-checker-rseidelsohn": "^4.2.6",
"prettier": "^2.4.1",
"prettier-plugin-svelte": "^2.10.1",
@ -43,7 +53,9 @@
"svelte-preprocess-esbuild": "^3.0.1",
"tslib": "^2.0.3",
"tsx": "^3.12.0",
"typescript": "^5.0.4"
"typescript": "^5.0.4",
"vite": "^5.0.12",
"vitest": "^1.2.1"
},
"dependencies": {
"@bufbuild/protobuf": "^1.2.1",
@ -74,5 +86,6 @@
"not < 1%",
"Chrome 77",
"iOS 14.5"
]
],
"type": "module"
}

View File

@ -248,6 +248,7 @@ is_win = sys.platform.startswith("win32")
# also covers *BSD
is_lin = not is_mac and not is_win
dev_mode = os.getenv("ANKIDEV", "")
hmr_mode = os.getenv("HMR", "")
INVALID_FILENAME_CHARS = ':*?"<>|'

View File

@ -3,7 +3,6 @@
from __future__ import annotations
import json
from typing import Callable
import aqt
@ -55,7 +54,7 @@ class CardInfoDialog(QDialog):
self.web = AnkiWebView(kind=AnkiWebViewKind.BROWSER_CARD_INFO)
self.web.setVisible(False)
self.web.load_ts_page("card-info")
self.web.load_sveltekit_page(f"card-info/{card_id}")
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.web)
@ -64,17 +63,13 @@ class CardInfoDialog(QDialog):
layout.addWidget(buttons)
qconnect(buttons.rejected, self.reject)
self.setLayout(layout)
self.web.eval("anki.cardInfoPromise = anki.setupCardInfo(document.body);")
self.update_card(card_id)
def update_card(self, card_id: CardId | None) -> None:
try:
self.mw.col.get_card(card_id)
except NotFoundError:
card_id = None
self.web.eval(
f"anki.cardInfoPromise.then((c) => c.updateStats({json.dumps(card_id)}));"
)
self.web.eval(f"window.location.href = '/card-info/{card_id}';")
def reject(self) -> None:
if self._on_close:

View File

@ -53,15 +53,12 @@ class ChangeNotetypeDialog(QDialog):
self.web = AnkiWebView(kind=AnkiWebViewKind.CHANGE_NOTETYPE)
self.web.setVisible(False)
self.web.load_ts_page("change-notetype")
self.web.load_sveltekit_page(f"change-notetype/{notetype_id}")
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.web)
self.setLayout(layout)
self.web.eval(
f"""anki.setupChangeNotetypePage({notetype_id}, {notetype_id});"""
)
self.setWindowTitle(tr.browsing_change_notetype())
def reject(self) -> None:

View File

@ -1,9 +1,9 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/root-vars";
@use "sass/vars" as *;
@use "sass/card-counts";
@use "sass/elevation" as *;
@use "../../../../../ts/lib/sass/root-vars";
@use "../../../../../ts/lib/sass/vars" as *;
@use "../../../../../ts/lib/sass/card-counts";
@use "../../../../../ts/lib/sass/elevation" as *;
table {
padding: 1rem;

View File

@ -1,10 +1,10 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/root-vars";
@use "sass/vars" as *;
@use "sass/card-counts";
@use "sass/button-mixins" as button;
@use "../../../../../ts/lib/sass/root-vars";
@use "../../../../../ts/lib/sass/vars" as *;
@use "../../../../../ts/lib/sass/card-counts";
@use "../../../../../ts/lib/sass/button-mixins" as button;
.smallLink {
font-size: 10px;

View File

@ -1,9 +1,9 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/root-vars";
@use "sass/vars" as *;
@use "sass/card-counts";
@use "../../../../../ts/lib/sass/root-vars";
@use "../../../../../ts/lib/sass/vars" as *;
@use "../../../../../ts/lib/sass/card-counts";
:root {
--focus-color: #{palette-of(border-focus)};

View File

@ -1,10 +1,10 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/root-vars";
@use "sass/vars" as *;
@use "sass/elevation" as *;
@use "sass/button-mixins" as button;
@use "../../../../../ts/lib/sass/root-vars";
@use "../../../../../ts/lib/sass/vars" as *;
@use "../../../../../ts/lib/sass/elevation" as *;
@use "../../../../../ts/lib/sass/button-mixins" as button;
.header {
display: grid;

View File

@ -1,9 +1,9 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/root-vars";
@use "sass/scrollbar";
@use "sass/buttons";
@use "../../../../../ts/lib/sass/root-vars";
@use "../../../../../ts/lib/sass/scrollbar";
@use "../../../../../ts/lib/sass/buttons";
* {
// border-box would be better, but we need to

View File

@ -1,5 +1,5 @@
{
"extends": "../../../../../ts/tsconfig.json",
"extends": "../../../../../ts/tsconfig_legacy.json",
"include": ["*.ts"],
"references": [],
"compilerOptions": {

View File

@ -43,17 +43,13 @@ class DeckOptionsDialog(QDialog):
addCloseShortcut(self)
self.web = AnkiWebView(kind=AnkiWebViewKind.DECK_OPTIONS)
self.web.load_ts_page("deck-options")
self.web.load_sveltekit_page(f"deck-options/{self._deck['id']}")
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.web)
self.setLayout(layout)
self.show()
self.web.hide_while_preserving_layout()
self.web.eval(
f"""const $deckOptions = anki.setupDeckOptions({self._deck["id"]});"""
)
self.setWindowTitle(
without_unicode_isolation(tr.actions_options_for(val=self._deck["name"]))
)

View File

@ -21,7 +21,6 @@ class ImportArgs:
title = "importLog"
kind = AnkiWebViewKind.IMPORT_LOG
ts_page = "import-page"
setup_function_name = "setupImportPage"
def args_json(self) -> str:
return json.dumps(self.path)
@ -32,26 +31,16 @@ class JsonFileArgs(ImportArgs):
return json.dumps(dict(type="json_file", path=self.path))
@dataclass
class JsonStringArgs(ImportArgs):
json: str
def args_json(self) -> str:
return json.dumps(dict(type="json_string", path=self.path, json=self.json))
class CsvArgs(ImportArgs):
title = "csv import"
kind = AnkiWebViewKind.IMPORT_CSV
ts_page = "import-csv"
setup_function_name = "setupImportCsvPage"
class AnkiPackageArgs(ImportArgs):
title = "anki package import"
kind = AnkiWebViewKind.IMPORT_ANKI_PACKAGE
ts_page = "import-anki-package"
setup_function_name = "setupImportAnkiPackagePage"
class ImportDialog(QDialog):
@ -76,17 +65,13 @@ class ImportDialog(QDialog):
self.web = AnkiWebView(kind=self.args.kind)
self.web.setVisible(False)
self.web.load_ts_page(self.args.ts_page)
self.web.load_sveltekit_page(f"{self.args.ts_page}/{self.args.path}")
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.web)
self.setLayout(layout)
restoreGeom(self, self.args.title, default_size=(800, 800))
self.web.evalWithCallback(
f"anki.{self.args.setup_function_name}({self.args.args_json()});",
lambda _: self.web.setFocus(),
)
self.setWindowTitle(tr.decks_import_file())
def reject(self) -> None:

View File

@ -13,12 +13,12 @@ from anki.collection import Collection, Progress
from anki.errors import Interrupted
from anki.foreign_data import mnemosyne
from anki.lang import without_unicode_isolation
from anki.utils import tmpdir
from aqt.import_export.import_dialog import (
AnkiPackageArgs,
CsvArgs,
ImportDialog,
JsonFileArgs,
JsonStringArgs,
)
from aqt.operations import QueryOp
from aqt.progress import ProgressUpdate
@ -95,10 +95,16 @@ class MnemosyneImporter(Importer):
@staticmethod
def do_import(mw: aqt.main.AnkiQt, path: str) -> None:
def on_success(json: str) -> None:
json_path = os.path.join(tmpdir(), path)
with open(json_path, "wb") as file:
file.write(json.encode("utf8"))
ImportDialog(mw, JsonFileArgs(path=json_path))
QueryOp(
parent=mw,
op=lambda col: mnemosyne.serialize(path, col.decks.current()["id"]),
success=lambda json: ImportDialog(mw, JsonStringArgs(path=path, json=json)),
success=on_success,
).with_progress().run_in_background()

View File

@ -109,6 +109,7 @@ class MainWebView(AnkiWebView):
self.setFocusPolicy(Qt.FocusPolicy.WheelFocus)
self.setMinimumWidth(400)
self.setAcceptDrops(True)
print("todo: windows paths in import screen")
# Importing files via drag & drop
##########################################################################

View File

@ -166,7 +166,7 @@ def _mime_for_path(path: str) -> str:
if path.endswith(".css"):
# some users may have invalid mime type in the Windows registry
return "text/css"
elif path.endswith(".js"):
elif path.endswith(".js") or path.endswith(".mjs"):
return "application/javascript"
else:
# autodetect
@ -255,11 +255,18 @@ def _builtin_data(path: str) -> bytes:
def _handle_builtin_file_request(request: BundledFileRequest) -> Response:
path = request.path
# do we need to serve the fallback page?
immutable = "immutable" in path
if path.startswith("sveltekit/") and not immutable:
path = "sveltekit/index.html"
mimetype = _mime_for_path(path)
data_path = f"data/web/{path}"
try:
data = _builtin_data(data_path)
return Response(data, mimetype=mimetype)
response = Response(data, mimetype=mimetype)
if immutable:
response.headers["Cache-Control"] = "max-age=31536000"
return response
except FileNotFoundError:
if dev_mode:
print(f"404: {data_path}")
@ -316,10 +323,29 @@ def handle_request(pathin: str) -> Response:
)
def is_sveltekit_page(path: str) -> bool:
page_name = path.split("/")[0]
return page_name in [
"graphs",
"congrats",
"card-info",
"change-notetype",
"deck-options",
"import-anki-package",
"import-csv",
"import-page",
]
def _extract_internal_request(
path: str,
) -> BundledFileRequest | DynamicRequest | NotFound | None:
"Catch /_anki references and rewrite them to web export folder."
if is_sveltekit_page(path):
path = f"_anki/sveltekit/_app/{path}"
if path.startswith("_app/"):
path = path.replace("_app", "_anki/sveltekit/_app")
prefix = "_anki/"
if not path.startswith(prefix):
return None
@ -662,6 +688,7 @@ def _check_dynamic_request_permissions():
context == PageContext.NON_LEGACY_PAGE
or context == PageContext.EDITOR
or context == PageContext.ADDON_PAGE
or os.environ.get("ANKI_API_PORT")
):
pass
elif context == PageContext.REVIEWER and request.path in (
@ -698,7 +725,7 @@ def _extract_page_context() -> PageContext:
from urllib.parse import parse_qs, urlparse
referer = urlparse(request.headers.get("Referer", ""))
if referer.path.startswith("/_anki/pages/"):
if referer.path.startswith("/_anki/pages/") or is_sveltekit_page(referer.path[1:]):
return PageContext.NON_LEGACY_PAGE
elif referer.path == "/_anki/legacyPageData":
query_params = parse_qs(referer.query)

View File

@ -204,7 +204,7 @@ class Overview:
)
def _show_finished_screen(self) -> None:
self.web.load_ts_page("congrats")
self.web.load_sveltekit_page("congrats")
def _desc(self, deck: dict[str, Any]) -> str:
if deck["dyn"]:

View File

@ -134,7 +134,7 @@ class NewDeckStats(QDialog):
return False
def refresh(self) -> None:
self.form.web.load_ts_page("graphs")
self.form.web.load_sveltekit_page("graphs")
class DeckStats(QDialog):

View File

@ -14,7 +14,7 @@ import anki
import anki.lang
from anki._legacy import deprecated
from anki.lang import is_rtl
from anki.utils import is_lin, is_mac, is_win
from anki.utils import hmr_mode, is_lin, is_mac, is_win
from aqt import colors, gui_hooks
from aqt.qt import *
from aqt.theme import theme_manager
@ -129,10 +129,13 @@ class AnkiWebPage(QWebEnginePage):
def acceptNavigationRequest(
self, url: QUrl, navType: Any, isMainFrame: bool
) -> bool:
from aqt.mediasrv import is_sveltekit_page
if (
not self.open_links_externally
or "_anki/pages" in url.path()
or url.path() == "/_anki/legacyPageData"
or is_sveltekit_page(url.path()[1:])
):
return super().acceptNavigationRequest(url, navType, isMainFrame)
@ -758,6 +761,23 @@ html {{ {font} }}
self.load_url(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html{extra}"))
self.add_dynamic_styling_and_props_then_show()
def load_sveltekit_page(self, path: str) -> None:
from aqt import mw
self.set_open_links_externally(True)
if theme_manager.night_mode:
extra = "#night"
else:
extra = ""
if hmr_mode:
server = "http://127.0.0.1:5173/"
else:
server = mw.serverURL()
self.load_url(QUrl(f"{server}{path}{extra}"))
self.add_dynamic_styling_and_props_then_show()
def force_load_hack(self) -> None:
"""Force process to initialize.
Must be done on Windows prior to changing current working directory."""

View File

@ -288,9 +288,16 @@ fn get_modules(langs: &[LanguageIdentifier], desired_modules: &[String]) -> Vec<
let mut buf = String::new();
let lang_name = remapped_lang_name(&lang);
if let Some(strings) = STRINGS.get(lang_name) {
for module_name in desired_modules {
if let Some(text) = strings.get(module_name.as_str()) {
buf.push_str(text);
if desired_modules.is_empty() {
// empty list, provide all modules
for value in strings.values() {
buf.push_str(value)
}
} else {
for module_name in desired_modules {
if let Some(text) = strings.get(module_name.as_str()) {
buf.push_str(text);
}
}
}
}

View File

@ -16,41 +16,32 @@ use crate::extract::Variable;
use crate::extract::VariableKind;
pub fn write_ts_interface(modules: &[Module]) -> Result<()> {
let mut dts_out = header();
let mut js_out = header();
write_translate_method(&mut js_out);
dts_out.push_str("export declare const funcs: any;\n");
let mut ts_out = header();
write_imports(&mut ts_out);
render_module_map(modules, &mut dts_out, &mut js_out);
render_methods(modules, &mut dts_out, &mut js_out);
render_module_map(modules, &mut ts_out);
render_methods(modules, &mut ts_out);
if let Ok(path) = env::var("STRINGS_JS") {
if let Ok(path) = env::var("STRINGS_TS") {
let path = PathBuf::from(path);
create_dir_all(path.parent().unwrap())?;
write_file_if_changed(path, js_out)?;
}
if let Ok(path) = env::var("STRINGS_DTS") {
let path = PathBuf::from(path);
create_dir_all(path.parent().unwrap())?;
write_file_if_changed(path, dts_out)?;
write_file_if_changed(path, ts_out)?;
}
Ok(())
}
fn render_module_map(modules: &[Module], dts_out: &mut String, js_out: &mut String) {
dts_out.push_str("export declare enum ModuleName {\n");
js_out.push_str("export const ModuleName = {};\n");
fn render_module_map(modules: &[Module], ts_out: &mut String) {
ts_out.push_str("export enum ModuleName {\n");
for module in modules {
let name = &module.name;
let upper = name.to_upper_case();
writeln!(dts_out, r#" {upper} = "{name}","#).unwrap();
writeln!(js_out, r#"ModuleName["{upper}"] = "{name}";"#).unwrap();
writeln!(ts_out, r#" {upper} = "{name}","#).unwrap();
}
dts_out.push('}');
ts_out.push('}');
}
fn render_methods(modules: &[Module], dts_out: &mut String, js_out: &mut String) {
fn render_methods(modules: &[Module], ts_out: &mut String) {
for module in modules {
for translation in &module.translations {
let text = &translation.text;
@ -59,20 +50,14 @@ fn render_methods(modules: &[Module], dts_out: &mut String, js_out: &mut String)
let arg_types = get_arg_types(&translation.variables);
let args = get_args(&translation.variables);
let maybe_args = if translation.variables.is_empty() {
""
"".to_string()
} else {
"args"
arg_types
};
writeln!(
dts_out,
"
/** {text} */
export declare function {func_name}({arg_types}): string;",
)
.unwrap();
writeln!(
js_out,
ts_out,
r#"
/** {text} */
export function {func_name}({maybe_args}) {{
return translate("{key}", {args})
}}"#,
@ -101,23 +86,15 @@ fn get_args(variables: &[Variable]) -> String {
}
fn typescript_arg_name(name: &str) -> String {
let name = name.replace('-', "_").to_camel_case();
if name == "new" {
"new_".into()
} else {
name
}
name.replace('-', "_").to_camel_case()
}
fn write_translate_method(buf: &mut String) {
fn write_imports(buf: &mut String) {
buf.push_str(
"
// tslib is responsible for injecting getMessage helper in
export const funcs = {};
function translate(key, args = {}) {
return funcs.getMessage(key, args) ?? `missing key: ${key}`;
}
import { translate } from './ftl-helpers';
export { firstLanguage, setBundles } from './ftl-helpers';
export { FluentBundle, FluentResource } from '@fluent/bundle';
",
);
}

View File

@ -74,7 +74,7 @@ fn ts_help_pages() -> impl Iterator<Item = &'static str> {
static ref QUOTED_URL: Regex = Regex::new("\"(http.+)\"").unwrap();
}
QUOTED_URL
.captures_iter(include_str!("../../../ts/lib/help-page.ts"))
.captures_iter(include_str!("../../../ts/lib/tslib/help-page.ts"))
.map(|caps| caps.get(1).unwrap().as_str())
}

View File

@ -14,11 +14,10 @@ use inflections::Inflect;
use itertools::Itertools;
pub(crate) fn write_ts_interface(services: &[BackendService]) -> Result<()> {
let root = Path::new("../../out/ts/lib");
let root = Path::new("../../out/ts/lib/generated");
create_dir_all(root)?;
let mut dts_out = String::new();
let mut js_out = String::new();
let mut ts_out = String::new();
let mut referenced_packages = HashSet::new();
for service in services {
@ -30,30 +29,26 @@ pub(crate) fn write_ts_interface(services: &[BackendService]) -> Result<()> {
let method = MethodDetails::from_method(method);
record_referenced_type(&mut referenced_packages, &method.input_type);
record_referenced_type(&mut referenced_packages, &method.output_type);
write_dts_method(&method, &mut dts_out);
write_js_method(&method, &mut js_out);
write_ts_method(&method, &mut ts_out);
}
}
let imports = imports(referenced_packages);
write_file_if_changed(
root.join("backend.d.ts"),
format!("{}{}{}", dts_header(), imports, dts_out),
)?;
write_file_if_changed(
root.join("backend.js"),
format!("{}{}{}", js_header(), imports, js_out),
root.join("backend.ts"),
format!("{}{}{}", ts_header(), imports, ts_out),
)?;
Ok(())
}
fn dts_header() -> String {
fn ts_header() -> String {
r#"// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; https://www.gnu.org/licenses/agpl.html
import type { PlainMessage } from "@bufbuild/protobuf";
import type { PostProtoOptions } from "./post";
import { postProto } from "./post";
"#
.into()
}
@ -72,7 +67,7 @@ fn imports(referenced_packages: HashSet<String>) -> String {
out
}
fn write_dts_method(
fn write_ts_method(
MethodDetails {
method_name,
input_type,
@ -84,38 +79,12 @@ fn write_dts_method(
let comments = format_comments(comments);
writeln!(
out,
r#"{comments}export declare function {method_name}(input: PlainMessage<{input_type}>, options?: PostProtoOptions): Promise<{output_type}>;"#
r#"{comments}export async function {method_name}(input: PlainMessage<{input_type}>, options?: PostProtoOptions): Promise<{output_type}> {{
return await postProto("{method_name}", new {input_type}(input), {output_type}, options);
}}"#
).unwrap()
}
fn js_header() -> String {
r#"// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; https://www.gnu.org/licenses/agpl.html
import { postProto } from "./post";
"#
.into()
}
fn write_js_method(
MethodDetails {
method_name,
input_type,
output_type,
..
}: &MethodDetails,
out: &mut String,
) {
write!(
out,
r#"export async function {method_name}(input, options = {{}}) {{
return await postProto("{method_name}", new {input_type}(input), {output_type}, options);
}}
"#
)
.unwrap();
}
fn format_comments(comments: &Option<String>) -> String {
comments
.as_ref()

View File

@ -42,6 +42,7 @@ const IGNORED_FOLDERS: &[&str] = &[
"./target",
".mypy_cache",
"./extra",
"./ts/.svelte-kit",
];
fn main() -> Result<()> {
@ -90,7 +91,7 @@ impl LintContext {
.into_iter()
.map(Some)
.collect();
if exts.contains(&path.extension()) {
if exts.contains(&path.extension()) && !sveltekit_temp_file(path.as_str()) {
self.check_copyright(path)?;
self.check_triple_slash(path)?;
}
@ -202,6 +203,11 @@ impl LintContext {
}
}
/// Annoyingly, sveltekit writes temp files into ts/ folder when it's running.
fn sveltekit_temp_file(path: &str) -> bool {
path.contains("vite.config.ts.timestamp")
}
fn check_cargo_deny() -> Result<()> {
Command::run("cargo install cargo-deny@0.14.12")?;
Command::run("cargo deny check")?;

View File

@ -31,7 +31,7 @@ if (!sourcemap) {
define: {
"process.browser": "true",
},
tsconfig: "ts/tsconfig.json",
tsconfig: "ts/tsconfig_legacy.json",
}),
sveltePreprocess({ typescript: false }),
];
@ -52,7 +52,7 @@ build({
preserveSymlinks: true,
sourcemap: sourcemap ? "inline" : false,
plugins: [
sassPlugin({ loadPaths: [".", "node_modules"] }),
sassPlugin({ loadPaths: ["node_modules"] }),
sveltePlugin({
compilerOptions: { css: inlineCss ? "injected" : "external" },
preprocess: sveltePlugins,

View File

@ -1,59 +0,0 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import type {
CardStatsResponse,
CardStatsResponse_StatsRevlogEntry,
} from "@tslib/anki/stats_pb";
import { cardStats } from "@tslib/backend";
import Container from "../components/Container.svelte";
import Row from "../components/Row.svelte";
import CardInfoPlaceholder from "./CardInfoPlaceholder.svelte";
import CardStats from "./CardStats.svelte";
import Revlog from "./Revlog.svelte";
export let includeRevlog: boolean = true;
let stats: CardStatsResponse | null = null;
let revlog: CardStatsResponse_StatsRevlogEntry[] | null = null;
export async function updateStats(cardId: bigint | null): Promise<void> {
const requestedCardId = cardId;
if (cardId === null) {
stats = null;
revlog = null;
return;
}
const updatedStats = await cardStats({ cid: cardId });
/* Skip if another update has been triggered in the meantime. */
if (requestedCardId === cardId) {
stats = updatedStats;
if (includeRevlog) {
revlog = stats.revlog;
}
}
}
</script>
<Container breakpoint="md" --gutter-inline="1rem" --gutter-block="0.5rem">
{#if stats}
<Row>
<CardStats {stats} />
</Row>
{#if revlog}
<Row>
<Revlog {revlog} />
</Row>
{/if}
{:else}
<CardInfoPlaceholder />
{/if}
</Container>

View File

@ -1 +0,0 @@
@import "sass/base";

View File

@ -1,5 +0,0 @@
{
"extends": "../tsconfig.json",
"include": ["*"],
"references": [{ "path": "../lib" }]
}

View File

@ -1,10 +0,0 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/// <reference types="../lib/image-import" />
export { default as arrowLeftIcon } from "bootstrap-icons/icons/arrow-left.svg";
export { default as arrowRightIcon } from "bootstrap-icons/icons/arrow-right.svg";
export { default as minusIcon } from "bootstrap-icons/icons/dash-lg.svg";
export { default as exclamationIcon } from "bootstrap-icons/icons/exclamation-circle.svg";
export { default as plusIcon } from "bootstrap-icons/icons/plus-lg.svg";

View File

@ -1,12 +0,0 @@
{
"extends": "../tsconfig.json",
"include": ["*"],
"references": [
{ "path": "../lib" },
{ "path": "../sveltelib" },
{ "path": "../components" }
],
"compilerOptions": {
"types": ["jest"]
}
}

View File

@ -1,15 +0,0 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/// <reference types="../lib/image-import" />
export { default as hsplitIcon } from "@mdi/svg/svg/arrow-split-horizontal.svg";
export { default as vsplitIcon } from "@mdi/svg/svg/arrow-split-vertical.svg";
export { default as chevronDown } from "@mdi/svg/svg/chevron-down.svg";
export { default as chevronLeft } from "@mdi/svg/svg/chevron-left.svg";
export { default as chevronRight } from "@mdi/svg/svg/chevron-right.svg";
export { default as chevronUp } from "@mdi/svg/svg/chevron-up.svg";
export { default as horizontalHandle } from "@mdi/svg/svg/drag-horizontal.svg";
export { default as verticalHandle } from "@mdi/svg/svg/drag-vertical.svg";
export { default as infoCircle } from "@mdi/svg/svg/help-circle.svg";
export { default as revertIcon } from "bootstrap-icons/icons/arrow-counterclockwise.svg";

View File

@ -1,5 +0,0 @@
{
"extends": "../tsconfig.json",
"include": ["*"],
"references": [{ "path": "../lib" }, { "path": "../sveltelib" }]
}

View File

@ -1,8 +0,0 @@
{
"extends": "../tsconfig.json",
"include": ["*"],
"references": [{ "path": "../lib" }],
"compilerOptions": {
"types": ["node"]
}
}

View File

@ -1,7 +0,0 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/// <reference types="../lib/image-import" />
export { default as chevronDown } from "@mdi/svg/svg/chevron-down.svg";
export { default as gearIcon } from "bootstrap-icons/icons/gear.svg";

View File

@ -1,12 +0,0 @@
{
"extends": "../tsconfig.json",
"include": ["*"],
"references": [
{ "path": "@tslib" },
{ "path": "../sveltelib" },
{ "path": "../components" }
],
"compilerOptions": {
"types": ["jest"]
}
}

View File

@ -1,15 +0,0 @@
{
"extends": "../tsconfig.json",
"include": [
"*",
"location/*",
"surround/*",
"surround/apply/*",
"surround/build/*",
"surround/tree/*"
],
"references": [{ "path": "../lib" }],
"compilerOptions": {
"types": ["jest"]
}
}

View File

@ -9,10 +9,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts">
import type { Writable } from "svelte/store";
import { updateAllState } from "../components/WithState.svelte";
import actionList from "../sveltelib/action-list";
import type { MirrorAction } from "../sveltelib/dom-mirror";
import type { SetupInputHandlerAction } from "../sveltelib/input-handler";
import { updateAllState } from "$lib/components/WithState.svelte";
import actionList from "$lib/sveltelib/action-list";
import type { MirrorAction } from "$lib/sveltelib/dom-mirror";
import type { SetupInputHandlerAction } from "$lib/sveltelib/input-handler";
import type { ContentEditableAPI } from "./content-editable";
import {
fixRTLKeyboardNav,

View File

@ -22,7 +22,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { onDestroy } from "svelte";
import { writable } from "svelte/store";
import { pageTheme } from "../sveltelib/theme";
import { pageTheme } from "$lib/sveltelib/theme";
import { convertMathjax, unescapeSomeEntities } from "./mathjax";
export let mathjax: string;

View File

@ -8,10 +8,10 @@ import { isApplePlatform } from "@tslib/platform";
import { registerShortcut } from "@tslib/shortcuts";
import type { Callback } from "@tslib/typing";
import type { SelectionLocation } from "../domlib/location";
import { restoreSelection, saveSelection } from "../domlib/location";
import { placeCaretAfterContent } from "../domlib/place-caret";
import { HandlerList } from "../sveltelib/handler-list";
import type { SelectionLocation } from "$lib/domlib/location";
import { restoreSelection, saveSelection } from "$lib/domlib/location";
import { placeCaretAfterContent } from "$lib/domlib/place-caret";
import { HandlerList } from "$lib/sveltelib/handler-list";
/**
* Workaround: If you try to invoke an IME after calling

View File

@ -1,4 +1,4 @@
@use "sass/scrollbar";
@use "../lib/sass/scrollbar";
* {
max-width: 100%;

View File

@ -5,8 +5,9 @@ import { getSelection, isSelectionCollapsed } from "@tslib/cross-browser";
import { elementIsBlock, hasBlockAttribute, nodeIsElement, nodeIsText } from "@tslib/dom";
import { on } from "@tslib/events";
import { moveChildOutOfElement } from "../domlib/move-nodes";
import { placeCaretAfter, placeCaretBefore } from "../domlib/place-caret";
import { moveChildOutOfElement } from "$lib/domlib/move-nodes";
import { placeCaretAfter, placeCaretBefore } from "$lib/domlib/place-caret";
import type { FrameHandle } from "./frame-handle";
import { checkHandles, frameElementTagName, FrameEnd, FrameStart, isFrameHandle } from "./frame-handle";

View File

@ -7,9 +7,10 @@ import { on } from "@tslib/events";
import type { Unsubscriber } from "svelte/store";
import { get } from "svelte/store";
import { moveChildOutOfElement } from "../domlib/move-nodes";
import { placeCaretAfter } from "../domlib/place-caret";
import { isComposing } from "../sveltelib/composition";
import { moveChildOutOfElement } from "$lib/domlib/move-nodes";
import { placeCaretAfter } from "$lib/domlib/place-caret";
import { isComposing } from "$lib/sveltelib/composition";
import type { FrameElement } from "./frame-element";
/**

View File

@ -1,6 +1,4 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/// <reference types="../lib/image-import" />
export { default as mathIcon } from "@mdi/svg/svg/math-integral-box.svg";

View File

@ -3,7 +3,8 @@
import { on } from "@tslib/events";
import { placeCaretAfter, placeCaretBefore } from "../domlib/place-caret";
import { placeCaretAfter, placeCaretBefore } from "$lib/domlib/place-caret";
import type { DecoratedElement, DecoratedElementConstructor } from "./decorated";
import { FrameElement, frameElement } from "./frame-element";
import Mathjax_svelte from "./Mathjax.svelte";

View File

@ -1,11 +0,0 @@
{
"extends": "../tsconfig.json",
"include": ["*"],
"references": [
{ "path": "../components" },
{ "path": "../lib" },
{ "path": "../domlib" },
{ "path": "../sveltelib" },
{ "path": "../mathjax" }
]
}

View File

@ -3,7 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import ButtonGroupItem from "../components/ButtonGroupItem.svelte";
import ButtonGroupItem from "$lib/components/ButtonGroupItem.svelte";
import type { NoteEditorAPI } from "./NoteEditor.svelte";
import NoteEditor from "./NoteEditor.svelte";
import PreviewButton from "./PreviewButton.svelte";

View File

@ -3,15 +3,16 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { isApplePlatform } from "@tslib/platform";
import { getPlatformString } from "@tslib/shortcuts";
import { createEventDispatcher } from "svelte";
import { get } from "svelte/store";
import ButtonGroup from "../components/ButtonGroup.svelte";
import IconButton from "../components/IconButton.svelte";
import Shortcut from "../components/Shortcut.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import { clozeIcon, incrementClozeIcon } from "./icons";
import { context as noteEditorContext } from "./NoteEditor.svelte";
import { editingInputIsRichText } from "./rich-text-input";

View File

@ -20,7 +20,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { createEventDispatcher, getContext, onMount } from "svelte";
import type { Writable } from "svelte/store";
import { pageTheme } from "../sveltelib/theme";
import { pageTheme } from "$lib/sveltelib/theme";
import {
darkTheme,
lightTheme,

View File

@ -3,7 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import Badge from "../components/Badge.svelte";
import Badge from "$lib/components/Badge.svelte";
import { chevronDown } from "./icons";
export let collapsed = false;

View File

@ -3,8 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
</script>
<span class="duplicate-link-container">

View File

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script context="module" lang="ts">
import type { Writable } from "svelte/store";
import contextProperty from "../sveltelib/context-property";
import contextProperty from "$lib/sveltelib/context-property";
export interface FocusableInputAPI {
readonly name: string;

View File

@ -26,8 +26,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { registerPackage } from "@tslib/runtime-require";
import contextProperty from "../sveltelib/context-property";
import lifecycleHooks from "../sveltelib/lifecycle-hooks";
import contextProperty from "$lib/sveltelib/context-property";
import lifecycleHooks from "$lib/sveltelib/lifecycle-hooks";
const key = Symbol("editorField");
const [context, setContextProperty] = contextProperty<EditorFieldAPI>(key);
@ -50,7 +50,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { Writable } from "svelte/store";
import { writable } from "svelte/store";
import Collapsible from "../components/Collapsible.svelte";
import Collapsible from "$lib/components/Collapsible.svelte";
import type { Destroyable } from "./destroyable";
import EditingArea from "./EditingArea.svelte";
@ -119,7 +120,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</div>
<style lang="scss">
@use "sass/elevation" as *;
@use "../lib/sass/elevation" as *;
/* Make sure labels are readable on custom Qt backgrounds */
.field-container {

View File

@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import ScrollArea from "components/ScrollArea.svelte";
import ScrollArea from "$lib/components/ScrollArea.svelte";
</script>
<!--

View File

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { pageTheme } from "../sveltelib/theme";
import { pageTheme } from "$lib/sveltelib/theme";
export let offsetX = 0;
export let offsetY = 0;

View File

@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import CollapseLabel from "./CollapseLabel.svelte";

View File

@ -5,7 +5,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script context="module" lang="ts">
import type { Writable } from "svelte/store";
import Collapsible from "../components/Collapsible.svelte";
import Collapsible from "$lib/components/Collapsible.svelte";
import type { EditingInputAPI } from "./EditingArea.svelte";
import type { EditorToolbarAPI } from "./editor-toolbar";
import type { EditorFieldAPI } from "./EditorField.svelte";
@ -23,8 +24,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { registerPackage } from "@tslib/runtime-require";
import contextProperty from "../sveltelib/context-property";
import lifecycleHooks from "../sveltelib/lifecycle-hooks";
import contextProperty from "$lib/sveltelib/context-property";
import lifecycleHooks from "$lib/sveltelib/lifecycle-hooks";
const key = Symbol("noteEditor");
const [context, setContextProperty] = contextProperty<NoteEditorAPI>(key);
@ -40,16 +41,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script>
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { type ImageLoadedEvent, resetIOImage } from "image-occlusion/mask-editor";
import { onMount, tick } from "svelte";
import { get, writable } from "svelte/store";
import Absolute from "../components/Absolute.svelte";
import Badge from "../components/Badge.svelte";
import { TagEditor } from "../tag-editor";
import { commitTagEdits } from "../tag-editor/TagInput.svelte";
import Absolute from "$lib/components/Absolute.svelte";
import Badge from "$lib/components/Badge.svelte";
import { TagEditor } from "$lib/tag-editor";
import { commitTagEdits } from "$lib/tag-editor/TagInput.svelte";
import {
type ImageLoadedEvent,
resetIOImage,
} from "../routes/image-occlusion/mask-editor";
import { ChangeTimer } from "./change-timer";
import { clearableArray } from "./destroyable";
import DuplicateLink from "./DuplicateLink.svelte";
@ -385,21 +390,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
});
}
import { ImageOcclusionFieldIndexes } from "@tslib/anki/image_occlusion_pb";
import { getImageOcclusionFields } from "@tslib/backend";
import { ImageOcclusionFieldIndexes } from "@generated/anki/image_occlusion_pb";
import { getImageOcclusionFields } from "@generated/backend";
import { wrapInternal } from "@tslib/wrap";
import Shortcut from "components/Shortcut.svelte";
import ImageOcclusionPage from "image-occlusion/ImageOcclusionPage.svelte";
import ImageOcclusionPicker from "image-occlusion/ImageOcclusionPicker.svelte";
import type { IOMode } from "image-occlusion/lib";
import { exportShapesToClozeDeletions } from "image-occlusion/shapes/to-cloze";
import Shortcut from "$lib/components/Shortcut.svelte";
import { mathjaxConfig } from "../editable/mathjax-element";
import ImageOcclusionPage from "../routes/image-occlusion/ImageOcclusionPage.svelte";
import ImageOcclusionPicker from "../routes/image-occlusion/ImageOcclusionPicker.svelte";
import type { IOMode } from "../routes/image-occlusion/lib";
import { exportShapesToClozeDeletions } from "../routes/image-occlusion/shapes/to-cloze";
import {
hideAllGuessOne,
ioImageLoadedStore,
ioMaskEditorVisible,
} from "image-occlusion/store";
import { mathjaxConfig } from "../editable/mathjax-element";
} from "../routes/image-occlusion/store";
import CollapseLabel from "./CollapseLabel.svelte";
import * as oldEditorAdapter from "./old-editor-adapter";

View File

@ -3,11 +3,12 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { getPlatformString, registerShortcut } from "@tslib/shortcuts";
import { createEventDispatcher, onDestroy } from "svelte";
import Badge from "../components/Badge.svelte";
import Badge from "$lib/components/Badge.svelte";
import { context as editorFieldContext } from "./EditorField.svelte";
import { plainTextIcon } from "./icons";

View File

@ -15,12 +15,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script>
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { getPlatformString } from "@tslib/shortcuts";
import LabelButton from "../components/LabelButton.svelte";
import Shortcut from "../components/Shortcut.svelte";
import LabelButton from "$lib/components/LabelButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
const keyCombination = "Control+Shift+P";
function preview(): void {

View File

@ -3,11 +3,12 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { getPlatformString, registerShortcut } from "@tslib/shortcuts";
import { createEventDispatcher, onDestroy } from "svelte";
import Badge from "../components/Badge.svelte";
import Badge from "$lib/components/Badge.svelte";
import { context as editorFieldContext } from "./EditorField.svelte";
import { richTextIcon } from "./icons";

View File

@ -3,12 +3,13 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { getPlatformString, registerShortcut } from "@tslib/shortcuts";
import { onMount } from "svelte";
import Badge from "../components/Badge.svelte";
import Badge from "$lib/components/Badge.svelte";
import { context as editorFieldContext } from "./EditorField.svelte";
import { stickyIcon } from "./icons";

View File

@ -8,16 +8,17 @@
import "./legacy.scss";
import "./editor-base.scss";
import "@tslib/runtime-require";
import "../sveltelib/export-runtime";
import "$lib/sveltelib/export-runtime";
import { setupI18n } from "@tslib/i18n";
import { uiResolve } from "@tslib/ui";
import * as contextKeys from "../components/context-keys";
import IconButton from "../components/IconButton.svelte";
import LabelButton from "../components/LabelButton.svelte";
import WithContext from "../components/WithContext.svelte";
import WithState from "../components/WithState.svelte";
import * as contextKeys from "$lib/components/context-keys";
import IconButton from "$lib/components/IconButton.svelte";
import LabelButton from "$lib/components/LabelButton.svelte";
import WithContext from "$lib/components/WithContext.svelte";
import WithState from "$lib/components/WithState.svelte";
import BrowserEditor from "./BrowserEditor.svelte";
import NoteCreator from "./NoteCreator.svelte";
import * as editorContextKeys from "./NoteEditor.svelte";

View File

@ -12,7 +12,7 @@ export class ChangeTimer {
schedule(action: () => void, delay: number): void {
this.clear();
this.action = action;
this.value = setTimeout(this.fireImmediately, delay);
this.value = setTimeout(this.fireImmediately, delay) as any;
}
clear(): void {

View File

@ -16,7 +16,7 @@ import "codemirror/addon/display/placeholder";
import CodeMirror from "codemirror";
import type { Readable } from "svelte/store";
import storeSubscribe from "../sveltelib/store-subscribe";
import storeSubscribe from "$lib/sveltelib/store-subscribe";
export { CodeMirror };

View File

@ -1,12 +1,12 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@import "sass/base";
@import "../lib/sass/base";
$btn-disabled-opacity: 0.4;
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/button-group";
@import "sass/bootstrap-tooltip";
@import "../lib/sass/bootstrap-tooltip";
html,
body {

View File

@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import ButtonGroup from "../../components/ButtonGroup.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
export let buttons: string[];

View File

@ -3,24 +3,25 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { getListItem } from "@tslib/dom";
import { preventDefault } from "@tslib/events";
import * as tr from "@tslib/ftl";
import { getPlatformString, registerShortcut } from "@tslib/shortcuts";
import { onMount } from "svelte";
import ButtonGroup from "../../components/ButtonGroup.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import ButtonGroupItem, {
createProps,
setSlotHostContext,
updatePropsList,
} from "../../components/ButtonGroupItem.svelte";
import ButtonToolbar from "../../components/ButtonToolbar.svelte";
import DynamicallySlottable from "../../components/DynamicallySlottable.svelte";
import IconButton from "../../components/IconButton.svelte";
import Popover from "../../components/Popover.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import { execCommand } from "../../domlib";
} from "$lib/components/ButtonGroupItem.svelte";
import ButtonToolbar from "$lib/components/ButtonToolbar.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import Popover from "$lib/components/Popover.svelte";
import WithFloating from "$lib/components/WithFloating.svelte";
import { execCommand } from "$lib/domlib";
import { context } from "../NoteEditor.svelte";
import { editingInputIsRichText } from "../rich-text-input";
import CommandIconButton from "./CommandIconButton.svelte";

View File

@ -3,10 +3,11 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { removeStyleProperties } from "@tslib/styling";
import type { MatchType } from "../../domlib/surround";
import type { MatchType } from "$lib/domlib/surround";
import { boldIcon } from "./icons";
import TextAttributeButton from "./TextAttributeButton.svelte";

View File

@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import Shortcut from "../../components/Shortcut.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
export let keyCombination: string | null = null;
export let value: string;

View File

@ -5,11 +5,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts">
import { getPlatformString } from "@tslib/shortcuts";
import IconButton from "../../components/IconButton.svelte";
import Shortcut from "../../components/Shortcut.svelte";
import WithState from "../../components/WithState.svelte";
import { updateStateByKey } from "../../components/WithState.svelte";
import { execCommand, queryCommandState } from "../../domlib";
import IconButton from "$lib/components/IconButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import WithState from "$lib/components/WithState.svelte";
import { updateStateByKey } from "$lib/components/WithState.svelte";
import { execCommand, queryCommandState } from "$lib/domlib";
import { context as noteEditorContext } from "../NoteEditor.svelte";
import { editingInputIsRichText } from "../rich-text-input";

View File

@ -5,8 +5,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script context="module" lang="ts">
import type { Writable } from "svelte/store";
import { resetAllState, updateAllState } from "../../components/WithState.svelte";
import type { DefaultSlotInterface } from "../../sveltelib/dynamic-slotting";
import { resetAllState, updateAllState } from "$lib/components/WithState.svelte";
import type { DefaultSlotInterface } from "$lib/sveltelib/dynamic-slotting";
export function updateActiveButtons(event: Event) {
updateAllState(event);
@ -39,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
AddonButtons,
};
import contextProperty from "../../sveltelib/context-property";
import contextProperty from "$lib/sveltelib/context-property";
const key = Symbol("editorToolbar");
const [context, setContextProperty] = contextProperty<EditorToolbarAPI>(key);
@ -51,9 +51,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { createEventDispatcher } from "svelte";
import { writable } from "svelte/store";
import ButtonToolbar from "../../components/ButtonToolbar.svelte";
import DynamicallySlottable from "../../components/DynamicallySlottable.svelte";
import Item from "../../components/Item.svelte";
import ButtonToolbar from "$lib/components/ButtonToolbar.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import Item from "$lib/components/Item.svelte";
import BlockButtons from "./BlockButtons.svelte";
import ImageOcclusionButton from "./ImageOcclusionButton.svelte";
import InlineButtons from "./InlineButtons.svelte";

View File

@ -3,14 +3,15 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { removeStyleProperties } from "@tslib/styling";
import { singleCallback } from "@tslib/typing";
import { onMount } from "svelte";
import IconButton from "../../components/IconButton.svelte";
import type { FormattingNode, MatchType } from "../../domlib/surround";
import IconButton from "$lib/components/IconButton.svelte";
import type { FormattingNode, MatchType } from "$lib/domlib/surround";
import { chevronDown } from "../icons";
import { surrounder } from "../rich-text-input";
import ColorPicker from "./ColorPicker.svelte";

View File

@ -3,17 +3,21 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import ButtonGroup from "components/ButtonGroup.svelte";
import DynamicallySlottable from "components/DynamicallySlottable.svelte";
import IconButton from "components/IconButton.svelte";
import { ioImageLoadedStore, ioMaskEditorVisible } from "image-occlusion/store";
import * as tr from "@generated/ftl";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import ButtonGroupItem, {
createProps,
setSlotHostContext,
updatePropsList,
} from "../../components/ButtonGroupItem.svelte";
} from "$lib/components/ButtonGroupItem.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import {
ioImageLoadedStore,
ioMaskEditorVisible,
} from "../../routes/image-occlusion/store";
import { mdiTableRefresh, mdiViewDashboard } from "./icons";
export let api = {};

View File

@ -3,9 +3,10 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import ButtonGroup from "../../components/ButtonGroup.svelte";
import DynamicallySlottable from "../../components/DynamicallySlottable.svelte";
import Item from "../../components/Item.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import Item from "$lib/components/Item.svelte";
import BoldButton from "./BoldButton.svelte";
import HighlightColorButton from "./HighlightColorButton.svelte";
import ItalicButton from "./ItalicButton.svelte";

View File

@ -3,10 +3,11 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { removeStyleProperties } from "@tslib/styling";
import type { MatchType } from "../../domlib/surround";
import type { MatchType } from "$lib/domlib/surround";
import { italicIcon } from "./icons";
import TextAttributeButton from "./TextAttributeButton.svelte";

View File

@ -3,15 +3,16 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { getPlatformString } from "@tslib/shortcuts";
import { wrapInternal } from "@tslib/wrap";
import DropdownItem from "../../components/DropdownItem.svelte";
import IconButton from "../../components/IconButton.svelte";
import Popover from "../../components/Popover.svelte";
import Shortcut from "../../components/Shortcut.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import DropdownItem from "$lib/components/DropdownItem.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import Popover from "$lib/components/Popover.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import WithFloating from "$lib/components/WithFloating.svelte";
import { mathjaxConfig } from "../../editable/mathjax-element";
import { context as noteEditorContext } from "../NoteEditor.svelte";
import type { RichTextInputAPI } from "../rich-text-input";

View File

@ -3,19 +3,19 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { getPlatformString } from "@tslib/shortcuts";
import ButtonGroup from "../../components/ButtonGroup.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import ButtonGroupItem, {
createProps,
setSlotHostContext,
updatePropsList,
} from "../../components/ButtonGroupItem.svelte";
import DynamicallySlottable from "../../components/DynamicallySlottable.svelte";
import LabelButton from "../../components/LabelButton.svelte";
import Shortcut from "../../components/Shortcut.svelte";
} from "$lib/components/ButtonGroupItem.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import LabelButton from "$lib/components/LabelButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
export let api = {};

View File

@ -3,14 +3,15 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import CheckBox from "../../components/CheckBox.svelte";
import DropdownItem from "../../components/DropdownItem.svelte";
import IconButton from "../../components/IconButton.svelte";
import Popover from "../../components/Popover.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import CheckBox from "$lib/components/CheckBox.svelte";
import DropdownItem from "$lib/components/DropdownItem.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import Popover from "$lib/components/Popover.svelte";
import WithFloating from "$lib/components/WithFloating.svelte";
import { mathjaxConfig } from "../../editable/mathjax-element";
import { shrinkImagesByDefault } from "../image-overlay/ImageOverlay.svelte";
import { closeHTMLTags } from "../plain-text-input/PlainTextInput.svelte";

View File

@ -3,13 +3,14 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import ButtonGroup from "../../components/ButtonGroup.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import ButtonGroupItem, {
createProps,
setSlotHostContext,
updatePropsList,
} from "../../components/ButtonGroupItem.svelte";
import DynamicallySlottable from "../../components/DynamicallySlottable.svelte";
} from "$lib/components/ButtonGroupItem.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import OptionsButton from "./OptionsButton.svelte";
export let api = {};

View File

@ -3,19 +3,20 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { altPressed, shiftPressed } from "@tslib/keys";
import { getPlatformString } from "@tslib/shortcuts";
import { singleCallback } from "@tslib/typing";
import { onMount } from "svelte";
import CheckBox from "../../components/CheckBox.svelte";
import DropdownItem from "../../components/DropdownItem.svelte";
import IconButton from "../../components/IconButton.svelte";
import Popover from "../../components/Popover.svelte";
import Shortcut from "../../components/Shortcut.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import type { MatchType } from "../../domlib/surround";
import CheckBox from "$lib/components/CheckBox.svelte";
import DropdownItem from "$lib/components/DropdownItem.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import Popover from "$lib/components/Popover.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import WithFloating from "$lib/components/WithFloating.svelte";
import type { MatchType } from "$lib/domlib/surround";
import { chevronDown } from "../icons";
import { surrounder } from "../rich-text-input";
import type { RemoveFormat } from "./EditorToolbar.svelte";

View File

@ -3,10 +3,11 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { removeStyleProperties } from "@tslib/styling";
import type { MatchType } from "../../domlib/surround";
import type { MatchType } from "$lib/domlib/surround";
import { subscriptIcon } from "./icons";
import TextAttributeButton from "./TextAttributeButton.svelte";

View File

@ -3,10 +3,11 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { removeStyleProperties } from "@tslib/styling";
import type { MatchType } from "../../domlib/surround";
import type { MatchType } from "$lib/domlib/surround";
import { superscriptIcon } from "./icons";
import TextAttributeButton from "./TextAttributeButton.svelte";

View File

@ -3,21 +3,22 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { promiseWithResolver } from "@tslib/promise";
import { registerPackage } from "@tslib/runtime-require";
import { getPlatformString } from "@tslib/shortcuts";
import ButtonGroup from "../../components/ButtonGroup.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import ButtonGroupItem, {
createProps,
setSlotHostContext,
updatePropsList,
} from "../../components/ButtonGroupItem.svelte";
import DynamicallySlottable from "../../components/DynamicallySlottable.svelte";
import IconButton from "../../components/IconButton.svelte";
import Shortcut from "../../components/Shortcut.svelte";
} from "$lib/components/ButtonGroupItem.svelte";
import DynamicallySlottable from "$lib/components/DynamicallySlottable.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import { context } from "../NoteEditor.svelte";
import { setFormat } from "../old-editor-adapter";
import type { RichTextInputAPI } from "../rich-text-input";

View File

@ -7,10 +7,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { singleCallback } from "@tslib/typing";
import { onMount } from "svelte";
import IconButton from "../../components/IconButton.svelte";
import Shortcut from "../../components/Shortcut.svelte";
import WithState, { updateStateByKey } from "../../components/WithState.svelte";
import type { MatchType } from "../../domlib/surround";
import IconButton from "$lib/components/IconButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import WithState, { updateStateByKey } from "$lib/components/WithState.svelte";
import type { MatchType } from "$lib/domlib/surround";
import { surrounder } from "../rich-text-input";
import { context as editorToolbarContext } from "./EditorToolbar.svelte";

View File

@ -3,16 +3,17 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@tslib/ftl";
import { getPlatformString } from "@tslib/shortcuts";
import { removeStyleProperties } from "@tslib/styling";
import { singleCallback } from "@tslib/typing";
import { onMount } from "svelte";
import IconButton from "../../components/IconButton.svelte";
import Shortcut from "../../components/Shortcut.svelte";
import type { FormattingNode, MatchType } from "../../domlib/surround";
import IconButton from "$lib/components/IconButton.svelte";
import Shortcut from "$lib/components/Shortcut.svelte";
import type { FormattingNode, MatchType } from "$lib/domlib/surround";
import { withFontColor } from "../helpers";
import { chevronDown } from "../icons";
import { surrounder } from "../rich-text-input";

View File

@ -3,9 +3,10 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import type { MatchType } from "$lib/domlib/surround";
import type { MatchType } from "../../domlib/surround";
import { underlineIcon } from "./icons";
import TextAttributeButton from "./TextAttributeButton.svelte";

View File

@ -1,8 +1,6 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/// <reference types="../../lib/image-import" />
export { default as cogIcon } from "@mdi/svg/svg/cog.svg";
export { default as colorHelperIcon } from "@mdi/svg/svg/color-helper.svg";
export { default as highlightColorIcon } from "@mdi/svg/svg/format-color-highlight.svg";

View File

@ -1,8 +1,6 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
/// <reference types="../lib/image-import" />
export { default as alertIcon } from "@mdi/svg/svg/alert.svg";
export { default as chevronDown } from "@mdi/svg/svg/chevron-down.svg";
export { default as chevronUp } from "@mdi/svg/svg/chevron-up.svg";

View File

@ -3,12 +3,13 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@tslib/ftl";
import * as tr from "@generated/ftl";
import { removeStyleProperties } from "@tslib/styling";
import { createEventDispatcher } from "svelte";
import ButtonGroup from "../../components/ButtonGroup.svelte";
import IconButton from "../../components/IconButton.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import { floatLeftIcon, floatNoneIcon, floatRightIcon } from "./icons";
export let image: HTMLImageElement;

View File

@ -9,16 +9,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script>
<script lang="ts">
import * as tr from "@generated/ftl";
import { on } from "@tslib/events";
import * as tr from "@tslib/ftl";
import { removeStyleProperties } from "@tslib/styling";
import type { Callback } from "@tslib/typing";
import { tick } from "svelte";
import ButtonToolbar from "../../components/ButtonToolbar.svelte";
import Popover from "../../components/Popover.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import WithOverlay from "../../components/WithOverlay.svelte";
import ButtonToolbar from "$lib/components/ButtonToolbar.svelte";
import Popover from "$lib/components/Popover.svelte";
import WithFloating from "$lib/components/WithFloating.svelte";
import WithOverlay from "$lib/components/WithOverlay.svelte";
import type { EditingInputAPI } from "../EditingArea.svelte";
import HandleBackground from "../HandleBackground.svelte";
import HandleControl from "../HandleControl.svelte";

View File

@ -3,13 +3,14 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "@generated/ftl";
import { directionKey } from "@tslib/context-keys";
import * as tr from "@tslib/ftl";
import { createEventDispatcher, getContext } from "svelte";
import type { Readable } from "svelte/store";
import ButtonGroup from "../../components/ButtonGroup.svelte";
import IconButton from "../../components/IconButton.svelte";
import ButtonGroup from "$lib/components/ButtonGroup.svelte";
import IconButton from "$lib/components/IconButton.svelte";
import { sizeActual, sizeClear, sizeMinimized } from "./icons";
export let isSizeConstrained: boolean;

View File

@ -1,7 +1,7 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass:color";
@use "sass/button-mixins" as button;
@use "../lib/sass/button-mixins" as button;
.linkb {
$size: var(--buttons-size);

Some files were not shown because too many files have changed in this diff Show More