Upgrade to QuillV2

This commit is contained in:
Scott Nonnenberg 2025-01-28 07:31:42 -10:00 committed by GitHub
parent fb04b1ede3
commit 7575bda35b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 910 additions and 1227 deletions

View file

@ -226,6 +226,40 @@ Signal Desktop makes use of the following open source projects.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## @signalapp/quill-cjs
Copyright (c) 2017-2024, Slab
Copyright (c) 2014, Jason Chen
Copyright (c) 2013, salesforce.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## @types/fabric
MIT License
@ -1914,38 +1948,6 @@ Signal Desktop makes use of the following open source projects.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## parchment
Copyright (c) 2015, Jason Chen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## pify
MIT License
@ -2053,63 +2055,6 @@ Signal Desktop makes use of the following open source projects.
License: MIT
## quill
Copyright (c) 2014, Jason Chen
Copyright (c) 2013, salesforce.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## quill-delta
The MIT License (MIT)
Copyright (c) 2014 Jason Chen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## react
MIT License
@ -2676,30 +2621,6 @@ Signal Desktop makes use of the following open source projects.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## react-quill
The MIT License (MIT)
Copyright (c) 2020, zenoamaro <zenoamaro@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## react-redux
The MIT License (MIT)

View file

@ -84,7 +84,7 @@
type="text/css"
/>
<link
href="node_modules/react-quill/dist/quill.core.css"
href="node_modules/@signalapp/quill-cjs/dist/quill.core.css"
rel="stylesheet"
type="text/css"
/>

350
package-lock.json generated
View file

@ -23,6 +23,7 @@
"@react-spring/web": "9.7.5",
"@signalapp/better-sqlite3": "9.0.9",
"@signalapp/libsignal-client": "0.65.4",
"@signalapp/quill-cjs": "2.1.2",
"@signalapp/ringrtc": "2.49.3",
"@types/fabric": "4.5.3",
"backbone": "1.6.0",
@ -70,14 +71,11 @@
"p-map": "2.1.0",
"p-queue": "6.6.2",
"p-timeout": "4.1.0",
"parchment": "1.1.4",
"pify": "3.0.0",
"pino": "9.5.0",
"protobufjs": "7.3.2",
"proxy-agent": "6.4.0",
"qrcode-generator": "1.4.4",
"quill": "1.3.7",
"quill-delta": "4.0.1",
"react": "17.0.2",
"react-aria": "3.35.1",
"react-aria-components": "1.4.1",
@ -88,7 +86,6 @@
"react-hot-loader": "4.13.1",
"react-intl": "6.8.7",
"react-popper": "2.3.0",
"react-quill": "2.0.0-beta.4",
"react-redux": "8.1.3",
"react-router-dom": "5.0.1",
"react-textarea-autosize": "8.5.5",
@ -165,7 +162,6 @@
"@types/pify": "5.0.4",
"@types/pixelmatch": "5.2.6",
"@types/pngjs": "6.0.5",
"@types/quill": "1.3.10",
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/react-redux": "7.1.34",
@ -6561,6 +6557,33 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/@signalapp/parchment-cjs": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@signalapp/parchment-cjs/-/parchment-cjs-3.0.1.tgz",
"integrity": "sha512-hSBMQ1M7wE4GcC8ZeNtvpJF+DAJg3eIRRf1SiHS3I3Algav/sgJJNm6HIYm6muHuK7IJmuEjkL3ILSXgmu0RfQ==",
"license": "BSD-3-Clause"
},
"node_modules/@signalapp/quill-cjs": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@signalapp/quill-cjs/-/quill-cjs-2.1.2.tgz",
"integrity": "sha512-y2sgqdivlrG41J4Zvt/82xtH/PZjDlgItqlD2g/Cv3ZbjlR6cGhTNXbfNygCJB8nXj+C7I28pjt1Zm3k0pv2mg==",
"license": "BSD-3-Clause",
"dependencies": {
"@signalapp/parchment-cjs": "3.0.1",
"eventemitter3": "^5.0.1",
"lodash": "^4.17.21",
"quill-delta": "^5.1.0"
},
"engines": {
"npm": ">=8.2.3"
}
},
"node_modules/@signalapp/quill-cjs/node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/@signalapp/ringrtc": {
"version": "2.49.3",
"resolved": "https://registry.npmjs.org/@signalapp/ringrtc/-/ringrtc-2.49.3.tgz",
@ -8725,14 +8748,6 @@
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
"node_modules/@types/quill": {
"version": "1.3.10",
"resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz",
"integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==",
"dependencies": {
"parchment": "^1.1.2"
}
},
"node_modules/@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
@ -12363,16 +12378,47 @@
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.0",
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
"set-function-length": "^1.2.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"get-intrinsic": "^1.2.6"
},
"engines": {
"node": ">= 0.4"
@ -13004,14 +13050,6 @@
"node": ">=12"
}
},
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/clone-deep": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
@ -14239,11 +14277,6 @@
"node": ">=6"
}
},
"node_modules/deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
"integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw=="
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@ -14787,6 +14820,21 @@
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/duplexer2": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@ -15355,13 +15403,11 @@
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
@ -15381,6 +15427,19 @@
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
"dev": true
},
"node_modules/es-object-atoms": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
"integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-shim-unscopables": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
@ -17086,7 +17145,8 @@
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"node_modules/extend-shallow": {
"version": "2.0.1",
@ -17149,11 +17209,6 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
},
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@ -17944,16 +17999,22 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
"get-proto": "^1.0.0",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@ -17972,6 +18033,20 @@
"node": ">=8.0.0"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-stdin": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
@ -18304,12 +18379,13 @@
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3"
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -18437,23 +18513,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
@ -18462,12 +18527,13 @@
}
},
"node_modules/has-tostringtag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.2"
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
@ -19412,12 +19478,20 @@
}
},
"node_modules/is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha512-P5rExV1phPi42ppoMWy7V63N3i173RY921l4JJ7zonMSxK+OWGPj76GD+cUKUb68l4vQXcJp2SsG+r/A4ABVzg==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
"integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-decimal": {
@ -21992,6 +22066,12 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
"license": "MIT"
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -22032,6 +22112,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
@ -22460,6 +22546,16 @@
"node": ">=10"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mathml-tag-names": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
@ -25078,11 +25174,6 @@
"tslib": "^2.0.3"
}
},
"node_modules/parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -26522,47 +26613,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/quill": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
"dependencies": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.2",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
}
},
"node_modules/quill-delta": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.0.1.tgz",
"integrity": "sha512-8MdJmDlsnkGTMP3VVqVb/S95l+X691/xY1iQ3tmDDBWkqUyqh72+tBnaEStxfEa5YqzozGeYWz8j2B3pni5Bew==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
"license": "MIT",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
}
},
"node_modules/quill/node_modules/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
},
"node_modules/quill/node_modules/quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
"fast-diff": "^1.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
},
"engines": {
"node": ">=0.10"
"node": ">= 12.0.0"
}
},
"node_modules/quill-delta/node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"license": "Apache-2.0"
},
"node_modules/rambda": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/rambda/-/rambda-7.3.0.tgz",
@ -27092,20 +27162,6 @@
"react-dom": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-quill": {
"version": "2.0.0-beta.4",
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
"integrity": "sha512-KyAHvAlPjP4xLElKZJefMth91Z6FbbXRvq9OSu6xN3KBaoasLP9p+3dcxg4Ywr4tBlpMGXcPszYSAgd5CpJ45Q==",
"dependencies": {
"@types/quill": "^1.3.10",
"lodash": "^4.17.4",
"quill": "^1.3.7"
},
"peerDependencies": {
"react": "^16 || ^17",
"react-dom": "^16 || ^17"
}
},
"node_modules/react-redux": {
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz",
@ -27489,14 +27545,18 @@
"dev": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
"integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3",
"functions-have-names": "^1.2.2"
"call-bind": "^1.0.8",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"set-function-name": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
@ -28461,6 +28521,22 @@
"node": ">= 0.4"
}
},
"node_modules/set-function-name": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"functions-have-names": "^1.2.3",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",

View file

@ -114,6 +114,7 @@
"@react-spring/web": "9.7.5",
"@signalapp/better-sqlite3": "9.0.9",
"@signalapp/libsignal-client": "0.65.4",
"@signalapp/quill-cjs": "2.1.2",
"@signalapp/ringrtc": "2.49.3",
"@types/fabric": "4.5.3",
"backbone": "1.6.0",
@ -161,14 +162,11 @@
"p-map": "2.1.0",
"p-queue": "6.6.2",
"p-timeout": "4.1.0",
"parchment": "1.1.4",
"pify": "3.0.0",
"pino": "9.5.0",
"protobufjs": "7.3.2",
"proxy-agent": "6.4.0",
"qrcode-generator": "1.4.4",
"quill": "1.3.7",
"quill-delta": "4.0.1",
"react": "17.0.2",
"react-aria": "3.35.1",
"react-aria-components": "1.4.1",
@ -179,7 +177,6 @@
"react-hot-loader": "4.13.1",
"react-intl": "6.8.7",
"react-popper": "2.3.0",
"react-quill": "2.0.0-beta.4",
"react-redux": "8.1.3",
"react-router-dom": "5.0.1",
"react-textarea-autosize": "8.5.5",
@ -256,7 +253,6 @@
"@types/pify": "5.0.4",
"@types/pixelmatch": "5.2.6",
"@types/pngjs": "6.0.5",
"@types/quill": "1.3.10",
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/react-redux": "7.1.34",

View file

@ -1,185 +0,0 @@
diff --git a/node_modules/quill/dist/quill.js b/node_modules/quill/dist/quill.js
index 811b3d0..9243ab6 100644
--- a/node_modules/quill/dist/quill.js
+++ b/node_modules/quill/dist/quill.js
@@ -4295,7 +4295,7 @@ var Scroll = function (_Parchment$Scroll) {
value: function enable() {
var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
- this.domNode.setAttribute('contenteditable', enabled);
+ this.domNode.setAttribute('contenteditable', enabled ? 'plaintext-only' : false);
}
}, {
key: 'formatAt',
@@ -8896,7 +8896,8 @@ var debug = (0, _logger2.default)('quill:clipboard');
var DOM_KEY = '__ql-matcher';
-var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];
+// var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];
+var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline]];
var ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) {
memo[attr.keyName] = attr;
@@ -8916,10 +8917,10 @@ var Clipboard = function (_Module) {
var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options));
- _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this));
- _this.container = _this.quill.addContainer('ql-clipboard');
- _this.container.setAttribute('contenteditable', true);
- _this.container.setAttribute('tabindex', -1);
+ // _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this));
+ // _this.container = _this.quill.addContainer('ql-clipboard');
+ // _this.container.setAttribute('contenteditable', true);
+ // _this.container.setAttribute('tabindex', -1);
_this.matchers = [];
CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
@@ -8941,28 +8942,33 @@ var Clipboard = function (_Module) {
key: 'convert',
value: function convert(html) {
if (typeof html === 'string') {
- this.container.innerHTML = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
+ // this.container.innerHTML = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
+ var parser = new DOMParser();
+ var htmlNoSpaces = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
+ this.container = parser.parseFromString(htmlNoSpaces, "text/html").body;
return this.convert();
}
var formats = this.quill.getFormat(this.quill.selection.savedRange.index);
- if (formats[_code2.default.blotName]) {
- var text = this.container.innerText;
- this.container.innerHTML = '';
- return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName]));
- }
+ // if (formats[_code2.default.blotName]) {
+ // var text = this.container.innerText;
+ // this.container.innerHTML = '';
+ // return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName]));
+ // }
var _prepareMatching = this.prepareMatching(),
_prepareMatching2 = _slicedToArray(_prepareMatching, 2),
elementMatchers = _prepareMatching2[0],
textMatchers = _prepareMatching2[1];
- var delta = traverse(this.container, elementMatchers, textMatchers);
+ // var delta = traverse(this.container, elementMatchers, textMatchers);
+ var delta = traverse(this.container, elementMatchers, textMatchers, formats);
// Remove trailing newline
if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1));
}
debug.log('convert', this.container.innerHTML, delta);
- this.container.innerHTML = '';
+ // this.container.innerHTML = '';
+ this.container = null;
return delta;
}
}, {
@@ -9056,9 +9062,10 @@ function applyFormat(delta, format, value) {
}
function computeStyle(node) {
- if (node.nodeType !== Node.ELEMENT_NODE) return {};
- var DOM_KEY = '__ql-computed-style';
- return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
+ // if (node.nodeType !== Node.ELEMENT_NODE) return {};
+ // var DOM_KEY = '__ql-computed-style';
+ // return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
+ return node.style;
}
function deltaEndsWith(delta, text) {
@@ -9070,28 +9077,43 @@ function deltaEndsWith(delta, text) {
}
return endText.slice(-1 * text.length) === text;
}
+function deltaIs(delta, text) {
+ var allText = "";
+ for (var i = delta.ops.length - 1; i >= 0 && allText.length <= text.length; --i) {
+ var op = delta.ops[i];
+ if (typeof op.insert !== 'string') break;
+ allText = op.insert + allText;
+ }
+ return allText === text;
+}
function isLine(node) {
if (node.childNodes.length === 0) return false; // Exclude embed blocks
var style = computeStyle(node);
- return ['block', 'list-item'].indexOf(style.display) > -1;
+ // return ['block', 'list-item'].indexOf(style.display) > -1;
+ return ['block', 'list-item'].indexOf(style.display) > -1 || node.nodeName === 'DIV' || node.nodeName === 'P' || node.nodeName === 'TIME';
}
-function traverse(node, elementMatchers, textMatchers) {
+// function traverse(node, elementMatchers, textMatchers) {
+function traverse(node, elementMatchers, textMatchers, attributes) {
// Post-order
if (node.nodeType === node.TEXT_NODE) {
return textMatchers.reduce(function (delta, matcher) {
- return matcher(node, delta);
+ // return matcher(node, delta);
+ return matcher(node, delta, attributes);
}, new _quillDelta2.default());
} else if (node.nodeType === node.ELEMENT_NODE) {
return [].reduce.call(node.childNodes || [], function (delta, childNode) {
- var childrenDelta = traverse(childNode, elementMatchers, textMatchers);
+ // var childrenDelta = traverse(childNode, elementMatchers, textMatchers);
+ var childrenDelta = traverse(childNode, elementMatchers, textMatchers, attributes);
if (childNode.nodeType === node.ELEMENT_NODE) {
childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) {
- return matcher(childNode, childrenDelta);
+ // return matcher(childNode, childrenDelta);
+ return matcher(childNode, childrenDelta, attributes);
}, childrenDelta);
childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) {
- return matcher(childNode, childrenDelta);
+ // return matcher(childNode, childrenDelta);
+ return matcher(childNode, childrenDelta, attributes);
}, childrenDelta);
}
return delta.concat(childrenDelta);
@@ -9177,8 +9199,10 @@ function matchIndent(node, delta) {
}
function matchNewline(node, delta) {
- if (!deltaEndsWith(delta, '\n')) {
- if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) {
+ // if (!deltaEndsWith(delta, '\n')) {
+ if (!deltaIs(delta, '\n') && !deltaEndsWith(delta, '\n\n')) {
+ // if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) {
+ if (delta.length() > 0 && isLine(node)) {
delta.insert('\n');
}
}
@@ -9186,7 +9210,7 @@ function matchNewline(node, delta) {
}
function matchSpacing(node, delta) {
- if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) {
+ if (isLine(node) && node.nextElementSibling != null && !deltaIs(delta, '\n') && !deltaEndsWith(delta, '\n\n')) {
var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);
if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) {
delta.insert('\n');
@@ -9214,7 +9238,7 @@ function matchStyles(node, delta) {
return delta;
}
-function matchText(node, delta) {
+function matchText(node, delta, attributes) {
var text = node.data;
// Word represents empty line with <o:p>&nbsp;</o:p>
if (node.parentNode.tagName === 'O:P') {
@@ -9238,7 +9262,7 @@ function matchText(node, delta) {
text = text.replace(/\s+$/, replacer.bind(replacer, false));
}
}
- return delta.insert(text);
+ return delta.insert(text, attributes);
}
exports.default = Clipboard;
\ No newline at end of file

View file

@ -136,6 +136,7 @@
padding-block: 6px;
padding-inline: 12px;
border-radius: 8px;
// This needs to be quite high to enable usage in popups like ForwardMessageModal
z-index: variables.$z-index-above-popup;
display: flex;

View file

@ -3,7 +3,7 @@
import * as React from 'react';
// @ts-expect-error -- no types
import 'react-quill/dist/quill.core.css';
import '@signalapp/quill-cjs/dist/quill.core.css';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';

View file

@ -3,12 +3,15 @@
import * as React from 'react';
import Delta from 'quill-delta';
import ReactQuill from 'react-quill';
import Quill, { Delta } from '@signalapp/quill-cjs';
import {
matchText,
matchNewline,
matchBreak,
} from '@signalapp/quill-cjs/modules/clipboard';
import classNames from 'classnames';
import { Manager, Reference } from 'react-popper';
import type { DeltaStatic, KeyboardStatic, RangeStatic } from 'quill';
import Quill from 'quill';
import type { Range as RangeStatic } from '@signalapp/quill-cjs';
import { MentionCompletion } from '../quill/mentions/completion';
import { FormattingMenu, QuillFormattingStyle } from '../quill/formatting/menu';
@ -56,7 +59,6 @@ import { getClassNamesFor } from '../util/getClassNamesFor';
import { isNotNil } from '../util/isNotNil';
import * as log from '../logging/log';
import * as Errors from '../types/errors';
import { useRefMerger } from '../hooks/useRefMerger';
import { useEmojiSearch } from '../hooks/useEmojiSearch';
import type { LinkPreviewType } from '../types/message/LinkPreviews';
import { StagedLinkPreview } from './conversation/StagedLinkPreview';
@ -72,22 +74,23 @@ import {
import { missingCaseError } from '../util/missingCaseError';
import { AutoSubstituteAsciiEmojis } from '../quill/auto-substitute-ascii-emojis';
import { dropNull } from '../util/dropNull';
import { SimpleQuillWrapper } from './SimpleQuillWrapper';
Quill.register('formats/emoji', EmojiBlot);
Quill.register('formats/mention', MentionBlot);
Quill.register('formats/block', DirectionalBlot);
Quill.register('formats/monospace', MonospaceBlot);
Quill.register('formats/spoiler', SpoilerBlot);
Quill.register('modules/autoSubstituteAsciiEmojis', AutoSubstituteAsciiEmojis);
Quill.register('modules/emojiCompletion', EmojiCompletion);
Quill.register('modules/mentionCompletion', MentionCompletion);
Quill.register('modules/formattingMenu', FormattingMenu);
Quill.register('modules/signalClipboard', SignalClipboard);
type HistoryStatic = {
undo(): void;
clear(): void;
};
Quill.register(
{
'formats/emoji': EmojiBlot,
'formats/mention': MentionBlot,
'formats/block': DirectionalBlot,
'formats/monospace': MonospaceBlot,
'formats/spoiler': SpoilerBlot,
'modules/autoSubstituteAsciiEmojis': AutoSubstituteAsciiEmojis,
'modules/emojiCompletion': EmojiCompletion,
'modules/mentionCompletion': MentionCompletion,
'modules/formattingMenu': FormattingMenu,
'modules/signalClipboard': SignalClipboard,
},
true
);
export type InputApi = {
focus: () => void;
@ -120,7 +123,6 @@ export type Props = Readonly<{
theme: ThemeType;
placeholder?: string;
sortedGroupMembers: ReadonlyArray<ConversationType> | null;
scrollerRef?: React.RefObject<HTMLDivElement>;
onDirtyChange?(dirty: boolean): unknown;
onEditorStateChange(options: {
bodyRanges: DraftBodyRanges;
@ -185,8 +187,6 @@ export function CompositionInput(props: Props): React.ReactElement {
theme,
} = props;
const refMerger = useRefMerger();
const [emojiCompletionElement, setEmojiCompletionElement] =
React.useState<JSX.Element>();
const [formattingChooserElement, setFormattingChooserElement] =
@ -200,8 +200,6 @@ export function CompositionInput(props: Props): React.ReactElement {
const mentionCompletionRef = React.useRef<MentionCompletion>();
const quillRef = React.useRef<Quill>();
const scrollerRefInner = React.useRef<HTMLDivElement>(null);
const propsRef = React.useRef<Props>(props);
const canSendRef = React.useRef<boolean>(false);
const memberRepositoryRef = React.useRef<MemberRepository>(
@ -311,13 +309,7 @@ export function CompositionInput(props: Props): React.ReactElement {
canSendRef.current = true;
quill.setText('');
const historyModule = quill.getModule('history');
if (historyModule === undefined) {
return;
}
historyModule.clear();
quill.history.clear();
};
const setContents = (
@ -334,11 +326,7 @@ export function CompositionInput(props: Props): React.ReactElement {
const delta = generateDelta(text || '', bodyRanges || []);
canSendRef.current = true;
// We need to cast here because we use @types/quill@1.3.10 which has types
// for quill-delta even though quill-delta is written in TS and has its own
// types. @types/quill@2.0.0 fixes the issue but react-quill has a peer-dep
// on the older quill types.
quill.setContents(delta as unknown as DeltaStatic);
quill.setContents(delta);
if (cursorToEnd) {
quill.setSelection(quill.getLength(), 0);
}
@ -410,11 +398,17 @@ export function CompositionInput(props: Props): React.ReactElement {
const quill = quillRef.current;
const changed = formattingChanged || mouseDownChanged;
if (quill && changed) {
quill.getModule('formattingMenu').updateOptions({
const formattingMenu = quill.getModule('formattingMenu');
if (!(formattingMenu instanceof FormattingMenu)) {
throw new Error(
'CompositionInput: formattingMenu module not properly initialized'
);
}
formattingMenu.updateOptions({
isMenuEnabled: isFormattingEnabled,
isMouseDown,
});
quill.options.formats = getQuillFormats();
}
}, [
isFormattingEnabled,
@ -424,7 +418,17 @@ export function CompositionInput(props: Props): React.ReactElement {
]);
React.useEffect(() => {
quillRef.current?.getModule('signalClipboard').updateOptions({
const signalClipboard = quillRef.current?.getModule('signalClipboard');
if (!signalClipboard) {
return;
}
if (!(signalClipboard instanceof SignalClipboard)) {
throw new Error(
'CompositionInput: signalClipboard module not properly initialized'
);
}
signalClipboard.updateOptions({
isDisabled: !isActive,
});
}, [isActive]);
@ -527,6 +531,10 @@ export function CompositionInput(props: Props): React.ReactElement {
}
const [blotToDelete] = quill.getLeaf(selection.index);
if (!blotToDelete) {
return true;
}
if (isMentionBlot(blotToDelete)) {
const contents = quill.getContents(0, selection.index - 1);
const restartDelta = getDeltaToRestartMention(contents.ops);
@ -553,10 +561,18 @@ export function CompositionInput(props: Props): React.ReactElement {
const { text, bodyRanges } = getTextAndRanges();
if (quill !== undefined) {
const historyModule: HistoryStatic = quill.getModule('history');
// This is pretty ugly, but it seems that Chromium tries to replicate the computed
// style of removed DOM elements. 100% reproducible by selecting formatted lines and
// typing new text. This code removes the style tags that we don't want there, and
// quill doesn't know about. It can result formatting on the resultant message that
// doesn't match the composer.
const withStyles = quill.container.querySelectorAll('[style]');
for (const node of withStyles) {
node.attributes.removeNamedItem('style');
}
if (text.length > MAX_LENGTH) {
historyModule.undo();
quill.history.undo();
propsRef.current.onTextTooLong();
return;
}
@ -722,7 +738,7 @@ export function CompositionInput(props: Props): React.ReactElement {
const delta = generateDelta(draftText || '', draftBodyRanges || []);
return (
<ReactQuill
<SimpleQuillWrapper
className={`${BASE_CLASS_NAME}__quill`}
onChange={() => callbacksRef.current.onChange()}
defaultValue={delta}
@ -732,7 +748,13 @@ export function CompositionInput(props: Props): React.ReactElement {
isDisabled: !isActive,
},
clipboard: {
defaultMatchersOverride: [],
disableDefaultListeners: true,
matchers: [
[Node.TEXT_NODE, matchText],
[Node.TEXT_NODE, matchNewline],
['br', matchBreak],
[Node.ELEMENT_NODE, matchNewline],
['IMG', matchEmojiImage],
['IMG', matchEmojiBlot],
['STRONG', matchBold],
@ -746,23 +768,27 @@ export function CompositionInput(props: Props): React.ReactElement {
},
keyboard: {
bindings: {
onEnter: {
key: 13,
handler: () => callbacksRef.current.onEnter(),
}, // 13 = Enter
onShortKeyEnter: {
key: 13, // 13 = Enter
ShortEnter: {
key: 'Enter',
shortKey: true,
handler: () => callbacksRef.current.onShortKeyEnter(),
},
onEscape: {
key: 27,
Enter: {
key: 'Enter',
handler: () => callbacksRef.current.onEnter(),
},
Escape: {
key: 'Escape',
handler: () => callbacksRef.current.onEscape(),
}, // 27 = Escape
onBackspace: {
key: 8,
},
Backspace: {
key: 'Backspace',
handler: () => callbacksRef.current.onBackspace(),
}, // 8 = Backspace
},
Tab: {
key: 'Tab',
handler: () => callbacksRef.current.onTab(),
},
},
},
emojiCompletion: {
@ -795,27 +821,18 @@ export function CompositionInput(props: Props): React.ReactElement {
readOnly={disabled}
ref={element => {
if (element) {
const quill = element.getEditor();
const keyboard = quill.getModule('keyboard') as KeyboardStatic;
const quill = element.getQuill();
if (!quill) {
throw new Error(
'CompositionInput: wrapper did not return quill!'
);
}
// force the tab handler to be prepended, otherwise it won't be
// executed: https://github.com/quilljs/quill/issues/1967
keyboard.bindings[9].unshift({
key: 9,
handler: () => callbacksRef.current.onTab(),
}); // 9 = Tab
// also, remove the default \t insertion binding
keyboard.bindings[9].pop();
quillRef.current = quill;
// When loading a multi-line message out of a draft, the cursor
// position needs to be pushed to the end of the input manually.
quill.once('editor-change', () => {
const scroller = scrollerRefInner.current;
if (scroller != null) {
quill.scrollingContainer = scroller;
}
setTimeout(() => {
quill.setSelection(quill.getLength(), 0);
quill.root.classList.add('ql-editor--loaded');
@ -831,10 +848,22 @@ export function CompositionInput(props: Props): React.ReactElement {
}
}
);
quillRef.current = quill;
emojiCompletionRef.current = quill.getModule('emojiCompletion');
mentionCompletionRef.current =
quill.getModule('mentionCompletion');
const emojiCompletion = quill.getModule('emojiCompletion');
if (!(emojiCompletion instanceof EmojiCompletion)) {
throw new Error(
'CompositionInput: emojiCompletion module not properly initialized'
);
}
emojiCompletionRef.current = emojiCompletion;
const mentionCompletion = quill.getModule('mentionCompletion');
if (!(mentionCompletion instanceof MentionCompletion)) {
throw new Error(
'CompositionInput: mentionCompletion module not properly initialized'
);
}
mentionCompletionRef.current = mentionCompletion;
}
}}
/>
@ -918,11 +947,6 @@ export function CompositionInput(props: Props): React.ReactElement {
)}
{children}
<div
ref={
props.scrollerRef
? refMerger(scrollerRefInner, props.scrollerRef)
: scrollerRefInner
}
onClick={focus}
onScroll={onScroll}
className={classNames(

View file

@ -24,7 +24,6 @@ export type CompositionTextAreaProps = {
maxLength?: number;
placeholder?: string;
whenToShowRemainingCount?: number;
scrollerRef?: React.RefObject<HTMLDivElement>;
onScroll?: (ev: React.UIEvent<HTMLElement, UIEvent>) => void;
onPickEmoji: (e: EmojiPickDataType) => void;
onChange: (
@ -71,7 +70,6 @@ export function CompositionTextArea({
placeholder,
platform,
recentEmojis,
scrollerRef,
skinTone,
theme,
whenToShowRemainingCount = Infinity,
@ -153,7 +151,6 @@ export function CompositionTextArea({
placeholder={placeholder}
platform={platform}
quotedMessageId={null}
scrollerRef={scrollerRef}
sendCounter={0}
theme={theme}
skinTone={skinTone ?? null}

View file

@ -0,0 +1,80 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { createRef } from 'react';
import Quill from '@signalapp/quill-cjs';
import type { Delta } from '@signalapp/quill-cjs';
export type Props = {
className: string;
defaultValue: Delta | undefined;
formats: Array<string>;
modules: Record<string, unknown>;
onChange?(): void;
placeholder: string;
readOnly: boolean | undefined;
};
export class SimpleQuillWrapper extends React.Component<Props> {
quill: Quill | undefined;
quillElement = createRef<HTMLDivElement>();
override shouldComponentUpdate(): boolean {
return false;
}
override componentDidMount(): void {
this.createQuill();
if (!this.quill) {
throw new Error(
'SimpleQuillWrapper.componentDidMount: this.quill not set!'
);
}
this.quill.on('editor-change', this.props.onChange);
const { defaultValue } = this.props;
if (defaultValue) {
this.quill.setContents(defaultValue);
}
}
override componentWillUnmount(): void {
if (!this.quill) {
return;
}
this.quill.off('editor-change', this.props.onChange);
this.quill = undefined;
}
createQuill(): void {
if (this.quill) {
throw new Error('createQuill: this.quill already set!');
}
if (!this.quillElement?.current) {
throw new Error('createQuill: this.quillElement is not set!');
}
this.quill = new Quill(this.quillElement.current, {
formats: this.props.formats,
modules: this.props.modules,
placeholder: this.props.placeholder,
readOnly: this.props.readOnly,
});
}
// eslint-disable-next-line react/no-unused-class-component-methods
public getQuill(): Quill | undefined {
return this.quill;
}
override render(): JSX.Element {
return (
<div className={`quill ${this.props.className}`}>
<div ref={this.quillElement} />
</div>
);
}
}

View file

@ -1,8 +1,10 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type Quill from 'quill';
import Delta from 'quill-delta';
import type Quill from '@signalapp/quill-cjs';
import { Delta } from '@signalapp/quill-cjs';
import * as log from '../../logging/log';
import type { EmojiData } from '../../components/emoji/lib';
import {
convertShortName,
@ -77,11 +79,18 @@ export class AutoSubstituteAsciiEmojis {
const [blot, index] = this.quill.getLeaf(range.index);
if (blot?.text == null) {
const text = blot?.value();
if (!text) {
return;
}
if (typeof text !== 'string') {
log.error(
'AutoSubstituteAsciiEmojis: returned blot value was not a string'
);
return;
}
const textBeforeCursor = blot.text.slice(0, index);
const textBeforeCursor = text.slice(0, index);
const match = textBeforeCursor.match(EMOJI_REGEXP);
if (match == null) {
return;

View file

@ -1,14 +1,12 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Quill from 'quill';
import BlockBlot from '@signalapp/quill-cjs/blots/block';
const Block = Quill.import('blots/block');
export class DirectionalBlot extends BlockBlot {
static override tagName = 'div';
export class DirectionalBlot extends Block {
static tagName = 'div';
static create(value: string): Node {
static override create(value: string): HTMLElement {
const node = super.create(value);
node.setAttribute('dir', 'auto');
return node;

View file

@ -1,13 +1,10 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type Parchment from 'parchment';
import Quill from 'quill';
import EmbedBlot from '@signalapp/quill-cjs/blots/embed';
import { emojiToImage } from '../../components/emoji/lib';
const Embed: typeof Parchment.Embed = Quill.import('blots/embed');
// the DOM structure of this EmojiBlot should match the other emoji implementations:
// ts/components/conversation/Emojify.tsx
// ts/components/emoji/Emoji.tsx
@ -17,7 +14,7 @@ export type EmojiBlotValue = Readonly<{
source?: string;
}>;
export class EmojiBlot extends Embed {
export class EmojiBlot extends EmbedBlot {
static override blotName = 'emoji';
static override tagName = 'img';

View file

@ -1,8 +1,8 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Quill from 'quill';
import Delta from 'quill-delta';
import type Quill from '@signalapp/quill-cjs';
import { Delta } from '@signalapp/quill-cjs';
import React from 'react';
import _, { isNumber } from 'lodash';
@ -18,8 +18,6 @@ import { getBlotTextPartitions, matchBlotTextPartitions } from '../util';
import { handleOutsideClick } from '../../util/handleOutsideClick';
import * as log from '../../logging/log';
const Keyboard = Quill.import('modules/keyboard');
type EmojiPickerOptions = {
onPickEmoji: (emoji: EmojiPickDataType) => void;
setEmojiPickerElement: (element: JSX.Element | null) => void;
@ -72,10 +70,10 @@ export class EmojiCompletion {
return true;
};
this.quill.keyboard.addBinding({ key: Keyboard.keys.UP }, changeIndex(-1));
this.quill.keyboard.addBinding({ key: Keyboard.keys.RIGHT }, clearResults);
this.quill.keyboard.addBinding({ key: Keyboard.keys.DOWN }, changeIndex(1));
this.quill.keyboard.addBinding({ key: Keyboard.keys.LEFT }, clearResults);
this.quill.keyboard.addBinding({ key: 'ArrowUp' }, changeIndex(-1));
this.quill.keyboard.addBinding({ key: 'ArrowRight' }, clearResults);
this.quill.keyboard.addBinding({ key: 'ArrowDown' }, changeIndex(1));
this.quill.keyboard.addBinding({ key: 'ArrowLeft' }, clearResults);
this.quill.keyboard.addBinding(
{
// 186 + Shift = Colon
@ -116,8 +114,14 @@ export class EmojiCompletion {
getCurrentLeafTextPartitions(): [string, string] {
const range = this.quill.getSelection();
const [blot, index] = this.quill.getLeaf(range ? range.index : -1);
const text = blot?.value();
if (text && typeof text !== 'string') {
throw new Error(
'EmojiCompletion/getCurrentLeafTextPartitions: Blot value was not a string'
);
}
return getBlotTextPartitions(blot.text, index);
return getBlotTextPartitions(text, index);
}
onSelectionChange(): void {

View file

@ -1,10 +1,11 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Delta from 'quill-delta';
import type { Matcher, AttributeMap } from 'quill';
import { Delta } from '@signalapp/quill-cjs';
import type { AttributeMap } from '@signalapp/quill-cjs';
import { insertEmojiOps } from '../util';
import type { Matcher } from '../util';
export const matchEmojiImage: Matcher = (
node: Element,
@ -22,9 +23,10 @@ export const matchEmojiImage: Matcher = (
};
export const matchEmojiBlot: Matcher = (
node: HTMLElement,
delta: Delta,
attributes: AttributeMap
node,
delta,
_scroll,
attributes
): Delta => {
if (node.classList.contains('emoji-blot')) {
const { emoji: value, source } = node.dataset;
@ -34,9 +36,10 @@ export const matchEmojiBlot: Matcher = (
};
export const matchEmojiText: Matcher = (
node: HTMLElement,
_delta: Delta,
attributes: AttributeMap
node,
_delta,
_scroll,
attributes
): Delta => {
if (!('data' in node)) {
return new Delta();

View file

@ -1,10 +1,11 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Delta from 'quill-delta';
import type { Matcher, AttributeMap } from 'quill';
import { Delta } from '@signalapp/quill-cjs';
import type { AttributeMap } from '@signalapp/quill-cjs';
import { QuillFormattingStyle } from './menu';
import type { Matcher } from '../util';
function applyStyleToOps(
delta: Delta,
@ -24,8 +25,9 @@ function applyStyleToOps(
}
export const matchBold: Matcher = (
_node: HTMLElement,
delta: Delta,
_node,
delta,
_scroll,
attributes: AttributeMap
): Delta => {
if (delta.length() > 0) {
@ -38,6 +40,7 @@ export const matchBold: Matcher = (
export const matchItalic: Matcher = (
_node: HTMLElement,
delta: Delta,
_scroll,
attributes: AttributeMap
): Delta => {
if (delta.length() > 0) {
@ -48,9 +51,10 @@ export const matchItalic: Matcher = (
};
export const matchStrikethrough: Matcher = (
_node: HTMLElement,
delta: Delta,
attributes: AttributeMap
_node,
delta,
_scroll,
attributes
): Delta => {
if (delta.length() > 0) {
return applyStyleToOps(delta, QuillFormattingStyle.strike, attributes);
@ -60,9 +64,10 @@ export const matchStrikethrough: Matcher = (
};
export const matchMonospace: Matcher = (
node: HTMLElement,
delta: Delta,
attributes: AttributeMap
node,
delta,
_scroll,
attributes
): Delta => {
const classes = [
'MessageTextRenderer__formatting--monospace',
@ -85,9 +90,10 @@ export const matchMonospace: Matcher = (
};
export const matchSpoiler: Matcher = (
node: HTMLElement,
delta: Delta,
attributes: AttributeMap
node,
delta,
_scroll,
attributes
): Delta => {
const classes = [
'quill--spoiler',

View file

@ -1,9 +1,9 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type Quill from 'quill';
import type { KeyboardContext } from 'quill';
import type Op from 'quill-delta/dist/Op';
import type Quill from '@signalapp/quill-cjs';
import type { Op } from '@signalapp/quill-cjs';
import type { Context as KeyboardContext } from '@signalapp/quill-cjs/modules/keyboard';
import React from 'react';
import classNames from 'classnames';
import { Popper } from 'react-popper';
@ -23,11 +23,11 @@ const MENU_TEXT_BUFFER = 12; // pixels
// Note: Keyboard shortcuts are defined in the constructor below, and when using
// <FormattingButton /> below. They're also referenced in ShortcutGuide.tsx.
const BOLD_CHAR = 'B';
const ITALIC_CHAR = 'I';
const MONOSPACE_CHAR = 'E';
const SPOILER_CHAR = 'B';
const STRIKETHROUGH_CHAR = 'X';
const BOLD_CHAR = 'b';
const ITALIC_CHAR = 'i';
const MONOSPACE_CHAR = 'e';
const SPOILER_CHAR = 'b';
const STRIKETHROUGH_CHAR = 'x';
type FormattingPickerOptions = {
i18n: LocalizerType;
@ -95,42 +95,49 @@ export class FormattingMenu {
// above the built-in shortcuts, which don't exactly do what we want.
const boldCharCode = BOLD_CHAR.charCodeAt(0);
this.quill.keyboard.addBinding(
{ key: BOLD_CHAR, shortKey: true },
(_range, context) =>
this.toggleForStyle(QuillFormattingStyle.bold, context)
);
quill.keyboard.bindings[boldCharCode].unshift(
quill.keyboard.bindings[boldCharCode].pop()
);
// We want to be sure that we're the only handler for this charCode.
this.quill.keyboard.bindings[boldCharCode] = [];
this.quill.keyboard.addBinding({
key: BOLD_CHAR,
shortKey: true,
handler: (_range, context) =>
this.toggleForStyle(QuillFormattingStyle.bold, context),
});
const italicCharCode = ITALIC_CHAR.charCodeAt(0);
this.quill.keyboard.addBinding(
{ key: ITALIC_CHAR, shortKey: true },
(_range, context) =>
this.toggleForStyle(QuillFormattingStyle.italic, context)
);
quill.keyboard.bindings[italicCharCode].unshift(
quill.keyboard.bindings[italicCharCode].pop()
);
// No other handlers for this charCode!
this.quill.keyboard.bindings[italicCharCode] = [];
this.quill.keyboard.addBinding({
key: ITALIC_CHAR,
shortKey: true,
handler: (_range, context) =>
this.toggleForStyle(QuillFormattingStyle.italic, context),
});
// No need for changing priority for these new keybindings
// No need for changing priority for these totally new keybindings
this.quill.keyboard.addBinding(
{ key: MONOSPACE_CHAR, shortKey: true },
(_range, context) =>
this.toggleForStyle(QuillFormattingStyle.monospace, context)
);
this.quill.keyboard.addBinding(
{ key: STRIKETHROUGH_CHAR, shortKey: true, shiftKey: true },
(_range, context) =>
this.toggleForStyle(QuillFormattingStyle.strike, context)
);
this.quill.keyboard.addBinding(
{ key: SPOILER_CHAR, shortKey: true, shiftKey: true },
(_range, context) =>
this.toggleForStyle(QuillFormattingStyle.spoiler, context)
);
this.quill.keyboard.addBinding({
key: MONOSPACE_CHAR,
shortKey: true,
handler: (_range, context) =>
this.toggleForStyle(QuillFormattingStyle.monospace, context),
});
this.quill.keyboard.addBinding({
// We need to hook both because of windows/linux and the shift keu
key: [STRIKETHROUGH_CHAR, STRIKETHROUGH_CHAR.toUpperCase()],
shortKey: true,
shiftKey: true,
handler: (_range, context) =>
this.toggleForStyle(QuillFormattingStyle.strike, context),
});
this.quill.keyboard.addBinding({
// We need to hook both because of windows/linux and the shift keu
key: [SPOILER_CHAR, SPOILER_CHAR.toUpperCase()],
shortKey: true,
shiftKey: true,
handler: (_range, context) =>
this.toggleForStyle(QuillFormattingStyle.spoiler, context),
});
}
destroy(): void {
@ -261,21 +268,26 @@ export class FormattingMenu {
this.render();
}
isStyleEnabledInSelection(style: QuillFormattingStyle): boolean | undefined {
const selection = this.quill.getSelection();
if (!selection || !selection.length) {
return;
}
const contents = this.quill.getContents(selection.index, selection.length);
static isStyleEnabledForOps(
ops: Array<Op>,
style: QuillFormattingStyle
): boolean {
// Note: we special-case single \n ops because Quill doesn't apply formatting to them
if (isAllNewlines(contents.ops)) {
if (isAllNewlines(ops)) {
return false;
}
return contents.ops.every(
op => op.attributes?.[style] || isNewlineOnlyOp(op)
);
return ops.every(op => op.attributes?.[style] || isNewlineOnlyOp(op));
}
isStyleEnabledInSelection(style: QuillFormattingStyle): boolean {
const selection = this.quill.getSelection();
if (!selection || !selection.length) {
return false;
}
const contents = this.quill.getContents(selection.index, selection.length);
return FormattingMenu.isStyleEnabledForOps(contents.ops, style);
}
toggleForStyle(style: QuillFormattingStyle, context?: KeyboardContext): void {
@ -342,7 +354,7 @@ export class FormattingMenu {
isActive={isStyleEnabledInSelection(QuillFormattingStyle.bold)}
label={i18n('icu:Keyboard--composer--bold')}
onLongHover={onLongHover}
popupGuideShortcut={`${metaKey} + ${BOLD_CHAR}`}
popupGuideShortcut={`${metaKey} + ${BOLD_CHAR.toUpperCase()}`}
popupGuideText={i18n('icu:FormatMenu--guide--bold')}
style={QuillFormattingStyle.bold}
toggleForStyle={toggleForStyle}
@ -354,7 +366,7 @@ export class FormattingMenu {
)}
label={i18n('icu:Keyboard--composer--italic')}
onLongHover={onLongHover}
popupGuideShortcut={`${metaKey} + ${ITALIC_CHAR}`}
popupGuideShortcut={`${metaKey} + ${ITALIC_CHAR.toUpperCase()}`}
popupGuideText={i18n('icu:FormatMenu--guide--italic')}
style={QuillFormattingStyle.italic}
toggleForStyle={toggleForStyle}
@ -366,7 +378,7 @@ export class FormattingMenu {
)}
label={i18n('icu:Keyboard--composer--strikethrough')}
onLongHover={onLongHover}
popupGuideShortcut={`${metaKey} + ${shiftKey} + ${STRIKETHROUGH_CHAR}`}
popupGuideShortcut={`${metaKey} + ${shiftKey} + ${STRIKETHROUGH_CHAR.toUpperCase()}`}
popupGuideText={i18n('icu:FormatMenu--guide--strikethrough')}
style={QuillFormattingStyle.strike}
toggleForStyle={toggleForStyle}
@ -378,7 +390,7 @@ export class FormattingMenu {
)}
label={i18n('icu:Keyboard--composer--monospace')}
onLongHover={onLongHover}
popupGuideShortcut={`${metaKey} + ${MONOSPACE_CHAR}`}
popupGuideShortcut={`${metaKey} + ${MONOSPACE_CHAR.toUpperCase()}`}
popupGuideText={i18n('icu:FormatMenu--guide--monospace')}
style={QuillFormattingStyle.monospace}
toggleForStyle={toggleForStyle}
@ -389,7 +401,7 @@ export class FormattingMenu {
QuillFormattingStyle.spoiler
)}
onLongHover={onLongHover}
popupGuideShortcut={`${metaKey} + ${shiftKey} + ${SPOILER_CHAR}`}
popupGuideShortcut={`${metaKey} + ${shiftKey} + ${SPOILER_CHAR.toUpperCase()}`}
popupGuideText={i18n('icu:FormatMenu--guide--spoiler')}
label={i18n('icu:Keyboard--composer--spoiler')}
style={QuillFormattingStyle.spoiler}

View file

@ -1,15 +1,15 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type Parchment from 'parchment';
import Quill from 'quill';
const Inline: typeof Parchment.Inline = Quill.import('blots/inline');
import InlineBlot from '@signalapp/quill-cjs/blots/inline';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyRecord = Record<string, any>;
export class MonospaceBlot extends Inline {
export class MonospaceBlot extends InlineBlot {
static override blotName = 'monospace';
static override className = 'quill--monospace';
static override formats(): boolean {
return true;
}
@ -21,10 +21,3 @@ export class MonospaceBlot extends Inline {
}
}
}
MonospaceBlot.blotName = 'monospace';
MonospaceBlot.className = 'quill--monospace';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore See this workaround: https://github.com/quilljs/quill/issues/2312#issuecomment-1097922620
Inline.order.splice(Inline.order.indexOf('bold'), 0, MonospaceBlot.blotName);

View file

@ -1,17 +1,19 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type Parchment from 'parchment';
import Quill from 'quill';
const Inline: typeof Parchment.Inline = Quill.import('blots/inline');
import InlineBlot from '@signalapp/quill-cjs/blots/inline';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyRecord = Record<string, any>;
export class SpoilerBlot extends Inline {
static override formats(): boolean {
return true;
export class SpoilerBlot extends InlineBlot {
static override blotName = 'spoiler';
static override className = 'quill--spoiler';
static override formats(): AnyRecord {
return {
spoiler: true,
};
}
override optimize(context: AnyRecord): void {
@ -21,10 +23,3 @@ export class SpoilerBlot extends Inline {
}
}
}
SpoilerBlot.blotName = 'spoiler';
SpoilerBlot.className = 'quill--spoiler';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore See this workaround: https://github.com/quilljs/quill/issues/2312#issuecomment-1097922620
Inline.order.splice(Inline.order.indexOf('bold'), 0, SpoilerBlot.blotName);

View file

@ -1,23 +1,15 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable max-classes-per-file */
import React from 'react';
import Parchment from 'parchment';
import Quill from 'quill';
import EmbedBlot from '@signalapp/quill-cjs/blots/embed';
import { render } from 'react-dom';
import { Emojify } from '../../components/conversation/Emojify';
import { normalizeAci } from '../../util/normalizeAci';
import type { MentionBlotValue } from '../util';
declare class QuillEmbed extends Parchment.Embed {
contentNode: HTMLElement;
}
const Embed: typeof QuillEmbed = Quill.import('blots/embed');
export class MentionBlot extends Embed {
export class MentionBlot extends EmbedBlot {
static override blotName = 'mention';
static override className = 'mention-blot';

View file

@ -2,8 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import _ from 'lodash';
import type Quill from 'quill';
import Delta from 'quill-delta';
import type Quill from '@signalapp/quill-cjs';
import { Delta } from '@signalapp/quill-cjs';
import type { RefObject } from 'react';
import React from 'react';

View file

@ -1,19 +1,19 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Delta from 'quill-delta';
import { Delta } from '@signalapp/quill-cjs';
import type { RefObject } from 'react';
import type { Matcher, AttributeMap } from 'quill';
import { assertDev } from '../../util/assert';
import { isAciString } from '../../util/isAciString';
import type { MemberRepository } from '../memberRepository';
import type { Matcher } from '../util';
export const matchMention: (
memberRepositoryRef: RefObject<MemberRepository>
) => Matcher =
(memberRepositoryRef: RefObject<MemberRepository>) =>
(node: HTMLElement, delta: Delta, attributes: AttributeMap): Delta => {
(node, delta, _scroll, attributes): Delta => {
const memberRepository = memberRepositoryRef.current;
if (memberRepository) {

View file

@ -1,23 +1,10 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type Quill from 'quill';
import Delta from 'quill-delta';
const prepareText = (text: string) => {
const entities: Array<[RegExp, string]> = [
[/&/g, '&amp;'],
[/</g, '&lt;'],
[/>/g, '&gt;'],
];
const escapedEntities = entities.reduce(
(acc, [re, replaceValue]) => acc.replace(re, replaceValue),
text
);
return `<span>${escapedEntities}</span>`;
};
import type Quill from '@signalapp/quill-cjs';
import { Delta } from '@signalapp/quill-cjs';
import { FormattingMenu, QuillFormattingStyle } from '../formatting/menu';
import { insertEmojiOps } from '../util';
type ClipboardOptions = Readonly<{
isDisabled: boolean;
@ -50,7 +37,7 @@ export class SignalClipboard {
return;
}
const clipboard = this.quill.getModule('clipboard');
const { clipboard } = this.quill;
const selection = this.quill.getSelection();
const text = event.clipboardData.getData('text/plain');
const signal = event.clipboardData.getData('text/signal');
@ -72,11 +59,37 @@ export class SignalClipboard {
return;
}
const { ops } = this.quill.getContents(selection.index, selection.length);
// Only enable formatting on the pasted text if the entire selection has it enabled!
const formats =
selection.length === 0
? this.quill.getFormat(selection.index)
: {
[QuillFormattingStyle.bold]: FormattingMenu.isStyleEnabledForOps(
ops,
QuillFormattingStyle.bold
),
[QuillFormattingStyle.italic]: FormattingMenu.isStyleEnabledForOps(
ops,
QuillFormattingStyle.italic
),
[QuillFormattingStyle.monospace]:
FormattingMenu.isStyleEnabledForOps(
ops,
QuillFormattingStyle.monospace
),
[QuillFormattingStyle.spoiler]: FormattingMenu.isStyleEnabledForOps(
ops,
QuillFormattingStyle.spoiler
),
[QuillFormattingStyle.strike]: FormattingMenu.isStyleEnabledForOps(
ops,
QuillFormattingStyle.strike
),
};
const clipboardDelta = signal
? clipboard.convert(signal)
: clipboard.convert(prepareText(text));
const { scrollTop } = this.quill.scrollingContainer;
? clipboard.convert({ html: signal }, formats)
: new Delta(insertEmojiOps(clipboard.convert({ text }, formats).ops, {}));
this.quill.selection.update('silent');
@ -88,7 +101,7 @@ export class SignalClipboard {
.concat(clipboardDelta);
this.quill.updateContents(delta, 'user');
this.quill.setSelection(delta.length() - selection.length, 0, 'silent');
this.quill.scrollingContainer.scrollTop = scrollTop;
this.quill.scrollSelectionIntoView();
this.quill.focus();
}, 1);

112
ts/quill/types.d.ts vendored
View file

@ -1,112 +0,0 @@
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type UpdatedDelta from 'quill-delta';
import type { MentionCompletion } from './mentions/completion';
import type { EmojiCompletion } from './emoji/completion';
import type { FormattingMenu } from './formatting/menu';
import type { SignalClipboard } from './signal-clipboard';
declare module 'react-quill' {
// `react-quill` uses a different but compatible version of Delta
// tell it to use the type definition from the `quill-delta` library
type DeltaStatic = UpdatedDelta;
}
// We want to extend some existing interfaces.
/* eslint-disable no-restricted-syntax */
declare module 'quill' {
// this type is fixed in @types/quill, but our version of react-quill cannot
// use the version of quill that has this fix in its typings
// doing this manually allows us to use the correct type
// https://github.com/DefinitelyTyped/DefinitelyTyped/commit/6090a81c7dbd02b6b917f903a28c6c010b8432ea#diff-bff5e435d15f8f99f733c837e76945bced86bb85e93a75467015cc9b33b48212
interface UpdatedKey {
key: string | number;
shiftKey?: boolean;
shortKey?: boolean;
}
export type AttributeMap = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
};
export type Matcher = (
node: HTMLElement,
delta: UpdatedDelta,
attributes: AttributeMap
) => UpdatedDelta;
export type UpdatedTextChangeHandler = (
delta: UpdatedDelta,
oldContents: UpdatedDelta,
source: Sources
) => void;
export type UpdatedEditorChangeHandler = (
eventName: 'text-change' | 'selection-change'
) => void;
interface LeafBlot {
text?: string;
// Quill doesn't make it easy to type this result.
// (It's probably doable, but not worth our time.)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value(): any;
}
interface HistoryStatic {
undo(): void;
clear(): void;
}
interface ClipboardStatic {
convert(html: string): UpdatedDelta;
matchers: Array<unknown>;
}
interface SelectionStatic {
update(source: string): void;
}
interface Quill {
updateContents(delta: UpdatedDelta, source?: Sources): UpdatedDelta;
getContents(index?: number, length?: number): UpdatedDelta;
getLeaf(index: number): [LeafBlot, number];
// in-code reference missing in @types
scrollingContainer: HTMLElement;
on(
eventName: 'text-change',
handler: UpdatedTextChangeHandler
): EventEmitter;
on(
eventName: 'editor-change',
handler: UpdatedEditorChangeHandler
): EventEmitter;
getModule(module: 'clipboard'): ClipboardStatic;
getModule(module: 'emojiCompletion'): EmojiCompletion;
getModule(module: 'formattingMenu'): FormattingMenu;
getModule(module: 'history'): HistoryStatic;
getModule(module: 'mentionCompletion'): MentionCompletion;
getModule(module: 'signalClipboard'): SignalClipboard;
getModule(module: string): unknown;
selection: SelectionStatic;
options: Record<string, unknown>;
}
export type KeyboardContext = {
format: Record<string, unknown>;
};
interface KeyboardStatic {
addBinding(
key: UpdatedKey,
callback: (range: RangeStatic, context: KeyboardContext) => void
): void;
// in-code reference missing in @types
bindings: Record<string | number, Array<unknown>>;
}
}
/* eslint-enable no-restricted-syntax */

View file

@ -2,9 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import emojiRegex from 'emoji-regex';
import Delta from 'quill-delta';
import type { LeafBlot, DeltaOperation, AttributeMap } from 'quill';
import type Op from 'quill-delta/dist/Op';
import { Delta } from '@signalapp/quill-cjs';
import type { AttributeMap, Op, Parchment } from '@signalapp/quill-cjs';
import type {
DisplayNode,
@ -19,6 +18,14 @@ import { isNotNil } from '../util/isNotNil';
import type { AciString } from '../types/ServiceId';
import { emojiToData } from '../components/emoji/lib';
export type Matcher = (
node: HTMLElement,
delta: Delta,
_scroll: Record<string, unknown>,
// Note: this field is added in our fork
attributes: AttributeMap
) => Delta;
export type MentionBlotValue = {
aci: AciString;
title: string;
@ -28,13 +35,13 @@ export type FormattingBlotValue = {
style: BodyRange.Style;
};
export const isEmojiBlot = (blot: LeafBlot): blot is EmojiBlot =>
export const isEmojiBlot = (blot: Parchment.LeafBlot): blot is EmojiBlot =>
blot.value() && blot.value().emoji;
export const isMentionBlot = (blot: LeafBlot): blot is MentionBlot =>
export const isMentionBlot = (blot: Parchment.LeafBlot): blot is MentionBlot =>
blot.value() && blot.value().mention;
export const isFormatting = (blot: LeafBlot): blot is MentionBlot =>
export const isFormatting = (blot: Parchment.LeafBlot): blot is MentionBlot =>
blot.value() && blot.value().style;
export type RetainOp = Op & { retain: number };
@ -63,7 +70,7 @@ export const isInsertEmojiOp = (op: Op): op is InsertEmojiOp =>
export const isInsertMentionOp = (op: Op): op is InsertMentionOp =>
isSpecificInsertOp(op, 'mention');
export const getTextFromOps = (ops: Array<DeltaOperation>): string =>
export const getTextFromOps = (ops: Array<Op>): string =>
ops
.reduce((acc, op) => {
if (typeof op.insert === 'string') {
@ -130,31 +137,31 @@ function extractAllFormats(
...params,
style: BOLD,
previousData: result[BOLD],
hasStyle: op?.attributes?.[QuillFormattingStyle.bold],
hasStyle: Boolean(op?.attributes?.[QuillFormattingStyle.bold]),
});
result[ITALIC] = extractFormatRange({
...params,
style: ITALIC,
previousData: result[ITALIC],
hasStyle: op?.attributes?.[QuillFormattingStyle.italic],
hasStyle: Boolean(op?.attributes?.[QuillFormattingStyle.italic]),
});
result[MONOSPACE] = extractFormatRange({
...params,
style: MONOSPACE,
previousData: result[MONOSPACE],
hasStyle: op?.attributes?.[QuillFormattingStyle.monospace],
hasStyle: Boolean(op?.attributes?.[QuillFormattingStyle.monospace]),
});
result[SPOILER] = extractFormatRange({
...params,
style: SPOILER,
previousData: result[SPOILER],
hasStyle: op?.attributes?.[QuillFormattingStyle.spoiler],
hasStyle: Boolean(op?.attributes?.[QuillFormattingStyle.spoiler]),
});
result[STRIKETHROUGH] = extractFormatRange({
...params,
style: STRIKETHROUGH,
previousData: result[STRIKETHROUGH],
hasStyle: op?.attributes?.[QuillFormattingStyle.strike],
hasStyle: Boolean(op?.attributes?.[QuillFormattingStyle.strike]),
});
return result;
@ -280,12 +287,18 @@ export const getBlotTextPartitions = (
};
export const matchBlotTextPartitions = (
blot: LeafBlot,
blot: Parchment.LeafBlot | null,
index: number,
leftRegExp: RegExp,
rightRegExp?: RegExp
): Array<RegExpMatchArray | null> => {
const [leftText, rightText] = getBlotTextPartitions(blot.text, index);
const text = blot?.value();
if (text && typeof text !== 'string') {
// This can be an EmojiBlot, for example
return [];
}
const [leftText, rightText] = getBlotTextPartitions(text, index);
const leftMatch = leftRegExp.exec(leftText);
let rightMatch = null;

View file

@ -23,7 +23,6 @@ export type SmartCompositionTextAreaProps = Pick<
| 'theme'
| 'maxLength'
| 'whenToShowRemainingCount'
| 'scrollerRef'
>;
export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(

View file

@ -46,7 +46,7 @@ describe('emojiCompletion', () => {
it('returns left and right text', () => {
mockQuill.getSelection.returns({ index: 0, length: 0 });
const blot = {
text: ':smile:',
value: () => ':smile:',
};
mockQuill.getLeaf.returns([blot, 2]);
const [leftLeafText, rightLeafText] =
@ -79,7 +79,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text: 'smi',
value: () => 'smi',
};
mockQuill.getLeaf.returns([blot, 3]);
@ -99,7 +99,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text: '10:30',
value: () => '10:30',
};
mockQuill.getLeaf.returns([blot, 5]);
@ -119,7 +119,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text: ':s',
value: () => ':s',
};
mockQuill.getLeaf.returns([blot, 2]);
@ -139,7 +139,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text: ':smy',
value: () => ':smy',
};
mockQuill.getLeaf.returns([blot, 4]);
@ -159,7 +159,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text: ':smi',
value: () => ':smi',
};
mockQuill.getLeaf.returns([blot, 4]);
@ -185,7 +185,7 @@ describe('emojiCompletion', () => {
beforeEach(() => {
const blot = {
text,
value: () => text,
};
mockQuill.getLeaf.returns([blot, 7]);
@ -210,7 +210,7 @@ describe('emojiCompletion', () => {
beforeEach(() => {
const blot = {
text,
value: () => text,
};
mockQuill.getSelection.returns({
index: 13,
@ -246,7 +246,7 @@ describe('emojiCompletion', () => {
beforeEach(() => {
const blot = {
text,
value: () => text,
};
mockQuill.getLeaf.returns([blot, 7]);
@ -274,7 +274,7 @@ describe('emojiCompletion', () => {
describe('and given it matches a short name', () => {
beforeEach(() => {
const blot = {
text: validEmoji,
value: () => validEmoji,
};
mockQuill.getLeaf.returns([blot, middleCursorIndex]);
@ -297,7 +297,7 @@ describe('emojiCompletion', () => {
describe('and given it does not match a short name', () => {
beforeEach(() => {
const blot = {
text: invalidEmoji,
value: () => invalidEmoji,
};
mockQuill.getLeaf.returns([blot, middleCursorIndex]);
@ -323,7 +323,7 @@ describe('emojiCompletion', () => {
beforeEach(() => {
const blot = {
text,
value: () => text,
};
mockQuill.getLeaf.returns([blot, 6]);
@ -365,7 +365,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text,
value: () => text,
};
mockQuill.getLeaf.returns([blot, index]);
@ -393,7 +393,7 @@ describe('emojiCompletion', () => {
});
const blot = {
text,
value: () => text,
};
mockQuill.getLeaf.returns([blot, index]);

View file

@ -2,10 +2,11 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import Delta from 'quill-delta';
import { Delta } from '@signalapp/quill-cjs';
import type { SinonStub } from 'sinon';
import sinon from 'sinon';
import type { Quill, KeyboardStatic } from 'quill';
import type Quill from '@signalapp/quill-cjs';
import type Keyboard from '@signalapp/quill-cjs/modules/keyboard';
import type { MutableRefObject } from 'react';
import type { MentionCompletionOptions } from '../../../quill/mentions/completion';
@ -17,6 +18,10 @@ import { ThemeType } from '../../../types/Util';
import { getDefaultConversationWithServiceId } from '../../../test-both/helpers/getDefaultConversation';
import { setupI18n } from '../../../util/setupI18n';
type MiniLeafBlot = {
value: () => string;
};
const me: ConversationType = getDefaultConversationWithServiceId({
id: '666777',
title: 'Fred Savage',
@ -70,7 +75,7 @@ describe('MentionCompletion', () => {
Partial<{ [K in keyof Quill]: SinonStub }>,
'keyboard'
> & {
keyboard: Partial<{ [K in keyof KeyboardStatic]: SinonStub }>;
keyboard: Partial<{ [K in keyof Keyboard]: SinonStub }>;
};
let mentionCompletion: MentionCompletion;
@ -169,7 +174,10 @@ describe('MentionCompletion', () => {
beforeEach(() => {
mentionCompletion.results = members;
mockQuill.getSelection?.returns({ index: 5 });
mockQuill.getLeaf?.returns([{ text: '@shia' }, 5]);
const blot: MiniLeafBlot = {
value: () => '@shia',
};
mockQuill.getLeaf?.returns([blot, 5]);
insertMentionStub = sinon.stub(mentionCompletion, 'insertMention');
});
@ -210,7 +218,10 @@ describe('MentionCompletion', () => {
describe('from the middle of a string', () => {
beforeEach(() => {
mockQuill.getSelection?.returns({ index: 9 });
mockQuill.getLeaf?.returns([{ text: 'foo @shia bar' }, 9]);
const blot: MiniLeafBlot = {
value: () => 'foo @shia bar',
};
mockQuill.getLeaf?.returns([blot, 9]);
});
it('inserts correctly', () => {
@ -237,8 +248,8 @@ describe('MentionCompletion', () => {
beforeEach(() => {
mockQuill.getSelection?.returns({ index });
const blot = {
text,
const blot: MiniLeafBlot = {
value: () => text,
};
mockQuill.getLeaf?.returns([blot, index]);
@ -265,8 +276,8 @@ describe('MentionCompletion', () => {
const text = '@zoe';
const index = text.length;
mockQuill.getSelection?.returns({ index });
const blot = {
text,
const blot: MiniLeafBlot = {
value: () => text,
};
mockQuill.getLeaf?.returns([blot, index]);
mentionCompletion.completeMention(2);

View file

@ -3,7 +3,7 @@
import { assert } from 'chai';
import type { RefObject } from 'react';
import Delta from 'quill-delta';
import { Delta } from '@signalapp/quill-cjs';
import type { AciString } from '../../../types/ServiceId';
import { generateAci } from '../../../types/ServiceId';
@ -101,6 +101,7 @@ describe('matchMention', () => {
title: memberMahershala.title,
}),
EMPTY_DELTA,
{},
existingAttributes
);
const { ops } = result;
@ -129,6 +130,7 @@ describe('matchMention', () => {
title: memberMahershala.title,
}),
EMPTY_DELTA,
{},
{}
);
const { ops } = result;
@ -155,6 +157,7 @@ describe('matchMention', () => {
title: 'Nonexistent',
}),
EMPTY_DELTA,
{},
{}
);
const { ops } = result;
@ -178,6 +181,7 @@ describe('matchMention', () => {
title: 'Nonexistent',
}),
EMPTY_DELTA,
{},
{}
);
const { ops } = result;
@ -195,7 +199,12 @@ describe('matchMention', () => {
});
it('passes other clipboard elements through', () => {
const result = matcher(createMockElement('ignore', {}), EMPTY_DELTA, {});
const result = matcher(
createMockElement('ignore', {}),
EMPTY_DELTA,
{},
{}
);
assert.equal(result, EMPTY_DELTA);
});
});

View file

@ -93,6 +93,178 @@
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:21:51.887Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
"line": " innerHTML",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
"line": " const [start, end] = outerHTML.split(`>${innerHTML}<`);",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-outerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
"line": " outerHTML,",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-outerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
"line": " const [start, end] = outerHTML.split(`>${innerHTML}<`);",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-outerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
"line": " return blot.domNode instanceof Element ? blot.domNode.outerHTML : '';",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/quill.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/quill.js",
"line": " const html = this.container.innerHTML.trim();",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/dist/quill.core.js",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-22T02:55:11.616Z"
},
{
"rule": "DOM-outerHTML",
"path": "node_modules/@signalapp/quill-cjs/dist/quill.core.js",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-22T02:55:11.616Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/dist/quill.js",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-22T02:55:11.616Z"
},
{
"rule": "DOM-outerHTML",
"path": "node_modules/@signalapp/quill-cjs/dist/quill.js",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-22T02:55:11.616Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/modules/normalizeExternalHTML/normalizers/msWord.js",
"line": " li.innerHTML = listItem.element.innerHTML;",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-10T05:52:01.516Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/modules/normalizeExternalHTML/normalizers/msWord.js",
"line": " const html = doc.documentElement.innerHTML;",
"reasonCategory": "usageTrusted",
"updated": "2025-01-10T05:52:01.516Z",
"reasonDetail": "Reading contents of DOM"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/modules/syntax.js",
"line": " container.innerHTML = highlight(this.options.hljs, language, text);",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-10T05:52:01.516Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/themes/base.js",
"line": " button.innerHTML = icons[name][''] + icons[name].rtl;",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-10T05:52:01.516Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/themes/base.js",
"line": " button.innerHTML = icons[name];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/themes/base.js",
"line": " button.innerHTML = icons[name][value];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/ui/color-picker.js",
"line": " this.label.innerHTML = label;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/ui/icon-picker.js",
"line": " if (this.label.innerHTML === item.innerHTML) return;",
"reasonCategory": "notExercisedByOurApp",
"updated": "2025-01-10T05:52:01.516Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/ui/icon-picker.js",
"line": " item.innerHTML = icons[item.getAttribute('data-value') || ''];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/ui/icon-picker.js",
"line": " this.label.innerHTML = item.innerHTML;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/ui/picker.js",
"line": " label.innerHTML = DropdownIcon;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/ui/tooltip.js",
"line": " this.root.innerHTML = this.constructor.TEMPLATE;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@sindresorhus/is/dist/index.js",
@ -761,6 +933,49 @@
"reasonCategory": "testCode",
"updated": "2024-06-24T19:19:28.335Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\t\tmultiArgs: false,",
"reasonCategory": "falseMatch",
"updated": "2024-12-20T19:43:51.589Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\tmultiArgs: false,",
"reasonCategory": "falseMatch",
"updated": "2024-12-20T19:43:51.589Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\t\tconst value = options.multiArgs ? args : args[0];",
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
"updated": "2024-12-20T19:43:51.589Z",
"reasonDetail": "<optional>"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\tconst value = options.multiArgs ? args : args[0];",
"reasonCategory": "otherUtilityCode",
"updated": "2024-12-20T19:43:51.589Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\terror = options.multiArgs ? args : args[0];",
"reasonCategory": "otherUtilityCode",
"updated": "2024-12-20T19:46:49.191Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\tconst value = options.multiArgs ? args : args[0];",
"reasonCategory": "otherUtilityCode",
"updated": "2024-12-20T19:46:49.191Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/parse-entities/decode-entity.browser.js",
@ -863,295 +1078,6 @@
"updated": "2022-01-04T20:32:10.596Z",
"reasonDetail": "Sample code never required by us."
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/core/quill.js",
"line": " let html = this.container.innerHTML.trim();",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/core/quill.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.core.js",
"line": " this.container.innerHTML = html.replace(/\\>\\r?\\n +\\</g, '><'); // Remove spaces between tags",
"reasonCategory": "notExercisedByOurApp",
"updated": "2023-05-17T16:29:59.196Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.core.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "notExercisedByOurApp",
"updated": "2023-05-17T16:29:59.196Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.core.js",
"line": " var html = this.container.innerHTML.trim();",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.core.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.core.js",
"line": " debug.log('convert', this.container.innerHTML, delta);",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.core.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " var html = this.container.innerHTML.trim();",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " label.innerHTML = _dropdown2.default;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " button.innerHTML = icons[name][''] + icons[name]['rtl'];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " button.innerHTML = icons[name];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " button.innerHTML = icons[name][value];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " debug.log('convert', this.container.innerHTML, delta);",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " _this.label.innerHTML = label;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " item.innerHTML = icons[item.getAttribute('data-value') || ''];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " this.label.innerHTML = item.innerHTML;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " this.root.innerHTML = this.constructor.TEMPLATE;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " this.domNode.innerHTML = _highlight(text);",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " // this.container.innerHTML = html.replace(/\\>\\r?\\n +\\</g, '><'); // Remove spaces between tags",
"reasonCategory": "usageTrusted",
"updated": "2023-09-28T00:50:24.377Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " // this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2023-09-28T00:50:24.377Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.js",
"line": " // this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2023-09-28T00:50:24.377Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/dist/quill.min.js",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/modules/clipboard.js",
"line": " this.container.innerHTML = html.replace(/\\>\\r?\\n +\\</g, '><'); // Remove spaces between tags",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/modules/clipboard.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/modules/clipboard.js",
"line": " this.container.innerHTML = '';",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/modules/clipboard.js",
"line": " debug.log('convert', this.container.innerHTML, delta);",
"reasonCategory": "usageTrusted",
"updated": "2021-12-01T01:31:12.757Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/modules/syntax.js",
"line": " this.domNode.innerHTML = highlight(text);",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/themes/base.js",
"line": " button.innerHTML = icons[name][''] + icons[name]['rtl'];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/themes/base.js",
"line": " button.innerHTML = icons[name];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/themes/base.js",
"line": " button.innerHTML = icons[name][value];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/ui/color-picker.js",
"line": " this.label.innerHTML = label;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/ui/icon-picker.js",
"line": " item.innerHTML = icons[item.getAttribute('data-value') || ''];",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/ui/icon-picker.js",
"line": " this.label.innerHTML = item.innerHTML;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/ui/picker.js",
"line": " label.innerHTML = DropdownIcon;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/quill/ui/tooltip.js",
"line": " this.root.innerHTML = this.constructor.TEMPLATE;",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for quill"
},
{
"rule": "eval",
"path": "node_modules/raw-body/node_modules/depd/index.js",
@ -1201,51 +1127,6 @@
"reasonCategory": "usageTrusted",
"updated": "2024-11-14T18:53:33.345Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/react-quill/dist/react-quill.js",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for react-quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/react-quill/dist/react-quill.js",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for react-quill"
},
{
"rule": "React-findDOMNode",
"path": "node_modules/react-quill/dist/react-quill.js",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for react-quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/react-quill/lib/index.js",
"line": " (_b = (_a = _this).onEditorChangeText) === null || _b === void 0 ? void 0 : _b.call(_a, _this.editor.root.innerHTML, rangeOrDelta, source, _this.unprivilegedEditor);",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for react-quill"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/react-quill/lib/index.js",
"line": " getHTML: function () { return e.root.innerHTML; },",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for react-quill"
},
{
"rule": "React-findDOMNode",
"path": "node_modules/react-quill/lib/index.js",
"line": " var element = react_dom_1.default.findDOMNode(this.editingArea);",
"reasonCategory": "usageTrusted",
"updated": "2020-10-13T18:36:57.012Z",
"reasonDetail": "necessary for react-quill"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.cjs.js",
@ -2008,13 +1889,6 @@
"updated": "2022-06-25T00:06:19.860Z",
"reasonDetail": "Not used for DOM manipulation"
},
{
"rule": "React-useRef",
"path": "ts/components/CompositionInput.tsx",
"line": " const scrollerRefInner = React.useRef<HTMLDivElement>(null);",
"reasonCategory": "usageTrusted",
"updated": "2022-10-03T16:06:12.837Z"
},
{
"rule": "React-useRef",
"path": "ts/components/CompositionRecording.tsx",
@ -2811,48 +2685,5 @@
"line": " message.innerHTML = window.i18n('icu:optimizingApplication');",
"reasonCategory": "usageTrusted",
"updated": "2021-09-17T21:02:59.414Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\t\tmultiArgs: false,",
"reasonCategory": "falseMatch",
"updated": "2024-12-20T19:43:51.589Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\t\tconst value = options.multiArgs ? args : args[0];",
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
"updated": "2024-12-20T19:43:51.589Z",
"reasonDetail": "<optional>"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\tmultiArgs: false,",
"reasonCategory": "falseMatch",
"updated": "2024-12-20T19:43:51.589Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\tconst value = options.multiArgs ? args : args[0];",
"reasonCategory": "otherUtilityCode",
"updated": "2024-12-20T19:43:51.589Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\terror = options.multiArgs ? args : args[0];",
"reasonCategory": "otherUtilityCode",
"updated": "2024-12-20T19:46:49.191Z"
},
{
"rule": "thenify-multiArgs",
"path": "node_modules/p-event/index.js",
"line": "\t\tconst value = options.multiArgs ? args : args[0];",
"reasonCategory": "otherUtilityCode",
"updated": "2024-12-20T19:46:49.191Z"
}
]