Experimental Shortcuts settings import/export

This commit is contained in:
Lim Chee Aun 2023-08-15 20:14:09 +08:00
parent 4817eddc2a
commit 8790b20354
7 changed files with 551 additions and 144 deletions

278
package-lock.json generated
View file

@ -10,21 +10,22 @@
"dependencies": {
"@formatjs/intl-localematcher": "~0.4.0",
"@github/text-expander-element": "~2.5.0",
"@iconify-icons/mingcute": "~1.2.6",
"@iconify-icons/mingcute": "~1.2.7",
"@justinribeiro/lite-youtube": "~1.5.0",
"@szhsin/react-menu": "~4.0.2",
"@uidotdev/usehooks": "~2.0.1",
"@szhsin/react-menu": "~4.0.3",
"@uidotdev/usehooks": "~2.1.0",
"dayjs": "~1.11.9",
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
"fast-deep-equal": "~3.1.3",
"idb-keyval": "~6.2.1",
"just-debounce-it": "~3.2.0",
"lz-string": "^1.5.0",
"masto": "~5.11.4",
"mem": "~9.0.2",
"p-retry": "~5.1.2",
"p-throttle": "~5.1.0",
"preact": "~10.16.0",
"preact": "~10.17.0",
"react-hotkeys-hook": "~4.4.1",
"react-intersection-observer": "~9.5.2",
"react-quick-pinch-zoom": "~4.9.0",
@ -42,10 +43,10 @@
"@preact/preset-vite": "~2.5.0",
"@trivago/prettier-plugin-sort-imports": "~4.2.0",
"postcss": "~8.4.27",
"postcss-dark-theme-class": "~0.7.3",
"postcss-preset-env": "~9.1.0",
"postcss-dark-theme-class": "~0.8.0",
"postcss-preset-env": "~9.1.1",
"twitter-text": "~3.1.0",
"vite": "~4.4.7",
"vite": "~4.4.9",
"vite-plugin-generate-file": "~0.0.4",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-pwa": "~0.16.4",
@ -1965,9 +1966,9 @@
}
},
"node_modules/@csstools/media-query-list-parser": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.3.tgz",
"integrity": "sha512-ATul1u+pic4aVpstgueqxEv4MsObEbszAxfTXpx9LHaeD3LAh+wFqdCteyegWmjk0k5rkSCAvIOaJe9U3DD09w==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.4.tgz",
"integrity": "sha512-V/OUXYX91tAC1CDsiY+HotIcJR+vPtzrX8pCplCpT++i8ThZZsq5F5dzZh/bDM3WUOjrvC1ljed1oSJxMfjqhw==",
"dev": true,
"funding": [
{
@ -2301,9 +2302,9 @@
}
},
"node_modules/@csstools/postcss-media-minmax": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.6.tgz",
"integrity": "sha512-BmwKkqEzzQz6D+5ctoacsiGrq4kVgd1PMEPwkwdR0qFaL2C2nguGsWG87xEw+HIts/2yxhIPTm7Jp3DQq+wn3Q==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.7.tgz",
"integrity": "sha512-5LGLdu8cJgRPmvkjUNqOPKIKeHbyQmoGKooB5Rh0mp5mLaNI9bl+IjFZ2keY0cztZYsriJsGf6Lu8R5XetuwoQ==",
"dev": true,
"funding": [
{
@ -2319,7 +2320,7 @@
"@csstools/css-calc": "^1.1.3",
"@csstools/css-parser-algorithms": "^2.3.1",
"@csstools/css-tokenizer": "^2.2.0",
"@csstools/media-query-list-parser": "^2.1.3"
"@csstools/media-query-list-parser": "^2.1.4"
},
"engines": {
"node": "^14 || ^16 || >=18"
@ -2329,9 +2330,9 @@
}
},
"node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-2.0.1.tgz",
"integrity": "sha512-UvMYxXT3R011whbxzRwLx7d7eNGyVsnZo7waAmf10ZGnT34XidY+rsdFnk6OdFwuG6FYqw3/tptQEAZOmUgvLw==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-2.0.2.tgz",
"integrity": "sha512-kQJR6NvTRidsaRjCdHGjra2+fLoFiDQOm5B2aZrhmXqng/hweXjruboKzB326rxQO2L0m0T+gCKbZgyuncyhLg==",
"dev": true,
"funding": [
{
@ -2346,7 +2347,7 @@
"dependencies": {
"@csstools/css-parser-algorithms": "^2.3.1",
"@csstools/css-tokenizer": "^2.2.0",
"@csstools/media-query-list-parser": "^2.1.3"
"@csstools/media-query-list-parser": "^2.1.4"
},
"engines": {
"node": "^14 || ^16 || >=18"
@ -3009,9 +3010,9 @@
}
},
"node_modules/@iconify-icons/mingcute": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@iconify-icons/mingcute/-/mingcute-1.2.6.tgz",
"integrity": "sha512-ToWyd3IuI+bU+q51T0GMEv7utgVksEAlzVoTSEK9GmxYG6qvUX0rKo2wMrRA4X+cv9WqDRJqJZN0pue9uUszDQ==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@iconify-icons/mingcute/-/mingcute-1.2.7.tgz",
"integrity": "sha512-GObX5YACRhYunL6L8nJ3PGQ+vs9vzvsx8FBZSCs2S3awMwIPKpWPVnjIlx7urnyN5qJoHZGV0iiEVjOmdHDTuw==",
"dependencies": {
"@iconify/types": "*"
}
@ -3249,9 +3250,9 @@
}
},
"node_modules/@szhsin/react-menu": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-4.0.2.tgz",
"integrity": "sha512-cYpktkWng7jCTPKog33w5iYldbaHQso5aJFd+7j3SkhInqYWjxiG0TtxUS0c5yFqLm6woGQEJHiBpiYHIaYMxg==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-4.0.3.tgz",
"integrity": "sha512-TPsOKLEkesE79802evnLt2Mbv/+zwRJdX8776/vxK5ST9SK8SO0A8kRrus6JuxijLxZxFpmY/3VMdoyeCWQHKA==",
"dependencies": {
"prop-types": "^15.7.2",
"react-transition-state": "^2.1.0"
@ -3395,9 +3396,9 @@
"dev": true
},
"node_modules/@uidotdev/usehooks": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.0.1.tgz",
"integrity": "sha512-rJXxE3Y8g9utRbOS9Pj9tIvrnOdaakHIhLbMxBlErV8HydnGD0DveD82aLBfVTh1hBp5IXqpeHpMrPE9WIT7vQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.1.0.tgz",
"integrity": "sha512-D7SJiNQC1BOHgtE2dy2KvOtnRNaLWTFFHvcBLg7lZ8Jz7YcimxdUY3spqpvf/mVkGCuUHee8i/79p5vVkBgsYQ==",
"engines": {
"node": ">=16"
},
@ -3710,9 +3711,9 @@
}
},
"node_modules/browserslist": {
"version": "4.21.9",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==",
"version": "4.21.10",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
"integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
"dev": true,
"funding": [
{
@ -3729,9 +3730,9 @@
}
],
"dependencies": {
"caniuse-lite": "^1.0.30001503",
"electron-to-chromium": "^1.4.431",
"node-releases": "^2.0.12",
"caniuse-lite": "^1.0.30001517",
"electron-to-chromium": "^1.4.477",
"node-releases": "^2.0.13",
"update-browserslist-db": "^1.0.11"
},
"bin": {
@ -3781,9 +3782,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001512",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz",
"integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==",
"version": "1.0.30001519",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz",
"integrity": "sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==",
"dev": true,
"funding": [
{
@ -4139,9 +4140,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.451",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.451.tgz",
"integrity": "sha512-YYbXHIBxAHe3KWvGOJOuWa6f3tgow44rBW+QAuwVp2DvGqNZeE//K2MowNdWS7XE8li5cgQDrX1LdBr41LufkA==",
"version": "1.4.490",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz",
"integrity": "sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==",
"dev": true
},
"node_modules/es-abstract": {
@ -5280,6 +5281,14 @@
"node": ">=10"
}
},
"node_modules/lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"bin": {
"lz-string": "bin/bin.js"
}
},
"node_modules/magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
@ -5485,9 +5494,9 @@
}
},
"node_modules/node-releases": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==",
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
"dev": true
},
"node_modules/normalize-range": {
@ -5862,17 +5871,23 @@
}
},
"node_modules/postcss-dark-theme-class": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/postcss-dark-theme-class/-/postcss-dark-theme-class-0.7.3.tgz",
"integrity": "sha512-M9vtfh8ORzQsVdT9BWb+xpEDAzC7nHBn7wVc988/JkEVLPupKcUnV0jw7RZ8sSj0ovpqN1POf6PLdt19JCHfhQ==",
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/postcss-dark-theme-class/-/postcss-dark-theme-class-0.8.0.tgz",
"integrity": "sha512-/zyywenvSJVlG1Ie/MLkQBhoh0sTOKPQa+3exaBVAmeITuGscGZu1NuJc5qpv2+ywIkBujL9OU26bj0DdUgY2Q==",
"dev": true,
"engines": {
"node": ">=12.0"
},
"funding": {
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"engines": {
"node": ">=18.0"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
@ -6097,9 +6112,9 @@
}
},
"node_modules/postcss-nesting": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.0.tgz",
"integrity": "sha512-knqwW65kxssmyIFadRSimaiRyLVRd0MdwfabesKw6XvGLwSOCJ+4zfvNQQCOOYij5obwpZzDpODuGRv2PCyiUw==",
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.1.tgz",
"integrity": "sha512-6LCqCWP9pqwXw/njMvNK0hGY44Fxc4B2EsGbn6xDcxbNRzP8GYoxT7yabVVMLrX3quqOJ9hg2jYMsnkedOf8pA==",
"dev": true,
"funding": [
{
@ -6204,9 +6219,9 @@
}
},
"node_modules/postcss-preset-env": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.1.0.tgz",
"integrity": "sha512-G+x9BD7jb9uHBB7o720emXV00CP+VdWeirJsHC5ERSpbTd2e6Xg7vHzT+a6UkxFyddALuV+Q8wJMgeTKaau+Pg==",
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.1.1.tgz",
"integrity": "sha512-rMPEqyTLm8JLbvaHnDAdQg6SN4Z/NDOsm+CRefg4HmSOiNpTcBXaw4RAaQbfTNe8BB75l4NpoQ6sMdrutdEpdQ==",
"dev": true,
"funding": [
{
@ -6231,8 +6246,8 @@
"@csstools/postcss-logical-float-and-clear": "^2.0.0",
"@csstools/postcss-logical-resize": "^2.0.0",
"@csstools/postcss-logical-viewport-units": "^2.0.1",
"@csstools/postcss-media-minmax": "^1.0.6",
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.1",
"@csstools/postcss-media-minmax": "^1.0.7",
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.2",
"@csstools/postcss-nested-calc": "^3.0.0",
"@csstools/postcss-normalize-display-values": "^3.0.0",
"@csstools/postcss-oklab-function": "^3.0.1",
@ -6244,7 +6259,7 @@
"@csstools/postcss-trigonometric-functions": "^3.0.1",
"@csstools/postcss-unset-value": "^3.0.0",
"autoprefixer": "^10.4.14",
"browserslist": "^4.21.9",
"browserslist": "^4.21.10",
"css-blank-pseudo": "^6.0.0",
"css-has-pseudo": "^6.0.0",
"css-prefers-color-scheme": "^9.0.0",
@ -6267,7 +6282,7 @@
"postcss-initial": "^4.0.1",
"postcss-lab-function": "^6.0.1",
"postcss-logical": "^7.0.0",
"postcss-nesting": "^12.0.0",
"postcss-nesting": "^12.0.1",
"postcss-opacity-percentage": "^2.0.0",
"postcss-overflow-shorthand": "^5.0.0",
"postcss-page-break": "^3.0.4",
@ -6357,9 +6372,9 @@
"dev": true
},
"node_modules/preact": {
"version": "10.16.0",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.16.0.tgz",
"integrity": "sha512-XTSj3dJ4roKIC93pald6rWuB2qQJO9gO2iLLyTe87MrjQN+HklueLsmskbywEWqCHlclgz3/M4YLL2iBr9UmMA==",
"version": "10.17.0",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.17.0.tgz",
"integrity": "sha512-SNsI8cbaCcUS5tbv9nlXuCfIXnJ9ysBMWk0WnB6UWwcVA3qZ2O6FxqDFECMAMttvLQcW/HaNZUe2BLidyvrVYw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
@ -6699,9 +6714,9 @@
}
},
"node_modules/rollup": {
"version": "3.26.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz",
"integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz",
"integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
@ -7362,14 +7377,14 @@
}
},
"node_modules/vite": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
"integrity": "sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==",
"version": "4.4.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
"dev": true,
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.26",
"rollup": "^3.25.2"
"postcss": "^8.4.27",
"rollup": "^3.27.1"
},
"bin": {
"vite": "bin/vite.js"
@ -9230,9 +9245,9 @@
"dev": true
},
"@csstools/media-query-list-parser": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.3.tgz",
"integrity": "sha512-ATul1u+pic4aVpstgueqxEv4MsObEbszAxfTXpx9LHaeD3LAh+wFqdCteyegWmjk0k5rkSCAvIOaJe9U3DD09w==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.4.tgz",
"integrity": "sha512-V/OUXYX91tAC1CDsiY+HotIcJR+vPtzrX8pCplCpT++i8ThZZsq5F5dzZh/bDM3WUOjrvC1ljed1oSJxMfjqhw==",
"dev": true,
"requires": {}
},
@ -9359,26 +9374,26 @@
}
},
"@csstools/postcss-media-minmax": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.6.tgz",
"integrity": "sha512-BmwKkqEzzQz6D+5ctoacsiGrq4kVgd1PMEPwkwdR0qFaL2C2nguGsWG87xEw+HIts/2yxhIPTm7Jp3DQq+wn3Q==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.7.tgz",
"integrity": "sha512-5LGLdu8cJgRPmvkjUNqOPKIKeHbyQmoGKooB5Rh0mp5mLaNI9bl+IjFZ2keY0cztZYsriJsGf6Lu8R5XetuwoQ==",
"dev": true,
"requires": {
"@csstools/css-calc": "^1.1.3",
"@csstools/css-parser-algorithms": "^2.3.1",
"@csstools/css-tokenizer": "^2.2.0",
"@csstools/media-query-list-parser": "^2.1.3"
"@csstools/media-query-list-parser": "^2.1.4"
}
},
"@csstools/postcss-media-queries-aspect-ratio-number-values": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-2.0.1.tgz",
"integrity": "sha512-UvMYxXT3R011whbxzRwLx7d7eNGyVsnZo7waAmf10ZGnT34XidY+rsdFnk6OdFwuG6FYqw3/tptQEAZOmUgvLw==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-2.0.2.tgz",
"integrity": "sha512-kQJR6NvTRidsaRjCdHGjra2+fLoFiDQOm5B2aZrhmXqng/hweXjruboKzB326rxQO2L0m0T+gCKbZgyuncyhLg==",
"dev": true,
"requires": {
"@csstools/css-parser-algorithms": "^2.3.1",
"@csstools/css-tokenizer": "^2.2.0",
"@csstools/media-query-list-parser": "^2.1.3"
"@csstools/media-query-list-parser": "^2.1.4"
}
},
"@csstools/postcss-nested-calc": {
@ -9663,9 +9678,9 @@
}
},
"@iconify-icons/mingcute": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@iconify-icons/mingcute/-/mingcute-1.2.6.tgz",
"integrity": "sha512-ToWyd3IuI+bU+q51T0GMEv7utgVksEAlzVoTSEK9GmxYG6qvUX0rKo2wMrRA4X+cv9WqDRJqJZN0pue9uUszDQ==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@iconify-icons/mingcute/-/mingcute-1.2.7.tgz",
"integrity": "sha512-GObX5YACRhYunL6L8nJ3PGQ+vs9vzvsx8FBZSCs2S3awMwIPKpWPVnjIlx7urnyN5qJoHZGV0iiEVjOmdHDTuw==",
"requires": {
"@iconify/types": "*"
}
@ -9865,9 +9880,9 @@
}
},
"@szhsin/react-menu": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-4.0.2.tgz",
"integrity": "sha512-cYpktkWng7jCTPKog33w5iYldbaHQso5aJFd+7j3SkhInqYWjxiG0TtxUS0c5yFqLm6woGQEJHiBpiYHIaYMxg==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-4.0.3.tgz",
"integrity": "sha512-TPsOKLEkesE79802evnLt2Mbv/+zwRJdX8776/vxK5ST9SK8SO0A8kRrus6JuxijLxZxFpmY/3VMdoyeCWQHKA==",
"requires": {
"prop-types": "^15.7.2",
"react-transition-state": "^2.1.0"
@ -9987,9 +10002,9 @@
"dev": true
},
"@uidotdev/usehooks": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.0.1.tgz",
"integrity": "sha512-rJXxE3Y8g9utRbOS9Pj9tIvrnOdaakHIhLbMxBlErV8HydnGD0DveD82aLBfVTh1hBp5IXqpeHpMrPE9WIT7vQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.1.0.tgz",
"integrity": "sha512-D7SJiNQC1BOHgtE2dy2KvOtnRNaLWTFFHvcBLg7lZ8Jz7YcimxdUY3spqpvf/mVkGCuUHee8i/79p5vVkBgsYQ==",
"requires": {}
},
"@vue/compiler-core": {
@ -10229,14 +10244,14 @@
}
},
"browserslist": {
"version": "4.21.9",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==",
"version": "4.21.10",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
"integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001503",
"electron-to-chromium": "^1.4.431",
"node-releases": "^2.0.12",
"caniuse-lite": "^1.0.30001517",
"electron-to-chromium": "^1.4.477",
"node-releases": "^2.0.13",
"update-browserslist-db": "^1.0.11"
}
},
@ -10271,9 +10286,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001512",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz",
"integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==",
"version": "1.0.30001519",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz",
"integrity": "sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==",
"dev": true
},
"capital-case": {
@ -10505,9 +10520,9 @@
}
},
"electron-to-chromium": {
"version": "1.4.451",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.451.tgz",
"integrity": "sha512-YYbXHIBxAHe3KWvGOJOuWa6f3tgow44rBW+QAuwVp2DvGqNZeE//K2MowNdWS7XE8li5cgQDrX1LdBr41LufkA==",
"version": "1.4.490",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz",
"integrity": "sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==",
"dev": true
},
"es-abstract": {
@ -11357,6 +11372,11 @@
"yallist": "^4.0.0"
}
},
"lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="
},
"magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
@ -11507,9 +11527,9 @@
}
},
"node-releases": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==",
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
"dev": true
},
"normalize-range": {
@ -11719,9 +11739,9 @@
}
},
"postcss-dark-theme-class": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/postcss-dark-theme-class/-/postcss-dark-theme-class-0.7.3.tgz",
"integrity": "sha512-M9vtfh8ORzQsVdT9BWb+xpEDAzC7nHBn7wVc988/JkEVLPupKcUnV0jw7RZ8sSj0ovpqN1POf6PLdt19JCHfhQ==",
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/postcss-dark-theme-class/-/postcss-dark-theme-class-0.8.0.tgz",
"integrity": "sha512-/zyywenvSJVlG1Ie/MLkQBhoh0sTOKPQa+3exaBVAmeITuGscGZu1NuJc5qpv2+ywIkBujL9OU26bj0DdUgY2Q==",
"dev": true,
"requires": {}
},
@ -11814,9 +11834,9 @@
}
},
"postcss-nesting": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.0.tgz",
"integrity": "sha512-knqwW65kxssmyIFadRSimaiRyLVRd0MdwfabesKw6XvGLwSOCJ+4zfvNQQCOOYij5obwpZzDpODuGRv2PCyiUw==",
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.0.1.tgz",
"integrity": "sha512-6LCqCWP9pqwXw/njMvNK0hGY44Fxc4B2EsGbn6xDcxbNRzP8GYoxT7yabVVMLrX3quqOJ9hg2jYMsnkedOf8pA==",
"dev": true,
"requires": {
"@csstools/selector-specificity": "^3.0.0",
@ -11856,9 +11876,9 @@
}
},
"postcss-preset-env": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.1.0.tgz",
"integrity": "sha512-G+x9BD7jb9uHBB7o720emXV00CP+VdWeirJsHC5ERSpbTd2e6Xg7vHzT+a6UkxFyddALuV+Q8wJMgeTKaau+Pg==",
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.1.1.tgz",
"integrity": "sha512-rMPEqyTLm8JLbvaHnDAdQg6SN4Z/NDOsm+CRefg4HmSOiNpTcBXaw4RAaQbfTNe8BB75l4NpoQ6sMdrutdEpdQ==",
"dev": true,
"requires": {
"@csstools/postcss-cascade-layers": "^4.0.0",
@ -11873,8 +11893,8 @@
"@csstools/postcss-logical-float-and-clear": "^2.0.0",
"@csstools/postcss-logical-resize": "^2.0.0",
"@csstools/postcss-logical-viewport-units": "^2.0.1",
"@csstools/postcss-media-minmax": "^1.0.6",
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.1",
"@csstools/postcss-media-minmax": "^1.0.7",
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.2",
"@csstools/postcss-nested-calc": "^3.0.0",
"@csstools/postcss-normalize-display-values": "^3.0.0",
"@csstools/postcss-oklab-function": "^3.0.1",
@ -11886,7 +11906,7 @@
"@csstools/postcss-trigonometric-functions": "^3.0.1",
"@csstools/postcss-unset-value": "^3.0.0",
"autoprefixer": "^10.4.14",
"browserslist": "^4.21.9",
"browserslist": "^4.21.10",
"css-blank-pseudo": "^6.0.0",
"css-has-pseudo": "^6.0.0",
"css-prefers-color-scheme": "^9.0.0",
@ -11909,7 +11929,7 @@
"postcss-initial": "^4.0.1",
"postcss-lab-function": "^6.0.1",
"postcss-logical": "^7.0.0",
"postcss-nesting": "^12.0.0",
"postcss-nesting": "^12.0.1",
"postcss-opacity-percentage": "^2.0.0",
"postcss-overflow-shorthand": "^5.0.0",
"postcss-page-break": "^3.0.4",
@ -11962,9 +11982,9 @@
"dev": true
},
"preact": {
"version": "10.16.0",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.16.0.tgz",
"integrity": "sha512-XTSj3dJ4roKIC93pald6rWuB2qQJO9gO2iLLyTe87MrjQN+HklueLsmskbywEWqCHlclgz3/M4YLL2iBr9UmMA=="
"version": "10.17.0",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.17.0.tgz",
"integrity": "sha512-SNsI8cbaCcUS5tbv9nlXuCfIXnJ9ysBMWk0WnB6UWwcVA3qZ2O6FxqDFECMAMttvLQcW/HaNZUe2BLidyvrVYw=="
},
"prettier": {
"version": "2.8.0",
@ -12191,9 +12211,9 @@
"dev": true
},
"rollup": {
"version": "3.26.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz",
"integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz",
"integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
@ -12662,15 +12682,15 @@
}
},
"vite": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
"integrity": "sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==",
"version": "4.4.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
"dev": true,
"requires": {
"esbuild": "^0.18.10",
"fsevents": "~2.3.2",
"postcss": "^8.4.26",
"rollup": "^3.25.2"
"postcss": "^8.4.27",
"rollup": "^3.27.1"
}
},
"vite-plugin-generate-file": {

View file

@ -12,21 +12,22 @@
"dependencies": {
"@formatjs/intl-localematcher": "~0.4.0",
"@github/text-expander-element": "~2.5.0",
"@iconify-icons/mingcute": "~1.2.6",
"@iconify-icons/mingcute": "~1.2.7",
"@justinribeiro/lite-youtube": "~1.5.0",
"@szhsin/react-menu": "~4.0.2",
"@uidotdev/usehooks": "~2.0.1",
"@szhsin/react-menu": "~4.0.3",
"@uidotdev/usehooks": "~2.1.0",
"dayjs": "~1.11.9",
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
"fast-deep-equal": "~3.1.3",
"idb-keyval": "~6.2.1",
"just-debounce-it": "~3.2.0",
"lz-string": "^1.5.0",
"masto": "~5.11.4",
"mem": "~9.0.2",
"p-retry": "~5.1.2",
"p-throttle": "~5.1.0",
"preact": "~10.16.0",
"preact": "~10.17.0",
"react-hotkeys-hook": "~4.4.1",
"react-intersection-observer": "~9.5.2",
"react-quick-pinch-zoom": "~4.9.0",
@ -44,10 +45,10 @@
"@preact/preset-vite": "~2.5.0",
"@trivago/prettier-plugin-sort-imports": "~4.2.0",
"postcss": "~8.4.27",
"postcss-dark-theme-class": "~0.7.3",
"postcss-preset-env": "~9.1.0",
"postcss-dark-theme-class": "~0.8.0",
"postcss-preset-env": "~9.1.1",
"twitter-text": "~3.1.0",
"vite": "~4.4.7",
"vite": "~4.4.9",
"vite-plugin-generate-file": "~0.0.4",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-pwa": "~0.16.4",

View file

@ -1417,6 +1417,11 @@ body:has(.media-modal-container + .status-deck) .media-post-link {
.tag.collapsed {
margin: 0;
}
.tag.insignificant {
border: 1px solid var(--outline-color);
color: var(--text-insignificant-color);
background-color: var(--bg-faded-color);
}
.tag.danger {
background-color: var(--red-color);
}

View file

@ -90,6 +90,11 @@ export const ICONS = {
announce: () => import('@iconify-icons/mingcute/announcement-line'),
alert: () => import('@iconify-icons/mingcute/alert-line'),
round: () => import('@iconify-icons/mingcute/round-fill'),
'arrow-up-circle': () =>
import('@iconify-icons/mingcute/arrow-up-circle-line'),
'arrow-down-circle': () =>
import('@iconify-icons/mingcute/arrow-down-circle-line'),
clipboard: () => import('@iconify-icons/mingcute/clipboard-line'),
};
function Icon({

View file

@ -126,3 +126,52 @@
display: flex;
gap: 16px;
}
/* Import/Export */
#import-export-container input[type='text'] {
font-family: var(--monospace-font);
}
#import-export-container section {
margin: 8px 0;
background-color: var(--bg-faded-color);
border-radius: 16px;
padding: 8px;
}
#import-export-container section h3 {
margin: 0 0 8px;
}
#import-export-container section h3 * {
vertical-align: middle;
}
#import-export-container section p {
margin: 8px 0;
}
#import-export-container section details > summary {
cursor: pointer;
}
#import-export-container .import-settings-list {
border-radius: 8px;
overflow: hidden;
margin: 8px 0 0;
padding: 0;
counter-reset: index;
}
#import-export-container .import-settings-list li {
background-color: var(--bg-blur-color);
margin: 0 0 2px;
padding: 8px 4px;
display: flex;
gap: 4px;
}
#import-export-container .import-settings-list li::before {
content: counter(index);
counter-increment: index;
display: inline-block;
width: 1.2em;
text-align: right;
margin-right: 8px;
color: var(--text-insignificant-color);
font-size: 90%;
flex-shrink: 0;
}

View file

@ -1,17 +1,23 @@
import './shortcuts-settings.css';
import {
compressToEncodedURIComponent,
decompressFromEncodedURIComponent,
} from 'lz-string';
import mem from 'mem';
import { useEffect, useRef, useState } from 'preact/hooks';
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useSnapshot } from 'valtio';
import floatingButtonUrl from '../assets/floating-button.svg';
import multiColumnUrl from '../assets/multi-column.svg';
import tabMenuBarUrl from '../assets/tab-menu-bar.svg';
import { api } from '../utils/api';
import showToast from '../utils/show-toast';
import states from '../utils/states';
import AsyncText from './AsyncText';
import Icon from './icon';
import MenuConfirm from './menu-confirm';
import Modal from './modal';
const SHORTCUTS_LIMIT = 9;
@ -202,6 +208,7 @@ function ShortcutsSettings({ onClose }) {
const [lists, setLists] = useState([]);
const [followedHashtags, setFollowedHashtags] = useState([]);
const [showForm, setShowForm] = useState(false);
const [showImportExport, setShowImportExport] = useState(false);
useEffect(() => {
(async () => {
@ -432,6 +439,10 @@ function ShortcutsSettings({ onClose }) {
</p>
</div>
)}
<p class="insignificant">
{shortcuts.length >= SHORTCUTS_LIMIT &&
`Max ${SHORTCUTS_LIMIT} shortcuts`}
</p>
<p
style={{
display: 'flex',
@ -439,10 +450,13 @@ function ShortcutsSettings({ onClose }) {
alignItems: 'center',
}}
>
<span class="insignificant">
{shortcuts.length >= SHORTCUTS_LIMIT &&
`Max ${SHORTCUTS_LIMIT} shortcuts`}
</span>
<button
type="button"
class="light"
onClick={() => setShowImportExport(true)}
>
Import/export
</button>
<button
type="button"
disabled={shortcuts.length >= SHORTCUTS_LIMIT}
@ -478,6 +492,21 @@ function ShortcutsSettings({ onClose }) {
/>
</Modal>
)}
{showImportExport && (
<Modal
class="light"
onClick={(e) => {
if (e.target === e.currentTarget) {
setShowImportExport(false);
}
}}
>
<ImportExport
shortcuts={shortcuts}
onClose={() => setShowImportExport(false)}
/>
</Modal>
)}
</div>
);
}
@ -650,4 +679,296 @@ function ShortcutForm({
);
}
function ImportExport({ shortcuts, onClose }) {
const shortcutsStr = useMemo(
() =>
compressToEncodedURIComponent(JSON.stringify(shortcuts.filter(Boolean))),
[shortcuts],
);
const [importShortcutStr, setImportShortcutStr] = useState('');
const [importUIState, setImportUIState] = useState('default');
const parsedImportShortcutStr = useMemo(() => {
if (!importShortcutStr) {
setImportUIState('default');
return null;
}
try {
const parsed = JSON.parse(
decompressFromEncodedURIComponent(importShortcutStr),
);
// Very basic validation, I know
if (!Array.isArray(parsed)) throw new Error('Not an array');
setImportUIState('default');
return parsed;
} catch (err) {
// Fallback to JSON string parsing
// There's a chance that someone might want to import a JSON string instead of the compressed version
try {
const parsed = JSON.parse(importShortcutStr);
if (!Array.isArray(parsed)) throw new Error('Not an array');
setImportUIState('default');
return parsed;
} catch (err) {
setImportUIState('error');
return null;
}
}
}, [importShortcutStr]);
const hasCurrentSettings = states.shortcuts.length > 0;
return (
<div id="import-export-container" class="sheet">
{!!onClose && (
<button type="button" class="sheet-close" onClick={onClose}>
<Icon icon="x" />
</button>
)}
<header>
<h2>
Import/Export{' '}
<small class="ib insignificant">Shortcuts settings</small>
</h2>
</header>
<main tabindex="-1">
<section>
<h3>
<Icon icon="arrow-down-circle" size="l" class="insignificant" />{' '}
<span>Import</span>
</h3>
<p>
<input
type="text"
name="import"
placeholder="Paste settings here"
class="block"
onInput={(e) => {
setImportShortcutStr(e.target.value);
}}
/>
</p>
{!!parsedImportShortcutStr &&
Array.isArray(parsedImportShortcutStr) && (
<>
<p>
<b>{parsedImportShortcutStr.length}</b> shortcut
{parsedImportShortcutStr.length > 1 ? 's' : ''}{' '}
<small class="insignificant">
({importShortcutStr.length} characters)
</small>
</p>
<ol class="import-settings-list">
{parsedImportShortcutStr.map((shortcut) => (
<li>
<span
style={{
opacity: shortcuts.some((s) =>
// Compare all properties
Object.keys(s).every(
(key) => s[key] === shortcut[key],
),
)
? 1
: 0,
}}
>
*
</span>
<span>
{TYPE_TEXT[shortcut.type]}
{shortcut.type === 'list' && ' ⚠️'}{' '}
{TYPE_PARAMS[shortcut.type]?.map?.(
({ text, name, type }) =>
shortcut[name] ? (
<>
<span class="tag collapsed insignificant">
{text}:{' '}
{type === 'checkbox'
? shortcut[name] === 'on'
? '✅'
: '❌'
: shortcut[name]}
</span>{' '}
</>
) : null,
)}
</span>
</li>
))}
</ol>
<p>
<small>* Exists in current settings</small>
<br />
<small>
List may not work if it's from a different account.
</small>
</p>
</>
)}
{importUIState === 'error' && (
<p class="error">
<small> Invalid settings format</small>
</p>
)}
<p>
{hasCurrentSettings && (
<>
<MenuConfirm
confirmLabel="Append these shortcuts to current settings?"
menuFooter={
<div class="footer">
Only shortcuts that dont exist in current settings will
be appended.
</div>
}
onClick={() => {
// states.shortcuts = [
// ...states.shortcuts,
// ...parsedImportShortcutStr,
// ];
// Append non-unique shortcuts only
const nonUniqueShortcuts = parsedImportShortcutStr.filter(
(shortcut) =>
!states.shortcuts.some((s) =>
// Compare all properties
Object.keys(s).every(
(key) => s[key] === shortcut[key],
),
),
);
if (!nonUniqueShortcuts.length) {
showToast('No new shortcuts to import');
return;
}
let newShortcuts = [
...states.shortcuts,
...nonUniqueShortcuts,
];
const exceededLimit = newShortcuts.length > SHORTCUTS_LIMIT;
if (exceededLimit) {
// If exceeded, trim it
newShortcuts = newShortcuts.slice(0, SHORTCUTS_LIMIT);
}
states.shortcuts = newShortcuts;
showToast(
exceededLimit
? `Shortcuts settings imported. Exceeded max ${SHORTCUTS_LIMIT}, so the rest are not imported.`
: 'Shortcuts settings imported',
);
onClose?.();
}}
>
<button
type="button"
class="plain2"
disabled={!parsedImportShortcutStr}
>
Import & append
</button>
</MenuConfirm>{' '}
</>
)}
<MenuConfirm
confirmLabel={
hasCurrentSettings
? 'Override current settings?'
: 'Import settings?'
}
menuItemClassName={hasCurrentSettings ? 'danger' : undefined}
onClick={() => {
states.shortcuts = parsedImportShortcutStr;
showToast('Shortcuts settings imported');
onClose?.();
}}
>
<button
type="button"
class="plain2"
disabled={!parsedImportShortcutStr}
>
{hasCurrentSettings ? 'or override…' : 'Import…'}
</button>
</MenuConfirm>
</p>
</section>
<section>
<h3>
<Icon icon="arrow-up-circle" size="l" class="insignificant" />{' '}
<span>Export</span>
</h3>
<p>
<input
style={{ width: '100%' }}
type="text"
value={shortcutsStr}
readOnly
onClick={(e) => {
e.target.select();
// Copy url to clipboard
try {
navigator.clipboard.writeText(e.target.value);
showToast('Shortcuts settings copied');
} catch (e) {
console.error(e);
showToast('Unable to copy shortcuts settings');
}
}}
/>
</p>
<p>
<button
type="button"
class="plain2"
onClick={() => {
try {
navigator.clipboard.writeText(shortcutsStr);
showToast('Shortcut settings copied');
} catch (e) {
console.error(e);
showToast('Unable to copy shortcut settings');
}
}}
>
<Icon icon="clipboard" /> <span>Copy</span>
</button>{' '}
{navigator?.share &&
navigator?.canShare?.({
text: shortcutsStr,
}) && (
<button
type="button"
class="plain2"
onClick={() => {
try {
navigator.share({
text: shortcutsStr,
});
} catch (e) {
console.error(e);
alert("Sharing doesn't seem to work.");
}
}}
>
<Icon icon="share" /> <span>Share</span>
</button>
)}{' '}
{shortcutsStr.length > 0 && (
<small class="insignificant">
{shortcutsStr.length} characters
</small>
)}
</p>
<details>
<summary class="insignificant">
<small>Raw Shortcuts settings JSON</small>
</summary>
<textarea style={{ width: '100%' }} rows={10} readOnly>
{JSON.stringify(shortcuts.filter(Boolean), null, 2)}
</textarea>
</details>
</section>
</main>
</div>
);
}
export default ShortcutsSettings;

View file

@ -11,6 +11,8 @@
--main-width: 40em;
text-size-adjust: none;
--hairline-width: 1px;
--monospace-font: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono',
Menlo, Courier, monospace;
--blue-color: royalblue;
--purple-color: blueviolet;
@ -288,6 +290,11 @@ button.large {
padding: 12px;
}
:is(input[type='text'], textarea, select).block {
display: block;
width: 100%;
}
button.small {
font-size: 90%;
padding: 4px 8px;
@ -304,8 +311,7 @@ pre {
pre code,
code {
font-size: 90%;
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono',
Menlo, Courier, monospace;
font-family: var(--monospace-font);
}
@media (prefers-color-scheme: dark) {