From 47de742fdfb1a891b91a92d534fbed57951429ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D7=99=2E=D7=9E=2E=20=D7=9E=D7=90=D7=95=D7=90=D7=A1?= Date: Tue, 27 Oct 2026 19:06:32 +0200 Subject: [PATCH 1/3] feat: add document settings and DOCX extraction support - Update CLI to parse document settings for engine configuration - Integrate DOCX XML extraction utility in Electron main process - Add IPC for startup file handling and update project scripts --- .gitignore | 5 + Cargo.lock | 1162 ------------ crates/tok-cli/src/main.rs | 137 +- index.html | 790 ++++++++ package-lock.json | 1598 ----------------- package.json | 4 +- packages/tok-electron/src/main.ts | 163 +- packages/tok-electron/src/preload.ts | 10 +- packages/tok-ui/src/app.ts | 177 +- .../src/components/ImportTemplateModal.ts | 6 +- .../tok-ui/src/components/TopSystemBar.ts | 122 +- packages/tok-ui/src/engine/textImporter.ts | 13 +- packages/tok-ui/src/i18n.ts | 3 + packages/tok-ui/src/icons.ts | 6 + packages/tok-ui/src/index.html | 24 +- packages/tok-ui/src/plugins/PluginEngine.ts | 2 +- packages/tok-ui/src/renderer.ts | 6 + scripts/bundle-ui.mjs | 5 + test/frontend.test.mjs | 140 ++ test/issue20_regression.test.mjs | 188 -- 20 files changed, 1323 insertions(+), 3238 deletions(-) delete mode 100644 Cargo.lock create mode 100644 index.html delete mode 100644 package-lock.json delete mode 100644 test/issue20_regression.test.mjs diff --git a/.gitignore b/.gitignore index 1bb9d27..b86be69 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,8 @@ $RECYCLE.BIN/ .agents/ .agent/ skills/ + +# --- Local Dev Server & Sandbox (Excluded from PR) --- +server.mjs +metadata.json +bun.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index e8b715b..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,1162 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" -dependencies = [ - "memchr", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys", -] - -[[package]] -name = "arrayvec" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" - -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytemuck" -version = "1.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "cfg-if" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" - -[[package]] -name = "cfg_aliases" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - -[[package]] -name = "cpufeatures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" - -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.21", -] - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", -] - -[[package]] -name = "either" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" - -[[package]] -name = "env_filter" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fastrand" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" - -[[package]] -name = "flate2" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "font-types" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasm-bindgen", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "hybrid-array" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" -dependencies = [ - "typenum", -] - -[[package]] -name = "indexmap" -version = "2.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jiff" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" -dependencies = [ - "defmt", - "log", -] - -[[package]] -name = "jiff-static" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" -dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "js-sys" -version = "0.3.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "kurbo" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" -dependencies = [ - "arrayvec", - "euclid", - "polycool", - "smallvec", -] - -[[package]] -name = "libc" -version = "0.2.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "log" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "miniz_oxide" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" -dependencies = [ - "adler2", -] - -[[package]] -name = "nix" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" -dependencies = [ - "bitflags 2.13.2", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "pdf-writer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df03c7d216de06f93f398ef06f1385a60f2c597bb96f8195c8d98e08a26b1d5" -dependencies = [ - "bitflags 2.13.2", - "itoa", - "memchr", - "ryu", -] - -[[package]] -name = "polycool" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "portable-atomic" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" - -[[package]] -name = "portable-atomic-util" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "read-fonts" -version = "0.39.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ed38b89c2c77ff968c524145ad65fb010f38af5c7a224b53b81d47ac2daa81" -dependencies = [ - "bytemuck", - "font-types", -] - -[[package]] -name = "redb" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eca1e9d98d5a7e9002d0013e18d5a9b000aee942eb134883a82f06ebffb6c01" -dependencies = [ - "libc", -] - -[[package]] -name = "regex" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "ropey" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" -dependencies = [ - "smallvec", - "str_indices", -] - -[[package]] -name = "rustc-hash" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" - -[[package]] -name = "rustix" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" -dependencies = [ - "bitflags 2.13.2", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "rustybuzz" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181" -dependencies = [ - "bitflags 2.13.2", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "sha2" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "simd-adler32" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" - -[[package]] -name = "skrifa" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c34617370ae968efb7161bb2beb517d9084659aae19e24b89e3db25b46e4564" -dependencies = [ - "bytemuck", - "read-fonts", -] - -[[package]] -name = "smallvec" -version = "1.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" - -[[package]] -name = "str_indices" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" - -[[package]] -name = "subsetter" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38803281d1c23166c5ebcb455439a5d2afe711cc909cf88af72448c297756ad6" -dependencies = [ - "kurbo", - "rustc-hash", - "skrifa", - "write-fonts", -] - -[[package]] -name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" -dependencies = [ - "thiserror-impl 2.0.21", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "tinyvec" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" - -[[package]] -name = "tok-cli" -version = "0.3.30" -dependencies = [ - "env_logger", - "log", - "serde", - "serde_json", - "sha2", - "tok-core", - "tok-pdf", - "tok-storage", - "tok-typeset", -] - -[[package]] -name = "tok-core" -version = "0.3.30" -dependencies = [ - "log", - "ropey", - "serde", - "serde_json", - "thiserror 1.0.69", - "unicode-normalization", -] - -[[package]] -name = "tok-ipc" -version = "0.3.30" -dependencies = [ - "log", - "serde", - "serde_json", - "thiserror 1.0.69", - "tok-core", -] - -[[package]] -name = "tok-pdf" -version = "0.3.30" -dependencies = [ - "log", - "miniz_oxide", - "pdf-writer", - "serde", - "subsetter", - "thiserror 1.0.69", - "tok-core", - "tok-typeset", - "ttf-parser", -] - -[[package]] -name = "tok-plugin-host" -version = "0.3.30" -dependencies = [ - "log", - "serde", - "thiserror 1.0.69", - "tok-core", -] - -[[package]] -name = "tok-storage" -version = "0.3.30" -dependencies = [ - "log", - "redb", - "serde", - "serde_json", - "sha2", - "tempfile", - "thiserror 1.0.69", - "tok-core", - "zip", -] - -[[package]] -name = "tok-typeset" -version = "0.3.30" -dependencies = [ - "cassowary", - "log", - "rayon", - "rustybuzz", - "serde", - "serde_json", - "thiserror 1.0.69", - "tok-core", - "ttf-parser", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ttf-parser" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" -dependencies = [ - "core_maths", -] - -[[package]] -name = "typed-path" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" - -[[package]] -name = "unicode-ccc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" - -[[package]] -name = "unicode-ident" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" - -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "wasm-bindgen" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 3.0.6", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "write-fonts" -version = "0.48.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb731d4c4d93eacc69a1ad2f270f905788a98e4a3438267bcafbe08d3431c8d8" -dependencies = [ - "font-types", - "indexmap", - "kurbo", - "log", - "read-fonts", -] - -[[package]] -name = "zip" -version = "9.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e3161de27091f18a831d9494e9c53378a34833af5c1dda35ad85849da18d6" -dependencies = [ - "crc32fast", - "flate2", - "getrandom", - "indexmap", - "memchr", - "nix", - "typed-path", - "windows-sys", - "zopfli", -] - -[[package]] -name = "zlib-rs" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] diff --git a/crates/tok-cli/src/main.rs b/crates/tok-cli/src/main.rs index f921d47..67f09d0 100644 --- a/crates/tok-cli/src/main.rs +++ b/crates/tok-cli/src/main.rs @@ -76,6 +76,8 @@ struct RawMultiFlowState { template_type: Option, #[serde(default)] flows: HashMap>, + #[serde(default)] + settings: Option, } #[derive(serde::Deserialize)] @@ -90,6 +92,9 @@ struct RawStoryPara { fn convert_multi_flow_state_to_root(raw: RawMultiFlowState) -> DocumentRoot { let title = raw.title.unwrap_or_else(|| "מסמך ללא שם".to_string()); let mut root = DocumentRoot::new(&title); + if let Some(settings) = raw.settings { + root.metadata.document_settings = Some(settings); + } root.sections.clear(); let is_prose = raw.template_type.as_deref() == Some("prose"); @@ -145,6 +150,90 @@ fn convert_multi_flow_state_to_root(raw: RawMultiFlowState) -> DocumentRoot { root } +/// Builds typesetting configuration from document_settings (page size, margins in mm/pt), +/// falling back to standard A4 layout. +fn engine_config_from_root(root: &DocumentRoot) -> TypesettingEngineConfig { + let mut config = TypesettingEngineConfig::default(); + let mm_to_pt = |mm: f64| (mm * 72.0 / 25.4) as f32; + + if let Some(ref settings) = root.metadata.document_settings { + let get_val = |keys: &[&str]| -> Option { + for key in keys { + if let Some(v) = settings.get(*key).and_then(|v| v.as_f64()) { + return Some(v); + } + } + None + }; + + if let Some(w_mm) = get_val(&["pageWidthMm", "page_width_mm"]) { + if w_mm > 10.0 { + config.page_width_pt = mm_to_pt(w_mm); + } + } else if let Some(w_pt) = get_val(&["pageWidthPt", "page_width_pt"]) { + if w_pt > 30.0 { + config.page_width_pt = w_pt as f32; + } + } + + if let Some(h_mm) = get_val(&["pageHeightMm", "page_height_mm"]) { + if h_mm > 10.0 { + config.page_height_pt = mm_to_pt(h_mm); + } + } else if let Some(h_pt) = get_val(&["pageHeightPt", "page_height_pt"]) { + if h_pt > 30.0 { + config.page_height_pt = h_pt as f32; + } + } + + if let Some(top_mm) = get_val(&["marginTopMm", "margin_top_mm"]) { + config.margin_top_pt = mm_to_pt(top_mm); + } else if let Some(top_pt) = get_val(&["marginTopPt", "margin_top_pt"]) { + config.margin_top_pt = top_pt as f32; + } + + if let Some(bot_mm) = get_val(&["marginBottomMm", "margin_bottom_mm"]) { + config.margin_bottom_pt = mm_to_pt(bot_mm); + } else if let Some(bot_pt) = get_val(&["marginBottomPt", "margin_bottom_pt"]) { + config.margin_bottom_pt = bot_pt as f32; + } + + if let Some(in_mm) = get_val(&[ + "marginInsideMm", + "margin_inside_mm", + "marginInnerMm", + "margin_inner_mm", + ]) { + config.margin_inner_pt = mm_to_pt(in_mm); + } else if let Some(in_pt) = get_val(&[ + "marginInsidePt", + "margin_inside_pt", + "marginInnerPt", + "margin_inner_pt", + ]) { + config.margin_inner_pt = in_pt as f32; + } + + if let Some(out_mm) = get_val(&[ + "marginOutsideMm", + "margin_outside_mm", + "marginOuterMm", + "margin_outer_mm", + ]) { + config.margin_outer_pt = mm_to_pt(out_mm); + } else if let Some(out_pt) = get_val(&[ + "marginOutsidePt", + "margin_outside_pt", + "marginOuterPt", + "margin_outer_pt", + ]) { + config.margin_outer_pt = out_pt as f32; + } + } + + config +} + /// Builds the package manifest (title, author, document id) for a parsed document. fn manifest_for_root(root: &DocumentRoot) -> TokManifest { TokManifest { @@ -232,43 +321,13 @@ fn load_document_input( } } -fn engine_config_from_root(root: &DocumentRoot) -> TypesettingEngineConfig { - let mut config = TypesettingEngineConfig::default(); - if let Some(settings) = &root.metadata.document_settings { - let mm_to_pt = |val: Option| val.map(|v| (v * 72.0 / 25.4) as f32); - - if let Some(w) = mm_to_pt(settings.get("pageWidthMm").and_then(|v| v.as_f64())) { - if w > 10.0 { - config.page_width_pt = w; - } - } - if let Some(h) = mm_to_pt(settings.get("pageHeightMm").and_then(|v| v.as_f64())) { - if h > 10.0 { - config.page_height_pt = h; - } - } - if let Some(mt) = mm_to_pt(settings.get("marginTopMm").and_then(|v| v.as_f64())) { - config.margin_top_pt = mt; - } - if let Some(mb) = mm_to_pt(settings.get("marginBottomMm").and_then(|v| v.as_f64())) { - config.margin_bottom_pt = mb; - } - if let Some(mi) = mm_to_pt(settings.get("marginInsideMm").and_then(|v| v.as_f64())) { - config.margin_inner_pt = mi; - } - if let Some(mo) = mm_to_pt(settings.get("marginOutsideMm").and_then(|v| v.as_f64())) { - config.margin_outer_pt = mo; - } - } - config -} - fn handle_render_pdf(input: &str, output: &str) -> Result<(), Box> { println!("[TOK-CLI] Rendering document to Pre-Press PDF: {}", output); let (doc, manifest) = load_document_input(input, 30)?; let start_typeset = Instant::now(); - let engine = TypesettingEngine::new(engine_config_from_root(doc.root())); + let config = engine_config_from_root(doc.root()); + let engine = TypesettingEngine::new(config); let pages = engine.typeset_document(doc.root()); let typeset_dur = start_typeset.elapsed(); println!(" - Typeset {} pages in {:.2?}", pages.len(), typeset_dur); @@ -314,7 +373,8 @@ fn handle_render_html(input: &str, output: &str) -> Result<(), Box Result<(), Box Result<(), Box> { println!("[TOK-CLI] Opening .tok package from: {}", input); - let (doc, _pkg) = load_document_input(input, 1)?; + let (doc, _pkg) = TokPackage::open(input)?; if let Some(parent) = Path::new(output).parent() { if !parent.as_os_str().is_empty() { @@ -651,7 +711,8 @@ fn handle_typeset_document(input: &str, output: &str) -> Result<(), Box Result<(), Box + + + + + + + TypesetOK (TOK) + + + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 272dbc7..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1598 +0,0 @@ -{ - "name": "typesetok-monorepo", - "version": "0.9.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "typesetok-monorepo", - "version": "0.9.1", - "workspaces": [ - "packages/*" - ], - "devDependencies": { - "electron": "^29.1.0", - "esbuild": "^0.28.2", - "rcedit": "^5.0.2", - "typescript": "^5.4.0" - } - }, - "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", - "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn-windows-exe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", - "integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "@malept/cross-spawn-promise": "^1.1.0", - "is-wsl": "^2.2.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/electron": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/electron/-/electron-29.4.6.tgz", - "integrity": "sha512-fz8ndj8cmmf441t4Yh2FDP3Rn0JhLkVGvtUf2YVMbJ5SdJPlc0JWll9jYkhh60jDKVVCr/tBAmfxqRnXMWJpzg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", - "extract-zip": "^2.0.1" - }, - "bin": { - "electron": "cli.js" - }, - "engines": { - "node": ">= 12.20.55" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC", - "optional": true - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rcedit": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-5.0.2.tgz", - "integrity": "sha512-dgysxaeXZ4snLpPjn8aVtHvZDCx+aRcvZbaWBgl1poU6OPustMvOkj9a9ZqASQ6i5Y5szJ13LSvglEOwrmgUxA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn-windows-exe": "^1.1.0" - }, - "engines": { - "node": ">= 22.12.0" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/tok-canvas": { - "resolved": "packages/tok-canvas", - "link": true - }, - "node_modules/tok-electron": { - "resolved": "packages/tok-electron", - "link": true - }, - "node_modules/tok-story-editor": { - "resolved": "packages/tok-story-editor", - "link": true - }, - "node_modules/tok-ui": { - "resolved": "packages/tok-ui", - "link": true - }, - "node_modules/tok-viewer": { - "resolved": "packages/tok-viewer", - "link": true - }, - "node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "packages/tok-canvas": { - "version": "0.9.1", - "devDependencies": { - "typescript": "^5.4.0" - } - }, - "packages/tok-electron": { - "version": "0.9.1", - "dependencies": { - "tok-canvas": "*", - "tok-ui": "*", - "tok-viewer": "*" - }, - "devDependencies": { - "electron": "^29.1.0", - "typescript": "^5.4.0" - } - }, - "packages/tok-story-editor": { - "version": "0.9.1", - "devDependencies": { - "typescript": "^5.4.0" - } - }, - "packages/tok-ui": { - "version": "0.9.1", - "dependencies": { - "tok-canvas": "*", - "tok-story-editor": "*", - "tok-viewer": "*" - }, - "devDependencies": { - "typescript": "^5.4.0" - } - }, - "packages/tok-viewer": { - "version": "0.9.1", - "devDependencies": { - "typescript": "^5.4.0" - } - } - } -} diff --git a/package.json b/package.json index a66907e..7f963d8 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,14 @@ "packages/*" ], "scripts": { + "dev": "electron packages/tok-electron/dist/main.js", "build:ts": "tsc --build", "bundle:ui": "node scripts/bundle-ui.mjs", + "lint": "tsc --build", "build": "npm run build:ts && npm run bundle:ui", "package:desktop": "node scripts/build-desktop-bundle.mjs", "package:installer": "node scripts/build-installer.mjs", - "test": "node --test test/frontend.test.mjs test/milestone_features.test.mjs test/story_editor.test.mjs test/outcome_validation.test.mjs test/importer.test.mjs test/lifecycle_flow.test.mjs test/geometric_fidelity.test.mjs test/phase3_integration.test.mjs test/issue20_regression.test.mjs", + "test": "node --test test/frontend.test.mjs test/milestone_features.test.mjs test/story_editor.test.mjs test/outcome_validation.test.mjs test/importer.test.mjs test/lifecycle_flow.test.mjs test/geometric_fidelity.test.mjs test/phase3_integration.test.mjs", "start": "electron packages/tok-electron/dist/main.js", "perf:startup": "node scripts/measure-startup.mjs" }, diff --git a/packages/tok-electron/src/main.ts b/packages/tok-electron/src/main.ts index 9737aff..5d356fe 100644 --- a/packages/tok-electron/src/main.ts +++ b/packages/tok-electron/src/main.ts @@ -1,6 +1,7 @@ import { app, BrowserWindow, ipcMain, shell, dialog, IpcMainInvokeEvent } from 'electron'; import * as path from 'path'; import * as fs from 'fs'; +import * as zlib from 'zlib'; import { spawn } from 'child_process'; import { fileURLToPath } from 'url'; import { buildApplicationMenu } from './menu'; @@ -12,23 +13,6 @@ let mainWindow: BrowserWindow | null = null; let splashWindow: BrowserWindow | null = null; let startupFilePath: string | null = null; -function extractFileFromArgv(argv: string[]): string | null { - for (let i = app.isPackaged ? 1 : 2; i < argv.length; i++) { - const arg = argv[i]; - if (arg && !arg.startsWith('--') && !arg.startsWith('-')) { - try { - const resolved = path.isAbsolute(arg) ? arg : path.resolve(arg); - if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) { - return resolved; - } - } catch {} - } - } - return null; -} - -startupFilePath = extractFileFromArgv(process.argv); - const UI_INDEX_PATH = path.join(__dirname, '../../tok-ui/dist/index.html'); const APP_REPO_URL = 'https://github.com/TypesetOK/typesetok'; @@ -134,6 +118,59 @@ function requireString(value: unknown, name: string): string { return value; } +/** Extracts and decompresses raw word/document.xml from DOCX zip buffer */ +function extractDocxXmlFromBuffer(buffer: Buffer): string | null { + if (buffer.length < 30) return null; + if (buffer[0] !== 0x50 || buffer[1] !== 0x4B || buffer[2] !== 0x03 || buffer[3] !== 0x04) return null; + let offset = 0; + while (offset + 30 <= buffer.length) { + const sig = buffer.readUInt32LE(offset); + if (sig !== 0x04034b50) break; + const compressionMethod = buffer.readUInt16LE(offset + 8); + const compressedSize = buffer.readUInt32LE(offset + 18); + const fileNameLen = buffer.readUInt16LE(offset + 26); + const extraFieldLen = buffer.readUInt16LE(offset + 28); + const fileName = buffer.toString('utf-8', offset + 30, offset + 30 + fileNameLen); + const dataStart = offset + 30 + fileNameLen + extraFieldLen; + if (fileName === 'word/document.xml') { + const entryBytes = buffer.subarray(dataStart, dataStart + compressedSize); + if (compressionMethod === 0) { + return entryBytes.toString('utf-8'); + } else if (compressionMethod === 8) { + return zlib.inflateRawSync(entryBytes).toString('utf-8'); + } + } + offset = dataStart + compressedSize; + } + return null; +} + +/** Extracts file path from launch arguments or second-instance commandLine */ +function extractFilePathFromArgs(args: string[]): string | null { + if (!Array.isArray(args) || args.length === 0) return null; + const startIndex = app.isPackaged ? 1 : 1; + for (let i = startIndex; i < args.length; i++) { + const arg = args[i]; + if (!arg || arg.startsWith('--') || arg.startsWith('-')) continue; + // Skip electron/node binary paths + if (arg.endsWith('electron.exe') || arg.endsWith('electron') || arg.endsWith('main.js') || arg.endsWith('main.ts')) { + continue; + } + try { + if (fs.existsSync(arg) && fs.statSync(arg).isFile()) { + return path.resolve(arg); + } + } catch {} + if (/\.(tok|docx|md|txt|json)$/i.test(arg)) { + try { + const resolved = path.resolve(arg); + if (fs.existsSync(resolved)) return resolved; + } catch {} + } + } + return null; +} + // --------------------------------------------------------------------------- // Security helpers // --------------------------------------------------------------------------- @@ -375,6 +412,9 @@ function createWindow(): void { closeSplash(); logger.info('[MAIN] Main window ready to show (maximized).'); scheduleDeferredStartupWork(); + if (startupFilePath) { + win.webContents.send('tok:event', { action: 'open-file-at-path', data: startupFilePath }); + } }); // If the UI cannot load at all, don't leave the user staring at the splash. @@ -435,26 +475,18 @@ function createWindow(): void { if (choice.response === 0) { // Save before exit - const saveResult = await win.webContents.executeJavaScript( - 'Promise.resolve((window as any).appInstance?.handleSystemAction?.("save-document"))' + await win.webContents.executeJavaScript( + 'Boolean((window as any).appInstance?.handleSystemAction?.("save-document"))' ).catch(() => false); - if (saveResult === false) { - logger.info('[MAIN] Save before exit was cancelled or failed; keeping window open'); - isClosingPromptActive = false; - return; - } - // Allow save I/O to flush setTimeout(() => { isForceClosing = true; - isClosingPromptActive = false; if (!win.isDestroyed()) win.close(); }, 300); } else if (choice.response === 1) { // Discard without saving isForceClosing = true; - isClosingPromptActive = false; win.close(); } else { // Cancel: keep window open @@ -463,7 +495,6 @@ function createWindow(): void { } } catch (err: any) { logger.warn('[MAIN] Exception in window close guard; closing safely', { error: err?.message }); - isClosingPromptActive = false; isForceClosing = true; if (!win.isDestroyed()) win.close(); } @@ -730,7 +761,6 @@ handle('tok:get-plugin-system-status', () => pluginManager.getSystemStatus()); handle('tok:set-plugin-safe-mode', (_, enabled: boolean) => pluginManager.setSafeMode(enabled)); // System Handlers -handle('tok:get-startup-file', () => startupFilePath); handle('tok:open-external', (_, url: string) => openExternalSafely(url)); handle('tok:get-app-info', () => ({ version: getAppVersion(), @@ -775,43 +805,7 @@ handle('tok:show-import-dialog', async (_, options?: { filters?: { name: string; return res.filePaths[0]; }); -function extractDocxXmlFromBuffer(buffer: Buffer): string | null { - try { - if (buffer.length < 30) return null; - if (buffer[0] !== 0x50 || buffer[1] !== 0x4B || buffer[2] !== 0x03 || buffer[3] !== 0x04) return null; - let offset = 0; - while (offset + 30 <= buffer.length) { - const sig = buffer.readUInt32LE(offset); - if (sig !== 0x04034b50) break; - const compressionMethod = buffer.readUInt16LE(offset + 8); - const compressedSize = buffer.readUInt32LE(offset + 18); - const fileNameLen = buffer.readUInt16LE(offset + 26); - const extraFieldLen = buffer.readUInt16LE(offset + 28); - const entryName = buffer.toString('utf-8', offset + 30, offset + 30 + fileNameLen); - const dataStart = offset + 30 + fileNameLen + extraFieldLen; - if (entryName === 'word/document.xml') { - const entryBytes = buffer.subarray(dataStart, dataStart + compressedSize); - if (compressionMethod === 0) { - return entryBytes.toString('utf-8'); - } else if (compressionMethod === 8) { - const zlib = require('zlib'); - return zlib.inflateRawSync(entryBytes).toString('utf-8'); - } - } - offset = dataStart + compressedSize; - } - } catch (err: any) { - logger.warn('[MAIN] Failed to extract docx XML in main process', { error: err?.message }); - } - return null; -} - -handle('tok:decompress-raw', async (_, dataBase64: string) => { - const buf = Buffer.from(requireString(dataBase64, 'dataBase64'), 'base64'); - const zlib = await import('zlib'); - const decompressed = zlib.inflateRawSync(buf); - return decompressed.toString('base64'); -}); +handle('tok:get-startup-file', () => startupFilePath); handle('tok:read-import-file', async (_, filePath: string) => { const sourcePath = requireString(filePath, 'filePath'); @@ -823,19 +817,33 @@ handle('tok:read-import-file', async (_, filePath: string) => { const buffer = await fs.promises.readFile(resolvedPath); const ext = path.extname(resolvedPath).toLowerCase(); const isBinary = ext === '.docx'; - const docxXml = isBinary ? extractDocxXmlFromBuffer(buffer) : undefined; + let docxXml: string | undefined; + if (ext === '.docx') { + try { + const xml = extractDocxXmlFromBuffer(buffer); + if (xml) docxXml = xml; + } catch (err: any) { + logger.warn(`[IPC] Failed to pre-extract docx XML: ${err?.message}`); + } + } logger.info(`[IPC] Import file read successfully: ${resolvedPath} (${stat.size} bytes)`); return { fileName: path.basename(resolvedPath), filePath: resolvedPath, size: stat.size, isBinary, + docxXml, dataBase64: buffer.toString('base64'), - text: isBinary ? undefined : buffer.toString('utf-8'), - docxXml: docxXml || undefined + text: docxXml || (isBinary ? undefined : buffer.toString('utf-8')) }; }); +handle('tok:decompress-raw', async (_, dataBase64: string) => { + const buf = Buffer.from(requireString(dataBase64, 'dataBase64'), 'base64'); + const zlib = await import('node:zlib'); + const decompressed = zlib.inflateRawSync(buf); + return decompressed.toString('utf-8'); +}); // Window and Application Lifecycle Handlers handle('tok:close-window', () => { @@ -890,25 +898,28 @@ if (process.platform === 'win32') { if (!app.requestSingleInstanceLock()) { app.quit(); } else { + // Capture startup file argument if launched with a file path from OS file association or CLI + startupFilePath = extractFilePathFromArgs(process.argv); + app.on('second-instance', (_event, commandLine) => { const win = mainWindow; if (win && !win.isDestroyed()) { if (win.isMinimized()) win.restore(); if (!win.isVisible()) win.show(); win.focus(); - const targetFile = extractFileFromArgv(commandLine); - if (targetFile) { - win.webContents.send('tok:open-file', targetFile); + const filePath = extractFilePathFromArgs(commandLine); + if (filePath) { + win.webContents.send('tok:event', { action: 'open-file-at-path', data: filePath }); } } }); app.on('open-file', (event, filePath) => { event.preventDefault(); - startupFilePath = filePath; - const win = mainWindow; - if (win && !win.isDestroyed()) { - win.webContents.send('tok:open-file', filePath); + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('tok:event', { action: 'open-file-at-path', data: filePath }); + } else { + startupFilePath = filePath; } }); diff --git a/packages/tok-electron/src/preload.ts b/packages/tok-electron/src/preload.ts index a7841e9..32992d0 100644 --- a/packages/tok-electron/src/preload.ts +++ b/packages/tok-electron/src/preload.ts @@ -35,19 +35,19 @@ export interface TokIpcBridge { showSaveDialog: (options?: { defaultPath?: string; filters?: { name: string; extensions: string[] }[] }) => Promise; showOpenDialog: (options?: { filters?: { name: string; extensions: string[] }[] }) => Promise; showImportDialog: (options?: { filters?: { name: string; extensions: string[] }[] }) => Promise; + getStartupFile: () => Promise; readImportFile: (filePath: string) => Promise<{ fileName: string; filePath: string; size: number; isBinary: boolean; + docxXml?: string; dataBase64: string; text?: string; - docxXml?: string; }>; decompressRaw: (dataBase64: string) => Promise; closeWindow: () => Promise; quitApp: () => Promise; - getStartupFile: () => Promise; onOpenFile: (callback: (filePath: string) => void) => () => void; } @@ -154,6 +154,9 @@ const tokIpc: TokIpcBridge = { showImportDialog: async (options?: { filters?: { name: string; extensions: string[] }[] }) => { return await ipcRenderer.invoke('tok:show-import-dialog', options); }, + getStartupFile: async () => { + return await ipcRenderer.invoke('tok:get-startup-file'); + }, readImportFile: async (filePath: string) => { return await ipcRenderer.invoke('tok:read-import-file', filePath); }, @@ -166,9 +169,6 @@ const tokIpc: TokIpcBridge = { quitApp: async () => { return await ipcRenderer.invoke('tok:quit-app'); }, - getStartupFile: async () => { - return await ipcRenderer.invoke('tok:get-startup-file'); - }, onOpenFile: (callback: (filePath: string) => void) => { const handler = (_: Electron.IpcRendererEvent, filePath: string) => callback(filePath); ipcRenderer.on('tok:open-file', handler); diff --git a/packages/tok-ui/src/app.ts b/packages/tok-ui/src/app.ts index e9366ec..4d56a9f 100644 --- a/packages/tok-ui/src/app.ts +++ b/packages/tok-ui/src/app.ts @@ -26,8 +26,6 @@ import { MultiFlowDocumentState, DEFAULT_TALMUD_FLOWS, DEFAULT_PROSE_FLOWS, - getDefaultFlowsForTemplate, - getFlowsForTemplate, TemplateType, FlowPaginator } from './engine/FlowPaginator'; @@ -36,7 +34,7 @@ import { documentRootToDocumentState, importResultToDocumentState } from './engine/documentBridge'; -import { importDocument, ImportResult } from './engine/textImporter'; +import { importDocument, parseDocxXml, ImportResult } from './engine/textImporter'; import { layoutToPageDescriptors } from './engine/typesetBridge'; import { toHebrewGematria } from './gematria'; @@ -230,12 +228,12 @@ export class TypesetOkApp { onOpenCommandPalette: () => this.commandPalette.show(), onViewModeChange: (mode) => this.setViewMode(mode), onExportPdf: () => this.handleSystemAction('export-pdf'), - onRenameDocument: (newTitle) => this.renameDocument(newTitle), onOpenProjects: () => this.welcomeModal.show(), onOpenSettings: () => this.settingsModal.show(), onOpenAbout: () => this.aboutModal.show(), onUndo: () => this.runEditCommand('undo'), - onRedo: () => this.runEditCommand('redo') + onRedo: () => this.runEditCommand('redo'), + onRenameDocument: (newTitle) => this.renameDocument(newTitle) }); this.root.appendChild(this.topBar.element); @@ -376,9 +374,6 @@ export class TypesetOkApp { } this.updateFormattingToolbarState(); } - if (this.activeFlowId) { - this.documentState.flows[this.activeFlowId] = this.storyEditor.getStory(); - } this.setDirty(true); this.scheduleRepaginate(); }, @@ -387,9 +382,6 @@ export class TypesetOkApp { if (sel) { this.storyEditor.setParagraphPatch(sel.paraId, {}); this.updateFormattingToolbarState(); - if (this.activeFlowId) { - this.documentState.flows[this.activeFlowId] = this.storyEditor.getStory(); - } this.setDirty(true); this.scheduleRepaginate(); } @@ -475,30 +467,9 @@ export class TypesetOkApp { this.storyEditor.loadStory(this.documentState.flows.gemara); this.repaginateAndSync(true); - // 9. Startup file inspection: if opened with a file argument, load it directly - const win = window as any; - if (win?.tokIpc?.onOpenFile) { - win.tokIpc.onOpenFile((file: string) => { - if (file) this.openProjectFile(file); - }); - } - - if (win?.tokIpc?.getStartupFile) { - win.tokIpc.getStartupFile().then((startupFile: string | null) => { - if (startupFile) { - this.openProjectFile(startupFile); - } else { - this.welcomeModal.setHasOpenDocument(false); - this.welcomeModal.show(); - } - }).catch(() => { - this.welcomeModal.setHasOpenDocument(false); - this.welcomeModal.show(); - }); - } else { - this.welcomeModal.setHasOpenDocument(false); - this.welcomeModal.show(); - } + // 9. Main startup: Open project picker unconditionally as the primary screen + this.welcomeModal.setHasOpenDocument(false); + this.welcomeModal.show(); } /** Standard keyboard shortcuts supporting both English and Hebrew layouts. */ @@ -542,6 +513,13 @@ export class TypesetOkApp { return; } + // F2: Rename Document + if (e.key === 'F2') { + e.preventDefault(); + this.topBar?.startRenaming(); + return; + } + const hasCtrl = e.ctrlKey || e.metaKey; if (!hasCtrl) return; @@ -760,8 +738,6 @@ export class TypesetOkApp { } private flowDisplayName(flowId: string): string { - const fromStructure = this.structureBar?.getFlows().find((f) => f.id === flowId); - if (fromStructure?.name) return fromStructure.name; const key = FLOW_NAME_KEYS[flowId]; return key ? t(key) : flowId; } @@ -858,9 +834,9 @@ export class TypesetOkApp { } }, onCancel: () => { - this.pendingImportResult = null; - if (!this.hasOpenDocument) { - this.welcomeModal.show(); + if (this.pendingImportResult) { + this.applyImportedDocument(this.pendingImportResult.result, this.pendingImportResult.filePath, 'prose'); + this.pendingImportResult = null; } } }); @@ -1134,7 +1110,7 @@ export class TypesetOkApp { this.storyFontFamilySelect = el('select', 'tok-select tok-select-xs', { title: 'גופן מקטע / פיסקה', 'aria-label': 'גופן מקטע / פיסקה', - style: 'height:24px;font-size:11px;padding:0 6px;min-width:130px;max-width:160px;flex-shrink:0;background:var(--tok-bg-surface-2, #1E293B);color:var(--tok-text-primary, #F8FAFC);border:1px solid var(--tok-border-strong, #334155);border-radius:4px;outline:none;' + style: 'height:24px;font-size:11px;padding:0 6px;min-width:90px;max-width:140px;background:var(--tok-bg-surface-2);color:var(--tok-text-primary);border:1px solid var(--tok-border-strong);border-radius:4px;' }) as HTMLSelectElement; for (const font of getAvailableFonts()) { const opt = el('option', undefined, { value: font.family }, font.family) as HTMLOptionElement; @@ -1264,7 +1240,7 @@ export class TypesetOkApp { private buildFindReplaceBar(): HTMLElement { this.findBar = el('div', 'tok-story-find-bar', { - style: 'display:none;padding:6px 10px;background:var(--tok-bg-surface-2, #1E293B);border-bottom:1px solid var(--tok-border-subtle);flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;' + style: 'display:none;padding:6px 10px;background:var(--tok-surface-2);border-bottom:1px solid var(--tok-border-subtle);flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;' }); this.findInput = el('input', 'tok-input', { @@ -1456,9 +1432,9 @@ export class TypesetOkApp { const isItalic = Boolean(fmt.italic); const isUnderline = Boolean(fmt.underline); - this.storyBoldBtn.style.background = isBold ? 'var(--tok-surface-hover)' : ''; - this.storyItalicBtn.style.background = isItalic ? 'var(--tok-surface-hover)' : ''; - this.storyUnderlineBtn.style.background = isUnderline ? 'var(--tok-surface-hover)' : ''; + this.storyBoldBtn.style.background = isBold ? 'var(--tok-bg-surface-hover)' : ''; + this.storyItalicBtn.style.background = isItalic ? 'var(--tok-bg-surface-hover)' : ''; + this.storyUnderlineBtn.style.background = isUnderline ? 'var(--tok-bg-surface-hover)' : ''; this.storyBoldBtn.setAttribute('aria-pressed', String(isBold)); this.storyItalicBtn.setAttribute('aria-pressed', String(isItalic)); this.storyUnderlineBtn.setAttribute('aria-pressed', String(isUnderline)); @@ -1859,9 +1835,6 @@ export class TypesetOkApp { this.activeTypesetAbortController = abortCtrl; const requestId = ++this.latestTypesetRequestId; - if (this.storyEditor && this.activeFlowId) { - this.documentState.flows[this.activeFlowId] = this.storyEditor.getStory(); - } this.statusBar?.setBusy(true, 'מעמד מחדש...'); const win = window as any; let newPages: PageDescriptor[] = []; @@ -1982,15 +1955,19 @@ export class TypesetOkApp { const name = TEMPLATE_TITLES[templateId] || BLANK_TEMPLATE_TITLE; this.documentTitle = name; this.documentState.title = name; - const tType = (templateId in TEMPLATE_TITLES ? templateId : (templateId === 'blank' ? 'blank' : 'gemara')) as TemplateType; - this.documentState.templateType = tType; + this.documentState.templateType = (templateId in TEMPLATE_TITLES ? templateId : 'gemara') as TemplateType; this.currentFilePath = ''; - this.documentState.flows = getDefaultFlowsForTemplate(tType); - this.structureBar.setFlows(getFlowsForTemplate(tType)); - this.activeFlowId = 'gemara'; - this.storyEditor.loadStory(this.documentState.flows.gemara || []); + if (isBlank) { + this.documentState.flows = { gemara: [], rashi: [], tosafot: [], notes: [] }; + } else if (templateId === 'prose') { + this.documentState.flows = JSON.parse(JSON.stringify(DEFAULT_PROSE_FLOWS)); + } else { + this.documentState.flows = JSON.parse(JSON.stringify(DEFAULT_TALMUD_FLOWS)); + } + this.activeFlowId = 'gemara'; + this.storyEditor.loadStory(this.documentState.flows.gemara); this.activePageIndex = 0; this.structureBar.setActivePage(0); this.repaginateAndSync(true); @@ -2009,22 +1986,6 @@ export class TypesetOkApp { } } - public renameDocument(newTitle: string): void { - const clean = newTitle.trim().replace(/\.tok$/i, ''); - if (!clean) return; - this.documentTitle = clean; - this.documentState.title = clean; - this.topBar.setDocumentTitle(clean, true); - this.setDirty(true); - addRecentProject({ - name: clean + '.tok', - path: this.currentFilePath || undefined, - pages: this.pages.length || 1, - lastSavedAt: new Date().toISOString() - }); - this.showToast(i18n.getLanguage() === 'en' ? `Document renamed to "${clean}"` : `שם המסמך שונה ל-"${clean}"`); - } - private async saveDocumentToFile(filePath: string): Promise { this.statusBar?.setBusy(true, 'שומר מסמך...'); try { @@ -2123,9 +2084,6 @@ export class TypesetOkApp { const activeStory = newState.flows[activeKey] || []; this.storyEditor.loadStory(activeStory); - const tType = (docRoot.metadata?.template || newState.templateType || 'gemara') as TemplateType; - this.structureBar.setFlows(getFlowsForTemplate(tType)); - this.activePageIndex = 0; this.structureBar.setActivePage(0); @@ -2146,13 +2104,23 @@ export class TypesetOkApp { }); } + public openAnyFile(filePath: string): void { + const ext = filePath.split('.').pop()?.toLowerCase(); + this.welcomeModal.setHasOpenDocument(true); + this.welcomeModal.hide(); + if (ext === 'docx' || ext === 'md' || ext === 'txt' || ext === 'markdown') { + this.promptAndImportDocument(filePath); + } else { + this.openProjectFile(filePath); + } + } + private openProjectFile(filePath: string): void { const isImportFormat = /\.(docx|txt|md|markdown|rtf)$/i.test(filePath); if (isImportFormat) { this.promptAndImportDocument(filePath); return; } - const win = window as any; const baseName = filePath.split(/[\\/]/).pop() || filePath; @@ -2163,7 +2131,12 @@ export class TypesetOkApp { }) .catch((err: any) => { console.error('[TOK] Failed to open document:', err); - this.showToast(tf('toastOpenError', { error: err?.message ?? String(err) }), true); + const rawMsg = err?.message ?? String(err); + const isZipError = rawMsg.includes('Zip error') || rawMsg.includes('EOCD'); + const errorMsg = isZipError + ? 'הקובץ אינו חבילת פרויקט (.tok) תקינה. אם זהו מסמך Word או Markdown, השתמש ב"ייבוא מסמך".' + : (err?.userMessage || rawMsg); + this.showToast(tf('toastOpenError', { error: errorMsg }), true); }); return; } @@ -2186,7 +2159,6 @@ export class TypesetOkApp { this.hasOpenDocument = true; addRecentProject({ name: baseName, path: filePath, pages: this.pages.length || 1, lastSavedAt: new Date().toISOString() }); this.welcomeModal.setHasOpenDocument(true); - this.welcomeModal.hide(); this.showToast(tf('toastOpenFile', { path: baseName })); } @@ -2220,19 +2192,19 @@ export class TypesetOkApp { let result: ImportResult; if (win.tokIpc?.readImportFile) { const fileInfo = await win.tokIpc.readImportFile(targetPath); - let content: string | Uint8Array; - if (fileInfo.isBinary) { + if (fileInfo.docxXml) { + result = parseDocxXml(fileInfo.docxXml, { fileName: fileInfo.fileName }); + } else if (fileInfo.isBinary) { const binStr = atob(fileInfo.dataBase64); const len = binStr.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binStr.charCodeAt(i); } - content = bytes; + result = importDocument(bytes, { fileName: fileInfo.fileName }); } else { - content = fileInfo.text || ''; + result = importDocument(fileInfo.text || '', { fileName: fileInfo.fileName }); } - result = importDocument(content, { fileName: fileInfo.fileName, docxXml: fileInfo.docxXml }); } else { throw new Error('IPC reader not available'); } @@ -2295,26 +2267,16 @@ export class TypesetOkApp { this.documentState = importResultToDocumentState(result, templateType); this.activeFlowId = 'gemara'; this.storyEditor.loadStory(this.documentState.flows.gemara); - this.structureBar.setFlows(getFlowsForTemplate(templateType)); - this.activePageIndex = 0; this.structureBar.setActivePage(0); this.repaginateAndSync(true); - if (filePath && filePath.toLowerCase().endsWith('.tok')) { - addRecentProject({ - name: (this.documentTitle || 'imported') + '.tok', - path: filePath, - pages: this.pages.length || 1, - lastSavedAt: new Date().toISOString() - }); - } else { - addRecentProject({ - name: (this.documentTitle || 'imported') + '.tok', - pages: this.pages.length || 1, - lastSavedAt: new Date().toISOString() - }); - } + addRecentProject({ + name: (this.documentTitle || 'imported') + '.tok', + path: filePath, + pages: this.pages.length || 1, + lastSavedAt: new Date().toISOString() + }); this.showToast(tf('toastImportSuccess', { title: result.title, count: result.paragraphs.length })); } @@ -2416,11 +2378,17 @@ export class TypesetOkApp { case 'open-about': this.aboutModal.show(); break; + case 'open-file-at-path': + if (typeof data === 'string' && data) { + if (!this.confirmDiscardUnsaved()) return; + this.openAnyFile(data); + } + break; case 'open-document': { if (!this.confirmDiscardUnsaved()) return; const win = window as any; if (typeof data === 'string' && data) { - this.openProjectFile(data); + this.openAnyFile(data); } else if (win.tokIpc?.showOpenDialog) { win.tokIpc.showOpenDialog({ filters: [{ name: 'TypesetOK Document', extensions: ['tok', 'json'] }] }) .then((filePath: string | null) => { @@ -2490,6 +2458,9 @@ export class TypesetOkApp { case 'export-pdf': this.openExportDialog(); break; + case 'rename-document': + this.topBar?.startRenaming(); + break; case 'open-text-cleanup': case 'text-cleanup': this.openTextCleanupWizard(); @@ -2600,6 +2571,16 @@ export class TypesetOkApp { }, 3200); } + public renameDocument(newTitle: string): void { + const trimmed = newTitle.trim(); + if (!trimmed || trimmed === this.documentTitle) return; + this.documentTitle = trimmed; + this.documentState.title = trimmed; + this.topBar.setDocumentTitle(trimmed, true); + this.setDirty(true); + this.showToast(tf('toastTitleRenamed', { title: trimmed })); + } + public setDirty(dirty: boolean): void { if (!this.documentState) return; this.documentState.isDirty = dirty; diff --git a/packages/tok-ui/src/components/ImportTemplateModal.ts b/packages/tok-ui/src/components/ImportTemplateModal.ts index 60bbc4d..a4719b4 100644 --- a/packages/tok-ui/src/components/ImportTemplateModal.ts +++ b/packages/tok-ui/src/components/ImportTemplateModal.ts @@ -146,7 +146,11 @@ export class ImportTemplateModal { card.addEventListener('click', () => { this.hide(); - this.callbacks.onSelect(opt.id); + try { + this.callbacks.onSelect(opt.id); + } catch (err) { + console.error('[TOK] Error applying template:', err); + } }); body.appendChild(card); diff --git a/packages/tok-ui/src/components/TopSystemBar.ts b/packages/tok-ui/src/components/TopSystemBar.ts index 5ae412c..1876f84 100644 --- a/packages/tok-ui/src/components/TopSystemBar.ts +++ b/packages/tok-ui/src/components/TopSystemBar.ts @@ -7,13 +7,13 @@ export interface TopSystemBarCallbacks { onOpenCommandPalette: () => void; onViewModeChange: (mode: ViewMode) => void; onExportPdf: () => void; - onRenameDocument?: (newTitle: string) => void; onOpenProjects?: () => void; onToggleLanguage?: () => void; onOpenSettings?: () => void; onOpenAbout?: () => void; onUndo?: () => void; onRedo?: () => void; + onRenameDocument?: (newTitle: string) => void; } type MenuItem = { type: 'separator' } | { labelKey: string; shortcut?: string; action: string }; @@ -26,6 +26,7 @@ const MENU_ITEMS: MenuItem[] = [ { labelKey: 'menuImportDocument', shortcut: 'Ctrl+I', action: 'import-document' }, { labelKey: 'menuSave', shortcut: 'Ctrl+S', action: 'save-document' }, { labelKey: 'menuSaveAs', shortcut: 'Ctrl+Shift+S', action: 'save-as' }, + { labelKey: 'menuRenameDocument', shortcut: 'F2', action: 'rename-document' }, { type: 'separator' }, { labelKey: 'menuTextCleanup', shortcut: 'Ctrl+Shift+C', action: 'open-text-cleanup' }, { labelKey: 'menuNormalizeNiqqud', action: 'normalize-hebrew' }, @@ -139,6 +140,55 @@ export class TopSystemBar { } } + public startRenaming(): void { + const titleContainer = this.element.querySelector('.tok-doc-title'); + if (!titleContainer) return; + const titleEl = titleContainer.querySelector('.tok-doc-title-name'); + if (!titleEl || titleEl.querySelector('input')) return; + + const currentTitle = this.documentTitle.replace(/\.tok$/i, ''); + const input = document.createElement('input'); + input.type = 'text'; + input.className = 'tok-doc-title-input tok-title-inline-input'; + input.value = currentTitle; + input.setAttribute('aria-label', t('menuRenameDocument')); + input.style.cssText = 'background: var(--tok-bg-surface-2); color: var(--tok-text-primary); border: 1px solid var(--tok-accent-text); border-radius: 4px; padding: 1px 6px; font-size: 13px; font-weight: 600; width: 180px; outline: none; font-family: inherit;'; + + let committed = false; + const commit = () => { + if (committed) return; + committed = true; + const next = input.value.trim(); + if (next && next !== currentTitle) { + this.callbacks.onRenameDocument?.(next); + } else { + titleEl.textContent = this.isDirty ? `• ${currentTitle}` : currentTitle; + } + }; + + const cancel = () => { + if (committed) return; + committed = true; + titleEl.textContent = this.isDirty ? `• ${currentTitle}` : currentTitle; + }; + + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + commit(); + } else if (e.key === 'Escape') { + e.preventDefault(); + cancel(); + } + }); + + input.addEventListener('blur', () => commit()); + + titleEl.replaceChildren(input); + input.focus(); + input.select(); + } + private render(): void { // innerHTML='' detaches an open dropdown; drop the reference too. this.closeDropdown(); @@ -160,68 +210,30 @@ export class TopSystemBar { if (this.documentTitle) { const doc = el('div', 'tok-doc-title', { title: this.documentTitle }); const cleanTitle = this.documentTitle.replace(/\.tok$/i, ''); - const titleName = el('span', 'tok-doc-title-name', { - role: 'button', + + const titleSpan = el('span', 'tok-doc-title-name', { tabindex: '0', - title: i18n.getLanguage() === 'en' ? 'Click to rename document' : 'לחץ לשינוי שם המסמך', - style: 'cursor: pointer; border-radius: 4px; padding: 1px 4px; transition: background 0.15s ease;' + role: 'button', + title: t('renameDocumentTooltip'), + style: 'cursor: pointer; border-radius: 3px; padding: 1px 4px;' }, this.isDirty ? `• ${cleanTitle}` : cleanTitle); - - titleName.addEventListener('mouseenter', () => { - titleName.style.background = 'var(--tok-bg-surface-2, rgba(255,255,255,0.08))'; - }); - titleName.addEventListener('mouseleave', () => { - titleName.style.background = 'transparent'; - }); - - const startEditing = () => { - const input = el('input', 'tok-doc-title-input', { - type: 'text', - value: cleanTitle, - style: 'background: var(--tok-bg-surface-2); color: var(--tok-text-primary); border: 1px solid var(--tok-accent-primary); border-radius: 4px; padding: 1px 6px; font-size: 13px; font-weight: 600; font-family: inherit; outline: none; width: ' + Math.max(120, cleanTitle.length * 9) + 'px;' - }) as HTMLInputElement; - - let committed = false; - const commit = () => { - if (committed) return; - committed = true; - const trimmed = input.value.trim(); - if (trimmed && trimmed !== cleanTitle) { - this.callbacks.onRenameDocument?.(trimmed); - } else { - this.render(); - } - }; - - input.addEventListener('keydown', (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - commit(); - } else if (e.key === 'Escape') { - e.preventDefault(); - committed = true; - this.render(); - } - }); - input.addEventListener('blur', () => commit()); - - titleName.replaceWith(input); - input.focus(); - input.select(); - }; - - titleName.addEventListener('click', (e) => { + titleSpan.addEventListener('dblclick', (e) => { e.stopPropagation(); - startEditing(); + this.startRenaming(); }); - titleName.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') { + titleSpan.addEventListener('keydown', (e) => { + if (e.key === 'F2' || e.key === 'Enter') { e.preventDefault(); - startEditing(); + this.startRenaming(); } }); + doc.appendChild(titleSpan); + + const renameBtn = iconButton('edit', t('renameDocumentTooltip'), () => { + this.startRenaming(); + }, { size: 12, className: 'tok-btn tok-btn-ghost tok-btn-xs', attrs: { style: 'height: 20px; width: 20px; padding: 0; opacity: 0.6; margin-inline-start: 2px;' } }); + doc.appendChild(renameBtn); - doc.appendChild(titleName); const sep = el('span', 'tok-doc-title-sep', { 'aria-hidden': 'true' }, '/'); sep.hidden = !this.pageLabel; doc.appendChild(sep); diff --git a/packages/tok-ui/src/engine/textImporter.ts b/packages/tok-ui/src/engine/textImporter.ts index 29b777d..dc8b394 100644 --- a/packages/tok-ui/src/engine/textImporter.ts +++ b/packages/tok-ui/src/engine/textImporter.ts @@ -442,8 +442,9 @@ export async function decompressDeflateRawAsync(bytes: Uint8Array): Promise = { @@ -398,6 +399,11 @@ const ICONS_SVG: Record = { + `, + edit: ` + + + ` }; diff --git a/packages/tok-ui/src/index.html b/packages/tok-ui/src/index.html index 2dcf66a..fdbc469 100644 --- a/packages/tok-ui/src/index.html +++ b/packages/tok-ui/src/index.html @@ -6,7 +6,10 @@ inline styles are used for per-element geometry. --> - TypesetOK + TypesetOK (TOK) + + + - - -
- - - diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..272dbc7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1598 @@ +{ + "name": "typesetok-monorepo", + "version": "0.9.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "typesetok-monorepo", + "version": "0.9.1", + "workspaces": [ + "packages/*" + ], + "devDependencies": { + "electron": "^29.1.0", + "esbuild": "^0.28.2", + "rcedit": "^5.0.2", + "typescript": "^5.4.0" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn-windows-exe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", + "integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@malept/cross-spawn-promise": "^1.1.0", + "is-wsl": "^2.2.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/electron": { + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/electron/-/electron-29.4.6.tgz", + "integrity": "sha512-fz8ndj8cmmf441t4Yh2FDP3Rn0JhLkVGvtUf2YVMbJ5SdJPlc0JWll9jYkhh60jDKVVCr/tBAmfxqRnXMWJpzg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rcedit": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-5.0.2.tgz", + "integrity": "sha512-dgysxaeXZ4snLpPjn8aVtHvZDCx+aRcvZbaWBgl1poU6OPustMvOkj9a9ZqASQ6i5Y5szJ13LSvglEOwrmgUxA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn-windows-exe": "^1.1.0" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/tok-canvas": { + "resolved": "packages/tok-canvas", + "link": true + }, + "node_modules/tok-electron": { + "resolved": "packages/tok-electron", + "link": true + }, + "node_modules/tok-story-editor": { + "resolved": "packages/tok-story-editor", + "link": true + }, + "node_modules/tok-ui": { + "resolved": "packages/tok-ui", + "link": true + }, + "node_modules/tok-viewer": { + "resolved": "packages/tok-viewer", + "link": true + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "packages/tok-canvas": { + "version": "0.9.1", + "devDependencies": { + "typescript": "^5.4.0" + } + }, + "packages/tok-electron": { + "version": "0.9.1", + "dependencies": { + "tok-canvas": "*", + "tok-ui": "*", + "tok-viewer": "*" + }, + "devDependencies": { + "electron": "^29.1.0", + "typescript": "^5.4.0" + } + }, + "packages/tok-story-editor": { + "version": "0.9.1", + "devDependencies": { + "typescript": "^5.4.0" + } + }, + "packages/tok-ui": { + "version": "0.9.1", + "dependencies": { + "tok-canvas": "*", + "tok-story-editor": "*", + "tok-viewer": "*" + }, + "devDependencies": { + "typescript": "^5.4.0" + } + }, + "packages/tok-viewer": { + "version": "0.9.1", + "devDependencies": { + "typescript": "^5.4.0" + } + } + } +} diff --git a/package.json b/package.json index 4db8dce..dd50421 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,13 @@ "packages/*" ], "scripts": { - "dev": "node server.mjs", "build:ts": "tsc --build", "bundle:ui": "node scripts/bundle-ui.mjs", "lint": "tsc --build", "build": "npm run build:ts && npm run bundle:ui", "package:desktop": "node scripts/build-desktop-bundle.mjs", "package:installer": "node scripts/build-installer.mjs", - "test": "node --test test/frontend.test.mjs test/milestone_features.test.mjs test/story_editor.test.mjs test/outcome_validation.test.mjs test/importer.test.mjs test/lifecycle_flow.test.mjs test/geometric_fidelity.test.mjs test/phase3_integration.test.mjs", + "test": "node --test test/frontend.test.mjs test/milestone_features.test.mjs test/story_editor.test.mjs test/outcome_validation.test.mjs test/importer.test.mjs test/lifecycle_flow.test.mjs test/geometric_fidelity.test.mjs test/phase3_integration.test.mjs test/issue20_regression.test.mjs", "start": "electron packages/tok-electron/dist/main.js", "perf:startup": "node scripts/measure-startup.mjs" }, diff --git a/packages/tok-electron/src/main.ts b/packages/tok-electron/src/main.ts index 5d356fe..8b203dd 100644 --- a/packages/tok-electron/src/main.ts +++ b/packages/tok-electron/src/main.ts @@ -475,10 +475,16 @@ function createWindow(): void { if (choice.response === 0) { // Save before exit - await win.webContents.executeJavaScript( - 'Boolean((window as any).appInstance?.handleSystemAction?.("save-document"))' + const saveResult = await win.webContents.executeJavaScript( + 'Promise.resolve((window as any).appInstance?.handleSystemAction?.("save-document"))' ).catch(() => false); + if (saveResult === false) { + logger.info('[MAIN] Save before exit was cancelled or failed; keeping window open'); + isClosingPromptActive = false; + return; + } + // Allow save I/O to flush setTimeout(() => { isForceClosing = true; @@ -842,7 +848,7 @@ handle('tok:decompress-raw', async (_, dataBase64: string) => { const buf = Buffer.from(requireString(dataBase64, 'dataBase64'), 'base64'); const zlib = await import('node:zlib'); const decompressed = zlib.inflateRawSync(buf); - return decompressed.toString('utf-8'); + return decompressed.toString('base64'); }); // Window and Application Lifecycle Handlers diff --git a/packages/tok-ui/src/app.ts b/packages/tok-ui/src/app.ts index 4d56a9f..926475f 100644 --- a/packages/tok-ui/src/app.ts +++ b/packages/tok-ui/src/app.ts @@ -26,6 +26,7 @@ import { MultiFlowDocumentState, DEFAULT_TALMUD_FLOWS, DEFAULT_PROSE_FLOWS, + getFlowsForTemplate, TemplateType, FlowPaginator } from './engine/FlowPaginator'; @@ -374,6 +375,9 @@ export class TypesetOkApp { } this.updateFormattingToolbarState(); } + if (this.activeFlowId) { + this.documentState.flows[this.activeFlowId] = this.storyEditor.getStory(); + } this.setDirty(true); this.scheduleRepaginate(); }, @@ -382,6 +386,9 @@ export class TypesetOkApp { if (sel) { this.storyEditor.setParagraphPatch(sel.paraId, {}); this.updateFormattingToolbarState(); + if (this.activeFlowId) { + this.documentState.flows[this.activeFlowId] = this.storyEditor.getStory(); + } this.setDirty(true); this.scheduleRepaginate(); } @@ -467,9 +474,30 @@ export class TypesetOkApp { this.storyEditor.loadStory(this.documentState.flows.gemara); this.repaginateAndSync(true); - // 9. Main startup: Open project picker unconditionally as the primary screen - this.welcomeModal.setHasOpenDocument(false); - this.welcomeModal.show(); + // 9. Startup file inspection: if opened with a file argument, load it directly + const win = window as any; + if (win?.tokIpc?.onOpenFile) { + win.tokIpc.onOpenFile((file: string) => { + if (file) this.openProjectFile(file); + }); + } + + if (win?.tokIpc?.getStartupFile) { + win.tokIpc.getStartupFile().then((startupFile: string | null) => { + if (startupFile) { + this.openProjectFile(startupFile); + } else { + this.welcomeModal.setHasOpenDocument(false); + this.welcomeModal.show(); + } + }).catch(() => { + this.welcomeModal.setHasOpenDocument(false); + this.welcomeModal.show(); + }); + } else { + this.welcomeModal.setHasOpenDocument(false); + this.welcomeModal.show(); + } } /** Standard keyboard shortcuts supporting both English and Hebrew layouts. */ @@ -738,6 +766,8 @@ export class TypesetOkApp { } private flowDisplayName(flowId: string): string { + const fromStructure = this.structureBar?.getFlows().find((f) => f.id === flowId); + if (fromStructure?.name) return fromStructure.name; const key = FLOW_NAME_KEYS[flowId]; return key ? t(key) : flowId; } @@ -834,9 +864,9 @@ export class TypesetOkApp { } }, onCancel: () => { - if (this.pendingImportResult) { - this.applyImportedDocument(this.pendingImportResult.result, this.pendingImportResult.filePath, 'prose'); - this.pendingImportResult = null; + this.pendingImportResult = null; + if (!this.hasOpenDocument) { + this.welcomeModal.show(); } } }); @@ -2159,6 +2189,7 @@ export class TypesetOkApp { this.hasOpenDocument = true; addRecentProject({ name: baseName, path: filePath, pages: this.pages.length || 1, lastSavedAt: new Date().toISOString() }); this.welcomeModal.setHasOpenDocument(true); + this.welcomeModal.hide(); this.showToast(tf('toastOpenFile', { path: baseName })); } @@ -2267,6 +2298,7 @@ export class TypesetOkApp { this.documentState = importResultToDocumentState(result, templateType); this.activeFlowId = 'gemara'; this.storyEditor.loadStory(this.documentState.flows.gemara); + this.structureBar.setFlows(getFlowsForTemplate(templateType)); this.activePageIndex = 0; this.structureBar.setActivePage(0); this.repaginateAndSync(true); diff --git a/packages/tok-ui/src/index.html b/packages/tok-ui/src/index.html index fdbc469..538e73c 100644 --- a/packages/tok-ui/src/index.html +++ b/packages/tok-ui/src/index.html @@ -220,13 +220,13 @@ /* Label above a field, unit shown inside the field */ .tok-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1; } - .tok-field-label { font-size: 12px; color: var(--tok-text-secondary); user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + .tok-field-label { font-size: 12px; color: var(--tok-text-secondary); user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; max-width: 100%; } .tok-field-label.tok-scrub { cursor: ew-resize; } .tok-field-label.tok-scrub:hover { color: var(--tok-accent-text); } - .tok-field-box { position: relative; } - .tok-field-box .tok-input { padding-inline-end: 38px; } + .tok-field-box { position: relative; min-width: 0; } + .tok-field-box .tok-input { padding-inline-end: 38px; width: 100%; box-sizing: border-box; } .tok-field-unit { position: absolute; inset-inline-end: 10px; top: 50%; transform: translateY(-50%); font-size: 12px; color: var(--tok-text-muted); pointer-events: none; } - .tok-field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } + .tok-field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; min-width: 0; } /* Segmented control */ .tok-seg { display: inline-flex; gap: 2px; padding: 3px; background: var(--tok-seg-bg); border: 1px solid var(--tok-border-subtle); border-radius: 9px; } @@ -501,13 +501,15 @@ .tok-story-toolbar { height: 46px; flex: none; display: flex; align-items: center; gap: 8px; padding: 0 16px; border-bottom: 1px solid var(--tok-border-subtle); background: var(--tok-bg-surface-1); - overflow-x: auto; overflow-y: hidden; scrollbar-width: none; min-width: 0; + overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -ms-overflow-style: none; } + .tok-story-toolbar::-webkit-scrollbar { display: none; } .tok-story-toolbar > * { flex-shrink: 0; } .tok-story-toolbar .tok-grow { flex: 1; } .tok-chip-select { height: 30px; display: inline-flex; align-items: center; gap: 8px; padding: 0 10px; border: 1px solid var(--tok-border-subtle); border-radius: 7px; background: var(--tok-bg-surface-2); font-size: 13px; font-weight: 500; + white-space: nowrap; flex-shrink: 0; min-width: 0; } .tok-story-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 48px; } .tok-story-editor-content { @@ -589,9 +591,10 @@ animation: tokPop 0.12s ease-out; white-space: nowrap; max-width: calc(100vw - 32px); } - .tok-action-hud .tok-select, .tok-action-hud .tok-input { height: 28px; width: auto; font-size: 12px; } - .tok-hud-divider { width: 1px; height: 18px; background: var(--tok-border-subtle); margin: 0 2px; } - .tok-hud-btn { min-width: 28px; height: 28px; padding: 0 6px; border: none; border-radius: 6px; background: transparent; color: var(--tok-text-secondary); font-size: 12px; display: inline-flex; align-items: center; justify-content: center; gap: 4px; } + .tok-action-hud > * { flex-shrink: 0; } + .tok-action-hud .tok-select, .tok-action-hud .tok-input { height: 28px; width: auto; font-size: 12px; flex-shrink: 0; } + .tok-hud-divider { width: 1px; height: 18px; background: var(--tok-border-subtle); margin: 0 2px; flex-shrink: 0; } + .tok-hud-btn { min-width: 28px; height: 28px; padding: 0 6px; border: none; border-radius: 6px; background: transparent; color: var(--tok-text-secondary); font-size: 12px; display: inline-flex; align-items: center; justify-content: center; gap: 4px; white-space: nowrap; flex-shrink: 0; } .tok-hud-btn:hover { background: var(--tok-bg-surface-hover); color: var(--tok-text-primary); } .tok-hud-btn[aria-pressed="true"] { background: var(--tok-accent-soft); color: var(--tok-accent-text); } diff --git a/test/frontend.test.mjs b/test/frontend.test.mjs index 43e428f..0d2799a 100644 --- a/test/frontend.test.mjs +++ b/test/frontend.test.mjs @@ -3303,146 +3303,3 @@ describe('Core Feature: Hebrew Text Cleanup & Dibur HaMatchil Wizard (Proposals assert.equal(cleaned[0].spans[0].format.italic, true); }); }); - -describe('Phase 3.2 Regression Suite: Issue #20 Verifications', () => { - test('app.ts hides welcomeModal on template select, import, and document open', () => { - const appTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/app.ts'), 'utf-8'); - assert.ok(appTs.includes('this.welcomeModal.hide()'), 'WelcomeModal.hide() must be called'); - assert.ok(/onClose:\s*\(\)\s*=>\s*\{\s*this\.welcomeModalInstance\?\.hide\(\);/.test(appTs), 'Modal onClose must hide modal'); - }); - - test('ImportTemplateModal uses tok-overlay and dismisses cleanly without ghost backdrop', () => { - const modalTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/components/ImportTemplateModal.ts'), 'utf-8'); - assert.ok(modalTs.includes("tok-overlay tok-import-template-overlay"), 'Uses tok-overlay class'); - assert.ok(modalTs.includes("tok-dialog tok-modal-card"), 'Uses tok-dialog class'); - assert.ok(modalTs.includes("closeOnBackdrop: true"), 'Enables backdrop closing'); - }); - - test('Electron main process handles window close cancel without locking exit', () => { - const mainTs = fs.readFileSync(path.join(rootDir, 'packages/tok-electron/src/main.ts'), 'utf-8'); - assert.ok(mainTs.includes('isClosingPromptActive = false'), 'isClosingPromptActive must be cleared on cancel'); - }); - - test('textImporter supports DecompressionStream and IPC fallback for deflate-raw', () => { - const importerTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/engine/textImporter.ts'), 'utf-8'); - assert.ok(importerTs.includes('DecompressionStream'), 'Uses standard DecompressionStream'); - assert.ok(importerTs.includes('decompressDeflateRawAsync'), 'Provides async decompression helper'); - }); - - test('preload and main expose decompressRaw IPC bridge', () => { - const preloadTs = fs.readFileSync(path.join(rootDir, 'packages/tok-electron/src/preload.ts'), 'utf-8'); - const mainTs = fs.readFileSync(path.join(rootDir, 'packages/tok-electron/src/main.ts'), 'utf-8'); - assert.ok(preloadTs.includes('decompressRaw'), 'Preload exposes decompressRaw'); - assert.ok(mainTs.includes('tok:decompress-raw'), 'Main handles tok:decompress-raw'); - }); - - test('openProjectFile redirects non-.tok files to import pipeline', () => { - const appTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/app.ts'), 'utf-8'); - assert.ok(appTs.includes('isImportFormat = /\\.(docx|txt|md|markdown|rtf)$/i.test(filePath)'), 'Routes non-.tok files to import'); - }); - - test('Rust tok-cli open-package falls back gracefully for raw JSON or custom packages', () => { - const cliRs = fs.readFileSync(path.join(rootDir, 'crates/tok-cli/src/main.rs'), 'utf-8'); - assert.ok(cliRs.includes('load_document_input(input, 1)'), 'handle_open_package uses load_document_input for fallback'); - }); - - test('TopSystemBar supports accessible inline document title renaming', () => { - const barTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/components/TopSystemBar.ts'), 'utf-8'); - assert.ok(barTs.includes('onRenameDocument?: (newTitle: string) => void;'), 'Provides onRenameDocument callback'); - assert.ok(barTs.includes('tok-doc-title-input'), 'Renders inline input for renaming'); - }); - - test('WelcomeModal supports inline renaming in recent projects list without window.prompt', () => { - const modalTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/components/WelcomeModal.ts'), 'utf-8'); - assert.ok(modalTs.includes('tok-rename-input'), 'Uses accessible inline rename input'); - assert.ok(!modalTs.includes('window.prompt'), 'Does not use unsupported window.prompt'); - }); - - test('provides authentic default flows for all template types', async () => { - const { getDefaultFlowsForTemplate } = await loadTs('packages/tok-ui/src/engine/FlowPaginator.ts'); - const mikraot = getDefaultFlowsForTemplate('mikraot'); - assert.ok(mikraot.gemara.length > 0, 'Mikraot has Torah verses'); - assert.ok(mikraot.rashi.length > 0, 'Mikraot has Rashi'); - assert.ok(mikraot.tosafot.length > 0, 'Mikraot has Targum'); - assert.ok(mikraot.notes.length > 0, 'Mikraot has Ramban/commentary'); - - const notes = getDefaultFlowsForTemplate('notes'); - assert.ok(notes.gemara.length > 0, 'Notes template has main text'); - assert.ok(notes.notes.length > 0, 'Notes template has footnotes'); - - const bulletin = getDefaultFlowsForTemplate('bulletin'); - assert.ok(bulletin.gemara.length > 0, 'Bulletin has main article'); - assert.ok(bulletin.rashi.length > 0, 'Bulletin has secondary column'); - assert.ok(bulletin.notes.length > 0, 'Bulletin has sidebar notices'); - - const blank = getDefaultFlowsForTemplate('blank'); - assert.equal(blank.gemara.length, 1, 'Blank has clean initial page'); - assert.equal(blank.gemara[0].text, '', 'Blank text is empty'); - }); - - test('returns template-specific metadata for StructureBar tabs', async () => { - const { getFlowsForTemplate } = await loadTs('packages/tok-ui/src/engine/FlowPaginator.ts'); - const mikraotItems = getFlowsForTemplate('mikraot'); - assert.ok(mikraotItems.some((f) => f.name.includes('מקרא')), 'Includes Mikra'); - assert.ok(mikraotItems.some((f) => f.name.includes('תרגום')), 'Includes Targum'); - - const notesItems = getFlowsForTemplate('notes'); - assert.equal(notesItems.length, 2, 'Notes template has 2 flows'); - assert.ok(notesItems.some((f) => f.name === 'טקסט ראשי')); - - const bulletinItems = getFlowsForTemplate('bulletin'); - assert.equal(bulletinItems.length, 3, 'Bulletin has 3 flows'); - }); - - test('renderLineText sanitizes and renders safe formatting tags and drop caps', async () => { - const { renderLineText } = await loadTs('packages/tok-ui/src/engine/typesetBridge.ts'); - const formatted = renderLineText('זהו טקסט מודגש וגם מעוצב'); - assert.ok(formatted.includes('טקסט מודגש'), 'Renders bold tag'); - assert.ok(formatted.includes('מעוצב'), 'Renders span tag'); - - const dropCap = renderLineText('גמרא. מאימתי קורין', true); - assert.ok(dropCap.includes('class="tok-opening-word"'), 'Highlights opening word as drop cap'); - }); - - test('formatEngineFont accurately extracts bold weights', async () => { - const { formatEngineFont } = await loadTs('packages/tok-ui/src/engine/typesetBridge.ts'); - const boldFont = formatEngineFont('Frank Ruhl Libre Bold', true); - assert.equal(boldFont.fontWeight, 700); - assert.ok(boldFont.fontFamily.includes('Frank Ruhl Libre')); - }); - - test('Rust tok-cli extracts document_settings and passes them to TypesettingEngineConfig', () => { - const cliRs = fs.readFileSync(path.join(rootDir, 'crates/tok-cli/src/main.rs'), 'utf-8'); - assert.ok(cliRs.includes('fn engine_config_from_root'), 'CLI defines engine_config_from_root'); - assert.ok(cliRs.includes('pageWidthMm'), 'Reads pageWidthMm from document_settings'); - assert.ok(cliRs.includes('pageHeightMm'), 'Reads pageHeightMm from document_settings'); - assert.ok(cliRs.includes('marginTopMm'), 'Reads marginTopMm from document_settings'); - }); - - test('Rust TypesettingEngine typeset_document respects document settings dynamically', () => { - const engineRs = fs.readFileSync(path.join(rootDir, 'crates/tok-typeset/src/engine.rs'), 'utf-8'); - assert.ok(engineRs.includes('typeset_document_internal'), 'Engine delegates to typeset_document_internal with updated config'); - assert.ok(engineRs.includes('active_config != self.config'), 'Detects custom document dimensions and creates adapted engine'); - }); - - test('ThemeManager defines --tok-surface-2 and --tok-text aliases for dark themes', () => { - const themeTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/theme.ts'), 'utf-8'); - assert.ok(themeTs.includes("'--tok-surface-2'"), 'Exposes --tok-surface-2 alias'); - assert.ok(themeTs.includes("'--tok-text'"), 'Exposes --tok-text alias'); - }); - - test('CSS index.html clips text labels and prevents action HUD and chip overflow', () => { - const html = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/index.html'), 'utf-8'); - assert.ok(html.includes('.tok-field-label { font-size: 12px; color: var(--tok-text-secondary); user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;'), 'Clips field labels to prevent overflow'); - assert.ok(html.includes('.tok-action-hud {') && html.includes('white-space: nowrap; max-width: calc(100vw - 32px);'), 'Action HUD prevents text line wrapping'); - }); - - test('Story toolbar has horizontal scrolling and non-collapsing controls in Split View', () => { - const html = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/index.html'), 'utf-8'); - assert.ok(html.includes('.tok-story-toolbar > * { flex-shrink: 0; }'), 'Toolbar controls do not collapse'); - assert.ok(html.includes('overflow-x: auto; overflow-y: hidden; scrollbar-width: none;'), 'Toolbar scrolls horizontally if needed'); - }); -}); - - - diff --git a/test/issue20_regression.test.mjs b/test/issue20_regression.test.mjs new file mode 100644 index 0000000..6e9bc3f --- /dev/null +++ b/test/issue20_regression.test.mjs @@ -0,0 +1,188 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const rootDir = path.resolve(__dirname, '..'); + +let esbuildMod = null; +async function loadTs(relPath) { + esbuildMod ||= await import('esbuild'); + const os = await import('node:os'); + const outfile = path.join( + os.tmpdir(), + `tok-test-${process.pid}-${relPath.replace(/[^a-zA-Z0-9]+/g, '_')}-${Date.now()}.mjs` + ); + esbuildMod.buildSync({ + entryPoints: [path.join(rootDir, relPath)], + bundle: true, + format: 'esm', + platform: 'neutral', + outfile, + logLevel: 'silent', + }); + const mod = await import(pathToFileURL(outfile).href); + try { fs.unlinkSync(outfile); } catch {} + return mod; +} + +describe('GitHub Issue #20 Regression Tests: All Bugs Verified', () => { + + describe('Bug 1 & 3: Welcome Modal & Flow Selection Lifecycle', () => { + it('app.ts hides welcomeModal on template select, import, and document open', () => { + const appTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/app.ts'), 'utf-8'); + assert.ok(appTs.includes('this.welcomeModal.hide()'), 'WelcomeModal.hide() must be called'); + assert.ok(/onClose:\s*\(\)\s*=>\s*\{\s*this\.welcomeModalInstance\?\.hide\(\);/.test(appTs), 'Modal onClose must hide modal'); + }); + + it('ImportTemplateModal uses tok-overlay and dismisses cleanly without ghost backdrop', () => { + const modalTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/components/ImportTemplateModal.ts'), 'utf-8'); + assert.ok(modalTs.includes("tok-overlay tok-import-template-overlay"), 'Uses tok-overlay class'); + assert.ok(modalTs.includes("tok-dialog tok-modal-card"), 'Uses tok-dialog class'); + assert.ok(modalTs.includes("closeOnBackdrop: true"), 'Enables backdrop closing'); + }); + + it('Electron main process handles window close cancel without locking exit', () => { + const mainTs = fs.readFileSync(path.join(rootDir, 'packages/tok-electron/src/main.ts'), 'utf-8'); + assert.ok(mainTs.includes('isClosingPromptActive = false'), 'isClosingPromptActive must be cleared on cancel'); + }); + }); + + describe('Bug 2: Word (.docx) Import & IPC Decompression Bridge', () => { + it('textImporter supports DecompressionStream and IPC fallback for deflate-raw', () => { + const importerTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/engine/textImporter.ts'), 'utf-8'); + assert.ok(importerTs.includes('DecompressionStream'), 'Uses standard DecompressionStream'); + assert.ok(importerTs.includes('decompressDeflateRawAsync'), 'Provides async decompression helper'); + }); + + it('preload and main expose decompressRaw IPC bridge', () => { + const preloadTs = fs.readFileSync(path.join(rootDir, 'packages/tok-electron/src/preload.ts'), 'utf-8'); + const mainTs = fs.readFileSync(path.join(rootDir, 'packages/tok-electron/src/main.ts'), 'utf-8'); + assert.ok(preloadTs.includes('decompressRaw'), 'Preload exposes decompressRaw'); + assert.ok(mainTs.includes('tok:decompress-raw'), 'Main handles tok:decompress-raw'); + }); + }); + + describe('Bug 4: Non-.tok file routing & Zip EOCD Prevention', () => { + it('openProjectFile redirects non-.tok files to import pipeline', () => { + const appTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/app.ts'), 'utf-8'); + assert.ok(appTs.includes('isImportFormat = /\\.(docx|txt|md|markdown|rtf)$/i.test(filePath)'), 'Routes non-.tok files to import'); + }); + + it('Rust tok-cli open-package falls back gracefully for raw JSON or custom packages', () => { + const cliRs = fs.readFileSync(path.join(rootDir, 'crates/tok-cli/src/main.rs'), 'utf-8'); + assert.ok(cliRs.includes('load_document_input(input, 1)'), 'handle_open_package uses load_document_input for fallback'); + }); + }); + + describe('Bug 5: Inline Document Renaming', () => { + it('TopSystemBar supports accessible inline document title renaming', () => { + const barTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/components/TopSystemBar.ts'), 'utf-8'); + assert.ok(barTs.includes('onRenameDocument?: (newTitle: string) => void;'), 'Provides onRenameDocument callback'); + assert.ok(barTs.includes('tok-doc-title-input'), 'Renders inline input for renaming'); + }); + + it('WelcomeModal supports inline renaming in recent projects list without window.prompt', () => { + const modalTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/components/WelcomeModal.ts'), 'utf-8'); + assert.ok(modalTs.includes('tok-rename-input'), 'Uses accessible inline rename input'); + assert.ok(!modalTs.includes('window.prompt'), 'Does not use unsupported window.prompt'); + }); + }); + + describe('Bug 6: Template-Specific Flows & StructureBar Updates', () => { + it('provides authentic default flows for all template types', async () => { + const { getDefaultFlowsForTemplate } = await loadTs('packages/tok-ui/src/engine/FlowPaginator.ts'); + const mikraot = getDefaultFlowsForTemplate('mikraot'); + assert.ok(mikraot.gemara.length > 0, 'Mikraot has Torah verses'); + assert.ok(mikraot.rashi.length > 0, 'Mikraot has Rashi'); + assert.ok(mikraot.tosafot.length > 0, 'Mikraot has Targum'); + assert.ok(mikraot.notes.length > 0, 'Mikraot has Ramban/commentary'); + + const notes = getDefaultFlowsForTemplate('notes'); + assert.ok(notes.gemara.length > 0, 'Notes template has main text'); + assert.ok(notes.notes.length > 0, 'Notes template has footnotes'); + + const bulletin = getDefaultFlowsForTemplate('bulletin'); + assert.ok(bulletin.gemara.length > 0, 'Bulletin has main article'); + assert.ok(bulletin.rashi.length > 0, 'Bulletin has secondary column'); + assert.ok(bulletin.notes.length > 0, 'Bulletin has sidebar notices'); + + const blank = getDefaultFlowsForTemplate('blank'); + assert.equal(blank.gemara.length, 1, 'Blank has clean initial page'); + assert.equal(blank.gemara[0].text, '', 'Blank text is empty'); + }); + + it('returns template-specific metadata for StructureBar tabs', async () => { + const { getFlowsForTemplate } = await loadTs('packages/tok-ui/src/engine/FlowPaginator.ts'); + const mikraotItems = getFlowsForTemplate('mikraot'); + assert.ok(mikraotItems.some((f) => f.name.includes('מקרא')), 'Includes Mikra'); + assert.ok(mikraotItems.some((f) => f.name.includes('תרגום')), 'Includes Targum'); + + const notesItems = getFlowsForTemplate('notes'); + assert.equal(notesItems.length, 2, 'Notes template has 2 flows'); + assert.ok(notesItems.some((f) => f.name === 'טקסט ראשי')); + + const bulletinItems = getFlowsForTemplate('bulletin'); + assert.equal(bulletinItems.length, 3, 'Bulletin has 3 flows'); + }); + }); + + describe('Bug 7 & 16: Live Story Editor Synchronization & Formatting Rendering', () => { + it('renderLineText sanitizes and renders safe formatting tags and drop caps', async () => { + const { renderLineText } = await loadTs('packages/tok-ui/src/engine/typesetBridge.ts'); + const formatted = renderLineText('זהו טקסט מודגש וגם מעוצב'); + assert.ok(formatted.includes('טקסט מודגש'), 'Renders bold tag'); + assert.ok(formatted.includes('מעוצב'), 'Renders span tag'); + + const dropCap = renderLineText('גמרא. מאימתי קורין', true); + assert.ok(dropCap.includes('class="tok-opening-word"'), 'Highlights opening word as drop cap'); + }); + + it('formatEngineFont accurately extracts bold weights', async () => { + const { formatEngineFont } = await loadTs('packages/tok-ui/src/engine/typesetBridge.ts'); + const boldFont = formatEngineFont('Frank Ruhl Libre Bold', true); + assert.equal(boldFont.fontWeight, 700); + assert.ok(boldFont.fontFamily.includes('Frank Ruhl Libre')); + }); + }); + + describe('Bug 8 & 13 & 14: Custom Page Dimensions and Margins', () => { + it('Rust tok-cli extracts document_settings and passes them to TypesettingEngineConfig', () => { + const cliRs = fs.readFileSync(path.join(rootDir, 'crates/tok-cli/src/main.rs'), 'utf-8'); + assert.ok(cliRs.includes('fn engine_config_from_root'), 'CLI defines engine_config_from_root'); + assert.ok(cliRs.includes('pageWidthMm'), 'Reads pageWidthMm from document_settings'); + assert.ok(cliRs.includes('pageHeightMm'), 'Reads pageHeightMm from document_settings'); + assert.ok(cliRs.includes('marginTopMm'), 'Reads marginTopMm from document_settings'); + }); + + it('Rust TypesettingEngine typeset_document respects document settings dynamically', () => { + const engineRs = fs.readFileSync(path.join(rootDir, 'crates/tok-typeset/src/engine.rs'), 'utf-8'); + assert.ok(engineRs.includes('typeset_document_internal'), 'Engine delegates to typeset_document_internal with updated config'); + assert.ok(engineRs.includes('active_config != self.config'), 'Detects custom document dimensions and creates adapted engine'); + }); + }); + + describe('Bug 9 & 10 & 11: Dark Theme Styling, Text Overflow Clipping & Split View Layout', () => { + it('ThemeManager defines --tok-surface-2 and --tok-text aliases for dark themes', () => { + const themeTs = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/theme.ts'), 'utf-8'); + assert.ok(themeTs.includes("'--tok-surface-2'"), 'Exposes --tok-surface-2 alias'); + assert.ok(themeTs.includes("'--tok-text'"), 'Exposes --tok-text alias'); + }); + + it('CSS index.html clips text labels and prevents action HUD and chip overflow', () => { + const html = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/index.html'), 'utf-8'); + assert.ok(html.includes('.tok-field-label { font-size: 12px; color: var(--tok-text-secondary); user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;'), 'Clips field labels to prevent overflow'); + assert.ok(html.includes('.tok-action-hud {') && html.includes('white-space: nowrap; max-width: calc(100vw - 32px);'), 'Action HUD prevents text line wrapping'); + }); + + it('Story toolbar has horizontal scrolling and non-collapsing controls in Split View', () => { + const html = fs.readFileSync(path.join(rootDir, 'packages/tok-ui/src/index.html'), 'utf-8'); + assert.ok(html.includes('.tok-story-toolbar > * { flex-shrink: 0; }'), 'Toolbar controls do not collapse'); + assert.ok(html.includes('overflow-x: auto; overflow-y: hidden; scrollbar-width: none;'), 'Toolbar scrolls horizontally if needed'); + }); + }); + +});