6 Commits

Author SHA1 Message Date
nick comer
42865ff2bb feat: tab group support 2025-05-28 23:34:54 -04:00
nick comer
ced8d54eb0 improve readme 2024-06-19 11:32:51 -04:00
nick comer
170ebf971d fix: license + readme 2024-06-14 14:25:39 -04:00
nick comer
6dc2074870 fix: little bit of code cleanup 2024-06-14 13:57:35 -04:00
nick comer
0ce204a948 fix: unused parameter on favicon function 2024-06-14 13:48:03 -04:00
nick comer
4f2db92534 feat: experiment with more structured query pattern
fix: change to more robust "autofocus" mechanism to fix issue where popup would
show and not be automatically focused on the input
2024-05-10 10:09:51 -04:00
8 changed files with 499 additions and 247 deletions

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2024 Nicholas Comer
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.

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# HyperTab
A brutally simple browser extension that targets Firefox meant to be a replica of Chrome's tab switcher. Quickly open, search, then switch to any open tab.
https://github.com/nkcmr/HyperTab/assets/1791521/c60acea5-23c3-4c80-9288-518d06dcc83a
## Usage
Open tab search with <kbd>⌘</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd> (it would be <kbd>A</kbd>, but Firefox uses this for add-on management). Search is automatically focused so you should be able to type some fuzzy search and immediately narrow down what you're looking for.
## Why?
Switching from Chrome to Firefox is mostly fine. But Firefox's current "story" for navigating between tabs is really sub-par (IMO). This extension, for me, implements a _must have_ feature for any browser I am using: Quick tab search and switch.
## Privacy/Security
You're probably here to make sure this extension isn't doing anything sneak, right? Good for you, everyone who is able to understand code should do this. Have a look around and see for yourself. **But,** I will save you a bunch of time: this browser extension does not persist anything and does not talk to any servers; it doesn't need to!

View File

@@ -36,5 +36,5 @@
} }
}, },
"permissions": ["tabs", "*://*/*"] "permissions": ["tabs", "tabGroups", "*://*/*"]
} }

401
package-lock.json generated
View File

@@ -7,23 +7,24 @@
"dependencies": { "dependencies": {
"@types/chrome": "^0.0.251", "@types/chrome": "^0.0.251",
"@types/lodash.uniq": "^4.5.9", "@types/lodash.uniq": "^4.5.9",
"@types/react": "^18.2.37", "@types/react": "^18.3.23",
"@types/react-dom": "^18.2.15", "@types/react-dom": "^18.3.7",
"esbuild": "^0.19.5", "esbuild": "^0.19.12",
"fuse.js": "^7.0.0", "fuse.js": "^7.1.0",
"lodash.uniq": "^4.5.0", "lodash.uniq": "^4.5.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"react": "^18.2.0", "react": "^18.3.1",
"react-dom": "^18.2.0", "react-dom": "^18.3.1",
"react-hotkeys-hook": "^4.4.1", "react-hotkeys-hook": "^4.6.2",
"styled-components": "^6.1.9", "styled-components": "^6.1.18",
"typescript": "^5.2.2" "typescript": "^5.8.3"
} }
}, },
"node_modules/@emotion/is-prop-valid": { "node_modules/@emotion/is-prop-valid": {
"version": "1.2.1", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
"integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@emotion/memoize": "^0.8.1" "@emotion/memoize": "^0.8.1"
} }
@@ -31,20 +32,38 @@
"node_modules/@emotion/memoize": { "node_modules/@emotion/memoize": {
"version": "0.8.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
"license": "MIT"
}, },
"node_modules/@emotion/unitless": { "node_modules/@emotion/unitless": {
"version": "0.8.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
}, },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
"integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
@@ -54,12 +73,13 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
"integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
@@ -69,12 +89,13 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
"integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
@@ -84,12 +105,13 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
"integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@@ -99,12 +121,13 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
"integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@@ -114,12 +137,13 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
"integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"freebsd" "freebsd"
@@ -129,12 +153,13 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
"integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"freebsd" "freebsd"
@@ -144,12 +169,13 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
"integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -159,12 +185,13 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
"integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -174,12 +201,13 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
"integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -189,12 +217,13 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
"integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -204,12 +233,13 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
"integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -219,12 +249,13 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
"integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -234,12 +265,13 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
"integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -249,12 +281,13 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
"integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -264,12 +297,13 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
"integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@@ -279,12 +313,13 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
"integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"netbsd" "netbsd"
@@ -294,12 +329,13 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
"integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"openbsd" "openbsd"
@@ -309,12 +345,13 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
"integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"sunos" "sunos"
@@ -324,12 +361,13 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
"integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@@ -339,12 +377,13 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
"integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@@ -354,12 +393,13 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
"integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@@ -414,32 +454,29 @@
"integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A=="
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "18.2.37", "version": "18.3.23",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz",
"integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-dom": { "node_modules/@types/react-dom": {
"version": "18.2.15", "version": "18.3.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"dependencies": { "license": "MIT",
"@types/react": "*" "peerDependencies": {
"@types/react": "^18.0.0"
} }
}, },
"node_modules/@types/scheduler": {
"version": "0.16.6",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz",
"integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA=="
},
"node_modules/@types/stylis": { "node_modules/@types/stylis": {
"version": "4.2.0", "version": "4.2.5",
"resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz",
"integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==",
"license": "MIT"
}, },
"node_modules/ansi-styles": { "node_modules/ansi-styles": {
"version": "3.2.1", "version": "3.2.1",
@@ -595,9 +632,10 @@
} }
}, },
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.2", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
}, },
"node_modules/define-data-property": { "node_modules/define-data-property": {
"version": "1.1.1", "version": "1.1.1",
@@ -718,10 +756,11 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.19.5", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
"integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT",
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
}, },
@@ -729,28 +768,29 @@
"node": ">=12" "node": ">=12"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/android-arm": "0.19.5", "@esbuild/aix-ppc64": "0.19.12",
"@esbuild/android-arm64": "0.19.5", "@esbuild/android-arm": "0.19.12",
"@esbuild/android-x64": "0.19.5", "@esbuild/android-arm64": "0.19.12",
"@esbuild/darwin-arm64": "0.19.5", "@esbuild/android-x64": "0.19.12",
"@esbuild/darwin-x64": "0.19.5", "@esbuild/darwin-arm64": "0.19.12",
"@esbuild/freebsd-arm64": "0.19.5", "@esbuild/darwin-x64": "0.19.12",
"@esbuild/freebsd-x64": "0.19.5", "@esbuild/freebsd-arm64": "0.19.12",
"@esbuild/linux-arm": "0.19.5", "@esbuild/freebsd-x64": "0.19.12",
"@esbuild/linux-arm64": "0.19.5", "@esbuild/linux-arm": "0.19.12",
"@esbuild/linux-ia32": "0.19.5", "@esbuild/linux-arm64": "0.19.12",
"@esbuild/linux-loong64": "0.19.5", "@esbuild/linux-ia32": "0.19.12",
"@esbuild/linux-mips64el": "0.19.5", "@esbuild/linux-loong64": "0.19.12",
"@esbuild/linux-ppc64": "0.19.5", "@esbuild/linux-mips64el": "0.19.12",
"@esbuild/linux-riscv64": "0.19.5", "@esbuild/linux-ppc64": "0.19.12",
"@esbuild/linux-s390x": "0.19.5", "@esbuild/linux-riscv64": "0.19.12",
"@esbuild/linux-x64": "0.19.5", "@esbuild/linux-s390x": "0.19.12",
"@esbuild/netbsd-x64": "0.19.5", "@esbuild/linux-x64": "0.19.12",
"@esbuild/openbsd-x64": "0.19.5", "@esbuild/netbsd-x64": "0.19.12",
"@esbuild/sunos-x64": "0.19.5", "@esbuild/openbsd-x64": "0.19.12",
"@esbuild/win32-arm64": "0.19.5", "@esbuild/sunos-x64": "0.19.12",
"@esbuild/win32-ia32": "0.19.5", "@esbuild/win32-arm64": "0.19.12",
"@esbuild/win32-x64": "0.19.5" "@esbuild/win32-ia32": "0.19.12",
"@esbuild/win32-x64": "0.19.12"
} }
}, },
"node_modules/escape-string-regexp": { "node_modules/escape-string-regexp": {
@@ -803,9 +843,10 @@
} }
}, },
"node_modules/fuse.js": { "node_modules/fuse.js": {
"version": "7.0.0", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
"integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
"license": "Apache-2.0",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }
@@ -1215,15 +1256,16 @@
} }
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.7", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@@ -1341,9 +1383,10 @@
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
}, },
"node_modules/pidtree": { "node_modules/pidtree": {
"version": "0.3.1", "version": "0.3.1",
@@ -1365,9 +1408,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.31", "version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -1382,10 +1425,11 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.6", "nanoid": "^3.3.7",
"picocolors": "^1.0.0", "picocolors": "^1.1.1",
"source-map-js": "^1.0.2" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
@@ -1397,9 +1441,10 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
}, },
"node_modules/react": { "node_modules/react": {
"version": "18.2.0", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@@ -1408,21 +1453,23 @@
} }
}, },
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.2.0", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"scheduler": "^0.23.0" "scheduler": "^0.23.2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^18.2.0" "react": "^18.3.1"
} }
}, },
"node_modules/react-hotkeys-hook": { "node_modules/react-hotkeys-hook": {
"version": "4.4.1", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.4.1.tgz", "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.6.2.tgz",
"integrity": "sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==", "integrity": "sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.1", "react": ">=16.8.1",
"react-dom": ">=16.8.1" "react-dom": ">=16.8.1"
@@ -1504,9 +1551,10 @@
} }
}, },
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.23.0", "version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
} }
@@ -1592,9 +1640,10 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -1694,19 +1743,20 @@
} }
}, },
"node_modules/styled-components": { "node_modules/styled-components": {
"version": "6.1.9", "version": "6.1.18",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.9.tgz", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.18.tgz",
"integrity": "sha512-aBOqs0uMsYufFXSE4q6cA6Ty1fwZuMk4BJRHfiGSna59F1otnxiDelwhN4fEwmBtIymmF0ZqXHnpSigr2ps9Cg==", "integrity": "sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@emotion/is-prop-valid": "1.2.1", "@emotion/is-prop-valid": "1.2.2",
"@emotion/unitless": "0.8.1", "@emotion/unitless": "0.8.1",
"@types/stylis": "4.2.0", "@types/stylis": "4.2.5",
"css-to-react-native": "3.2.0", "css-to-react-native": "3.2.0",
"csstype": "3.1.2", "csstype": "3.1.3",
"postcss": "8.4.31", "postcss": "8.4.49",
"shallowequal": "1.1.0", "shallowequal": "1.1.0",
"stylis": "4.3.1", "stylis": "4.3.2",
"tslib": "2.5.0" "tslib": "2.6.2"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16"
@@ -1721,9 +1771,10 @@
} }
}, },
"node_modules/stylis": { "node_modules/stylis": {
"version": "4.3.1", "version": "4.3.2",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
"integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==",
"license": "MIT"
}, },
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "5.5.0", "version": "5.5.0",
@@ -1748,9 +1799,10 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.5.0", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"license": "0BSD"
}, },
"node_modules/typed-array-buffer": { "node_modules/typed-array-buffer": {
"version": "1.0.0", "version": "1.0.0",
@@ -1814,9 +1866,10 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.2.2", "version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"

View File

@@ -20,16 +20,16 @@
"dependencies": { "dependencies": {
"@types/chrome": "^0.0.251", "@types/chrome": "^0.0.251",
"@types/lodash.uniq": "^4.5.9", "@types/lodash.uniq": "^4.5.9",
"@types/react": "^18.2.37", "@types/react": "^18.3.23",
"@types/react-dom": "^18.2.15", "@types/react-dom": "^18.3.7",
"esbuild": "^0.19.5", "esbuild": "^0.19.12",
"fuse.js": "^7.0.0", "fuse.js": "^7.1.0",
"lodash.uniq": "^4.5.0", "lodash.uniq": "^4.5.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"react": "^18.2.0", "react": "^18.3.1",
"react-dom": "^18.2.0", "react-dom": "^18.3.1",
"react-hotkeys-hook": "^4.4.1", "react-hotkeys-hook": "^4.6.2",
"styled-components": "^6.1.9", "styled-components": "^6.1.18",
"typescript": "^5.2.2" "typescript": "^5.8.3"
} }
} }

View File

@@ -1,3 +1,5 @@
import type { Tab } from "../types";
declare const browser: typeof chrome; declare const browser: typeof chrome;
// tabSwitches is a stack of all tab activations. every time a tab becomes // tabSwitches is a stack of all tab activations. every time a tab becomes
@@ -35,19 +37,39 @@ setInterval(() => {
tabSwitches = uniq(tabSwitches); tabSwitches = uniq(tabSwitches);
}, 1000); }, 1000);
async function listTabs(): Promise<chrome.tabs.Tab[]> { async function listTabs(): Promise<Tab[]> {
const _tabs = await browser.tabs.query({}); const [tabs, groups] = await Promise.all([
browser.tabs.query({}).then(
// filter out file:///... things, safari does not really have // filter out file:///... things, safari does not really have
// safari://... things like chrome // safari://... things like chrome
const tabs = _tabs.filter((t) => t.url || t.title); (allTabs) => allTabs.filter((t) => t.url || t.title)
),
browser.tabGroups
.query({})
.then((groups): Map<number, chrome.tabGroups.TabGroup> => {
return new Map(groups.map((g) => [g.id, g]));
}),
]);
const hit = new Map<number, boolean>( const hit = new Map<number, boolean>(
tabs.filter((t) => !!t.id).map((t) => [t.id!, false]) tabs.filter((t) => !!t.id).map((t) => [t.id!, false])
); );
const tabsById = new Map<number, chrome.tabs.Tab>( const tabsById = new Map<number, Tab>(
tabs.filter((t) => !!t.id).map((t) => [t.id!, t]) tabs
.filter((t) => !!t.id)
.map((t) => {
const group = t.groupId > 0 ? groups.get(t.groupId) : undefined;
return [
t.id!,
{
...t,
group,
},
];
})
); );
const resultTabs = [];
const resultTabs: Tab[] = [];
for (let tabId of tabSwitches.slice(1)) { for (let tabId of tabSwitches.slice(1)) {
if (hit.get(tabId)) { if (hit.get(tabId)) {
continue; continue;

View File

@@ -10,6 +10,7 @@ import React, {
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import { useHotkeys } from "react-hotkeys-hook"; import { useHotkeys } from "react-hotkeys-hook";
import { styled } from "styled-components"; import { styled } from "styled-components";
import type { Tab } from "../types";
import "./scrollIntoViewIfNeededPolyfill"; import "./scrollIntoViewIfNeededPolyfill";
function t( function t(
@@ -31,8 +32,8 @@ function hostname(url: string): string {
const browser = chrome; const browser = chrome;
interface BackgroundPage { interface BackgroundPage {
listTabs(): Promise<chrome.tabs.Tab[]>; listTabs(): Promise<Tab[]>;
closeTab(tabID: number): Promise<chrome.tabs.Tab[]>; closeTab(tabID: number): Promise<Tab[]>;
} }
function useBackgroundPage(): BackgroundPage { function useBackgroundPage(): BackgroundPage {
@@ -76,7 +77,7 @@ function useBackgroundPage(): BackgroundPage {
waiter.current.set(id, { waiter.current.set(id, {
reject, reject,
resolve(value) { resolve(value) {
resolve(value as chrome.tabs.Tab[]); resolve(value as Tab[]);
}, },
}); });
port.current.postMessage({ rpc: "listTabs", id }); port.current.postMessage({ rpc: "listTabs", id });
@@ -88,7 +89,7 @@ function useBackgroundPage(): BackgroundPage {
waiter.current.set(id, { waiter.current.set(id, {
reject, reject,
resolve(value) { resolve(value) {
resolve(value as chrome.tabs.Tab[]); resolve(value as Tab[]);
}, },
}); });
port.current.postMessage({ rpc: "closeTab", id, args: { tabID } }); port.current.postMessage({ rpc: "closeTab", id, args: { tabID } });
@@ -147,41 +148,11 @@ const HighlightMatches: FunctionComponent<{
); );
}; };
// // <big_sigh> ... function faviconURL(t: Tab): string | undefined {
// function faviconsWork(tabURL: string, size: number): Promise<boolean> {
// return new Promise((resolve) => {
// const hiddenDiv = document.createElement("div", {});
// hiddenDiv.setAttribute("style", "display:none;");
// const testImg = document.createElement("img");
// testImg.src = faviconURL({ url: tabURL } as chrome.tabs.Tab, 32)!;
// testImg.onerror = () => {
// document.body.removeChild(hiddenDiv);
// resolve(false);
// };
// testImg.onload = () => {
// document.body.removeChild(hiddenDiv);
// resolve(true);
// };
// hiddenDiv.appendChild(testImg);
// document.body.appendChild(hiddenDiv);
// });
// }
function faviconURL(t: chrome.tabs.Tab, size: number): string | undefined {
return ( return (
t.favIconUrl ?? t.favIconUrl ??
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
); );
// if (t.favIconUrl) {
// return t.favIconUrl;
// }
// if (!t.url) {
// return;
// }
// const url = new URL(browser.runtime.getURL("/_favicon/"));
// url.searchParams.set("pageUrl", t.url);
// url.searchParams.set("size", `${size}`);
// return url.toString();
} }
const prefersDarkMode = (): boolean => { const prefersDarkMode = (): boolean => {
@@ -261,6 +232,9 @@ const TabItemFavicon = styled.div<{ $dark: boolean }>`
padding: 7px; padding: 7px;
flex-shrink: 0; flex-shrink: 0;
`; `;
const TabItemCloseBox = styled.div`
cursor: pointer;
`;
const TabItemMain = styled.div` const TabItemMain = styled.div`
width: ${TAB_ITEM_MAIN_WIDTH}px; width: ${TAB_ITEM_MAIN_WIDTH}px;
`; `;
@@ -272,12 +246,71 @@ const TabItemMainTitle = styled.div<{ $dark: boolean }>`
text-overflow: ellipsis; text-overflow: ellipsis;
`; `;
const TabItemMainHostname = styled.div<{ $dark: boolean }>` const TabItemMainHostname = styled.div<{ $dark: boolean }>`
margin-top: 3px;
color: ${(props) => (props.$dark ? "#a9a9a9" : "#6e6e6e")}; color: ${(props) => (props.$dark ? "#a9a9a9" : "#6e6e6e")};
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
`; `;
type ColorSet<C extends string> = `${C}` | `${C}-invert` | `${C}-pale`;
const tabGroupColors: {
[K in ColorSet<chrome.tabGroups.ColorEnum>]: string;
} = {
blue: "oklch(48% 0.2 260)",
"blue-invert": "oklch(83% 0.17 260)",
"blue-pale": "oklch(97% 0.05 260)",
purple: "oklch(48% 0.2 315)",
"purple-invert": "oklch(83% 0.14 315)",
"purple-pale": "oklch(97% 0.05 315)",
cyan: "oklch(48% 0.2 205)",
"cyan-invert": "oklch(83% 0.11 205)",
"cyan-pale": "oklch(97% 0.05 205)",
orange: "oklch(48% 0.2 50)",
"orange-invert": "oklch(86% 0.14 50)",
"orange-pale": "oklch(97% 0.05 50)",
yellow: "oklch(51% 0.23 90)",
"yellow-invert": "oklch(86% 0.14 90)",
"yellow-pale": "oklch(97% 0.05 90)",
pink: "oklch(48% 0.2 360)",
"pink-invert": "oklch(83% 0.14 360)",
"pink-pale": "oklch(97% 0.05 360)",
green: "oklch(48% 0.2 145)",
"green-invert": "oklch(83% 0.14 145)",
"green-pale": "oklch(97% 0.05 145)",
red: "oklch(48% 0.2 15)",
"red-invert": "oklch(83% 0.14 15)",
"red-pale": "oklch(97% 0.05 15)",
grey: "#5e6a77",
"grey-invert": "#99a6b4",
"grey-pale": "#f2f9ff",
};
const TabItemGroupPill = styled.span<{
$dark: boolean;
$color: chrome.tabGroups.ColorEnum;
}>`
background-color: ${(props) =>
props.$dark
? tabGroupColors[`${props.$color}-invert`]
: tabGroupColors[props.$color]};
color: ${(props) =>
props.$dark ? `#15141a` : tabGroupColors[`${props.$color}-pale`]};
font-weight: 500;
padding: 3px 5px;
border-radius: 4px;
display: inline-block;
`;
const SearchContainer = styled.div` const SearchContainer = styled.div`
display: flex; display: flex;
width: ${SEARCH_CONTAINER_WIDTH}px; width: ${SEARCH_CONTAINER_WIDTH}px;
@@ -298,24 +331,79 @@ const SearchInput = styled.input`
font-size: 1.2em; font-size: 1.2em;
`; `;
type searchField = {
aliases?: string[];
filterValues: (value: string | null) => boolean;
evaluate: (t: Tab, value: string | null) => boolean;
};
const searchFields: Record<string, searchField> = {
sys: {
filterValues(value) {
return ["true", "false"].includes(value?.toLowerCase() ?? "");
},
evaluate(t, value) {
if (!t.url) {
return value === "true";
}
const ishttp = !!t.url.match(/^https?:\/\//i);
return ishttp !== (value === "true");
},
},
pinned: {
filterValues(value) {
return ["true", "false"].includes(value?.toLowerCase() ?? "");
},
evaluate(t, value) {
return t.pinned === (value === "true");
},
},
hostname: {
aliases: ["domain"],
filterValues(value) {
return true;
},
evaluate(t, value) {
if (!t.url) {
return false;
}
return hostname(t.url).includes(value ?? "");
},
},
} as const;
function getSearchField(key: string): searchField | null {
return key in searchFields
? searchFields[key]
: Object.values(searchFields).find((sf) => {
return sf.aliases?.includes(key) ?? false;
}) ?? null;
}
function structuredQuery(query: string): Map<string, string | null> {
return new Map<string, string | null>(
query
.split(/\s+/g)
.map((pair): [string, string | null] => {
const [key, value] = pair.split(":", 2);
return [key, value ?? null];
})
.filter(([key, value]) => {
const sf = getSearchField(key);
if (!sf) {
return false;
}
return sf.filterValues(value);
})
);
}
const Popup: FunctionComponent = () => { const Popup: FunctionComponent = () => {
const darkMode = useDarkMode(); const darkMode = useDarkMode();
const [tabSelector, setTabSelector] = useState(0); const [tabSelector, setTabSelector] = useState(0);
const [tabs, setTabs] = useState<chrome.tabs.Tab[]>([]); const [tabs, setTabs] = useState<Tab[]>([]);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
// const FAVICON_NOT_SUPPORTED = 0;
// const FAVICON_SUPPORTED_VIA_EXT_URL = 1;
// const FAVICON_SUPPORTED_VIA_TAB_DATA = 2;
// const [enableFavicons, setEnabledFavicons] = useState(FAVICON_NOT_SUPPORTED);
// useEffect(() => {
// faviconsWork("https://www.google.com", 32).then((ok) => {
// if (enableFavicons === 0) {
// setEnabledFavicons(FAVICON_SUPPORTED_VIA_EXT_URL);
// }
// });
// }, []);
useEffect(() => { useEffect(() => {
if (tabs.length === 0) { if (tabs.length === 0) {
return; return;
@@ -327,7 +415,21 @@ const Popup: FunctionComponent = () => {
}, [setTabSelector, searchQuery]); }, [setTabSelector, searchQuery]);
const searchIndex = useMemo(() => { const searchIndex = useMemo(() => {
const result = new Fuse(tabs, { const result = new Fuse(tabs, {
keys: ["title", "url"], keys: [
"title",
{
name: "hostname",
getFn(obj) {
return obj.url ? hostname(obj.url) : "";
},
},
{
name: "group",
getFn(obj) {
return obj.group?.title ?? "";
},
},
],
includeMatches: true, includeMatches: true,
}); });
return result; return result;
@@ -335,7 +437,25 @@ const Popup: FunctionComponent = () => {
const searchResults = useMemo(() => { const searchResults = useMemo(() => {
if (!searchQuery) { if (!searchQuery) {
return tabs.map( return tabs.map(
(t, i): FuseResult<chrome.tabs.Tab> => ({ (t, i): FuseResult<Tab> => ({
item: t,
refIndex: i,
})
);
}
const sq = structuredQuery(searchQuery);
if (sq.size > 0) {
return tabs
.filter((tab) => {
for (let [key, value] of sq.entries()) {
if (!getSearchField(key)!.evaluate(tab, value)) {
return false;
}
}
return true;
})
.map(
(t, i): FuseResult<Tab> => ({
item: t, item: t,
refIndex: i, refIndex: i,
}) })
@@ -409,7 +529,14 @@ const Popup: FunctionComponent = () => {
}, [selectedTabEle.current]); }, [selectedTabEle.current]);
const [tabHover, setTabHover] = useState<number | null>(null); const [tabHover, setTabHover] = useState<number | null>(null);
const inputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
requestIdleCallback(() => {
// this is more reliable than autoFocus attribute. sometimes autoFocus
// would let the popup open and then input would not be focused.
inputRef.current?.focus();
});
}, [inputRef.current]);
return ( return (
<div> <div>
<SearchContainer> <SearchContainer>
@@ -428,8 +555,8 @@ const Popup: FunctionComponent = () => {
</SearchIconLeftContainer> </SearchIconLeftContainer>
<SearchInputRightContainer> <SearchInputRightContainer>
<SearchInput <SearchInput
ref={inputRef}
type="text" type="text"
autoFocus
placeholder={t("ui_search_tabs")} placeholder={t("ui_search_tabs")}
value={searchQuery} value={searchQuery}
onKeyDown={(e) => { onKeyDown={(e) => {
@@ -458,7 +585,7 @@ const Popup: FunctionComponent = () => {
<TabListEmpty>No Results Found</TabListEmpty> <TabListEmpty>No Results Found</TabListEmpty>
) : null} ) : null}
{searchResults.map((t, i) => { {searchResults.map((t, i) => {
const favicURL = faviconURL(t.item, 32); const favicURL = faviconURL(t.item);
const showCloseAction = tabHover === t.item.id! && !t.item.pinned; const showCloseAction = tabHover === t.item.id! && !t.item.pinned;
return ( return (
<TabItem <TabItem
@@ -480,7 +607,7 @@ const Popup: FunctionComponent = () => {
}} }}
> >
{showCloseAction ? ( {showCloseAction ? (
<div <TabItemCloseBox
title="Close Tab" title="Close Tab"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -500,7 +627,7 @@ const Popup: FunctionComponent = () => {
> >
<path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm4.151 17.943l-4.143-4.102-4.117 4.159-1.833-1.833 4.104-4.157-4.162-4.119 1.833-1.833 4.155 4.102 4.106-4.16 1.849 1.849-4.1 4.141 4.157 4.104-1.849 1.849z" /> <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm4.151 17.943l-4.143-4.102-4.117 4.159-1.833-1.833 4.104-4.157-4.162-4.119 1.833-1.833 4.155 4.102 4.106-4.16 1.849 1.849-4.1 4.141 4.157 4.104-1.849 1.849z" />
</svg> </svg>
</div> </TabItemCloseBox>
) : ( ) : (
<img width={16} height={16} src={favicURL} /> <img width={16} height={16} src={favicURL} />
)} )}
@@ -514,6 +641,17 @@ const Popup: FunctionComponent = () => {
</TabItemMainTitle> </TabItemMainTitle>
<TabItemMainHostname $dark={darkMode}> <TabItemMainHostname $dark={darkMode}>
{t.item.url ? hostname(t.item.url) : ""} {t.item.url ? hostname(t.item.url) : ""}
{t.item.group ? (
<>
&nbsp;|&nbsp;
<TabItemGroupPill
$dark={darkMode}
$color={t.item.group.color}
>
{t.item.group.title}
</TabItemGroupPill>
</>
) : null}
</TabItemMainHostname> </TabItemMainHostname>
</TabItemMain> </TabItemMain>
</TabItem> </TabItem>

3
src/types.ts Normal file
View File

@@ -0,0 +1,3 @@
export type Tab = chrome.tabs.Tab & {
group?: chrome.tabGroups.TabGroup;
};