From 9f6ec87101062180f1c8841a9f4975387628bb67 Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Mon, 9 Mar 2026 15:35:13 -0500 Subject: [PATCH 01/77] Update package-lock.json --- package-lock.json | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index d3f4391a4..39e249dbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -178,7 +178,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2011,7 +2010,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2125,7 +2123,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2166,7 +2163,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -3425,7 +3421,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -4567,7 +4562,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5142,7 +5136,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6332,7 +6325,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8579,7 +8571,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -9211,7 +9202,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -9591,7 +9581,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10798,7 +10787,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10854,7 +10842,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -10869,6 +10856,7 @@ "integrity": "sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "postcss": "^8.4.20" } @@ -10941,7 +10929,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11078,7 +11065,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11088,7 +11074,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12191,7 +12176,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", @@ -12667,7 +12651,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13151,7 +13134,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -13243,7 +13225,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, From 73e32bea798bc63b0227031730fc3cab01072deb Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 5 Apr 2026 09:12:53 -0500 Subject: [PATCH 02/77] Revert S&E Error with is isGFM function --- client/components/codeEditor/codeEditor.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index cd140ad07..c7d337a94 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -189,12 +189,12 @@ const CodeEditor = createReactClass({ // Use for GFM tabs that use common hot-keys isGFM : function() { - if((this.isGFM()) || (this.props.tab === 'brewSnippets')) return true; + if((this.props.tab === 'brewText') || (this.props.tab === 'brewSnippets')) return true; return false; }, isBrewText : function() { - if(this.isGFM()) return true; + if(this.props.tab === 'brewText') return true; return false; }, From 2a997404e37ce2bc7d2902a07e605005267bacf8 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 19 Apr 2026 14:24:27 -0500 Subject: [PATCH 03/77] A plausible fix for the hoisting issue I don't know if it's the *best* fix. --- client/homebrew/brewRenderer/brewRenderer.jsx | 7 +- package-lock.json | 85 ++++++++----------- package.json | 2 +- 3 files changed, 41 insertions(+), 53 deletions(-) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index efdfce5c9..753c9a2c9 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -220,7 +220,8 @@ const BrewRenderer = (props)=>{ } }; - const renderPages = ()=>{ + const renderPages = (forceRender = false)=>{ + if(props.errors && props.errors.length) return renderedPages; @@ -232,7 +233,7 @@ const BrewRenderer = (props)=>{ renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1); _.forEach(rawPages, (page, index)=>{ - if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){ + if((isInView(index) || !renderedPages[index] || forceRender) && typeof window !== 'undefined'){ renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range } }); @@ -280,7 +281,7 @@ const BrewRenderer = (props)=>{ }); setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame - renderPages(); //Make sure page is renderable before showing + renderPages(true); //Make sure page is renderable before showing setState((prevState)=>({ ...prevState, isMounted : true, diff --git a/package-lock.json b/package-lock.json index b67bcb18f..b6910697e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "marked-nonbreaking-spaces": "^1.0.1", "marked-smartypants-lite": "^1.0.3", "marked-subsuper-text": "^1.0.4", - "marked-variables": "^1.0.5", + "marked-variables": "file:../marked-variables", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", "mongoose": "^9.3.3", @@ -85,6 +85,38 @@ "npm": ">=10.8 <12" } }, + "../marked-variables": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "expr-eval": "^2.0.2", + "romans": "^3.1.0", + "written-number": "^0.11.1" + }, + "devDependencies": { + "@babel/core": "^7.28.6", + "@babel/preset-env": "^7.26.0", + "@markedjs/testutils": "17.0.1-2", + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^12.0.2", + "@semantic-release/npm": "^13.1.3", + "@semantic-release/release-notes-generator": "^14.0.2", + "babel-jest": "^30.2.0", + "dedent-tabs": "^0.10.3", + "eslint": "^8.57.1", + "jest": "^30.2.0", + "jest-cli": "^30.2.0", + "rollup": "^4.29.2", + "semantic-release": "^25.0.2" + }, + "peerDependencies": { + "marked": ">=3 <18" + } + }, "node_modules/@acemir/cssom": { "version": "0.9.31", "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", @@ -178,7 +210,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2011,7 +2042,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2125,7 +2155,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2174,7 +2203,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -3394,7 +3422,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -4530,7 +4557,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5116,7 +5142,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -6290,7 +6315,6 @@ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -6648,12 +6672,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/expr-eval": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", - "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==", - "license": "MIT" - }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -8553,7 +8571,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -9171,7 +9188,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -9549,7 +9565,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -9633,18 +9648,8 @@ } }, "node_modules/marked-variables": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/marked-variables/-/marked-variables-1.0.5.tgz", - "integrity": "sha512-iDsQoXA4lD1ZdPk0Rh6lHRZ2pPNRpd+dfojKvqlbgHw8VhlJfd9YBMK4Zf3McNpkF33VllJ2WbhRCfWw56gOfw==", - "license": "MIT", - "dependencies": { - "expr-eval": "^2.0.2", - "romans": "^3.1.0", - "written-number": "^0.11.1" - }, - "peerDependencies": { - "marked": ">=3 <18" - } + "resolved": "../marked-variables", + "link": true }, "node_modules/markedLegacy": { "name": "marked", @@ -10752,7 +10757,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10808,7 +10812,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -10823,6 +10826,7 @@ "integrity": "sha512-TXbU+h6vVRW+86c/+ewhWq9k7pr7ijASTnepVhCQiC87zAOTkvB1v2dHyWP+ggstSTX/PNvjzS+IOqzejndz9w==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "postcss": "^8.4.20" } @@ -10895,7 +10899,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11039,7 +11042,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11049,7 +11051,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -11333,12 +11334,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/romans": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/romans/-/romans-3.1.0.tgz", - "integrity": "sha512-URNEK8THW0suBPXUhwtHIK0CkKrtochZ8LhMPTbFoMKLtx46ImFVU+k/OaR2hdcWU/IiUeDpsKfAMKN45+79EQ==", - "license": "MIT" - }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -12117,7 +12112,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", @@ -13060,7 +13054,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -13433,12 +13426,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/written-number": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/written-number/-/written-number-0.11.1.tgz", - "integrity": "sha512-LhQ68uUnzHH0bwm/QiGA9JwqgadSDOwqB2AIs/LBsrOY6ScqVXKRN2slTCeKAhstDBJ/Of/Yxcjn0pnQmVlmtg==", - "license": "MIT" - }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/package.json b/package.json index 2ac02e4a1..e243a7a30 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "marked-nonbreaking-spaces": "^1.0.1", "marked-smartypants-lite": "^1.0.3", "marked-subsuper-text": "^1.0.4", - "marked-variables": "^1.0.5", + "marked-variables": "file:../marked-variables", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", "mongoose": "^9.3.3", From 20556276ac2d98e6e7f8f90816c40e7ac8cf0baa Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 19 Apr 2026 14:29:45 -0500 Subject: [PATCH 04/77] Wah --- package-lock.json | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index b67bcb18f..c9ba9b3d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -178,7 +178,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2011,7 +2010,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2125,7 +2123,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2174,7 +2171,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -3394,7 +3390,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -4530,7 +4525,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5116,7 +5110,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -6290,7 +6283,6 @@ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -8553,7 +8545,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -9171,7 +9162,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -9549,7 +9539,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10752,7 +10741,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10808,7 +10796,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -10823,6 +10810,7 @@ "integrity": "sha512-TXbU+h6vVRW+86c/+ewhWq9k7pr7ijASTnepVhCQiC87zAOTkvB1v2dHyWP+ggstSTX/PNvjzS+IOqzejndz9w==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "postcss": "^8.4.20" } @@ -10895,7 +10883,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11039,7 +11026,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11049,7 +11035,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12117,7 +12102,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", @@ -13060,7 +13044,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", From 59cdfcb19e042828e8664c33067d1003e82e6d3b Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 19 Apr 2026 14:34:30 -0500 Subject: [PATCH 05/77] Rework "force" to attempt to find variable references and only force those pages --- client/homebrew/brewRenderer/brewRenderer.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 753c9a2c9..851495d64 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -220,7 +220,7 @@ const BrewRenderer = (props)=>{ } }; - const renderPages = (forceRender = false)=>{ + const renderPages = (checkHoists = false)=>{ if(props.errors && props.errors.length) return renderedPages; @@ -233,6 +233,7 @@ const BrewRenderer = (props)=>{ renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1); _.forEach(rawPages, (page, index)=>{ + const forceRender = checkHoists && (page.match(/([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g)); if((isInView(index) || !renderedPages[index] || forceRender) && typeof window !== 'undefined'){ renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range } From 55acb1dc607e64e0c7cc09cc0e6e2f2074eb8e93 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 20 Apr 2026 14:00:42 -0500 Subject: [PATCH 06/77] CORRECTLY revert from testing against local marked-variables. --- package-lock.json | 66 ++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6910697e..c9ba9b3d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "marked-nonbreaking-spaces": "^1.0.1", "marked-smartypants-lite": "^1.0.3", "marked-subsuper-text": "^1.0.4", - "marked-variables": "file:../marked-variables", + "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", "mongoose": "^9.3.3", @@ -85,38 +85,6 @@ "npm": ">=10.8 <12" } }, - "../marked-variables": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "expr-eval": "^2.0.2", - "romans": "^3.1.0", - "written-number": "^0.11.1" - }, - "devDependencies": { - "@babel/core": "^7.28.6", - "@babel/preset-env": "^7.26.0", - "@markedjs/testutils": "17.0.1-2", - "@rollup/plugin-commonjs": "^29.0.0", - "@rollup/plugin-node-resolve": "^16.0.0", - "@semantic-release/changelog": "^6.0.3", - "@semantic-release/commit-analyzer": "^13.0.0", - "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^12.0.2", - "@semantic-release/npm": "^13.1.3", - "@semantic-release/release-notes-generator": "^14.0.2", - "babel-jest": "^30.2.0", - "dedent-tabs": "^0.10.3", - "eslint": "^8.57.1", - "jest": "^30.2.0", - "jest-cli": "^30.2.0", - "rollup": "^4.29.2", - "semantic-release": "^25.0.2" - }, - "peerDependencies": { - "marked": ">=3 <18" - } - }, "node_modules/@acemir/cssom": { "version": "0.9.31", "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", @@ -6672,6 +6640,12 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/expr-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", + "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==", + "license": "MIT" + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -9648,8 +9622,18 @@ } }, "node_modules/marked-variables": { - "resolved": "../marked-variables", - "link": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/marked-variables/-/marked-variables-1.0.5.tgz", + "integrity": "sha512-iDsQoXA4lD1ZdPk0Rh6lHRZ2pPNRpd+dfojKvqlbgHw8VhlJfd9YBMK4Zf3McNpkF33VllJ2WbhRCfWw56gOfw==", + "license": "MIT", + "dependencies": { + "expr-eval": "^2.0.2", + "romans": "^3.1.0", + "written-number": "^0.11.1" + }, + "peerDependencies": { + "marked": ">=3 <18" + } }, "node_modules/markedLegacy": { "name": "marked", @@ -11334,6 +11318,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/romans": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/romans/-/romans-3.1.0.tgz", + "integrity": "sha512-URNEK8THW0suBPXUhwtHIK0CkKrtochZ8LhMPTbFoMKLtx46ImFVU+k/OaR2hdcWU/IiUeDpsKfAMKN45+79EQ==", + "license": "MIT" + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -13426,6 +13416,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/written-number": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/written-number/-/written-number-0.11.1.tgz", + "integrity": "sha512-LhQ68uUnzHH0bwm/QiGA9JwqgadSDOwqB2AIs/LBsrOY6ScqVXKRN2slTCeKAhstDBJ/Of/Yxcjn0pnQmVlmtg==", + "license": "MIT" + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/package.json b/package.json index e243a7a30..2ac02e4a1 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "marked-nonbreaking-spaces": "^1.0.1", "marked-smartypants-lite": "^1.0.3", "marked-subsuper-text": "^1.0.4", - "marked-variables": "file:../marked-variables", + "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", "mongoose": "^9.3.3", From 01b3d91232d944fc88d0591838823097b2bcba8a Mon Sep 17 00:00:00 2001 From: Petrinich Sergey Date: Tue, 21 Apr 2026 22:54:09 +0300 Subject: [PATCH 07/77] add copy-url-with-page to share/edit pages Adds a "copy url (page N)" option to the Share dropdown (edit page) and the source dropdown (share page). Copies a URL with a #pN hash anchor. The existing scrollToHash handler in brewRenderer already navigates to the correct page when the URL is opened. --- client/homebrew/navbar/share.navitem.jsx | 8 +++++++- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/sharePage/sharePage.jsx | 13 +++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/client/homebrew/navbar/share.navitem.jsx b/client/homebrew/navbar/share.navitem.jsx index d0c659e2c..e329a4560 100644 --- a/client/homebrew/navbar/share.navitem.jsx +++ b/client/homebrew/navbar/share.navitem.jsx @@ -17,7 +17,7 @@ const getRedditLink = (brew)=>{ return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(brew.title.toWellFormed())}&text=${encodeURIComponent(text)}`; }; -export default ({ brew })=>( +export default ({ brew, currentPage })=>( share @@ -28,6 +28,12 @@ export default ({ brew })=>( {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}`);}}> copy url + {currentPage > 1 && + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}#p${currentPage}`);}}> + copy url (page {currentPage}) + } post to reddit diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 176158e2c..b41f1bc74 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -365,7 +365,7 @@ const EditPage = (props)=>{ - + diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 093fc8965..7f22e6c86 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -92,6 +92,19 @@ const SharePage = (props)=>{ clone to new + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}`);}}> + copy url + + {currentBrewRendererPageNum > 1 && + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}#p${currentBrewRendererPageNum}`);}}> + copy url (page {currentBrewRendererPageNum}) + } )} From 899737b5583d4f2bcd7a81b0931f05b2cfc620cc Mon Sep 17 00:00:00 2001 From: Petrinich Sergey Date: Tue, 21 Apr 2026 23:01:24 +0300 Subject: [PATCH 08/77] use fa-hashtag icon for copy-url-with-page item --- client/homebrew/pages/sharePage/sharePage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 7f22e6c86..8df241d7b 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -101,7 +101,7 @@ const SharePage = (props)=>{ {currentBrewRendererPageNum > 1 && {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}#p${currentBrewRendererPageNum}`);}}> copy url (page {currentBrewRendererPageNum}) } From 889b5de9c6009524c1f4ff92ec3587abaf529750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 22 Apr 2026 19:30:49 +0200 Subject: [PATCH 09/77] update themes --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 565fe43bb..86152bef8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.4.0", + "codemirror-5-themes": "^1.5.0", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", @@ -6094,9 +6094,9 @@ } }, "node_modules/codemirror-5-themes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.4.0.tgz", - "integrity": "sha512-3WKAmpTLqcE1MXFLHdNtwQYaxlWZXS69MY79UiNsFpW9KedMkf/vBYBjUAmacDXPKAJjdPSNMGmCPIZi21yoXA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.5.0.tgz", + "integrity": "sha512-RamWsE/r5euSYF1K3ap8cQKPqBV6cgWr7y75Ce8b4x3A389GKcD4p3b7mMEl2qLdVmmGpMUnTsXarStKZvuLXA==", "dependencies": { "@codemirror/view": "^6.41.1" } diff --git a/package.json b/package.json index b25885497..e7e4778b6 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.4.0", + "codemirror-5-themes": "^1.5.0", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", From 1cd4a698540d302bc1d2597c30fcd5be54ad6db7 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 23 Apr 2026 13:00:32 -0400 Subject: [PATCH 10/77] Make common "wrapSelected" function --- client/components/codeEditor/customKeyMaps.js | 171 ++++-------------- 1 file changed, 37 insertions(+), 134 deletions(-) diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/customKeyMaps.js index 7da06b119..a05cbacd9 100644 --- a/client/components/codeEditor/customKeyMaps.js +++ b/client/components/codeEditor/customKeyMaps.js @@ -17,125 +17,28 @@ const indentLess = (view)=>{ return true; }; -const makeBold = (view)=>{ +const wrapSelection = (prefix, suffix) => (view) => { const { from, to } = view.state.selection.main; const selected = view.state.doc.sliceString(from, to); - let text, cursor; + let text, selection; if(from === to) { - text = '****'; - cursor = from + 2; - } else if(selected.startsWith('**') && selected.endsWith('**')) { - text = selected.slice(2, -2); - cursor = from + text.length; - } else { - text = `**${selected}**`; - cursor = from + text.length; + text = prefix + suffix; + selection = { anchor: from + prefix.length, head: from + prefix.length }; + } + else if(selected.startsWith(prefix) && selected.endsWith(suffix)) { + text = selected.slice(prefix.length, -suffix.length); + selection = { anchor: from, head: from + text.length }; + } + else { + text = `${prefix}${selected}${suffix}`; + selection = { anchor: from, head: from + text.length }; } view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; - -const makeItalic = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = '**'; - cursor = from + 1; - } else if(selected.startsWith('*') && selected.endsWith('*')) { - text = selected.slice(2, -2); - cursor = from + text.length; - } else { - text = `*${selected}*`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; - -const makeUnderline = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = ''; - cursor = from + 3; - } else if(selected.startsWith('') && selected.endsWith('')) { - text = selected.slice(3, -4); - cursor = from + text.length; - } else { - text = `${selected}`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; -const makeSuper = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = '^^'; - cursor = from + 1; - } else if(selected.startsWith('^') && selected.endsWith('^')) { - text = selected.slice(1, -1); - cursor = from + text.length; - } else { - text = `^${selected}^`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; - -const makeSub = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = '^^^^'; - cursor = from + 2; - } else if(selected.startsWith('^^') && selected.endsWith('^^')) { - text = selected.slice(2, -2); - cursor = from + text.length; - } else { - text = `^^${selected}^^`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, + selection }); return true; @@ -151,8 +54,8 @@ const makeNbsp = (view) => { const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>'; view.dispatch({ - changes: { from, to: from, insert }, - selection: { anchor: from + insert.length }, + changes : { from, to: from, insert }, + selection : { anchor: from + insert.length }, }); return true; @@ -268,27 +171,27 @@ export const generalKeymap = Prec.high(keymap.of([ export const markdownKeymap = Prec.highest(keymap.of([ //{ key: 'Shift-Tab', run: indentMore }, - { key: 'Shift-Tab', run: indentLess }, - { key: 'Mod-b', run: makeBold }, - { key: 'Mod-i', run: makeItalic }, - { key: 'Mod-u', run: makeUnderline }, - { key: 'Shift-Mod-=', run: makeSuper }, - { key: 'Mod-=', run: makeSub }, - { key: 'Mod-.', run: makeNbsp }, - { key: 'Shift-Mod-.', run: makeSpace }, - { key: 'Shift-Mod-,', run: removeSpace }, - { key: 'Mod-m', run: makeSpan }, - { key: 'Shift-Mod-m', run: makeDiv }, - { key: 'Mod-/', run: makeComment }, - { key: 'Mod-k', run: makeLink }, - { key: 'Mod-l', run: makeList('UL') }, - { key: 'Shift-Mod-l', run: makeList('OL') }, - { key: 'Shift-Mod-1', run: makeHeader(1) }, - { key: 'Shift-Mod-2', run: makeHeader(2) }, - { key: 'Shift-Mod-3', run: makeHeader(3) }, - { key: 'Shift-Mod-4', run: makeHeader(4) }, - { key: 'Shift-Mod-5', run: makeHeader(5) }, - { key: 'Shift-Mod-6', run: makeHeader(6) }, + { key: 'Shift-Tab', run: indentLess }, + { key: 'Mod-b', run: wrapSelection('**', '**') }, // makeBold + { key: 'Mod-i', run: wrapSelection('*', '*') }, // makeItalic + { key: 'Mod-u', run: wrapSelection('', '') }, // makeUnderline + { key: 'Shift-Mod-=', run: wrapSelection('^', '^') }, // makeSuper + { key: 'Mod-=', run: wrapSelection('^^', '^^') }, // makeSub + { key: 'Mod-.', run: makeNbsp }, + { key: 'Shift-Mod-.', run: makeSpace }, + { key: 'Shift-Mod-,', run: removeSpace }, + { key: 'Mod-m', run: makeSpan }, + { key: 'Shift-Mod-m', run: makeDiv }, + { key: 'Mod-/', run: makeComment }, + { key: 'Mod-k', run: makeLink }, + { key: 'Mod-l', run: makeList('UL') }, + { key: 'Shift-Mod-l', run: makeList('OL') }, + { key: 'Shift-Mod-1', run: makeHeader(1) }, + { key: 'Shift-Mod-2', run: makeHeader(2) }, + { key: 'Shift-Mod-3', run: makeHeader(3) }, + { key: 'Shift-Mod-4', run: makeHeader(4) }, + { key: 'Shift-Mod-5', run: makeHeader(5) }, + { key: 'Shift-Mod-6', run: makeHeader(6) }, + { key: 'Mod-Enter', run: newPage }, { key: 'Shift-Mod-Enter', run: newColumn }, - { key: 'Mod-Enter', run: newPage }, ])); From 4fee82d3b73d717c1d5b7cb72f0863552326815c Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 23 Apr 2026 14:36:55 -0400 Subject: [PATCH 11/77] Simplify regex, fix crash on empty desc.length, only highlight if there is a Term line with content --- client/components/codeEditor/customHighlight.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 345e253d3..8797704cc 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -137,14 +137,14 @@ export function tokenizeCustomMarkdown(text) { for (let i = lineNumber + 1; i < lines.length; i++) { const nextLine = lines[i]; const onlyColonsMatch = /^:*$/.test(nextLine); - const defMatch = /^(::)(.*\S.*)?\s*$/.exec(nextLine); + const defMatch = /^(::)(.+)$/.exec(nextLine); if(!onlyColonsMatch && defMatch) { defs.push({ colons: defMatch[1], desc: defMatch[2], line: i }); endLine = i; } else break; } - if(defs.length > 0) { + if(defs.length > 0 && lineText.trim().length > 0) { tokens.push({ line : startLine, type : customTags.definitionList, @@ -175,7 +175,7 @@ export function tokenizeCustomMarkdown(text) { line : d.line, type : customTags.definitionDesc, from : d.colons.length, - to : d.colons.length + d.desc.length, + to : d.colons.length + d.desc?.length, }); }); } From cadf9ca63bdc0d8e3b26ccd678fe414b1fd9751f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 24 Apr 2026 01:05:29 +0200 Subject: [PATCH 12/77] update codemirror themes --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 565fe43bb..f7657daf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.4.0", + "codemirror-5-themes": "^1.5.1", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", @@ -6094,9 +6094,9 @@ } }, "node_modules/codemirror-5-themes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.4.0.tgz", - "integrity": "sha512-3WKAmpTLqcE1MXFLHdNtwQYaxlWZXS69MY79UiNsFpW9KedMkf/vBYBjUAmacDXPKAJjdPSNMGmCPIZi21yoXA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.5.1.tgz", + "integrity": "sha512-vHUyvOYy8yhqCDkps5LwoUFXHXIuKjE32n6EPP5v004fXmkZkrAC9mUsr+anNyOniaixE1W+xwcM4lgnbYfWsQ==", "dependencies": { "@codemirror/view": "^6.41.1" } diff --git a/package.json b/package.json index b25885497..6989c444f 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.4.0", + "codemirror-5-themes": "^1.5.1", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", From cb4bc16c69784d2b5c9386d9035cd4b708f86ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 24 Apr 2026 10:17:28 +0200 Subject: [PATCH 13/77] move codemirror extensions into their own folder --- client/components/codeEditor/codeEditor.jsx | 10 +++++----- .../codeEditor/{ => extensions}/autocompleteEmoji.js | 0 .../codeEditor/{ => extensions}/customFolding.js | 0 .../codeEditor/{ => extensions}/customHighlight.js | 0 .../codeEditor/{ => extensions}/customKeyMaps.js | 0 .../{ => extensions}/legacyCustomHighlight.js | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename client/components/codeEditor/{ => extensions}/autocompleteEmoji.js (100%) rename client/components/codeEditor/{ => extensions}/customFolding.js (100%) rename client/components/codeEditor/{ => extensions}/customHighlight.js (100%) rename client/components/codeEditor/{ => extensions}/customKeyMaps.js (100%) rename client/components/codeEditor/{ => extensions}/legacyCustomHighlight.js (100%) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 5f46fc3e4..3495570ff 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -24,7 +24,7 @@ import { languages } from '@codemirror/language-data'; import { css } from '@codemirror/lang-css'; import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; import { html } from '@codemirror/lang-html'; -import { autocompleteEmoji } from './autocompleteEmoji.js'; +import { autocompleteEmoji } from './extensions/autocompleteEmoji.js'; import { searchKeymap, search } from '@codemirror/search'; import { closeBrackets } from '@codemirror/autocomplete'; @@ -40,10 +40,10 @@ const highlightCompartment = new Compartment(); console.log(themes); -import { generalKeymap, markdownKeymap } from './customKeyMaps.js'; -import foldOnPages from './customFolding.js'; -import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './customHighlight.js'; -import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './legacyCustomHighlight.js'; +import { generalKeymap, markdownKeymap } from './extensions/customKeyMaps.js'; +import foldOnPages from './extensions/customFolding.js'; +import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './extensions/customHighlight.js'; +import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './extensions/legacyCustomHighlight.js'; const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; diff --git a/client/components/codeEditor/autocompleteEmoji.js b/client/components/codeEditor/extensions/autocompleteEmoji.js similarity index 100% rename from client/components/codeEditor/autocompleteEmoji.js rename to client/components/codeEditor/extensions/autocompleteEmoji.js diff --git a/client/components/codeEditor/customFolding.js b/client/components/codeEditor/extensions/customFolding.js similarity index 100% rename from client/components/codeEditor/customFolding.js rename to client/components/codeEditor/extensions/customFolding.js diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js similarity index 100% rename from client/components/codeEditor/customHighlight.js rename to client/components/codeEditor/extensions/customHighlight.js diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/extensions/customKeyMaps.js similarity index 100% rename from client/components/codeEditor/customKeyMaps.js rename to client/components/codeEditor/extensions/customKeyMaps.js diff --git a/client/components/codeEditor/legacyCustomHighlight.js b/client/components/codeEditor/extensions/legacyCustomHighlight.js similarity index 100% rename from client/components/codeEditor/legacyCustomHighlight.js rename to client/components/codeEditor/extensions/legacyCustomHighlight.js From 0587266e22c07645a8066b4a00467d8700472170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 24 Apr 2026 12:34:16 +0200 Subject: [PATCH 14/77] repair injection highlight and add image previews --- client/components/codeEditor/codeEditor.jsx | 102 ++++++++++++++++-- client/components/codeEditor/codeEditor.less | 38 +++++++ .../codeEditor/extensions/customHighlight.js | 8 +- 3 files changed, 134 insertions(+), 14 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 3495570ff..d677dc65e 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -38,15 +38,30 @@ const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const themeCompartment = new Compartment(); const highlightCompartment = new Compartment(); -console.log(themes); - import { generalKeymap, markdownKeymap } from './extensions/customKeyMaps.js'; import foldOnPages from './extensions/customFolding.js'; import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './extensions/customHighlight.js'; import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './extensions/legacyCustomHighlight.js'; +import { syntaxTree } from '@codemirror/language'; const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; +function getUrl(node, doc) { + let url = null; + + const cursor = node.node.cursor(); + + if (cursor.firstChild()) { + do { + if (cursor.name === "URL") { + url = doc.sliceString(cursor.from, cursor.to); + break; + } + } while (cursor.nextSibling()); + } + + return url; +} const createHighlightPlugin = (renderer, tab)=>{ //this function takes the custom tokens created in the tokenize function in customhighlight files //takes the tokens defined by that function and assigns classes to them @@ -76,21 +91,84 @@ const createHighlightPlugin = (renderer, tab)=>{ let pageCount = 1; let snippetCount = 0; + const tree = syntaxTree(view.state); +tree.iterate({ + enter: (node) => { + if (node.name === "Image") { + const url = getUrl(node, view.state.doc); + + if (!url) return; + + decos.push( + Decoration.mark({ + class: "cm-image", + attributes: { + "style": `--preview-img:url(${url});` + } + }).range(node.from, node.to) + ); + } + } +}); + + tokens.forEach((tok)=>{ const line = view.state.doc.line(tok.line + 1); + // INLINE TOKENS (marks) if(tok.from != null && tok.to != null && tok.from < tok.to) { - decos.push(Decoration.mark({ class: `cm-${tok.type}` }).range(line.from + tok.from, line.from + tok.to)); - } else { - decos.push(Decoration.line({ class: `cm-${tok.type}` }).range(line.from)); - if(tok.type === 'pageLine' && tab === 'brewText') { - pageCount++; - line.from === 0 && pageCount--; - decos.push(Decoration.line({ attributes: { 'data-page-number': pageCount } }).range(line.from)); + const from = line.from + tok.from; + const to = line.from + tok.to; + + const attrs = {}; + console.log(tok); + // attach URL only for links + if(tok.type === 'Image' && tok.url) { + + attrs['data-url'] = tok.url; } + + decos.push( + Decoration.mark({ + class : `cm-${tok.type}`, + ...(Object.keys(attrs).length + ? { attributes: attrs } + : {}) + }).range(from, to) + ); + } + + // LINE TOKENS + else { + decos.push( + Decoration.line({ + class : `cm-${tok.type}` + }).range(line.from) + ); + + if(tok.type === 'pageLine' && tab === 'brewText') { + pageCount++; + if(line.from === 0) pageCount--; + + decos.push( + Decoration.line({ + attributes : { + 'data-page-number' : pageCount + } + }).range(line.from) + ); + } + if(tok.type === 'snippetLine' && tab === 'brewSnippets') { snippetCount++; - decos.push(Decoration.line({ attributes: { 'data-page-number': snippetCount } }).range(line.from)); + + decos.push( + Decoration.line({ + attributes : { + 'data-page-number' : snippetCount + } + }).range(line.from) + ); } } }); @@ -99,7 +177,9 @@ const createHighlightPlugin = (renderer, tab)=>{ return Decoration.set(decos); } }, - { decorations: (v)=>v.decorations } + { + decorations : (v)=>v.decorations + } ); }; diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index 84ed8058d..3594742fd 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -157,6 +157,44 @@ outline : 1px inset #00000055 !important; } + .cm-image[style] { + position: relative; + + &::before, &::after { + content:""; + width:100px; + height:100px; + position:absolute; + bottom:0; + left:0; + translate:0 100%; + display:block; + z-index:1000; + pointer-events: none; + opacity:0; + transition:0.2s opacity; + } + + &::before{ + background-color: #fff; + border-radius:10px; + border:3px solid grey; + } + + &::after { + background-image: var(--preview-img); + background-size:80%; + background-repeat:no-repeat; + background-position:center; + filter:drop-shadow(0 0 5px #0008); + } + + &:hover::before, + &:hover::after { + opacity:1; + } + } + /* Tab character visualization (optional) */ //.cm-tab { // background: url(...) no-repeat right; diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 8797704cc..92761a78b 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -182,13 +182,15 @@ export function tokenizeCustomMarkdown(text) { } if(lineText.includes('{') && lineText.includes('}')) { - const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gm; + const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gmd; let match; + while ((match = injectionRegex.exec(lineText)) !== null) { + console.log(match.indices[1][0]); tokens.push({ line : lineNumber, - from : match.index, - to : match.index + match[1].length, + from : match.indices[1][0], + to : match.indices[1][0] + match[1].length, type : customTags.injection, }); } From fac3ef15fae817928f517a9dcec595439851d40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 24 Apr 2026 12:42:14 +0200 Subject: [PATCH 15/77] format --- client/components/codeEditor/codeEditor.jsx | 34 ++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index d677dc65e..dbe042d73 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -51,9 +51,9 @@ function getUrl(node, doc) { const cursor = node.node.cursor(); - if (cursor.firstChild()) { + if(cursor.firstChild()) { do { - if (cursor.name === "URL") { + if(cursor.name === 'URL') { url = doc.sliceString(cursor.from, cursor.to); break; } @@ -92,24 +92,24 @@ const createHighlightPlugin = (renderer, tab)=>{ let snippetCount = 0; const tree = syntaxTree(view.state); -tree.iterate({ - enter: (node) => { - if (node.name === "Image") { - const url = getUrl(node, view.state.doc); + tree.iterate({ + enter : (node)=>{ + if(node.name === 'Image') { + const url = getUrl(node, view.state.doc); - if (!url) return; + if(!url) return; - decos.push( - Decoration.mark({ - class: "cm-image", - attributes: { - "style": `--preview-img:url(${url});` + decos.push( + Decoration.mark({ + class : 'cm-image', + attributes : { + 'style' : `--preview-img:url(${url});` + } + }).range(node.from, node.to) + ); + } } - }).range(node.from, node.to) - ); - } - } -}); + }); tokens.forEach((tok)=>{ From 8872adfd95ed1767dc369f35f7b8aa831f9835ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 24 Apr 2026 13:18:18 +0200 Subject: [PATCH 16/77] make sure tall images fit --- client/components/codeEditor/codeEditor.less | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index 3594742fd..074d1e265 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -162,8 +162,6 @@ &::before, &::after { content:""; - width:100px; - height:100px; position:absolute; bottom:0; left:0; @@ -176,14 +174,20 @@ } &::before{ + width:200px; + height:200px; background-color: #fff; border-radius:10px; border:3px solid grey; } &::after { + width:190px; + height:190px; + //padding of 5px + 3px border: 8px + translate:8px calc(100% + 8px); background-image: var(--preview-img); - background-size:80%; + background-size:contain; background-repeat:no-repeat; background-position:center; filter:drop-shadow(0 0 5px #0008); From ce2406992a0aafc9970feb866c34010f51c222ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 25 Apr 2026 18:22:44 +0200 Subject: [PATCH 17/77] fix save on load --- client/homebrew/pages/editPage/editPage.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index b41f1bc74..f9afeea96 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -121,7 +121,15 @@ const EditPage = (props)=>{ if(autoSaveEnabled) trySave(false, hasChange); }, [currentBrew]); + const didMount = useRef(false); + useEffect(()=>{ + console.log('savegoogle has changed'); + if (!didMount.current) { + didMount.current = true; + return; + } + trySave(true); }, [saveGoogle]); From 59fc123f7d4b9cd14e3aec8e23eba39d318047e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 25 Apr 2026 18:25:41 +0200 Subject: [PATCH 18/77] remove log --- client/homebrew/pages/editPage/editPage.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index f9afeea96..0dad57855 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -124,7 +124,6 @@ const EditPage = (props)=>{ const didMount = useRef(false); useEffect(()=>{ - console.log('savegoogle has changed'); if (!didMount.current) { didMount.current = true; return; From adfb9a2128bcabcdee0ca90e91276bac54ecc8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 25 Apr 2026 18:26:28 +0200 Subject: [PATCH 19/77] remove log on themes --- client/components/codeEditor/codeEditor.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 5f46fc3e4..87e76e2e7 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -38,8 +38,6 @@ const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const themeCompartment = new Compartment(); const highlightCompartment = new Compartment(); -console.log(themes); - import { generalKeymap, markdownKeymap } from './customKeyMaps.js'; import foldOnPages from './customFolding.js'; import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './customHighlight.js'; From 00efbb7244470ea06deae2bf16638b17e1ba476a Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Sun, 26 Apr 2026 21:09:18 -0500 Subject: [PATCH 20/77] fix state variable `themeSelector` --- client/homebrew/editor/snippetbar/snippetbar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 07a20fa08..597a01045 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -170,7 +170,7 @@ const Snippetbar = createReactClass({ this.props.updateEditorTheme(e.target.value); this.setState({ - showThemeSelector : false, + themeSelector : false, }); }, From a2e5688edeb3d44300bfe4cc2c8d927c529f04fe Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Tue, 28 Apr 2026 22:21:35 -0500 Subject: [PATCH 21/77] Add lazy loading to images --- shared/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/markdown.js b/shared/markdown.js index d2a108e01..05a564254 100644 --- a/shared/markdown.js +++ b/shared/markdown.js @@ -83,7 +83,7 @@ renderer.image = function (token) { if(href === null) return text; - let out = `${text} Date: Tue, 28 Apr 2026 22:51:27 -0500 Subject: [PATCH 22/77] Update mustache-syntax.test.js --- tests/markdown/mustache-syntax.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js index 95ca2f58d..378263e58 100644 --- a/tests/markdown/mustache-syntax.test.js +++ b/tests/markdown/mustache-syntax.test.js @@ -324,7 +324,7 @@ describe('Injection: When an injection tag follows an element', ()=>{ it('Renders an image element with injected style', function() { const source = '![alt text](https://i.imgur.com/hMna6G0.png){position:absolute}'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

alt text

'); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

alt text

'); }); it('Renders an element modified by only the first of two consecutive injections', function() { @@ -343,19 +343,19 @@ describe('Injection: When an injection tag follows an element', ()=>{ it('Renders an image with added attributes', function() { const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png) {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); }); it('Renders an image with "=" in the url, and added attributes', function() { const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png?auth=12345&height=1024) {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); }); it('Renders an image and added attributes with "=" in the value, ', function() { const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png) {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); }); }); From d04f401c90f944fd9ba561ee8e78946e2ff9effd Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Wed, 29 Apr 2026 23:36:58 -0500 Subject: [PATCH 23/77] Fix lazy loading Print issues When initiating the print dialog, it first grabs all img elements with `loading="lazy"` attribute, flips that to `eager`, and then waits for every image to load before resolving a promise and opening the Print dialog. --- shared/helpers.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index d2a9c8b73..e8b5596b6 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -105,11 +105,25 @@ const splitTextStyleAndMetadata = (brew)=>{ if(typeof brew.tags === 'string') brew.tags = brew.tags ? [brew.tags] : []; }; -const printCurrentBrew = ()=>{ +const printCurrentBrew = async ()=>{ if(window.typeof !== 'undefined') { + const iframeDoc = window.frames['BrewRenderer'].contentDocument; + + // get all img elements with lazy loading (currently only elements generated through MarkedJS) + const lazyImages = [...iframeDoc.querySelectorAll('img[loading="lazy"]')]; + lazyImages.forEach((img)=>{ img.loading = 'eager'; }); + + // waits for images to load before resolving promise and opening print dialog + await Promise.all( + lazyImages + .filter((img)=>!img.complete) + .map((img)=>new Promise((resolve)=>{ img.onload = resolve; img.onerror = resolve; })) + ); + window.frames['BrewRenderer'].contentWindow.print(); + //Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear - const node = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0); + const node = iframeDoc.getElementsByClassName('brewRenderer').item(0); node.style.display='none'; node.offsetHeight; // accessing this is enough to trigger a reflow node.style.display=''; From 9af2577c6e9282069774bff200955273575a530a Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Thu, 30 Apr 2026 21:05:08 -0500 Subject: [PATCH 24/77] Add print cycle events and loading msg Since the print cycle now loads all images not-yet-loaded (due to lazy loading), there can be a moment of time where it appears pressing Get PDF is doing nothing, depending on connection speed. To add a "loading" message, a custom event is fired at the start and end of the print cycle (before the print dialog comes up). --- client/homebrew/navbar/print.navitem.jsx | 20 ++++++++++-- shared/helpers.js | 39 ++++++++++++++---------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/client/homebrew/navbar/print.navitem.jsx b/client/homebrew/navbar/print.navitem.jsx index ea262cf03..e669214b3 100644 --- a/client/homebrew/navbar/print.navitem.jsx +++ b/client/homebrew/navbar/print.navitem.jsx @@ -1,9 +1,25 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import Nav from './nav.jsx'; import { printCurrentBrew } from '@shared/helpers.js'; export default function(){ + const [printing, setPrinting] = useState(false); + + // listen for print cycle events to display "loading" message since it can take some time. + useEffect(()=>{ + document.addEventListener('print:startprep', handlePrintStartPrep); + document.addEventListener('print:finishedprep', handlePrintPrepFinished); + return ()=>{ + document.removeEventListener('print:startprep', handlePrintStartPrep); + document.removeEventListener('print:finishedprep', handlePrintPrepFinished); + } + }, []); + + const handlePrintStartPrep = ()=>{ setPrinting(true); }; + + const handlePrintPrepFinished = ()=>{ setPrinting(false); }; + return - get PDF + {printing ? 'loading' : 'get PDF'} ; }; diff --git a/shared/helpers.js b/shared/helpers.js index e8b5596b6..eeb09daf4 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -107,26 +107,33 @@ const splitTextStyleAndMetadata = (brew)=>{ const printCurrentBrew = async ()=>{ if(window.typeof !== 'undefined') { - const iframeDoc = window.frames['BrewRenderer'].contentDocument; + // fire a custom event for the print cycle + document.dispatchEvent(new CustomEvent('print:startprep')); + try { + const iframeDoc = window.frames['BrewRenderer'].contentDocument; - // get all img elements with lazy loading (currently only elements generated through MarkedJS) - const lazyImages = [...iframeDoc.querySelectorAll('img[loading="lazy"]')]; - lazyImages.forEach((img)=>{ img.loading = 'eager'; }); + // get all img elements with lazy loading (currently only elements generated through MarkedJS) + const lazyImages = [...iframeDoc.querySelectorAll('img[loading="lazy"]')]; + lazyImages.forEach((img)=>{ img.loading = 'eager'; }); - // waits for images to load before resolving promise and opening print dialog - await Promise.all( - lazyImages - .filter((img)=>!img.complete) - .map((img)=>new Promise((resolve)=>{ img.onload = resolve; img.onerror = resolve; })) - ); + // waits for images to load before resolving promise and opening print dialog + await Promise.all( + lazyImages + .filter((img)=>!img.complete) + .map((img)=>new Promise((resolve)=>{ img.onload = resolve; img.onerror = resolve; })) + ); - window.frames['BrewRenderer'].contentWindow.print(); + window.frames['BrewRenderer'].contentWindow.print(); - //Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear - const node = iframeDoc.getElementsByClassName('brewRenderer').item(0); - node.style.display='none'; - node.offsetHeight; // accessing this is enough to trigger a reflow - node.style.display=''; + //Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear + const node = iframeDoc.getElementsByClassName('brewRenderer').item(0); + node.style.display='none'; + node.offsetHeight; // accessing this is enough to trigger a reflow + node.style.display=''; + } finally { + // when lazy load images have all been loaded, and the doc re-rendered for print preview, emit 'finished' event. + document.dispatchEvent(new CustomEvent('print:finishedprep')); + } } }; From 9935f54080f78a79cd2fe75c048e0038dc4cdf23 Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Thu, 30 Apr 2026 21:11:33 -0500 Subject: [PATCH 25/77] Update variables.test.js --- tests/markdown/variables.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js index 884553703..ad23c87c1 100644 --- a/tests/markdown/variables.test.js +++ b/tests/markdown/variables.test.js @@ -315,21 +315,21 @@ describe('Normal Links and Images', ()=>{ const source = `![alt text](url)`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent` -

alt text

`.trimReturns()); +

alt text

`.trimReturns()); }); it('Renders normal images with a title', function() { const source = 'An image ![alt text](url "and title")!'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent` -

An image alt text!

`.trimReturns()); +

An image alt text!

`.trimReturns()); }); it('Applies curly injectors to images', function() { const source = `![alt text](url){width:100px}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent` -

alt text

`.trimReturns()); +

alt text

`.trimReturns()); }); it('Renders normal links', function() { @@ -438,25 +438,25 @@ describe('Regression Tests', ()=>{ it('Handle Extra spaces in image alt-text 1', function(){ const source='![ where is my image??](http://i.imgur.com/hMna6G0.png)'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered).toBe('

\"where

'); + expect(rendered).toBe('

\"where

'); }); it('Handle Extra spaces in image alt-text 2', function(){ const source='![where is my image??](http://i.imgur.com/hMna6G0.png)'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered).toBe('

\"where

'); + expect(rendered).toBe('

\"where

'); }); it('Handle Extra spaces in image alt-text 3', function(){ const source='![where is my image?? ](http://i.imgur.com/hMna6G0.png)'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered).toBe('

\"where

'); + expect(rendered).toBe('

\"where

'); }); it('Handle Extra spaces in image alt-text 4', function(){ const source='![where is my image??](http://i.imgur.com/hMna6G0.png){height=20%,width=20%}'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered).toBe('

\"where

'); + expect(rendered).toBe('

\"where

'); }); }); From e34c5f9b3235db484e61c5774b1593a0c58eb1b8 Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Thu, 30 Apr 2026 22:28:04 -0500 Subject: [PATCH 26/77] Add font files, remove google fonts references Adds a `shared/naturalcrit/styles/fonts` directory, and Open Sans font files for the 4 weights we were pulling from Google Fonts. --- client/homebrew/brewRenderer/brewRenderer.jsx | 1 - index.html | 4 -- shared/naturalcrit/styles/core.less | 1 + shared/naturalcrit/styles/fonts/fonts.css | 36 ++++++++++++++++++ .../fonts/open-sans-latin-300-normal.woff2 | Bin 0 -> 18660 bytes .../fonts/open-sans-latin-400-normal.woff2 | Bin 0 -> 18640 bytes .../fonts/open-sans-latin-600-normal.woff2 | Bin 0 -> 18620 bytes .../fonts/open-sans-latin-700-normal.woff2 | Bin 0 -> 18204 bytes 8 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 shared/naturalcrit/styles/fonts/fonts.css create mode 100644 shared/naturalcrit/styles/fonts/open-sans-latin-300-normal.woff2 create mode 100644 shared/naturalcrit/styles/fonts/open-sans-latin-400-normal.woff2 create mode 100644 shared/naturalcrit/styles/fonts/open-sans-latin-600-normal.woff2 create mode 100644 shared/naturalcrit/styles/fonts/open-sans-latin-700-normal.woff2 diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 202c1a375..26834c84e 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -29,7 +29,6 @@ const TOOLBAR_STATE_KEY = 'HB_renderer_toolbarState'; const INITIAL_CONTENT = dedent` - diff --git a/index.html b/index.html index fad6fd43a..d6bd3157d 100644 --- a/index.html +++ b/index.html @@ -5,10 +5,6 @@ - diff --git a/shared/naturalcrit/styles/core.less b/shared/naturalcrit/styles/core.less index 3ef75144d..64cd81396 100644 --- a/shared/naturalcrit/styles/core.less +++ b/shared/naturalcrit/styles/core.less @@ -12,6 +12,7 @@ font-family : 'CodeBold'; src : url('./CODE Bold.otf') format('opentype'); } +@import './fonts/fonts.css'; html,body, #reactRoot { height : 100vh; min-height : 100vh; diff --git a/shared/naturalcrit/styles/fonts/fonts.css b/shared/naturalcrit/styles/fonts/fonts.css new file mode 100644 index 000000000..5bb10a3cb --- /dev/null +++ b/shared/naturalcrit/styles/fonts/fonts.css @@ -0,0 +1,36 @@ +/* open-sans-latin-wght-normal */ +@font-face { + font-family : 'Open Sans'; + font-style : normal; + font-weight : 300; + src : url('open-sans-latin-300-normal.woff2') format('woff2'); + font-display : swap; + unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} + +@font-face { + font-family : 'Open Sans'; + font-style : normal; + font-weight : normal; + src : url('open-sans-latin-400-normal.woff2') format('woff2'); + font-display : swap; + unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} + +@font-face { + font-family : 'Open Sans'; + font-style : normal; + font-weight : 600; + src : url('open-sans-latin-600-normal.woff2') format('woff2'); + font-display : swap; + unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} + +@font-face { + font-family : 'Open Sans'; + font-style : normal; + font-weight : bold; + src : url('open-sans-latin-700-normal.woff2') format('woff2'); + font-display : swap; + unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} diff --git a/shared/naturalcrit/styles/fonts/open-sans-latin-300-normal.woff2 b/shared/naturalcrit/styles/fonts/open-sans-latin-300-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d6c8549e82d163a6d2bee504b7400f5bb86fc08d GIT binary patch literal 18660 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 HymTr6e|$ER literal 0 HcmV?d00001 diff --git a/shared/naturalcrit/styles/fonts/open-sans-latin-400-normal.woff2 b/shared/naturalcrit/styles/fonts/open-sans-latin-400-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e2d3fa4ea21557e7ee10e08b42a82dd3de54ea4f GIT binary patch literal 18640 zcmV)2K+L~)Pew8T0RR9107%dP5&!@I0E+|w07z#50RR9100000000000000000000 z0000Qd>fDo9ENBHU_Vn-K~!D3 zcn66e422&Xu6hS>XHN`n2cU0q?RXlA5H=13eBkq<2sRD~;C|uc|NoSvGRC%}+Ybb4 zRk6etlrSf(%u9rMH1xJD+=|lqqH)NJ@DYTlcXo^zUPdM;S zd~lOs@iLngP6vg$+&5Q?sW;wd?N5-T<2GxP`E!gHq=rGZ1d%YG$-CnE9#_~;Fn$=U z6R$8)UlRFd(Z4~>TEP(-`y|&O((QkCuYfTi#1J6{I9f!-7NSC^EgC?d4;$Y{tuYjP z0UQ54>;)^<2C>u@YaxL*Jb2$FN&jqWBbxeHN7Cy6xU24*GtN0@td2O9bw*Z3($>;Y zDcWX;5M^bgj9CbG$1P;cGjA_9#`rNNfwUG*vdLXRmK_-fBngCKa%E?#NSWFS#jXnQ z?{JKDLu|On-G-=NYotzS6Yv)c<`&un(cf;O7HN25olXdmT-M-l-u(U2)~DtZ&cA4@ zbkM2}wRO;W%iX201%KIYK4 z8Gk_1?yVr)sdh&FGhI0|yEeb#5bw*`4FIj=C%!u=e)c&)umN(gOwps$y;6O;GSwBO zQH29MUOb~B=|lnZX!@ z{Y~u;u37qcj5#5ZNKrkHr4|Iu=u9m|M8b{rc%5$}*ZW}Gb;}@wn2Ql3%W?aAS$`SE zrpMvpk@H3Yih>}tJcfgS!WM%VBx{M`DF{KNp-e4MppGq~QDsf{DSy zhmt@}$`-_8ZZoNX%Yw8_?_;LEIj%vc!p+AB4_Ijnji#YgL)DXZ&CN(kp(-ed40O#1 z(z0R?LjeAM0VGE>txE&{B~AHFeoCR30*=BmLud)a7ezA$)VlIkJp-Qsz(ASrGJ!D3 zhA8NEnf-nvyDLsvR!9*pkO zWzFml_7iK0nSGvz^{(5^$km)rQ;y|8*0PxCjO9Rv(wojSryJNlA7 ze$>0Vu8TQFjr7E}4y9Me@{P7@R_0VM=rUSQ=hNBKO_eq>jg%qr3tB?+_R3HyO`*xt zX%($dzm}#;7F12U67S>qsoYaBR!~>!Fxr+W6HCnG2Zy>q$*Pi1#5B+6;oMd#j*1i^ zl#OKvGG_q#WCuX$+5wbsm-j4RS;LX4gm=r<>U-Ia5W(*}fND9?Jv+jQ6T$CnV8+M! zkx;k8X&n&(epp3`D2h+iGJK`Rls_|LRd&>Wn(9<^q=wG_&8T0ID|4y2Ggx+VO9FwMJFSKR{9G z7@*!9h5Jf~F_R7W<4@{sa$N%ovgmq!Yhtyw8HOpx@B$OQ_$tcWR!)Pb)hZ~tK;wfF zAQvA7hMH7wG%XYP?SJYWd&}2dZN%Z3xFQo*-s_7Ly$MeCnWmAcQgD!m`pH4e1Zt^m zCjC&fdP@(Gqoyal2q#WCZ{7dsSplfTbs#jouDipr6TtHyT#%M!;H|19*4Wr^c!N{3 z{)d9T?;yOta7W%7*xZjI4sd`~Y@wOkOhHlL&}`NN|IPXeg2rLLd0g-HejJ~Un0lhV zQO``r_cxYU}=k>O^^>Vv`v)B2}9H>NwM66fkM!4 z7n|llt|gejPy~x_93fqwrp!IlQtxv^#qLiZEbA1tZ~*69=K$(m zlh%8xaxO#5SjeV`(BSPu7IRV;LyiDT%h#+s39QX@1|UlTz{a)_K)`~i2TfAy?1&Fq z@vF}v1gN-@qbC6Qk@N&nl&4>Y2Qzn3;ygW>MoBYEYLoOpm_bG^gA}AZfh4ZV5~&E? zC1*dgHWTC{w-@9yW8Pe9B2{|66m zJ-qpFk5wyHJD(gev!sn(C@b zwW^|ASyo!2C@zv07UawFa&xk?GNlr+NGRZEr1Q9G95#!|pwm*Q6f%jJOb7rG|M|)B zwk!pH!j1LG_Nq#LusNsOJ4e7y;Oq*9EkTc*8H`=4sg3cP4K%oHZC1pUY#d^gYsB(WYg zDPcc<;qO#{$cbP}UJRYImpIEhq>ddsnKm>&l)Nvif5i9zv8h!PK|RIz<7m>^yISGO z2q50o-7kccgt|`~Yz?O(pq1KF!L~w0QTGQZX%63=cn+>~66-fe*paB~UxyPH5bTYGk5=_7rt&!~-7C#)8~P}RQ(%0T!v z#5U{^6CC%|qSPh9`rppAbxz8QxdHcxWA*6h=gC33x$qNp*DWK`@o zd>fv!0~M^u9Y|Y>^9NV}CVNY6hp}D;mk-fvr-2G9Zf&VY@t(XuLv%s znzd^7AgXNN zLg~;Vy-&;K=4p5-U=esx2^u=#+oZ2Dv2hk6bV+oyCWMUGdK*%ngZ1aF_E&2Fmv<4mijZ$)a8cv3x80_SFijE> z@dLg?1hhjs!g}#c9u1Ub??2Y0{n6wW+IVpF2JN!zG-8@D*)4I^qD8Yh6zk4_V;Ex!>KhJ1?r{eEeHq5Z>bNc zIlyWu;-bV<73~8B1WUnIy=Sd9&0Jk-`<5 zrVeE-2gz%8K%=YHB*2)%NH^(Z#4>NFXc8$i+N1%FU z^B1c(+}5=?6akuE%e+Tu4hA}5X@3So;t|)mYC$^+?Qnu6MesryX3obE7QL|uH~~4E zS_76HDm*UNFWe=q*nQqHhwc-pkf4rnz#1A*)< zqo_&xnRMuecD}3%i}?lzvt>2UJv`$LTC+_4pqaYvku&6cbTu-1kmY(rOY5Cjjv|hN zYiA6bL*1b?JQ8YF^7Tc3;B2&SA(+^JvNo$WESQ32s(NuZ%X^dn<~XQjl*aHc1eqJ( zyAp3`B6LbzW{`aO`B{Sg+g7Ox z&?y3$$=;yd;ilz+dVxm-#3nWr0BrhbM=B23%Elr2&%F4e1U*3mPfl}m;`Pg=0guHZX@#;?9V(eh+;_|dBa=-jb zzFCVp#Fn}z+%jcV(T*cHBOrq}wnn;e47fuf6Fyt*FA;858|d0(pK5AlX!#8i#DmWX zI>Z0ZfolHR_}ZGigGadfTyO~grhlfWT?qe0gF(V6oC%_IJgDiD!{x= zNTu?PfhV?BmBMXno%E475=w7W?|_yJ3gey+0glkPwLct3*1Wo2XTsCmH86-@52dkpLbld!LIeKfW?cq zghE?EuOoPp#qMkd1GG9{c~cQ0zv^pzwMD=TuUV|OORm>g7Pjii1iL1SqA#pq%o!E^ z6L6K3nI~DCWaE%^gTHOPIN9g*$|X{ShD zD9&f!8an_mVxa9hyi1xt@eDXHAmtQxDU}Q(5(v@|Q^JPm$t8pu4i_`}r%ZriGNK3V z)3aEJg>YyD>m_WtWmz03Z9!vE{MsP>m9xZ&IN}hIu4Ih?C6;p@T_G^z$_EnUwGc&V6!5>qvaT<}^9ysk>4lL(2e_U8)JqlOE|Kw^ zDDBYKrKQ+e4zIqpdxq%Y9nvbDu6^qL@R~V+1uNXPr46~$_F?Gtfor_R^$1>GUgi$? zVPrkki#T`CP}j%=r~9;4ohCX5l>nP?FJWhfDJKz&Ki}!EGRPsyqC>tiUxDNZ2t|Y$ zfOGiKcu3gXsKm;a+;mkGELB%7fGb5lbHTJdw@^F42ztZ;j431+v_5O5BKQBh0|sp-1*2Rlt6jP6_Cj$A z?YBp_81v@;S1F@ln-K>`ceQI$%rBrkqnj`z@?quH z-23AuQUdtr?IKFN#h_l;=e)e>45Fo+Z}IX#9kgpr_dkyVlv*P~_hH6S7(gX z?j3vE6N+vF8>KC7Q;J@qkjD$6ZZ98&fMOR{kz`>&jyXGomrOH%z@~1E*U=yaOYOes z5l@9a$*kYHg3@=oGjr)}j}@^}fI z^sg%)5@r3BIEy;V*A6iMXZi$?(cKsMx6=uoMgo=Wxbvu7`r|OEiQ2O#h??h~QTxg$ z>3JnBxmwZ(@-*=@sAqk=34aD>p6@6l^Qg($Kdz;1YB)`5cuk8k^}5x1ICO|&*C)9D zBd$^SEe|SQ+ZR#El2XO|!kSsj+`L6!0`3laX=+X{<<7TSO={lzDwWd?NUnV_GMb1CpNtLB#KHCPAX`}V_SfB1f?dUF%!OE`Q0SI zBilD#JH(+{L%q8_f;mK#CsCZ2p4`GYxQZnB z#J#a($BqiUDaoS102R*!p`2C)4mmWS0nH0fX9+#Onbmx791B@8-uaqblfJAko^Ck4 zEoPsl$dl)+hmz{x!AtQlpgpDk%$`~Dm^o|&o)Av~8)sK#zVxlJm_e`f9UT#H&Dg9y z-w?MIhBV#gZF9F==2>jjkrx&%H%S}sY0*+ZgW8WeB{UD?Aq&s6R97aF`{2w=v zL*4>?O~EA_l0jn`Vp2N(N+=vEubcgicD%9rzTuaA8P6U?9G(s%%jFhzUjv&S47dFZDh zUnOUkF13TL(QjU|T!G^{!(}*|2aDNju-ZujQ;BktkP0OJk=qO(2Q}uzs&S9=%u%U-?FdKon_s4 zgi3B`P)`O$y+-x?QRas__TdhUvsH>E)|l~326V$^9^hiB1p z5zj~W&uvxG!O)$R%3W;uL_8tT!gAq)hfyg#FM8^!{EDqvAqnr3&4LQatW7;lDeply zE^3d35f>7^7I$^;xf-rX5Zt74>(71L=Q~6}V|=jqV|YSj0IDuof;bBzp~qz^h~j+^ zWX7|#3!O45{Uefao;P|%|5P-=;}GJE5J%3Qw>LKe8PMF`Q7v7oCN!!B<21;+s7oGr z$`eCVJx{C>JU)e(SsX$n?6gk&DLu%Y(U7|xUmQC-RhP(()F_HS)p*}Tacvmf;5N*a`N00u3WIQ?5C^ip2k$)_jt?2@TpeL_n+KN*s2I%+ia@Q{p)YruJi6}`M)s<{z~2MNexzyPuLa_ zezD>w`MDzyAnrUD$fV5n4!Pabp`VrXau@s0$#PT=955_Vpx?2*3_$ONE#n7V$FNAN zd@L*I$&)aoTRTtZB&~3mH2eo3xo=C2hfCU2F0*8I{sv`7yEV(BRjcMx%mZ!n!I%C} zJh+83(8C~#7}M~$$7u?qHY8}zxk!!rY63==zK-4`mDdd@z7Mvd5 z)KM1Ytxg7Y*Y-=hpC1d1U?MS znFxXspah5_uw$+{g3|$iT(C*)>*QoNCG`gVv7P@kQhdAyHzHBwQzy+|L?4v=7vfAs zn?L$o-+qloz{W>L;Nl5*95Nyji*$8bU4!h%2k+PoE>v|$#5SQ=!1^fmLd!BcjJcqv zEo`j^VOXx3kkr+-i9{0J*Q1RFcAgDGvtV(Ss{3V|?)ss39l?Q~gvmA?YXZ-TIBl7^ z62k(s!g8*9reO0z02vh7j`bXY(6_GYp(|W|V_xs0rE`wYi>tR)ifFN+$@x(kZdG?$ z$XjwkB1n-5duZ7)0q(!v9N72W^XI0Y_8W$N;tMc-H=trtCa0z-GfXyfW@?f#6=leb zSY7L_Jq1yw{eJqat#RJH{s>wHj1+kiD^NN0PQ$9xs^ zXWU`lO4fMR1nIXO|t)Pm~OL|e|u#oF=iZP_950f)BP8H zW^Iz~m@oQ&=(jfI5wP*FNPIj2k4Hwrut?hw)7oy!wXxkUYuhkOua=_Sl;_v88%G}= zl`+2iO2l}uGC%%kDUSiW`^~My9qrAfrR{p;j*aVy_HC_7Wm_x4-r>#>y95B662RXJ zO3eDSl8*3p7r!$d&7Jb1{+SeWc5Sb=GXHQg8_iP~S}DW9=`9z91#^|<{43)Zisgw2 ztFPUV52cxuo|X*$PU|_B`Q_ev)x}?m&z4^Trw0vZ?0FQCeeLLhc5!C;u^4S{cb|G3 zpH|0J^i|f=>Ol&4IjfYO_$j^j?VFn*!gEU26Do2>oA zdD;tgAiu~u(~IoNvuy*7Pk%0-F1zTD@jpNez7v_LFcSoZ;@+hL=aGF*f3srXb5Djy z;A331gP*cQ2mYJ;ba3>&;9y9ewMP$Cv@QI^!^1-7t+U(Si5ab7{Dw1mvddkAiWj$4 zJ%!6m-C@!XZ+NYvbBhM!L|=AaDpP~TKwId_Dm-X6MDOi2bf*D=k8uL)Z`NO=7w;=g zr|KFrX;5L%Kq1$dU87ohBsT1SDkzz+$>H>O6|%rY|H>RfD_>Ev?-IifoL(|wI5aeF zP?0Fb9bAzgf)8mBU@M$1HmOV{1yrI;LrwuvKowMMKDnS@;fY}->%o8Xo(+zE6dVjL zvJEs)v+To9J~||Hww$$lBVn|K3+hkh$u4&emc6{A?vgJvbw`Nbf6LX6%`F(aCi-*x znG6kP8Bf2giU5+ORWhxe{NyIg(vj}(Y2`-g;h_|xhLGDR`G z%E(z5cnT^b!=I0q>5Hqg_QD9ei$fJMWK;yg=NphK`UH?mXj_lDzqipKxnOzE^J_%q@;R&SZCkFT+J)rRN#phh3YYJYm+s8^T|TwfPk(A4cls97lL_Ru z54kd2aO$~RKs)`~0Y|6N%N$g54L8HVR&3u#XZ0vrYL9;q4gYRCigiERtEtk=7oGj5 z)Gz$v;UO)HSaAf|3W_vESDj039!`GMajeY#W8ZA%tPj(@pYFA=tLjbAv3}-fW`X0< z)hz{6ijCmZ0BjE)WDa%UIDYy2GMR1uxeLHG9)E*AGLKyP;j;Gs)_?806B-)m@LQ<4 z+0X&qGjYGzOXpEGUG>R#7n+1_k-h)@KNaXtkb#e}tA`n1d{|+Ymw7K4bio zWE&hS42I_m0hU{40NnDnjAb*=?z8Lmr}lRwjGg&eEK1!FkDh*b6<_6vW6LgWL-rK! zReS4L%L=j4ZH0mp|J#B-lc zQ;5W#>r(YNg>>R*3YrripZwnFTmb#b3LrY0>-b#_-68m81i3DMD zN$>G)^gpUrq<-hF_k?g|Kg&tm4{x7j-X4(XwUw`U%GIuN*1i@o)x%)8k|Pbzoa*7F zq4x>wi{(jUw`r-y3$;_PZCLwUJi_YRQRDS#lw}84c#HH0q9gaf`I+Stm#$xGo|dG}D_0z-oHd_EYcutoTXM;|Nbl&e10`?)bH%_RFTz&O8U!b~=Z!gYC$5 zO~2)!>{!_8*ZbOy@0ey!OO-ekdRF{ryW9^PI^W)8Xj=X3&r2sy%L&DVm5awb5l?k5 z*S(eX@aZt`rj`?K_}|CEq7M8qboI8oxC4P61=z~~{@3MJKY-;+Kl^Q_wcU`l@!c+K zb`i&`Xl{tA1=UIzv$hb3MQhD{Y;vc9zUV@9^sv{~na+vSg^4=WrgjoDpjQIhv9uAG$hE`=;TE6 z)>~F?Hzh^_u;@JNRCtgyDH6}hpy`W`LfECI^<&wYj@Iu;>rf3hh5w`=}+F#i0s2wTv3wI=_yDeqT$?)dd7*+B>g+?xtM;;T6DRYT*St4CFv zpLQ5p^docj`*8rNreB@v5vaVf^NRV(1;+UpQaf{YKeHrB=AsGGda@n&h%>627(0P) zzkd4VA!DA_sB{}e(4!ET3|t^*cY#@zT?qZ%HUb^& zlS6WdI=_6zWLWb*@7UP76vN9+#w1{ruIw}9qLBD>Y`RIXElaLetCclX)#|e9y6Tb| zb%;xv^SuBI26~%^jHVBu=oVSLh0YseXWMF98mkQgqv_I@s=$CLU0ZUuq*>wkr+8= z3Y#t-d+=0wO}kZB)in;_r9NpB0A4y_$6*Ur*H7KMJ$6N3rYk+zv%Z`&ekDDkB0D~% zs3|>uu@Pq`HoNb$vCc}V@h4~|1rU)ieR1d86ZCpaW{Fl*)O<*BS z9{?n5hPL1zG#O7&RmE#mossyUWu@w$$GVY$xBA+D)IQ5P{CQ)(vaB~Be_q#wKV!@* zpE>Z(As8&!_}K$^Qt_{`v7Or#B24+#PH9~fcwqv-tJ+)+TmQ&U)rBW^l(ln{<~KF@ zbRf=lW~p;qiZGWNE)0Bx#J>CLvA;zQZd(XtDyRAS&p^-Pihs{)+nUC6LrK=|!#V)3 z`b=Ejk%n5>r1g8)^L%-q>8tD4O|KzA&H~@$t*g#^&1fqxOnDSH6V}e5P7vF7Ubj++ zv+EK(;OIU1vhEAXEy@YMw|g-@$D^g)^?Z6r1Wt|0hsykiXsM+ei(4aMvfY-j>5-W zF+bETc{&qaFpHZnXs)@^9Esl%tYTg}l3W;1Z&sgl{79S~5&W$vch1FI^X&1CG#A>D z>($E_s=nWq=O)2h!(6gS7AglJgvx|+0FnIDdC*tsKTtyCA_+ov`H2i!@-5f@-;XkpPmqhsD)l{Y=i)(IjNKCl^BW7JGywXs}Lw zKUc+)5fvU@4<;3xKZ0&N$x7tv>bIv*F({;r{DBd?gf2%)~Ri&x# z1|6zD^qTEojKXl-hXAuSgxDxG|LxWKtFYSaxpe_#?S4{#%AvKBpWde5tcGGxN~mMI zo}b>_u_H)@MnNl4y0&y)yZ*-tC<;>r{m|B&!E0%E2vF50r4VatiBy_~M5-;LNg53~ zC8d^3%+-@WR^DCk#9CO_B+Mr);X97(K-3`Shivh*usFJh#}5%U9Y1y0cGy{849v;V zV7w_%PBEwYcGtPm5h&R!q8qI~MmHs+(zxDD#M_w#) z<;?O|*`1wViD&0U)BeJ*W|#gq1n8>2s%nL3#7k0+ z;|+i@Sl1oRE$~)*Bv4~Hx?ii4W|sV6Y9G6u)t>!a!4feEDO{|e$Ql>9+#7Q^XCc4N zeyCh$yLvloU>0A}lfvi}2x*-?v`n#q#_SXdn4N|cL20H`&J$$I#W_;BfG3eV;O2qJvb|j=}`x;7cF{K4cDsgIbpQB66 zgC3(Mlg-P&zcq1(NXAl6_bpMh9vf9yP@5l8o|{IYs&I03fm$YEV$tj*6skBV6U%$q z5e@WK`8p$T3>J?~M=0EXr4%HWXdH1-L}H7_ppA==S{uMjtBlrI`&$q;@uKNw0IxP* z($}{()^-UHOO0q>{Jky3fUTRK0{G1(_%BkdA)0kb_S2QqMK3N3^+Ar)UQ(0X2vS+( z`>lFY=)qC&0$8dwZ$S~Q@L&Xn;T^nP&lk1CfW7zZ!o*#WQ_(`(V~)WO6=SpOeI4^l zQ|76qerYEoWiOzxieHi7(#Blvu9J0&m=-9jky~UJ=KK7 zMgjpLo@HuazPw$&LD~@N+d?OxkvWfo<{j=#;Oq@A&CjTz*@%87KCNAiBR6?&RAGJE zcO&Jw94b}44L{$apfW7?RcExQ+gO*$;^yDC((MrEvD7pDkr(2rY#v0~lFiDsgyBhy znJBuKGR;-_8PMkD;;KC(2)_HAiB>;$v7Cg!z;XMc;-mYcQAk(Y`>xr^9032^{qxnG z#h_o;9uA~F3+@m0N~OT=t*Z z^52h#g#`LvZu&ovEmnX0r2(NtD%}c1k}R=6oGEE&?4Ft8Ebx(OyQ;Ghq==jzC8cP8 zMpnA_(!pky!s8L-5NUfEEx%77%`~Q8ZH7OMp#(XtsEvF0Y0kD14JWjb_amu+ALco2 z{+xtI#?d-GL1`~aFgMq|CXOzmQ^0RYz$InV%U`x0Fq}Z}n-zU)oi{In&||+O$K|k7 z*yuP^0O$@4K?+X}%a_KgSS3Q^B%xH=vs*lf6F~CaV#;$f+0oz>HklBT8ZG4p@?z0& z9t)eA1gFLF3`}K8eGny=KVhC|Q;uCHH8!5b6zE*I@Ljq+B}$+_Q?8dQlXm(V_Tuxfj@YZuyODp6ec{)F%BbnYKzq+f+IB9!mCm^$Dzk(SlurFJn z$_wk=NZR*RYjVy_m5A5<1r0cUMs4w~>0s|$wdVEE-ijBGRYppcrzrXlEvT|}gU7Jl zOUV07Hz$s@{-r?K$08iAkP!~{^2%lo-?@T>7Sq#eVZH*MQ*Kx5#nyL*(5H#$2cdf& zpc9`4pKb(D1ofrjOxO%vss8VA^9a2etm9V7$&w}n8FNc(Z}0y#t)}=O4AA(NyroMmQONSo+#?*LgGN~pMB-D!NKul;Y=vik1_ zx_zx1HE~J^rKG?Zd$!Fy@bq3M=3HlHiK0V_In~~cJ=ZB6-_$T@wmTG>1@9|ogn-g~ zI@3$C3Uggmsiiq-X~o&BGFPsokf#`S3o=QYxy^Rla^9yKv)DQQ@?2U|nwwo+nkjCq zv*gv6NyO0@6daCAij0^vzVJkpy(Rc1-&Y7nQ+J1jvcQy30@yd4{3g?`T+uGx!)(?j zg8g9euf3Qr`UU;gELCw1kalCU^8D&fBHWBN`y^L8Q3HjAy$9k z-3ZjXYAWo5UDljkc}u=?HiV;voGH+t$_H92n6UvuFyNmycHEr92zvEc<0m?W?{S-Xs{P2=nowm_#T zTf1qz-CY~8Hi^BjU(>0WNiU-^R^$V z9e1DX`QxjWVB^Po%aM$X@>KHz2{wX?b(nR_kL+Ho`6dD1!mp3F=3AU{}7*0c_0 z`!;NJn{jDx!NJG;{|t)&X9Ie^pw{t={;S}&<*tVAnc*>P2k==#NgD(%R&X+ya$Zgh zWyj~MJ`*6@NVeopqmn0b_1od#ns&GJki3}6$wGlreI*KZ=|pRbv^AbkM2QRJkdhvw z{c-6u`B*vY<1eTPA5xWux+Q()9Di{- z{^C^p=oE7C@34pwOyqnD3o6C7of;gCtMpU^KW84r`qqWOlb(geM-i}56a=P(k>Ke_ zWFQf-sYoI<6%mVIAl%#^Jx>v`i4NAqGP_x-U0OH{M)gbBmF<)rt@5vG%y4n_!;S3! z;>`=wSEC~bK7ak(^zFD>z!{sF=8Oq4*kiL(tTB79&+dWnRuyG=1Y4-0ffLtj$+x~) zlEA4lbvKE~t!*Tsu$Rr5Ys!P3kQk;|u1g8L;BcIObdZ#jh-73+Bn&R!!zJyJpf5FT zx;YPeO4K*O*7_w|<&^W#+h8S{j9AkauI+k0ilrSe>hS;l1bOIp9Phh-EHH%AypWZJwynbO_>J09CGyCYpzzf!)FJ6-XN`?=%<|bun z7vx^MAJh#jK{fg5q?F3Jq=v8An|V4!_FE@ZI{2171O^&IbBS>Oj2zPuA%@|fhUqTmWB1$~Oz3R6h|l_aYryI~?_+wyd9 z>?7Z7f_koacu3@AIVX7sLuD^=;g8>No5yAs4c+6tfUE@!h8;Ju{bPH|!w#qw?1839 z$rFfj5)mDv*c;-AKjnot+JBr!Ehx>=pYc$Z>XbovCxHXKv6&8!}~kD#o;2Cii-9Hv8Q~jp>D;q+j$w&&9$B%ovqpq!bUChu!o>H`swqnDAIV{g1RJz^Ys#5#1M`lEjIFGRdt8}@ZYzDdV+ zsM~-fGujl%*cHPYVwFF|cx1GsSE5@ZMFx&cK?76bn(3IjD~3=JFb;|8Tj?e^)s+2j zkx^$KBIVZca$bkOz4cP#NcN8%*eAqlR<2K!$N5fPG_D#K{TP?q0_U}7b+rSzja;Vf zw4`fYi}-um4F9{D@$V@+y_&F}_rF1Cc?Ex7aRl`o&U?Ji*L=W-c+*KP%s&QD&*8ks z`+Us@e26zD@#*l8SPmr!Juv3YQ=5e)ywRG!`ae|*NRGP-R|$1Vn2DwUPmi;)sOa$0H`&%kXg&9#nc1S z1SZrIF)Et9L_QHhHK;MpRwa`9^nB*HnL44V_w36UJ(aSR)N>;M4W3@wPHw0Z3i|T< z0mv+9sR8O4w(J5rAZ&R7s6wHkj}7ZRjyVh_vUNfP48=+yeiPX`fk7)Zw+%%w4NB|% z<{DE?Nz0B@*y;z#;jL|;H+HpddD@k{A|FR5tc}-_-LuMR4lckQKA=St>r`je#2d+; zD*v2v>ux!||Hn?sbNB@PbIjef^sBv=JLA*;?6ca1yf4#n`Jg4PLkr}Q3sC_r_ZYmE zkD4_J2Q1uWCBe%(&F}!HWw|wI8t7c1_V&+rEgsgw7rBv+smUe1hBo8pQFo)tc$K#q zuMEgpTsUZjyLy`7^vf9@;Iu67RWuEnE7acp`MrXNm08rA8g}(o8~0@W9!go8qaX@w zw*{iVUf%9%qb{0)Jw@6n513DCv+2RrFbM*TUF}?qrG%*yIXL)^?yZ;+irgEA5g2xO z)_Q$7tairc#bMRAXrA+LK*6co2xFZ%V$`h{_xWMs$SSoA>&ym|-d7OVdf`@k8uK6Q z?lTG6qw8)>jPMd#QV00}hz#oej6@7a?3B2w8_|iztOFzr#lUGhU!IM(Iz-9}Q)G7m zAZh2(W6I#YVK|*2JGQ^MC@_lwP0CBhbdP*Y$^4@TMLGCx3uBsXHZqvJ6Z%>_#t=AZ z0UP05AWKazXpO9Kvk_((ZAs}VGrGNQmf44AMY<#d`Q#{OY@exl^xi0it<{2TxJH#Y zb`N%yBw;uNpzqeQR8AsNg;mxz>$Ptk^=9WtW;0OCMJ@{lkxmy`qxET_f2!dIQqu_% zC{onXv~pB2j;Ld>87eEI1iP4~^d+avC5*D{t_>PRnix8Zn|h@{8--BjXk(NnB;Yza zBbTeI>2z&L>gb4ZN_umu{`jwzb@OLQ{4c*a84;3^N^`KmN}<4EG^v!?vLna9$Cc0(sK}4QsW0kHl0j-)i)m2Wm5`Zg zy^XXpalz!fh@$*K_?au)(li>_*H?KTOG=Z(e4Ksf9ir`etpr%@xFM&iy0*1bX)2T; z$4_Tyww7zq?^cPb4_ir+W*eHGD_9~uZt#q9I1a3j9T*-_>DdV)UAQ8s(Kx4-{h;4i*dufZyczE+pcH>fbM#6|4<$C3Bn*9xRs2GOSLjG4o%m(+n+ zFf?v&BSXKkSS&iMj#uqFY^P`w9fzEoAc3XA!x7_pAA%?(tlZyj)1S@dt{WR*Wwx?n zDP|dD9VID&qScvI*D-~>wlR7aQRVE4cFobSv87d|DL>4#-)m8ZK1l%YVS`BT0G6&j zz=}2{cx^1`vG~jeQ^W8qflvzl(tsc2x zIFY<)8In3^o63Rts9~>CdDyyE_OS#I#4Y9-DCJxP%Zx=~>vT-Z+l`}Pfm}(@T&yBN zK0$cLJZ-jS@E&?klp_E!q@W2DEYdL*{ANw}K?^5TfPTup^Q16cEOR|MU~TP8Vdpug zY5+8wtstDP@!v0PXSEnjO!PF%Jf-AdXk7 zEIjYdXqdSvy%F=kwOw26`Wv*5>*x3Yx}NgO;>JoGTy2 zBZ)mV6KdM_>@?HC<304M_0oj`RRGuubD}5-~T&-6^y6ts#V;k84m~C z2gdmK7Et>#f*9M9ZI>}6 z8`NqSRB)olySj#3NJkejw&AHPnvdd{|eaDRU~k>_{sii1S2Q04C4_Yt!6yjmB}#w;@D zPzgfS0>E^-)+BW>6wwRsx-`zH@>Z(9T6E|v?>9NiHF(n*zNV0A-@q_RQOdZ#LBtp# z(LCQSEy8fU;h&P z5C|Ol>0^)PQ(3RbMKmrjfEifp$kHTdxQpVA4m|b;!G0g*t;p5G+KSHY_og=Rz9{aEt)&ahiwimjkU!U_gB1#ybI^BTxReY1IHO&zYyf(Agk8e?d%=)I)N=J z)qfKF5+*(5+Yf>Ls5OKSpw#70+MZ5)^mQG7I2@lfnLkuNWcVVs5fQ!aDPH)L){*&Jx*-SyBINAS{%0)Tu0NZNpK=#Gh# zDD^LkK3rKoe%f31#X7(Utxz1^9$=k*g~#n;5r(5t;6qLBHn|zBeS9>Wj>6^VeRi2+ zS0$r9Q@+eOp7uEck15B|DAlBnT}Ft%yOSe(^-y+Y`OzI<#=G1fClh3d4S`~D!h~|W zOaKW4E*wDUwNvwa{!ZobV*mq9y62ry*WmzI^aWjSbR`JL#d+({!%VVcWwX;zJDx@kA?!kWi8{2E14$%`5B(yp5Q%q7mmV^c#_ zU&4TXf_*_~YEc252x9jsHD=KmpqYzKo+W!lm9P(17Lfa4LfA$#0UBjQTbRa{;j^n4Vj>gh>@b2DKiftkT?%apqJi_-SlxecoloLeS_N%Ct+v8wLIZ_cwyI@t{U zFq_3OG|syRo~lDAUC-rp7!E;Ol#edgwJE=9v;^->JYiOR^Y|;ZUIQ;_a`q#w!;{j+ z{0kAjAU_3VTK8xT3CtJLx^RM1h5e$zC^Fu7T*?+QGI}7h!#Np5(u}MZ`lxyTpf5g9 zKChK)$kXB%YKRc!gn3>vAP4{kC(r#qTe%Sv_}#j-HI}Wn-W~gnX&ybg^UB@V?z81H zz^yzekR`xe0Rj#(34%q-C5dC5Mln#q*UpMN(JLC*?Akm-u;#1f57{4tnNA#1!azqH~|039Fu{Fvgqa4 zOGqPWmE}sCR}`vi<-|1j_=U%z(V*q16$<;h2 z8K_5*0YKey^&1VPFKLzOIoe4^qc=Ir51=(-7hL)xXDO}4gn-h33-u_n0H_CRK5s}>7N z!oGG=Ew7R+Nq89xLjdA9PaGn!ps>?TJHM49K}Iy=21KZ1xMOK&2Iz&#PGpU1R}VXu_@w2oythNp=j{6M^ID>7s5#OxcwyCM^z>N|bJ*diO6g z_cWA9ut`HJmGGq%idig8$(CgAygH|FP!y!Zo{DH&y%5hpN;?>oY;7PS=q|+M@r2S~ zk;SLrX>F_Pd)5w7ET52N5tSbraRfpM0|x9oRsSd#!v?l!13UI4+LCf0(~cu2a_uQN zbKy$h#+?UGUX(iU)=?*&`RJmnZo2D1#g|%7z4YeCpGJT{L4t({6((GSNKvB2h!rPZ z0#Wq`k>7-*2eh8QZ<{|xg_uvlrLNe-&>(qyyEu-JO5!V=SCD<+s1 zR>T^!!Uj_vcRxf}VuP=~_-2L8nw_>qnsl`?oRO)C(FJFnbKWaiuDIxu2H8HCeBqD;Br^b9JzR;gN*5k?tlkLAV~ZLD!j-q{!1h!|UKbJJ~i z-E+%!J2cwugq?OdX^M@po!H`#!w$p_%ydjG8E<86V{2#c;OOLx2t>%&Id3QD;G9aM zGngzkhs)y&gd(v-Dw8XeDz!$d(;JK?ZJ3trxb=SZk4En1BH-u*p*nKiR%p&wyudCG zb~F+QB}3_gym!P=+p8{7|LnX`{{?C-@1H;idoE9)Y|4bvp=@2Q2_-`LK;xiJyVKD~ zwvIS9(P(-lN|o3xUf{O)zt+|dXCpV1$;*3$tKh4ND{-RgVTL8MA<@L_i$j=oZ0{hO zs2mcrNtAL2b5V}k^XB3W{QDqq(Y0kks`Fb(`#UJZGxK6^g)Z2udmZG9y0=As@bbj` z2Im{*H+$A0%+3sC@RnVyz%96N3lw$+;4N(7E$qe40InM@2nWC|0D=SW z5a=Ng0KDeFI}QW_JskH3mUvZhJ}r4y^?q8qZ>6K6`d@i9!zwnIg;Yw5uzUGGMIKh1 zicpj~@=RBhOesjh@}oS+qAGb&8FC{$a^lI!j^jQyrXw?qDALf81n7u^m~shx!XO5- zpB%ntD;b_I-OB^dMIYDgvvD`zu9mT;b@i3g+rzwYFu%QkI1zV{~KBV+Tq6iK`fUpGmA)ZFX(G~FNUj&_~ zRQO<#3)?DR1OeOgaW;WI41EHGNsqJU8ACk+%5mX~FMKcoM2Fohx)X%`V%Kf~Opf>* zVt3`|gup(v^%%@=-fP(E&BJY4ftEs0d-?Kzt5?%A0gr90^*J=fBW9ENBHU_Vn-K~!1*f+z@qd4 zCI^Wh422&XlX^8{TSm;_JP_i})f_gWs9mI$(8xfraR5-0vC04cEx9oU`k+=l6xj}i zvSe0cR@277!GXtBV#;Yc{MB727vEm0~FLPhLiXOuP0 znCING#u+Pa?9bgfH|GC0_IHba=f?W~{@uO0*SoXG5M<}MQW+*S4i&*+Dv}E1;@B>m zQ=nz(G-eq8|LW3jkO5^G@O(Z(C00mD+PZ+lo@FoKIOgSG*gDy_OkL(K>R1ZC;UZ0n zsONn+PW;w(n5a0Wbr((n8UaK}Iyh{{#r?kT(jBRJP+DKnIo5Y?+%+GLROxj8I?EY>Vdj6yaCd>% zr|433A`>l!K+-gFwzIR{A(FlQ0q(8Uo}XJt6HITa?@tkLDxnZUCX)+T%3YHO(5cPu zTPtl>|EmS=onv*Y0wgS;Tu>@YW$*v@b!Gd$ zgy1u~>=|vw5zZcL)^Wn=%{bMQIZF@dl_BFrES$15QR zN#l@m6>=wF1HlP4#ZXP<<1*FmTr^FNMcYSTeN+8N=jh_}(innZ305;aPteZa?9blu z7^)>g-YKIBi0T_cBx2vNc)_3PF;##TKFSEZX4Hba%aHws{C&gm2NlT@Ji z013bmE=`&%R_w|(x;EXe$Fe(SC4K$!B#15Bz&`kBx=+4KbqBacdI_B>1n>UjvoF5- zMh@irzXDv02NwBz-?v8s;;G`j4M03IYjGVA&q|ov1H|*V2U~z32Vnh100I1Yo&@dZ zr@r{*r-@?_K;YeSAuy_#G6m{DlxIvM=tb0fB@U48bI<-4vIkA zG8N!W6eqAWb0Gi>2_8BG6%ZI+T-dRo2DH5bsFj@FHUX|Idv6=)O8;94_i}3A4Mv2e z%*|J@wWJ4a>XcP31vwKGQ_;YJtK+1EiWIz4X$8_Bk@GemM+7|MROC&}L=1f>gCERz zjmy}L#TbvlXpj0R%gf3A>hJxrxB6v29o~GuZ}-){*ys90%X+5Ahxr-oL*3nNy{D_Y zvR2t*RE<`D9jLBqsrsrci5KHiL1k5%|L1~=tH@$jND&KL=yEHEvMN*=)$W3p zihKk}!2sp__f?RcYl;m)DnPxW*_cJz2Q zWo?$X(?G?PN204!GQJTRgI$WCys3z!6XjL-#H3e^rb`_d$c`*lHBN)nE&-|EsSI&T zInb~8E~PKhvd6~9*ov3&H0~#z@2HzVVbqM%Y~2TVZPo>)kXJrQ6F4^8-Vz6X%RN}29KmHK@#7}8IH(crF3pn`2G zXQDj6x#qdzl1pAwwEF@WzFo=4pGVXDs+=o8otlgQ4Io958flxgFRlw0Kcx0&fKO%* zJ0aK?aIHsRM8clXU>hp+SEXJa2&0ClFbq*5=XoGA_Db{LzHEXbJFQlWw|dnGQE1yW zjx|=OURZ-jDGR`U?e{miLD zr|cd=?Gf&w{^TA>?e)+7sT_~UQ;=7bDBGzns-V~;#%szQlLPG62eLlkHG%6@p8C|y z9jFH=UA_Jr3ZAyLy$M;**qfb)r01)?N{;x;4*O}4$*AUfewR^1HO7LydHaCR|x@Qa?jGr4d%-;V`Mid zMNTo<8_H-91Mt#>71=^k&<#93y z6o?&@mq2OtBfy|H3<6N!#alxaq-{jiz4@aqsJlYn8|I}^@L%AdCWE5rd!VmVM$Z8g zBkkGVr0M)2R0>Kt8sGxltEk3)09FR5y48OB$|0Ac7|Mzt1x%oo1dNSgBwrEwAr;1v zmV4a{1v@3d4*azHBnR^BTNhLbj?O#uWL zI1HDkjB0BPI6_7Jgr4|@Bu7^u7?cZ`xcd{72VA?CB=H;M7>qoTt9d^k76>8-QG%FU zM?_o{adE^Is;nxjVKuE-bz8kb9I?Y88HWFVuhUv6L4@8W`eOk|K@<b zQx~o;>rd<4IPp7{FPE@96^2w!Ei!C(UR0AEVGrK37uRXH`4BGG- z$mWG`6LbB?>kVe-$Y@{J>tHazdMmeJ`p{m#JfW56ZvuS5xUMHStXxb$Zl=SUjj0#T zMd|AGNY!M&LW3H^OQjzD>b+B?Ac#T~!pI@x<`L*V++v@@9p0Fk-o&sYXsJTwhyX>K zD|)Au78)>NT7^Bj!%L=8HJYd0hm@$+pi^Fc$~y%lYPAQESSi1=b?7m&ovXr824Kr0 z57o$w81laEy#I6Dd2!T5s6Gg6Z!*YO=9>vSR9%r~6!a9j(OI>xA4JyS4~p8I-RihdWFQfex*IUl|Ff*WLcwIB{_-|H9z(zr~RHn5k!lTTGZydZ+mJRga60>{{>f)imq zZX8RFCGVTk*}lbv#J1f^1YIe~-mWuB=%b$fq)KQ z+@2lFk4=1Pz?;U_`<-H)CSc7VCfenW8RgT*L zArxzNauXD1spm)B9@s;BLS@vFuv`3!b^jrNkMKRhGPH>EPX_*=Ta8CegBF?W3iU9I2I$ZUm4b zUEYTlSGeh0Syk-;Oy5K78lrw2iv@!v@7m23;pU`q!4J8H6pSwE3jPsyi)f*wP(NBU z22)Aq#(FUG4c)4A7E+DR_Fh>Mvle-#imD-xzzu}ye}&hA ztB&IeahTJI0fy7sumS`l4w;gzH|Yp)0`oEr!9;E4lbS@raB$%ILaQ}?pqDo=Tx6Ac zacPZbO9fTLZyk*SjF5e#{UczlO<&^}_&3d9*`?uqeUqcUE3U^HWp*aOS;6UcN*07W z*Hn)h?pFWuQ1!P_a!&xQdV(#l27uxFD$)v z?nnR!%wroEBK;D?D{tGUapz^XWyi~nrH1eDRhbiL6~fe|-_R|=lnON|ydhFzBshU$ z&j$nF1^K~*M5*vk`UoA;J7Eq!!rNR*48ZE)t^OFh%h85gwJ@&!jtr+VA4Hs{qz5B=WQ^}93Ouh@EVT11LgW;;Fv?h1BT25=4LK~ zGx-_7^8QCk1LI?CB@O#vMpX0uD=ylVKMPnq`%XAnm*`CdOY_7X3zuFTse>+P={(J& zA>%pAWt=1O+FQADe=icV*5Ii6i1~vfkk@Zipm|LSWLs z9Dir?k<-8ke{ZcEMoeEytp=PqC*Jp((<762RMATTIDjD80WVD6!p&_+TQzr*=s{?a zk2j@>KHIsQo*H*R>ej6_KJ8g}>|7iK*hY;tA3Oj^yrE@#{#+f>0k&KropwAjN~j_Q z9DrB8cKn2LP7e_>xKB%kcFh9h%~Z45{V@-pPK^EmKnCayI4NCw2!?AbR;eNI2-+}( zUk9+vIiR0jMKlKtjW@3yf&sM@9jrdqr-dp|2b7>Fp~_FEQbqX+D3<6gDBZtti1Ab~ z1bObeXwZQXN?dC*zviDf19HfUF;2OQ0=dkoY>D^$iLJPqXiqmjFcb4_Vrn~=6Ujfs z8t1s0e0k-%^IEPdb7Dv2&iVJ^Qy=1aiX1PQx@wn!xH-W?)RSZ!s>~pE_7GxB(%hJLC9%F6_YhSEPA(JI>sTqzy-l zaUn)L+s+_I1HW7I>QT_>hrPOJPzIR_waJ6;zTRskZG2wUKo`P%0r7-n&Li?Bqc-!p zk&y^^;26*H2LdTSR_2aBd8+V&MXr{_;>H%ZRdm0Y9k{d#84p~63RHmW61roepU^Q> z(Tc!!5hjC72ZeL2%<(lTn$99;DQO&qSF@A^9T%MFhD{I16Qyw7XWIN z$|StfQ!Xw`Rl6i(Qxy2^cOgr8sPX3?m}F|v%i_eNK;eAk_X3DesDo|;ouazEwvX(L zb{28Mnk0!5zeQI;YxPn9=b!rWD+mb{9{*Sxf86t!EH5BT+}b zC~me4o(nk;zB2Qmna#_Y`>~YhX?~S5GG~F;|A8eggJZbWW=)q{#I?o@B)b zGy7>Su`|J;ady`6r=r3tD9KG5|5*S3M4oe*m7O_>Ddl7zb?gCi;zeSW?v^o8JMsJ+ zvp_QvAa+TPER-x&wX&pESM>6#UJ{-jTV$uYS`m5`cYjwl=Zq&>5=wn73U#77!U?S` zB_%A&IqF=X+YmQd=t7TW6wpJ{d5if_wl}YOZg8v+BSQ@Dv(LN0Fs6w-l4Wz}&b4Aw z|K6+9do39Q{qPonOmJO`x;aNK@0Kt*p+IXLs$ug&4+Q@$!BN#_8L;A>)N{yDIm#0@ zF_+`_1_AWQz@qB*317;%+fWVOIgQ-?dEw>=Tz_V3Xf(|UFm*B3N*zvoA+X^@1V0%C zAPU>oi)No!q(H@ncR~4XzBW&bA{Vgg%mFwS&7Qy)tbS1sm(_x244Q;0rj*%PXo^&k z{i!W!VQE+1B4k&J99@l*AR(DgK9_IuXdi({y2*twa*X%Tr{=2St&J{lzS1GzSigdE&p%@m>k$gvvYRQ^SGIU0$S#ciGDN|{S60|kJK7Pv zu~En6+fVB(`ood8FsSb?7MUgdGp|>(b4QKpWSIW4?8^2?({HU{Rc7Wxu*S|BHa|tS39Yz=k7Nl}p@=j!QOf-%hm;v73uw_;#81Ndipvq=e**Vp}^pH0zA_dTO#S+9=5K7RGB`*l) zQIHc(auz!!D83~KeU|Bca@*Kg8bXXo47|>r*QiBz<-J#`!1OoOhqqwB*a9&d0v{|H)8d}->O$o1t--SLuvIkr? z218YvfMRt->FRqW#MlvCGa4$;(i6BE@pi{MJ%eOQBSUzzOW6Y}mdjw_g~ ztH3$7xz%CIK8!|FliEo{fnyW8K$Zk-69Ur1h|G^ zo*Xi>6g*Lu3XT;#zG;Aza;WJiHl(AgM-p&0*;(BK4K8p^tp{M6*)SrwWBRS ziVynwKac&<6<7J<=R08mPzIGjVTmICFBu@CjSvThPw_@-uxtH%sORhNnT1b&#K#x^ zw9ThCu|DQsB>NZ4Iyno0eAm#p<3(p;eSN~PbXpkR$CruM)?SgB@7im&OJsKP-KhJ# zNOCt+8?mGH&xQ8?8`3{TdDR)w^DtBzF{cjA&FP=Cp-C7R`DOU8@w4DRPx-v7MWE{z zL5MpPlV_gEko*>hg=EGazCh5n946i|P~MvXK4BGRN<2z7>@y8g$Pk;Kib|^&wf}kQ z3iNho>`o5F*D9HAi_?{uqIrk06$zR6Urhm2K{MtJ2BgW=o>zU)E*gXj~BS)pg z2&x0tuU9+blTlInHiK`0v~6#>v4=Ed5x?&dH%}%>#&=a@MLF*#rJ7}71^l-85D?$7 zgF&`oqZ3>5vlF_}jZl*h1OX|;51Vm1$x|RC<2garU|w*_p$?-Mzba%x;gzOSE!RFD z_H&maIFic=`BnNDZ)G>fVL$Im{lxq=T}zR^KHR_E*`%l#$$Da8CDPAV+43dKyhNI2 zcv}E}?RGY4G^8u|QsPUucm(oRL7I7qzLlnR%UnyxTu1X?%9(G zHDeFvl4)C8akP8$3*!&xQfQyHjyinHER2oK6gz!8w(OV>701PiiuM1(uD)w)(jLSC zvn}u|1|wCu9kYx-w7eeA7E05Fjr{?wtQ2utd~7c{ETFXKe0|u2Q%1ujUfOhS9_Pa3 zx%|wZUw1C(U{FbXYHNLTq~?w(t-0w|sd)9j%+>rg(@4u3(Y7}nW0v;UibH7pQOByX zljYKV=zvsWj5w))T!2h)ekAIr`)q{md-mxJ9RI<8)&I5szB$YD7dB+8Q>nJ#Xh;}@ zxI@w0mfn@91V`O!YrGquf3C5o?D2p3i`lE@1oPWu%Ns6nf>!U4qF709?F70Sc3FUS zTl;>^5}xYLS2N*cl%amG?;RTY5HoI*zQf~uO7u><AMOVUy(UeB<^-6R*g zEq~Y6fHu&-`|G5Bd>CsjW4Bd@M>BHQ{5_mw2)bsEfubVU z2V>8pgLu90#D->l6?xgbK78O3)2mv@3RP2Ps#OM28Z)aU$KP`MRCht5UJjHb3z9mb z`meLD|36w&CJaFH>)dkTPM?3T4hCg5d9O*wV?UoS@8V3GaE#U?j2GegPtA_DguWP# z(wx2sP8-R%?GS0hSP3#2a#m+Kz4^98<_v%90=Vql3sV2Q?~e})qF47ty;sb}mE={` zf1&|-8G4Poq*^o_LB%AIZLk{%DZSDMQf4@uk!=MK`9OFl0OxW1!@6{vIz~blyhSu2g4w}QUH-Fn6 z?V<&q*%*uZdaAN(@5xH`*D=t#@zbs|6;Yqn%{pcJkXJWiy~eA`?+5iBKrMKQ?$t3! zMdm@5(Ck=gcM<*XRTJU5(-*3oq6#ojqB25hmR0MTF3L`RjVd_annvAQ#wQsbs@@ex z+9c2Qv!wpQ{>Q^3T}5JPZ?YlgY*V@9_!fVV)~ZozOne?;_P{rbQ|RCw>Ej>B-a}5d zA{dmD68K)VoZ`Iczc{VgYxZ30pfhaLsC5J-+18EjiScDs5|XS797ogH-3G@$ygjl` zT%XWntjOl}m41NiSlY$C^Dj+CyJmrwOlL!;unU*gtQ8K6Qlp??szK05t%$N+kW_h{JF*X|&g^bj3^aCE zWI*7qXlNXaJ)rg?_U$fb-XJVWGjqZPxTrh`d0?;c%wGh%Z`Nq^3I+silc3zwgScB|Xt6HLR*< z00H}+8lOXTJ_Ov;I+}BSe^JHiu@mJdO%pT^ZitKiJGWNzdpkM6?=>q5JbqD%^yZd^ z`Ho2n{f0}J(NWFTAJ<{PUuD(O)8Gbamz$~$e4EnV*L?y?mX$ugrWy>z*Cy|t5 zTj;61MU$XO__6%e!_vz2rKQRCxk?XzIGb zww-qrxX8B=IpEM#wPO$d%!6?^Dnz++xQGtGzq>7!h=rtMCr;nKy*V!7O5^tw&W#6s zF37YJg?r$*S>cZ3#g?0lrF805b>qoJC|vOv%Q6D$%sv`sVb_s#{Q8+Ys~_nrUO8d* z^~g*5UYjY6QhNi}PE6@I8a*-uW(qDzVc# z&CQ{>>;q*Zi=4*uA_OcE$9*zyUb159mbrP?LXN{yu^}Gjm7V`bhX1ZwQ7JvdvnMF( zcAf`>@8|aT{2AGyW^qi?}N0NcRe2&(Gz?I#8nHJ%6$09mzi?Iwp zw^NzRm3ZlTg3#&7ookf#oM8kBc{v32higPm0?`Lmn}JTTO0sNbkn%X~ue;9(7A#n1 zq!}`z%PJ%)#@)sIj>t9MoM+lX56;)ajoGo)gXZFjbZ{}9zTx|78GDoa=#;IFuRilW zG$td|5PzG&I12AjwMZpbphzV%OwMfMt*w!=B&jg9B-~}9#9V(!kIDG)qK;$I9H+=L zZ=$@Y`>w}iXyMdB4d71wRQ)V%Zloevi>sflmpc-L(Dr(T(6ZlI!52~7v<;|Nis{+Yb3QAe^OOl z)af<(-$nNlt`qp}$EXyz2Y7}GP3b4;LX;;R=b0E*vFzKv@F5CrPxQ0li*kif6_L6y zk8kg1PmD6!HZ&ZD&I9t}ydoJVdlqTrK=iZaXXa(7s|cZ@T)v&3Juwo#z0kSj$EZkj z5AdW5QRA;Q^$d*(!YZw+($Kt^2v68;?^zOPd`hd>mEx1TJKBr{kJqvFE)Ia6lq+%% zM(6G}JXtXZfqa8%Ya{}Jq_WF7Q(NVm*(b_S3E|h4trsIl9SOG{}dnJBn;L! zImOo>lkDr0Oh#28F&&>6l-&Ww-}-27(n zs&LE{Ydo!xXIY}_S90wy=-_S-QO|DGM8IVo-+-^>J_r5(lK2sF9g^ z(A)yFzng<~Y zPmIUCs;`LN7@dB>tEu^$Gd3Rms-Y(8<;cWFtp0AT3-vXng{Lz0c;$KCL4gUjZX6;x zh|^`C21$k>yvT08&M9!BYu1q#ij=PZ#7I``|Z9n z{W%*sBPJLDOl43-QJ?P>-%Sr|=f*KRx)})xdl{_U;c{#*osUDDSQ;Z1hUhV|3|jiS z>T$C-XLU6x+4^7V`j+j?_}YzUguviPBci3tOIN2D>!6r|+g<*=iXLgMzhinrW(cvi zv;?XxxK-U#EC^-_$T~`q(AbRNg2^UXP=(?916Y%4tAMC5yq`A*o0wgdoD@QKbH;gi zdc>LMGP54Fdg`0pMZ$_hX|WL;TGT;3d0KLaa1u(0XE2tGyHxJdgVN0Lq;x0m9j1^F zP_!*^6A*rzqtet;QEE|y^F*;ZlE9U#1%&i8Af$EZ*Ca*Dl4G$6_m%NZG*Vz09J*LYRua;kAu4~vvgG&aN zv6R_;`^v@EU7*_C!c_L0+&}NTnHQd&?@hwCOKp;c)zu}(OOC?>iaS(yE@ZuWAc8B8 z`UCVtbr%*ANqB;|n2ZkyEGiDb>vO~_1IC2r`ffPX`Ot^ia6;G|S`bZECN(U?3yv%2P*CLRfPpnC;R z(kociazpafsWJ3ua&U0IPk=b`v%Ii+k5c1PrlW*Ma2D4s)z|S|22M%j-_{Itzsaw7dhk&G<3DqoiJ3ZK4hz|c_^dGs{#$!k+2#D?Fy{+$sr+K1 zS93~?S5q;S*IspW!5zWHQIf5Z;;kE7Fn4qAO@lKLgAa;oXD8M{_*^@lD0cO`+4#W- z=WLcFpv~$5LWyB|a7H;hG#g7M-l&D@T7D*#BPD_S3U+X+9}$1W$)Oypo^DlRdyWjV zX{$Q#AoJcO_*P-oM^Lv+N|o#Ge!h^fHYd`6>+(uwi6uMWt*UVh1fz3d3y{CJ#w`?* zY?a@$gC%y%m3PQ(?Gq&@D%!+HKocfM^H%n;L=o;skSY#e)?G!)Yv?s%qwyu<%f!oG zOCn|0XJ*Hn8P8udQ&C4>UQRnm7Bo6tDjj2gS*~pM%G8JhafRx(;mpL&+XdNA4oF_g z4fl_9WQ-kl492}uTz8ijz{7B_@XNd3R966hWJSMTR}B=eCHTcoXNNc1E}d>JJAJ&w zV?xa0#^tj-MvJ8$W8$z0HDO(Ya^rWVizEeaYbroaMhbZ;VYn|dr9T_smW#e$?^61iD|V33Ay%bPLB8N z?Cv=_rKriH?#l<7Ct>hR6DJ!Ql43(d8k;BHFWOmG)e>(-GY+yQAx$h2FByhjI2nCX zQN(i30CFri`|OGa(c=MeC*n9X#C&$VGG0wPP`1&qzz~u&e&4WMzf!9>N6qo2G&^OK z@Ednt1h%o|o2LGIWmfLj-$TbCjD|eYWhdmMQsBuu#T77i->_V-QoA@_r+PENUo3Jb z;4|H^;shSHDANN^5PA3)Dr(Eio>5hMl#K>Pl~kW*o~@$lSwqe1@2tM7VuL@e=Lgd2 z7M-&3?t02)MYhwib2VYonrg*ZdRJpf#AjJWgrOyB%$2TNDqt8?9^AdRR+Gx`eILZ#8wUN|duu>YZB4D6)m@#s!~3>Bm0NfIedE|UyNh;ABVxj1FT z>-Nd3_KF%{y2iM^;Cd=N^`^jDUnVQs#_y2MpR(mqsgDykd))rNg}qM<&AZOFiZqt8 zhfp<(dn%h34owEu>;34()R?)m1VkEgC7ki^WIZD~zv@g-+cJgn$MpG6ryHaLAUoBLCgx@zJ|z!J#>tg90RAE*tI zy02I{;?QQU|2bQ&==i;ad7U5kIzQ$$Fy=e+pS{yTtn*Yb!;FioJF$1bvp`R1eLr*- zhm_d5``xqma>3(Vg3wqI&07yjr1_#f=)Ob>9qoapp>=c;?gWQNQ#2GK8#E3F?F@9Z zw(PezzF1vdLs7CRuTId?F~JTDy?b!~z&}R@M>ikdJG6bIpWxrp+2P+pz++lE+cB+b z@VuKwgdMqiBOV|!O31EKxlqN?ByU<8?=b&s6d)m*QJy8M`hx5lmAM zHb_RGiwl;{pal?ub+uWy`Q5DW>1w{ka(wFyvldR=k=q?HVJa%5xR%Li5MVS?!0)hMYk(n=97k0#7 z;9NcIDwotbHCJuOvm)CS$SRoI;;xqEd9qLkwS9HX_s!>Jn=d%E+|Z)!DIl4WAD99$UlI1n+E zRDuS31WP1Y^aS*Zk)H+d1xo<#&Wn_(Rb>xtz}ic7BjxYXYNrDj!&gJf#IQ^WLMjr3 za`>wDANoOWhI?y0?@qqS2ZFJOo&rLnHB*5;H~wTg8kipXkz~2UFT`FhMAX3tj=&&C zgyZa!_U0ivN@yYP+MgPGW3h`VI@QUjI2Bc=X4$#Q{LJ0nhy$c?<;FD9Hn_M(hXZo+ z!n}$@N^&10<%Y2)Wp%3FI4sT}%B&GpWQ03A5apc=qqVc~=)xgtrJVXqcHRKazrsbF z!@2y3AM;y&!cVd3YgUhcC*T~;>kn5mz#ssjpK83J+Lcoy*Pu>O6BVaAk|S*4I(TH$427 zaXv@QYgv3JLO^D|6ry$JZ97}x+M1igOcbaLQIKkZpl3ouB}F?xf0sml0u50>qDB5| zWD`>lN<3h-+)1Di-qG@8WpdUXK>Uzp?HVlfuEWy!l(FGF7N*g`e))XOh~j8@4l!kM z7)E+|rf3HpS7;)_vY=&|zUIj0nlmqKF#gB)=e2N=aZ*ooxSUOr+cJNLYONu>+EOWUuw|Dk8vRcW@?U$BM0)F zO_u9jo~}|%Kwh!ZxdQTo*7^?XL-v=HM*iDeihb7l4(Xl!bBRX&-8qSro;g9DU3z7L7kL|t!;a~Z%TZzO ziKE$z0tz@*#cKZlD=Fn%PNKEI)2jB^_x0@jfYgZ)TRBi?9gSzW*$Ml-184m-3<+_` z`;Tz_j)f|tmY#*ohdje&eNr!gln-=a^# zR&?1Kn|!pB76a>QwM~P9qnI!&&B^)`z?yY5x?pF7U@WX2(zZjt%)f{%Ot=FLV+kPJ+Cve6Zd;zf@CXZ9;=4R)AQWA zen!$;I1o>U5zdFqZKn{7Qo3^_+8Q=U5>vOgS(-*sAHW}XvQ&=|D#B3h+QlL^q+A_s zxx(gP5QcJWFj}L(Xf?GwE!x)vR~TDXh=*vDjhTLw4B9m>}(57zjU1xD=tCpA-FJ}--H5i8VJXPrWAPJK-r}P=YAyV4l zfzp1^DhnNJT&oNEG*HGVJI~ireK_J4UG2qMP_#*&R7_M?g1eW6fTSIvj*E;VGE=x- zF<400%KiOJTLr5|$8O0}n!Dg=(>N;OSvw+7ZM-AyGKhwq%x@B$s#O-&hizxnr=U}B zZp>rvLrOwXW^h)F=_RwA^&*hbkk0S--O6@D^Q@uUU^TEj!qt;ABm_Oz@eFA|QhZev zCw-74kft=evlc=_k!5OM4UlD7qttAB+bty+nZ_A>;c007`s=y!-FJ%xY~%P{e?t_t{j38hp~FyNhHx->8x( z{bP+;c^t9Zb@ziqx??+2c0Ffn5>nS`2?Xv%a-y@qsxcC^1Id)}Qo-_YaJ|%{6Oo^^ z)nEZ*-Y1Y|C<@zN$GrBiw+R-{6^8qzI^wsR#P8hX{lSPKz~GB=1F%|BFa!f6Z5fMU zzj!_9U{eM3=Nx)pit|aewwXP!wInm#1OKo(hB@)oxT0ygx zk9|KX&2%UQCQBTce}EC;bKR>!SND|@nG*8MbYT{8;7H!f2!$+jei$0>TP|m@{s5on_ zgxS%FK8iRU-edbe_=Wvj-K=cg;gDs4{eA@&sd>14UxH^#Ij!}qM7S(69~smUz91522qVda@c z-Lth1%Q8v)VV*}s!j2_#2}D(?N}SWUC9Ll#DQC7jD_E_Hk>7D>$u*LuYtz?K$A!+$ zZ*MOy==1CAdMn{8)VZ4nf#;cYv0e7LFiuSjpfVHH3jkN6g(}=bRmd#jPpR=n)u)T5 ze&aZ~o9_vwS(?$;LxVBnqm8w;7p08<8}&G+G?_;=?jxE?`HSRkFcN5JF;v-N$IjlU zCG2J8wl#-AZ;F;tj~JNlYz`ug)U4n|)4XZpuVI2mT|GLunqCbGebUB93lO6vN-Q0^ zLoG}=?OP6q;bs%l5^}a!n92LgOCy*tRu36HzhukXFAF{SN8=#5TO0^9v|yLp)M}Sx zAK?QSkrlxV-zV_;ogiNtvL&Xn(;qm1f-AXbx@^2n?w)^6C|zntNk4#6r+?t-;-cB= zO_RK|H_xhE)XL`;zDrD5pFao3YqutZO1{6ybmWo?{~C%%T|R(2z|gh?HR8lB&4h8} zBo56 zn@*#ss=@%Pa#s{abnfv{_mqUoo(?Lr?df0e+lk%gf?#>j1Z+VURaK@+le$cjaCavs z_S&)Rjvc!LW}N0_G8}rkxEL`NFBY}jrYWF6;F%$ay-w9&GPzdl^d-Pim43a}nSVp9(mA*xD1x}WsHex@RZOe6@a1PU+(v4AD)wZ)P z2*Or`XTSWE?A6JR#Ivrvafmsk6KZ1HV1-U`)M0}DL%7$bm|N30`B>sn=D?DA64ily zbgzQ8-S9xTj%*RMQAV^Q)#$v<8bCB|Cw2ll3qKjn$j;8(yCqqMR;{of*+~F*D%>YS z`%?jt2yXY2$FWoy7gq|vm>{i}NWKrZZP{Z9zU^h2Y*Pw>l*0Gwf?9Eoy-Ydmi!_z2 z?fG3FJ5&%Edq7Ircl1N7&lJAf2Fk0s7$f(AkR5@hUfhUteR>jMUrRZS5^nL=waER4 z(261t-fhC(g7;3D3=hd)hf}S0(`Of06`SoOhH*Sj60E-;8^J?EEZt-)4{K10tV*=S|*fGR@Da@ z6hp)L+h*=CMcx6K-C1FyH4;Vn0?m5=pe>)FZyR(D`L)jr#z!G&)4Zy5Hh=)i%5QI& ze~GQ=e<|)%1b*88_}g>efO#&wHlF(L-2vOD9stKT;A4)Ew08#(9UxLLOP&uOSLrLx z>dz~bwmYb<;uViI$8MLly}_8bxbZ=C)}ar>zRg4Nv*OYgDWykD$D9WTwFMhu9~;p| zm|J}MtG2+EHE(Qq(GjD8lbt`$`~?W?(57z70x4PI=={8WrX<%{ZZigws z`kBq>Jt#6dG+I9-f);XM!Q6mqA{4Ydf!V-qv6PnK&)FAe~h#Y(?!nb7};Ox(U(+edz?>&CZ;+IzBVl{f0 z-yTIv=(bx1)L&7bC8{gTIt!(nFuIqOoIR^&iP{FNPsz}45^hcSwcrB%qB+ZGPIk?g!;|p;_)(I>T zeiHG%NKzs}i$r)i7!c_vfenkah({$lj%Gk$R8-kPk>d<|v1f0!^JPgEIXY-3Tck;t zOC&~)p~9n7;>X&yELg0vDk$NM!_MqDyi_`?VQ&Of2m)Tqss`Oll&jw#SJql;BT^oY z5L?8u8ZwID#G!ZyOp_#(uO?_YPbkGx{A5+?|Gxp=Jj9Ro)Q zUqm7$5(t_BjTQEa%^S%+4q5_xer4+bCl6U%1rP~>01$HfSpBP9g(@|>sM7$Y2}X-H z9lPn$qtAdLBX9^v#!R4?GGoqyB`el!*s^2Kfg>l*T)1-M&Vwf}-q3vb@`K?I3nxGz zJOUyTvLF<}LQv6!qGMnR!xAn6Tcjw_IAX+#!worLK&2`tb$P8?lLiNk84P)#J`{io z?Xd%-2sDNgQ0u(Mp$r@{?u*a9>Nn|<3#Rb!wTO3Ff{WH%v+jzk-VnIux*Mh?+Scl> z+wMrR=^u4cBukYpO@>}VnMAT=%atQfzPAb#5-U=o*s?xS5~a#)dFNQ{1gQWsX8q;9 zhaS0S&ODhVe^{_+MU9D21*rk2oOa?T5`1g0imIBrhNhObj;@}*fgv<%tRiQIR2rSZ zWU)D19$z37i4i7|%H#^Ak+F%XnYjgjSU3R!;Sm;c6be{2(zEi`%KQ!o|46zUe_vPJ zMwglWSVDjW(!5fxXX?sLwsa1Q9eBfH8(PW!&N+DFwp|YHE);MLxHovTI5-77eb|Th z_Vo5zqw4;Ig-+Nto4aylv24O^Y)k(&ZTtNq4l>}D;|W6^ZrcT5%RzKbX6 zZ_8_|{`uR{18>aV#Z8FW1@{zYmjhSkSCpK`OZ$i;d`u?_sV1_dVWy6+n0K5Sp81RZfg+KuC0|Qm7-aK zD$Nj62OGtt=sHNZI+1k@NSaAhH9=t$ujoJ}-zh+Dhm$*DafLrL2EOTs$nr%b!p8_2 zZHP|Z23hbjnBr+bg*)c=byB-Ht1L$cYq+I_T+l*GUpLy+w}qRTVqmHk^;AdGbA+4d z!r{icxojjn5ePU2^$d1vM-O)!zzubNk4tF(rthQ+k7|2pyGM18zE-TE>+9Zir|Ad{ zt2nBMRoNn&+r`Lt5k?uyDwve5b?y4ChT*P=a+gIMg|PB0$VB$v{$)$RUjZphd#@`& z`A#0aT^dzJSp`XHElVBHT8_*4XPmbV`pObLsPIn@7Wf95A%<+|Kp_q*hOmLb%d3b@ zzUspVFznhEpU*{n^WZk?y-I{T^9GZ7S=bw34NuP^TT+G-ufBK9ENBHU_Vn-K~!1*f+z@qdDUbqj3 zU^Ji(H_#siqrUV25;|(f*J(uF=IOSv5r^F0~0n1PX!J!e_=T zR9HmqA~POHvO4T~!)V1sNyjl1=u9w*2y7_?f_yQv;p}$tv!a9_%_F^hW)$kclYf_) zg#*5hce8mFZWtZ1S=6ichV5Uduz&J;|2WjJqFR$4qoI*xlb~Ar|1S%==YAqoyhNZ8 zENDQrEEJF;zwzWMru)wH54_v{&mCNmh|q4N`?@A7u8BtIBO&4u&ws;(Bit;bF_ukQ ze4=e*(Xuh8tzlzX+pD>MclRI{D02R!cV$pFlh2e7j44oxvPPz^Q)8;+6EX(P4N2Z6 zdNagd6!A_Xu?(FO$|k3pytX8d8An-Dsr5^p{z+H~|FIzL)ph`TWQU*<{1CCZ5Q#7m zyPw&;a_^_uTLQa9G&Ds|uPBI4Nq0SpfBU$_5el3;V^Ch%B4{nplWTUiWv;@C4um?oUx z!TwHF0ZrCSCcKh}W-LmFV0jmnlRIVAZ)3iVY57rsl?lRkGyl(GcV>5q<$}@y3*tnA z7f9j)K=Mums5=vc1E@>cRfq{79TXCEF?J3iRIXe)mmwCFo62PyD!;3oplTB>{F;`S zf<$N(aH z9iLzBF^sc5S3kz)l%!}(g&$)D6vrlCk(;pv%7Dl-C`N15Mu!_79cxB(u0>Ic-O;^{ z%a?mIOotCpAALGRKl=_{qu_FRdX|KNX{BF$)#;l=5#R4>aH?cVj3+%sq5$fRW?d~% zZ`D=Q0QI(tG96IwDmByt1qZCIZu|+KULWw@U9;oZGLgeTWzTpNS!*~J0vCdHl2q}k zBQmiOA_b#(AaNkj@ja3)s}CQVWc07oS9@Umx2GKewhnD`r~jpY*Zw%wm8{b!M7?-X z-o6kxbGb21(-7NiwGwA`RoCcSh=-mDo_S#C?Z%XwIs1*C!T zX_#EQqu5MCGt@&h!@kqk`a+-T zBi+`H-oh`Q^=e=~lL^3p%eeJGtXJvV)syk0zUgGjSCA zU7huzUf0ulP`By|hR)TAI)r9>Ye#LWwPmZN6UDZ})mFTy=s1#LL z;u2PRC00y@S77;+Te%h+BQRjRM%wZx{X*Y;s+akANb+9Z7}0@T`jjJki1JLI%46LY z=mEO>A-82~uI{1KdLT;|O9#l&Sv*pM>iIs zsZ8b`GxAZjjPuQyUn1AvV0;-`I$bGdPZ?05qco{bL!>O)v>ESWT`(l=FL3FR1;lJV zZ%!aIrR8(C>k*p`I7@wbHR=pa!!ShCyNKF;kuir%YIP<kMCB0}<2 zeJ9&Gr3OyGLxDa>@20nE*S8~ldJZ4ES%X&Jf*7^GY7fw_XHn=~@4)@;A-DJP2T`xJ zX>FQapT0*Gr^lZs6NB}MO$rKxPtWboZW8xLKU*q^(dXnSqc4#5!+TyjONN0+1!pYX zj;+dLuEMl2p2NZG;lUmrbYn3X%~MnJOVEj0vSH^cMAHk4^RP0zGBE|Uxq5X1{`IoS z91Jh4&#!>L^cKS3YjFPZ)UlyyjlmEaMtgX56)34wPudn2BMhA#t+D*A86D~26*zQy z95WWvvF%8e{NcB=lbFgGg86XciqdybQg5G8iH=yl53sD%d&VRXND^uP!HLC%LIOkB z0NNF&`%uI(R|ldr1!KWTUA<_tI{mrYk3d1EUb<`KwAIla;nA3O``nb1#|>StOnP38 z{)9$*@|tw%r0d6%!VxHQhqLF`T92-jA{^EwfD&zSq$VApxH9OY#;+ek&=3+He3@!y!Y%D9-uus@x8J&zlU@2mFHjiyAX6F_hhmNHzW;ZCdF&6ATG)ltVeZ++Ew+N)I$JEUT5Ffw zP-8nml51|o^Gt)O``9`T%B#)E2-%DNxi>$ zk2oAMhI3=kWC^zCOtq?iK6fqRUlKG~&ls_6*v-ny zqJpimVW_%!L9>LKpqPr|YHUuKJmCVNEOQ9M+_wq`z)E8rGbkXIznrg8KsX_S)I7d* zQU)Ai?MM5L{b3zpb|iUU6oC^x#GBrHvg&J{Ypr2sQ7qY zvIq$^8zE>k2=5|cOw}cI{fO%Gy0F`{)Zw2H4vZEESDGUC20{zD$C*S7k5X#kn&l1U zEy_(X^_x^hj*q$rD+HW~>mY(rc6YuZ?3!J-TXcjjMf@y(hk?Z7Cokk=X zg}m%g)qUL=vYLcvj(`?mE}$#b+|gVJ8~q6L6WSTg5f2TPx8al)r@8%-@klRt9fj@&ive#$dEPa9%)G? z1}iwHX|%itt_i^6SCD1+#ly@=Fvd53;FiVbGE9?Pn}XMpOqAVME0>a;0`N%(r`#>xjxOpot@)$Ebco)Q*SQ;&{M{X z*kVH*ATUk&I5~dwK}XJc_cA1(gsoGmaTg2V`Z2=55Y-nln9-Q?wt1!;=?F=f@*&?N zA#IYT6wfdn%_EbNZpT-&GoIX78xL-Fc8Rkw&V6f;D`COXgLpa;+noXFu@oU~rr<{z zBBaGP+ENkHJhv=}x$f;$!rRPEIFn0TT>$*>>4($qHl=55JoxH(m4Z=#?XEfimIDAK zizLS+kd2LH1Ysae`vE5vOP2E)!Z4H_0(9eBTu}`4G^p!rJjn&95u}zc2*&sRME0?g zx%3AMq4(sZga^`n4|%C-{mC6QnhmAYAU*rJ_CNxv4SatDqLtxl?25PB8O+<*zpB4j zpl8vxoUhCo3BYt{Zg!lCh>~RsI=7#Ik>{~BbCOpwAwZMg4mHFEMt2+_Px8J9Z!&b`trifMD1X5M+haBs$(#!U1IB>0qnH3T(skqzWhG^zwj7|=x{dg%&42ZGj#oL3 zxKJ;~Gfha{Qe3Vm4k--}@dRpfO#c{=X2_9oGUZM?cq_>}Lp15w-RUhf{E@5dXS1pa zN@H_Eo0ZXNxj!n@?IZ^7&O%9pRRaR1!Bd$qpp~FL6#}svw6tC-_*a5NMf=CZ9T^Fo z9M49gqI7#J8|ASlOlRrRTL(zc$23SnxjQhc+K5Y|*lzjKlD8KYxSRTP%mXx9P;G`| zm!m|b>XU*%ibzN#k?;WE?AJP62AN_+l;htj7hoc~6lU$*UDQst1ODhP^eQ_BDhTI5 zvbYw;{+-~=1ZFQ*`T(9jfMY;FRExbA4}i|rQD5*l7`jn-zRtS!CMnZ0=Wn~_OX?8m zXbd>X7DboHh$$oh6YoRSJeP!5P6#cIz34E7H#6f?Gz13rUbLR z$GwRUenhsXpy6J{Y=u=bgqC@u`C@J$l(ozhEF?UMK4F*E@-n?jwT=kt{<=YPh51omg z<=}A=1w-L26XsONJB)A{$%YA)zlt@C{UQe4{=&G)Aak5ja;%O?2yAnUQRE0*JEHQy z1BZ&@l~=05nNKzEN`?|rx<@l>KM>hre2}eK@9m5NE&zKAu=fFPQ`+DSBme@q z;A?Z(Qt6W(Hk?p4w!IRf;75{x5V(o7;4hOi9y!$+wzXu(^DhQ+w;?Vv%A5UdE&+TQ zP^W{=gKI~kOd0!O$AS7xK(DM=SZ#>fFR?$oLDGt%OKZMZLhUHVK)$lI;0i}w$hMHK zUt4uiTVS**ouT@5?o{hTQHf-EKE;?yR`Fe^)ic1=+@NE^EMSegVN@@PrRHn_HMTi7 zBd+me9WyfD1je>x4w&wVHEDYee7Mf$DyQqr9M~Y5EB-#I&^dng5iH#=RnB|s);?9!B5Z3K;8=>r z+V;Cpmz_dtzB1~+y}1+Ph0-9xdbUV&)uXN_hEv{n$t{O|Wips3iv~8xWYngb`uUq~ zr!;s6qJjNMdk8-jrRNf89(d05G;yjK!~tjWcc43EPnA`c+lTV|9QyuvPF? z@qrdwma*b}}^ zJ}-h3oAXAFvK_MMc%yuv#)#kNM}}T*YX~M5>Py*j$@vz`(6uN1Bc(b65RKtAD<;NI z7>n+Pud}v6(<2>uDS&NQ?s1Ohx2@t>UA=$EnAP*uuP|9SWm_!Ta#C7}j!PX$H;?Op zFYTm^WfwHul8ahU)wOPhtYZ{fw4V~{R7?_Z%F6pF5++Hnh1q=_@|5-w^5eXakdg(I zwR2G+nddOtlbawz(s1@}r!zd;i4wB4(B{@UH@XTatgEg&1agYMvDB&5;Ip1Cws4Nk zq5&!$I)nfJNJcwx(J&(sw{*OlGVb8AEk@F#dqlTK??MncTu=4`0tBr%lA|P{AvbE= zZ0LGZ*TnQi7N@Sg#Ub>k&y%yvqJd&1F}FM=ggWLiQ86`YaUKhDo-z;UjwqFl(j{>6 z=~2jBKm<0OTXVnp&N@Vl1hHz6H~kU=l{S-0vfg1#Y&Q)fW)-_o4h!Qs`isuCFTuyl`nPt2h$7)E#6Q&c{;h}hv zsGo9`Px}ePQbjJhR8v;V5ni*c2}G&K@;{7YD$wypwKfLUY2l8@GZhCg7eo;9F?$IQ zupsB!*xt$YR=_LUN~Z|%2UMVet%R7hy55wwLV`P{b~AFBN$RMUjOnOYJ^&Sgw%E`p z9gfDSy{jU|^ax*&gZ(KL9bJr4rKHZ~kaIN6_Z`)QF)oyqPsihH`ZJgOS;*riV*zT; zGGk>*O(9UG&5VX;xByC$DYxNVwgt{`318~##+1PJ?F-co0Hi75cL%%FvwPavLHw!& zR7^Mmp9VfR9DB0*vhkBTZCV+h|Se1auX3CO#J0zdHX z`U11U(d}Fa{9D7=)$q!K3nG>e71-1@1q64BsQ|d&h8C43*P1j8+DaNp?rlT-aXzCu0{>=CXumKk2KgraE(YT93UvN zM1DR6gb9m2XeSvXx5#+Av8p#cbkW4`ERT zw2GU0o;F_JfCXrkdc|U6@Kw)qbW-2DOmN&3fDUG~4d-rROQ%S>;VUeD*c_rt&GlU{-!nwZ z5o0$?4#rM${h(j`*nw2Nk||qUyKR6m+X;)BcY;hTKR`dHfZEF4D;D6l?tO%`mc3W* z$J4=+#T+Y~GLk@tt%> zs_U^y1i>jYSb%9Y0?+l+Z!XeM-I8IEwk?0Bi+aJ6Fs{0I9XZ#Q=;`Ba5ll?5;05_T zoO2@=d(1`AkzdS$TgnRB+G^ylm7euuCGD%K$=ngCOy_Hhx z-5WLh3B7)HC9rXQS&t*l^6t|)Pq{&Q$F&bo%so?7ZszeIv9e_-K>_(?&?>dYaAl<- zk7AG_uMXGO5is{+&E%FBG1lHr5jsGY=1&j;@;{G#Z(CP@-ILqo zeuZv)ojOWV{fabsuUk{1>gC>YG^ZxVOqe=0I)LPEkY|5)r8C0rs=JG#3C2+?w%&59 zg)7yFC+`Ee^93PHQ_>gaUGfK=fNcs-279%^rs*=XCk?h#IjSx;T0#x!vCi(KVEWB3 z238SaZZ)U+ZK3;kj&J}Y;7Z;74k!N=y`;mv{npNj0L-*o7 zU4H!XO!zl>qHWdbR;%lw9GC+;^lf^fUEJE#BU)ykOrah)$=5rMb`EN+FNBIi@?JB* zW3Wuypc43bQA2;t;Qfnp?pF@u7GhE(eCN)c?%=&Ou6p3-x6KImHqLC{^YOuh$*+!e zyT0DOJ@#?mb!1qbULV$gM26Ql>ci_&cOQw`Xa0o2d=y^G{fBw$djk@*Z>DO0R$;HA zcj9g7(%@JYe z0WZ}SEd871^$7l?W_3bG+}o;$aE5o_Z*_ra0z^nrhB-s?TyyhF*EZ*T`27o4Lc)0Z zIGFg*oetZxblUHQ@1QFbYn!YhG2+B^w+%qyc%v&*25U8u3t080g?_S~MHc44w@ z;Nab=|9a8?yIEgRQrlj5@8~o$Z>!NlMgN& z00l}X+OEs1&#qY4^2|ziSaT_m6ts{Oayw3#+YpIX(2JIp4<^AxvdOQ=9*R>Yq*UGSpl=xg^G*BAPU?M?JA?&FeHr}To1K)Z=$1mz{QySM)@vw%< zd?J(r?^g3xNNSs>o=Zl?p66BXtd$Y_8w<%WG`z4N&6uriS$&6U*NUZ;DM~fv`HTcK zOOo!p;~zC4mhT)8OqNmzQISM?{LeemSR^TyAfxzTO?W-@C+}otU5y?0yP5A8zCZqo zZJX0ID!I9^;ec9pwSRrhlgGM|6Ir?XGxu4}^C^_n$#C+NgsJVPYv5y4prBqX)y4HL zDtZ=UoLk27nKW1PGSm3er)}xmmblp(f*#C2SQZ=S=I5NU_jD{GKP7nbPmZ)8C>q>SS~_6D~*4R?Y{fw@;Y`*ZK0Buc=&H^ND7=I-hZ0B)D?e- zLJtTAx_BZ|1wrYuvYjH?e|C1NtMcZ9>H*1RNWZ(uV}Tv#xnFpy05qDrt?SbV{piN; z6%+f}zvt~X{11jh)fP{+K_?~f&ynr_RZSmbzCTo(_WT3ZcyW%s`y>u+DQe8krxmaU z?P}N$Wdjh2h8@)!9Ip}pR08T!+>yGa@*|d_!K@Wa3zwFRj6iBYMG7SgxX@@m4@H=+ zvSN%<4#CMWlT#_icWI2xM?YyM0^FsVz3l~T7P~gnXi9Fl)Kgp}h>Kv9g-JnC?2G&k zY;JSJe$8s~?8b^F{#1|>Y9kIg45@qqIn+&`pWwUixcL9pMDc8#G(!4!|G@D?mu%+i zpKS&5iGP-ZaLl}5Y&w{R`C?OFKOpTGKVW?N@H5wn2*N@9kHmcZ@4eZ>`!&)+IPm}V z(tY~#$tyfM7W+d7`x&}+He%aa+V|&MXzcwz4D(1qkm9Ehye&-Fey&daXm}ar;beV# zdX7_7EWn#9ws_W@Q5|O?9C4``sj=P%UbvZ?lmCvC9H(DaKQf(y<2Z0+d69)EBKN3j z)4sT_bm9O6eL+i}W2f2HzDef1>HehpccMqvU7HyeM*}=<>YSH2jM*O~Z3YnK6M!+H zo#!{<#C#*cdvP|?_M4V0@EXwh<}SC$g3d4(SYsL8wp~`ZEtuDv@u$y z@9^>xR{XJr<+1g=ZQ=H5!|Xw`09&69&|?pN{1A0;fRVoY*(mv;sj8(F8W10jzSw4e z)-YBT9D)mcpBZTVHk;VEnC&9kSY7J{V?T2 zEe*#%Rc$9PcW(_mva}%VaaIXAHz*hv7N13F%Af}ZRbz{!*glz|`rxDdk&3G^Wk~5K z4SanlD~SOOP7jSq!PjC0h|-vK6`WPR@8b6MTG7#7cg}P3)Ntz;cJF0KM9{aL361;u zSFBLzamg#n8(dhiW)Q&41lgJsU85CpP|eadfxzGBU!!nkDctMMn!Rvl$=UyzJ0DQ! zB19lu@;p@Tg6uQ@mUwZ@$c5AX8V{+5+C>(FTP$-`y7TK6u>>$PROpuDuA;u?Bun~y zdv`4~`2#s(ixC!_1t&lIeEcXkX;I7~>Y3Xc! ziSu1nKXfMT=FkuPw*=ohXc}Ebj2#7LlvrTgJGP+!pdz!P*G zf|vE)QN*oJS43Gz@=en6MOV1QYJ~Tf2E~BYAlM|`-XSmFF*a5)4hy`kS^c<5H*Xra zVbipHWpmLUL>}TD6TTPLyxFR5?!SkRwc5OIS%06eEqBnhTEyY1+*I#Klg>dGtJ~Z^ z$zU!MuiK5*@mKiXpgay?xW=NB$SHUS;ET6!e%M;%4edJLr?{MGbQT*2&xR=`D{2e3 zUgr0M81`7_skYTSL$zM4h`oQEYEOSAsrK|F6P*)|9XPRe^bGzU^jz^8IExq)Vlm=Q zS(m^Ih(;q&HG~CE>0FvhAZN;0Y?e$eV5-9WJUvobj)UPYiTQ+TFitnkmhN5t0NGoU zt;@|-u@Pg{&PWSM0{uLMLZonHA6Ama~4)1bEZX^OFdY61)&a%uG>lrV*!yIGX;*^ ztC@bViy)aeC9g+*bQBe9Us0tRNaAbW`S`UWnL^i^j_%Ll0UdJY*ZzZ%!A{W4*y zWhDJjNyMO>#AL`w;e#cm;Vb1?Or3h%CGwf(=qs4-v0|%}20cRqXP|GpRNJ#cPa`d4 z!T0dx&JQ`Hno!rTHhAOJ@If(!gXdF1hl4*6<*f_KtMaz){qTl9666Mk&SKRNL0F*H6o2I2coo}dm>BHH-qTG8rH zOV22e1WUsNbIgF1VdRXA02nY>79|N126nSa^*&XPqJ-`rp4u+=<;vEfb2cmou56x^ zXTLrDA@O-45S^LkM}!K6Z{U3{2}OivFurCFJAc|WR&jK~JQ^}5;8ve8A?jYemq%Ts z1JtuR=x#kKxaR_h=d|U6I-qO(jU@|8yp7Pcpe_x+CDZ+xFtX{xX|F!FR=k6+lZdNu zW@dJJ(ObOKo(-<8whl;lqtD7)#t#S&s1VqiQ~=4jFP)X<6hZ2f1aI_j^*O#m>P_w1 zKNesD>^NlJzZVPyzk&B5n$A^Il%g1tDs@Du*Od(P{aawMP?n&7-NFs}m8z>XLF7r}7@v^+H7A8Y%$o z=Ue4GXW_KZIWTgcpecau`wuT$>{P}b-bV36&-b}rZGIX$iCPZn2uPw}l1be``auhb zAeC2mQav905~h!%rc`^1;p@(Yo?*##IJcL8yPuX)<>@z>_YI4_mIHVW6Us?Md;v)) zD4^nra;kecXJ(z)dPQ&@{|&*nE+CDrOkiaB-v^SYD~J~G-+ztZOq(N9XAEwKTcq#z zoKSu|($L^K;ej|iQ+h6f2F`ts^X-D5(nVy^$?Cc4Wm#b&Y;x39nWZnk zsnf$%=5s9A;hS;3(yk``%9VQkW(6=hR_rsUZyM}tYU%_k;^KYs*VNaqSgx<1kOE`l zWlOfy*Dm*Vx&|n@8XFVE;UHsTxyWeF#Ej343VcqK+zAnC$hGIE=--^4`Z2Y$iNEjr z$IorwrVbydd6N<<*-XS-GyENJvhN4;sI}~uI1Q=!(OR=&S2!EWIF=6Ti>5HLLLV8A*@eGtz*crHQIY=O5(t~oioK-;5gW|EL~`=Pjne00|~j#i_V71gZp?S-J`N# zBvy{1E`5GB5*HI3h?Sx8mWIV;`nXm$5a_6+L>7vW!NMda(J?F2SJ9M2Her*L>*pMc z_At4XT)J4|W?EC`a`}p@cEcK%k}H?Au4AT-#r}D@mb7}`xN3Q;Q+)GiO()6#>PhbO z76TEw*6qVaE6@K#J42DBczzdheJhan6I zytS0VW1&Hel!|k+Rh|M*!E9B9?EmGti8F6k$$nH|HGb)9E_7y4NU? zl-z&cbLdodBX3?or_s?_nP|Su{x)Kgm&Vj0{y_ww6&_9(og9EtZ`DR_&T6^Llx*ay z3fJ>yA|Jn=t2&CwVG@X7V9*AU@P1u*bMtdcqU+b>|Db?~r|kC4k7sAFv};hy7$dVT6SjHlJfH^7q>s0Lw}gI8hd#+}@KF6;5=4ww)~ga13b4 z#V9_UBGdjkzd5dsd_o*)9Bsn9jcJ%{ z7@1BxKNez@N3D*OBjQM2-(z6}WFZ{op0)YW?3^~puVcs#PNVsxAa^maoxH*+fnJbI?rurhB={gq4NwM}#?e2<#BywkLi@0{E zQ@O+pn2MQ)i<%)}A-GVq7|Yq%K6%_LT+f1;w}})Q%_*Q|Xr7Mx#yM5UET(hc4hoXt z-^~mD6JVVGbJhu{NO3K-5wQ>MvhY~*de3{i{k!Ul&i|%p?*F^8^8J5r6^1|OKfkw0 zt{;?w9D8dNuU$1V7#SHFJ}`Xm2n`t=hPQ_fkJdxiNqf!Tyc7%Kw{0xqx67pBz(cE< zn^}aI!O^epggp1Sjm@mjM{<2u&HVl>%kxTD;W&x>%}!it zB~03?H{PJE8ilR-z73yGIsg6l;hi$EJ`5#g6zK~(>OoYG2js*OHl3Z)@8eS|NF*WC zgM~R!T1JI(bcQmVzruIpmoMU=!X@GK?9^1~G%cAH#)^lU-*66B>M($G9+a9xJ_u*kE^ub^vB>!S|_=8w&`Broy27}&?+O00L z?y^>w((&XZ6iOPMC?!BXDr(Cebek;VP!%UN+CX2izGe^})QoTYvs*g4r9hg~b{}iM z>1eCNx2`p7znHq7ZrW5kf4{O9rvi{e#2PRo*N9Ge-YZ?bOBF~AAM#MMTF5NyynXP3{-&wZMh!X(hy%=E-~Ryv!`BE%h9Eu?V_4(-dsBdY~@ z$YA}gZ7o{siA$G_Dl?>+MC!d>&j7@~*m@sj9AmgBk(H5|bv=^ahFriLlQK&bfOP8d z%j0lc{GgaC@ppX+PA7IspB9o3c}!(>)S#!=Ksl**oTGUOo@bF33LZ^DI+PYVU*QsU zp|m5fVR2q#^6{b?QNPvDwg=12PXcy|G+v2oT6jt3)PGG|Odn`;jQtaQxQF%hX%8K8 z@K4L^Q5np}AhTwa@Nl3?lcINLc4strgk@_r>t9qg%$o+@*ck?wwU(O^V#GT({2;8Y z@g9jx#ZfMhYu108IMw>lp+?Kk#tmf`s5tUh6Sfnw>$_P!K2bdXY1-o}tSfiW9kGZb zRg%;&gaUjew^X8v0bUa6d3dgli z%ojyre?xKE%ErQ;U};#WG&n>W8fNWm3%zGci_&HhbD^=pRDW_X(I4vHdS-Cp+SNnK zU@C|V&jLX~t!Moh(=T#gILLJ62@8Amtu%-4xMn7drB8sf=G^Fkdq7(q@f>ftxn{rP zVB*}IuffLvsM9*fSDN=G-3ZR+*XKd6IHURbB1{~^kh*Ta=UohLZ*L8}ByTmOuI4lb z%`~8NY04?x&1f3K%!$pQ2a93d`?ogB1VjNT^G>wkwMMxWg{aU$ew$l)7?O|CW~Cx@#c z#iv6SfUFtaw?m}YO28#Iq=+CH?GPb-*D&%Sa+7ymMYGC1HK6{OANq|6 z`+M3~99wV3cP{Zh0W!klsCmf*6c@$t#yYmEL!bvh3ruX*^z^|sL+HCMOSpHMr_0L7 zO!1x!Xpw}iIXt|obQD9aq$D6{grq@Eri@&&zoNwb-_yhW!$uR64~7N*-GbFdT=5Hu z3ervr@FFcY?Z%sPACCRiUZTH{&sI}?QJ1ML{Nk-?9VN+s_Pbp%Su_ptp4~s`V zh)9aTXT*?F(#z<{K5m)xw3Gy9S|*v9l8{1A@$$62Mqx;4?hEjnJXaDIGJ}?`fWXt8 zes4Z4aYA-?mjaf!qqgk)cI&q3ts!*;I@TQGo#9r0118j$f@WvB90p3`0NGt}A zPKZZ)dvR~6)~4Ir#3B3XJILDmgIWywm&+Wos##q9+%gjOa|ry9+~fMfmo|Ka%Q_|q1Cn7=T>Dl^q z>o!Zqai#92=Xo_d(-TSk`a%i}4KMm1;}pZ{+tm91K9*(}s+RYtv(L2p2}K?nMUVS? zqwSq&*2GvOLmE!r_na0R`Oij(|OaDdSP z+WcfR5eeXe+`uJ{dqM^a1W_EF>s6>WHO1~tPdL2k8JAaRDTpQ8=6Akg?86w z>7klh53R4nQtkSwMQ)nA1@Q2lZf&b)6~tUU=HM*~2MXhB$Y zu{VJNo&o|M4eS>AtM}32R0*QG$aEL_>m@k+KDz{8Y`R%e5w5?wKK4^eH$( ztiXoIeI9MmGF)1XoFIH6?o>`Y*tt^ak8OuZDRx1~aZ=)h<^@{oBUTiGcQ0m-d!urL{ScOP2EzLb{T53A5m!3JEvGn9l zw`&hABxA3@$#3h!bc1fx@9Pir*Lu7DPgU+eAqL+wpF0j|~C z$dLW$sP+}%HmnWB9Qh~G$M$Cl-x34;^~tfSMJ=_H z&WYk+kv6S1cr6C|`8`9x!95Kh9gZ_*tmjQq_WnK2dds$2eq)X6*({3`?sYiq!*TL< zAKk6fE)2|1Pv6I){%_0g7ag5Vvcd7uzP;mw-$DH_){2{a+}<~r*W~>DZr={YXx-QM z_fSn7!q%H?uQ5NshMrb+_>ey+*G*d=(ov(ZWB)Eag*;o60?PXuO_RTpp{VsH?^!9PeaMY~!!Fyt?zzoI!lVf37(RC%)Ye9mP9fp_A8rKu^+&(@jwd3wM zjXcAgH_c3=b6N;l3|83*PEk0N*^j$S(%_g5#ueXWbNcxgu z(fKr|rBr+(_W1NT>mMUrL%!$?DHIE`xh?%r#bRWF!gog-&Z+zDPYJJE+)LH{^pm&N ze0lk3M?UMJZU@dH#xZ-0sz|A{tVUUgg5t$^XS=Q_zy_1K6-bo`sdM&_aEVl1d2d7Be{~v?LaJ0lfmhiEj~F@Q3C-fYL`**V^IUFI%N_py`@Qp%t(xOpAo1*MoNZjlo zOv68PB_q(RR*DCzmlxtTOtuu8Q=4V=bCnxI$nB;9{eGRQc-%;pv6Cgr>p7zo$_p&X z@0?$s#9*RbCKZt&Dzb{7j#9qg!bLKhPFj(q4i<%%(1r+FD^j3P8pJva<_)5z9asUt zf$&(hdh90wXP=8M#UY;q*u>>!sH>Pp5N$ZXQw5PQf!PDX#Unv*eq8=Pn6E}Lr<1kq zd=NrKUnelpE0H{Tx?w+pI`~?;@z|3wjnkxKx)ApeabymACkOxtI<;nPpa%bfs-|{* zl7KL-<`ogR2ECbOqykkHkrra2niz|!yzn&2cRn8MZ@;DJv(Gjgu#Dp`-MRM_a!1|> z)B69gsPZ3Z2{kW#Mcs7<$H46<^opwNS6#qZ*Do!qhK~5$-IdC{B-Fu@kFoXDlQo1m z=E@$BPz5m;S9e;E<_o#&CI*4mp(w4fA zwl+Yo7F6GYW>2(PI|wuKDg&(c4UIPP2d?9KV|wSH_r-bwpb-=TfP$he*70t=9*scR zSODD_`_7mAbTLHDH1o2qH-kgQoaqcS6HS$HKJNZ?Ui-^=OKPewAOMf`;8FXUd_O9! zut^Co*qIPb@@C~CEo3?I}@T1jB zlL7hwna0FsZ&};gV!+C6VpbK{Vga!PNnwEPEuZ(Sy#1&q`*ehcNE^85I4Tc=;dfY$Ea2vq&?6mhPLJ(s|&owXE|2SZY1R+c?B=EC@m zAP|W>+Muva3P$0hy?lKSm)*onMAAl2Qoxy=uDS-Ux{h;0Wx^Pp@m^?@^@N#4iGP-S zd+_Gl|G_uxMX07sX0yq%zZ zin(&Ot})fS0?t_vc>@gH>rPE11Rd9Q#tr*sjI?%Pu&aqXAhW#2{>ZB$v1nVC&bK6>d zZ!}5P!4(R^XSVd7G?``fk2x=BJrnJtP=0G=TGgydC zQZHP6f|t*P`k}AOVmUdlTyh3e{nGl;yw7`^uir-;5B99&JqU663m#v+s+X>5lJ|6a zV*A>^uf9+4Sz^`m*Z08aIH)k<)|!@g(___(_5{^^KDq~HiI-Fj<~7-75s}cu+)lDS zsy%J5#>O~;XE#&;`iWt)1`PFE?jA;JeV+IJNzKE@?s!i|45X}KFf0b6io@mGNXgxc^)=f(?-A~npMWaLNJysDX3288>TYl4AXl@5 zJ*oNN7D(Y*&!_1W8P#~NLb8>VtT&4kC_})J08nyWqy1v>R;BSn0Hs#Dr@hfMQO|H< zmR+LdRR|7~Di_Ku7|xF$EQgFTtg;mHBcDA;t^hg;y<=t!_v6TC(CuZhFmo%~W_E?_ z%W~GL*LotYbBP~eHXI*T>Pz@UwQn1!;>8a#KkzRMzUvUot)4eVSoR4yqvW2Y*r*p; z1CsXRMz9l-0;z?Tz2H7?Paq8nR5r3yknqhej_vV-)5*J?lWl0{4C|Sb{jFqT&Bf5} znylhj%B*IS5M zI?%{Y3qfyc{t<1*@$E8DU-VvhcNtJ@ea7!k;*=jBMNsXO(-h{BzB4KA-l-PoJbvqAiZyqxSuSdwe$9Yi zMeG0@N5i$xxfH7v2qUUleynwTR7!}y42cU=qf|(nK15)!A2=gn2fJzcmkiDm-K^6s zTp;Jx9+YxCv6fOH=juTgn%>>(^LxstjdBI?!8C-`Oq7k|XOX)G5TG1?orsz=V@dxP zvl|ltCw_V48vwulz}&jOkM86VcQZo}P=Emc>0f@WLd<-r-tIRp?(+`Yoacw@_YB55 zX~KvQ0q+J7`PD5{TY{_!@PhO+AsvZhIHPGoQ`CQaL7o@jAPQyXMi6x_cO;rX**L=^Nx~^snk6)5rvWn})VIMyjnI zk%;&Pbv1R{giSVV8UnC=4~&<9-hq_I=4!L55F2wBbPIkJtl#;JysuLZE^B+EAMJGS zZBXvQdIDky(U(nHLVk_t&dgwJcR_;wV&x*aux-*^KU2nxug`4h8DO43wiPQkt=~wh z5WZa#sG_21i-xVV+*V2HO1JJATWa}GIXzPLdVD{bRK(e%9I38%v!yLI1`u`B=|D& z^I0z0PHWz-+6S2+uYzF2hwikHJTe-LBgjV^cdVCU*)@xMcYC5S3T5*qW7bi3EY{>F z@-SF`uYZbg@&(L?vZaDFEgjXct&A7RAAuM3&V!RD$m=o;RPewNGgjD`vhc_TUxM)E zSO$)N3J$MH@XICn-BAC{)-L+XV)EHtg7cy8tbJ2m2XJq*yl-7?b*8c}Yo#K+`=qjb zp3uGE$3X!30du3tFyz9axcDp}e{j|~7z8-eruXi4&da|Y#E}SBUyejEbsUL_(RL)x zi}gqb4-+Y&*B-$UFZn?bfYU`Di2#d#9nrfR@u=%O=|!C-y_dOz7idtYRK0u#rD_dU zu27M>6-qg9l^R`Ezsrg&tDp>lFR@0c_)>4r=nO8UAxER>Wo?2EvZ{8!QX&%4LWOz* z&MMW5BGj;EG?bd7Ymg1E3`Ni;WA>!Lo}|`RYLX$7rmsm#<2g+`!A!OK$=rnm$ zR-nX*2^oswKq_jFk&)4@QmZ-T4cQzL6Hr+tYWr8 zr32ui z@#e!9#gD%Lfr11J5h_f$2$7;hx4=si%{0w2>)3+3d-tttcl@ie`2*u%!wFF_S~R-Ub&FXc*N?e#C8=Tg{?`6RMJ)NAH#L8ph8l0@&bvpQtQ@gRcaX)C0RaGe z0eBXGfPfBv+}AIT%4^fQ&2JsQDnSaP>7q3oXw-QXYBtJMT$%1tD&tDH8g<-Pi_j<% zMj?w+kt$S^1_eZiO40`v^yJHEU6GVZQMM~JN_W0^%lzVPxgp#Z{VU{Pu?7jX;&tS0m&Z>&L`{?%iQ%i(v2A4eQ$S>0&e zezTZ?FTY-OaoE-`qs3KRfx!w`+(2bTr`b Date: Thu, 30 Apr 2026 22:28:56 -0500 Subject: [PATCH 27/77] Move Code Bold/Light to fonts directory --- shared/naturalcrit/styles/core.less | 8 -------- .../naturalcrit/styles/{ => fonts}/CODE Bold.otf | Bin .../naturalcrit/styles/{ => fonts}/CODE Light.otf | Bin shared/naturalcrit/styles/fonts/fonts.css | 14 ++++++++++++++ 4 files changed, 14 insertions(+), 8 deletions(-) rename shared/naturalcrit/styles/{ => fonts}/CODE Bold.otf (100%) rename shared/naturalcrit/styles/{ => fonts}/CODE Light.otf (100%) diff --git a/shared/naturalcrit/styles/core.less b/shared/naturalcrit/styles/core.less index 64cd81396..3ff3e37fe 100644 --- a/shared/naturalcrit/styles/core.less +++ b/shared/naturalcrit/styles/core.less @@ -4,14 +4,6 @@ @import './animations.less'; @import './colors.less'; @import './tooltip.less'; -@font-face { - font-family : 'CodeLight'; - src : url('./CODE Light.otf') format('opentype'); -} -@font-face { - font-family : 'CodeBold'; - src : url('./CODE Bold.otf') format('opentype'); -} @import './fonts/fonts.css'; html,body, #reactRoot { height : 100vh; diff --git a/shared/naturalcrit/styles/CODE Bold.otf b/shared/naturalcrit/styles/fonts/CODE Bold.otf similarity index 100% rename from shared/naturalcrit/styles/CODE Bold.otf rename to shared/naturalcrit/styles/fonts/CODE Bold.otf diff --git a/shared/naturalcrit/styles/CODE Light.otf b/shared/naturalcrit/styles/fonts/CODE Light.otf similarity index 100% rename from shared/naturalcrit/styles/CODE Light.otf rename to shared/naturalcrit/styles/fonts/CODE Light.otf diff --git a/shared/naturalcrit/styles/fonts/fonts.css b/shared/naturalcrit/styles/fonts/fonts.css index 5bb10a3cb..355c854b2 100644 --- a/shared/naturalcrit/styles/fonts/fonts.css +++ b/shared/naturalcrit/styles/fonts/fonts.css @@ -34,3 +34,17 @@ font-display : swap; unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } + +@font-face { + font-family : 'CodeLight'; + font-style : normal; + src : url('./CODE Light.otf') format('opentype'); + font-display : block; +} + +@font-face { + font-family : 'CodeBold'; + font-style : normal; + src : url('./CODE Bold.otf') format('opentype'); + font-display : block; +} \ No newline at end of file From 137b6bc690a8809fc819ba9c02d699d104064cf6 Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Fri, 1 May 2026 11:03:52 -0500 Subject: [PATCH 28/77] remove unused 300 & 600 font weights Only 400 and "800" are used in the codebase for Open Sans. And, we've only ever had up to 700 as available weights, so everything that is set to "800" is actually getting weight 700 (which looks better). --- shared/naturalcrit/styles/fonts/fonts.css | 22 ++++-------------- .../fonts/open-sans-latin-300-normal.woff2 | Bin 18660 -> 0 bytes .../fonts/open-sans-latin-600-normal.woff2 | Bin 18620 -> 0 bytes 3 files changed, 5 insertions(+), 17 deletions(-) delete mode 100644 shared/naturalcrit/styles/fonts/open-sans-latin-300-normal.woff2 delete mode 100644 shared/naturalcrit/styles/fonts/open-sans-latin-600-normal.woff2 diff --git a/shared/naturalcrit/styles/fonts/fonts.css b/shared/naturalcrit/styles/fonts/fonts.css index 355c854b2..f7c0c4371 100644 --- a/shared/naturalcrit/styles/fonts/fonts.css +++ b/shared/naturalcrit/styles/fonts/fonts.css @@ -1,12 +1,4 @@ /* open-sans-latin-wght-normal */ -@font-face { - font-family : 'Open Sans'; - font-style : normal; - font-weight : 300; - src : url('open-sans-latin-300-normal.woff2') format('woff2'); - font-display : swap; - unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; -} @font-face { font-family : 'Open Sans'; @@ -17,15 +9,11 @@ unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -@font-face { - font-family : 'Open Sans'; - font-style : normal; - font-weight : 600; - src : url('open-sans-latin-600-normal.woff2') format('woff2'); - font-display : swap; - unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; -} - +/* +* Nowhere is font-weight: 700 actually used with Open Sans, everything is set to 800. +* But, 800 *is* too bold. And since we don't have an 800 font file, it's just using the +* 700 font file and it looks fine. Not sure it's worth changing everything to 700? +*/ @font-face { font-family : 'Open Sans'; font-style : normal; diff --git a/shared/naturalcrit/styles/fonts/open-sans-latin-300-normal.woff2 b/shared/naturalcrit/styles/fonts/open-sans-latin-300-normal.woff2 deleted file mode 100644 index d6c8549e82d163a6d2bee504b7400f5bb86fc08d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18660 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 HymTr6e|$ER diff --git a/shared/naturalcrit/styles/fonts/open-sans-latin-600-normal.woff2 b/shared/naturalcrit/styles/fonts/open-sans-latin-600-normal.woff2 deleted file mode 100644 index f48dda7fc49a8ac138da63129451bcb4af0dd2af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18620 zcmV(_K-9l?Pew8T0RR9107$$55&!@I0E*B607y{)0RR9100000000000000000000 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 Date: Fri, 1 May 2026 15:17:30 -0500 Subject: [PATCH 29/77] Fix font snippet "Open Sans" Previously it was using 'OpenSans' as the font name, when it's actually 'Open Sans'. --- client/homebrew/editor/snippetbar/snippetbar.less | 2 +- themes/V3/Blank/snippets.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 37853ca75..0ee9e1ea3 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -184,7 +184,7 @@ content : 'ABC'; } - &.OpenSans {font-family : 'OpenSans';} + &.OpenSans {font-family : 'Open Sans';} &.CodeBold {font-family : 'CodeBold';} &.CodeLight {font-family : 'CodeLight';} &.ScalySansRemake {font-family : 'ScalySansRemake';} diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js index 4e9ec1afb..f70efed26 100644 --- a/themes/V3/Blank/snippets.js +++ b/themes/V3/Blank/snippets.js @@ -820,7 +820,7 @@ export default [ { name : 'Open Sans', icon : 'font OpenSans', - gen : dedent`{{font-family:OpenSans Dummy Text}}` + gen : dedent`{{font-family:"Open Sans" Dummy Text}}` }, { name : 'Code Bold', From a34a5d187b2d98553f68a6732a2003b754a20252 Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Sat, 2 May 2026 15:36:56 -0500 Subject: [PATCH 30/77] Fix issue with closing brace not highlighting --- client/components/codeEditor/customHighlight.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 8797704cc..86bad59c8 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -182,13 +182,14 @@ export function tokenizeCustomMarkdown(text) { } if(lineText.includes('{') && lineText.includes('}')) { - const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gm; + const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gmd; let match; while ((match = injectionRegex.exec(lineText)) !== null) { + console.log(match[2]); tokens.push({ line : lineNumber, - from : match.index, - to : match.index + match[1].length, + from : match.indices[1][0], + to : match.indices[1][1], type : customTags.injection, }); } From d758fc2d20c90cce2ec92b6a0fcd094350af414d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 3 May 2026 17:53:09 +0200 Subject: [PATCH 31/77] fix scroll to page not working after switching editors --- client/components/codeEditor/codeEditor.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 5f46fc3e4..d74bba01a 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -184,7 +184,7 @@ const CodeEditor = forwardRef( const createExtensions = ({ onChange, language, editorTheme })=>{ const setEventListeners = EditorView.updateListener.of((update)=>{ if(update.docChanged) { - recomputePages(update.state.doc); + tab === 'brewText' && recomputePages(update.state.doc); onChange(update.state.doc.toString()); } if(update.selectionSet) { @@ -250,7 +250,7 @@ const CodeEditor = forwardRef( extensions : createExtensions({ onChange, language, editorTheme }), }); - recomputePages(state.doc); + tab === 'brewText' && recomputePages(state.doc); viewRef.current = new EditorView({ state, From 6faca50a1992cc5b2618914e3302d3ae2aad8e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 3 May 2026 18:00:15 +0200 Subject: [PATCH 32/77] simplify --- client/components/codeEditor/codeEditor.jsx | 5 +- package-lock.json | 243 +++++++++++--------- 2 files changed, 141 insertions(+), 107 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index d74bba01a..f1f26157d 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -156,6 +156,7 @@ const CodeEditor = forwardRef( const pageMap = useRef([]); const recomputePages = (doc)=>{ + if (tab !== 'brewText') return; const pages = [0]; const text = doc.toString(); let offset = 0; @@ -184,7 +185,7 @@ const CodeEditor = forwardRef( const createExtensions = ({ onChange, language, editorTheme })=>{ const setEventListeners = EditorView.updateListener.of((update)=>{ if(update.docChanged) { - tab === 'brewText' && recomputePages(update.state.doc); + recomputePages(update.state.doc); onChange(update.state.doc.toString()); } if(update.selectionSet) { @@ -250,7 +251,7 @@ const CodeEditor = forwardRef( extensions : createExtensions({ onChange, language, editorTheme }), }); - tab === 'brewText' && recomputePages(state.doc); + recomputePages(state.doc); viewRef.current = new EditorView({ state, diff --git a/package-lock.json b/package-lock.json index 86152bef8..2a32536ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.5.0", + "codemirror-5-themes": "^1.5.1", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", @@ -177,9 +177,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -261,9 +261,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -271,7 +271,7 @@ "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", + "@babel/traverse": "^7.29.0", "semver": "^6.3.1" }, "engines": { @@ -489,9 +489,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -549,6 +549,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", @@ -1783,18 +1799,19 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", - "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.3.tgz", + "integrity": "sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.29.0", + "@babel/compat-data": "^7.29.3", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", @@ -4055,9 +4072,9 @@ "license": "MIT" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.8.tgz", - "integrity": "sha512-kpjr2jy2w71w0oqAMI8oibBmiF9lXxWkEQs5gMkW4hVE48bsqINGLxnCSYW62ck/NHXJQpQEfA9WlJ1sY0eqBg==", + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.10.tgz", + "integrity": "sha512-DDb3OAw8ezai9p2i1F7R5wMVmyrMIuT8ixjV56R4Hl4cazCo2tOMTDyPR5rrCUcHiMbWHzCXOdife5OD6H1C8w==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -4697,14 +4714,14 @@ "license": "MIT" }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz", + "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", + "@typescript-eslint/tsconfig-utils": "^8.59.1", + "@typescript-eslint/types": "^8.59.1", "debug": "^4.4.3" }, "engines": { @@ -4719,14 +4736,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz", + "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4737,9 +4754,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz", + "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==", "dev": true, "license": "MIT", "engines": { @@ -4754,9 +4771,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz", + "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==", "dev": true, "license": "MIT", "engines": { @@ -4768,16 +4785,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz", + "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.59.1", + "@typescript-eslint/tsconfig-utils": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -4848,16 +4865,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", - "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz", + "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0" + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4872,13 +4889,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz", + "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.59.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5245,9 +5262,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -5706,9 +5723,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", - "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", + "version": "2.10.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.27.tgz", + "integrity": "sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -5952,9 +5969,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", "funding": [ { "type": "opencollective", @@ -6094,9 +6111,9 @@ } }, "node_modules/codemirror-5-themes": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.5.0.tgz", - "integrity": "sha512-RamWsE/r5euSYF1K3ap8cQKPqBV6cgWr7y75Ce8b4x3A389GKcD4p3b7mMEl2qLdVmmGpMUnTsXarStKZvuLXA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.5.1.tgz", + "integrity": "sha512-vHUyvOYy8yhqCDkps5LwoUFXHXIuKjE32n6EPP5v004fXmkZkrAC9mUsr+anNyOniaixE1W+xwcM4lgnbYfWsQ==", "dependencies": { "@codemirror/view": "^6.41.1" } @@ -6656,9 +6673,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.342", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.342.tgz", - "integrity": "sha512-GTuy59SdGxYgz+HN8KwOjFAVF2gfoKEmv0PFholcvVtbI9GPDND0m6ynGX3gAKOavcHRLrcfNy0QMbHbAemYdw==", + "version": "1.5.349", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.349.tgz", + "integrity": "sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==", "license": "ISC" }, "node_modules/emittery": { @@ -10601,13 +10618,13 @@ } }, "node_modules/mongoose": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.5.0.tgz", - "integrity": "sha512-B4blGFkFL1s0G24URuMvx0qTlx+gRVLmfO7WcSz8NcmW/XHEJ3G69capdyW1iRsGKiycp1tkwKHnxHbnwjwmPw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.6.1.tgz", + "integrity": "sha512-3T8/b0plM3ZJPW3WjlzVMIGJEYYTjgDPQ05Qzru3xu3/wOPSFKWYxdwUF2dl8h3NG5dVkzIuOkZdLacnlLf/sA==", "license": "MIT", "dependencies": { "kareem": "3.3.0", - "mongodb": "~7.1", + "mongodb": "~7.2", "mpath": "0.9.0", "mquery": "6.0.0", "ms": "2.1.3", @@ -10621,14 +10638,30 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/mongoose/node_modules/mongodb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.1.tgz", - "integrity": "sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz", + "integrity": "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.3.0", - "bson": "^7.1.1", + "bson": "^7.2.0", "mongodb-connection-string-url": "^7.0.0" }, "engines": { @@ -10948,9 +10981,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "license": "MIT" }, "node_modules/normalize-path": { @@ -11412,9 +11445,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", "funding": [ { "type": "opencollective", @@ -11513,9 +11546,9 @@ "license": "MIT" }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -11646,9 +11679,9 @@ } }, "node_modules/qified/node_modules/hookified": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.1.1.tgz", - "integrity": "sha512-AHb76R16GB5EsPBE2J7Ko5kiEyXwviB9P5SMrAKcuAu4vJPZttViAbj9+tZeaQE5zjDme+1vcHP78Yj/WoAveA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", "dev": true, "license": "MIT" }, @@ -12787,9 +12820,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "17.8.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.8.0.tgz", - "integrity": "sha512-oHkld9T60LDSaUQ4CSVc+tlt9eUoDlxhaGWShsUCKyIL14boZfmK5bSphZqx64aiC5tCqX+BsQMTMoSz8D1zIg==", + "version": "17.10.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.10.0.tgz", + "integrity": "sha512-cI7I6HHEYOHHVNVci+s92WlA3QfmNhjwFdgCgYV3TLEysilOjk+B3EFxMED1xY9GYB0Kre3OD+mSLj19VLTIvA==", "dev": true, "funding": [ { @@ -12804,9 +12837,9 @@ "license": "MIT", "peer": true, "dependencies": { - "@csstools/css-calc": "^3.1.1", + "@csstools/css-calc": "^3.2.0", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-syntax-patches-for-csstree": "^1.1.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0", "@csstools/selector-resolve-nested": "^4.0.0", @@ -12831,7 +12864,7 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.5.9", + "postcss": "^8.5.13", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", @@ -12946,9 +12979,9 @@ } }, "node_modules/stylelint/node_modules/string-width": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", - "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", "dev": true, "license": "MIT", "dependencies": { @@ -13163,9 +13196,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -13269,22 +13302,22 @@ } }, "node_modules/tldts": { - "version": "7.0.28", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz", - "integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==", + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.28" + "tldts-core": "^7.0.30" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.28", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz", - "integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==", + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", "dev": true, "license": "MIT" }, From 4edae9ef5a21942ef9728c9979fe01725946c4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 3 May 2026 18:01:37 +0200 Subject: [PATCH 33/77] some cleanup --- client/components/codeEditor/codeEditor.jsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index f1f26157d..4dbd04f0d 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -17,8 +17,7 @@ import { crosshairCursor, } from '@codemirror/view'; import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state'; -import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, syntaxHighlighting } from '@codemirror/language'; -import { foldEffect } from '@codemirror/language'; +import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, foldEffect, syntaxHighlighting } from '@codemirror/language'; import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands'; import { languages } from '@codemirror/language-data'; import { css } from '@codemirror/lang-css'; @@ -38,8 +37,6 @@ const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const themeCompartment = new Compartment(); const highlightCompartment = new Compartment(); -console.log(themes); - import { generalKeymap, markdownKeymap } from './customKeyMaps.js'; import foldOnPages from './customFolding.js'; import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './customHighlight.js'; From 72465317f3091ad55269d10478e35037d8a256c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 4 May 2026 00:31:43 +0200 Subject: [PATCH 34/77] store scroll positions --- client/components/codeEditor/codeEditor.jsx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 4dbd04f0d..43065462f 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -148,12 +148,13 @@ const CodeEditor = forwardRef( const editorRef = useRef(null); const viewRef = useRef(null); const docsRef = useRef({}); + const tabRef = useRef(tab); const prevTabRef = useRef(tab); - + const scrollRef = useRef({}); const pageMap = useRef([]); const recomputePages = (doc)=>{ - if (tab !== 'brewText') return; + if(tab !== 'brewText') return; const pages = [0]; const text = doc.toString(); let offset = 0; @@ -265,11 +266,10 @@ const CodeEditor = forwardRef( ticking = true; requestAnimationFrame(()=>{ const top = view.scrollDOM.scrollTop; + scrollRef.current[tabRef.current] = top; const block = view.lineBlockAtHeight(top); - - const page = findPageFromPos(block.from); // CHANGED + const page = findPageFromPos(block.from); onViewChange(page); - ticking = false; }); }; @@ -288,6 +288,7 @@ const CodeEditor = forwardRef( const view = viewRef.current; if(!view) return; + tabRef.current = tab; const prevTab = prevTabRef.current; if(prevTab !== tab) { @@ -303,6 +304,15 @@ const CodeEditor = forwardRef( } view.setState(nextState); + + const savedScroll = scrollRef.current[tab]; + + if(savedScroll != null) { + requestAnimationFrame(()=>{ + view.scrollDOM.scrollTop = savedScroll; + }); + } + prevTabRef.current = tab; } view.focus(); From 65d11069746c46d71df2ce4e08a4d678aa247038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 4 May 2026 00:42:10 +0200 Subject: [PATCH 35/77] this should fix it --- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/newPage/newPage.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index b41f1bc74..6c27d1f84 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -216,7 +216,7 @@ const EditPage = (props)=>{ const brewToSave = { ...brew, text : brew.text.normalize('NFC'), - pageCount : ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1, + pageCount : ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm)) || []).length + 1, patches : stringifyPatches(makePatches(encodeURI(lastSavedBrew.current.text.normalize('NFC')), encodeURI(brew.text.normalize('NFC')))), hash : await md5(lastSavedBrew.current.text.normalize('NFC')), textBin : undefined, diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 7ddb05c0b..270ca89a0 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -156,7 +156,7 @@ const NewPage = (props)=>{ const updatedBrew = { ...currentBrew }; splitTextStyleAndMetadata(updatedBrew); - const pageRegex = updatedBrew.renderer === 'legacy' ? /\\page/g : /^\\page$/gm; + const pageRegex = updatedBrew.renderer === 'legacy' ? /\\page/g : /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm; updatedBrew.pageCount = (updatedBrew.text.match(pageRegex) || []).length + 1; const res = await request From 1f2a3b164bd6b898aacbfdddbc5b322aaa811fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 03:27:51 +0000 Subject: [PATCH 36/77] Bump the prod-dependencies group across 1 directory with 3 updates Bumps the prod-dependencies group with 3 updates in the / directory: [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env), [mongoose](https://github.com/Automattic/mongoose) and [nanoid](https://github.com/ai/nanoid). Updates `@babel/preset-env` from 7.29.2 to 7.29.3 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.29.3/packages/babel-preset-env) Updates `mongoose` from 9.5.0 to 9.6.1 - [Release notes](https://github.com/Automattic/mongoose/releases) - [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md) - [Commits](https://github.com/Automattic/mongoose/compare/9.5.0...9.6.1) Updates `nanoid` from 5.1.7 to 5.1.11 - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/5.1.7...5.1.11) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-version: 7.29.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: mongoose dependency-version: 9.6.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies - dependency-name: nanoid dependency-version: 5.1.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 84 +++++++++++++++++++++++------------------------ package.json | 6 ++-- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7657daf1..cb42fc147 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0", - "@babel/preset-env": "^7.29.2", + "@babel/preset-env": "^7.29.3", "@babel/preset-react": "^7.28.5", "@babel/runtime": "^7.29.2", "@codemirror/autocomplete": "^6.20.1", @@ -62,8 +62,8 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.3.3", - "nanoid": "5.1.7", + "mongoose": "^9.6.1", + "nanoid": "5.1.11", "nconf": "^0.13.0", "node": "^25.9.0", "react": "^19.2.4", @@ -177,9 +177,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -190,7 +190,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -549,6 +548,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", @@ -1783,18 +1798,19 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", - "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.3.tgz", + "integrity": "sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.29.0", + "@babel/compat-data": "^7.29.3", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", @@ -2023,7 +2039,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2629,7 +2644,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2678,7 +2692,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -4055,9 +4068,9 @@ "license": "MIT" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.8.tgz", - "integrity": "sha512-kpjr2jy2w71w0oqAMI8oibBmiF9lXxWkEQs5gMkW4hVE48bsqINGLxnCSYW62ck/NHXJQpQEfA9WlJ1sY0eqBg==", + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", + "integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -4081,7 +4094,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -5217,7 +5229,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5803,7 +5814,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -6984,7 +6994,6 @@ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -9247,7 +9256,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -9865,7 +9873,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -10243,7 +10250,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10601,13 +10607,13 @@ } }, "node_modules/mongoose": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.5.0.tgz", - "integrity": "sha512-B4blGFkFL1s0G24URuMvx0qTlx+gRVLmfO7WcSz8NcmW/XHEJ3G69capdyW1iRsGKiycp1tkwKHnxHbnwjwmPw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.6.1.tgz", + "integrity": "sha512-3T8/b0plM3ZJPW3WjlzVMIGJEYYTjgDPQ05Qzru3xu3/wOPSFKWYxdwUF2dl8h3NG5dVkzIuOkZdLacnlLf/sA==", "license": "MIT", "dependencies": { "kareem": "3.3.0", - "mongodb": "~7.1", + "mongodb": "~7.2", "mpath": "0.9.0", "mquery": "6.0.0", "ms": "2.1.3", @@ -10622,13 +10628,13 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.1.tgz", - "integrity": "sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz", + "integrity": "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.3.0", - "bson": "^7.1.1", + "bson": "^7.2.0", "mongodb-connection-string-url": "^7.0.0" }, "engines": { @@ -10692,9 +10698,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", - "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", + "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", "funding": [ { "type": "github", @@ -11430,7 +11436,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11486,7 +11491,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -11501,6 +11505,7 @@ "integrity": "sha512-TXbU+h6vVRW+86c/+ewhWq9k7pr7ijASTnepVhCQiC87zAOTkvB1v2dHyWP+ggstSTX/PNvjzS+IOqzejndz9w==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "postcss": "^8.4.20" } @@ -11573,7 +11578,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11717,7 +11721,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11727,7 +11730,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12802,7 +12804,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", @@ -13745,7 +13746,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/package.json b/package.json index 6989c444f..82c872158 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0", - "@babel/preset-env": "^7.29.2", + "@babel/preset-env": "^7.29.3", "@babel/preset-react": "^7.28.5", "@babel/runtime": "^7.29.2", "@codemirror/autocomplete": "^6.20.1", @@ -138,8 +138,8 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.3.3", - "nanoid": "5.1.7", + "mongoose": "^9.6.1", + "nanoid": "5.1.11", "nconf": "^0.13.0", "node": "^25.9.0", "react": "^19.2.4", From 102d1f4186df20aacdf2b7841d6a0fad30a9ac8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 4 May 2026 16:02:15 +0200 Subject: [PATCH 37/77] restore folds when changing tab --- client/components/codeEditor/codeEditor.jsx | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 43065462f..722301795 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -17,7 +17,7 @@ import { crosshairCursor, } from '@codemirror/view'; import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state'; -import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, foldEffect, syntaxHighlighting } from '@codemirror/language'; +import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, foldEffect, foldState, syntaxHighlighting } from '@codemirror/language'; import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands'; import { languages } from '@codemirror/language-data'; import { css } from '@codemirror/lang-css'; @@ -151,6 +151,7 @@ const CodeEditor = forwardRef( const tabRef = useRef(tab); const prevTabRef = useRef(tab); const scrollRef = useRef({}); + const foldsRef = useRef({}); const pageMap = useRef([]); const recomputePages = (doc)=>{ @@ -180,6 +181,14 @@ const CodeEditor = forwardRef( return page; }; + const getFoldRanges = (state) => { + const folds = []; + state.field(foldState, false)?.between(0, state.doc.length, (from, to) => { + folds.push({ from, to }); + }); + return folds; + }; + const createExtensions = ({ onChange, language, editorTheme })=>{ const setEventListeners = EditorView.updateListener.of((update)=>{ if(update.docChanged) { @@ -284,6 +293,14 @@ const CodeEditor = forwardRef( }; }, []); + const restoreFolds = (view, folds) => { + if (!folds?.length) return; + + view.dispatch({ + effects: folds.map(f => foldEffect.of(f)) + }); + }; + useEffect(()=>{ const view = viewRef.current; if(!view) return; @@ -291,6 +308,8 @@ const CodeEditor = forwardRef( tabRef.current = tab; const prevTab = prevTabRef.current; + foldsRef.current[prevTab] = getFoldRanges(view.state); + if(prevTab !== tab) { docsRef.current[prevTab] = view.state; @@ -304,6 +323,7 @@ const CodeEditor = forwardRef( } view.setState(nextState); + restoreFolds(view, foldsRef.current[tab]); const savedScroll = scrollRef.current[tab]; From 6e39a707060676369f2e910f475d318be876d26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 4 May 2026 16:03:07 +0200 Subject: [PATCH 38/77] lint --- client/components/codeEditor/codeEditor.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 722301795..3b0ff5854 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -181,9 +181,9 @@ const CodeEditor = forwardRef( return page; }; - const getFoldRanges = (state) => { + const getFoldRanges = (state)=>{ const folds = []; - state.field(foldState, false)?.between(0, state.doc.length, (from, to) => { + state.field(foldState, false)?.between(0, state.doc.length, (from, to)=>{ folds.push({ from, to }); }); return folds; @@ -293,11 +293,11 @@ const CodeEditor = forwardRef( }; }, []); - const restoreFolds = (view, folds) => { - if (!folds?.length) return; + const restoreFolds = (view, folds)=>{ + if(!folds?.length) return; view.dispatch({ - effects: folds.map(f => foldEffect.of(f)) + effects : folds.map((f)=>foldEffect.of(f)) }); }; @@ -326,7 +326,7 @@ const CodeEditor = forwardRef( restoreFolds(view, foldsRef.current[tab]); const savedScroll = scrollRef.current[tab]; - + if(savedScroll != null) { requestAnimationFrame(()=>{ view.scrollDOM.scrollTop = savedScroll; From 60b5f6bac20138167e7549e6b01778d0f36e0209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 4 May 2026 16:06:53 +0200 Subject: [PATCH 39/77] fix tab insert --- client/components/codeEditor/customKeyMaps.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/customKeyMaps.js index a05cbacd9..194f79167 100644 --- a/client/components/codeEditor/customKeyMaps.js +++ b/client/components/codeEditor/customKeyMaps.js @@ -3,6 +3,17 @@ import { keymap } from '@codemirror/view'; import { undo, redo, indentMore, deleteLine } from '@codemirror/commands'; import { Prec } from '@codemirror/state'; +const insertTab = (view) => { + const { from, to } = view.state.selection.main; + + view.dispatch({ + changes: { from, to, insert: ' ' }, + selection: { anchor: from + 2 } + }); + + return true; +}; + const indentLess = (view)=>{ const { from, to } = view.state.selection.main; const lines = []; @@ -162,7 +173,7 @@ const newPage = (view)=>{ }; export const generalKeymap = Prec.high(keymap.of([ - { key: 'Tab', run: indentMore }, + { key: 'Tab', run: insertTab }, { key: 'Mod-z', run: undo }, //i think it may be unnecessary { key: 'Mod-Shift-z', run: redo }, { key: 'Mod-y', run: redo }, From 80093a1998aaae140990635c8e867890ee4721d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= <5ecleric.naturalcrit@gmail.com> Date: Mon, 4 May 2026 16:13:15 +0200 Subject: [PATCH 40/77] remove console.log --- client/components/codeEditor/customHighlight.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 86bad59c8..0d53e2f33 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -185,7 +185,6 @@ export function tokenizeCustomMarkdown(text) { const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gmd; let match; while ((match = injectionRegex.exec(lineText)) !== null) { - console.log(match[2]); tokens.push({ line : lineNumber, from : match.indices[1][0], From 9f6e03fd80f49e48dc9cc5be64622ba03324f910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 4 May 2026 16:24:41 +0200 Subject: [PATCH 41/77] add strikethrough highlight --- client/components/codeEditor/codeEditor.less | 4 ++++ .../components/codeEditor/customHighlight.js | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index 84ed8058d..4ca374291 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -100,6 +100,10 @@ vertical-align : sub; color : rgb(123, 123, 15); } + .cm-strikethrough { + text-decoration: line-through; + } + .cm-definitionList { .cm-definitionTerm { color : rgb(96, 117, 143); } .cm-definitionColon:not(:has(.cm-comment)) { diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 0d53e2f33..09a9af18f 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -16,6 +16,7 @@ const customTags = { definitionTerm : 'definitionTerm', // .cm-definitionTerm definitionDesc : 'definitionDesc', // .cm-definitionDesc definitionColon : 'definitionColon', // .cm-definitionColon + strikethrough : 'strikethrough', // .cm-strikethrough //CSS @@ -81,6 +82,23 @@ export function tokenizeCustomMarkdown(text) { } } + // --- Strikethrough --- + if(/\~/.test(lineText)) { + const strikethroughRegex = /~(?!\s)(.+?)(? Date: Mon, 4 May 2026 16:35:45 +0200 Subject: [PATCH 42/77] lock sync --- package-lock.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a32536ac..c8c412bba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0", - "@babel/preset-env": "^7.29.2", + "@babel/preset-env": "^7.29.3", "@babel/preset-react": "^7.28.5", "@babel/runtime": "^7.29.2", "@codemirror/autocomplete": "^6.20.1", @@ -62,8 +62,8 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.3.3", - "nanoid": "5.1.7", + "mongoose": "^9.6.1", + "nanoid": "5.1.11", "nconf": "^0.13.0", "node": "^25.9.0", "react": "^19.2.4", @@ -4072,9 +4072,9 @@ "license": "MIT" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.10.tgz", - "integrity": "sha512-DDb3OAw8ezai9p2i1F7R5wMVmyrMIuT8ixjV56R4Hl4cazCo2tOMTDyPR5rrCUcHiMbWHzCXOdife5OD6H1C8w==", + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", + "integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -4580,9 +4580,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, "license": "MIT", "optional": true, @@ -7498,9 +7498,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.1.tgz", + "integrity": "sha512-h2r7rcm6Ee/J8o0LD5djLuFVcfbZxhvho4vvsbeV0aMvXjUgqv4YpxpkEx0d68l6+IleVfLAdVEfhR7QNMkGHQ==", "dev": true, "funding": [ { @@ -10725,9 +10725,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", - "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", + "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", "funding": [ { "type": "github", From 339d2844a79fd58b1f4e4eae95ee9cbcd058cab3 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 4 May 2026 16:01:39 -0400 Subject: [PATCH 43/77] Change logic to fire on button click, rather than on state change Ideally side effects (server calls, etc.) should happen outside of useEffect anyway for reasons like this; Putting the save in the button click keeps the logic in one place instead of spread across multiple functions and renders, (This change was already made in my local copy toward combining Edit/New/Home pages.) --- client/homebrew/pages/editPage/editPage.jsx | 23 +++++++-------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 0dad57855..1c5e54b5e 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -90,7 +90,7 @@ const EditPage = (props)=>{ const handleControlKeys = (e)=>{ if(!(e.ctrlKey || e.metaKey)) return; - if(e.keyCode === 83) trySaveRef.current(true); + if(e.keyCode === 83) trySaveRef.current(true, true, saveGoogle); if(e.keyCode === 80) printCurrentBrew(); if([83, 80].includes(e.keyCode)) { e.stopPropagation(); @@ -118,20 +118,9 @@ const EditPage = (props)=>{ const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current); setUnsavedChanges(hasChange); - if(autoSaveEnabled) trySave(false, hasChange); + if(autoSaveEnabled) trySave(false, hasChange, saveGoogle); }, [currentBrew]); - const didMount = useRef(false); - - useEffect(()=>{ - if (!didMount.current) { - didMount.current = true; - return; - } - - trySave(true); - }, [saveGoogle]); - const handleSplitMove = ()=>{ editorRef.current?.update(); }; @@ -190,11 +179,13 @@ const EditPage = (props)=>{ }; const toggleGoogleStorage = ()=>{ + const newSaveGoogle = !saveGoogle; setSaveGoogle((prev)=>!prev); setError(null); + trySave(true, true, newSaveGoogle); }; - const trySave = (immediate = false, hasChanges = true)=>{ + const trySave = (immediate = false, hasChanges = true, saveToGoogle = false)=>{ clearTimeout(saveTimeout.current); if(isSaving) return; if(!hasChanges && !immediate) return; @@ -203,7 +194,7 @@ const EditPage = (props)=>{ saveTimeout.current = setTimeout(async ()=>{ setIsSaving(true); setError(null); - await save(currentBrew, saveGoogle) + await save(currentBrew, saveToGoogle) .catch((err)=>{ setError(err); }); @@ -321,7 +312,7 @@ const EditPage = (props)=>{ // #3 - Unsaved changes exist, click to save, show SAVE NOW if(unsavedChanges) - return trySave(true)} color='blue' icon='fas fa-save'>save now; + return trySave(true, true, saveGoogle)} color='blue' icon='fas fa-save'>save now; // #4 - No unsaved changes, autosave is ON, show AUTO-SAVED if(autoSaveEnabled) From f5acb27c346d86f8508e53e9a6ea158ae601df11 Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Tue, 5 May 2026 22:38:43 -0500 Subject: [PATCH 44/77] Revert "Fix font snippet "Open Sans"" This reverts commit 34d634a34979b292b30006e6a6092d36f1a77284. --- client/homebrew/editor/snippetbar/snippetbar.less | 2 +- themes/V3/Blank/snippets.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 0ee9e1ea3..37853ca75 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -184,7 +184,7 @@ content : 'ABC'; } - &.OpenSans {font-family : 'Open Sans';} + &.OpenSans {font-family : 'OpenSans';} &.CodeBold {font-family : 'CodeBold';} &.CodeLight {font-family : 'CodeLight';} &.ScalySansRemake {font-family : 'ScalySansRemake';} diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js index f70efed26..4e9ec1afb 100644 --- a/themes/V3/Blank/snippets.js +++ b/themes/V3/Blank/snippets.js @@ -820,7 +820,7 @@ export default [ { name : 'Open Sans', icon : 'font OpenSans', - gen : dedent`{{font-family:"Open Sans" Dummy Text}}` + gen : dedent`{{font-family:OpenSans Dummy Text}}` }, { name : 'Code Bold', From 01060c38c8186da0170bea63051ec6cda1e30bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 8 May 2026 23:23:48 +0200 Subject: [PATCH 45/77] make wrapselection work with multiple selections --- client/components/codeEditor/customKeyMaps.js | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/customKeyMaps.js index 194f79167..717f30f4e 100644 --- a/client/components/codeEditor/customKeyMaps.js +++ b/client/components/codeEditor/customKeyMaps.js @@ -29,27 +29,25 @@ const indentLess = (view)=>{ }; const wrapSelection = (prefix, suffix) => (view) => { - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); + const changes = []; - let text, selection; + for(const range of view.state.selection.ranges) { + const { from, to } = range; + const selected = view.state.doc.sliceString(from, to); - if(from === to) { - text = prefix + suffix; - selection = { anchor: from + prefix.length, head: from + prefix.length }; - } - else if(selected.startsWith(prefix) && selected.endsWith(suffix)) { - text = selected.slice(prefix.length, -suffix.length); - selection = { anchor: from, head: from + text.length }; - } - else { - text = `${prefix}${selected}${suffix}`; - selection = { anchor: from, head: from + text.length }; + let text; + + if(from === to) { text = prefix + suffix } + else if(selected.startsWith(prefix) && selected.endsWith(suffix)) { + text = selected.slice(prefix.length, -suffix.length); + } + else {text = `${prefix}${selected}${suffix}`} + + changes.push({ from, to, insert: text }); } view.dispatch({ - changes : { from, to, insert: text }, - selection + changes }); return true; From e9dba94dd5bdf1887aa3489f42db9e4bf0b8af30 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 9 May 2026 17:58:52 +1200 Subject: [PATCH 46/77] Escape $ in HTML replace function --- server/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index 18b1d68bc..608c3dc17 100644 --- a/server/app.js +++ b/server/app.js @@ -593,7 +593,7 @@ export default async function createApp(vite) { html = html.replace( '', - `\n\n${ogMetaTags}` + `\n\n${ogMetaTags}` ); return html; From 82bab129413cc30b0447cfa5d50de172b9e12cf9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 9 May 2026 18:08:44 +1200 Subject: [PATCH 47/77] Use function instead of pattern escaping --- server/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index 608c3dc17..b1e0e1c25 100644 --- a/server/app.js +++ b/server/app.js @@ -593,7 +593,7 @@ export default async function createApp(vite) { html = html.replace( '', - `\n\n${ogMetaTags}` + ()=>{ return `\n\n${ogMetaTags}`; } ); return html; From 93d51fe4995bdf5deed022efec9d340a5e59f6e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 10:38:05 +0000 Subject: [PATCH 48/77] Bump fast-uri from 3.1.1 to 3.1.2 Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.1...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 41 ++++------------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8c412bba..0050547dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -190,7 +190,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2040,7 +2039,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2646,7 +2644,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2695,7 +2692,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -4098,7 +4094,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -5234,7 +5229,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5820,7 +5814,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -7001,7 +6994,6 @@ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -7498,9 +7490,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.1.tgz", - "integrity": "sha512-h2r7rcm6Ee/J8o0LD5djLuFVcfbZxhvho4vvsbeV0aMvXjUgqv4YpxpkEx0d68l6+IleVfLAdVEfhR7QNMkGHQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -9264,7 +9256,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -9882,7 +9873,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -10260,7 +10250,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10638,22 +10627,6 @@ "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/gcp-metadata": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", - "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/mongoose/node_modules/mongodb": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz", @@ -11463,7 +11436,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11519,7 +11491,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -11534,6 +11505,7 @@ "integrity": "sha512-TXbU+h6vVRW+86c/+ewhWq9k7pr7ijASTnepVhCQiC87zAOTkvB1v2dHyWP+ggstSTX/PNvjzS+IOqzejndz9w==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "postcss": "^8.4.20" } @@ -11606,7 +11578,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11750,7 +11721,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11760,7 +11730,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12835,7 +12804,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-calc": "^3.2.0", "@csstools/css-parser-algorithms": "^4.0.0", @@ -13778,7 +13746,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", From cc2963b7651095bc03e6ebaf0c0a4500202f388d Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 10 May 2026 12:14:54 +1200 Subject: [PATCH 49/77] Increase information about mismatch brew content --- shared/helpers.js | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index d2a9c8b73..5cfa8b376 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -160,9 +160,35 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{ // Char-level diff for (let i = 0; i < Math.min(clientText.length, serverText.length); i++) { if(clientText[i] !== serverText[i]) { + const getMismatchContext = (text, index, name, size = 10)=>{ + const lower = Math.max(index - size, 0); + const upper = Math.min(index + size, text.length); + const slice = `${JSON.stringify(text.slice(lower, index - 1)).slice(1, -1)}\u001B[31m${JSON.stringify(text[i]).slice(1, -1)}\u001B[0m${JSON.stringify(text.slice(index+1, upper)).slice(1, -1)}`; + const lineNo = text.slice(0, index).split('\n').length; + const code = `U+${text.charCodeAt(i).toString(16).toUpperCase()}`; + + return { + name, + lineNo, + code, + lower, + upper, + slice + }; + }; + + const boundSize = 10; + + const clientContext = getMismatchContext(clientText, i, 'Client', boundSize); + const serverContext = getMismatchContext(serverText, i, 'Server', boundSize); + + const logContext = (context)=>{ + console.log(` ${context.name} - line ${context.lineNo} : (${context.code})\t${context.slice}`); + }; + console.log(`Char mismatch at index ${i}:`); - console.log(` Client: '${clientText[i]}' (U+${clientText.charCodeAt(i).toString(16).toUpperCase()})`); - console.log(` Server: '${serverText[i]}' (U+${serverText.charCodeAt(i).toString(16).toUpperCase()})`); + logContext(clientContext); + logContext(serverContext); break; } } From 9e4d9ee35d87b15702f3bd51e6bc262e94a3584f Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 10 May 2026 12:23:06 +1200 Subject: [PATCH 50/77] Fix missing character --- shared/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/helpers.js b/shared/helpers.js index 5cfa8b376..eb3dba6d8 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -163,7 +163,7 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{ const getMismatchContext = (text, index, name, size = 10)=>{ const lower = Math.max(index - size, 0); const upper = Math.min(index + size, text.length); - const slice = `${JSON.stringify(text.slice(lower, index - 1)).slice(1, -1)}\u001B[31m${JSON.stringify(text[i]).slice(1, -1)}\u001B[0m${JSON.stringify(text.slice(index+1, upper)).slice(1, -1)}`; + const slice = `${JSON.stringify(text.slice(lower, index)).slice(1, -1)}\u001B[31m${JSON.stringify(text[i]).slice(1, -1)}\u001B[0m${JSON.stringify(text.slice(index+1, upper)).slice(1, -1)}`; const lineNo = text.slice(0, index).split('\n').length; const code = `U+${text.charCodeAt(i).toString(16).toUpperCase()}`; From 10506ad521ee2a5fb91cc39ec217d36202686f67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 16:36:28 +0000 Subject: [PATCH 51/77] Bump the prod-dependencies group across 1 directory with 9 updates Bumps the prod-dependencies group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.29.3` | `7.29.5` | | [@codemirror/autocomplete](https://github.com/codemirror/autocomplete) | `6.20.1` | `6.20.2` | | [@codemirror/view](https://github.com/codemirror/view) | `6.41.1` | `6.42.1` | | [express-static-gzip](https://github.com/tkoenig89/express-static-gzip) | `3.0.0` | `3.0.1` | | [fs-extra](https://github.com/jprichardson/node-fs-extra) | `11.3.4` | `11.3.5` | | [mongoose](https://github.com/Automattic/mongoose) | `9.6.1` | `9.6.2` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.5` | `19.2.6` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.2.5` | `19.2.6` | | [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) | `7.14.2` | `7.15.0` | Updates `@babel/preset-env` from 7.29.3 to 7.29.5 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.29.5/packages/babel-preset-env) Updates `@codemirror/autocomplete` from 6.20.1 to 6.20.2 - [Changelog](https://github.com/codemirror/autocomplete/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/autocomplete/commits) Updates `@codemirror/view` from 6.41.1 to 6.42.1 - [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/view/commits) Updates `express-static-gzip` from 3.0.0 to 3.0.1 - [Release notes](https://github.com/tkoenig89/express-static-gzip/releases) - [Commits](https://github.com/tkoenig89/express-static-gzip/compare/v3.0.0...v3.0.1) Updates `fs-extra` from 11.3.4 to 11.3.5 - [Changelog](https://github.com/jprichardson/node-fs-extra/blob/master/CHANGELOG.md) - [Commits](https://github.com/jprichardson/node-fs-extra/compare/11.3.4...11.3.5) Updates `mongoose` from 9.6.1 to 9.6.2 - [Release notes](https://github.com/Automattic/mongoose/releases) - [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md) - [Commits](https://github.com/Automattic/mongoose/compare/9.6.1...9.6.2) Updates `react` from 19.2.5 to 19.2.6 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react) Updates `react-dom` from 19.2.5 to 19.2.6 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react-dom) Updates `react-router` from 7.14.2 to 7.15.0 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-version: 7.29.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: "@codemirror/autocomplete" dependency-version: 6.20.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: "@codemirror/view" dependency-version: 6.42.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies - dependency-name: express-static-gzip dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: fs-extra dependency-version: 11.3.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: mongoose dependency-version: 9.6.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react dependency-version: 19.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-dom dependency-version: 19.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-router dependency-version: 7.15.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 82 +++++++++++++++++++++++------------------------ package.json | 18 +++++------ 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0050547dd..1bca191d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,10 @@ "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0", - "@babel/preset-env": "^7.29.3", + "@babel/preset-env": "^7.29.5", "@babel/preset-react": "^7.28.5", "@babel/runtime": "^7.29.2", - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.2", "@codemirror/commands": "^6.10.3", "@codemirror/highlight": "^0.19.8", "@codemirror/lang-css": "^6.3.1", @@ -25,7 +25,7 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.40.0", + "@codemirror/view": "^6.42.1", "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^20.1.0", "@lezer/highlight": "^1.2.3", @@ -41,9 +41,9 @@ "dedent": "^1.7.1", "express": "^5.1.0", "express-async-handler": "^1.2.0", - "express-static-gzip": "3.0.0", + "express-static-gzip": "3.0.1", "fflate": "^0.8.2", - "fs-extra": "^11.3.3", + "fs-extra": "^11.3.5", "hash-wasm": "^4.12.0", "idb-keyval": "^6.2.2", "js-yaml": "^4.1.1", @@ -62,14 +62,14 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.6.1", + "mongoose": "^9.6.2", "nanoid": "5.1.11", "nconf": "^0.13.0", "node": "^25.9.0", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.6", + "react-dom": "^19.2.6", "react-frame-component": "^5.3.2", - "react-router": "^7.14.0", + "react-router": "^7.15.0", "sanitize-filename": "1.6.4", "superagent": "^10.2.1" }, @@ -1274,9 +1274,9 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", @@ -1798,9 +1798,9 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.3.tgz", - "integrity": "sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==", + "version": "7.29.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", + "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.3", @@ -1842,7 +1842,7 @@ "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.0", + "@babel/plugin-transform-modules-systemjs": "^7.29.4", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-new-target": "^7.27.1", @@ -2065,9 +2065,9 @@ } }, "node_modules/@codemirror/autocomplete": { - "version": "6.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz", - "integrity": "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==", + "version": "6.20.2", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.2.tgz", + "integrity": "sha512-G5FPkgIiLjOgZMjqVjvuKQ1rGPtHogLldJr33eFJdVLtmwY+giGrlv/ewljLz6b9BSQLkjxuwBc6g6omDM+YxQ==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -2545,9 +2545,9 @@ "license": "MIT" }, "node_modules/@codemirror/view": { - "version": "6.41.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.41.1.tgz", - "integrity": "sha512-ToDnWKbBnke+ZLrP6vgTTDScGi5H37YYuZGniQaBzxMVdtCxMrslsmtnOvbPZk4RX9bvkQqnWR/WS/35tJA0qg==", + "version": "6.42.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.42.1.tgz", + "integrity": "sha512-ToN3oFc0nsxNUYVF5P0ztLgbC4UPPjPtA9aKYhkOKQaZASpOUo6ISXyQLP66ctVwlDc+j6Jv0uK5IFALkiXztg==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.6.0", @@ -7407,9 +7407,9 @@ "license": "MIT" }, "node_modules/express-static-gzip": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-3.0.0.tgz", - "integrity": "sha512-36O10S0asHl3QojOBQQ0ZjXNtElmhgPS6erSUCCZymXkB/CK1mnGqOj4BTJN+FYRDIzVFnzo3wLFCZJvAk6rQQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-3.0.1.tgz", + "integrity": "sha512-LMeU/3YjFlFUa4vrPX+RoMMRW5mIpF4Iysgs6gX7A59WCY4BzyF3O28mBr4eMlWuW4DU9wVAVuVcfx29ln1N6g==", "license": "MIT", "dependencies": { "mime-types": "^3.0.1", @@ -7782,9 +7782,9 @@ } }, "node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -10607,9 +10607,9 @@ } }, "node_modules/mongoose": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.6.1.tgz", - "integrity": "sha512-3T8/b0plM3ZJPW3WjlzVMIGJEYYTjgDPQ05Qzru3xu3/wOPSFKWYxdwUF2dl8h3NG5dVkzIuOkZdLacnlLf/sA==", + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.6.2.tgz", + "integrity": "sha512-7m8HntjkoRnwEmuPC0kdlwcZXJOQf4twumFj+PNzg/anqqZE2Er7hQslqyzy07mP3JcFjoTSgH5765PyqOXsxw==", "license": "MIT", "dependencies": { "kareem": "3.3.0", @@ -11717,24 +11717,24 @@ } }, "node_modules/react": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", - "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", - "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.5" + "react": "^19.2.6" } }, "node_modules/react-frame-component": { @@ -11765,9 +11765,9 @@ } }, "node_modules/react-router": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.2.tgz", - "integrity": "sha512-yCqNne6I8IB6rVCH7XUvlBK7/QKyqypBFGv+8dj4QBFJiiRX+FG7/nkdAvGElyvVZ/HQP5N19wzteuTARXi5Gw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.0.tgz", + "integrity": "sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", diff --git a/package.json b/package.json index 82c872158..8603b38f5 100644 --- a/package.json +++ b/package.json @@ -88,10 +88,10 @@ "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0", - "@babel/preset-env": "^7.29.3", + "@babel/preset-env": "^7.29.5", "@babel/preset-react": "^7.28.5", "@babel/runtime": "^7.29.2", - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.2", "@codemirror/commands": "^6.10.3", "@codemirror/highlight": "^0.19.8", "@codemirror/lang-css": "^6.3.1", @@ -101,7 +101,7 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.40.0", + "@codemirror/view": "^6.42.1", "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^20.1.0", "@lezer/highlight": "^1.2.3", @@ -117,9 +117,9 @@ "dedent": "^1.7.1", "express": "^5.1.0", "express-async-handler": "^1.2.0", - "express-static-gzip": "3.0.0", + "express-static-gzip": "3.0.1", "fflate": "^0.8.2", - "fs-extra": "^11.3.3", + "fs-extra": "^11.3.5", "hash-wasm": "^4.12.0", "idb-keyval": "^6.2.2", "js-yaml": "^4.1.1", @@ -138,14 +138,14 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.6.1", + "mongoose": "^9.6.2", "nanoid": "5.1.11", "nconf": "^0.13.0", "node": "^25.9.0", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.6", + "react-dom": "^19.2.6", "react-frame-component": "^5.3.2", - "react-router": "^7.14.0", + "react-router": "^7.15.0", "sanitize-filename": "1.6.4", "superagent": "^10.2.1" }, From 118ec1bc503cc70045562ff73517c3babd761c23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 16:41:15 +0000 Subject: [PATCH 52/77] Bump the dev-dependencies group across 1 directory with 3 updates Bumps the dev-dependencies group with 3 updates in the / directory: [babel-jest](https://github.com/jestjs/jest/tree/HEAD/packages/babel-jest), [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) and [stylelint](https://github.com/stylelint/stylelint). Updates `babel-jest` from 30.3.0 to 30.4.1 - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.4.1/packages/babel-jest) Updates `jest` from 30.3.0 to 30.4.2 - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.4.2/packages/jest) Updates `stylelint` from 17.10.0 to 17.11.0 - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md) - [Commits](https://github.com/stylelint/stylelint/compare/17.10.0...17.11.0) --- updated-dependencies: - dependency-name: babel-jest dependency-version: 30.4.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: jest dependency-version: 30.4.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: stylelint dependency-version: 17.11.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 666 +++++++++++++++++++++++----------------------- package.json | 6 +- 2 files changed, 342 insertions(+), 330 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1bca191d1..059ae8765 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,18 +75,18 @@ }, "devDependencies": { "@stylistic/stylelint-plugin": "^5.0.1", - "babel-jest": "^30.3.0", + "babel-jest": "^30.4.1", "babel-plugin-transform-import-meta": "^2.3.3", "eslint": "9.7", "eslint-plugin-jest": "^29.15.1", "eslint-plugin-react": "^7.37.5", "globals": "^16.4.0", - "jest": "^30.3.0", + "jest": "^30.4.2", "jest-expect-message": "^1.1.3", "jsdom": "^28.1.0", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^17.6.0", + "stylelint": "^17.11.0", "stylelint-config-recess-order": "^7.7.0", "stylelint-config-recommended": "^18.0.0", "supertest": "^7.1.4", @@ -3493,17 +3493,17 @@ } }, "node_modules/@jest/console": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", - "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", "slash": "^3.0.0" }, "engines": { @@ -3511,38 +3511,39 @@ } }, "node_modules/@jest/core": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", - "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.3.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "ci-info": "^4.2.0", "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", - "jest-changed-files": "30.3.0", - "jest-config": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-resolve-dependencies": "30.3.0", - "jest-runner": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "jest-watcher": "30.3.0", - "pretty-format": "30.3.0", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", "slash": "^3.0.0" }, "engines": { @@ -3558,9 +3559,9 @@ } }, "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", "dev": true, "license": "MIT", "engines": { @@ -3568,39 +3569,39 @@ } }, "node_modules/@jest/environment": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", - "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", - "jest-mock": "30.3.0" + "jest-mock": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.3.0", - "jest-snapshot": "30.3.0" + "expect": "30.4.1", + "jest-snapshot": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", - "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3611,18 +3612,18 @@ } }, "node_modules/@jest/fake-timers": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", - "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", - "@sinonjs/fake-timers": "^15.0.0", + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", "@types/node": "*", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -3639,47 +3640,47 @@ } }, "node_modules/@jest/globals": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", - "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/types": "30.3.0", - "jest-mock": "30.3.0" + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "jest-regex-util": "30.0.1" + "jest-regex-util": "30.4.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/reporters": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", - "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@jridgewell/trace-mapping": "^0.3.25", "@types/node": "*", "chalk": "^4.1.2", @@ -3692,9 +3693,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^5.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", "slash": "^3.0.0", "string-length": "^4.0.2", "v8-to-istanbul": "^9.0.1" @@ -3712,9 +3713,9 @@ } }, "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3725,13 +3726,13 @@ } }, "node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "natural-compare": "^1.4.0" @@ -3756,14 +3757,14 @@ } }, "node_modules/@jest/test-result": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", - "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", "@types/istanbul-lib-coverage": "^2.0.6", "collect-v8-coverage": "^1.0.2" }, @@ -3772,15 +3773,15 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", - "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.3.0", + "@jest/test-result": "30.4.1", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", + "jest-haste-map": "30.4.1", "slash": "^3.0.0" }, "engines": { @@ -3788,23 +3789,23 @@ } }, "node_modules/@jest/transform": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", - "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@jridgewell/trace-mapping": "^0.3.25", "babel-plugin-istanbul": "^7.0.1", "chalk": "^4.1.2", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", "pirates": "^4.0.7", "slash": "^3.0.0", "write-file-atomic": "^5.0.1" @@ -3814,14 +3815,14 @@ } }, "node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", @@ -4543,9 +4544,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz", - "integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4915,9 +4916,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", "dev": true, "license": "ISC" }, @@ -5538,16 +5539,16 @@ } }, "node_modules/babel-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", - "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "30.3.0", + "@jest/transform": "30.4.1", "@types/babel__core": "^7.20.5", "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.3.0", + "babel-preset-jest": "30.4.0", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "slash": "^3.0.0" @@ -5580,9 +5581,9 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", - "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", "dev": true, "license": "MIT", "dependencies": { @@ -5673,13 +5674,13 @@ } }, "node_modules/babel-preset-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", - "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "30.3.0", + "babel-plugin-jest-hoist": "30.4.0", "babel-preset-current-node-syntax": "^1.2.0" }, "engines": { @@ -7334,18 +7335,18 @@ } }, "node_modules/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.3.0", + "@jest/expect-utils": "30.4.1", "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -9251,16 +9252,16 @@ } }, "node_modules/jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", - "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.3.0", - "@jest/types": "30.3.0", + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", "import-local": "^3.2.0", - "jest-cli": "30.3.0" + "jest-cli": "30.4.2" }, "bin": { "jest": "bin/jest.js" @@ -9278,14 +9279,14 @@ } }, "node_modules/jest-changed-files": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", - "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", "dev": true, "license": "MIT", "dependencies": { "execa": "^5.1.1", - "jest-util": "30.3.0", + "jest-util": "30.4.1", "p-limit": "^3.1.0" }, "engines": { @@ -9293,29 +9294,29 @@ } }, "node_modules/jest-circus": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", - "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "co": "^4.6.0", "dedent": "^1.6.0", "is-generator-fn": "^2.1.0", - "jest-each": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", "p-limit": "^3.1.0", - "pretty-format": "30.3.0", + "pretty-format": "30.4.1", "pure-rand": "^7.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.6" @@ -9325,21 +9326,21 @@ } }, "node_modules/jest-cli": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", - "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", "chalk": "^4.1.2", "exit-x": "^0.2.2", "import-local": "^3.2.0", - "jest-config": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", "yargs": "^17.7.2" }, "bin": { @@ -9358,33 +9359,33 @@ } }, "node_modules/jest-config": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", - "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.3.0", - "@jest/types": "30.3.0", - "babel-jest": "30.3.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "deepmerge": "^4.3.1", "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-circus": "30.3.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-runner": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", "parse-json": "^5.2.0", - "pretty-format": "30.3.0", + "pretty-format": "30.4.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -9409,25 +9410,25 @@ } }, "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.3.0", + "@jest/diff-sequences": "30.4.0", "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "pretty-format": "30.3.0" + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-docblock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", - "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", "dev": true, "license": "MIT", "dependencies": { @@ -9438,36 +9439,36 @@ } }, "node_modules/jest-each": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", - "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "chalk": "^4.1.2", - "jest-util": "30.3.0", - "pretty-format": "30.3.0" + "jest-util": "30.4.1", + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-environment-node": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", - "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", - "jest-mock": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0" + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -9481,20 +9482,20 @@ "license": "MIT" }, "node_modules/jest-haste-map": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", - "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", "anymatch": "^3.1.3", "fb-watchman": "^2.0.2", "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", "picomatch": "^4.0.3", "walker": "^1.0.8" }, @@ -9506,49 +9507,50 @@ } }, "node_modules/jest-leak-detector": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", - "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "pretty-format": "30.3.0" + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", "picomatch": "^4.0.3", - "pretty-format": "30.3.0", + "pretty-format": "30.4.1", "slash": "^3.0.0", "stack-utils": "^2.0.6" }, @@ -9557,15 +9559,15 @@ } }, "node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", - "jest-util": "30.3.0" + "jest-util": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -9590,9 +9592,9 @@ } }, "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", "dev": true, "license": "MIT", "engines": { @@ -9600,18 +9602,18 @@ } }, "node_modules/jest-resolve": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", - "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", + "jest-haste-map": "30.4.1", "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", "slash": "^3.0.0", "unrs-resolver": "^1.7.11" }, @@ -9620,46 +9622,46 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", - "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.3.0" + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-runner": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", - "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.3.0", - "@jest/environment": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "emittery": "^0.13.1", "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-leak-detector": "30.3.0", - "jest-message-util": "30.3.0", - "jest-resolve": "30.3.0", - "jest-runtime": "30.3.0", - "jest-util": "30.3.0", - "jest-watcher": "30.3.0", - "jest-worker": "30.3.0", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -9668,32 +9670,32 @@ } }, "node_modules/jest-runtime": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", - "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/globals": "30.3.0", + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", "@jest/source-map": "30.0.1", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "cjs-module-lexer": "^2.1.0", "collect-v8-coverage": "^1.0.2", "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -9702,9 +9704,9 @@ } }, "node_modules/jest-snapshot": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", "dev": true, "license": "MIT", "dependencies": { @@ -9713,20 +9715,20 @@ "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", + "@jest/expect-utils": "30.4.1", "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "babel-preset-current-node-syntax": "^1.2.0", "chalk": "^4.1.2", - "expect": "30.3.0", + "expect": "30.4.1", "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "pretty-format": "30.3.0", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", "semver": "^7.7.2", "synckit": "^0.11.8" }, @@ -9735,9 +9737,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -9748,13 +9750,13 @@ } }, "node_modules/jest-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", - "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", @@ -9766,18 +9768,18 @@ } }, "node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "camelcase": "^6.3.0", "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "30.3.0" + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -9797,19 +9799,19 @@ } }, "node_modules/jest-watcher": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", - "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "emittery": "^0.13.1", - "jest-util": "30.3.0", + "jest-util": "30.4.1", "string-length": "^4.0.2" }, "engines": { @@ -9817,15 +9819,15 @@ } }, "node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", + "jest-util": "30.4.1", "merge-stream": "^2.0.0", "supports-color": "^8.1.1" }, @@ -10223,9 +10225,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -11546,15 +11548,16 @@ } }, "node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", + "@jest/schemas": "30.4.1", "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -11748,13 +11751,22 @@ "react-dom": ">= 16.8 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/react-is": { + "node_modules/react-is-18": { + "name": "react-is", "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "license": "MIT" }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", + "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", + "dev": true, + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", @@ -12789,9 +12801,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "17.10.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.10.0.tgz", - "integrity": "sha512-cI7I6HHEYOHHVNVci+s92WlA3QfmNhjwFdgCgYV3TLEysilOjk+B3EFxMED1xY9GYB0Kre3OD+mSLj19VLTIvA==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.11.0.tgz", + "integrity": "sha512-/3czzmbF9XdGWvReDF3Ex4R23Ajolo7j8RB2bFNEqk6Ht356nlpVV+G5bG2Qt8AW1ofJzXztBRDnAtd7cgowWA==", "dev": true, "funding": [ { @@ -12836,7 +12848,7 @@ "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", - "string-width": "^8.2.0", + "string-width": "^8.2.1", "supports-hyperlinks": "^4.4.0", "svg-tags": "^1.0.0", "table": "^6.9.0", diff --git a/package.json b/package.json index 8603b38f5..f4027a1c3 100644 --- a/package.json +++ b/package.json @@ -151,18 +151,18 @@ }, "devDependencies": { "@stylistic/stylelint-plugin": "^5.0.1", - "babel-jest": "^30.3.0", + "babel-jest": "^30.4.1", "babel-plugin-transform-import-meta": "^2.3.3", "eslint": "9.7", "eslint-plugin-jest": "^29.15.1", "eslint-plugin-react": "^7.37.5", "globals": "^16.4.0", - "jest": "^30.3.0", + "jest": "^30.4.2", "jest-expect-message": "^1.1.3", "jsdom": "^28.1.0", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^17.6.0", + "stylelint": "^17.11.0", "stylelint-config-recess-order": "^7.7.0", "stylelint-config-recommended": "^18.0.0", "supertest": "^7.1.4", From 793adafe3216a55d8ba5674d74ba2d0b69392982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 10 May 2026 19:35:09 +0200 Subject: [PATCH 53/77] fix work on first load for images inside other nodes --- client/components/codeEditor/codeEditor.jsx | 16 ++++++++++++---- .../codeEditor/extensions/customHighlight.js | 1 - 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index ef28bf509..90ab1fde0 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -17,7 +17,17 @@ import { crosshairCursor, } from '@codemirror/view'; import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state'; -import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, foldEffect, foldState, syntaxHighlighting } from '@codemirror/language'; +import { + foldAll as foldAllCmd, + unfoldAll as unfoldAllCmd, + foldGutter, + foldKeymap, + foldEffect, + foldState, + syntaxHighlighting, + syntaxTree, + ensureSyntaxTree +} from '@codemirror/language'; import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands'; import { languages } from '@codemirror/language-data'; import { css } from '@codemirror/lang-css'; @@ -41,7 +51,6 @@ import { generalKeymap, markdownKeymap } from './extensions/customKeyMaps.js'; import foldOnPages from './extensions/customFolding.js'; import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './extensions/customHighlight.js'; import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './extensions/legacyCustomHighlight.js'; -import { syntaxTree } from '@codemirror/language'; const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; @@ -90,7 +99,7 @@ const createHighlightPlugin = (renderer, tab)=>{ let pageCount = 1; let snippetCount = 0; - const tree = syntaxTree(view.state); + const tree = ensureSyntaxTree(view.state, view.state.doc.length, 50) || syntaxTree(view.state); tree.iterate({ enter : (node)=>{ if(node.name === 'Image') { @@ -120,7 +129,6 @@ const createHighlightPlugin = (renderer, tab)=>{ const to = line.from + tok.to; const attrs = {}; - console.log(tok); // attach URL only for links if(tok.type === 'Image' && tok.url) { diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 39103fdac..ad562e8f1 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -204,7 +204,6 @@ export function tokenizeCustomMarkdown(text) { let match; while ((match = injectionRegex.exec(lineText)) !== null) { - console.log(match.indices[1][0]); tokens.push({ line : lineNumber, from : match.indices[1][0], From 70d9614fb18325718b302e3008a603e5d0f4cad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 10 May 2026 19:39:26 +0200 Subject: [PATCH 54/77] lint --- client/components/codeEditor/codeEditor.jsx | 32 ++++++--------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 90ab1fde0..c70e1109b 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -17,13 +17,13 @@ import { crosshairCursor, } from '@codemirror/view'; import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state'; -import { - foldAll as foldAllCmd, +import { + foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, - foldGutter, - foldKeymap, - foldEffect, - foldState, + foldGutter, + foldKeymap, + foldEffect, + foldState, syntaxHighlighting, syntaxTree, ensureSyntaxTree @@ -152,30 +152,14 @@ const createHighlightPlugin = (renderer, tab)=>{ class : `cm-${tok.type}` }).range(line.from) ); - if(tok.type === 'pageLine' && tab === 'brewText') { pageCount++; if(line.from === 0) pageCount--; - - decos.push( - Decoration.line({ - attributes : { - 'data-page-number' : pageCount - } - }).range(line.from) - ); + decos.push(Decoration.line({ attributes: { 'data-page-number': pageCount } }).range(line.from)); } - if(tok.type === 'snippetLine' && tab === 'brewSnippets') { snippetCount++; - - decos.push( - Decoration.line({ - attributes : { - 'data-page-number' : snippetCount - } - }).range(line.from) - ); + decos.push(Decoration.line({ attributes: { 'data-page-number': snippetCount } }).range(line.from)); } } }); From aac384390b74428e1b9f0ebf81ad7fe30baa1cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 10 May 2026 19:41:11 +0200 Subject: [PATCH 55/77] lint --- client/components/codeEditor/codeEditor.jsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index c70e1109b..ae91361cb 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -123,13 +123,11 @@ const createHighlightPlugin = (renderer, tab)=>{ tokens.forEach((tok)=>{ const line = view.state.doc.line(tok.line + 1); - // INLINE TOKENS (marks) if(tok.from != null && tok.to != null && tok.from < tok.to) { const from = line.from + tok.from; const to = line.from + tok.to; const attrs = {}; - // attach URL only for links if(tok.type === 'Image' && tok.url) { attrs['data-url'] = tok.url; @@ -143,10 +141,7 @@ const createHighlightPlugin = (renderer, tab)=>{ : {}) }).range(from, to) ); - } - - // LINE TOKENS - else { + } else { decos.push( Decoration.line({ class : `cm-${tok.type}` @@ -168,9 +163,7 @@ const createHighlightPlugin = (renderer, tab)=>{ return Decoration.set(decos); } }, - { - decorations : (v)=>v.decorations - } + { decorations: (v)=>v.decorations } ); }; From b39502d806a2caf337196806575558fc57256aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 10 May 2026 19:42:02 +0200 Subject: [PATCH 56/77] lint more --- client/components/codeEditor/codeEditor.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index ae91361cb..ba60bf125 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -119,7 +119,6 @@ const createHighlightPlugin = (renderer, tab)=>{ } }); - tokens.forEach((tok)=>{ const line = view.state.doc.line(tok.line + 1); @@ -450,7 +449,6 @@ const CodeEditor = forwardRef( injectText : (text)=>{ const view = viewRef.current; - view.dispatch( view.state.replaceSelection(text) ); From 2971fe0679c482a789c01e771a543bdab68b3797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 11:47:00 +0200 Subject: [PATCH 57/77] add transition delay --- client/components/codeEditor/codeEditor.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index da31e4c4d..16d99bc30 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -174,7 +174,7 @@ z-index:1000; pointer-events: none; opacity:0; - transition:0.2s opacity; + transition:0.2s opacity 0.5s; } &::before{ From 0f7aa0ad49aed5b2cd50110a2196d26b23868707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 11:49:41 +0200 Subject: [PATCH 58/77] change tok for token --- client/components/codeEditor/codeEditor.jsx | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index ba60bf125..254d2977d 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -119,22 +119,22 @@ const createHighlightPlugin = (renderer, tab)=>{ } }); - tokens.forEach((tok)=>{ - const line = view.state.doc.line(tok.line + 1); + tokens.forEach((token)=>{ + const line = view.state.doc.line(token.line + 1); - if(tok.from != null && tok.to != null && tok.from < tok.to) { - const from = line.from + tok.from; - const to = line.from + tok.to; + if(token.from != null && token.to != null && token.from < token.to) { + const from = line.from + token.from; + const to = line.from + token.to; const attrs = {}; - if(tok.type === 'Image' && tok.url) { + if(token.type === 'Image' && token.url) { - attrs['data-url'] = tok.url; + attrs['data-url'] = token.url; } decos.push( Decoration.mark({ - class : `cm-${tok.type}`, + class : `cm-${token.type}`, ...(Object.keys(attrs).length ? { attributes: attrs } : {}) @@ -143,15 +143,15 @@ const createHighlightPlugin = (renderer, tab)=>{ } else { decos.push( Decoration.line({ - class : `cm-${tok.type}` + class : `cm-${token.type}` }).range(line.from) ); - if(tok.type === 'pageLine' && tab === 'brewText') { + if(token.type === 'pageLine' && tab === 'brewText') { pageCount++; if(line.from === 0) pageCount--; decos.push(Decoration.line({ attributes: { 'data-page-number': pageCount } }).range(line.from)); } - if(tok.type === 'snippetLine' && tab === 'brewSnippets') { + if(token.type === 'snippetLine' && tab === 'brewSnippets') { snippetCount++; decos.push(Decoration.line({ attributes: { 'data-page-number': snippetCount } }).range(line.from)); } From df4c59cbd6b6a605eb1dcee77269d571e81ee98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 12:06:21 +0200 Subject: [PATCH 59/77] move highlight plugin into its own file --- client/components/codeEditor/codeEditor.jsx | 130 +---------------- .../codeEditor/extensions/customHighlight.js | 132 +++++++++++++++++- 2 files changed, 130 insertions(+), 132 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 254d2977d..c5cf669bd 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -1,4 +1,4 @@ -/* eslint max-lines: ["error", { "max": 400 }] */ +/* eslint max-lines: ["error", { "max": 405 }] */ import './codeEditor.less'; import React, { useEffect, useRef, forwardRef, useImperativeHandle } from 'react'; @@ -10,7 +10,6 @@ import { highlightActiveLine, scrollPastEnd, Decoration, - ViewPlugin, drawSelection, dropCursor, rectangularSelection, @@ -18,15 +17,12 @@ import { } from '@codemirror/view'; import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state'; import { - foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, foldEffect, foldState, syntaxHighlighting, - syntaxTree, - ensureSyntaxTree } from '@codemirror/language'; import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands'; import { languages } from '@codemirror/language-data'; @@ -49,123 +45,11 @@ const highlightCompartment = new Compartment(); import { generalKeymap, markdownKeymap } from './extensions/customKeyMaps.js'; import foldOnPages from './extensions/customFolding.js'; -import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './extensions/customHighlight.js'; -import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './extensions/legacyCustomHighlight.js'; +import { customHighlightPlugin, customHighlightStyle } from './extensions/customHighlight.js'; +import { legacyCustomHighlightStyle } from './extensions/legacyCustomHighlight.js'; const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; -function getUrl(node, doc) { - let url = null; - - const cursor = node.node.cursor(); - - if(cursor.firstChild()) { - do { - if(cursor.name === 'URL') { - url = doc.sliceString(cursor.from, cursor.to); - break; - } - } while (cursor.nextSibling()); - } - - return url; -} -const createHighlightPlugin = (renderer, tab)=>{ - //this function takes the custom tokens created in the tokenize function in customhighlight files - //takes the tokens defined by that function and assigns classes to them - //it also creates page number and snippet number widgets - - let tokenize; - - if(tab === 'brewStyles') { - tokenize = tokenizeCustomCSS; - } else { - tokenize = renderer === 'V3' ? tokenizeCustomMarkdown : legacyTokenizeCustomMarkdown; - } - - return ViewPlugin.fromClass( - class { - constructor(view) { - this.decorations = this.buildDecorations(view); - } - update(update) { - if(update.docChanged) { - this.decorations = this.buildDecorations(update.view); - } - } - buildDecorations(view) { - const decos = []; - const tokens = tokenize(view.state.doc.toString()); - let pageCount = 1; - let snippetCount = 0; - - const tree = ensureSyntaxTree(view.state, view.state.doc.length, 50) || syntaxTree(view.state); - tree.iterate({ - enter : (node)=>{ - if(node.name === 'Image') { - const url = getUrl(node, view.state.doc); - - if(!url) return; - - decos.push( - Decoration.mark({ - class : 'cm-image', - attributes : { - 'style' : `--preview-img:url(${url});` - } - }).range(node.from, node.to) - ); - } - } - }); - - tokens.forEach((token)=>{ - const line = view.state.doc.line(token.line + 1); - - if(token.from != null && token.to != null && token.from < token.to) { - const from = line.from + token.from; - const to = line.from + token.to; - - const attrs = {}; - if(token.type === 'Image' && token.url) { - - attrs['data-url'] = token.url; - } - - decos.push( - Decoration.mark({ - class : `cm-${token.type}`, - ...(Object.keys(attrs).length - ? { attributes: attrs } - : {}) - }).range(from, to) - ); - } else { - decos.push( - Decoration.line({ - class : `cm-${token.type}` - }).range(line.from) - ); - if(token.type === 'pageLine' && tab === 'brewText') { - pageCount++; - if(line.from === 0) pageCount--; - decos.push(Decoration.line({ attributes: { 'data-page-number': pageCount } }).range(line.from)); - } - if(token.type === 'snippetLine' && tab === 'brewSnippets') { - snippetCount++; - decos.push(Decoration.line({ attributes: { 'data-page-number': snippetCount } }).range(line.from)); - } - } - }); - - decos.sort((a, b)=>a.from - b.from || a.to - b.to); - return Decoration.set(decos); - } - }, - { decorations: (v)=>v.decorations } - ); -}; - const setProgrammaticCursorLine = StateEffect.define(); const programmaticCursorLineField = StateField.define({ @@ -272,8 +156,6 @@ const CodeEditor = forwardRef( ? syntaxHighlighting(customHighlightStyle) : syntaxHighlighting(legacyCustomHighlightStyle); - const customHighlightPlugin = createHighlightPlugin(renderer, tab); - const languageExtension = language === 'css' ? css() : [markdown({ base: markdownLanguage, codeLanguages: languages }), html({ autoCloseTags: true })]; const themeExtension = Array.isArray(themes[editorTheme]) ? themes[editorTheme] : themes[editorTheme] || themes['default']; @@ -296,7 +178,7 @@ const CodeEditor = forwardRef( }), //highlights - highlightCompartment.of([customHighlightPlugin, highlightExtension]), + highlightCompartment.of([customHighlightPlugin(renderer, tab), highlightExtension]), themeCompartment.of(themeExtension), highlightActiveLine(), highlightActiveLineGutter(), @@ -437,10 +319,8 @@ const CodeEditor = forwardRef( ? syntaxHighlighting(customHighlightStyle) : syntaxHighlighting(legacyCustomHighlightStyle); - const customHighlightPlugin = createHighlightPlugin(renderer, tab); - view.dispatch({ - effects : highlightCompartment.reconfigure([customHighlightPlugin, highlightExtension]), + effects : highlightCompartment.reconfigure([customHighlightPlugin(renderer, tab), highlightExtension]), }); }, [renderer, tab]); diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index ad562e8f1..afccea2d6 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -1,5 +1,15 @@ +/* eslint max-lines: ["error", { "max": 450 }] */ import { HighlightStyle } from '@codemirror/language'; import { tags } from '@lezer/highlight'; +import { legacyTokenizeCustomMarkdown } from './legacyCustomHighlight'; +import { + Decoration, + ViewPlugin, +} from '@codemirror/view'; +import { + syntaxTree, + ensureSyntaxTree +} from '@codemirror/language'; // Making the tokens const customTags = { @@ -23,7 +33,7 @@ const customTags = { variable : 'variable', }; -export function tokenizeCustomMarkdown(text) { +function tokenizeCustomMarkdown(text) { const tokens = []; const lines = text.split('\n'); @@ -86,8 +96,8 @@ export function tokenizeCustomMarkdown(text) { if(/\~/.test(lineText)) { const strikethroughRegex = /~(?!\s)(.+?)(?{ + if(node.name === 'Image') { + const url = getUrl(node, view.state.doc); + + if(!url) return; + + decos.push( + Decoration.mark({ + class : 'cm-image', + attributes : { + 'style' : `--preview-img:url(${url});` + } + }).range(node.from, node.to) + ); + } + } + }); + + tokens.forEach((token)=>{ + const line = view.state.doc.line(token.line + 1); + + if(token.from != null && token.to != null && token.from < token.to) { + const from = line.from + token.from; + const to = line.from + token.to; + + const attrs = {}; + if(token.type === 'Image' && token.url) { + + attrs['data-url'] = token.url; + } + + decos.push( + Decoration.mark({ + class : `cm-${token.type}`, + ...(Object.keys(attrs).length + ? { attributes: attrs } + : {}) + }).range(from, to) + ); + } else { + decos.push( + Decoration.line({ + class : `cm-${token.type}` + }).range(line.from) + ); + if(token.type === 'pageLine' && tab === 'brewText') { + pageCount++; + if(line.from === 0) pageCount--; + decos.push(Decoration.line({ attributes: { 'data-page-number': pageCount } }).range(line.from)); + } + if(token.type === 'snippetLine' && tab === 'brewSnippets') { + snippetCount++; + decos.push(Decoration.line({ attributes: { 'data-page-number': snippetCount } }).range(line.from)); + } + } + }); + + decos.sort((a, b)=>a.from - b.from || a.to - b.to); + return Decoration.set(decos); + } + }, + { decorations: (v)=>v.decorations } + ); +}; \ No newline at end of file From c66310fbe7401cc1de7e0a0fcb2d4b8074830c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 12:09:43 +0200 Subject: [PATCH 60/77] linting --- client/components/codeEditor/extensions/customHighlight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index afccea2d6..63251e49e 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -316,7 +316,6 @@ export const customHighlightStyle = HighlightStyle.define([ ]); - function getUrl(node, doc) { let url = null; @@ -333,6 +332,7 @@ function getUrl(node, doc) { return url; } + export function customHighlightPlugin(renderer, tab) { //this function takes the custom tokens created in the tokenize function in customhighlight files //takes the tokens defined by that function and assigns classes to them From 097b941580dad433f1c0c181b3087c7072aa7574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 12:45:46 +0200 Subject: [PATCH 61/77] move to use html img element --- client/components/codeEditor/codeEditor.less | 40 ++++++------------ .../codeEditor/extensions/customHighlight.js | 40 ++++++++++++++++-- client/icons/Broken-image-icon-in-Chrome.png | Bin 0 -> 9227 bytes 3 files changed, 48 insertions(+), 32 deletions(-) create mode 100644 client/icons/Broken-image-icon-in-Chrome.png diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index 16d99bc30..fbb2cacaa 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -161,44 +161,28 @@ outline : 1px inset #00000055 !important; } - .cm-image[style] { - position: relative; - - &::before, &::after { - content:""; + .cm-image { + position:relative; + + .cm-preview { + object-fit: contain; position:absolute; bottom:0; left:0; - translate:0 100%; - display:block; - z-index:1000; - pointer-events: none; - opacity:0; - transition:0.2s opacity 0.5s; - } - - &::before{ + height:200px; width:200px; height:200px; background-color: #fff; border-radius:10px; border:3px solid grey; + pointer-events: none; + opacity:0; + transition:0.2s opacity 0.5s; + translate:0 100%; + z-index:1000; } - &::after { - width:190px; - height:190px; - //padding of 5px + 3px border: 8px - translate:8px calc(100% + 8px); - background-image: var(--preview-img); - background-size:contain; - background-repeat:no-repeat; - background-position:center; - filter:drop-shadow(0 0 5px #0008); - } - - &:hover::before, - &:hover::after { + &:hover .cm-preview { opacity:1; } } diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 63251e49e..2a63bb398 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -333,6 +333,33 @@ function getUrl(node, doc) { return url; } +import { WidgetType } from '@codemirror/view'; + +class ImageWidget extends WidgetType { + constructor(url) { + super(); + this.url = url; + } + + toDOM() { + const img = document.createElement('img'); + img.loading = "lazy"; + img.className = 'cm-preview'; + img.src = this.url; + + + img.onerror = ()=>{ + img.src = 'client/icons/Broken-image-icon-in-Chrome.png'; + }; + + return img; + } + + eq(other) { + return other.url === this.url; + } +} + export function customHighlightPlugin(renderer, tab) { //this function takes the custom tokens created in the tokenize function in customhighlight files //takes the tokens defined by that function and assigns classes to them @@ -368,16 +395,21 @@ export function customHighlightPlugin(renderer, tab) { if(node.name === 'Image') { const url = getUrl(node, view.state.doc); + console.log(node.node.lastChild.from); + if(!url) return; decos.push( Decoration.mark({ - class : 'cm-image', - attributes : { - 'style' : `--preview-img:url(${url});` - } + class : 'cm-image' }).range(node.from, node.to) ); + decos.push( + Decoration.widget({ + widget : new ImageWidget(url), + side : 1 + }).range(node.node.lastChild.from) + ); } } }); diff --git a/client/icons/Broken-image-icon-in-Chrome.png b/client/icons/Broken-image-icon-in-Chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4d4fd4058dafcff3b47f056aca571b739b6163 GIT binary patch literal 9227 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 F@*masY7PJZ literal 0 HcmV?d00001 From 3c4bafd1ad7ab139e3d962445faad5fa93875d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 12:46:10 +0200 Subject: [PATCH 62/77] eslint changes --- client/components/codeEditor/extensions/customHighlight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 2a63bb398..79a4a18c9 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -1,4 +1,4 @@ -/* eslint max-lines: ["error", { "max": 450 }] */ +/* eslint max-lines: ["error", { "max": 500 }] */ import { HighlightStyle } from '@codemirror/language'; import { tags } from '@lezer/highlight'; import { legacyTokenizeCustomMarkdown } from './legacyCustomHighlight'; From d630882f18f03c21fd4be0ba20c7a125c963a168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 12:51:45 +0200 Subject: [PATCH 63/77] add padding --- client/components/codeEditor/codeEditor.less | 1 + 1 file changed, 1 insertion(+) diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index fbb2cacaa..f3658f290 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -172,6 +172,7 @@ height:200px; width:200px; height:200px; + padding:5px; background-color: #fff; border-radius:10px; border:3px solid grey; From 0bee33efc0ff85c3e231a67a6b158c13dd5dc5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 13 May 2026 20:58:40 +0200 Subject: [PATCH 64/77] file removal --- client/icons/Broken-image-icon-in-Chrome.png | Bin 9227 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 client/icons/Broken-image-icon-in-Chrome.png diff --git a/client/icons/Broken-image-icon-in-Chrome.png b/client/icons/Broken-image-icon-in-Chrome.png deleted file mode 100644 index 6c4d4fd4058dafcff3b47f056aca571b739b6163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9227 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 F@*masY7PJZ From 26ef5b10a01b787d412ffeac9b69a28bb30ba3c1 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 14 May 2026 20:36:44 +1200 Subject: [PATCH 65/77] Check for failed patches --- server/homebrew.api.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index c47e3640e..237d06af7 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -397,19 +397,26 @@ const api = { return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } + let result = []; try { const patches = parsePatch(brewFromClient.patches); // Patch to a throwaway variable while parallelizing - we're more concerned with error/no error. - const patchedResult = decodeURI(applyPatches(patches, encodeURI(brewFromServer.text))[0]); - if(patchedResult != brewFromClient.text) + result = applyPatches(patches, encodeURI(brewFromServer.text)); + const failedPatches = patches.map((patch, index)=>{if(!result[1][index]){ return patch; }}); + if(failedPatches > 0){ + throw (`Patch failure: ${failedPatches}/${result[1].length} did not apply`); + } + if(decodeURI(result[0]) != brewFromClient.text){ throw ('Patches did not apply cleanly, text mismatch detected'); + } // brew.text = applyPatches(patches, brewFromServer.text)[0]; } catch (err) { debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); console.error('Failed to apply patches:', { - //patches : brewFromClient.patches, - brewId : brewFromClient.editId || 'unknown', - error : err + patches : brewFromClient.patches, + result : result, + brewId : brewFromClient.editId || 'unknown', + error : err }); // While running in parallel, don't throw the error upstream. // throw err; // rethrow to preserve the 500 behavior From 16948f451fe86ec6dd4d266c204b9538d80f2010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 14 May 2026 11:08:09 +0200 Subject: [PATCH 66/77] add a comment --- client/components/codeEditor/extensions/customHighlight.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 79a4a18c9..410bfe0d1 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -395,7 +395,10 @@ export function customHighlightPlugin(renderer, tab) { if(node.name === 'Image') { const url = getUrl(node, view.state.doc); - console.log(node.node.lastChild.from); + const widgetPosition = node.node.lastChild.from; + //this is not exactly standard, but should hold, + //and is the shortest way i could find of positioning + //the image inside the cm-image node if(!url) return; @@ -408,7 +411,7 @@ export function customHighlightPlugin(renderer, tab) { Decoration.widget({ widget : new ImageWidget(url), side : 1 - }).range(node.node.lastChild.from) + }).range(widgetPosition) ); } } From eec4cf816757775f669b143e7856f303aa733002 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 14 May 2026 21:17:18 +1200 Subject: [PATCH 67/77] Comment out excessive logging items --- server/homebrew.api.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 237d06af7..afd7d8af9 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -413,10 +413,10 @@ const api = { } catch (err) { debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); console.error('Failed to apply patches:', { - patches : brewFromClient.patches, - result : result, - brewId : brewFromClient.editId || 'unknown', - error : err + // patches : brewFromClient.patches, + // result : result, + brewId : brewFromClient.editId || 'unknown', + error : err }); // While running in parallel, don't throw the error upstream. // throw err; // rethrow to preserve the 500 behavior From fd9df5f6ae287869f7d9ac5751a064bbf75e3f80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 03:23:59 +0000 Subject: [PATCH 68/77] Bump the prod-dependencies group with 2 updates Bumps the prod-dependencies group with 2 updates: [@codemirror/view](https://github.com/codemirror/view) and [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router). Updates `@codemirror/view` from 6.42.1 to 6.43.0 - [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/view/commits) Updates `react-router` from 7.15.0 to 7.15.1 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.1/packages/react-router) --- updated-dependencies: - dependency-name: "@codemirror/view" dependency-version: 6.43.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies - dependency-name: react-router dependency-version: 7.15.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 059ae8765..d724650f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.42.1", + "@codemirror/view": "^6.43.0", "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^20.1.0", "@lezer/highlight": "^1.2.3", @@ -69,7 +69,7 @@ "react": "^19.2.6", "react-dom": "^19.2.6", "react-frame-component": "^5.3.2", - "react-router": "^7.15.0", + "react-router": "^7.15.1", "sanitize-filename": "1.6.4", "superagent": "^10.2.1" }, @@ -2545,9 +2545,9 @@ "license": "MIT" }, "node_modules/@codemirror/view": { - "version": "6.42.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.42.1.tgz", - "integrity": "sha512-ToN3oFc0nsxNUYVF5P0ztLgbC4UPPjPtA9aKYhkOKQaZASpOUo6ISXyQLP66ctVwlDc+j6Jv0uK5IFALkiXztg==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", + "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.6.0", @@ -11777,9 +11777,9 @@ } }, "node_modules/react-router": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.0.tgz", - "integrity": "sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ==", + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz", + "integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", diff --git a/package.json b/package.json index f4027a1c3..90342b6a4 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.42.1", + "@codemirror/view": "^6.43.0", "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^20.1.0", "@lezer/highlight": "^1.2.3", @@ -145,7 +145,7 @@ "react": "^19.2.6", "react-dom": "^19.2.6", "react-frame-component": "^5.3.2", - "react-router": "^7.15.0", + "react-router": "^7.15.1", "sanitize-filename": "1.6.4", "superagent": "^10.2.1" }, From cd747924025c3783ff91eaa68e7038591e59f57e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 10:28:19 +0000 Subject: [PATCH 69/77] Bump stylelint in the dev-dependencies group across 1 directory Bumps the dev-dependencies group with 1 update in the / directory: [stylelint](https://github.com/stylelint/stylelint). Updates `stylelint` from 17.11.0 to 17.11.1 - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md) - [Commits](https://github.com/stylelint/stylelint/compare/17.11.0...17.11.1) --- updated-dependencies: - dependency-name: stylelint dependency-version: 17.11.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 27 ++++++++------------------- package.json | 2 +- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index d724650f5..4df84beb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,7 @@ "jsdom": "^28.1.0", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^17.11.0", + "stylelint": "^17.11.1", "stylelint-config-recess-order": "^7.7.0", "stylelint-config-recommended": "^18.0.0", "supertest": "^7.1.4", @@ -8926,16 +8926,6 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -11420,9 +11410,9 @@ } }, "node_modules/postcss": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", - "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "funding": [ { "type": "opencollective", @@ -12801,9 +12791,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.11.0.tgz", - "integrity": "sha512-/3czzmbF9XdGWvReDF3Ex4R23Ajolo7j8RB2bFNEqk6Ht356nlpVV+G5bG2Qt8AW1ofJzXztBRDnAtd7cgowWA==", + "version": "17.11.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.11.1.tgz", + "integrity": "sha512-+smN/HqVTggUx3iuAzOi9fPh8SrH+cJWlZrYVldXoJ06orWBhZ4Ue/QEp64oei6pVrAh4w3tG+Y12Vw7MbCFRQ==", "dev": true, "funding": [ { @@ -12838,13 +12828,12 @@ "html-tags": "^5.1.0", "ignore": "^7.0.5", "import-meta-resolve": "^4.2.0", - "is-plain-object": "^5.0.0", "mathml-tag-names": "^4.0.0", "meow": "^14.1.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.5.13", + "postcss": "^8.5.14", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", diff --git a/package.json b/package.json index 90342b6a4..ffd9ac423 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "jsdom": "^28.1.0", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^17.11.0", + "stylelint": "^17.11.1", "stylelint-config-recess-order": "^7.7.0", "stylelint-config-recommended": "^18.0.0", "supertest": "^7.1.4", From 074af5e8e0dba553ba3e30c75cb828f7acea5fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 16 May 2026 16:28:53 +0200 Subject: [PATCH 70/77] more solid block tokenizing --- client/components/codeEditor/codeEditor.less | 3 ++- .../codeEditor/extensions/customHighlight.js | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index f3658f290..ca77f29a8 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -63,7 +63,8 @@ &.term { color : rgb(96, 117, 143); } &.definition { color : rgb(97, 57, 178); } } - .cm-block:not(.cm-comment) { + .cm-block:not(.cm-comment), + .cm-block:not(.cm-comment) * { font-weight : bold; color : purple; } diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 410bfe0d1..9f08c2e1a 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -250,7 +250,16 @@ function tokenizeCustomMarkdown(text) { /^ *{{(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\1 *$|^ *}}$/, ); if(match) endCh = match.index + match[0].length; - tokens.push({ line: lineNumber, type: customTags.block }); + const closingMatch = lineText.match(/ *(}})/d); + + if(closingMatch) { + console.log('closing', closingMatch); + console.log(closingMatch.indices[1][0], closingMatch.indices[1][1]) + tokens.push({ line: lineNumber, from: closingMatch.indices[1][0], to: closingMatch.indices[1][1], type: customTags.block }); + } else { + tokens.push({ line: lineNumber, type: customTags.block }); + } + } }); From 4a565b929b058f992f24c892e4cbe6d50499681c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 18 May 2026 16:21:46 +0200 Subject: [PATCH 71/77] lint themes --- themes/V3/Blank/snippets/imageMask.gen.js | 20 ++++++++++---------- themes/codeMirror/darkbrewery.js | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/themes/V3/Blank/snippets/imageMask.gen.js b/themes/V3/Blank/snippets/imageMask.gen.js index 530ce01ac..833225b36 100644 --- a/themes/V3/Blank/snippets/imageMask.gen.js +++ b/themes/V3/Blank/snippets/imageMask.gen.js @@ -16,17 +16,17 @@ export default { edge : (side = 'bottom')=>{ const styles = ()=>{ switch (side) { - case 'bottom': - return `{width:100%,bottom:0%}` - break; - case 'top': - return `{width:100%,top:0%}` - break; - default: - return `{height:100%}` - break; + case 'bottom': + return `{width:100%,bottom:0%}`; + break; + case 'top': + return `{width:100%,top:0%}`; + break; + default: + return `{height:100%}`; + break; } - } + }; const rotation = { 'bottom' : 0, diff --git a/themes/codeMirror/darkbrewery.js b/themes/codeMirror/darkbrewery.js index fd010e6e0..202159715 100644 --- a/themes/codeMirror/darkbrewery.js +++ b/themes/codeMirror/darkbrewery.js @@ -93,7 +93,7 @@ export default EditorView.theme({ '.cm-strong' : { color: '#309dd2', fontWeight: 'bold' }, '.cm-em' : { fontStyle: 'italic' }, '.cm-keyword' : { color: '#fff' }, - '.cm-atom, .cm-value, .cm-color' : { color: '#c1939a' }, + '.cm-atom, .cm-value, .cm-color' : { color: '#c1939a' }, '.cm-number' : { color: '#2986cc' }, '.cm-def' : { color: '#2986cc' }, '.cm-list' : { color: '#3cbf30' }, From 15db3c9f664d70623327e37ad3cabf8c9536ce40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 18 May 2026 16:21:57 +0200 Subject: [PATCH 72/77] lint client --- .../components/codeEditor/customHighlight.js | 4 +- client/components/codeEditor/customKeyMaps.js | 46 +++++++++---------- .../homebrew/editor/snippetbar/snippetbar.jsx | 13 +++--- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 09a9af18f..622f8a3bf 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -86,8 +86,8 @@ export function tokenizeCustomMarkdown(text) { if(/\~/.test(lineText)) { const strikethroughRegex = /~(?!\s)(.+?)(? { - const { from, to } = view.state.selection.main; +const insertTab = (view)=>{ + const { from, to } = view.state.selection.main; - view.dispatch({ - changes: { from, to, insert: ' ' }, - selection: { anchor: from + 2 } - }); + view.dispatch({ + changes : { from, to, insert: ' ' }, + selection : { anchor: from + 2 } + }); - return true; + return true; }; const indentLess = (view)=>{ @@ -28,20 +28,18 @@ const indentLess = (view)=>{ return true; }; -const wrapSelection = (prefix, suffix) => (view) => { +const wrapSelection = (prefix, suffix)=>(view)=>{ const changes = []; - for(const range of view.state.selection.ranges) { + for (const range of view.state.selection.ranges) { const { from, to } = range; const selected = view.state.doc.sliceString(from, to); let text; - if(from === to) { text = prefix + suffix } - else if(selected.startsWith(prefix) && selected.endsWith(suffix)) { + if(from === to) { text = prefix + suffix; } else if(selected.startsWith(prefix) && selected.endsWith(suffix)) { text = selected.slice(prefix.length, -suffix.length); - } - else {text = `${prefix}${selected}${suffix}`} + } else {text = `${prefix}${selected}${suffix}`;} changes.push({ from, to, insert: text }); } @@ -53,21 +51,21 @@ const wrapSelection = (prefix, suffix) => (view) => { return true; }; -const makeNbsp = (view) => { - const { from } = view.state.selection.main; +const makeNbsp = (view)=>{ + const { from } = view.state.selection.main; - const prev2 = from >= 2 - ? view.state.doc.sliceString(from - 2, from) - : ''; + const prev2 = from >= 2 + ? view.state.doc.sliceString(from - 2, from) + : ''; - const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>'; + const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>'; - view.dispatch({ - changes : { from, to: from, insert }, - selection : { anchor: from + insert.length }, - }); + view.dispatch({ + changes : { from, to: from, insert }, + selection : { anchor: from + insert.length }, + }); - return true; + return true; }; const makeSpace = (view)=>{ diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 597a01045..ac9c7943a 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -30,18 +30,17 @@ import cm5Themes from 'codemirror-5-themes'; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const themeNames = Object.entries(themes) - .filter(([name, value]) => - Array.isArray(value) && + .filter(([name, value])=>Array.isArray(value) && !name.endsWith('Init') && !name.endsWith('Style') ) - .map(([name]) => name); + .map(([name])=>name); const EditorThemes = [ - 'default', - ...themeNames - .filter(name => name !== 'default') - .sort((a, b) => a.localeCompare(b)) + 'default', + ...themeNames + .filter((name)=>name !== 'default') + .sort((a, b)=>a.localeCompare(b)) ]; const execute = function(val, props){ From 34b26107c6b661d78d17082fcce7e4cc897ef51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 18 May 2026 16:22:03 +0200 Subject: [PATCH 73/77] lint server --- server/homebrew.api.js | 18 +++++++++--------- server/homebrew.model.js | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index c47e3640e..ba8c97b16 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -32,20 +32,20 @@ const isStaticTheme = (renderer, themeName)=>{ // }; -const migrateSystemsToTags = (brew) => { - if (!('systems' in brew)) return brew; +const migrateSystemsToTags = (brew)=>{ + if(!('systems' in brew)) return brew; - if (!Array.isArray(brew.systems) || brew.systems.length === 0) { + if(!Array.isArray(brew.systems) || brew.systems.length === 0) { brew.systems = undefined; return brew; } const systemMap = { - '5e': 'system:D&D 5e', - '4e': 'system:D&D 4e', - '3.5e': 'system:D&D 3.5e', - 'Pathfinder': 'system:Pathfinder 2e' + '5e' : 'system:D&D 5e', + '4e' : 'system:D&D 4e', + '3.5e' : 'system:D&D 3.5e', + 'Pathfinder' : 'system:Pathfinder 2e' }; - const systemTags = brew.systems.map(s => systemMap[s]); + const systemTags = brew.systems.map((s)=>systemMap[s]); brew.tags = _.uniq([...(brew.tags || []), ...systemTags]); brew.systems = undefined; @@ -188,7 +188,7 @@ const api = { stub.renderer = stub.renderer || undefined; // Clear empty strings stub = _.defaults(stub, DEFAULT_BREW_LOAD); // Fill in blank fields - + const fixedStub = migrateSystemsToTags(stub); req.brew = fixedStub; diff --git a/server/homebrew.model.js b/server/homebrew.model.js index b3d7702ce..e923ac928 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -15,7 +15,7 @@ const HomebrewSchema = mongoose.Schema({ description : { type: String, default: '' }, tags : { type: [String], index: true }, - systems : { type: [String], default: undefined }, + systems : { type: [String], default: undefined }, lang : { type: String, default: 'en', index: true }, renderer : { type: String, default: '', index: true }, authors : { type: [String], index: true }, From dff93002d01d1d661471bddb7855ad001c142884 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 18 May 2026 21:59:56 -0500 Subject: [PATCH 74/77] Clarify Regex behavior Add short circuit so the full hoisting only happens on brew load rather than every parse. Remove stray CR in unrelated file. --- client/components/codeEditor/codeEditor.jsx | 2 +- client/homebrew/brewRenderer/brewRenderer.jsx | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 3c75d52b0..3b0ff5854 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -454,4 +454,4 @@ const CodeEditor = forwardRef( }, ); -export default CodeEditor; \ No newline at end of file +export default CodeEditor; diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index b77a580c7..4925397ff 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -42,6 +42,7 @@ const BrewPage = (props)=>{ props = { contents : '', index : 0, + hoisted : false, ...props }; const pageRef = useRef(null); @@ -234,11 +235,16 @@ const BrewRenderer = (props)=>{ renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1); _.forEach(rawPages, (page, index)=>{ - const forceRender = checkHoists && (page.match(/([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g)); + const varsOnPageRegex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g; // Find out if there are any vars on the page. + const forceRender = checkHoists && + !props.hoisted && + (page.match(varsOnPageRegex)); // forceRender forces pages outside of the PPR range to render if true. + // This is necessary on the first load to fully populate the variable table. if((isInView(index) || !renderedPages[index] || forceRender) && typeof window !== 'undefined'){ renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range } }); + if(!props.hoisted) { props.hoisted = true; } // Only fully hoist once. return renderedPages; }; From 7a0348536cb8afdd3655a798b3300c612f74ca41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 23 May 2026 09:18:04 +0200 Subject: [PATCH 75/77] fix image request going crazy --- .../codeEditor/extensions/customHighlight.js | 4 +--- client/icons/broken-image.jpg | Bin 0 -> 14669 bytes 2 files changed, 1 insertion(+), 3 deletions(-) create mode 100644 client/icons/broken-image.jpg diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 9f08c2e1a..33c68da7a 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -253,8 +253,6 @@ function tokenizeCustomMarkdown(text) { const closingMatch = lineText.match(/ *(}})/d); if(closingMatch) { - console.log('closing', closingMatch); - console.log(closingMatch.indices[1][0], closingMatch.indices[1][1]) tokens.push({ line: lineNumber, from: closingMatch.indices[1][0], to: closingMatch.indices[1][1], type: customTags.block }); } else { tokens.push({ line: lineNumber, type: customTags.block }); @@ -358,7 +356,7 @@ class ImageWidget extends WidgetType { img.onerror = ()=>{ - img.src = 'client/icons/Broken-image-icon-in-Chrome.png'; + img.src = 'client/icons/broken-image.jpg'; }; return img; diff --git a/client/icons/broken-image.jpg b/client/icons/broken-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..52fa33195d2f902715a918e7d9610049cf670487 GIT binary patch literal 14669 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 Date: Wed, 27 May 2026 18:42:47 +0200 Subject: [PATCH 76/77] brewCleanup to functional component --- .../brewUtils/brewCleanup/brewCleanup.jsx | 92 ++++++++----------- 1 file changed, 40 insertions(+), 52 deletions(-) diff --git a/client/admin/brewUtils/brewCleanup/brewCleanup.jsx b/client/admin/brewUtils/brewCleanup/brewCleanup.jsx index 6cec01178..53498a54b 100644 --- a/client/admin/brewUtils/brewCleanup/brewCleanup.jsx +++ b/client/admin/brewUtils/brewCleanup/brewCleanup.jsx @@ -1,71 +1,59 @@ -import React from 'react'; -import createReactClass from 'create-react-class'; +import React, { useState } from 'react'; import request from 'superagent'; -const BrewCleanup = createReactClass({ - displayName : 'BrewCleanup', - getDefaultProps(){ - return {}; - }, - getInitialState() { - return { - count : 0, +const BrewCleanup = ({})=>{ + const [count, setCount] = useState(0); + const [pending, setPending] = useState(false); + const [primed, setPrimed] = useState(false); + const [error, setError] = useState(null); - pending : false, - primed : false, - err : null - }; - }, - prime(){ - this.setState({ pending: true }); + const prime = ()=>{ + setPending(true); request.get('/admin/cleanup') - .then((res)=>this.setState({ count: res.body.count, primed: true })) - .catch((err)=>this.setState({ error: err })) - .finally(()=>this.setState({ pending: false })); - }, - cleanup(){ - this.setState({ pending: true }); + .then((res)=>{setCount(res.body.count);setPrimed(true);}) + .catch((err)=>setError(err)) + .finally(()=>setPending(false)); + }; + const cleanup = ()=>{ + setPending(true); request.post('/admin/cleanup') - .then((res)=>this.setState({ count: res.body.count })) - .catch((err)=>this.setState({ error: err })) - .finally(()=>this.setState({ pending: false, primed: false })); - }, - renderPrimed(){ - if(!this.state.primed) return; + .then((res)=>setCount(res.body.count)) + .catch((err)=>setError(err)) + .finally(()=>{setPending(false);setPrimed(false);}); + }; + const renderPrimed = ()=>{ + if(!primed) return; + + if(!count) return
No Matching Brews found.
; - if(!this.state.count){ - return
No Matching Brews found.
; - } return
- - Found {this.state.count} Brews that could be removed. + Found {count} Brews that could be removed.
; - }, - render(){ - return
-

Brew Cleanup

-

Removes very short brews to tidy up the database

+ }; - - {this.renderPrimed()} + return
+

Brew Cleanup

+

Removes very short brews to tidy up the database

- {this.state.error - &&
{this.state.error.toString()}
+
; - } -}); + + {renderPrimed()} + + {error &&
{error.toString()}
} +
; + +}; export default BrewCleanup; From fff0b4d670dccfdadc3f75c5c4227f351b2e5e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 27 May 2026 18:49:48 +0200 Subject: [PATCH 77/77] moving to try catch better --- .../brewUtils/brewCleanup/brewCleanup.jsx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/client/admin/brewUtils/brewCleanup/brewCleanup.jsx b/client/admin/brewUtils/brewCleanup/brewCleanup.jsx index 53498a54b..721ec1e00 100644 --- a/client/admin/brewUtils/brewCleanup/brewCleanup.jsx +++ b/client/admin/brewUtils/brewCleanup/brewCleanup.jsx @@ -7,21 +7,34 @@ const BrewCleanup = ({})=>{ const [primed, setPrimed] = useState(false); const [error, setError] = useState(null); - const prime = ()=>{ + const prime = async ()=>{ setPending(true); - request.get('/admin/cleanup') - .then((res)=>{setCount(res.body.count);setPrimed(true);}) - .catch((err)=>setError(err)) - .finally(()=>setPending(false)); + try { + const res = await request.get('/admin/cleanup'); + + setCount(res.body.count); + setPrimed(true); + } catch (err) { + setError(err); + } finally { + setPending(false); + } }; - const cleanup = ()=>{ + + const cleanup = async ()=>{ setPending(true); - request.post('/admin/cleanup') - .then((res)=>setCount(res.body.count)) - .catch((err)=>setError(err)) - .finally(()=>{setPending(false);setPrimed(false);}); + try { + const res = await request.post('/admin/cleanup'); + + setCount(res.body.count); + } catch (err) { + setError(err); + } finally { + setPending(false); + setPrimed(false); + } }; const renderPrimed = ()=>{ if(!primed) return;