diff --git a/package-lock.json b/package-lock.json index d939ac5..ff8b8c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "eslint-plugin-unicorn": "^57.0.0", "globals": "^16.0.0", "obsidian": "^1.8.7", - "obsidian-dev-utils": "^21.0.0", + "obsidian-dev-utils": "latest", "obsidian-typings": "^2.35.0", "prettier": "3.5.3", "prettier-plugin-svelte": "^3.3.3", @@ -37,6 +37,7 @@ "svelte-eslint-parser": "^0.43.0", "svelte-infinite": "^0.5.0", "svelte-preprocess": "^6.0.3", + "type-fest": "^4.39.1", "typescript": "^5.8.2", "typescript-eslint": "^8.25.0" } @@ -330,9 +331,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-aws": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.9.tgz", - "integrity": "sha512-bDYdnnJGwSkIZ4gzrauu7qzOs/ZAY/FnU4k11LgdMI8BhwMfsbsy2EI1iS+sD/BI5ZnNT9kU5YR3WADeNOmhRg==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.10.tgz", + "integrity": "sha512-0qW4sI0GX8haELdhfakQNuw7a2pnWXz3VYQA2MpydH2xT2e6EN9DWFpKAi8DfcChm8MgDAogKkoHtIo075iYng==", "dev": true, "license": "MIT" }, @@ -389,9 +390,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-data-science": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.7.tgz", - "integrity": "sha512-XhAkK+nSW6zmrnWzusmZ1BpYLc62AWYHZc2p17u4nE2Z9XG5DleG55PCZxXQTKz90pmwlhFM9AfpkJsYaBWATA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.8.tgz", + "integrity": "sha512-uyAtT+32PfM29wRBeAkUSbkytqI8bNszNfAz2sGPtZBRmsZTYugKMEO9eDjAIE/pnT9CmbjNuoiXhk+Ss4fCOg==", "dev": true, "license": "MIT" }, @@ -403,9 +404,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-docker": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.12.tgz", - "integrity": "sha512-6d25ZPBnYZaT9D9An/x6g/4mk542R8bR3ipnby3QFCxnfdd6xaWiTcwDPsCgwN2aQZIQ1jX/fil9KmBEqIK/qA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.13.tgz", + "integrity": "sha512-85X+ZC/CPT3ie26DcfeMFkZSNuhS8DlAqPXzAjilHtGE/Nj+QnS3jyBz0spDJOJrjh8wx1+ro2oCK98sbVcztw==", "dev": true, "license": "MIT" }, @@ -424,9 +425,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-en_us": { - "version": "4.3.36", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.36.tgz", - "integrity": "sha512-GkXmwBvMomji6OKgeRbKyyHSk84zLexb+ImpR5HjjiIyGMw68wrbBbqOzn52NjZjLdjk7B0+tB2u6qYZbnDapw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.0.tgz", + "integrity": "sha512-TEfVT2NwvI9k1/ECjuC7GbULxenjJAbTLWMri1eMRk3mRGtqg5j0XzvvNRFuJWq8X48MdGVjsD+ZVI/VR94+eQ==", "dev": true, "license": "MIT" }, @@ -494,9 +495,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-golang": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.19.tgz", - "integrity": "sha512-VS+oinB2/CbgmHE06kMJlj52OVMZM0S2EEXph3oaroNTgTuclSwdFylQmOEjquZi55kW+n3FM9MyWXiitB7Dtg==", + "version": "6.0.20", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.20.tgz", + "integrity": "sha512-b7nd9XXs+apMMzNSWorjirQsbmlwcTC0ViQJU8u+XNose3z0y7oNeEpbTPTVoN1+1sO9aOHuFwfwoOMFCDS14Q==", "dev": true, "license": "MIT" }, @@ -612,9 +613,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-npm": { - "version": "5.1.33", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.33.tgz", - "integrity": "sha512-ZJ7mFmAmaxLNix8JEnCCGhsA4PskQjaF+aDcShyxD5o43yKhSwKg7ePhgHhwAa19FjB7Kea2h4k++Oshu8xziQ==", + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.34.tgz", + "integrity": "sha512-UrUYqRQX864Cx9QJkg7eEIxmjYGqcje+x1j7bzl+a3jCKwT6jm+p0off6VEOf3EReHP0dWUSYO3Q0+pLL/N+FQ==", "dev": true, "license": "MIT" }, @@ -640,13 +641,13 @@ "license": "MIT" }, "node_modules/@cspell/dict-python": { - "version": "4.2.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.16.tgz", - "integrity": "sha512-LkQssFt1hPOWXIQiD8ScTkz/41RL7Ti0V/2ytUzEW82dc0atIEksrBg8MuOjWXktp0Dk5tDwRLgmIvhV3CFFOA==", + "version": "4.2.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.17.tgz", + "integrity": "sha512-xqMKfVc8d7yDaOChFdL2uWAN3Mw9qObB/Zr6t5w1OHbi23gWs7V1lI9d0mXAoqSK6N3mosbum4OIq/FleQDnlw==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/dict-data-science": "^2.0.7" + "@cspell/dict-data-science": "^2.0.8" } }, "node_modules/@cspell/dict-r": { @@ -685,9 +686,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-software-terms": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.0.3.tgz", - "integrity": "sha512-YSNSt+7mTKMMJRT26+uOmWrx5Yaqs+WqTbLb0an0zIcnZHjxrH0eVYlvZmxQcF2JiDuhjQvo9YaEnu+MI09HZg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.0.5.tgz", + "integrity": "sha512-ZjAOa8FI8/JrxaRqKT3eS7AQXFjU174xxQoKYMkmdwSyNIj7WUCAg10UeLqeMjFVv36zIO0Hm0dD2+Bvn18SLA==", "dev": true, "license": "MIT" }, @@ -971,6 +972,21 @@ "tslib": "^2.4.0" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz", + "integrity": "sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", @@ -1426,9 +1442,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1549,9 +1565,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", "dev": true, "license": "MIT", "engines": { @@ -2644,9 +2660,9 @@ } }, "node_modules/@pkgr/core": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.0.tgz", - "integrity": "sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.1.tgz", + "integrity": "sha512-VzgHzGblFmUeBmmrk55zPyrQIArQN4vujc9shWytaPdB3P7qhi0cpaiKIr7tlCmFv2lYUwnLospIqjL9ZSAhhg==", "dev": true, "license": "MIT", "engines": { @@ -2787,9 +2803,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -2926,9 +2942,9 @@ } }, "node_modules/@types/luxon": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.0.tgz", - "integrity": "sha512-RtEj20xRyG7cRp142MkQpV3GRF8Wo2MtDkKLz65MQs7rM1Lh8bz+HtfPXCCJEYpnDFu6VwAq/Iv2Ikyp9Jw/hw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", + "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", "dev": true, "license": "MIT" }, @@ -2989,9 +3005,9 @@ "license": "MIT" }, "node_modules/@types/picomatch": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.2.tgz", - "integrity": "sha512-n0i8TD3UDB7paoMMxA3Y65vUncFJXjcUf7lQY7YyKGl6031FNjfsLs6pdLFCy2GNFxItPJG8GvvpbZc2skH7WA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.0.tgz", + "integrity": "sha512-J1Bng+wlyEERWSgJQU1Pi0HObCLVcr994xT/M+1wcl/yNRTGBupsCxthgkdYG+GCOMaQH7iSVUY3LJVBBqG7MQ==", "dev": true, "license": "MIT" }, @@ -3068,17 +3084,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz", - "integrity": "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", + "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/type-utils": "8.29.0", - "@typescript-eslint/utils": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/type-utils": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3098,16 +3114,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", - "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", + "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", "debug": "^4.3.4" }, "engines": { @@ -3123,14 +3139,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", - "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", + "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0" + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3141,14 +3157,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz", - "integrity": "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", + "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/utils": "8.29.0", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/utils": "8.29.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -3165,9 +3181,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", - "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", + "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", "dev": true, "license": "MIT", "engines": { @@ -3179,14 +3195,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", - "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", + "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3206,16 +3222,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz", - "integrity": "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0" + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3230,13 +3246,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", - "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", + "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/types": "8.29.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3448,9 +3464,9 @@ } }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.3.3.tgz", - "integrity": "sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.4.1.tgz", + "integrity": "sha512-8Tv+Bsd0BjGwfEedIyor4inw8atppRxM5BdUnIt+3mAm/QXUm7Dw74CHnXpfZKXkp07EXJGiA8hStqCINAWhdw==", "cpu": [ "arm64" ], @@ -3462,9 +3478,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.3.3.tgz", - "integrity": "sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.4.1.tgz", + "integrity": "sha512-X8c3PhWziEMKAzZz+YAYWfwawi5AEgzy/hmfizAB4C70gMHLKmInJcp1270yYAOs7z07YVFI220pp50z24Jk3A==", "cpu": [ "x64" ], @@ -3476,9 +3492,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.3.3.tgz", - "integrity": "sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.4.1.tgz", + "integrity": "sha512-UUr/nREy1UdtxXQnmLaaTXFGOcGxPwNIzeJdb3KXai3TKtC1UgNOB9s8KOA4TaxOUBR/qVgL5BvBwmUjD5yuVA==", "cpu": [ "x64" ], @@ -3490,9 +3506,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.3.3.tgz", - "integrity": "sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.4.1.tgz", + "integrity": "sha512-e3pII53dEeS8inkX6A1ad2UXE0nuoWCqik4kOxaDnls0uJUq0ntdj5d9IYd+bv5TDwf9DSge/xPOvCmRYH+Tsw==", "cpu": [ "arm" ], @@ -3504,9 +3520,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.3.3.tgz", - "integrity": "sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.4.1.tgz", + "integrity": "sha512-e/AKKd9gR+HNmVyDEPI/PIz2t0DrA3cyonHNhHVjrkxe8pMCiYiqhtn1+h+yIpHUtUlM6Y1FNIdivFa+r7wrEQ==", "cpu": [ "arm" ], @@ -3518,9 +3534,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.3.3.tgz", - "integrity": "sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.4.1.tgz", + "integrity": "sha512-vtIu34luF1jRktlHtiwm2mjuE8oJCsFiFr8hT5+tFQdqFKjPhbJXn83LswKsOhy0GxAEevpXDI4xxEwkjuXIPA==", "cpu": [ "arm64" ], @@ -3532,9 +3548,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.3.3.tgz", - "integrity": "sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.4.1.tgz", + "integrity": "sha512-H3PaOuGyhFXiyJd+09uPhGl4gocmhyi1BRzvsP8Lv5AQO3p3/ZY7WjV4t2NkBksm9tMjf3YbOVHyPWi2eWsNYw==", "cpu": [ "arm64" ], @@ -3546,9 +3562,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.3.3.tgz", - "integrity": "sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.4.1.tgz", + "integrity": "sha512-4+GmJcaaFntCi1S01YByqp8wLMjV/FyQyHVGm0vedIhL1Vfx7uHkz/sZmKsidRwokBGuxi92GFmSzqT2O8KcNA==", "cpu": [ "ppc64" ], @@ -3560,9 +3576,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.3.3.tgz", - "integrity": "sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.4.1.tgz", + "integrity": "sha512-6RDQVCmtFYTlhy89D5ixTqo9bTQqFhvNN0Ey1wJs5r+01Dq15gPHRXv2jF2bQATtMrOfYwv+R2ZR9ew1N1N3YQ==", "cpu": [ "s390x" ], @@ -3574,9 +3590,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.3.3.tgz", - "integrity": "sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.4.1.tgz", + "integrity": "sha512-XpU9uzIkD86+19NjCXxlVPISMUrVXsXo5htxtuG+uJ59p5JauSRZsIxQxzzfKzkxEjdvANPM/lS1HFoX6A6QeA==", "cpu": [ "x64" ], @@ -3588,9 +3604,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.3.3.tgz", - "integrity": "sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.4.1.tgz", + "integrity": "sha512-3CDjG/spbTKCSHl66QP2ekHSD+H34i7utuDIM5gzoNBcZ1gTO0Op09Wx5cikXnhORRf9+HyDWzm37vU1PLSM1A==", "cpu": [ "x64" ], @@ -3602,9 +3618,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.3.3.tgz", - "integrity": "sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.4.1.tgz", + "integrity": "sha512-50tYhvbCTnuzMn7vmP8IV2UKF7ITo1oihygEYq9wW2DUb/Y+QMqBHJUSCABRngATjZ4shOK6f2+s0gQX6ElENQ==", "cpu": [ "wasm32" ], @@ -3612,16 +3628,16 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.7" + "@napi-rs/wasm-runtime": "^0.2.8" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.3.3.tgz", - "integrity": "sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.4.1.tgz", + "integrity": "sha512-KyJiIne/AqV4IW0wyQO34wSMuJwy3VxVQOfIXIPyQ/Up6y/zi2P/WwXb78gHsLiGRUqCA9LOoCX+6dQZde0g1g==", "cpu": [ "arm64" ], @@ -3633,9 +3649,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.3.3.tgz", - "integrity": "sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.4.1.tgz", + "integrity": "sha512-y2NUD7pygrBolN2NoXUrwVqBpKPhF8DiSNE5oB5/iFO49r2DpoYqdj5HPb3F42fPBH5qNqj6Zg63+xCEzAD2hw==", "cpu": [ "ia32" ], @@ -3647,9 +3663,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.3.3.tgz", - "integrity": "sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.4.1.tgz", + "integrity": "sha512-hVXaObGI2lGFmrtT77KSbPQ3I+zk9IU500wobjk0+oX59vg/0VqAzABNtt3YSQYgXTC2a/LYxekLfND/wlt0yQ==", "cpu": [ "x64" ], @@ -3729,6 +3745,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4146,9 +4172,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001707", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", - "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", + "version": "1.0.30001712", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", + "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", "dev": true, "funding": [ { @@ -4434,6 +4460,16 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/compare-versions": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", @@ -5054,9 +5090,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "35.1.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-35.1.2.tgz", - "integrity": "sha512-ipYSDZEV3+PpHfJ8/oWlpMCvxwutX6xLvBz2HRPgEzSFzgLmGO7YXTjEow4DhDtCpGE+b95NTGoJaRAVQi5n7A==", + "version": "35.1.4", + "resolved": "https://registry.npmjs.org/electron/-/electron-35.1.4.tgz", + "integrity": "sha512-8HjE2wqxY//T09Of8k1eTpK/NeTG2FkTyRD+fyKXmec4wZVscGgZcmWFC0HYN4ktyHAjtplpxdFXjtqRnvzBMg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5074,9 +5110,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.129", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.129.tgz", - "integrity": "sha512-JlXUemX4s0+9f8mLqib/bHH8gOHf5elKS6KeWG3sk3xozb/JTq/RLXIv8OKUWiK4Ah00Wm88EFj5PYkFr4RUPA==", + "version": "1.5.134", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.134.tgz", + "integrity": "sha512-zSwzrLg3jNP3bwsLqWHmS5z2nIOQ5ngMnfMZOWWtXnqqQkPVyOipxK98w+1beLw1TB+EImPNcG8wVP/cLVs2Og==", "dev": true, "license": "ISC" }, @@ -5219,6 +5255,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -5386,19 +5429,19 @@ } }, "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", + "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", + "@eslint/js": "9.24.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -5446,22 +5489,6 @@ } } }, - "node_modules/eslint-compat-utils": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.4.tgz", - "integrity": "sha512-/u+GQt8NMfXO8w17QendT4gvO5acfxQsAKirAt0LVxDnr2N8YLCVbregaNc/Yhp7NM128DwCaRvr8PLDfeNkQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, "node_modules/eslint-config-prettier": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz", @@ -5498,9 +5525,9 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.1.tgz", - "integrity": "sha512-/dR9YMomeBlvfuvX5q0C3Y/2PHC9OCRdT2ijFwdfq/4Bq+4m5/lqstEp9k3P6ocha1pCbhoY9fkwVYLmOqR0VQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.2.tgz", + "integrity": "sha512-T2LqBXj87ndEC9t1LrDiPkzalSFzD4rrXr6BTzGdgMx1jdQM4T972guQvg7Ih+LNO51GURXI/qMHS5GF3h1ilw==", "dev": true, "license": "ISC", "dependencies": { @@ -5509,7 +5536,7 @@ "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.12", - "unrs-resolver": "^1.3.3" + "unrs-resolver": "^1.4.1" }, "engines": { "node": "^16.17.0 || >=18.6.0" @@ -5604,15 +5631,15 @@ } }, "node_modules/eslint-plugin-import-x": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.10.0.tgz", - "integrity": "sha512-5ej+0WILhX3D6wkcdsyYmPp10SUIK6fmuZ6KS8nf9MD8CJ6/S/3Dl7m21g+MLeaTMsvcEXo3JunNAbgHwXxs/g==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.10.2.tgz", + "integrity": "sha512-jO3Y6+zBUyTX5MVbbLSzoz6fe65t+WEBaXStRLM4EBhZWbuSwAH3cLwARtM0Yp4zRtZGp9sL2zzK7G9JkHR8LA==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.0", + "@pkgr/core": "^0.2.1", "@types/doctrine": "^0.0.9", - "@typescript-eslint/utils": "^8.28.0", + "@typescript-eslint/utils": "^8.29.0", "debug": "^4.4.0", "doctrine": "^3.0.0", "eslint-import-resolver-node": "^0.3.9", @@ -5622,7 +5649,7 @@ "semver": "^7.7.1", "stable-hash": "^0.0.5", "tslib": "^2.8.1", - "unrs-resolver": "^1.3.3" + "unrs-resolver": "^1.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5688,6 +5715,32 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "50.6.9", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.9.tgz", + "integrity": "sha512-7/nHu3FWD4QRG8tCVqcv+BfFtctUtEDWc29oeDXB4bwmDM2/r1ndl14AG/2DUntdqH7qmpvdemJKwb3R97/QEw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.49.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.6", + "escape-string-regexp": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.6.0", + "parse-imports": "^2.1.1", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, "node_modules/eslint-plugin-modules-newlines": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/eslint-plugin-modules-newlines/-/eslint-plugin-modules-newlines-0.0.7.tgz", @@ -5720,15 +5773,14 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.5.0.tgz", - "integrity": "sha512-986WKRAFoS5KN/jc0Z06akYxfNs+obca486sf9Y+0VbjRUfqJZYX/HSQR/js3xd3enrFqDLg8MToWgDOvCQUqQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.5.1.tgz", + "integrity": "sha512-Qn1slddZHfqYiDO6IN8/iN3YL+VuHlgYjm30FT+hh0Jf/TX0jeZMTJXQMajFm5f6f6hURi+XO8P+NPYD+T4jkg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.1", "@jridgewell/sourcemap-codec": "^1.5.0", - "eslint-compat-utils": "^0.6.4", "esutils": "^2.0.3", "known-css-properties": "^0.35.0", "postcss": "^8.4.49", @@ -5767,9 +5819,9 @@ } }, "node_modules/eslint-plugin-svelte/node_modules/svelte-eslint-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.1.tgz", - "integrity": "sha512-QLVGPIMDettl30qRHXU2VrPvVJKG8GsGstye7n8rFbEiu3gEARksuQg9Xu4GzubNxhGNM8stfBZkhyMbBQmjFA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.2.tgz", + "integrity": "sha512-vqFBRamDKo1l70KMfxxXj1/0Cco5TfMDnqaAjgz6D8PyoMhfMcDOLRkAwPg8WkMyZjMtQL3wW66TZ0x59iqO2w==", "dev": true, "license": "MIT", "dependencies": { @@ -5853,76 +5905,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-verify-tsdoc": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/eslint-plugin-verify-tsdoc/-/eslint-plugin-verify-tsdoc-1.0.20.tgz", - "integrity": "sha512-IZ02Qi2sVXxtAVLCfTl8v7ajZ6ppVwiOWW0b2ukLyiee8XJRJO+fV9SfWPCu2C/ZZm/T5Imf8zQsIG4z61wSgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "^0.14.2", - "@microsoft/tsdoc-config": "^0.16.2", - "@types/eslint": "^8.56.2", - "eslint-plugin-tsdoc": "^0.2.17", - "minimatch": "^9.0.4" - } - }, - "node_modules/eslint-plugin-verify-tsdoc/node_modules/@microsoft/tsdoc": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-verify-tsdoc/node_modules/@microsoft/tsdoc-config": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "ajv": "~6.12.6", - "jju": "~1.4.0", - "resolve": "~1.19.0" - } - }, - "node_modules/eslint-plugin-verify-tsdoc/node_modules/@types/eslint": { - "version": "8.56.12", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", - "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/eslint-plugin-verify-tsdoc/node_modules/eslint-plugin-tsdoc": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", - "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "0.16.2" - } - }, - "node_modules/eslint-plugin-verify-tsdoc/node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/eslint-scope": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", @@ -7557,6 +7539,16 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -8974,13 +8966,13 @@ } }, "node_modules/obsidian-dev-utils": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/obsidian-dev-utils/-/obsidian-dev-utils-21.1.0.tgz", - "integrity": "sha512-6BQIfmB8aLdhJKAz6z/gSrtClZw9r2VPRCeLr26iq2W+eA6hHDRAFTgxXn3My8AK0+qL9fqdxKXX/+V/vgqLCg==", + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/obsidian-dev-utils/-/obsidian-dev-utils-24.0.1.tgz", + "integrity": "sha512-4Ayh/WZSMq1D3M+RvoXQA4HC32V1Flgqeu8HDHZLSjaTozCdtz2p+0AlaVFsozvaK786/gTkP+7NZe0aBIoFvQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint/js": "^9.23.0", + "@eslint/js": "^9.24.0", "@guardian/eslint-plugin-tsdoc-required": "^0.1.3", "@lezer/common": "^1.2.3", "@stylistic/eslint-plugin": "^4.2.0", @@ -8989,13 +8981,13 @@ "@types/babel__core": "^7.20.5", "@types/doctrine": "^0.0.9", "@types/eslint": "^9.6.1", - "@types/luxon": "^3.6.0", - "@types/node": "^22.13.17", + "@types/luxon": "^3.6.2", + "@types/node": "^22.14.0", "@types/parsimmon": "^1.10.9", "@types/path-browserify": "^1.0.3", - "@types/picomatch": "^3.0.2", + "@types/picomatch": "^4.0.0", "@types/pug": "^2.0.10", - "@types/react": "^19.0.12", + "@types/react": "^19.1.0", "@types/shell-quote": "^1.7.5", "@typescript-eslint/eslint-plugin": "^8.29.0", "@typescript-eslint/parser": "^8.29.0", @@ -9010,14 +9002,13 @@ "esbuild": "^0.25.2", "esbuild-sass-plugin": "^3.3.1", "esbuild-svelte": "^0.9.2", - "eslint": "^9.23.0", - "eslint-import-resolver-typescript": "^4.3.1", - "eslint-plugin-import-x": "^4.10.0", + "eslint": "^9.24.0", + "eslint-import-resolver-typescript": "^4.3.2", + "eslint-plugin-import-x": "^4.10.2", + "eslint-plugin-jsdoc": "^50.6.9", "eslint-plugin-modules-newlines": "^0.0.7", - "eslint-plugin-perfectionist": "^4.10.1", + "eslint-plugin-perfectionist": "^4.11.0", "eslint-plugin-tsdoc": "^0.4.0", - "eslint-plugin-verify-tsdoc": "^1.0.20", - "eventemitter3": "^5.0.1", "glob": "^11.0.1", "jiti": "^2.4.2", "localforage": "^1.10.0", @@ -9030,31 +9021,24 @@ "path-browserify": "^1.0.1", "postcss": "^8.5.3", "postcss-modules": "^6.0.1", - "preact": "^10.26.4", + "preact": "^10.26.5", "react": "^19.1.0", "remark": "^15.0.1", "remark-parse": "^11.0.0", "remark-wiki-link": "^2.0.1", - "sass": "^1.86.1", + "sass": "^1.86.3", "shell-quote": "^1.8.2", "svelte-check": "^4.1.5", "svelte-preprocess": "^6.0.3", "tsx": "^4.19.3", - "type-fest": "^4.39.0", - "typescript": "^5.8.2", + "type-fest": "^4.39.1", + "typescript": "^5.8.3", "typescript-eslint": "^8.29.0" }, "bin": { "obsidian-dev-utils": "dist/bin/cli.cjs" } }, - "node_modules/obsidian-dev-utils/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, - "license": "MIT" - }, "node_modules/obsidian-typings": { "version": "2.41.4", "resolved": "https://registry.npmjs.org/obsidian-typings/-/obsidian-typings-2.41.4.tgz", @@ -9230,6 +9214,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/parse-imports": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", + "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", + "dev": true, + "license": "Apache-2.0 AND MIT", + "dependencies": { + "es-module-lexer": "^1.5.3", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -9651,9 +9649,9 @@ "license": "MIT" }, "node_modules/preact": { - "version": "10.26.4", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.4.tgz", - "integrity": "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w==", + "version": "10.26.5", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.5.tgz", + "integrity": "sha512-fmpDkgfGU6JYux9teDWLhj9mKN55tyepwYbxHgQuIxbWQzgFg5vk7Mrrtfx7xRxq798ynkY4DDDxZr235Kk+4w==", "dev": true, "license": "MIT", "funding": { @@ -10297,9 +10295,9 @@ } }, "node_modules/sass": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.1.tgz", - "integrity": "sha512-Yaok4XELL1L9Im/ZUClKu//D2OP1rOljKj0Gf34a+GzLbMveOzL7CfqYo+JUa5Xt1nhTCW+OcKp/FtR7/iqj1w==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.3.tgz", + "integrity": "sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==", "dev": true, "license": "MIT", "dependencies": { @@ -10318,9 +10316,9 @@ } }, "node_modules/sass-embedded": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.86.1.tgz", - "integrity": "sha512-LMJvytHh7lIUtmjGCqpM4cRdIDvPllLJKznNIK4L7EZJ77BLeUFoOSRXEOHq4G4gqy5CVhHUKlHslzCANkDOhQ==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.86.3.tgz", + "integrity": "sha512-3pZSp24ibO1hdopj+W9DuiWsZOb2YY6AFRo/jjutKLBkqJGM1nJjXzhAYfzRV+Xn5BX1eTI4bBTE09P0XNHOZg==", "dev": true, "license": "MIT", "peer": true, @@ -10341,32 +10339,32 @@ "node": ">=16.0.0" }, "optionalDependencies": { - "sass-embedded-android-arm": "1.86.1", - "sass-embedded-android-arm64": "1.86.1", - "sass-embedded-android-ia32": "1.86.1", - "sass-embedded-android-riscv64": "1.86.1", - "sass-embedded-android-x64": "1.86.1", - "sass-embedded-darwin-arm64": "1.86.1", - "sass-embedded-darwin-x64": "1.86.1", - "sass-embedded-linux-arm": "1.86.1", - "sass-embedded-linux-arm64": "1.86.1", - "sass-embedded-linux-ia32": "1.86.1", - "sass-embedded-linux-musl-arm": "1.86.1", - "sass-embedded-linux-musl-arm64": "1.86.1", - "sass-embedded-linux-musl-ia32": "1.86.1", - "sass-embedded-linux-musl-riscv64": "1.86.1", - "sass-embedded-linux-musl-x64": "1.86.1", - "sass-embedded-linux-riscv64": "1.86.1", - "sass-embedded-linux-x64": "1.86.1", - "sass-embedded-win32-arm64": "1.86.1", - "sass-embedded-win32-ia32": "1.86.1", - "sass-embedded-win32-x64": "1.86.1" + "sass-embedded-android-arm": "1.86.3", + "sass-embedded-android-arm64": "1.86.3", + "sass-embedded-android-ia32": "1.86.3", + "sass-embedded-android-riscv64": "1.86.3", + "sass-embedded-android-x64": "1.86.3", + "sass-embedded-darwin-arm64": "1.86.3", + "sass-embedded-darwin-x64": "1.86.3", + "sass-embedded-linux-arm": "1.86.3", + "sass-embedded-linux-arm64": "1.86.3", + "sass-embedded-linux-ia32": "1.86.3", + "sass-embedded-linux-musl-arm": "1.86.3", + "sass-embedded-linux-musl-arm64": "1.86.3", + "sass-embedded-linux-musl-ia32": "1.86.3", + "sass-embedded-linux-musl-riscv64": "1.86.3", + "sass-embedded-linux-musl-x64": "1.86.3", + "sass-embedded-linux-riscv64": "1.86.3", + "sass-embedded-linux-x64": "1.86.3", + "sass-embedded-win32-arm64": "1.86.3", + "sass-embedded-win32-ia32": "1.86.3", + "sass-embedded-win32-x64": "1.86.3" } }, "node_modules/sass-embedded-android-arm": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.86.1.tgz", - "integrity": "sha512-bcmKB67uCb9znune+QsE6cWIiKAHE9P+24/9vDPHwwN3BmmH1B/4mznNKKakdYMuxpgbeLrPcEScHEpQbdrIpA==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.86.3.tgz", + "integrity": "sha512-UyeXrFzZSvrGbvrWUBcspbsbivGgAgebLGJdSqJulgSyGbA6no3DWQ5Qpdd6+OAUC39BlpPu74Wx9s4RrVuaFw==", "cpu": [ "arm" ], @@ -10382,9 +10380,9 @@ } }, "node_modules/sass-embedded-android-arm64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.86.1.tgz", - "integrity": "sha512-SMY79YhNfq/gdz8MHqwEsnf/IjSnQFAmSEGDDv0vjL0yy9VZC/zhsxpsho8vbFEvTSEGFFlkGgPdzDuoozRrOg==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.86.3.tgz", + "integrity": "sha512-q+XwFp6WgAv+UgnQhsB8KQ95kppvWAB7DSoJp+8Vino8b9ND+1ai3cUUZPE5u4SnLZrgo5NtrbPvN5KLc4Pfyg==", "cpu": [ "arm64" ], @@ -10400,9 +10398,9 @@ } }, "node_modules/sass-embedded-android-ia32": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.86.1.tgz", - "integrity": "sha512-AX6I5qS8GbgcbBJ1o3uKVI5/7tq6evg/BO/wa0XaNqnzP4i/PojBaGh7EcZrg/spl//SfpS55eA18a0/AOi71w==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.86.3.tgz", + "integrity": "sha512-gTJjVh2cRzvGujXj5ApPk/owUTL5SiO7rDtNLrzYAzi1N5HRuLYXqk3h1IQY3+eCOBjGl7mQ9XyySbJs/3hDvg==", "cpu": [ "ia32" ], @@ -10418,9 +10416,9 @@ } }, "node_modules/sass-embedded-android-riscv64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.86.1.tgz", - "integrity": "sha512-Af6ZzRTRfIfx6KICJZ19je6OjOXhxo+v6z/lf/SXm5/1EaHGpGC5xIw4ivtj4nNINNoqkykfIDCjpzm1qWEPPQ==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.86.3.tgz", + "integrity": "sha512-Po3JnyiCS16kd6REo1IMUbFGYtvL9O0rmKaXx5vOuBaJD1LPy2LiSSp7TU7wkJ9IxsTDGzFaSeP1I9qb6D8VVg==", "cpu": [ "riscv64" ], @@ -10436,9 +10434,9 @@ } }, "node_modules/sass-embedded-android-x64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.86.1.tgz", - "integrity": "sha512-GW47z1AH8gXB7IG6EUbC5aDBDtiITeP5nUfEenE6vaaN0H17mBjIwSnEcKPPA1IdxzDpj+4bE/SGfiF0W/At4g==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.86.3.tgz", + "integrity": "sha512-+7h3jdDv/0kUFx0BvxYlq2fa7CcHiDPlta6k5OxO5K6jyqJwo9hc0Z052BoYEauWTqZ+vK6bB5rv2BIzq4U9nA==", "cpu": [ "x64" ], @@ -10454,9 +10452,9 @@ } }, "node_modules/sass-embedded-darwin-arm64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.86.1.tgz", - "integrity": "sha512-grBnDW5Rg+mEmZM7I9hJySS4MMXDwLMd+RyegQnr+SIJ3WA807Cw830+raALxgDY+UKKKhVEoq3FgbTo40Awgw==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.86.3.tgz", + "integrity": "sha512-EgLwV4ORm5Hr0DmIXo0Xw/vlzwLnfAiqD2jDXIglkBsc5czJmo4/IBdGXOP65TRnsgJEqvbU3aQhuawX5++x9A==", "cpu": [ "arm64" ], @@ -10472,9 +10470,9 @@ } }, "node_modules/sass-embedded-darwin-x64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.86.1.tgz", - "integrity": "sha512-XxSCMcmeADNouiJAr8G1oRnEhkivHKVLV5DRpfFnUK5FqtFCuSk3K18I+xIfpQDeZnjRL3t2VjsmEJuFiBYV8w==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.86.3.tgz", + "integrity": "sha512-dfKhfrGPRNLWLC82vy/vQGmNKmAiKWpdFuWiePRtg/E95pqw+sCu6080Y6oQLfFu37Iq3MpnXiSpDuSo7UnPWA==", "cpu": [ "x64" ], @@ -10490,9 +10488,9 @@ } }, "node_modules/sass-embedded-linux-arm": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.86.1.tgz", - "integrity": "sha512-Z57ZUcWPuoOHpnl3TiUf/x9wWF2dFtkjdv7hZQpFXYwK5eudHFeBErK6KNCos6jkif1KyeFELXT/HWOznitU/w==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.86.3.tgz", + "integrity": "sha512-+fVCIH+OR0SMHn2NEhb/VfbpHuUxcPtqMS34OCV3Ka99LYZUJZqth4M3lT/ppGl52mwIVLNYzR4iLe6mdZ6mYA==", "cpu": [ "arm" ], @@ -10508,9 +10506,9 @@ } }, "node_modules/sass-embedded-linux-arm64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.86.1.tgz", - "integrity": "sha512-zchms0BtaOrkvfvjRnl1PDWK931DxAeYEY2yKQceO/0OFtcBz1r480Kh/RjIffTNreJqIr9Mx4wFdP+icKwLpg==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.86.3.tgz", + "integrity": "sha512-tYq5rywR53Qtc+0KI6pPipOvW7a47ETY69VxfqI9BR2RKw2hBbaz0bIw6OaOgEBv2/XNwcWb7a4sr7TqgkqKAA==", "cpu": [ "arm64" ], @@ -10526,9 +10524,9 @@ } }, "node_modules/sass-embedded-linux-ia32": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.86.1.tgz", - "integrity": "sha512-WHntVnCgpiJPCmTeQrn5rtl1zJdd693TwpNGAFPzKD4FILPcVBKtWutl7COL6bKe/mKTf9OW0t6GBJ6mav2hAA==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.86.3.tgz", + "integrity": "sha512-CmQ5OkqnaeLdaF+bMqlYGooBuenqm3LvEN9H8BLhjkpWiFW8hnYMetiqMcJjhrXLvDw601KGqA5sr/Rsg5s45g==", "cpu": [ "ia32" ], @@ -10544,9 +10542,9 @@ } }, "node_modules/sass-embedded-linux-musl-arm": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.86.1.tgz", - "integrity": "sha512-DlPpyp3bIL8YMtxR22hkWBtuZY6ch3KAmQvqIONippPv96WTHi1iq5jclbE1YXpDtI8Wcus0x6apoDSKq8o95g==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.86.3.tgz", + "integrity": "sha512-SEm65SQknI4pl+mH5Xf231hOkHJyrlgh5nj4qDbiBG6gFeutaNkNIeRgKEg3cflXchCr8iV/q/SyPgjhhzQb7w==", "cpu": [ "arm" ], @@ -10562,9 +10560,9 @@ } }, "node_modules/sass-embedded-linux-musl-arm64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.86.1.tgz", - "integrity": "sha512-CwuHMRWSJFByHpgqcVtCSt29dMWhr0lpUTjaBCh9xOl0Oyz89dIqOxA0aMq+XU+thaDtOziJtMIfW6l35ZeykQ==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.86.3.tgz", + "integrity": "sha512-4zOr2C/eW89rxb4ozTfn7lBzyyM5ZigA1ZSRTcAR26Qbg/t2UksLdGnVX9/yxga0d6aOi0IvO/7iM2DPPRRotg==", "cpu": [ "arm64" ], @@ -10580,9 +10578,9 @@ } }, "node_modules/sass-embedded-linux-musl-ia32": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.86.1.tgz", - "integrity": "sha512-yjvVpAW1YS0VQNnIUtZTf0IrRDMa0wRjFWUtsLthVIxuXyjLy44+YULlfduxqcZe3rvI4+EqT7GorvviWo9NfQ==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.86.3.tgz", + "integrity": "sha512-84Tcld32LB1loiqUvczWyVBQRCChm0wNLlkT59qF29nxh8njFIVf9yaPgXcSyyjpPoD9Tu0wnq3dvVzoMCh9AQ==", "cpu": [ "ia32" ], @@ -10598,9 +10596,9 @@ } }, "node_modules/sass-embedded-linux-musl-riscv64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.86.1.tgz", - "integrity": "sha512-0zCUOMwX/hwPV1zimxM46dq/MdATSqbw6G646DwQ3/2V2Db1t9lfXBZqSavx8p/cqRp1JYTUPbJQV1gT4J7NYw==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.86.3.tgz", + "integrity": "sha512-IxEqoiD7vdNpiOwccybbV93NljBy64wSTkUOknGy21SyV43C8uqESOwTwW9ywa3KufImKm8L3uQAW/B0KhJMWg==", "cpu": [ "riscv64" ], @@ -10616,9 +10614,9 @@ } }, "node_modules/sass-embedded-linux-musl-x64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.86.1.tgz", - "integrity": "sha512-8KJ6kEj1N16V9E0g5PDSd4aVe1LwcVKROJcVqnzTKPMa/4j2VuNWep7D81OYchdQMm9Egn1RqV0jCwm0b2aSHQ==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.86.3.tgz", + "integrity": "sha512-ePeTPXUxPK6JgHcUfnrkIyDtyt+zlAvF22mVZv6y1g/PZFm1lSfX+Za7TYHg9KaYqaaXDiw6zICX4i44HhR8rA==", "cpu": [ "x64" ], @@ -10634,9 +10632,9 @@ } }, "node_modules/sass-embedded-linux-riscv64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.86.1.tgz", - "integrity": "sha512-rNJ1EfIkQpvBfMS1fBdyb+Gsji4yK0AwsV1T7NEcy21yDxDt7mdCgkAJiaN9qf7UEXuCuueQoed7WZoDaSpjww==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.86.3.tgz", + "integrity": "sha512-NuXQ72dwfNLe35E+RaXJ4Noq4EkFwM65eWwCwxEWyJO9qxOx1EXiCAJii6x8kkOh5daWuMU0VAI1B9RsJaqqQQ==", "cpu": [ "riscv64" ], @@ -10652,9 +10650,9 @@ } }, "node_modules/sass-embedded-linux-x64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.86.1.tgz", - "integrity": "sha512-DGCdUoYRRUKzRZz/q7plbB5Nean2+Uk4CqKF4RWAU0v1tHnDKKWmYfETryhWdB2WJM8QSn7O8qRebe6FCobB5g==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.86.3.tgz", + "integrity": "sha512-t8be9zJ5B82+og9bQmIQ83yMGYZMTMrlGA+uGWtYacmwg6w3093dk91Fx0YzNSZBp3Tk60qVYjCZnEIwy60x0g==", "cpu": [ "x64" ], @@ -10670,9 +10668,9 @@ } }, "node_modules/sass-embedded-win32-arm64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.86.1.tgz", - "integrity": "sha512-qRLZR3yLuk/3y64YhcltkwGclhPoK6EdiLP1e5SVw5+kughcs+mNUZ3rdvSAmCSA4vDv+XOiOjRpjxmpeon95Q==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.86.3.tgz", + "integrity": "sha512-4ghuAzjX4q8Nksm0aifRz8hgXMMxS0SuymrFfkfJlrSx68pIgvAge6AOw0edoZoe0Tf5ZbsWUWamhkNyNxkTvw==", "cpu": [ "arm64" ], @@ -10688,9 +10686,9 @@ } }, "node_modules/sass-embedded-win32-ia32": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.86.1.tgz", - "integrity": "sha512-o860a7/YGHZnGeY3l/e6yt3+ZMeDdDHmthTaKnw2wpJNEq0nmytYLTJQmjWPxEMz7O8AQ0LtcbDDrhivSog+KQ==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.86.3.tgz", + "integrity": "sha512-tCaK4zIRq9mLRPxLzBAdYlfCuS/xLNpmjunYxeWkIwlJo+k53h1udyXH/FInnQ2GgEz0xMXyvH3buuPgzwWYsw==", "cpu": [ "ia32" ], @@ -10706,9 +10704,9 @@ } }, "node_modules/sass-embedded-win32-x64": { - "version": "1.86.1", - "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.86.1.tgz", - "integrity": "sha512-7Z3wsVKfseJodmv689dDEV/JrXJH5TAclWNvHrEYW5BtoViOTU2pIDxRgLYzdKU9teIw5g6R0nJZb9M105oIKA==", + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.86.3.tgz", + "integrity": "sha512-zS+YNKfTF4SnOfpC77VTb0qNZyTXrxnAezSoRV0xnw6HlY+1WawMSSB6PbWtmbvyfXNgpmJUttoTtsvJjRCucg==", "cpu": [ "x64" ], @@ -10985,6 +10983,13 @@ "url": "https://github.com/steveukx/git-js?sponsor=1" } }, + "node_modules/slashes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", + "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", + "dev": true, + "license": "ISC" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -10996,9 +11001,9 @@ } }, "node_modules/spacetime": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-7.8.0.tgz", - "integrity": "sha512-H9kwMYcFycuW6VHN3NZ9V3RMafdrU/zkbD7digwhtKNDmCIbl0Oal0M1MhVR7FIEc7tQQHDP01ryrS1i3sp1CA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-7.9.0.tgz", + "integrity": "sha512-ySjARetkqdulQbprT5z1tzj7o5F0AcojwCEKmLK8bNEx39vFEEK9dq/oDbbT+l7mC149OHL0kQYHRmOB3nefOg==", "dev": true, "license": "Apache-2.0" }, @@ -11013,6 +11018,17 @@ "spdx-license-ids": "^3.0.0" } }, + "node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", @@ -11021,9 +11037,9 @@ "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11331,9 +11347,9 @@ } }, "node_modules/svelte": { - "version": "5.25.6", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.25.6.tgz", - "integrity": "sha512-RGkaeAXDuJdvhA1fdSM5GgD++vYfJYijZL0uN6kM2s/TRJ663jktBhZlF0qjzAJGR/34PtaeT3G8MKJY1EKeqg==", + "version": "5.25.8", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.25.8.tgz", + "integrity": "sha512-yRmjmT5rgCZUMfCKS5varGlSe/nQyr2oClyIirbBChfTFc00YjVAyVWo1zOH74La3hi5KRSkNJKncyJ04PwIYA==", "dev": true, "license": "MIT", "dependencies": { @@ -11564,6 +11580,36 @@ "node": ">=16.0.0" } }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/@pkgr/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", + "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -11710,9 +11756,9 @@ } }, "node_modules/type-fest": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.0.tgz", - "integrity": "sha512-w2IGJU1tIgcrepg9ZJ82d8UmItNQtOFJG0HCUE3SzMokKkTsruVDALl2fAdiEzJlfduoU+VyXJWIIUZ+6jV+nw==", + "version": "4.39.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz", + "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -11801,9 +11847,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11815,15 +11861,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.0.tgz", - "integrity": "sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", + "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.0", - "@typescript-eslint/parser": "8.29.0", - "@typescript-eslint/utils": "8.29.0" + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "@typescript-eslint/utils": "8.29.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11967,30 +12013,30 @@ } }, "node_modules/unrs-resolver": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.3.3.tgz", - "integrity": "sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.4.1.tgz", + "integrity": "sha512-MhPB3wBI5BR8TGieTb08XuYlE8oFVEXdSAgat3psdlRyejl8ojQ8iqPcjh094qCZ1r+TnkxzP6BeCd/umfHckQ==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/JounQin" }, "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.3.3", - "@unrs/resolver-binding-darwin-x64": "1.3.3", - "@unrs/resolver-binding-freebsd-x64": "1.3.3", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.3.3", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.3.3", - "@unrs/resolver-binding-linux-arm64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-arm64-musl": "1.3.3", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-s390x-gnu": "1.3.3", - "@unrs/resolver-binding-linux-x64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-x64-musl": "1.3.3", - "@unrs/resolver-binding-wasm32-wasi": "1.3.3", - "@unrs/resolver-binding-win32-arm64-msvc": "1.3.3", - "@unrs/resolver-binding-win32-ia32-msvc": "1.3.3", - "@unrs/resolver-binding-win32-x64-msvc": "1.3.3" + "@unrs/resolver-binding-darwin-arm64": "1.4.1", + "@unrs/resolver-binding-darwin-x64": "1.4.1", + "@unrs/resolver-binding-freebsd-x64": "1.4.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.4.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.4.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.4.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.4.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.4.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.4.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.4.1", + "@unrs/resolver-binding-linux-x64-musl": "1.4.1", + "@unrs/resolver-binding-wasm32-wasi": "1.4.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.4.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.4.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.4.1" } }, "node_modules/update-browserslist-db": { @@ -12075,6 +12121,17 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/varint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", diff --git a/package.json b/package.json index d0d8c84..6dfd303 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,12 @@ "eslint-plugin-unicorn": "^57.0.0", "globals": "^16.0.0", "obsidian": "^1.8.7", - "obsidian-dev-utils": "^21.0.0", + "obsidian-dev-utils": "latest", "obsidian-typings": "^2.35.0", "prettier": "3.5.3", "prettier-plugin-svelte": "^3.3.3", "sass": "^1.83.4", + "simple-git": "^3.27.0", "spacetime": "^7.8.0", "svelte": "^5.23.1", "svelte-eslint-parser": "^0.43.0", @@ -50,7 +51,7 @@ "svelte-preprocess": "^6.0.3", "typescript": "^5.8.2", "typescript-eslint": "^8.25.0", - "simple-git": "^3.27.0" + "type-fest": "^4.39.1" }, "overrides": { "boolean": "npm:dry-uninstall" diff --git a/scripts/buildWithSvelte.ts b/scripts/buildWithSvelte.ts index cfbdeb6..72071d4 100644 --- a/scripts/buildWithSvelte.ts +++ b/scripts/buildWithSvelte.ts @@ -1,3 +1,4 @@ +import type { BuildOptions } from 'esbuild'; import type { CliTaskResult } from 'obsidian-dev-utils/ScriptUtils/CliUtils'; import { @@ -9,9 +10,10 @@ export async function buildWithSvelte( developmentMode: boolean ): Promise { return await buildObsidianPlugin({ - customEsbuildOptions: { - dropLabels: developmentMode ? undefined : ['DEV'] + customizeEsbuildOptions: (options: BuildOptions) => { + options.dropLabels = developmentMode ? undefined : ['DEV']; }, + customEsbuildPlugins: [ { name: 'add-condition', diff --git a/scripts/formatWithPrettier.ts b/scripts/formatWithPrettier.ts index 1b34cb1..7089e02 100644 --- a/scripts/formatWithPrettier.ts +++ b/scripts/formatWithPrettier.ts @@ -1,8 +1,8 @@ -import { getDirname } from 'obsidian-dev-utils/Path'; +import { getFolderName } from 'obsidian-dev-utils/Path'; import { existsSync } from 'obsidian-dev-utils/ScriptUtils/NodeModules'; import { execFromRoot, - getRootDir, + getRootFolder, resolvePathFromRootSafe } from 'obsidian-dev-utils/ScriptUtils/Root'; @@ -11,7 +11,7 @@ export async function formatWithPrettier(rewrite: boolean): Promise { const prettierJsonPath = resolvePathFromRootSafe('.prettierrc.json'); if (!existsSync(prettierJsonPath)) { - const packageDirectory = getRootDir(getDirname(import.meta.url)); + const packageDirectory = getRootFolder(getFolderName(import.meta.url)); if (!packageDirectory) { throw new Error('Could not find package directory.'); } diff --git a/src/GitChangelogPlugin.svelte.ts b/src/GitChangelogPlugin.svelte.ts index ed0eb94..324faf8 100644 --- a/src/GitChangelogPlugin.svelte.ts +++ b/src/GitChangelogPlugin.svelte.ts @@ -1,16 +1,18 @@ +import type { GitChangelogPluginTypes } from 'constants.ts'; import type { ObsidianGitPlugin } from 'gitPluginTypes.ts'; -import type { Debouncer, PluginSettingTab } from 'obsidian'; -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; +import type { Debouncer } from 'obsidian'; +import type { GitChangelogSettings } from 'settings/settings.ts'; import type { SimpleGit } from 'simple-git'; import type { ReadonlyDeep } from 'type-fest'; import { FileChangelogManager } from 'core/FileChangelogManager.ts'; +import { runHashObjectEmptyTree } from 'core/gitOperations/runHashObjectEmptyTree.ts'; import { VaultChangelogManager } from 'core/VaultChangelogManager.ts'; import { addContextMenuItems } from 'menu.ts'; import { debounce, ItemView, Notice } from 'obsidian'; import { PluginBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginBase'; import { changelogGenerationSettingsChanged } from 'settings/helper.ts'; -import { GitChangelogPluginSettings } from 'settings/settings.ts'; +import { GitChangelogSettingsManager } from 'settings/settingsManager.ts'; import { getTimeZone } from 'settings/ui/CustomTimeZone.ts'; import { gitPluginCompatibleVersion, @@ -18,6 +20,7 @@ import { } from 'settings/ui/GitPluginWarning.ts'; import spacetime from 'spacetime'; import { TaskManager } from 'TaskManager.svelte.ts'; +import { applyDayStartHourSetting } from 'timeUtils.ts'; import { FILE_CHANGELOG_VIEW_CONFIG, FileChangelogView @@ -38,12 +41,12 @@ import { GitRepoMissingError } from './types.ts'; -export class GitChangelogPlugin extends PluginBase { +export class GitChangelogPlugin extends PluginBase { public debouncedChangelogSettingsChangedCheck: | Debouncer< [ - oldSettings: ReadonlyDeep, - newSettings: ReadonlyDeep + oldSettings: ReadonlyDeep, + newSettings: ReadonlyDeep ], void > @@ -52,7 +55,7 @@ export class GitChangelogPlugin extends PluginBase { public gitPluginState = $state(); public gitRepoReady = $state(); // Used for keeping relative interval labels like "today" and "yesterday" up to date - public currentDay = $state(); + public currentDay: string | undefined; /** * If we were to persist this, it would be less flexible if the user changes his repo or the hashing algorithm and we would need to either manually have a database of all possible empty tree hashes or validate it in runtime */ @@ -65,10 +68,9 @@ export class GitChangelogPlugin extends PluginBase { ); public detectedTimeZone: string | undefined; + public detectedLocale: string | undefined; public vaultChangelogManager = $state(); - public fileChangelogManager = $state(); - public settingsOfComputedCache?: ChangelogGenerationSettings; public statusBarStats?: StatusBarStats; public cachedActiveGitFile: string | undefined; @@ -150,40 +152,43 @@ export class GitChangelogPlugin extends PluginBase { } } - public override async saveSettings( - newSettings: GitChangelogPluginSettings, - debounceOnSettingsSave = true - ): Promise { - const oldSettings = this.settings; - await super.saveSettings(newSettings); - if (debounceOnSettingsSave) { - this.debouncedChangelogSettingsChangedCheck?.(oldSettings, newSettings); - } else { - this.onSettingsSave(oldSettings, newSettings); + public async getEmptyTreeHash(): Promise { + if (this.emptyTreeHash) { + return this.emptyTreeHash; } + const git = await this.getGit(); + const emptyTreeHash = await runHashObjectEmptyTree({ git }); + this.emptyTreeHash ??= emptyTreeHash; + return this.emptyTreeHash; } - protected override createPluginSettings( - data: unknown - ): GitChangelogPluginSettings { - return new GitChangelogPluginSettings(data); + // eslint-disable-next-line @typescript-eslint/require-await + public override async onSaveSettings( + _newSettings: GitChangelogSettings, + _oldSettings: GitChangelogSettings + ): Promise { + this.debouncedChangelogSettingsChangedCheck?.(_oldSettings, _newSettings); } - protected override createPluginSettingsTab(): null | PluginSettingTab { + protected override createPluginSettingsTab(): GitChangelogSettingsTab { return new GitChangelogSettingsTab(this); } - // Functions below are from obsidian-dev-utils (https://github.com/mnaoumov/obsidian-dev-utils/blob/main/src/obsidian/Plugin/PluginBase.ts) + protected override createSettingsManager(): GitChangelogSettingsManager { + return new GitChangelogSettingsManager(this); + } - protected override onloadComplete(): void { - // Runs when the plugin is unloaded. - this.register(() => { - this.debouncedChangelogSettingsChangedCheck?.cancel(); - this.fileChangelogManager?.taskManager.abort(); - this.vaultChangelogManager?.taskManager.abort(); - this.statusBarStats?.destroy(); - }); + // Runs when the plugin is unloaded. + // eslint-disable-next-line @typescript-eslint/require-await + protected override async onunloadImpl(): Promise { + this.debouncedChangelogSettingsChangedCheck?.cancel(); + this.fileChangelogManager?.taskManager.abort(); + this.vaultChangelogManager?.taskManager.abort(); + this.statusBarStats?.destroy(); + } + protected override async onloadImpl(): Promise { + await super.onloadImpl(); this.registerView(VAULT_CHANGELOG_VIEW_CONFIG.type, (leaf) => { return new VaultChangelogView(leaf, this); }); @@ -198,10 +203,10 @@ export class GitChangelogPlugin extends PluginBase { this.debouncedChangelogSettingsChangedCheck = debounce( ( - oldSettings: GitChangelogPluginSettings, - newSettings: GitChangelogPluginSettings + oldSettings: GitChangelogSettings, + newSettings: GitChangelogSettings ) => { - this.onSettingsSave(oldSettings, newSettings); + this.onSaveSettingsCheck(oldSettings, newSettings); }, // eslint-disable-next-line no-magic-numbers 700, @@ -220,7 +225,7 @@ export class GitChangelogPlugin extends PluginBase { taskManager: new TaskManager(this) }); - if (this.settings.statusBarStats) { + if (this.settings.statusBarStatsEnabled) { this.initStatusBar(); } @@ -241,11 +246,17 @@ export class GitChangelogPlugin extends PluginBase { this.registerInterval( window.setInterval( () => { - const timezone = getTimeZone( - this.settings.changelogGenerationSettings, - this - ); - this.currentDay = spacetime.now(timezone).format('day'); + const timeZone = getTimeZone(this); + const fullyAdjustedNewDate = applyDayStartHourSetting({ + timeZoneAdjustedDate: spacetime.now(timeZone), + dayStartHour: this.settings.dayStartHour + }); + + const fullyAdjustedNewDayString = fullyAdjustedNewDate.format('day'); + if (fullyAdjustedNewDayString !== this.currentDay) { + this.currentDay = fullyAdjustedNewDayString; + this.app.workspace.trigger('git-changelog:day-changed'); + } }, // eslint-disable-next-line no-magic-numbers 5 * 60 * 1000 @@ -262,70 +273,17 @@ export class GitChangelogPlugin extends PluginBase { await this.addVaultChangelogView(); await this.addFileChangelogView(); - const newSettings = this.settingsClone; - newSettings.firstStartup = false; - await this.saveSettings(newSettings, false); + await this.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.firstStartup = false; + } + ); } // Also checks the status of the Git plugin this.updateActiveGitFile(); } - /** - * Triggers events for the views to listen to instead of updating the changelogs directly from the settings so that if the views aren't active we don't do unnecessary computation. - * This allows us to check if relevant settings have changed and only then recompute changelogs, instead of recomputing after every single settings change. - */ - private onSettingsSave( - oldSettings: ReadonlyDeep, - newSettings: GitChangelogPluginSettings - ): void { - try { - if ( - changelogGenerationSettingsChanged({ - newChangelogSettings: newSettings.changelogGenerationSettings, - oldChangelogSettings: oldSettings.changelogGenerationSettings, - plugin: this - }) - ) { - // The main settings, that trigger recalculation for all stats when they change. - this.app.workspace.trigger('git-changelog:generation-settings-changed'); - } else { - if ( - this.fileChangelogManager?.generationSettingsChanged( - oldSettings, - newSettings - ) && - this.cachedActiveGitFile - ) { - this.app.workspace.trigger( - 'git-changelog:file-changelog-generation-settings-changed' - ); - } - if ( - this.statusBarStats && - StatusBarStats.generationSettingsChanged(oldSettings, newSettings) && - this.cachedActiveGitFile - ) { - this.app.workspace.trigger( - 'git-changelog:status-bar-settings-changed' - ); - } - if ( - this.vaultChangelogManager?.generationSettingsChanged( - oldSettings, - newSettings - ) - ) { - this.app.workspace.trigger( - 'git-changelog:vault-changelog-generation-settings-changed' - ); - } - } - } catch { - /* Empty */ - } - } - private setNewActiveGitFile(activeGitFile: string | undefined): void { this.fileChangelogManager?.resetAndGetSignal(); // Does this belong inside the file changelog class? this.cachedActiveGitFile = activeGitFile; @@ -359,4 +317,70 @@ export class GitChangelogPlugin extends PluginBase { /* Empty */ } } + + /** + * Triggers events for the views to listen to instead of updating the changelogs directly from the settings so that if the views aren't active we don't do unnecessary computation. + * This allows us to check if relevant settings have changed and only then recompute changelogs, instead of recomputing after every single settings change. + */ + private onSaveSettingsCheck( + oldSettings: ReadonlyDeep, + newSettings: GitChangelogSettings + ): void { + try { + if ( + changelogGenerationSettingsChanged({ + newSettings, + oldSettings + }) + ) { + // The main settings, that trigger recalculation for all stats when they change. + this.app.workspace.trigger('git-changelog:generation-settings-changed'); + return; + } + + if ( + this.fileChangelogManager?.specificSettingsChanged( + oldSettings, + newSettings + ) && + this.cachedActiveGitFile + ) { + this.app.workspace.trigger( + 'git-changelog:file-changelog-generation-settings-changed' + ); + } + + // Handle destroying and initializing the status bar stats + if (this.settings.statusBarStatsEnabled !== !!this.statusBarStats) { + const shouldEnableStatusBarStats = this.settings.statusBarStatsEnabled; + if (shouldEnableStatusBarStats) { + this.initStatusBar(); + } else { + this.statusBarStats?.destroy?.(); + this.statusBarStats = undefined; + } + } + // Handle status bar settings changes + else if ( + this.statusBarStats && + StatusBarStats.generationSettingsChanged(oldSettings, newSettings) && + this.cachedActiveGitFile + ) { + this.app.workspace.trigger('git-changelog:status-bar-settings-changed'); + } + + if ( + this.vaultChangelogManager?.specificSettingsChanged( + oldSettings, + newSettings + ) + ) { + this.app.workspace.trigger( + 'git-changelog:vault-changelog-generation-settings-changed' + ); + } + } catch { + /* Empty */ + } + } } diff --git a/src/Views/FileChangelog/Version.svelte b/src/Views/FileChangelog/Version.svelte index a57f87b..ea0d7b9 100644 --- a/src/Views/FileChangelog/Version.svelte +++ b/src/Views/FileChangelog/Version.svelte @@ -1,10 +1,12 @@ diff --git a/src/Views/VaultChangelog/VaultChangelog.svelte b/src/Views/VaultChangelog/VaultChangelog.svelte index d2e29ec..1a6d636 100644 --- a/src/Views/VaultChangelog/VaultChangelog.svelte +++ b/src/Views/VaultChangelog/VaultChangelog.svelte @@ -2,6 +2,7 @@ // Import { SimpleGit } from "src/gitManager/simpleGit"; import type GitChangelogPlugin from 'main.ts'; import type { EventRef } from 'obsidian'; + import type { GitChangelogSettings } from 'settings/settings.ts'; import { TOGGLE_FILES_SUMMARY_OPTION_ICON } from 'constants.ts'; import { setIcon } from 'obsidian'; @@ -141,16 +142,17 @@ collapseButton = undefined; }); - function toggleFilesSummaryOption(): void { + async function toggleFilesSummaryOption(): Promise { showFilesCountSummariesMode = showFilesCountSummariesMode === FilesSummariesDisplayMode.Total ? FilesSummariesDisplayMode.TextAndBinary : FilesSummariesDisplayMode.Total; - const newSettings = plugin.settingsClone; - newSettings.fileSummariesDisplayMode = showFilesCountSummariesMode; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - plugin.saveSettings(newSettings); + await plugin.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.fileSummariesDisplayMode = showFilesCountSummariesMode; + } + ); } function toggleCollapsedState(): void { diff --git a/src/Views/VaultChangelog/components/Version.svelte b/src/Views/VaultChangelog/components/Version.svelte index e03d832..b91141b 100644 --- a/src/Views/VaultChangelog/components/Version.svelte +++ b/src/Views/VaultChangelog/components/Version.svelte @@ -1,8 +1,12 @@ diff --git a/src/Views/formatters.ts b/src/Views/formatters.ts index a52dd99..9f8b1d2 100644 --- a/src/Views/formatters.ts +++ b/src/Views/formatters.ts @@ -3,11 +3,10 @@ import type { Spacetime } from 'spacetime'; import type { DiffFile } from 'types.ts'; import { normalizePath } from 'obsidian'; -import { getUserLocale } from 'settings/ui/CustomLocale.ts'; +import { getLocale } from 'settings/ui/CustomLocale.ts'; import { getTimeZone } from 'settings/ui/CustomTimeZone.ts'; -import { getDayStartTime } from 'settings/ui/DayStartTime.ts'; import spacetime from 'spacetime'; -import { applyDayStartTimeSetting } from 'timeUtils.ts'; +import { applyDayStartHourSetting } from 'timeUtils.ts'; import { ChangelogInterval } from 'types.ts'; import { assertNotNull, @@ -86,35 +85,35 @@ export function composeDailyVersionDisplayText({ return formatDate( fullyAdjustedEntryDate.toNativeDate(), - getUserLocale(plugin), - getTimeZone(plugin.settings.changelogGenerationSettings, plugin) + getLocale(plugin), + getTimeZone(plugin) ); } // Only for composing the UI string export function applyDayDisplayOffset({ - dayStartTime, - timezoneAdjustedDate + dayStartHour, + timeZoneAdjustedDate }: { - dayStartTime: number; - timezoneAdjustedDate: Spacetime; + dayStartHour: number; + timeZoneAdjustedDate: Spacetime; }): Spacetime { - const dayOffset = timezoneAdjustedDate.hour() < dayStartTime ? 1 : 0; - return timezoneAdjustedDate.clone().subtract(dayOffset, 'day'); + const dayOffset = timeZoneAdjustedDate.hour() < dayStartHour ? 1 : 0; + return timeZoneAdjustedDate.clone().subtract(dayOffset, 'day'); } export function composeHourlyVersionDisplayText({ fullyAdjustedCurrentDate, fullyAdjustedEntryDate, - timezoneAdjustedEntryDate, + timeZoneAdjustedEntryDate, plugin }: { fullyAdjustedCurrentDate: Spacetime; fullyAdjustedEntryDate: Spacetime; - timezoneAdjustedEntryDate: Spacetime; + timeZoneAdjustedEntryDate: Spacetime; plugin: GitChangelogPlugin; }): string { - // Use the fullyAdjusted dates only to check if the dates belong to the same day after the dayStartTime setting is applied. + // Use the fullyAdjusted dates only to check if the dates belong to the same day after the dayStartHour setting is applied. const isToday = fullyAdjustedEntryDate.isSame( fullyAdjustedCurrentDate, 'day' @@ -124,12 +123,9 @@ export function composeHourlyVersionDisplayText({ 'day' ); - const userLocale = getUserLocale(plugin); + const userLocale = getLocale(plugin); - const timeZone = getTimeZone( - plugin.settings.changelogGenerationSettings, - plugin - ); + const timeZone = getTimeZone(plugin); // Replaces the day part of the date time string with today or yesterday labels. if (isToday || isYesterday) { @@ -138,7 +134,7 @@ export function composeHourlyVersionDisplayText({ timeZone }); const timeString = timeFormatter.format( - timezoneAdjustedEntryDate.startOf('hour').toNativeDate() + timeZoneAdjustedEntryDate.startOf('hour').toNativeDate() ); return `${isToday ? 'Today' : 'Yesterday'}, ${timeString}`; } @@ -149,14 +145,14 @@ export function composeHourlyVersionDisplayText({ timeZone }); - // Use the timezoneAdjustedEntryDate in the UI and just potentially subtract a day if it crosses the dayStartTime boundary. Don't show the fake fullyAdjustedEntryDate that has the dayStartTime offset applied to hours. - const timezoneAdjustedEntryDateWithDayOffset = applyDayDisplayOffset({ - dayStartTime: getDayStartTime(plugin.settings.changelogGenerationSettings), - timezoneAdjustedDate: timezoneAdjustedEntryDate + // Use the timeZoneAdjustedEntryDate in the UI and just potentially subtract a day if it crosses the dayStartHour boundary. Don't show the fake fullyAdjustedEntryDate that has the dayStartHour offset applied to hours. + const timeZoneAdjustedEntryDateWithDayOffset = applyDayDisplayOffset({ + dayStartHour: plugin.settings.dayStartHour, + timeZoneAdjustedDate: timeZoneAdjustedEntryDate }); return formatter.format( - timezoneAdjustedEntryDateWithDayOffset.startOf('hour').toNativeDate() + timeZoneAdjustedEntryDateWithDayOffset.startOf('hour').toNativeDate() ); } @@ -169,30 +165,28 @@ export function composeMonthlyVersionDisplayText({ }): string { return formatMonthYear( fullyAdjustedEntryDate.toNativeDate(), - getUserLocale(plugin), - getTimeZone(plugin.settings.changelogGenerationSettings, plugin) + getLocale(plugin), + getTimeZone(plugin) ); } export function composeVersionTitle({ interval, plugin, - timezoneAdjustedEntryDate + timeZoneAdjustedEntryDate }: { interval: ChangelogInterval; plugin: GitChangelogPlugin; - timezoneAdjustedEntryDate: Spacetime; + timeZoneAdjustedEntryDate: Spacetime; }): string { - const timezoneAdjustedCurrentDate = spacetime.now( - getTimeZone(plugin.settings.changelogGenerationSettings, plugin) - ); - const fullyAdjustedCurrentDate = applyDayStartTimeSetting({ - dayStartTime: getDayStartTime(plugin.settings.changelogGenerationSettings), - timezoneAdjustedDate: timezoneAdjustedCurrentDate + const timeZoneAdjustedCurrentDate = spacetime.now(getTimeZone(plugin)); + const fullyAdjustedCurrentDate = applyDayStartHourSetting({ + dayStartHour: plugin.settings.dayStartHour, + timeZoneAdjustedDate: timeZoneAdjustedCurrentDate }); - const fullyAdjustedEntryDate = applyDayStartTimeSetting({ - dayStartTime: getDayStartTime(plugin.settings.changelogGenerationSettings), - timezoneAdjustedDate: timezoneAdjustedEntryDate + const fullyAdjustedEntryDate = applyDayStartHourSetting({ + dayStartHour: plugin.settings.dayStartHour, + timeZoneAdjustedDate: timeZoneAdjustedEntryDate }); switch (interval) { @@ -200,7 +194,7 @@ export function composeVersionTitle({ return composeHourlyVersionDisplayText({ fullyAdjustedCurrentDate, fullyAdjustedEntryDate, - timezoneAdjustedEntryDate, + timeZoneAdjustedEntryDate, plugin }); } @@ -251,7 +245,7 @@ export function composeWeeklyVersionDisplayText({ return 'Last week'; } - const userLocale = getUserLocale(plugin); + const userLocale = getLocale(plugin); const weekNumber = fullyAdjustedEntryDate.week(); @@ -261,10 +255,7 @@ export function composeWeeklyVersionDisplayText({ ); const nativeFullyAdjustedEntryWeek = fullyAdjustedEntryWeek.toNativeDate(); - const timeZone = getTimeZone( - plugin.settings.changelogGenerationSettings, - plugin - ); + const timeZone = getTimeZone(plugin); if (isCurrentYear) { const monthString = new Intl.DateTimeFormat(userLocale, { diff --git a/src/constants.ts b/src/constants.ts index 99b8501..b500f02 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,8 +1,21 @@ +import type GitChangelogPlugin from 'main.ts'; +import type { PluginTypesBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginTypesBase'; +import type { GitChangelogSettings } from 'settings/settings.ts'; +import type { GitChangelogSettingsManager } from 'settings/settingsManager.ts'; +import type { GitChangelogSettingsTab } from 'settings/settingsTab.ts'; + export const PLUGIN_NAME = 'Git Changelog'; export const PLUGIN_NAME_SENTENCE_CASE = 'Git changelog'; export const MIN_COMPATIBLE_GIT_PLUGIN_VERSION = '2.31.1'; export const MAX_TESTED_GIT_PLUGIN_VERSION = '2.32.1'; +export interface GitChangelogPluginTypes extends PluginTypesBase { + plugin: GitChangelogPlugin; + pluginSettings: GitChangelogSettings; + pluginSettingsManager: GitChangelogSettingsManager; + pluginSettingsTab: GitChangelogSettingsTab; +} + // Strings export const FEEDBACK_URL = 'https://github.com/shumadrid/obsidian-git-changelog/issues'; @@ -29,3 +42,7 @@ export const CHANGELOG_LOAD_AMOUNT_BASE_MULTIPLIER = 35; export const CHANGELOG_LOAD_AMOUNT_VERSIONS = 10; export const FILE_VIEW_VERSIONS_MULTIPLIER = 2.4; export const VAULT_MAX_COUNT_MULTIPLIER = 6; + +// Settings +export const MIN_RENAME_DETECTION_STRICTNESS = 1; +export const MAX_RENAME_DETECTION_STRICTNESS = 100; diff --git a/src/core/ChangelogEntry.svelte.ts b/src/core/ChangelogEntry.svelte.ts index 3bb6ba4..7dd54f4 100644 --- a/src/core/ChangelogEntry.svelte.ts +++ b/src/core/ChangelogEntry.svelte.ts @@ -8,7 +8,7 @@ import type { export abstract class ChangelogEntry { public constructor( - public timezoneAdjustedDate: Spacetime, + public timeZoneAdjustedDate: Spacetime, public commitHash: string // Represents a single commit that's just the latest commit of a certain interval (day,week...). ) {} @@ -33,16 +33,16 @@ export class FileChangelogEntry extends ChangelogEntry implements DiffFile { pathGitRelative, status, textDiffStats, - timezoneAdjustedDate + timeZoneAdjustedDate }: { commitHash: string; fromPathGitRelative?: string; pathGitRelative: string; status: DiffFileStatus; textDiffStats?: TextDiffStats; - timezoneAdjustedDate: Spacetime; + timeZoneAdjustedDate: Spacetime; }) { - super(timezoneAdjustedDate, commitHash); + super(timeZoneAdjustedDate, commitHash); this.pathGitRelative = pathGitRelative; this.status = status; this.fromPathGitRelative = fromPathGitRelative; @@ -78,7 +78,7 @@ export class VaultChangelogEntry extends ChangelogEntry { previousDayLastCommitHash, textFiles, textFilesSummaryCached, - timezoneAdjustedDate + timeZoneAdjustedDate }: { binaryFiles: DiffFile[]; binaryFilesSummaryCached: FilesSummary; @@ -86,9 +86,9 @@ export class VaultChangelogEntry extends ChangelogEntry { previousDayLastCommitHash?: string; textFiles: DiffFile[]; textFilesSummaryCached: FilesSummary; - timezoneAdjustedDate: Spacetime; + timeZoneAdjustedDate: Spacetime; }) { - super(timezoneAdjustedDate, commitHash); + super(timeZoneAdjustedDate, commitHash); this.textFiles = textFiles; this.binaryFiles = binaryFiles; this.textFilesSummaryCached = textFilesSummaryCached; diff --git a/src/core/ChangelogManager.svelte.ts b/src/core/ChangelogManager.svelte.ts index e44c7fb..07f031e 100644 --- a/src/core/ChangelogManager.svelte.ts +++ b/src/core/ChangelogManager.svelte.ts @@ -1,6 +1,6 @@ import type { ChangelogEntry } from 'core/ChangelogEntry.svelte.ts'; import type GitChangelogPlugin from 'main.ts'; -import type { GitChangelogPluginSettings } from 'settings/settings.ts'; +import type { GitChangelogSettings } from 'settings/settings.ts'; import type { Spacetime } from 'spacetime'; import type { TaskManager } from 'TaskManager.svelte.ts'; import type { ReadonlyDeep } from 'type-fest'; @@ -12,6 +12,7 @@ import { extractLastCommitsForInterval, GIT_MAX_CONCURRENT_PROCESSES } from 'core/helper.ts'; +import { getTimeZone } from 'settings/ui/CustomTimeZone.ts'; import { AbortError, ChangelogInterval } from 'types.ts'; import { assertNotNull } from 'utils.ts'; @@ -121,12 +122,10 @@ export abstract class ChangelogManager { }); } - public generationSettingsChanged( - oldSettings: ReadonlyDeep, - newSettings: GitChangelogPluginSettings - ): boolean { - return this.getInterval(oldSettings) !== this.getInterval(newSettings); - } + public abstract specificSettingsChanged( + oldSettings: ReadonlyDeep, + newSettings: GitChangelogSettings + ): boolean; public resetAndGetSignal(): AbortSignal { // Reset `visibleEntries` to undefined each time when you schedule a recompute so that the UI correctly updates to "loading" state while it waits for the stats to compute. @@ -143,7 +142,7 @@ export abstract class ChangelogManager { public abstract setNextInterval(): Promise; public abstract getInterval( - settings?: ReadonlyDeep + settings?: ReadonlyDeep ): ChangelogInterval; protected abstract updateEntries({ @@ -168,21 +167,23 @@ export abstract class ChangelogManager { // ActiveGitFile: the current file path of some live file version. // FilePath: the path of some file in history. + const git = await this.plugin.getGit(); // Gets all commits newer (>=) than the commit of the latest cached version. - const timezoneAdjustedLogs = await runLog({ + const timeZoneAdjustedLogs = await runLog({ abortSignal, filePath: activeGitFile, lowerBoundaryCommit: this.latestCachedVersion?.commitHash, maxCount: undefined, - plugin: this.plugin, - upperBoundaryCommit: undefined + upperBoundaryCommit: undefined, + git, + renameDetectionStrictness: this.plugin.settings.renameDetectionStrictness, + timeZone: getTimeZone(this.plugin) }); const extractedVersions = await extractLastCommitsForInterval({ - changelogGenerationSettings: - this.plugin.settings.changelogGenerationSettings, + dayStartHour: this.plugin.settings.dayStartHour, interval: this.getInterval(), - timezoneAdjustedLogs + timeZoneAdjustedLogs }); // Always recalculate the latest version in cached changelog (because it likely has outdated stats), but only if there are any versions to recalculate. @@ -194,7 +195,7 @@ export abstract class ChangelogManager { extractedVersions.push({ filePath: versionBeforeLatestCached.getPotentialGitFilePath(), hash: versionBeforeLatestCached.commitHash, - timezoneAdjustedDate: versionBeforeLatestCached.timezoneAdjustedDate + timeZoneAdjustedDate: versionBeforeLatestCached.timeZoneAdjustedDate }); } @@ -392,31 +393,34 @@ export abstract class ChangelogManager { ) { logCycles++; - const timezoneAdjustedLogs = await runLog({ + const git = await this.plugin.getGit(); + const timeZoneAdjustedLogs = await runLog({ abortSignal, filePath: startingFilePath, lowerBoundaryCommit: undefined, maxCount: logMaxCount, - plugin: this.plugin, + git, + renameDetectionStrictness: + this.plugin.settings.renameDetectionStrictness, + timeZone: getTimeZone(this.plugin), upperBoundaryCommit: startingCommit }); // All we need from a version is its latest commit, not all commits included in that interval const extractedVersions = await extractLastCommitsForInterval({ - changelogGenerationSettings: - this.plugin.settings.changelogGenerationSettings, + dayStartHour: this.plugin.settings.dayStartHour, interval, previouslySeenFullyAdjustedDates: fullyAdjustedSeenDates, - timezoneAdjustedLogs + timeZoneAdjustedLogs }); - if (timezoneAdjustedLogs.length < logMaxCount) { + if (timeZoneAdjustedLogs.length < logMaxCount) { reachedInitialCommit = true; } // If getting file changelog versions and need to loop many times, we need to track the file path across renames so that we can follow the target file across its whole history. - startingFilePath = timezoneAdjustedLogs.at(-1)?.filePath; + startingFilePath = timeZoneAdjustedLogs.at(-1)?.filePath; - startingCommit = timezoneAdjustedLogs.at(-1)?.hash; + startingCommit = timeZoneAdjustedLogs.at(-1)?.hash; lastCommitsInEachVersion.push(...extractedVersions); diff --git a/src/core/FileChangelogManager.ts b/src/core/FileChangelogManager.ts index 029e185..12a5634 100644 --- a/src/core/FileChangelogManager.ts +++ b/src/core/FileChangelogManager.ts @@ -1,6 +1,6 @@ import type { FileChangelogEntry } from 'core/ChangelogEntry.svelte.ts'; import type GitChangelogPlugin from 'main.ts'; -import type { GitChangelogPluginSettings } from 'settings/settings.ts'; +import type { GitChangelogSettings } from 'settings/settings.ts'; import type { TaskManager } from 'TaskManager.svelte.ts'; import type { ReadonlyDeep } from 'type-fest'; import type { ChangelogInterval, FileLogEntry } from 'types.ts'; @@ -12,8 +12,8 @@ import { } from 'constants.ts'; import { ChangelogManager } from 'core/ChangelogManager.svelte.ts'; import { runFileDiff } from 'core/gitOperations/runFileDiff.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; -import { validateChangelogInterval } from 'settings/validation/changelogInterval.ts'; +import { deepEqual } from 'obsidian-dev-utils/Object'; +import { pickFileChangelogSettings } from 'settings/settings.ts'; export class FileChangelogManager extends ChangelogManager { public constructor({ @@ -47,23 +47,30 @@ export class FileChangelogManager extends ChangelogManager { } public override async setNextInterval(): Promise { - const newSettings = this.plugin.settingsClone; + await this.plugin.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.fileChangelogInterval = this.getNextInterval(); + } + ); + this.plugin.app.workspace.trigger( + 'git-changelog:file-changelog-generation-settings-changed' + ); + } - newSettings.fileChangelogInterval = this.getNextInterval(); + public override specificSettingsChanged( + oldSettings: ReadonlyDeep, + newSettings: GitChangelogSettings + ): boolean { + const oldVaultGenerationSettings = pickFileChangelogSettings(oldSettings); + const newVaultGenerationSettings = pickFileChangelogSettings(newSettings); - await this.plugin.saveSettings(newSettings, false); + return !deepEqual(oldVaultGenerationSettings, newVaultGenerationSettings); } public override getInterval( - settings: ReadonlyDeep = this.plugin.settings + settings: ReadonlyDeep = this.plugin.settings ): ChangelogInterval { - const interval = settings.fileChangelogInterval; - - if (!validateChangelogInterval(interval)) { - return DEFAULT_SETTINGS.fileChangelogInterval; - } - - return interval; + return settings.fileChangelogInterval; } protected override async loadEntries({ @@ -124,11 +131,17 @@ export class FileChangelogManager extends ChangelogManager { newCommit: FileLogEntry; oldCommit?: FileLogEntry; }): Promise { + const git = await this.plugin.getGit(); return await runFileDiff({ abortSignal, newCommit, oldCommit, - plugin: this.plugin + plugin: this.plugin, + diffAlgorithm: this.plugin.settings.diffAlgorithm, + whitespaceIgnoreMode: this.plugin.settings.whitespaceIgnoreMode, + ignoreBlankLines: this.plugin.settings.ignoreBlankLines, + emptyTreeHash: await this.plugin.getEmptyTreeHash(), + git }); } diff --git a/src/core/VaultChangelogManager.ts b/src/core/VaultChangelogManager.ts index 98dc77e..922a68b 100644 --- a/src/core/VaultChangelogManager.ts +++ b/src/core/VaultChangelogManager.ts @@ -1,6 +1,6 @@ import type { VaultChangelogEntry } from 'core/ChangelogEntry.svelte.ts'; import type GitChangelogPlugin from 'main.ts'; -import type { GitChangelogPluginSettings } from 'settings/settings.ts'; +import type { GitChangelogSettings } from 'settings/settings.ts'; import type { TaskManager } from 'TaskManager.svelte.ts'; import type { ReadonlyDeep } from 'type-fest'; import type { ChangelogInterval, LogEntry } from 'types.ts'; @@ -13,8 +13,7 @@ import { import { ChangelogManager } from 'core/ChangelogManager.svelte.ts'; import { runRepoDiff } from 'core/gitOperations/runRepoDiff.ts'; import { deepEqual } from 'obsidian-dev-utils/Object'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; -import { validateChangelogInterval } from 'settings/validation/changelogInterval.ts'; +import { pickVaultChangelogSettings } from 'settings/settings.ts'; export class VaultChangelogManager extends ChangelogManager { private collapseFirstVersion: boolean | undefined; @@ -48,46 +47,30 @@ export class VaultChangelogManager extends ChangelogManager } public override async setNextInterval(): Promise { - const newSettings = this.plugin.settingsClone; - - newSettings.vaultChangelogGenerationSettings.interval = - this.getNextInterval(); - - await this.plugin.saveSettings(newSettings, false); + await this.plugin.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.vaultChangelogInterval = this.getNextInterval(); + } + ); + this.plugin.app.workspace.trigger( + 'git-changelog:vault-changelog-generation-settings-changed' + ); } - public override generationSettingsChanged( - oldSettings: ReadonlyDeep, - newSettings: GitChangelogPluginSettings + public override specificSettingsChanged( + oldSettings: ReadonlyDeep, + newSettings: GitChangelogSettings ): boolean { - if ( - !deepEqual( - oldSettings.vaultChangelogGenerationSettings - .excludeFilesAndFoldersLines, - newSettings.vaultChangelogGenerationSettings.excludeFilesAndFoldersLines - ) - ) { - return true; - } - if ( - oldSettings.vaultChangelogGenerationSettings.convertToIncludeList !== - newSettings.vaultChangelogGenerationSettings.convertToIncludeList - ) { - return true; - } - return super.generationSettingsChanged(oldSettings, newSettings); + const oldVaultGenerationSettings = pickVaultChangelogSettings(oldSettings); + const newVaultGenerationSettings = pickVaultChangelogSettings(newSettings); + + return !deepEqual(oldVaultGenerationSettings, newVaultGenerationSettings); } public override getInterval( - settings: ReadonlyDeep = this.plugin.settings + settings: ReadonlyDeep = this.plugin.settings ): ChangelogInterval { - const interval = settings.vaultChangelogGenerationSettings.interval; - - if (!validateChangelogInterval(interval)) { - return DEFAULT_SETTINGS.vaultChangelogGenerationSettings.interval; - } - - return interval; + return settings.vaultChangelogInterval; } protected override calculateVersionsToAppend(resetCache: boolean): number { @@ -105,11 +88,22 @@ export class VaultChangelogManager extends ChangelogManager newCommit: LogEntry; oldCommit?: LogEntry; }): Promise { + const git = await this.plugin.getGit(); return await runRepoDiff({ abortSignal, newCommit, oldCommit, - plugin: this.plugin + plugin: this.plugin, + git, + diffAlgorithm: this.plugin.settings.diffAlgorithm, + renameLimit: this.plugin.settings.renameLimit, + renameDetectionStrictness: this.plugin.settings.renameDetectionStrictness, + emptyTreeHash: await this.plugin.getEmptyTreeHash(), + excludeFilesAndFoldersLines: + this.plugin.settings.excludeFilesAndFoldersLines, + convertToIncludeList: this.plugin.settings.convertToIncludeList, + whitespaceIgnoreMode: this.plugin.settings.whitespaceIgnoreMode, + ignoreBlankLines: this.plugin.settings.ignoreBlankLines }); } diff --git a/src/core/findFirstFileCommitBefore.ts b/src/core/findFirstFileCommitBefore.ts index 9c470ea..24ec15d 100644 --- a/src/core/findFirstFileCommitBefore.ts +++ b/src/core/findFirstFileCommitBefore.ts @@ -1,8 +1,7 @@ -import type GitChangelogPlugin from 'main.ts'; +import type { SimpleGit } from 'simple-git'; import type { LogEntry } from 'types.ts'; import { runLog } from 'core/gitOperations/runLog.ts'; -import { getTimeZone } from 'settings/ui/CustomTimeZone.ts'; import spacetime from 'spacetime'; import { AbortError } from 'types.ts'; import { assertNotNull } from 'utils.ts'; @@ -14,21 +13,23 @@ export async function findFirstFileCommitBefore({ abortSignal, filePath, minutes, - plugin + timeZone, + git, + renameDetectionStrictness }: { abortSignal: AbortSignal; filePath: string; minutes: number; - plugin: GitChangelogPlugin; + timeZone: string; + git: SimpleGit; + renameDetectionStrictness: number; }): Promise { // eslint-disable-next-line no-magic-numbers const maxCount = Math.ceil(minutes / AVERAGE_COMMIT_FREQUENCY_MINUTES) + 20; let firstEntriesOutsideInterval: LogEntry[] = []; let startingFilePath = filePath; - const currentTime = spacetime.now( - getTimeZone(plugin.settings.changelogGenerationSettings, plugin) - ); + const currentTime = spacetime.now(timeZone); let startingCommit: string | undefined; while ( @@ -36,7 +37,7 @@ export async function findFirstFileCommitBefore({ !firstEntriesOutsideInterval.at(-1) || // Only continue if commit(s) that happened before the specified interval aren't reached yet. - assertNotNull(firstEntriesOutsideInterval.at(-1)).timezoneAdjustedDate.diff( + assertNotNull(firstEntriesOutsideInterval.at(-1)).timeZoneAdjustedDate.diff( currentTime, 'minutes' @@ -49,8 +50,10 @@ export async function findFirstFileCommitBefore({ filePath: startingFilePath, lowerBoundaryCommit: undefined, maxCount, - plugin, - upperBoundaryCommit: startingCommit + upperBoundaryCommit: startingCommit, + git, + renameDetectionStrictness, + timeZone }); if (firstEntriesOutsideInterval.length < maxCount) { @@ -73,7 +76,7 @@ export async function findFirstFileCommitBefore({ // We just need to get the most recent commit that's still outside the interval. for (const entry of firstEntriesOutsideInterval) { - if (entry.timezoneAdjustedDate.diff(currentTime, 'minutes') >= minutes) { + if (entry.timeZoneAdjustedDate.diff(currentTime, 'minutes') >= minutes) { return entry; } } diff --git a/src/core/gitOperations/findFirstCommitBefore.ts b/src/core/gitOperations/findFirstCommitBefore.ts index 5d33058..84a8d7f 100644 --- a/src/core/gitOperations/findFirstCommitBefore.ts +++ b/src/core/gitOperations/findFirstCommitBefore.ts @@ -1,19 +1,24 @@ -import type GitChangelogPlugin from 'main.ts'; +import type { SimpleGit } from 'simple-git'; import type { LogEntry } from 'types.ts'; -import { getTimeZone } from 'settings/ui/CustomTimeZone.ts'; import spacetime from 'spacetime'; import { AbortError } from 'types.ts'; export async function findFirstCommitBefore({ abortSignal, minutes, - plugin + git, + timeZone }: { abortSignal: AbortSignal; minutes: number; - plugin: GitChangelogPlugin; + git: SimpleGit; + timeZone: string; }): Promise { + if (abortSignal.aborted) { + throw new AbortError(); + } + const options: Record = { // "--no-patch": null, // eslint-disable-next-line no-magic-numbers @@ -28,15 +33,7 @@ export async function findFirstCommitBefore({ strictDate: true }; - if (abortSignal.aborted) { - throw new AbortError(); - } - const git = await plugin.getGit(); const result = await git.log(options); - const timezone = getTimeZone( - plugin.settings.changelogGenerationSettings, - plugin - ); if (abortSignal.aborted) { throw new AbortError(); @@ -50,6 +47,6 @@ export async function findFirstCommitBefore({ return result.all.map((entry) => ({ filePath: undefined, hash: entry.hash, - timezoneAdjustedDate: spacetime(entry.date).goto(timezone) + timeZoneAdjustedDate: spacetime(entry.date).goto(timeZone) }))[0]; } diff --git a/src/core/gitOperations/getEmptyTreeHash.ts b/src/core/gitOperations/getEmptyTreeHash.ts deleted file mode 100644 index 091c030..0000000 --- a/src/core/gitOperations/getEmptyTreeHash.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type GitChangelogPlugin from 'main.ts'; - -async function runHashObjectEmptyTree({ - plugin -}: { - plugin: GitChangelogPlugin; -}): Promise { - const git = await plugin.getGit(); - - const emptyTreeHash = await git.raw([ - 'hash-object', - '-t', - 'tree', - '/dev/null' - ]); - - return emptyTreeHash.trim(); -} - -export async function getEmptyTreeHash({ - plugin -}: { - plugin: GitChangelogPlugin; -}): Promise { - if (plugin.emptyTreeHash) { - return plugin.emptyTreeHash; - } - - const emptyTreeHash = await runHashObjectEmptyTree({ plugin }); - plugin.emptyTreeHash ??= emptyTreeHash; - return plugin.emptyTreeHash; -} diff --git a/src/core/gitOperations/helper.ts b/src/core/gitOperations/helper.ts index f10cfe3..280efa8 100644 --- a/src/core/gitOperations/helper.ts +++ b/src/core/gitOperations/helper.ts @@ -1,9 +1,6 @@ -import type GitChangelogPlugin from 'main.ts'; import type { DiffFile, FilesSummary } from 'types.ts'; import { normalizePath } from 'obsidian'; -import { getDiffAlgorithm } from 'settings/ui/DiffAlgorithmOptions.ts'; -import { getWhitespaceIgnoreMode } from 'settings/ui/WhitespaceIgnoreMode.ts'; import { DiffAlgorithm, DiffFileStatus, WhitespaceIgnoreMode } from 'types.ts'; import { getFileNameFromPath } from 'utils.ts'; import { getDisplayExtensionFromPath } from 'Views/formatters.ts'; @@ -23,11 +20,14 @@ export function addFileStatusToSummary( } } -export function assignDiffAlgorithm( - arguments_: string[], - plugin: GitChangelogPlugin -): void { - switch (getDiffAlgorithm(plugin.settings.changelogGenerationSettings)) { +export function assignDiffAlgorithm({ + arguments_, + diffAlgorithm +}: { + arguments_: string[]; + diffAlgorithm: DiffAlgorithm; +}): void { + switch (diffAlgorithm) { case DiffAlgorithm.Default: { arguments_.push('--diff-algorithm=default'); break; @@ -44,13 +44,15 @@ export function assignDiffAlgorithm( } } -export function assignWhitespaceIgnoreSettings( - arguments_: string[], - plugin: GitChangelogPlugin -): void { - const whitespaceIgnoreMode = getWhitespaceIgnoreMode( - plugin.settings.changelogGenerationSettings - ); +export function assignWhitespaceIgnoreSettings({ + arguments_, + whitespaceIgnoreMode, + ignoreBlankLines +}: { + arguments_: string[]; + whitespaceIgnoreMode: WhitespaceIgnoreMode; + ignoreBlankLines: boolean; +}): void { switch (whitespaceIgnoreMode) { case WhitespaceIgnoreMode.None: { break; @@ -69,7 +71,7 @@ export function assignWhitespaceIgnoreSettings( } } - if (plugin.settings.changelogGenerationSettings.ignoreBlankLines) { + if (ignoreBlankLines) { arguments_.push('--ignore-blank-lines'); } } diff --git a/src/core/gitOperations/isAncestor.ts b/src/core/gitOperations/isAncestor.ts index c7d67c0..d3dc3ae 100644 --- a/src/core/gitOperations/isAncestor.ts +++ b/src/core/gitOperations/isAncestor.ts @@ -1,4 +1,4 @@ -import type GitChangelogPlugin from 'main.ts'; +import type { SimpleGit } from 'simple-git'; /** * Doesn't work because of the way SimpleGit instance is set up in Git plugin. @@ -6,16 +6,15 @@ import type GitChangelogPlugin from 'main.ts'; export async function isAncestorOf({ newCommit, oldCommit, - plugin + git }: { newCommit: string; oldCommit: string; - plugin: GitChangelogPlugin; + git: SimpleGit; }): Promise { if (oldCommit === newCommit) { return true; } - const git = await plugin.getGit(); const result = await git.raw([ 'merge-base', @@ -23,7 +22,6 @@ export async function isAncestorOf({ oldCommit, newCommit ]); - // Plugin.consoleDebug(result); if (result === '1') { return true; diff --git a/src/core/gitOperations/runCheckIgnore.ts b/src/core/gitOperations/runCheckIgnore.ts index 9f757e6..f4aee73 100644 --- a/src/core/gitOperations/runCheckIgnore.ts +++ b/src/core/gitOperations/runCheckIgnore.ts @@ -1,22 +1,20 @@ -import type GitChangelogPlugin from 'main.ts'; +import type { SimpleGit } from 'simple-git'; import { AbortError } from 'types.ts'; export async function runCheckIgnore({ abortSignal, activeGitFile, - plugin + git }: { abortSignal: AbortSignal; activeGitFile: string; - plugin: GitChangelogPlugin; + git: SimpleGit; }): Promise { if (abortSignal.aborted) { throw new AbortError(); } - const gitCheckIgnoreResult = await plugin - .getGitPlugin() - .gitManager.git.checkIgnore(activeGitFile); + const gitCheckIgnoreResult = await git.checkIgnore(activeGitFile); if (abortSignal.aborted) { throw new AbortError(); diff --git a/src/core/gitOperations/runFileDiff.ts b/src/core/gitOperations/runFileDiff.ts index ff63a16..ff574ae 100644 --- a/src/core/gitOperations/runFileDiff.ts +++ b/src/core/gitOperations/runFileDiff.ts @@ -1,8 +1,12 @@ import type GitChangelogPlugin from 'main.ts'; -import type { FileLogEntry } from 'types.ts'; +import type { SimpleGit } from 'simple-git'; +import type { + DiffAlgorithm, + FileLogEntry, + WhitespaceIgnoreMode +} from 'types.ts'; import { FileChangelogEntry } from 'core/ChangelogEntry.svelte.ts'; -import { getEmptyTreeHash } from 'core/gitOperations/getEmptyTreeHash.ts'; import { assignDiffAlgorithm, assignWhitespaceIgnoreSettings, @@ -14,19 +18,33 @@ export async function runFileDiff({ abortSignal, newCommit, oldCommit, - plugin + plugin, + git, + diffAlgorithm, + whitespaceIgnoreMode, + ignoreBlankLines, + emptyTreeHash }: { abortSignal: AbortSignal; newCommit: FileLogEntry; oldCommit?: FileLogEntry; - plugin: GitChangelogPlugin; + plugin?: GitChangelogPlugin; + git: SimpleGit; + diffAlgorithm: DiffAlgorithm; + whitespaceIgnoreMode: WhitespaceIgnoreMode; + ignoreBlankLines: boolean; + emptyTreeHash: string; }): Promise { + if (abortSignal.aborted) { + throw new AbortError(); + } + const oldVersionIsEmpty = oldCommit === undefined || oldCommit.fileDeleted === true; if (oldVersionIsEmpty && newCommit.fileDeleted) { // I assumed that the other should always be defined if one is undefined, since newCommit.hash is only undefined for commits where the file was deleted, and it can't get deleted if it didn't exist before, but these are statuses calculated from comparing neighboring commits, but we are diffing selected commits only, so maybe it's possible that we get in a situation where we compare some initial version commit (that isn't the actual initial commit, so that commit could be a deletion of that file, if a file was newly added and then deleted in the same interval) with an empty state - plugin.consoleDebug( + plugin?.consoleDebug( 'oldCommit and newCommit are both undefined, assumption is wrong' ); @@ -39,13 +57,15 @@ export async function runFileDiff({ '--no-renames' // `--exit-code`, ]; - assignDiffAlgorithm(numstatArguments, plugin); - assignWhitespaceIgnoreSettings(numstatArguments, plugin); + assignDiffAlgorithm({ arguments_: numstatArguments, diffAlgorithm }); + assignWhitespaceIgnoreSettings({ + arguments_: numstatArguments, + whitespaceIgnoreMode, + ignoreBlankLines + }); // Only one of these can be true at the same time since we are returning early if they are both true. if (oldVersionIsEmpty || newCommit.fileDeleted) { - const emptyTreeHash = await getEmptyTreeHash({ plugin }); - numstatArguments.push( emptyTreeHash, oldVersionIsEmpty ? newCommit.hash : oldCommit.hash, @@ -77,11 +97,6 @@ export async function runFileDiff({ ); } - if (abortSignal.aborted) { - throw new AbortError(); - } - - const git = await plugin.getGit(); const diffNumstatResult = await git.diffSummary(numstatArguments); if ( @@ -113,7 +128,7 @@ export async function runFileDiff({ pathGitRelative: newCommit.filePath, // Passing oldCommit.filePath or undefined for fileDeleted case could be more logical, but not compatible with use in git commands. status: fileStatus, textDiffStats, - timezoneAdjustedDate: newCommit.timezoneAdjustedDate + timeZoneAdjustedDate: newCommit.timeZoneAdjustedDate }); return fileEntry; } diff --git a/src/core/gitOperations/runHashObjectEmptyTree.ts b/src/core/gitOperations/runHashObjectEmptyTree.ts new file mode 100644 index 0000000..655c3bf --- /dev/null +++ b/src/core/gitOperations/runHashObjectEmptyTree.ts @@ -0,0 +1,17 @@ +import type { SimpleGit } from 'simple-git'; + +export async function runHashObjectEmptyTree({ + git +}: { + git: SimpleGit; +}): Promise { + const emptyTreeHash = await git.raw([ + 'hash-object', + '-t', + 'tree', + '/dev/null' + ]); + + // Trimming is required. + return emptyTreeHash.trim(); +} diff --git a/src/core/gitOperations/runLog.ts b/src/core/gitOperations/runLog.ts index ada77ae..2155aa9 100644 --- a/src/core/gitOperations/runLog.ts +++ b/src/core/gitOperations/runLog.ts @@ -1,9 +1,7 @@ -import type GitChangelogPlugin from 'main.ts'; +import type { SimpleGit } from 'simple-git'; import type { LogEntry } from 'types.ts'; import { unescapeGitFileOutput } from 'core/gitOperations/helper.ts'; -import { getTimeZone } from 'settings/ui/CustomTimeZone.ts'; -import { getRenameDetectionStrictness } from 'settings/ui/RenameDetectionStrictnessSlider.ts'; import spacetime from 'spacetime'; import { AbortError } from 'types.ts'; import { assertNotNull } from 'utils.ts'; @@ -18,16 +16,23 @@ export async function runLog({ filePath, lowerBoundaryCommit, maxCount, - plugin, - upperBoundaryCommit + upperBoundaryCommit, + timeZone, + git, + renameDetectionStrictness }: { abortSignal: AbortSignal; filePath: string | undefined; lowerBoundaryCommit: string | undefined; maxCount?: number; - plugin: GitChangelogPlugin; upperBoundaryCommit: string | undefined; + git: SimpleGit; + timeZone: string; + renameDetectionStrictness: number; }): Promise { + if (abortSignal.aborted) { + throw new AbortError(); + } // This is confusing, and could be accidentally broken in the future const retrievingNewLogs = lowerBoundaryCommit !== undefined; const options: Record = { @@ -50,8 +55,7 @@ export async function runLog({ // `--follow` does not work well on non-linear history. It does not work for files that were just renamed in the working directory but haven't been committed yet. It needs commit information to track renames. // This problem can be solved by running a separate git diff name-status command before running git log, to detect potential renames and get the last committed filename of the current file, but the performance impact is not worth it. options['--follow'] = null; - options['--find-renames'] = - `${getRenameDetectionStrictness(plugin.settings.changelogGenerationSettings)}%`; + options['--find-renames'] = `${renameDetectionStrictness}%`; } if (upperBoundaryCommit) { @@ -60,15 +64,8 @@ export async function runLog({ options['--boundary'] = null; options[`${lowerBoundaryCommit}..HEAD`] = null; } - if (abortSignal.aborted) { - throw new AbortError(); - } - const git = await plugin.getGit(); + const result = await git.log(options); - const timezone = getTimeZone( - plugin.settings.changelogGenerationSettings, - plugin - ); const logs: LogEntry[] = []; for (const entry of result.all) { @@ -109,7 +106,7 @@ export async function runLog({ : undefined, hash: entry.hash, fileDeleted, - timezoneAdjustedDate: spacetime(entry.date).goto(timezone) + timeZoneAdjustedDate: spacetime(entry.date).goto(timeZone) }); } return logs; diff --git a/src/core/gitOperations/runRepoDiff.ts b/src/core/gitOperations/runRepoDiff.ts index 84ca8e8..1fda5df 100644 --- a/src/core/gitOperations/runRepoDiff.ts +++ b/src/core/gitOperations/runRepoDiff.ts @@ -1,9 +1,16 @@ /* eslint-disable no-magic-numbers */ import type GitChangelogPlugin from 'main.ts'; -import type { DiffFile, FilesSummary, LogEntry, TextDiffFile } from 'types.ts'; +import type { SimpleGit } from 'simple-git'; +import type { + DiffAlgorithm, + DiffFile, + FilesSummary, + LogEntry, + TextDiffFile, + WhitespaceIgnoreMode +} from 'types.ts'; import { VaultChangelogEntry } from 'core/ChangelogEntry.svelte.ts'; -import { getEmptyTreeHash } from 'core/gitOperations/getEmptyTreeHash.ts'; import { addFileStatusToSummary, assignDiffAlgorithm, @@ -11,8 +18,6 @@ import { calculateFileStatusRenamedOrMoved } from 'core/gitOperations/helper.ts'; import { convertGitIgnoreToPathspec } from 'settings/ui/ExcludeFilesAndFolders.ts'; -import { getRenameLimit } from 'settings/ui/RenameDetectionFileLimit.ts'; -import { getRenameDetectionStrictness } from 'settings/ui/RenameDetectionStrictnessSlider.ts'; import { AbortError, DiffFileStatus } from 'types.ts'; import { assertNotNull, insertSorted, parseContentChange } from 'utils.ts'; @@ -54,19 +59,38 @@ export function compareTextFiles( return leftFile.pathGitRelative.localeCompare(rightFile.pathGitRelative); } +// eslint-disable-next-line complexity export async function runRepoDiff({ abortSignal, newCommit, oldCommit, - plugin + git, + plugin, + excludeFilesAndFoldersLines, + convertToIncludeList, + renameLimit, + renameDetectionStrictness, + diffAlgorithm, + whitespaceIgnoreMode, + ignoreBlankLines, + emptyTreeHash }: { abortSignal: AbortSignal; newCommit: LogEntry; oldCommit?: LogEntry; - plugin: GitChangelogPlugin; + diffAlgorithm: DiffAlgorithm; + git: SimpleGit; + excludeFilesAndFoldersLines: readonly string[]; + plugin?: GitChangelogPlugin; + convertToIncludeList: boolean; + renameLimit: number; + emptyTreeHash: string; + renameDetectionStrictness: number; + ignoreBlankLines: boolean; + whitespaceIgnoreMode: WhitespaceIgnoreMode; }): Promise { if (newCommit === undefined) { - plugin.consoleDebug('newCommit is undefined'); + plugin?.consoleDebug('newCommit is undefined'); } if (abortSignal.aborted) { @@ -74,34 +98,38 @@ export async function runRepoDiff({ } const pathSpec = convertGitIgnoreToPathspec( - plugin.settings.vaultChangelogGenerationSettings - .excludeFilesAndFoldersLines, - plugin.settings.vaultChangelogGenerationSettings.convertToIncludeList + excludeFilesAndFoldersLines, + convertToIncludeList ); const numstatArguments = [ '--numstat', - `-l${getRenameLimit(plugin.settings.changelogGenerationSettings)}`, - `--find-renames=${getRenameDetectionStrictness(plugin.settings.changelogGenerationSettings)}%`, + `-l${renameLimit}`, + `--find-renames=${renameDetectionStrictness}%`, '--color-moved=no', // Don't use empty files as rename candidates. If you delete any empty file and add a new empty file, that file will be considered a rename unless this flag is used. '--no-rename-empty', '-z' ]; - assignDiffAlgorithm(numstatArguments, plugin); - assignWhitespaceIgnoreSettings(numstatArguments, plugin); + assignDiffAlgorithm({ arguments_: numstatArguments, diffAlgorithm }); + assignWhitespaceIgnoreSettings({ + arguments_: numstatArguments, + whitespaceIgnoreMode, + ignoreBlankLines + }); let statusResult: Record | undefined; if (oldCommit === undefined) { - const emptyTreeHash = await getEmptyTreeHash({ plugin }); numstatArguments.push(emptyTreeHash, newCommit.hash); } else { statusResult = await runRepoDiffStatus({ newCommit: newCommit.hash, oldCommit: oldCommit.hash, pathSpec, - plugin + plugin, + abortSignal, + git }); numstatArguments.push(oldCommit.hash, newCommit.hash); } @@ -110,11 +138,11 @@ export async function runRepoDiff({ numstatArguments.push('--', ...pathSpec); } + const diffNumstatResult = await git.diff(numstatArguments); + if (abortSignal.aborted) { throw new AbortError(); } - const git = await plugin.getGit(); - const diffNumstatResult = await git.diff(numstatArguments); const records = diffNumstatResult.split('\0').filter((token) => token !== ''); @@ -171,7 +199,7 @@ export async function runRepoDiff({ status = DiffFileStatus.Added; } else if (oldPath) { if (typeof oldPath !== 'string') { - plugin.consoleDebug('oldPath is not a string', oldPath); + plugin?.consoleDebug('oldPath is not a string', oldPath); } status = calculateFileStatusRenamedOrMoved(oldPath, filePath); } else if (assertNotNull(statusResult)[filePath]) { @@ -215,7 +243,7 @@ export async function runRepoDiff({ previousDayLastCommitHash: oldCommit?.hash, textFiles, textFilesSummaryCached: textFilesSummary, - timezoneAdjustedDate: newCommit.timezoneAdjustedDate + timeZoneAdjustedDate: newCommit.timeZoneAdjustedDate }); return dayEntry; diff --git a/src/core/gitOperations/runRepoDiffStatus.ts b/src/core/gitOperations/runRepoDiffStatus.ts index 47bb520..fe46048 100644 --- a/src/core/gitOperations/runRepoDiffStatus.ts +++ b/src/core/gitOperations/runRepoDiffStatus.ts @@ -1,6 +1,7 @@ import type GitChangelogPlugin from 'main.ts'; +import type { SimpleGit } from 'simple-git'; -import { DiffFileStatus } from 'types.ts'; +import { AbortError, DiffFileStatus } from 'types.ts'; import type { DiffResultNameStatusFile } from './simpleGitTypes.ts'; @@ -13,13 +14,21 @@ export async function runRepoDiffStatus({ newCommit, oldCommit, pathSpec, - plugin + git, + plugin, + abortSignal }: { newCommit: string; oldCommit?: string; pathSpec: string[]; - plugin: GitChangelogPlugin; + plugin?: GitChangelogPlugin; + abortSignal: AbortSignal; + git: SimpleGit; }): Promise | undefined> { + if (abortSignal.aborted) { + throw new AbortError(); + } + if (oldCommit === undefined) { return undefined; } @@ -38,7 +47,6 @@ export async function runRepoDiffStatus({ if (pathSpec.length > 0) { diffStatusArguments.push('--', ...pathSpec); } - const git = await plugin.getGit(); const diffStatusResult = await git.diffSummary(diffStatusArguments); const changedFilesMap: Record = {}; @@ -60,7 +68,7 @@ export async function runRepoDiffStatus({ file.status ?? DiffNameStatus.MODIFIED ) ) { - plugin.consoleDebug( + plugin?.consoleDebug( `Unexpected file status of ${file.file}:`, file.status ); diff --git a/src/core/gitOperations/runWorkingDirFileDiff.ts b/src/core/gitOperations/runWorkingDirFileDiff.ts index aa3460c..d6af8bd 100644 --- a/src/core/gitOperations/runWorkingDirFileDiff.ts +++ b/src/core/gitOperations/runWorkingDirFileDiff.ts @@ -1,6 +1,11 @@ /* eslint-disable unicorn/prevent-abbreviations */ -import type GitChangelogPlugin from 'main.ts'; -import type { LogEntry, TextDiffBaseStats } from 'types.ts'; +import type { SimpleGit } from 'simple-git'; +import type { + DiffAlgorithm, + LogEntry, + TextDiffBaseStats, + WhitespaceIgnoreMode +} from 'types.ts'; import { assignDiffAlgorithm, @@ -14,32 +19,41 @@ import { AbortError } from 'types.ts'; export async function runWorkingDirFileDiff({ abortSignal, oldCommit, - plugin, - activeGitFile + activeGitFile, + git, + diffAlgorithm, + whitespaceIgnoreMode, + ignoreBlankLines }: { + diffAlgorithm: DiffAlgorithm; abortSignal: AbortSignal; oldCommit: LogEntry; activeGitFile: string; - plugin: GitChangelogPlugin; + git: SimpleGit; + whitespaceIgnoreMode: WhitespaceIgnoreMode; + ignoreBlankLines: boolean; }): Promise { + if (abortSignal.aborted) { + throw new AbortError(); + } + // Status bar calculations should handle cases of undefined oldCommit and oldCommit.fileDeleted === true before reaching this function const numstatArguments = ['--numstat', '--color-moved=no', '--no-renames']; // Must come before the commit hashes and file paths - assignDiffAlgorithm(numstatArguments, plugin); - assignWhitespaceIgnoreSettings(numstatArguments, plugin); + assignDiffAlgorithm({ arguments_: numstatArguments, diffAlgorithm }); + assignWhitespaceIgnoreSettings({ + arguments_: numstatArguments, + whitespaceIgnoreMode, + ignoreBlankLines + }); numstatArguments.push( `${oldCommit.hash}:${oldCommit.filePath}`, activeGitFile ); - if (abortSignal.aborted) { - throw new AbortError(); - } - - const git = await plugin.getGit(); const diffNumstatResult = await git.diffSummary(numstatArguments); const textDiffStats = diff --git a/src/core/helper.ts b/src/core/helper.ts index e3dc1b4..663566e 100644 --- a/src/core/helper.ts +++ b/src/core/helper.ts @@ -1,9 +1,7 @@ -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; import type { Spacetime, TimeUnit } from 'spacetime'; import type { ChangelogInterval, FilesSummary, LogEntry } from 'types.ts'; -import { getDayStartTime } from 'settings/ui/DayStartTime.ts'; -import { applyDayStartTimeSetting } from 'timeUtils.ts'; +import { applyDayStartHourSetting } from 'timeUtils.ts'; export const GIT_MAX_CONCURRENT_PROCESSES = 6; @@ -68,24 +66,24 @@ export async function isSameAsync({ } export async function extractLastCommitsForInterval({ - changelogGenerationSettings, interval, previouslySeenFullyAdjustedDates, - timezoneAdjustedLogs + timeZoneAdjustedLogs, + dayStartHour }: { - changelogGenerationSettings: ChangelogGenerationSettings; interval: ChangelogInterval; previouslySeenFullyAdjustedDates?: Set; - timezoneAdjustedLogs: LogEntry[]; + timeZoneAdjustedLogs: LogEntry[]; + dayStartHour: number; }): Promise { const lastCommitsInEachInterval: LogEntry[] = []; const fullyAdjustedSeenDates = previouslySeenFullyAdjustedDates ?? new Set(); - for (const log of timezoneAdjustedLogs) { - const fullyAdjustedLogDate = applyDayStartTimeSetting({ - dayStartTime: getDayStartTime(changelogGenerationSettings), - timezoneAdjustedDate: log.timezoneAdjustedDate + for (const log of timeZoneAdjustedLogs) { + const fullyAdjustedLogDate = applyDayStartHourSetting({ + dayStartHour, + timeZoneAdjustedDate: log.timeZoneAdjustedDate }); const logDateAlreadySeen = await dateAlreadySeen({ diff --git a/src/menu.ts b/src/menu.ts index 715bbb9..545515c 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -1,5 +1,6 @@ import type GitChangelogPlugin from 'main.ts'; import type { MenuItem, WorkspaceLeaf } from 'obsidian'; +import type { GitChangelogSettings } from 'settings/settings.ts'; import { COPY_COMMIT_HASH_ICON, PLUGIN_NAME_SENTENCE_CASE } from 'constants.ts'; import { Menu, TFolder } from 'obsidian'; @@ -117,8 +118,7 @@ export function addExcludeMenuItem({ plugin }); const ruleAlreadyExists = lineNumber !== -1; - const isIncludeList = - plugin.settings.vaultChangelogGenerationSettings.convertToIncludeList; + const isIncludeList = plugin.settings.convertToIncludeList; let actionTitle: string; if (isIncludeList) { actionTitle = ruleAlreadyExists ? 'Re-exclude' : 'Include'; @@ -225,9 +225,7 @@ export function isAbsolutePathInExcludeFilesAndFolders({ isFolder, gitRelativePath }); - const existingLines = - plugin.settingsClone.vaultChangelogGenerationSettings - .excludeFilesAndFoldersLines; + const existingLines = plugin.settings.excludeFilesAndFoldersLines; // Trim unescaped trailing white space from existing lines, since it gets trimmed by git anyways. // By doing this we won't miss already existing lines that only differ in trailing white space. @@ -239,12 +237,11 @@ export async function removeExcludeFilesAndFoldersItem( lineNumber: number, plugin: GitChangelogPlugin ): Promise { - const newSettings = plugin.settingsClone; - newSettings.vaultChangelogGenerationSettings.excludeFilesAndFoldersLines.splice( - lineNumber, - 1 + await plugin.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.excludeFilesAndFoldersLines.splice(lineNumber, 1); + } ); - await plugin.saveSettings(newSettings); } export async function addExcludeFilesAndFoldersItem({ @@ -261,10 +258,9 @@ export async function addExcludeFilesAndFoldersItem({ isFolder }); - const newSettings = plugin.settingsClone; - newSettings.vaultChangelogGenerationSettings.excludeFilesAndFoldersLines.push( - gitIgnoreRule + await plugin.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.excludeFilesAndFoldersLines.push(gitIgnoreRule); + } ); - - await plugin.saveSettings(newSettings); } diff --git a/src/settings/components/setting.ts b/src/settings/components/setting.ts index d1651d4..42ac299 100644 --- a/src/settings/components/setting.ts +++ b/src/settings/components/setting.ts @@ -1,38 +1,25 @@ import type GitChangelogPlugin from 'main.ts'; -import type { TextComponent } from 'obsidian'; -import type { - ChangelogGenerationSettings, - IGitChangelogSettings -} from 'settings/settings.ts'; import type { GitChangelogSettingsTab } from 'settings/settingsTab.ts'; -import { moment } from 'obsidian'; -import { NumberComponent } from 'obsidian-dev-utils/obsidian/Components/NumberComponent'; -import { TimeComponent } from 'obsidian-dev-utils/obsidian/Components/TimeComponent'; import { SettingEx } from 'obsidian-dev-utils/obsidian/SettingEx'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; -export abstract class GitChangelogSetting { +export abstract class SettingComponent { protected containerEl: HTMLElement; protected disabled: boolean; protected plugin: GitChangelogPlugin; - protected settingTab?: GitChangelogSettingsTab; + protected settingTab: GitChangelogSettingsTab; public constructor({ - containerEl, disabled = false, - plugin, - settingTab + plugin }: { - containerEl: HTMLElement; disabled?: boolean; plugin: GitChangelogPlugin; - settingTab?: GitChangelogSettingsTab; }) { this.plugin = plugin; - this.containerEl = containerEl; this.disabled = disabled; - this.settingTab = settingTab; + this.settingTab = this.plugin.settingsTab; + this.containerEl = this.settingTab.containerEl; } public abstract display(): void; @@ -53,46 +40,9 @@ export abstract class GitChangelogSetting { */ protected refreshDisplayWithDelay(timeout = 80): void { if (this.settingTab) { - setTimeout(() => this.settingTab?.display(), timeout); - } - } - - /** - * Sets the value in the textbox for a given setting only if the saved value differs from the default value. - * If the saved value is the default value, it probably wasn't defined by the user, so it's better to display it as a placeholder. - */ - protected setNonDefaultValue({ - diffSettingsProperty, - settingsProperty, - text - }: { - diffSettingsProperty?: keyof ChangelogGenerationSettings; - settingsProperty: keyof IGitChangelogSettings; - text: NumberComponent | TextComponent | TimeComponent; - }): void { - const storedValue = diffSettingsProperty - ? (this.plugin.settings[settingsProperty] as ChangelogGenerationSettings)[ - diffSettingsProperty - ] - : this.plugin.settings[settingsProperty]; - const defaultValue = diffSettingsProperty - ? (DEFAULT_SETTINGS[settingsProperty] as ChangelogGenerationSettings)[ - diffSettingsProperty - ] - : DEFAULT_SETTINGS[settingsProperty]; - - if (defaultValue !== storedValue) { - if (text instanceof NumberComponent) { - text.setValue(Number(storedValue)); - } else if (text instanceof TimeComponent) { - text.setValue(moment.duration({ hours: Number(storedValue) })); - } else { - text.setValue( - typeof storedValue === 'object' - ? JSON.stringify(storedValue) - : String(storedValue) - ); - } + setTimeout(() => { + this.settingTab?.display(); + }, timeout); } } } diff --git a/src/settings/components/suggest.ts b/src/settings/components/suggest.ts index fea9e5a..0335ab8 100644 --- a/src/settings/components/suggest.ts +++ b/src/settings/components/suggest.ts @@ -13,23 +13,23 @@ export class TimeZoneSuggest extends AbstractInputSuggest { public getSuggestions(inputString: string): string[] { const lowerCaseInputString = inputString.toLowerCase(); - const timezones: string[] = []; + const timeZones: string[] = []; - for (const timezone of TIME_ZONES_LIST) { - if (timezone.toLowerCase().contains(lowerCaseInputString)) { - timezones.push(timezone); + for (const timeZone of TIME_ZONES_LIST) { + if (timeZone.toLowerCase().contains(lowerCaseInputString)) { + timeZones.push(timeZone); } } - return timezones; + return timeZones; } - public renderSuggestion(timezone: string, element: HTMLElement): void { - element.setText(timezone); + public renderSuggestion(timeZone: string, element: HTMLElement): void { + element.setText(timeZone); } - public override selectSuggestion(timezone: string): void { - this.inputEl.value = timezone; + public override selectSuggestion(timeZone: string): void { + this.inputEl.value = timeZone; this.inputEl.trigger('input'); this.close(); } diff --git a/src/settings/helper.ts b/src/settings/helper.ts index c311681..15548a7 100644 --- a/src/settings/helper.ts +++ b/src/settings/helper.ts @@ -1,37 +1,22 @@ -import type GitChangelogPlugin from 'main.ts'; -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; +import type { GitChangelogSettings } from 'settings/settings.ts'; +import type { ReadonlyDeep } from 'type-fest'; import { deepEqual } from 'obsidian-dev-utils/Object'; -import { - systemTimeZoneUnchanged, - validateCustomTimeZone -} from 'settings/ui/CustomTimeZone.ts'; +import { pickGeneralChangelogSettings } from 'settings/settings.ts'; export function changelogGenerationSettingsChanged({ - newChangelogSettings, - oldChangelogSettings, - plugin + newSettings, + oldSettings }: { - newChangelogSettings: ChangelogGenerationSettings; - oldChangelogSettings: ChangelogGenerationSettings; - plugin: GitChangelogPlugin; + newSettings: ReadonlyDeep; + oldSettings: ReadonlyDeep; }): boolean { - if (!oldChangelogSettings) { - return true; - } + const oldVaultGenerationSettings = pickGeneralChangelogSettings(oldSettings); + const newVaultGenerationSettings = pickGeneralChangelogSettings(newSettings); // IsAncestor run - if ( - !validateCustomTimeZone(newChangelogSettings.timezone) && - !systemTimeZoneUnchanged(plugin) - ) { - return true; - } + // Don't have to check if the detected system time zone or detected locale changed since they're only assigned once at startup. - if (deepEqual(oldChangelogSettings, newChangelogSettings)) { - return false; - } - - return true; + return !deepEqual(oldVaultGenerationSettings, newVaultGenerationSettings); } diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 4e1e6d4..eb96f3a 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -1,4 +1,5 @@ -import { PluginSettingsBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginSettingsBase'; +import type { Except, ReadonlyDeep } from 'type-fest'; + import spacetime from 'spacetime'; import { ChangelogInterval, @@ -15,142 +16,172 @@ export const TIME_ZONES_LIST = new Set(Object.keys(spacetime().timezones)); export const MAX_SUPPORTED_INTERVAL = 99_999; // ~69 days export const AUTO_DETECT_PLACEHOLDER = 'Auto-detect'; -/** - * Each change in these settings triggers a recalculation of the changelogs statistics. - */ -export interface ChangelogGenerationSettings { - // Time settings - dayStartHour: number; - // Diff settings - detectMovedContent: boolean; - diffAlgorithm: DiffAlgorithm; - measurementUnit: DiffMeasurementUnit; - renameDetectionStrictness: number; - renameLimit: string; - timezone: string; - locale: string; - whitespaceIgnoreMode: WhitespaceIgnoreMode; - ignoreBlankLines: boolean; -} - -export interface VaultChangelogGenerationSettings { - excludeFilesAndFoldersLines: string[]; - convertToIncludeList: boolean; - interval: ChangelogInterval; -} - -export interface IGitChangelogSettings { - autoCommitDisabledWarningDismissed: boolean; - changelogGenerationSettings: ChangelogGenerationSettings; - contentDeletionsAndMovesWarningThreshold: string; - dedicatedFileTypeSummaries: string[]; - fileChangelogInterval: ChangelogInterval; - fileExplorerInterval: string; - fileExplorerStats: FileExplorerStats; - filesChangesWarningThreshold: string; - fileSummariesDisplayMode: FilesSummariesDisplayMode; - firstStartup: boolean; - notifyOnContentDeletionsAndMovesThresholdReached: boolean; - notifyOnFilesChangesThresholdReached: boolean; - statusBarInterval: string; - statusBarStats: boolean; - vaultChangelogGenerationSettings: VaultChangelogGenerationSettings; -} - -export class GitChangelogPluginSettings - extends PluginSettingsBase - implements IGitChangelogSettings -{ +export class GitChangelogSettings { // State - public autoCommitDisabledWarningDismissed: boolean = - DEFAULT_SETTINGS.autoCommitDisabledWarningDismissed; + public autoCommitDisabledWarningDismissed = false; - public firstStartup: boolean = DEFAULT_SETTINGS.firstStartup; - - public changelogGenerationSettings: ChangelogGenerationSettings = - DEFAULT_CHANGELOG_GENERATION_SETTINGS; + public firstStartup = true; /** * The number refers to either words or lines depending on what the changelog is set up to count */ - public contentDeletionsAndMovesWarningThreshold: string = - DEFAULT_SETTINGS.contentDeletionsAndMovesWarningThreshold; + public contentDeletionsAndMovesWarningThreshold = '2000'; // ShowFilesSummaryCountOptions[]; //Set; // VaultChangelogFilesVisibility: VaultChangelogFilesVisibility; // NotifyOnLargeCommitAdditions: boolean; // NotifyOnLargeCommitAdditionsWarningThreshold: string; - public dedicatedFileTypeSummaries: string[] = [ - ...DEFAULT_SETTINGS.dedicatedFileTypeSummaries - ]; + public dedicatedFileTypeSummaries: string[] = []; - public fileChangelogInterval: ChangelogInterval = - DEFAULT_SETTINGS.fileChangelogInterval; + public fileExplorerInterval = '4320'; // In mins + public fileExplorerStats: FileExplorerStats = FileExplorerStats.Disabled; - public fileExplorerInterval: string = DEFAULT_SETTINGS.fileExplorerInterval; - public fileExplorerStats: FileExplorerStats = - DEFAULT_SETTINGS.fileExplorerStats; - - public filesChangesWarningThreshold: string = - DEFAULT_SETTINGS.filesChangesWarningThreshold; + public filesChangesWarningThreshold = '50'; public fileSummariesDisplayMode: FilesSummariesDisplayMode = - DEFAULT_SETTINGS.fileSummariesDisplayMode; + FilesSummariesDisplayMode.Total; - public notifyOnContentDeletionsAndMovesThresholdReached: boolean = - DEFAULT_SETTINGS.notifyOnContentDeletionsAndMovesThresholdReached; + public notifyOnContentDeletionsAndMovesThresholdReached = true; - public notifyOnFilesChangesThresholdReached: boolean = - DEFAULT_SETTINGS.notifyOnFilesChangesThresholdReached; + public notifyOnFilesChangesThresholdReached = false; - public statusBarInterval: string = DEFAULT_SETTINGS.statusBarInterval; - public statusBarStats: boolean = DEFAULT_SETTINGS.statusBarStats; - // Specific Changelog Generation Settings - public vaultChangelogGenerationSettings: VaultChangelogGenerationSettings = - DEFAULT_VAULT_CHANGELOG_GENERATION_SETTINGS; + public statusBarInterval = 30; // In mins + public statusBarStatsEnabled = false; - public constructor(data: unknown) { - super(); - // Object.assign(this, DEFAULT_SETTINGS); - this.init(data); - this._shouldSaveAfterLoad = true; - } + // FileGenerationSettings + public fileChangelogInterval: ChangelogInterval = ChangelogInterval.Daily; + + // VaultGenerationSettings + public excludeFilesAndFoldersLines: string[] = []; + public convertToIncludeList = false; + public vaultChangelogInterval = ChangelogInterval.Daily; + + /** + * Each change in these settings triggers a recalculation of all the changelogs statistics. + */ + // Time settings + public dayStartHour = 0; + // Diff settings + public detectMovedContent = true; + public diffAlgorithm = DiffAlgorithm.Inherit; + public diffMeasurementUnit = DiffMeasurementUnit.Words; + public renameDetectionStrictness = 50; + public renameLimit = 1000; + public timeZone = AUTO_DETECT_PLACEHOLDER; + public locale = AUTO_DETECT_PLACEHOLDER; + public whitespaceIgnoreMode = WhitespaceIgnoreMode.None; + public ignoreBlankLines = false; } -export const DEFAULT_VAULT_CHANGELOG_GENERATION_SETTINGS: VaultChangelogGenerationSettings = - { - excludeFilesAndFoldersLines: [], - convertToIncludeList: false, - interval: ChangelogInterval.Daily - } as const; +// This is needed for checking if the generation settings have changed. And on each new added setting, the developer will have to explicitly include or exclude the added setting from all of these categories. -export const DEFAULT_CHANGELOG_GENERATION_SETTINGS: ChangelogGenerationSettings = - { - dayStartHour: 0, - detectMovedContent: true, - diffAlgorithm: DiffAlgorithm.Inherit, - measurementUnit: DiffMeasurementUnit.Words, - renameDetectionStrictness: 50, - renameLimit: '1000', - timezone: AUTO_DETECT_PLACEHOLDER, - locale: AUTO_DETECT_PLACEHOLDER, - whitespaceIgnoreMode: WhitespaceIgnoreMode.None, - ignoreBlankLines: false - } as const; -export const DEFAULT_SETTINGS: IGitChangelogSettings = { - autoCommitDisabledWarningDismissed: false, - changelogGenerationSettings: DEFAULT_CHANGELOG_GENERATION_SETTINGS, - vaultChangelogGenerationSettings: DEFAULT_VAULT_CHANGELOG_GENERATION_SETTINGS, - contentDeletionsAndMovesWarningThreshold: '2000', - dedicatedFileTypeSummaries: [] as const, - fileChangelogInterval: ChangelogInterval.Daily, - fileExplorerInterval: '4320', // In mins - fileExplorerStats: FileExplorerStats.Disabled, - filesChangesWarningThreshold: '50', - fileSummariesDisplayMode: FilesSummariesDisplayMode.Total, - firstStartup: true, - notifyOnContentDeletionsAndMovesThresholdReached: true, - notifyOnFilesChangesThresholdReached: false, - statusBarInterval: '30', // In mins - statusBarStats: false -} as const; +export function pickVaultChangelogSettings( + settings: ReadonlyDeep +): ReadonlyDeep { + return { + excludeFilesAndFoldersLines: settings.excludeFilesAndFoldersLines, + convertToIncludeList: settings.convertToIncludeList, + vaultChangelogInterval: settings.vaultChangelogInterval + }; +} + +export function pickFileChangelogSettings( + settings: ReadonlyDeep +): ReadonlyDeep { + return { + fileChangelogInterval: settings.fileChangelogInterval + }; +} + +export function pickGeneralChangelogSettings( + settings: ReadonlyDeep +): ReadonlyDeep { + return { + dayStartHour: settings.dayStartHour, + detectMovedContent: settings.detectMovedContent, + diffAlgorithm: settings.diffAlgorithm, + diffMeasurementUnit: settings.diffMeasurementUnit, + renameDetectionStrictness: settings.renameDetectionStrictness, + renameLimit: settings.renameLimit, + timeZone: settings.timeZone, + locale: settings.locale, + whitespaceIgnoreMode: settings.whitespaceIgnoreMode, + ignoreBlankLines: settings.ignoreBlankLines + }; +} + +type VaultGenerationSettings = Except< + GitChangelogSettings, + | 'autoCommitDisabledWarningDismissed' + | 'contentDeletionsAndMovesWarningThreshold' + | 'dayStartHour' + | 'dedicatedFileTypeSummaries' + | 'detectMovedContent' + | 'diffAlgorithm' + | 'diffMeasurementUnit' + | 'fileChangelogInterval' + | 'fileExplorerInterval' + | 'fileExplorerStats' + | 'filesChangesWarningThreshold' + | 'fileSummariesDisplayMode' + | 'firstStartup' + | 'ignoreBlankLines' + | 'locale' + | 'notifyOnContentDeletionsAndMovesThresholdReached' + | 'notifyOnFilesChangesThresholdReached' + | 'renameDetectionStrictness' + | 'renameLimit' + | 'statusBarInterval' + | 'statusBarStatsEnabled' + | 'timeZone' + | 'whitespaceIgnoreMode' +>; + +type FileGenerationSettings = Except< + GitChangelogSettings, + | 'autoCommitDisabledWarningDismissed' + | 'contentDeletionsAndMovesWarningThreshold' + | 'convertToIncludeList' + | 'dayStartHour' + | 'dedicatedFileTypeSummaries' + | 'detectMovedContent' + | 'diffAlgorithm' + | 'diffMeasurementUnit' + | 'excludeFilesAndFoldersLines' + | 'fileExplorerInterval' + | 'fileExplorerStats' + | 'filesChangesWarningThreshold' + | 'fileSummariesDisplayMode' + | 'firstStartup' + | 'ignoreBlankLines' + | 'locale' + | 'notifyOnContentDeletionsAndMovesThresholdReached' + | 'notifyOnFilesChangesThresholdReached' + | 'renameDetectionStrictness' + | 'renameLimit' + | 'statusBarInterval' + | 'statusBarStatsEnabled' + | 'timeZone' + | 'vaultChangelogInterval' + | 'whitespaceIgnoreMode' +>; + +type GenerationSettings = Except< + GitChangelogSettings, + | 'autoCommitDisabledWarningDismissed' + | 'contentDeletionsAndMovesWarningThreshold' + | 'convertToIncludeList' + | 'dedicatedFileTypeSummaries' + | 'excludeFilesAndFoldersLines' + | 'fileChangelogInterval' + | 'fileExplorerInterval' + | 'fileExplorerStats' + | 'filesChangesWarningThreshold' + | 'fileSummariesDisplayMode' + | 'firstStartup' + | 'notifyOnContentDeletionsAndMovesThresholdReached' + | 'notifyOnFilesChangesThresholdReached' + | 'statusBarInterval' + | 'statusBarStatsEnabled' + | 'vaultChangelogInterval' +>; diff --git a/src/settings/settingsManager.ts b/src/settings/settingsManager.ts new file mode 100644 index 0000000..cc32a28 --- /dev/null +++ b/src/settings/settingsManager.ts @@ -0,0 +1,230 @@ +import type { GitChangelogPluginTypes } from 'constants.ts'; +import type { MaybeReturn } from 'obsidian-dev-utils/Type'; + +import { + MAX_RENAME_DETECTION_STRICTNESS, + MIN_RENAME_DETECTION_STRICTNESS +} from 'constants.ts'; +import { PluginSettingsManagerBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginSettingsManagerBase'; +import { + GitChangelogSettings, + MAX_SUPPORTED_INTERVAL +} from 'settings/settings.ts'; +import { validateLocale } from 'settings/ui/CustomLocale.ts'; +import { validateCustomTimeZone } from 'settings/ui/CustomTimeZone.ts'; +import { + ChangelogInterval, + DiffAlgorithm, + DiffMeasurementUnit, + FileExplorerStats, + WhitespaceIgnoreMode +} from 'types.ts'; + +export class GitChangelogSettingsManager extends PluginSettingsManagerBase { + // eslint-disable-next-line @typescript-eslint/require-await + protected override async onLoadRecord( + record: Record + ): Promise { + // Only migrate if this is legacy data - check for the existence of the old settings structure + if ('vaultChangelogGenerationSettings' in record) { + // Check if the legacy excludeFilesAndFoldersLines data exists + const vaultSettings = record.vaultChangelogGenerationSettings as + | Record + | undefined; + const legacyExcludeLines = vaultSettings?.excludeFilesAndFoldersLines; + + // Check if we need to migrate (legacy data exists) + if ( + legacyExcludeLines && + Array.isArray(legacyExcludeLines) && + legacyExcludeLines.length > 0 && + (record.excludeFilesAndFoldersLines === undefined || + record.excludeFilesAndFoldersLines === null || + !Array.isArray(record.excludeFilesAndFoldersLines) || + record.excludeFilesAndFoldersLines.length === 0) + ) { + // Migrate the data + record.excludeFilesAndFoldersLines = legacyExcludeLines; + // Remove the old settings structure to prevent future migrations + delete record.vaultChangelogGenerationSettings; + } + } + } + + protected override createDefaultSettings(): GitChangelogSettings { + return new GitChangelogSettings(); + } + + protected override registerValidators(): void { + super.registerValidators(); + this.registerValidator( + 'diffMeasurementUnit', + (measurementUnit): MaybeReturn => { + if (!Object.values(DiffMeasurementUnit).includes(measurementUnit)) { + return 'Invalid measurement unit'; + } + } + ); + + this.registerValidator( + 'contentDeletionsAndMovesWarningThreshold', + (contentDeletionsAndMovesWarningThreshold): MaybeReturn => { + if ( + !Number.isInteger(Number(contentDeletionsAndMovesWarningThreshold)) || + Number(contentDeletionsAndMovesWarningThreshold) < 1 + ) { + return 'Invalid threshold value'; + } + } + ); + + this.registerValidator('locale', (locale): MaybeReturn => { + if ( + !validateLocale(locale) && + locale !== this.getProperty('locale').defaultValue + ) { + return 'Invalid locale'; + } + }); + + this.registerValidator('timeZone', (timeZone): MaybeReturn => { + if ( + !validateCustomTimeZone(timeZone) && + timeZone !== this.getProperty('timeZone').defaultValue + ) { + return 'Invalid timeZone'; + } + }); + + const ONE_DAY_IN_HOURS = 24; + + this.registerValidator( + 'dayStartHour', + (dayStartHour): MaybeReturn => { + if ( + !Number.isInteger(dayStartHour) || + dayStartHour < 0 || + dayStartHour >= ONE_DAY_IN_HOURS + ) { + return 'Invalid day start time'; + } + } + ); + + this.registerValidator( + 'diffAlgorithm', + (diffAlgorithm): MaybeReturn => { + if (!Object.values(DiffAlgorithm).includes(diffAlgorithm)) { + return 'Invalid diff algorithm'; + } + } + ); + + this.registerValidator( + 'filesChangesWarningThreshold', + (filesChangesWarningThreshold): MaybeReturn => { + if ( + !Number.isInteger(Number(filesChangesWarningThreshold)) || + Number(filesChangesWarningThreshold) < 1 + ) { + return 'Invalid threshold value'; + } + } + ); + + this.registerValidator( + 'fileExplorerStats', + (fileExplorerStats): MaybeReturn => { + if (!Object.values(FileExplorerStats).includes(fileExplorerStats)) { + return 'Invalid option'; + } + } + ); + + this.registerValidator( + 'fileExplorerInterval', + (fileExplorerInterval): MaybeReturn => { + if ( + !Number.isInteger(Number(fileExplorerInterval)) || + Number(fileExplorerInterval) < 1 || + Number(fileExplorerInterval) > MAX_SUPPORTED_INTERVAL + ) { + return 'Invalid file explorer interval'; + } + } + ); + + this.registerValidator( + 'renameLimit', + (renameLimit): MaybeReturn => { + if ( + !( + Number.isInteger(renameLimit) && + Number(renameLimit) >= 0 && + // eslint-disable-next-line no-magic-numbers + Number(renameLimit) <= 99_999_999_999_999_999_999n + ) + ) { + return 'Invalid rename limit'; + } + } + ); + + this.registerValidator( + 'renameDetectionStrictness', + (renameDetectionStrictness): MaybeReturn => { + if ( + !Number.isInteger(renameDetectionStrictness) || + renameDetectionStrictness < MIN_RENAME_DETECTION_STRICTNESS || + renameDetectionStrictness > MAX_RENAME_DETECTION_STRICTNESS + ) { + return 'Invalid rename detection strictness'; + } + } + ); + + this.registerValidator( + 'statusBarInterval', + (statusBarInterval): MaybeReturn => { + if ( + !Number.isInteger(Number(statusBarInterval)) || + Number(statusBarInterval) < 1 || + Number(statusBarInterval) > MAX_SUPPORTED_INTERVAL + ) { + return 'Invalid status bar interval'; + } + } + ); + + this.registerValidator( + 'whitespaceIgnoreMode', + (whitespaceIgnoreMode): MaybeReturn => { + if ( + !Object.values(WhitespaceIgnoreMode).includes(whitespaceIgnoreMode) + ) { + return 'Invalid whitespace ignore mode'; + } + } + ); + + this.registerValidator( + 'fileChangelogInterval', + (fileChangelogInterval): MaybeReturn => { + if (!Object.values(ChangelogInterval).includes(fileChangelogInterval)) { + return 'Invalid file changelog interval'; + } + } + ); + + this.registerValidator( + 'vaultChangelogInterval', + (vaultChangelogInterval): MaybeReturn => { + if ( + !Object.values(ChangelogInterval).includes(vaultChangelogInterval) + ) { + return 'Invalid vault changelog interval'; + } + } + ); + } +} diff --git a/src/settings/settingsTab.ts b/src/settings/settingsTab.ts index f89d336..cfe2625 100644 --- a/src/settings/settingsTab.ts +++ b/src/settings/settingsTab.ts @@ -1,7 +1,8 @@ -import type { GitChangelogPlugin } from 'GitChangelogPlugin.svelte.ts'; +import type { GitChangelogPluginTypes } from 'constants.ts'; import { PluginSettingsTabBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginSettingsTabBase'; import { CustomLocale } from 'settings/ui/CustomLocale.ts'; +import { DayStartHour } from 'settings/ui/DayStartTime.ts'; import { DiffAlgorithmOptions } from 'settings/ui/DiffAlgorithmOptions.ts'; import { IgnoreBlankLinesToggle } from 'settings/ui/IgnoreBlankLinesToggle.ts'; import { IncludeItemsToggle } from 'settings/ui/IncludeItemsToggle.ts'; @@ -11,7 +12,6 @@ import { WhitespaceSettingsHeading } from 'settings/ui/WhitespaceSettingsHeading import { AutoCommitDisabledWarning } from './ui/AutoCommitDisabledWarning.ts'; import { CustomTimeZone } from './ui/CustomTimeZone.ts'; -import { DayStartTime } from './ui/DayStartTime.ts'; import { ExcludeFilesAndFolders } from './ui/ExcludeFilesAndFolders.ts'; import { GitPluginWarning } from './ui/GitPluginWarning.ts'; import { RenameDetectionFileLimit } from './ui/RenameDetectionFileLimit.ts'; @@ -20,7 +20,7 @@ import { StatusBarInterval } from './ui/StatusBarInterval.ts'; import { StatusBarStatsToggle } from './ui/StatusBarStatsToggle.ts'; // Commented-out settings are for features that will be implemented later -export class GitChangelogSettingsTab extends PluginSettingsTabBase { +export class GitChangelogSettingsTab extends PluginSettingsTabBase { public override display(): void { const { containerEl, plugin } = this; @@ -29,93 +29,60 @@ export class GitChangelogSettingsTab extends PluginSettingsTabBase { warningSetting.settingEl.remove(); - const newSettings = this.plugin.settingsClone; - newSettings.autoCommitDisabledWarningDismissed = true; // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); + this.plugin.settingsManager.editAndSave( + (settings: GitChangelogSettings): void => { + settings.autoCommitDisabledWarningDismissed = true; + } + ); }); }); } diff --git a/src/settings/ui/ChangelogMeasurementUnit.ts b/src/settings/ui/ChangelogMeasurementUnit.ts index fdb4694..71346c8 100644 --- a/src/settings/ui/ChangelogMeasurementUnit.ts +++ b/src/settings/ui/ChangelogMeasurementUnit.ts @@ -1,48 +1,16 @@ -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; - -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; import { DiffMeasurementUnit } from 'types.ts'; -export class ChangelogMeasurementUnit extends GitChangelogSetting { +export class ChangelogMeasurementUnit extends SettingComponent { public display(): void { this.createSetting() .setName('Changelog measurement unit') - .addDropdown((dropdown) => { - const options = { - [DiffMeasurementUnit.Lines]: 'Lines', - [DiffMeasurementUnit.Words]: 'Words' - }; - dropdown.addOptions(options); - dropdown.setValue( - getMeasurementUnit(this.plugin.settings.changelogGenerationSettings) - ); - - dropdown.onChange((value: DiffMeasurementUnit) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.measurementUnit = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); + .addTypedDropdown((dropdown) => { + dropdown.addOption(DiffMeasurementUnit.Lines, 'Lines'); + dropdown.addOption(DiffMeasurementUnit.Words, 'Words'); + this.settingTab.bind(dropdown, 'diffMeasurementUnit', { + shouldShowValidationMessage: false }); }); } } -export function getMeasurementUnit( - changelogGenerationSettings: ChangelogGenerationSettings -): DiffMeasurementUnit { - return DiffMeasurementUnit.Lines; - if (!validateMeasurementUnit(changelogGenerationSettings.measurementUnit)) { - return DEFAULT_SETTINGS.changelogGenerationSettings.measurementUnit; - } - - return changelogGenerationSettings.measurementUnit; -} - -export function validateMeasurementUnit( - measurementUnit: DiffMeasurementUnit -): boolean { - if (Object.values(DiffMeasurementUnit).includes(measurementUnit)) { - return true; - } - return false; -} diff --git a/src/settings/ui/ChangelogStatsInFileExplorerOptions.ts b/src/settings/ui/ChangelogStatsInFileExplorerOptions.ts index c8fccbd..8af88a1 100644 --- a/src/settings/ui/ChangelogStatsInFileExplorerOptions.ts +++ b/src/settings/ui/ChangelogStatsInFileExplorerOptions.ts @@ -1,29 +1,24 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; import { FileExplorerStats } from 'types.ts'; -export class ChangelogStatsInFileExplorerOptions extends GitChangelogSetting { +export class ChangelogStatsInFileExplorerOptions extends SettingComponent { public display(): void { this.createSetting() .setName('Show changelog stats in file explorer') - .addDropdown((dropdown) => { - const options = { - [FileExplorerStats.Disabled]: 'Disabled', - [FileExplorerStats.Folders]: 'Folders', - [FileExplorerStats.FoldersAndNotes]: 'Folders and notes' - }; - dropdown.addOptions(options); - dropdown.setValue( - this.plugin.settings.fileExplorerStats ?? - DEFAULT_SETTINGS.fileExplorerStats + .addTypedDropdown((dropdown) => { + dropdown.addOption(FileExplorerStats.Disabled, 'Disabled'); + dropdown.addOption(FileExplorerStats.Folders, 'Folders'); + dropdown.addOption( + FileExplorerStats.FoldersAndNotes, + 'Folders and notes' ); - dropdown.onChange((value: FileExplorerStats) => { - this.refreshDisplayWithDelay(0); - const newSettings = this.plugin.settingsClone; - newSettings.fileExplorerStats = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); + this.settingTab.bind(dropdown, 'fileExplorerStats', { + shouldShowValidationMessage: false, + onChanged: () => { + // eslint-disable-next-line no-magic-numbers + this.refreshDisplayWithDelay(30); + } }); }); } diff --git a/src/settings/ui/ContentDeletionsMovesThreshold.ts b/src/settings/ui/ContentDeletionsMovesThreshold.ts index e2c5fd9..05abe9d 100644 --- a/src/settings/ui/ContentDeletionsMovesThreshold.ts +++ b/src/settings/ui/ContentDeletionsMovesThreshold.ts @@ -1,7 +1,6 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class ContentDeletionsMovesThreshold extends GitChangelogSetting { +export class ContentDeletionsMovesThreshold extends SettingComponent { public display(): void { this.createSetting() .setName('Custom threshold for deletions/moves alert') @@ -9,33 +8,12 @@ export class ContentDeletionsMovesThreshold extends GitChangelogSetting { "Acceptable amount of deletions and moves between commits. Represents either words or lines, depending on your setup. Note that this doesn't mean between each interval but between the actual commits, meaning if your auto-commit interval is five minutes, this will trigger only if you manage to delete that many files inside those five minutes, which usually signals corruption or data loss." ) .addText((text) => { - text.setDisabled(this.disabled).onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.contentDeletionsAndMovesWarningThreshold = - validateContentDeletionsMovesThreshold(value) - ? value - : DEFAULT_SETTINGS.contentDeletionsAndMovesWarningThreshold; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); + text.setDisabled(this.disabled); // This.restrictToPositiveIntegerInput(text); - this.setNonDefaultValue({ - settingsProperty: 'contentDeletionsAndMovesWarningThreshold', - text + this.settingTab.bind(text, 'contentDeletionsAndMovesWarningThreshold', { + shouldShowValidationMessage: false }); }); } } - -export function validateContentDeletionsMovesThreshold( - contentDeletionsMovesThreshold: string -): boolean { - if ( - !Number.isInteger(Number(contentDeletionsMovesThreshold)) || - Number(contentDeletionsMovesThreshold) < 1 - ) { - return false; - } - return true; -} diff --git a/src/settings/ui/CustomLocale.ts b/src/settings/ui/CustomLocale.ts index 406840f..0af607d 100644 --- a/src/settings/ui/CustomLocale.ts +++ b/src/settings/ui/CustomLocale.ts @@ -1,50 +1,58 @@ import type GitChangelogPlugin from 'main.ts'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class CustomLocale extends GitChangelogSetting { +export class CustomLocale extends SettingComponent { public display(): void { this.createSetting() .setName('Date format locale') .setDesc(`Accepts a locale code (e.g. "en-US").`) .addText((text) => { text.inputEl.maxLength = 30; - text - .setPlaceholder(DEFAULT_SETTINGS.changelogGenerationSettings.locale) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.locale = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); - this.setNonDefaultValue({ - settingsProperty: 'changelogGenerationSettings', - diffSettingsProperty: 'locale', - text + this.settingTab.bind(text, 'locale', { + shouldShowValidationMessage: false }); }); } } -export function getUserLocale(plugin: GitChangelogPlugin): string { - const locale = plugin.settings.changelogGenerationSettings.locale; - if (validateLocale(locale)) { - return locale; +function getSystemLocale(plugin: GitChangelogPlugin): string { + if (plugin.detectedLocale === undefined) { + // Detect the system locale using Intl API and save it for the current Obsidian session. + const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale; + + if (validateLocale(systemLocale)) { + plugin.detectedLocale = systemLocale; + } else { + new Notice( + "Couldn't detect a valid system locale: Obsidian installer version might be too old.\nFallback to en-US." + ); + plugin.detectedLocale = 'en-US'; + } } - // Decided against using the new Obsidian language API so that the plugin is compatible with older versions of Obsidian (for now, will update later). - return Intl.DateTimeFormat().resolvedOptions().locale; + + return plugin.detectedLocale; } -export function validateLocale(locale?: string): boolean { +export function getLocale(plugin: GitChangelogPlugin): string { + return plugin.settings.locale === + plugin.settingsManager.getProperty('locale').defaultValue + ? getSystemLocale(plugin) + : plugin.settings.locale; +} + +export function validateLocale(locale: string): boolean { try { if (!locale || typeof locale !== 'string') { return false; } new Intl.Locale(locale); - return Intl.DateTimeFormat.supportedLocalesOf([locale]).length > 0; + if (Intl.DateTimeFormat.supportedLocalesOf([locale]).length === 0) { + return false; + } } catch { return false; } + return true; } diff --git a/src/settings/ui/CustomTimeZone.ts b/src/settings/ui/CustomTimeZone.ts index 8ca0a82..b5ee505 100644 --- a/src/settings/ui/CustomTimeZone.ts +++ b/src/settings/ui/CustomTimeZone.ts @@ -1,29 +1,17 @@ import type GitChangelogPlugin from 'main.ts'; -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; import { Notice } from 'obsidian'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; import { TimeZoneSuggest } from 'settings/components/suggest.ts'; -import { DEFAULT_SETTINGS, TIME_ZONES_LIST } from 'settings/settings.ts'; +import { TIME_ZONES_LIST } from 'settings/settings.ts'; -export class CustomTimeZone extends GitChangelogSetting { +export class CustomTimeZone extends SettingComponent { public display(): void { this.createSetting() .setName('Timezone') .addText((text) => { - text - .setPlaceholder(DEFAULT_SETTINGS.changelogGenerationSettings.timezone) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.timezone = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); - - this.setNonDefaultValue({ - diffSettingsProperty: 'timezone', - settingsProperty: 'changelogGenerationSettings', - text + this.settingTab.bind(text, 'timeZone', { + shouldShowValidationMessage: false }); new TimeZoneSuggest(this.plugin.app, text.inputEl); @@ -31,47 +19,33 @@ export class CustomTimeZone extends GitChangelogSetting { } } -export function detectSystemTimeZone(): string { - return Intl.DateTimeFormat().resolvedOptions().timeZone.toLowerCase(); -} +function getSystemTimeZone(plugin: GitChangelogPlugin): string { + if (plugin.detectedTimeZone === undefined) { + // Detect the system time zone using Intl API and save it for the current Obsidian session. + const systemTimeZone = Intl.DateTimeFormat() + .resolvedOptions() + .timeZone.toLowerCase(); -export function getSystemTimeZone(plugin: GitChangelogPlugin): string { - if (plugin.detectedTimeZone !== undefined) { - return plugin.detectedTimeZone; + if (validateCustomTimeZone(systemTimeZone)) { + plugin.detectedTimeZone = systemTimeZone; + } else { + new Notice( + "Couldn't detect a valid system time zone: Obsidian installer version might be too old.\nFallback to UTC." + ); + plugin.detectedTimeZone = 'utc'; + } } - const systemTimeZone = detectSystemTimeZone(); - if (validateCustomTimeZone(systemTimeZone)) { - plugin.detectedTimeZone = systemTimeZone; - } else { - new Notice( - "Couldn't detect a valid system time zone: Obsidian installer version might be too old.\nFallback to UTC." - ); - plugin.detectedTimeZone = 'utc'; - return 'utc'; - } return plugin.detectedTimeZone; } -export function getTimeZone( - changelogGenerationSettings: ChangelogGenerationSettings, - plugin: GitChangelogPlugin -): string { - return validateCustomTimeZone(changelogGenerationSettings.timezone) - ? changelogGenerationSettings.timezone - : getSystemTimeZone(plugin); +export function getTimeZone(plugin: GitChangelogPlugin): string { + return plugin.settings.timeZone === + plugin.settingsManager.getProperty('timeZone').defaultValue + ? getSystemTimeZone(plugin) + : plugin.settings.timeZone; } -export function systemTimeZoneUnchanged(plugin: GitChangelogPlugin): boolean { - const systemTimeZone = detectSystemTimeZone(); - const cachedSystemTimeZone = plugin.detectedTimeZone; - if (systemTimeZone !== cachedSystemTimeZone) { - plugin.detectedTimeZone = systemTimeZone; - return false; - } - return true; -} - -export function validateCustomTimeZone(timezone: string): boolean { - return TIME_ZONES_LIST.has(timezone.toLowerCase()); +export function validateCustomTimeZone(timeZone: string): boolean { + return TIME_ZONES_LIST.has(timeZone.toLowerCase()); } diff --git a/src/settings/ui/DayStartTime.ts b/src/settings/ui/DayStartTime.ts index dd5d028..d16b098 100644 --- a/src/settings/ui/DayStartTime.ts +++ b/src/settings/ui/DayStartTime.ts @@ -1,60 +1,31 @@ -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; - import { moment } from 'obsidian'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class DayStartTime extends GitChangelogSetting { +export class DayStartHour extends SettingComponent { public display(): void { this.createSetting() .setName('Day start time') .setDesc('Adjust the day based on your schedule.') .addTime((text) => { - text - .setValue( - moment.duration({ - hours: - this.plugin.settings.changelogGenerationSettings.dayStartHour - }) - ) - .onChange((value) => { + this.settingTab.bind(text, 'dayStartHour', { + componentToPluginSettingsValueConverter: ( + uiValue: moment.Duration + ) => { // Clip any minutes. The smallest possible interval is an hour and this value should be clipped to that. - // Allowing the day start time to be specified in minutes doesn't make sense because then you would need to handle the half an hour that belongs to the previous day and the other half an hour that belongs to the next day separately. - text.setValue( - moment.duration({ - hours: value.hours() - }) - ); - - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.dayStartHour = - value.hours(); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); - - text.inputEl.step = '3600'; + // Allowing the day start time to be specified in minutes doesn't make sense because then e.g. you would need to handle the half an hour that belongs to the previous day and the other half an hour that belongs to the next day separately. + return uiValue.hours(); + }, + onChanged: (value) => { + text.setValue(moment.duration(value, 'hours')); + }, + shouldShowValidationMessage: false, + shouldResetSettingWhenComponentIsEmpty: false, + pluginSettingsToComponentValueConverter: ( + pluginSettingsValue: number + ) => moment.duration(pluginSettingsValue, 'hours') + }); + // eslint-disable-next-line no-magic-numbers + text.setStep(3600); }); } } - -export function getDayStartTime( - changelogGenerationSettings: ChangelogGenerationSettings -): number { - return validateDayStartTime(changelogGenerationSettings.dayStartHour) - ? changelogGenerationSettings.dayStartHour - : DEFAULT_SETTINGS.changelogGenerationSettings.dayStartHour; -} - -export const ONE_DAY_IN_HOURS = 24; - -export function validateDayStartTime(dayStartTime: number): boolean { - if ( - !Number.isInteger(dayStartTime) || - dayStartTime < 0 || - dayStartTime >= ONE_DAY_IN_HOURS - ) { - return false; - } - return true; -} diff --git a/src/settings/ui/DeletionsNotificationThreshold.ts b/src/settings/ui/DeletionsNotificationThreshold.ts index c04e71a..5925fbf 100644 --- a/src/settings/ui/DeletionsNotificationThreshold.ts +++ b/src/settings/ui/DeletionsNotificationThreshold.ts @@ -1,27 +1,23 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class DeletionsNotificationThreshold extends GitChangelogSetting { +export class DeletionsNotificationThreshold extends SettingComponent { public display(): void { this.createSetting() .setName('Notify on large amount of changes.') .setDesc( 'Notify if changes between neighboring commits exceed a threshold, which can be a sign of data loss or corruption.' ) - .addToggle((toggle) => - toggle - .setValue( - this.plugin.settings - .notifyOnContentDeletionsAndMovesThresholdReached - ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.notifyOnContentDeletionsAndMovesThresholdReached = - value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - - this.refreshDisplayWithDelay(); - }) - ); + .addToggle((toggle) => { + this.settingTab.bind( + toggle, + 'notifyOnContentDeletionsAndMovesThresholdReached', + { + shouldShowValidationMessage: false, + onChanged: () => { + this.refreshDisplayWithDelay(); + } + } + ); + }); } } diff --git a/src/settings/ui/DetectMovedContentToggle.ts b/src/settings/ui/DetectMovedContentToggle.ts index 5477ff2..336c86d 100644 --- a/src/settings/ui/DetectMovedContentToggle.ts +++ b/src/settings/ui/DetectMovedContentToggle.ts @@ -1,6 +1,6 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class DetectMovedContentToggle extends GitChangelogSetting { +export class DetectMovedContentToggle extends SettingComponent { public display(): void { this.createSetting() .setName('Detect moved lines/words') @@ -8,16 +8,9 @@ export class DetectMovedContentToggle extends GitChangelogSetting { `If enabled, changelog will also track all moved words or lines between files or moved to another location in the same file. Adds significant computational overhead that increases with the number and size of changes.` ) .addToggle((toggle) => - toggle - .setValue( - this.plugin.settings.changelogGenerationSettings.detectMovedContent - ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.detectMovedContent = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }) + this.settingTab.bind(toggle, 'detectMovedContent', { + shouldShowValidationMessage: false + }) ); } } diff --git a/src/settings/ui/DiffAlgorithmOptions.ts b/src/settings/ui/DiffAlgorithmOptions.ts index d91ba38..43efc99 100644 --- a/src/settings/ui/DiffAlgorithmOptions.ts +++ b/src/settings/ui/DiffAlgorithmOptions.ts @@ -1,14 +1,12 @@ import type GitChangelogPlugin from 'main.ts'; -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; import { appendCodeBlock } from 'obsidian-dev-utils/HTMLElement'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; import { DiffAlgorithm } from 'types.ts'; const GIT_CONFIG_PATH = '.git/config'; -export class DiffAlgorithmOptions extends GitChangelogSetting { +export class DiffAlgorithmOptions extends SettingComponent { public display(): void { this.createSetting() .setName('Difference detection algorithm') @@ -27,9 +25,7 @@ export class DiffAlgorithmOptions extends GitChangelogSetting { button.setButtonText('Apply'); button.setDisabled(shouldDisableApplyButton(this.plugin)); button.onClick(async () => { - const diffAlgorithm = getDiffAlgorithm( - this.plugin.settings.changelogGenerationSettings - ); + const diffAlgorithm = this.plugin.settings.diffAlgorithm; if (diffAlgorithm !== DiffAlgorithm.Inherit) { try { const git = await this.plugin.getGit(); @@ -45,51 +41,25 @@ export class DiffAlgorithmOptions extends GitChangelogSetting { } }); }) - .addDropdown((dropdown) => { - const options = { - [DiffAlgorithm.Inherit]: 'Inherit git config', - [DiffAlgorithm.Default]: 'Default (Faster)', - [DiffAlgorithm.Minimal]: 'Minimal (More precise)' - } as const; - dropdown.addOptions(options); - - dropdown.setValue( - getDiffAlgorithm(this.plugin.settings.changelogGenerationSettings) - ); - - dropdown.onChange((value: DiffAlgorithm) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.diffAlgorithm = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings).then(() => { + .addTypedDropdown((dropdown) => { + dropdown.addOption(DiffAlgorithm.Inherit, 'Inherit git config'); + dropdown.addOption(DiffAlgorithm.Default, 'Default (Faster)'); + dropdown.addOption(DiffAlgorithm.Minimal, 'Minimal (More precise)'); + this.settingTab.bind(dropdown, 'diffAlgorithm', { + shouldShowValidationMessage: false, + onChanged: () => { // eslint-disable-next-line no-magic-numbers this.refreshDisplayWithDelay(30); - }); + } }); }); } } function shouldDisableApplyButton(plugin: GitChangelogPlugin): boolean { - const diffAlgorithm = getDiffAlgorithm( - plugin.settings.changelogGenerationSettings - ); + const diffAlgorithm = plugin.settings.diffAlgorithm; if (diffAlgorithm === DiffAlgorithm.Inherit) { return true; } return false; } - -export function getDiffAlgorithm( - changelogGenerationSettings: ChangelogGenerationSettings -): DiffAlgorithm { - if ( - !Object.values(DiffAlgorithm).includes( - changelogGenerationSettings.diffAlgorithm - ) - ) { - return DEFAULT_SETTINGS.changelogGenerationSettings.diffAlgorithm; - } - - return changelogGenerationSettings.diffAlgorithm; -} diff --git a/src/settings/ui/ExcludeFilesAndFolders.ts b/src/settings/ui/ExcludeFilesAndFolders.ts index 5cd0985..f7a993a 100644 --- a/src/settings/ui/ExcludeFilesAndFolders.ts +++ b/src/settings/ui/ExcludeFilesAndFolders.ts @@ -3,10 +3,9 @@ import type { ReadonlyDeep } from 'type-fest'; import { EXCLUDE_FILES_AND_FOLDERS } from 'constants.ts'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class ExcludeFilesAndFolders extends GitChangelogSetting { +export class ExcludeFilesAndFolders extends SettingComponent { public display(): void { this.createSetting() .setName( @@ -40,22 +39,15 @@ export class ExcludeFilesAndFolders extends GitChangelogSetting { }) ) .addTextArea((text) => { - text - .setValue( - joinExcludeItems( - this.plugin.settingsClone.vaultChangelogGenerationSettings - .excludeFilesAndFoldersLines ?? - DEFAULT_SETTINGS.vaultChangelogGenerationSettings - .excludeFilesAndFoldersLines - ) - ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.vaultChangelogGenerationSettings.excludeFilesAndFoldersLines = - splitExcludeItems(value); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); + this.settingTab.bind(text, 'excludeFilesAndFoldersLines', { + componentToPluginSettingsValueConverter: (uiValue) => + splitExcludeItems(uiValue), + + shouldShowValidationMessage: false, + pluginSettingsToComponentValueConverter: (pluginSettingsValue) => + joinExcludeItems(pluginSettingsValue) + }); + text.inputEl.classList.add('git-changelog-text-area'); }); } diff --git a/src/settings/ui/FileChangesNotificationThreshold.ts b/src/settings/ui/FileChangesNotificationThreshold.ts index 1b18214..db76202 100644 --- a/src/settings/ui/FileChangesNotificationThreshold.ts +++ b/src/settings/ui/FileChangesNotificationThreshold.ts @@ -1,39 +1,17 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class FileChangesNotificationThreshold extends GitChangelogSetting { +export class FileChangesNotificationThreshold extends SettingComponent { public display(): void { this.createSetting() .setName('Custom threshold for file changes alert') - .addText((text) => { - text.setDisabled(this.disabled).onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.contentDeletionsAndMovesWarningThreshold = - validateFileChangesNotificationThreshold(value) - ? value - : DEFAULT_SETTINGS.contentDeletionsAndMovesWarningThreshold; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); + this.settingTab.bind(text, 'filesChangesWarningThreshold', { + shouldShowValidationMessage: false }); - // This.restrictToPositiveIntegerInput(text); - this.setNonDefaultValue({ - settingsProperty: 'filesChangesWarningThreshold', - text - }); + text.setDisabled(this.disabled); + + // This.restrictToPositiveIntegerInput(text); }); } } - -export function validateFileChangesNotificationThreshold( - fileChangesNotificationThreshold: string -): boolean { - if ( - !Number.isInteger(Number(fileChangesNotificationThreshold)) || - Number(fileChangesNotificationThreshold) < 1 - ) { - return false; - } - return true; -} diff --git a/src/settings/ui/FileExplorerStatsInterval.ts b/src/settings/ui/FileExplorerStatsInterval.ts index 18190ad..066c047 100644 --- a/src/settings/ui/FileExplorerStatsInterval.ts +++ b/src/settings/ui/FileExplorerStatsInterval.ts @@ -1,42 +1,15 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS, MAX_SUPPORTED_INTERVAL } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class FileExplorerStatsInterval extends GitChangelogSetting { +export class FileExplorerStatsInterval extends SettingComponent { public display(): void { this.createSetting() - .setName('Interval for file explorer stats (minutes)') - .addText((text) => { - text.setDisabled(this.disabled).onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.fileExplorerInterval = validateFileExplorerStatsInterval( - value - ) - ? value - : DEFAULT_SETTINGS.fileExplorerInterval; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); + this.settingTab.bind(text, 'fileExplorerInterval', { + shouldShowValidationMessage: false }); - // This.restrictToPositiveIntegerInput(text, 5); - this.setNonDefaultValue({ - settingsProperty: 'fileExplorerInterval', - text - }); + text.setDisabled(this.disabled); }); } } - -export function validateFileExplorerStatsInterval( - fileExplorerStatsInterval: string -): boolean { - if ( - !Number.isInteger(Number(fileExplorerStatsInterval)) || - Number(fileExplorerStatsInterval) < 1 || - Number(fileExplorerStatsInterval) > MAX_SUPPORTED_INTERVAL - ) { - return false; - } - return true; -} diff --git a/src/settings/ui/GitPluginWarning.ts b/src/settings/ui/GitPluginWarning.ts index b196b2a..b332176 100644 --- a/src/settings/ui/GitPluginWarning.ts +++ b/src/settings/ui/GitPluginWarning.ts @@ -6,10 +6,10 @@ import { MAX_TESTED_GIT_PLUGIN_VERSION, MIN_COMPATIBLE_GIT_PLUGIN_VERSION } from 'constants.ts'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; import { GitPluginState } from 'types.ts'; -export class GitPluginWarning extends GitChangelogSetting { +export class GitPluginWarning extends SettingComponent { public display(): void { let desc: DocumentFragment | string; let setting: Setting; diff --git a/src/settings/ui/IgnoreBlankLinesToggle.ts b/src/settings/ui/IgnoreBlankLinesToggle.ts index 3734346..37e7ce7 100644 --- a/src/settings/ui/IgnoreBlankLinesToggle.ts +++ b/src/settings/ui/IgnoreBlankLinesToggle.ts @@ -1,6 +1,6 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class IgnoreBlankLinesToggle extends GitChangelogSetting { +export class IgnoreBlankLinesToggle extends SettingComponent { public display(): void { this.createSetting() .setName( @@ -13,16 +13,9 @@ export class IgnoreBlankLinesToggle extends GitChangelogSetting { ) .setDesc('Ignore the additions and removals of completely empty lines.') .addToggle((toggle) => - toggle - .setValue( - this.plugin.settings.changelogGenerationSettings.ignoreBlankLines - ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.ignoreBlankLines = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }) + this.settingTab.bind(toggle, 'ignoreBlankLines', { + shouldShowValidationMessage: false + }) ); } } diff --git a/src/settings/ui/IncludeItemsToggle.ts b/src/settings/ui/IncludeItemsToggle.ts index fd466cb..9fee465 100644 --- a/src/settings/ui/IncludeItemsToggle.ts +++ b/src/settings/ui/IncludeItemsToggle.ts @@ -1,8 +1,8 @@ import { EXCLUDE_FILES_AND_FOLDERS } from 'constants.ts'; import { appendCodeBlock } from 'obsidian-dev-utils/HTMLElement'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class IncludeItemsToggle extends GitChangelogSetting { +export class IncludeItemsToggle extends SettingComponent { public display(): void { this.createSetting() .setName( @@ -23,18 +23,9 @@ export class IncludeItemsToggle extends GitChangelogSetting { }) ) .addToggle((toggle) => - toggle - .setValue( - this.plugin.settings.vaultChangelogGenerationSettings - .convertToIncludeList - ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.vaultChangelogGenerationSettings.convertToInclude = - value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }) + this.settingTab.bind(toggle, 'convertToIncludeList', { + shouldShowValidationMessage: false + }) ); } } diff --git a/src/settings/ui/MiscellaneousButtons.ts b/src/settings/ui/MiscellaneousButtons.ts index cbde3f6..056f608 100644 --- a/src/settings/ui/MiscellaneousButtons.ts +++ b/src/settings/ui/MiscellaneousButtons.ts @@ -1,7 +1,7 @@ import { FEEDBACK_URL } from 'constants.ts'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class MiscellaneousButtons extends GitChangelogSetting { +export class MiscellaneousButtons extends SettingComponent { public display(): void { const bugReportDiv = this.createSetting(); bugReportDiv.addButton((button) => { diff --git a/src/settings/ui/RenameDetectionFileLimit.ts b/src/settings/ui/RenameDetectionFileLimit.ts index 40bca6e..1ba6172 100644 --- a/src/settings/ui/RenameDetectionFileLimit.ts +++ b/src/settings/ui/RenameDetectionFileLimit.ts @@ -1,10 +1,7 @@ -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; - -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; // https://github.com/git/git/blob/58b5801aa94ad5031978f8e42c1be1230b3d352f/diff.c#L58 - 1000 default -export class RenameDetectionFileLimit extends GitChangelogSetting { +export class RenameDetectionFileLimit extends SettingComponent { public display(): void { this.createSetting() @@ -20,42 +17,10 @@ export class RenameDetectionFileLimit extends GitChangelogSetting { ); }) ) - .addText((text) => { - text.inputEl.pattern = '[0-9]*'; - - text.inputEl.maxLength = 20; - text.inputEl.inputMode = 'numeric'; - text - .setPlaceholder( - DEFAULT_SETTINGS.changelogGenerationSettings.renameLimit - ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.renameLimit = String(value); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); - // This.restrictToPositiveIntegerInput(text); - - this.setNonDefaultValue({ - diffSettingsProperty: 'renameLimit', - settingsProperty: 'changelogGenerationSettings', - text + .addNumber((text) => { + this.settingTab.bind(text, 'renameLimit', { + shouldShowValidationMessage: false }); }); } } - -export function getRenameLimit( - changelogGenerationSettings: ChangelogGenerationSettings -): number { - if (!validateRenameLimit(changelogGenerationSettings.renameLimit)) { - return Number(DEFAULT_SETTINGS.changelogGenerationSettings.renameLimit); - } - - return Number(changelogGenerationSettings.renameLimit); -} - -export function validateRenameLimit(renameLimit: string): boolean { - return Number(renameLimit) >= 0 && Number.isInteger(renameLimit); -} diff --git a/src/settings/ui/RenameDetectionStrictnessSlider.ts b/src/settings/ui/RenameDetectionStrictnessSlider.ts index bc17a92..b8c9c19 100644 --- a/src/settings/ui/RenameDetectionStrictnessSlider.ts +++ b/src/settings/ui/RenameDetectionStrictnessSlider.ts @@ -1,16 +1,12 @@ -import type { SliderComponent } from 'obsidian'; -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; - +import { + MAX_RENAME_DETECTION_STRICTNESS, + MIN_RENAME_DETECTION_STRICTNESS +} from 'constants.ts'; import { ResetButton } from 'settings/components/resetButton.ts'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -const MIN_RENAME_DETECTION_STRICTNESS = 1; -const MAX_RENAME_DETECTION_STRICTNESS = 100; - -export class RenameDetectionStrictnessSlider extends GitChangelogSetting { +export class RenameDetectionStrictnessSlider extends SettingComponent { public display(): void { - let slider: SliderComponent; const setting = this.createSetting() .setName('File move/rename detection strictness') .setDesc( @@ -26,50 +22,25 @@ export class RenameDetectionStrictnessSlider extends GitChangelogSetting { ); new ResetButton(setting.controlEl).onClick(() => { - slider.setValue( - DEFAULT_SETTINGS.changelogGenerationSettings.renameDetectionStrictness + const settingProperty = this.plugin.settingsManager.getProperty( + 'renameDetectionStrictness' ); + settingProperty.setValue(settingProperty.defaultValue); + this.refreshDisplayWithDelay(); }); setting.addSlider((percent) => { - slider = percent; + this.settingTab.bind(percent, 'renameDetectionStrictness', { + shouldShowValidationMessage: false + }); percent - .setValue( - getRenameDetectionStrictness( - this.plugin.settings.changelogGenerationSettings - ) - ) .setLimits( MIN_RENAME_DETECTION_STRICTNESS, MAX_RENAME_DETECTION_STRICTNESS, 1 ) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.renameDetectionStrictness = - value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }) .setDynamicTooltip(); }); } } - -export function getRenameDetectionStrictness( - changelogGenerationSettings: ChangelogGenerationSettings -): number { - if ( - !Number.isInteger(changelogGenerationSettings.renameDetectionStrictness) || - changelogGenerationSettings.renameDetectionStrictness < - MIN_RENAME_DETECTION_STRICTNESS || - changelogGenerationSettings.renameDetectionStrictness > - MAX_RENAME_DETECTION_STRICTNESS - ) { - return DEFAULT_SETTINGS.changelogGenerationSettings - .renameDetectionStrictness; - } - - return changelogGenerationSettings.renameDetectionStrictness; -} diff --git a/src/settings/ui/StatusBarInterval.ts b/src/settings/ui/StatusBarInterval.ts index f8d6048..5825ebd 100644 --- a/src/settings/ui/StatusBarInterval.ts +++ b/src/settings/ui/StatusBarInterval.ts @@ -1,60 +1,18 @@ -import type { GitChangelogPluginSettings } from 'settings/settings.ts'; -import type { ReadonlyDeep } from 'type-fest'; +import { SettingComponent } from 'settings/components/setting.ts'; -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS, MAX_SUPPORTED_INTERVAL } from 'settings/settings.ts'; - -export class StatusBarInterval extends GitChangelogSetting { +export class StatusBarInterval extends SettingComponent { public display(): void { this.createSetting() .setName('Interval for status bar stats (minutes)') .setDesc( 'Works by comparing the live file version against the first commit before the interval.' ) - .addText((text) => { - text.inputEl.pattern = '[1-9][0-9]*'; - text.inputEl.maxLength = MAX_SUPPORTED_INTERVAL.toString().length; - text.inputEl.inputMode = 'numeric'; - text - .setDisabled(this.disabled) - .setPlaceholder(DEFAULT_SETTINGS.statusBarInterval) - .onChange((value) => { - const newSettings = this.plugin.settingsClone; - newSettings.statusBarInterval = validateStatusBarInterval( - String(value) - ) - ? String(value) - : DEFAULT_SETTINGS.statusBarInterval; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - }); - - this.setNonDefaultValue({ - settingsProperty: 'statusBarInterval', - text + .addNumber((text) => { + this.settingTab.bind(text, 'statusBarInterval', { + shouldShowValidationMessage: false }); + + text.setDisabled(this.disabled); }); } } - -export function getStatusBarInterval( - settings: ReadonlyDeep -): number { - if (!validateStatusBarInterval(settings.statusBarInterval)) { - return Number(DEFAULT_SETTINGS.statusBarInterval); - } - return Number(settings.statusBarInterval); -} - -export function validateStatusBarInterval( - statusBarAlternateInterval: string -): boolean { - if ( - !Number.isInteger(Number(statusBarAlternateInterval)) || - Number(statusBarAlternateInterval) < 1 || - Number(statusBarAlternateInterval) > MAX_SUPPORTED_INTERVAL - ) { - return false; - } - return true; -} diff --git a/src/settings/ui/StatusBarStatsToggle.ts b/src/settings/ui/StatusBarStatsToggle.ts index d64c1c6..b436db2 100644 --- a/src/settings/ui/StatusBarStatsToggle.ts +++ b/src/settings/ui/StatusBarStatsToggle.ts @@ -1,27 +1,16 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class StatusBarStatsToggle extends GitChangelogSetting { +export class StatusBarStatsToggle extends SettingComponent { public display(): void { this.createSetting() .setName('Active note live status bar stats') - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.statusBarStats) - .onChange((value) => { + .addToggle((toggle) => { + this.settingTab.bind(toggle, 'statusBarStatsEnabled', { + shouldShowValidationMessage: false, + onChanged: () => { this.refreshDisplayWithDelay(); - - const newSettings = this.plugin.settingsClone; - newSettings.statusBarStats = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); - - if (value) { - this.plugin.initStatusBar(); - } else { - this.plugin.statusBarStats?.destroy?.(); - this.plugin.statusBarStats = undefined; - } - }) - ); + } + }); + }); } } diff --git a/src/settings/ui/WhitespaceIgnoreMode.ts b/src/settings/ui/WhitespaceIgnoreMode.ts index eaaccfc..24b913f 100644 --- a/src/settings/ui/WhitespaceIgnoreMode.ts +++ b/src/settings/ui/WhitespaceIgnoreMode.ts @@ -1,10 +1,7 @@ -import type { ChangelogGenerationSettings } from 'settings/settings.ts'; - -import { GitChangelogSetting } from 'settings/components/setting.ts'; -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; import { WhitespaceIgnoreMode } from 'types.ts'; -export class WhitespaceIgnoreModeOptions extends GitChangelogSetting { +export class WhitespaceIgnoreModeOptions extends SettingComponent { public display(): void { this.createSetting() .setName( @@ -15,43 +12,26 @@ export class WhitespaceIgnoreModeOptions extends GitChangelogSetting { .setText('NEW'); }) ) - .addDropdown((dropdown) => { - const options = { - [WhitespaceIgnoreMode.None]: 'Track all whitespace changes', - [WhitespaceIgnoreMode.SpaceAtEol]: - 'Ignore whitespace changes at end of line', - [WhitespaceIgnoreMode.SpaceChange]: - 'Ignore changes to pre-existing whitespace', - [WhitespaceIgnoreMode.AllSpace]: 'Ignore all whitespace changes' - } as const; - dropdown.addOptions(options); - - dropdown.setValue( - getWhitespaceIgnoreMode( - this.plugin.settings.changelogGenerationSettings - ) + .addTypedDropdown((dropdown) => { + dropdown.addOption( + WhitespaceIgnoreMode.None, + 'Track all whitespace changes' ); - - dropdown.onChange((value: WhitespaceIgnoreMode) => { - const newSettings = this.plugin.settingsClone; - newSettings.changelogGenerationSettings.whitespaceIgnoreMode = value; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.plugin.saveSettings(newSettings); + dropdown.addOption( + WhitespaceIgnoreMode.SpaceAtEol, + 'Ignore whitespace changes at end of line' + ); + dropdown.addOption( + WhitespaceIgnoreMode.SpaceChange, + 'Ignore changes to pre-existing whitespace' + ); + dropdown.addOption( + WhitespaceIgnoreMode.AllSpace, + 'Ignore all whitespace changes' + ); + this.settingTab.bind(dropdown, 'whitespaceIgnoreMode', { + shouldShowValidationMessage: false }); }); } } - -export function getWhitespaceIgnoreMode( - changelogGenerationSettings: ChangelogGenerationSettings -): WhitespaceIgnoreMode { - if ( - !Object.values(WhitespaceIgnoreMode).includes( - changelogGenerationSettings.whitespaceIgnoreMode - ) - ) { - return DEFAULT_SETTINGS.changelogGenerationSettings.whitespaceIgnoreMode; - } - - return changelogGenerationSettings.whitespaceIgnoreMode; -} diff --git a/src/settings/ui/WhitespaceSettingsHeading.ts b/src/settings/ui/WhitespaceSettingsHeading.ts index 79fedd6..a3c6fbb 100644 --- a/src/settings/ui/WhitespaceSettingsHeading.ts +++ b/src/settings/ui/WhitespaceSettingsHeading.ts @@ -1,6 +1,6 @@ -import { GitChangelogSetting } from 'settings/components/setting.ts'; +import { SettingComponent } from 'settings/components/setting.ts'; -export class WhitespaceSettingsHeading extends GitChangelogSetting { +export class WhitespaceSettingsHeading extends SettingComponent { public display(): void { this.createSetting() .setHeading() diff --git a/src/settings/validation/changelogInterval.ts b/src/settings/validation/changelogInterval.ts deleted file mode 100644 index c9556d8..0000000 --- a/src/settings/validation/changelogInterval.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type GitChangelogPlugin from 'main.ts'; - -import { DEFAULT_SETTINGS } from 'settings/settings.ts'; -import { ChangelogInterval } from 'types.ts'; - -// Redundant currently -export function getChangelogIntervalFromSettings( - plugin: GitChangelogPlugin, - fileOrVault: 'file' | 'vault' -): ChangelogInterval { - const interval = - fileOrVault === 'file' - ? plugin.settings.fileChangelogInterval - : plugin.settings.vaultChangelogGenerationSettings.interval; - - if (!validateChangelogInterval(interval)) { - return fileOrVault === 'file' - ? DEFAULT_SETTINGS.fileChangelogInterval - : DEFAULT_SETTINGS.vaultChangelogGenerationSettings.interval; - } - - return interval; -} - -export function validateChangelogInterval( - changelogInterval: ChangelogInterval -): boolean { - if (Object.values(ChangelogInterval).includes(changelogInterval)) { - return true; - } - return false; -} diff --git a/src/statusBar.ts b/src/statusBar.ts index 6eb1205..ada5aae 100644 --- a/src/statusBar.ts +++ b/src/statusBar.ts @@ -1,4 +1,4 @@ -import type { GitChangelogPluginSettings } from 'settings/settings.ts'; +import type { GitChangelogSettings } from 'settings/settings.ts'; import type { TaskManager } from 'TaskManager.svelte.ts'; import type { ReadonlyDeep } from 'type-fest'; @@ -6,8 +6,6 @@ import { findFirstFileCommitBefore } from 'core/findFirstFileCommitBefore.ts'; import { runCheckIgnore } from 'core/gitOperations/runCheckIgnore.ts'; import { runWorkingDirFileDiff } from 'core/gitOperations/runWorkingDirFileDiff.ts'; import { MarkdownView } from 'obsidian'; -import { getMeasurementUnit } from 'settings/ui/ChangelogMeasurementUnit.ts'; -import { getStatusBarInterval } from 'settings/ui/StatusBarInterval.ts'; import { AbortError, DiffMeasurementUnit } from 'types.ts'; import { getGitRelativeFilePath } from 'Views/helper.ts'; @@ -68,12 +66,10 @@ export class StatusBarStats { } public static generationSettingsChanged( - oldSettings: ReadonlyDeep, - newSettings: GitChangelogPluginSettings + oldSettings: ReadonlyDeep, + newSettings: GitChangelogSettings ): boolean { - return ( - getStatusBarInterval(oldSettings) !== getStatusBarInterval(newSettings) - ); + return oldSettings.statusBarInterval !== newSettings.statusBarInterval; } public destroy(): void { @@ -90,7 +86,7 @@ export class StatusBarStats { if (abortSignal.aborted) { throw new AbortError(); } - if (this.plugin.settings.statusBarStats) { + if (this.plugin.settings.statusBarStatsEnabled) { const result = await this.calculateStatsForActiveFile( this.plugin.app.workspace.getActiveViewOfType(MarkdownView), abortSignal @@ -133,11 +129,15 @@ export class StatusBarStats { let additions = 0; let deletions = 0; + const git = await this.plugin.getGit(); + const oldCommit = await findFirstFileCommitBefore({ abortSignal, filePath: activeGitFile, - minutes: getStatusBarInterval(this.plugin.settings), - plugin: this.plugin + minutes: this.plugin.settings.statusBarInterval, + timeZone: await this.plugin.getEmptyTreeHash(), + git, + renameDetectionStrictness: this.plugin.settings.renameDetectionStrictness }); if (oldCommit && oldCommit.fileDeleted !== true) { @@ -145,7 +145,10 @@ export class StatusBarStats { abortSignal, oldCommit, activeGitFile, - plugin: this.plugin + git, + diffAlgorithm: this.plugin.settings.diffAlgorithm, + whitespaceIgnoreMode: this.plugin.settings.whitespaceIgnoreMode, + ignoreBlankLines: this.plugin.settings.ignoreBlankLines }); if (baseStats) { additions = baseStats.additions; @@ -160,15 +163,13 @@ export class StatusBarStats { const fileIsGitIgnored = await runCheckIgnore({ abortSignal, activeGitFile, - plugin: this.plugin + git }); if (fileIsGitIgnored) { return 'In .gitignore'; } - const measurementUnit = getMeasurementUnit( - this.plugin.settings.changelogGenerationSettings - ); + const measurementUnit = this.plugin.settings.diffMeasurementUnit; if (measurementUnit === DiffMeasurementUnit.Lines) { additions = activeFileView.editor.lineCount(); diff --git a/src/timeUtils.ts b/src/timeUtils.ts index 01f56e5..c734536 100644 --- a/src/timeUtils.ts +++ b/src/timeUtils.ts @@ -3,31 +3,31 @@ import type { Spacetime } from 'spacetime'; import type { LogEntry } from 'types.ts'; /** - * This function produces "fullyAdjusted" dates, which are dates that have the "day start time" setting applied to a "timeAdjustedDate". timezoneAdjustedDate is a date adjusted with the timezone setting specified in the settings tab. + * This function produces "fullyAdjusted" dates, which are dates that have the "day start time" setting applied to a "timeAdjustedDate". timeZoneAdjustedDate is a date adjusted with the timeZone setting specified in the settings tab. */ -export function applyDayStartTimeSetting({ - dayStartTime, - timezoneAdjustedDate +export function applyDayStartHourSetting({ + dayStartHour, + timeZoneAdjustedDate }: { - dayStartTime: number; - timezoneAdjustedDate: Spacetime; + dayStartHour: number; + timeZoneAdjustedDate: Spacetime; }): Spacetime { - return timezoneAdjustedDate.subtract(dayStartTime, 'hours'); + return timeZoneAdjustedDate.subtract(dayStartHour, 'hours'); } -export function getDayStartTimeAdjustedLogs( +export function getDayStartHourAdjustedLogs( logEntries: LogEntry[], - dayStartTime: number + dayStartHour: number ): LogEntry[] { - if (dayStartTime === 0) { + if (dayStartHour === 0) { return logEntries; } return logEntries.map((entry) => { return { ...entry, - fullyAdjustedDate: applyDayStartTimeSetting({ - dayStartTime, - timezoneAdjustedDate: entry.timezoneAdjustedDate + fullyAdjustedDate: applyDayStartHourSetting({ + dayStartHour, + timeZoneAdjustedDate: entry.timeZoneAdjustedDate }) }; }); diff --git a/src/types.ts b/src/types.ts index 9754416..df42564 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,6 +41,7 @@ declare module 'obsidian' { on( name: | 'git-changelog:active-git-file-changed' + | 'git-changelog:day-changed' | 'git-changelog:file-changelog-generation-settings-changed' | 'git-changelog:generation-settings-changed' | 'git-changelog:status-bar-settings-changed' @@ -61,6 +62,7 @@ declare module 'obsidian' { trigger( name: | 'git-changelog:active-git-file-changed' + | 'git-changelog:day-changed' | 'git-changelog:file-changelog-generation-settings-changed' | 'git-changelog:generation-settings-changed' | 'git-changelog:status-bar-settings-changed' @@ -174,7 +176,7 @@ export interface FilesSummary { export interface LogEntry { // Can be null for file logs, if the file was deleted in some commit hash: string; - timezoneAdjustedDate: Spacetime; + timeZoneAdjustedDate: Spacetime; // For file git logs only: // To track file renames through history