diff --git a/README.md b/README.md
index fa2b494..1e51d09 100644
--- a/README.md
+++ b/README.md
@@ -20,3 +20,49 @@ Once enabled, you can use the Raycast extension to interact with your Beeper cha
## Setup
See the [Beeper Desktop API Getting Started guide](https://developers.beeper.com/desktop-api/#get-started) for additional setup instructions.
+
+## Available Commands
+
+The extension provides the following commands:
+
+### List Accounts
+
+- **Description**: Display a list of all connected Beeper accounts
+- **Usage**: Activate from Raycast and select "List Accounts"
+
+### List Chats
+
+- **Description**: Display a list of all your Beeper chats
+- **Usage**: Activate from Raycast and select "List Chats"
+
+### Search Chats
+
+- **Description**: Search through your Beeper chats
+- **Usage**: Activate from Raycast and select "Search Chats", then enter search text
+
+### Unread Chats
+
+- **Description**: Display all chats with unread messages
+- **Usage**: Activate from Raycast and select "Unread Chats"
+
+### Send Message
+
+- **Description**: Quickly send a message to any chat
+- **Usage**: Activate from Raycast and select "Send Message"
+
+### Focus Beeper Desktop
+
+- **Description**: Bring Beeper Desktop to the foreground
+- **Usage**: Activate from Raycast and select "Focus Beeper Desktop"
+
+## NPM Commands
+
+The project uses the following npm scripts:
+
+| Command | Description |
+|---------|-------------|
+| `npm run dev` | Runs the extension in development mode |
+| `npm run build` | Compiles the extension for distribution |
+| `npm run lint` | Validates code using ESLint |
+| `npm run fix-lint` | Automatically fixes linting issues |
+| `npm run publish` | Publishes the extension to the Raycast Store |
diff --git a/assets/discord.svg b/assets/discord.svg
new file mode 100644
index 0000000..ce3b4de
--- /dev/null
+++ b/assets/discord.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/email.svg b/assets/email.svg
new file mode 100644
index 0000000..a108390
--- /dev/null
+++ b/assets/email.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/facebook.svg b/assets/facebook.svg
new file mode 100644
index 0000000..976bd6b
--- /dev/null
+++ b/assets/facebook.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/google-messages.svg b/assets/google-messages.svg
new file mode 100644
index 0000000..59155bc
--- /dev/null
+++ b/assets/google-messages.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/assets/imessage.svg b/assets/imessage.svg
new file mode 100644
index 0000000..201b9af
--- /dev/null
+++ b/assets/imessage.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/instagram.svg b/assets/instagram.svg
new file mode 100644
index 0000000..be77209
--- /dev/null
+++ b/assets/instagram.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/messenger.svg b/assets/messenger.svg
new file mode 100644
index 0000000..17476a1
--- /dev/null
+++ b/assets/messenger.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/signal.svg b/assets/signal.svg
new file mode 100644
index 0000000..2f01d47
--- /dev/null
+++ b/assets/signal.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/slack.svg b/assets/slack.svg
new file mode 100644
index 0000000..bb0b1f1
--- /dev/null
+++ b/assets/slack.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/assets/telegram.svg b/assets/telegram.svg
new file mode 100644
index 0000000..ee61f29
--- /dev/null
+++ b/assets/telegram.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/twitter.svg b/assets/twitter.svg
new file mode 100644
index 0000000..052dde9
--- /dev/null
+++ b/assets/twitter.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/whatsapp.svg b/assets/whatsapp.svg
new file mode 100644
index 0000000..d0f7ec5
--- /dev/null
+++ b/assets/whatsapp.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/metadata/beeper-1.png b/metadata/beeper-1.png
new file mode 100644
index 0000000..d13d27d
Binary files /dev/null and b/metadata/beeper-1.png differ
diff --git a/metadata/beeper-2.png b/metadata/beeper-2.png
new file mode 100644
index 0000000..86a0a39
Binary files /dev/null and b/metadata/beeper-2.png differ
diff --git a/package-lock.json b/package-lock.json
index 35f1ed9..cdb5e26 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,17 +7,19 @@
"name": "beeper",
"license": "MIT",
"dependencies": {
- "@beeper/desktop-api": "^0.1.4",
- "@raycast/api": "^1.102.6",
- "@raycast/utils": "^1.17.0"
+ "@beeper/desktop-api": "latest",
+ "@raycast/api": "^1.104.1",
+ "@raycast/utils": "^2.2.2",
+ "fuse.js": "^7.1.0"
},
"devDependencies": {
- "@raycast/eslint-config": "^2.0.4",
- "@types/node": "22.13.10",
- "@types/react": "19.0.10",
- "eslint": "^9.22.0",
- "prettier": "^3.5.3",
- "typescript": "^5.8.2"
+ "@raycast/eslint-config": "^2.1.1",
+ "@types/node": "22.19.3",
+ "@types/react": "19.2.7",
+ "eslint": "^9.39.2",
+ "prettier": "^3.7.4",
+ "simple-icons": "^15.22.0",
+ "typescript": "^5.9.3"
}
},
"node_modules/@beeper/desktop-api": {
@@ -27,9 +29,9 @@
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
- "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
],
@@ -43,9 +45,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
- "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
],
@@ -59,9 +61,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
- "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
],
@@ -75,9 +77,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
- "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
],
@@ -91,9 +93,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
- "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
],
@@ -107,9 +109,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
- "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
],
@@ -123,9 +125,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
- "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
],
@@ -139,9 +141,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
- "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
],
@@ -155,9 +157,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
- "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
],
@@ -171,9 +173,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
- "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
],
@@ -187,9 +189,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
- "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
],
@@ -203,9 +205,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
- "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
],
@@ -219,9 +221,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
- "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
],
@@ -235,9 +237,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
- "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
],
@@ -251,9 +253,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
- "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
],
@@ -267,9 +269,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
- "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
],
@@ -283,9 +285,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
- "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
"cpu": [
"x64"
],
@@ -299,9 +301,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
"cpu": [
"arm64"
],
@@ -315,9 +317,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
- "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
],
@@ -331,9 +333,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
"cpu": [
"arm64"
],
@@ -347,9 +349,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
- "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
],
@@ -363,9 +365,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
- "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
"cpu": [
"arm64"
],
@@ -379,9 +381,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
- "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
],
@@ -395,9 +397,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
- "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
],
@@ -411,9 +413,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
- "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
],
@@ -427,9 +429,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
- "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
],
@@ -443,9 +445,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz",
- "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==",
+ "version": "4.9.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -462,9 +464,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "version": "4.12.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -472,13 +474,13 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
- "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "version": "0.21.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.6",
+ "@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
@@ -486,44 +488,23 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "version": "0.4.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"dev": true,
"license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "version": "0.17.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -557,17 +538,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
@@ -581,23 +551,10 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@eslint/js": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz",
- "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==",
+ "version": "9.39.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint/js/-/js-9.39.3.tgz",
+ "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -608,9 +565,9 @@
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "version": "2.1.7",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -618,13 +575,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.4.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
@@ -683,17 +640,26 @@
"url": "https://github.com/sponsors/nzakas"
}
},
+ "node_modules/@inquirer/ansi": {
+ "version": "1.0.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/ansi/-/ansi-1.0.2.tgz",
+ "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@inquirer/checkbox": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz",
- "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==",
+ "version": "4.3.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/checkbox/-/checkbox-4.3.2.tgz",
+ "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/figures": "^1.0.13",
- "@inquirer/type": "^3.0.8",
- "ansi-escapes": "^4.3.2",
- "yoctocolors-cjs": "^2.1.2"
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
},
"engines": {
"node": ">=18"
@@ -708,13 +674,13 @@
}
},
"node_modules/@inquirer/confirm": {
- "version": "5.1.16",
- "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz",
- "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==",
+ "version": "5.1.21",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/confirm/-/confirm-5.1.21.tgz",
+ "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
},
"engines": {
"node": ">=18"
@@ -729,19 +695,19 @@
}
},
"node_modules/@inquirer/core": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz",
- "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==",
+ "version": "10.3.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/core/-/core-10.3.2.tgz",
+ "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==",
"license": "MIT",
"dependencies": {
- "@inquirer/figures": "^1.0.13",
- "@inquirer/type": "^3.0.8",
- "ansi-escapes": "^4.3.2",
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
"cli-width": "^4.1.0",
"mute-stream": "^2.0.0",
"signal-exit": "^4.1.0",
"wrap-ansi": "^6.2.0",
- "yoctocolors-cjs": "^2.1.2"
+ "yoctocolors-cjs": "^2.1.3"
},
"engines": {
"node": ">=18"
@@ -757,7 +723,7 @@
},
"node_modules/@inquirer/core/node_modules/wrap-ansi": {
"version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT",
"dependencies": {
@@ -770,14 +736,14 @@
}
},
"node_modules/@inquirer/editor": {
- "version": "4.2.18",
- "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz",
- "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==",
+ "version": "4.2.23",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/editor/-/editor-4.2.23.tgz",
+ "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/external-editor": "^1.0.1",
- "@inquirer/type": "^3.0.8"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/external-editor": "^1.0.3",
+ "@inquirer/type": "^3.0.10"
},
"engines": {
"node": ">=18"
@@ -792,14 +758,14 @@
}
},
"node_modules/@inquirer/expand": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz",
- "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==",
+ "version": "4.0.23",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/expand/-/expand-4.0.23.tgz",
+ "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8",
- "yoctocolors-cjs": "^2.1.2"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
},
"engines": {
"node": ">=18"
@@ -814,13 +780,13 @@
}
},
"node_modules/@inquirer/external-editor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz",
- "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==",
+ "version": "1.0.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+ "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
"license": "MIT",
"dependencies": {
- "chardet": "^2.1.0",
- "iconv-lite": "^0.6.3"
+ "chardet": "^2.1.1",
+ "iconv-lite": "^0.7.0"
},
"engines": {
"node": ">=18"
@@ -835,22 +801,22 @@
}
},
"node_modules/@inquirer/figures": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz",
- "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==",
+ "version": "1.0.15",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/figures/-/figures-1.0.15.tgz",
+ "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/input": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz",
- "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==",
+ "version": "4.3.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/input/-/input-4.3.1.tgz",
+ "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
},
"engines": {
"node": ">=18"
@@ -865,13 +831,13 @@
}
},
"node_modules/@inquirer/number": {
- "version": "3.0.18",
- "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz",
- "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==",
+ "version": "3.0.23",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/number/-/number-3.0.23.tgz",
+ "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
},
"engines": {
"node": ">=18"
@@ -886,14 +852,14 @@
}
},
"node_modules/@inquirer/password": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz",
- "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==",
+ "version": "4.0.23",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/password/-/password-4.0.23.tgz",
+ "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8",
- "ansi-escapes": "^4.3.2"
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
},
"engines": {
"node": ">=18"
@@ -908,21 +874,21 @@
}
},
"node_modules/@inquirer/prompts": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.4.tgz",
- "integrity": "sha512-MuxVZ1en1g5oGamXV3DWP89GEkdD54alcfhHd7InUW5BifAdKQEK9SLFa/5hlWbvuhMPlobF0WAx7Okq988Jxg==",
+ "version": "7.10.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/prompts/-/prompts-7.10.1.tgz",
+ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==",
"license": "MIT",
"dependencies": {
- "@inquirer/checkbox": "^4.2.2",
- "@inquirer/confirm": "^5.1.16",
- "@inquirer/editor": "^4.2.18",
- "@inquirer/expand": "^4.0.18",
- "@inquirer/input": "^4.2.2",
- "@inquirer/number": "^3.0.18",
- "@inquirer/password": "^4.0.18",
- "@inquirer/rawlist": "^4.1.6",
- "@inquirer/search": "^3.1.1",
- "@inquirer/select": "^4.3.2"
+ "@inquirer/checkbox": "^4.3.2",
+ "@inquirer/confirm": "^5.1.21",
+ "@inquirer/editor": "^4.2.23",
+ "@inquirer/expand": "^4.0.23",
+ "@inquirer/input": "^4.3.1",
+ "@inquirer/number": "^3.0.23",
+ "@inquirer/password": "^4.0.23",
+ "@inquirer/rawlist": "^4.1.11",
+ "@inquirer/search": "^3.2.2",
+ "@inquirer/select": "^4.4.2"
},
"engines": {
"node": ">=18"
@@ -937,14 +903,14 @@
}
},
"node_modules/@inquirer/rawlist": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz",
- "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==",
+ "version": "4.1.11",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/rawlist/-/rawlist-4.1.11.tgz",
+ "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/type": "^3.0.8",
- "yoctocolors-cjs": "^2.1.2"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
},
"engines": {
"node": ">=18"
@@ -959,15 +925,15 @@
}
},
"node_modules/@inquirer/search": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz",
- "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==",
+ "version": "3.2.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/search/-/search-3.2.2.tgz",
+ "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/figures": "^1.0.13",
- "@inquirer/type": "^3.0.8",
- "yoctocolors-cjs": "^2.1.2"
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
},
"engines": {
"node": ">=18"
@@ -982,16 +948,16 @@
}
},
"node_modules/@inquirer/select": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz",
- "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==",
+ "version": "4.4.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/select/-/select-4.4.2.tgz",
+ "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==",
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.2.0",
- "@inquirer/figures": "^1.0.13",
- "@inquirer/type": "^3.0.8",
- "ansi-escapes": "^4.3.2",
- "yoctocolors-cjs": "^2.1.2"
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
},
"engines": {
"node": ">=18"
@@ -1006,9 +972,9 @@
}
},
"node_modules/@inquirer/type": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz",
- "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==",
+ "version": "3.0.10",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@inquirer/type/-/type-3.0.10.tgz",
+ "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -1022,62 +988,24 @@
}
}
},
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/@oclif/core": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.5.2.tgz",
- "integrity": "sha512-eQcKyrEcDYeZJKu4vUWiu0ii/1Gfev6GF4FsLSgNez5/+aQyAUCjg3ZWlurf491WiYZTXCWyKAxyPWk8DKv2MA==",
+ "version": "4.8.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@oclif/core/-/core-4.8.1.tgz",
+ "integrity": "sha512-07mq0vKCWNsB85ZHeBMlTAiO0KLFqHyAeRK3bD2K8CI1tX3tiwkWw1lZQZkiw8MUBrhxdROhMkYMY4Q0l7JHqA==",
"license": "MIT",
"dependencies": {
"ansi-escapes": "^4.3.2",
"ansis": "^3.17.0",
"clean-stack": "^3.0.1",
"cli-spinners": "^2.9.2",
- "debug": "^4.4.0",
+ "debug": "^4.4.3",
"ejs": "^3.1.10",
"get-package-type": "^0.1.0",
"indent-string": "^4.0.0",
"is-wsl": "^2.2.0",
"lilconfig": "^3.1.3",
- "minimatch": "^9.0.5",
- "semver": "^7.6.3",
+ "minimatch": "^10.2.1",
+ "semver": "^7.7.3",
"string-width": "^4.2.3",
"supports-color": "^8",
"tinyglobby": "^0.2.14",
@@ -1090,9 +1018,9 @@
}
},
"node_modules/@oclif/plugin-autocomplete": {
- "version": "3.2.34",
- "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.2.34.tgz",
- "integrity": "sha512-KhbPcNjitAU7jUojMXJ3l7duWVub0L0pEr3r3bLrpJBNuIJhoIJ7p56Ropcb7OMH2xcaz5B8HGq56cTOe1FHEg==",
+ "version": "3.2.40",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.2.40.tgz",
+ "integrity": "sha512-HCfDuUV3l5F5Wz7SKkaoFb+OMQ5vKul8zvsPNgI0QbZcQuGHmn3svk+392wSfXboyA1gq8kzEmKPAoQK6r6UNw==",
"license": "MIT",
"dependencies": {
"@oclif/core": "^4",
@@ -1105,9 +1033,9 @@
}
},
"node_modules/@oclif/plugin-help": {
- "version": "6.2.32",
- "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.32.tgz",
- "integrity": "sha512-LrmMdo9EMJciOvF8UurdoTcTMymv5npKtxMAyonZvhSvGR8YwCKnuHIh00+SO2mNtGOYam7f4xHnUmj2qmanyA==",
+ "version": "6.2.37",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@oclif/plugin-help/-/plugin-help-6.2.37.tgz",
+ "integrity": "sha512-5N/X/FzlJaYfpaHwDC0YHzOzKDWa41s9t+4FpCDu4f9OMReds4JeNBaaWk9rlIzdKjh2M6AC5Q18ORfECRkHGA==",
"license": "MIT",
"dependencies": {
"@oclif/core": "^4"
@@ -1117,13 +1045,13 @@
}
},
"node_modules/@oclif/plugin-not-found": {
- "version": "3.2.67",
- "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.67.tgz",
- "integrity": "sha512-Q2VluSwTrh7Sk0ey88Lk5WSATn9AZ6TjYQIyt2QrQolOBErAgpDoDSMVRYuVNtjxPBTDBzz4MM54QRFa/nN4IQ==",
+ "version": "3.2.74",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@oclif/plugin-not-found/-/plugin-not-found-3.2.74.tgz",
+ "integrity": "sha512-6RD/EuIUGxAYR45nMQg+nw+PqwCXUxkR6Eyn+1fvbVjtb9d+60OPwB77LCRUI4zKNI+n0LOFaMniEdSpb+A7kQ==",
"license": "MIT",
"dependencies": {
- "@inquirer/prompts": "^7.8.4",
- "@oclif/core": "^4.5.2",
+ "@inquirer/prompts": "^7.10.1",
+ "@oclif/core": "^4.8.0",
"ansis": "^3.17.0",
"fast-levenshtein": "^3.0.0"
},
@@ -1132,18 +1060,18 @@
}
},
"node_modules/@raycast/api": {
- "version": "1.102.6",
- "resolved": "https://registry.npmjs.org/@raycast/api/-/api-1.102.6.tgz",
- "integrity": "sha512-P6j4nSOJe8wlsaf8gU/fsEnB4/oO8AXywcX3NiVQGiG9PWb/OyCCWQA8SFNdARsYyzNsJRlR+XRfMVzCBNv8Rw==",
+ "version": "1.104.6",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@raycast/api/-/api-1.104.6.tgz",
+ "integrity": "sha512-W3UnZrfh1EH9mteQQOibeUvniDnVsbRgcfSUgVWFTGuI6mzKLFzoM8mD6n5amYQM51JVvbuJbaqtfMqsTmnQCQ==",
"license": "MIT",
"dependencies": {
- "@oclif/core": "^4.4.1",
- "@oclif/plugin-autocomplete": "^3.2.31",
- "@oclif/plugin-help": "^6.2.29",
- "@oclif/plugin-not-found": "^3.2.57",
+ "@oclif/core": "^4.5.4",
+ "@oclif/plugin-autocomplete": "^3.2.35",
+ "@oclif/plugin-help": "^6.2.33",
+ "@oclif/plugin-not-found": "^3.2.68",
"@types/node": "22.13.10",
"@types/react": "19.0.10",
- "esbuild": "^0.25.5",
+ "esbuild": "^0.25.10",
"react": "19.0.0"
},
"bin": {
@@ -1169,18 +1097,42 @@
}
}
},
+ "node_modules/@raycast/api/node_modules/@types/node": {
+ "version": "22.13.10",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@types/node/-/node-22.13.10.tgz",
+ "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.20.0"
+ }
+ },
+ "node_modules/@raycast/api/node_modules/@types/react": {
+ "version": "19.0.10",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@types/react/-/react-19.0.10.tgz",
+ "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@raycast/api/node_modules/undici-types": {
+ "version": "6.20.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/undici-types/-/undici-types-6.20.0.tgz",
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "license": "MIT"
+ },
"node_modules/@raycast/eslint-config": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@raycast/eslint-config/-/eslint-config-2.0.4.tgz",
- "integrity": "sha512-Sz5Q0HVY8pW21Sv7CDcJw882OGubSM3zx5uiDRQMkgt7Hui3I+IZOqxDInnhLsGG52prSdU7sg3R097hMJkkQw==",
+ "version": "2.1.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@raycast/eslint-config/-/eslint-config-2.1.1.tgz",
+ "integrity": "sha512-W0kxF+FJ+BYQn0EKIV739j2ZrHEtjo/LclsoZgUWg3t364Dq75XKcjqYFYx+59/DBaamY0amdajlfuDAf6veAg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint/js": "^9.22.0",
- "@raycast/eslint-plugin": "^2.0.4",
- "eslint-config-prettier": "^10.1.1",
- "globals": "^16.0.0",
- "typescript-eslint": "^8.26.1"
+ "@eslint/js": "^9.36.0",
+ "@raycast/eslint-plugin": "^2.1.1",
+ "eslint-config-prettier": "^10.1.8",
+ "globals": "^16.4.0",
+ "typescript-eslint": "^8.45.0"
},
"peerDependencies": {
"eslint": ">=8.23.0",
@@ -1189,9 +1141,9 @@
}
},
"node_modules/@raycast/eslint-plugin": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@raycast/eslint-plugin/-/eslint-plugin-2.0.7.tgz",
- "integrity": "sha512-4dAdua+TFQu7ay3EuDmg5nT7boIAj3Rjf0V1aQ3w5uAx/j98DvI91h/zbbkVb4mIXCom1w7SfRrZQzYIOE+tZw==",
+ "version": "2.1.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@raycast/eslint-plugin/-/eslint-plugin-2.1.1.tgz",
+ "integrity": "sha512-r2gs8uIlNp6I2mLOyN/kReGlvigzEeuyQPl4yw7nwLy8Zxjfjhg8txMViaBux8juBWBxbSWq/IfW6ZA50oeOHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1202,20 +1154,21 @@
}
},
"node_modules/@raycast/utils": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/@raycast/utils/-/utils-1.19.1.tgz",
- "integrity": "sha512-/udUGcTZCgZZwzesmjBkqG5naQZTD/ZLHbqRwkWcF+W97vf9tr9raxKyQjKsdZ17OVllw2T3sHBQsVUdEmCm2g==",
+ "version": "2.2.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@raycast/utils/-/utils-2.2.2.tgz",
+ "integrity": "sha512-tZcyWCHZvz4L/i1CGEnSZkBoK6wwX1pzlTKjcWWugbrQyG0QCMOxjKJfRC/iNkD+hHaqhMWUj4Y0LNo/NknvFw==",
"license": "MIT",
"dependencies": {
- "cross-fetch": "^3.1.6",
- "dequal": "^2.0.3",
- "object-hash": "^3.0.0",
- "signal-exit": "^4.0.2",
- "stream-chain": "^2.2.5",
- "stream-json": "^1.8.0"
+ "dequal": "^2.0.3"
},
"peerDependencies": {
- "@raycast/api": ">=1.69.0"
+ "@raycast/api": ">=1.99.4",
+ "react": ">=19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ }
}
},
"node_modules/@types/estree": {
@@ -1227,45 +1180,46 @@
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.13.10",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
- "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
+ "version": "22.19.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@types/node/-/node-22.19.3.tgz",
+ "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
+ "devOptional": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~6.20.0"
+ "undici-types": "~6.21.0"
}
},
"node_modules/@types/react": {
- "version": "19.0.10",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
- "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
+ "version": "19.2.7",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz",
- "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
+ "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/type-utils": "8.42.0",
- "@typescript-eslint/utils": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/type-utils": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "ignore": "^7.0.5",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1275,14 +1229,14 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.42.0",
- "eslint": "^8.57.0 || ^9.0.0",
+ "@typescript-eslint/parser": "^8.56.1",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
@@ -1291,17 +1245,17 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz",
- "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/parser/-/parser-8.56.1.tgz",
+ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1311,20 +1265,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
- "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
+ "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.42.0",
- "@typescript-eslint/types": "^8.42.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/tsconfig-utils": "^8.56.1",
+ "@typescript-eslint/types": "^8.56.1",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1338,14 +1292,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz",
- "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
+ "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0"
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1356,9 +1310,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
- "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
+ "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1373,17 +1327,17 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz",
- "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz",
+ "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/utils": "8.42.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1393,14 +1347,14 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
- "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/types/-/types-8.56.1.tgz",
+ "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1412,22 +1366,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
- "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
+ "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.42.0",
- "@typescript-eslint/tsconfig-utils": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/project-service": "8.56.1",
+ "@typescript-eslint/tsconfig-utils": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1441,16 +1394,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz",
- "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/utils/-/utils-8.56.1.tgz",
+ "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0"
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1460,19 +1413,19 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
- "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
+ "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "eslint-visitor-keys": "^4.2.1"
+ "@typescript-eslint/types": "8.56.1",
+ "eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1483,13 +1436,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "version": "5.0.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -1537,7 +1490,7 @@
},
"node_modules/ansi-escapes": {
"version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
"license": "MIT",
"dependencies": {
@@ -1552,7 +1505,7 @@
},
"node_modules/ansi-regex": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
@@ -1576,7 +1529,7 @@
},
"node_modules/ansis": {
"version": "3.17.0",
- "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/ansis/-/ansis-3.17.0.tgz",
"integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
"license": "ISC",
"engines": {
@@ -1592,36 +1545,29 @@
},
"node_modules/async": {
"version": "3.2.6",
- "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "license": "MIT"
- },
- "node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "version": "4.0.4",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
+ "node_modules/brace-expansion": {
+ "version": "5.0.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/brace-expansion/-/brace-expansion-5.0.3.tgz",
+ "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
"license": "MIT",
"dependencies": {
- "fill-range": "^7.1.1"
+ "balanced-match": "^4.0.2"
},
"engines": {
- "node": ">=8"
+ "node": "18 || 20 || >=22"
}
},
"node_modules/callsites": {
@@ -1665,14 +1611,14 @@
}
},
"node_modules/chardet": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz",
- "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==",
+ "version": "2.1.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/chardet/-/chardet-2.1.1.tgz",
+ "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
"license": "MIT"
},
"node_modules/clean-stack": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/clean-stack/-/clean-stack-3.0.1.tgz",
"integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==",
"license": "MIT",
"dependencies": {
@@ -1687,7 +1633,7 @@
},
"node_modules/cli-spinners": {
"version": "2.9.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/cli-spinners/-/cli-spinners-2.9.2.tgz",
"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
"license": "MIT",
"engines": {
@@ -1699,7 +1645,7 @@
},
"node_modules/cli-width": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/cli-width/-/cli-width-4.1.0.tgz",
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
"license": "ISC",
"engines": {
@@ -1724,22 +1670,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cross-fetch": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz",
- "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==",
- "license": "MIT",
- "dependencies": {
- "node-fetch": "^2.7.0"
- }
- },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1756,15 +1686,15 @@
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1796,7 +1726,7 @@
},
"node_modules/ejs": {
"version": "3.1.10",
- "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"license": "Apache-2.0",
"dependencies": {
@@ -1811,14 +1741,14 @@
},
"node_modules/emoji-regex": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/esbuild": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
- "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "version": "0.25.12",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -1828,32 +1758,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.9",
- "@esbuild/android-arm": "0.25.9",
- "@esbuild/android-arm64": "0.25.9",
- "@esbuild/android-x64": "0.25.9",
- "@esbuild/darwin-arm64": "0.25.9",
- "@esbuild/darwin-x64": "0.25.9",
- "@esbuild/freebsd-arm64": "0.25.9",
- "@esbuild/freebsd-x64": "0.25.9",
- "@esbuild/linux-arm": "0.25.9",
- "@esbuild/linux-arm64": "0.25.9",
- "@esbuild/linux-ia32": "0.25.9",
- "@esbuild/linux-loong64": "0.25.9",
- "@esbuild/linux-mips64el": "0.25.9",
- "@esbuild/linux-ppc64": "0.25.9",
- "@esbuild/linux-riscv64": "0.25.9",
- "@esbuild/linux-s390x": "0.25.9",
- "@esbuild/linux-x64": "0.25.9",
- "@esbuild/netbsd-arm64": "0.25.9",
- "@esbuild/netbsd-x64": "0.25.9",
- "@esbuild/openbsd-arm64": "0.25.9",
- "@esbuild/openbsd-x64": "0.25.9",
- "@esbuild/openharmony-arm64": "0.25.9",
- "@esbuild/sunos-x64": "0.25.9",
- "@esbuild/win32-arm64": "0.25.9",
- "@esbuild/win32-ia32": "0.25.9",
- "@esbuild/win32-x64": "0.25.9"
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
}
},
"node_modules/escape-string-regexp": {
@@ -1869,25 +1799,24 @@
}
},
"node_modules/eslint": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz",
- "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
+ "version": "9.39.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/eslint/-/eslint-9.39.3.tgz",
+ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.34.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint/js": "9.39.3",
+ "@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
@@ -1975,17 +1904,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/eslint/node_modules/eslint-visitor-keys": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
@@ -1999,19 +1917,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/espree": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2096,36 +2001,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -2135,7 +2010,7 @@
},
"node_modules/fast-levenshtein": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz",
"integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==",
"license": "MIT",
"dependencies": {
@@ -2144,21 +2019,28 @@
},
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
- "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
"integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
"license": "MIT",
"engines": {
"node": ">= 4.9.1"
}
},
- "node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
}
},
"node_modules/file-entry-cache": {
@@ -2175,37 +2057,15 @@
}
},
"node_modules/filelist": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
- "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+ "version": "1.0.5",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/filelist/-/filelist-1.0.5.tgz",
+ "integrity": "sha512-ct/ckWBV/9Dg3MlvCXsLcSUyoWwv9mCKqlhLNB2DAuXR/NZolSXlQqP5dyy6guWlPXBhodZyZ5lGPQcbQDxrEQ==",
"license": "Apache-2.0",
"dependencies": {
- "minimatch": "^5.0.1"
- }
- },
- "node_modules/filelist/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
+ "minimatch": "^10.2.1"
},
"engines": {
- "node": ">=8"
+ "node": "20 || >=22"
}
},
"node_modules/find-up": {
@@ -2246,9 +2106,18 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/fuse.js": {
+ "version": "7.1.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/fuse.js/-/fuse.js-7.1.0.tgz",
+ "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/get-package-type": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"license": "MIT",
"engines": {
@@ -2269,9 +2138,9 @@
}
},
"node_modules/globals": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
- "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
+ "version": "16.5.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2281,13 +2150,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -2298,15 +2160,19 @@
}
},
"node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "version": "0.7.2",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/ignore": {
@@ -2348,7 +2214,7 @@
},
"node_modules/indent-string": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"license": "MIT",
"engines": {
@@ -2357,7 +2223,7 @@
},
"node_modules/is-docker": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"license": "MIT",
"bin": {
@@ -2382,7 +2248,7 @@
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
@@ -2402,19 +2268,9 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
"node_modules/is-wsl": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"license": "MIT",
"dependencies": {
@@ -2433,7 +2289,7 @@
},
"node_modules/jake": {
"version": "10.9.4",
- "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": {
@@ -2508,7 +2364,7 @@
},
"node_modules/lilconfig": {
"version": "3.1.3",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"license": "MIT",
"engines": {
@@ -2541,40 +2397,16 @@
"dev": true,
"license": "MIT"
},
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
"node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "license": "ISC",
+ "version": "10.2.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/minimatch/-/minimatch-10.2.3.tgz",
+ "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^5.0.2"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -2588,7 +2420,7 @@
},
"node_modules/mute-stream": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/mute-stream/-/mute-stream-2.0.0.tgz",
"integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
"license": "ISC",
"engines": {
@@ -2602,35 +2434,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "license": "MIT",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/object-hash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
- "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -2723,18 +2526,17 @@
},
"node_modules/picocolors": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
+ "version": "4.0.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
@@ -2751,9 +2553,9 @@
}
},
"node_modules/prettier": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
- "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "version": "3.8.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -2776,27 +2578,6 @@
"node": ">=6"
}
},
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
@@ -2816,51 +2597,16 @@
"node": ">=4"
}
},
- "node_modules/reusify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
"node_modules/safer-buffer": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.4",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -2894,7 +2640,7 @@
},
"node_modules/signal-exit": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"license": "ISC",
"engines": {
@@ -2904,24 +2650,29 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/stream-chain": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
- "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/stream-json": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz",
- "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "stream-chain": "^2.2.5"
+ "node_modules/simple-icons": {
+ "version": "15.22.0",
+ "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-15.22.0.tgz",
+ "integrity": "sha512-i/w5Ie4tENfGYbdCo2iJ+oies0vOFd8QXWHopKOUzudfLCvnmeheF2PpHp89Z2azpc+c2su3lMiWO/SpP+429A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/simple-icons"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/simple-icons"
+ }
+ ],
+ "license": "CC0-1.0",
+ "engines": {
+ "node": ">=0.12.18"
}
},
"node_modules/string-width": {
"version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
@@ -2935,7 +2686,7 @@
},
"node_modules/strip-ansi": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
@@ -2960,7 +2711,7 @@
},
"node_modules/supports-color": {
"version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"license": "MIT",
"dependencies": {
@@ -2974,13 +2725,13 @@
}
},
"node_modules/tinyglobby": {
- "version": "0.2.14",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
- "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "version": "0.2.15",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"license": "MIT",
"dependencies": {
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -2989,58 +2740,10 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/tinyglobby/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "license": "MIT"
- },
"node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "version": "2.4.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3065,7 +2768,7 @@
},
"node_modules/type-fest": {
"version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"license": "(MIT OR CC0-1.0)",
"engines": {
@@ -3076,9 +2779,9 @@
}
},
"node_modules/typescript": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "version": "5.9.3",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -3090,16 +2793,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz",
- "integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==",
+ "version": "8.56.1",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/typescript-eslint/-/typescript-eslint-8.56.1.tgz",
+ "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.42.0",
- "@typescript-eslint/parser": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/utils": "8.42.0"
+ "@typescript-eslint/eslint-plugin": "8.56.1",
+ "@typescript-eslint/parser": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3109,14 +2812,15 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/undici-types": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
- "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "version": "6.21.0",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "devOptional": true,
"license": "MIT"
},
"node_modules/uri-js": {
@@ -3129,22 +2833,6 @@
"punycode": "^2.1.0"
}
},
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "license": "BSD-2-Clause"
- },
- "node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3163,7 +2851,7 @@
},
"node_modules/widest-line": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/widest-line/-/widest-line-3.1.0.tgz",
"integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
"license": "MIT",
"dependencies": {
@@ -3185,13 +2873,13 @@
},
"node_modules/wordwrap": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
"license": "MIT"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
@@ -3221,7 +2909,7 @@
},
"node_modules/yoctocolors-cjs": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+ "resolved": "https://artifactory.skoda.vwgroup.com/artifactory/api/npm/front-npm-virtual/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
"integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
"license": "MIT",
"engines": {
diff --git a/package.json b/package.json
index 5cd4378..f3c0781 100644
--- a/package.json
+++ b/package.json
@@ -20,9 +20,45 @@
"mode": "no-view"
},
{
- "name": "find-chat",
- "title": "Find Chat",
- "description": "Find chats from Beeper Desktop",
+ "name": "list-chats",
+ "title": "List Chats",
+ "description": "List all your Beeper chats",
+ "mode": "view"
+ },
+ {
+ "name": "search-chats",
+ "title": "Search Chats",
+ "description": "Search through your Beeper chats",
+ "mode": "view"
+ },
+ {
+ "name": "unread-chats",
+ "title": "Unread Chats",
+ "description": "View all chats with unread messages",
+ "mode": "view"
+ },
+ {
+ "name": "send-message",
+ "title": "Send Message",
+ "description": "Quickly send a message to any chat",
+ "mode": "view"
+ },
+ {
+ "name": "search-messages",
+ "title": "Search Messages",
+ "description": "Search through your Beeper messages",
+ "mode": "view"
+ },
+ {
+ "name": "contacts",
+ "title": "Contacts",
+ "description": "Search contacts across connected Beeper accounts",
+ "mode": "view"
+ },
+ {
+ "name": "search-all",
+ "title": "Search Everything",
+ "description": "Search across all Beeper chats and messages",
"mode": "view"
}
],
@@ -34,20 +70,43 @@
"type": "textfield",
"required": false,
"default": "http://localhost:23373"
+ },
+ {
+ "name": "language",
+ "title": "Language",
+ "description": "Select the extension language",
+ "type": "dropdown",
+ "required": false,
+ "default": "en",
+ "data": [
+ {
+ "title": "English",
+ "value": "en"
+ },
+ {
+ "title": "Čeština",
+ "value": "cs"
+ }
+ ]
}
],
"dependencies": {
- "@beeper/desktop-api": "^0.1.4",
- "@raycast/api": "^1.102.6",
- "@raycast/utils": "^1.17.0"
+ "@beeper/desktop-api": "latest",
+ "@raycast/api": "^1.104.1",
+ "@raycast/utils": "^2.2.2",
+ "fuse.js": "^7.1.0"
},
"devDependencies": {
- "@raycast/eslint-config": "^2.0.4",
- "@types/node": "22.13.10",
- "@types/react": "19.0.10",
- "eslint": "^9.22.0",
- "prettier": "^3.5.3",
- "typescript": "^5.8.2"
+ "@raycast/eslint-config": "^2.1.1",
+ "@types/node": "22.19.3",
+ "@types/react": "19.2.7",
+ "eslint": "^9.39.2",
+ "prettier": "^3.7.4",
+ "simple-icons": "^15.22.0",
+ "typescript": "^5.9.3"
+ },
+ "overrides": {
+ "minimatch": ">=10.2.1"
},
"scripts": {
"build": "ray build",
@@ -57,4 +116,4 @@
"prepublishOnly": "echo \"\\n\\nIt seems like you are trying to publish the Raycast extension to npm.\\n\\nIf you did intend to publish it to npm, remove the \\`prepublishOnly\\` script and rerun \\`npm publish\\` again.\\nIf you wanted to publish it to the Raycast Store instead, use \\`npm run publish\\` instead.\\n\\n\" && exit 1",
"publish": "npx @raycast/api@latest publish"
}
-}
+}
\ No newline at end of file
diff --git a/scripts/generate-icons.js b/scripts/generate-icons.js
new file mode 100644
index 0000000..af95b3a
--- /dev/null
+++ b/scripts/generate-icons.js
@@ -0,0 +1,54 @@
+#!/usr/bin/env node
+
+/**
+ * Script to generate network icons from simple-icons
+ * Run: node scripts/generate-icons.js
+ */
+
+const fs = require('fs');
+const path = require('path');
+const simpleIcons = require('simple-icons');
+
+// Get icons from simple-icons
+const icons = {
+ slack: simpleIcons.siSlack,
+ whatsapp: simpleIcons.siWhatsapp,
+ telegram: simpleIcons.siTelegram,
+ discord: simpleIcons.siDiscord,
+ instagram: simpleIcons.siInstagram,
+ facebook: simpleIcons.siFacebook,
+ messenger: simpleIcons.siMessenger,
+ signal: simpleIcons.siSignal,
+ linkedin: simpleIcons.siLinkedin,
+ twitter: simpleIcons.siX,
+ imessage: simpleIcons.siImessage,
+ sms: simpleIcons.siAndroidmessages,
+ email: simpleIcons.siGmail,
+};
+
+const outputDir = path.join(__dirname, '..', 'assets', 'icons');
+
+// Ensure output directory exists
+if (!fs.existsSync(outputDir)) {
+ fs.mkdirSync(outputDir, { recursive: true });
+}
+
+// Generate SVG files with proper styling for Raycast
+// Use a dark color that Raycast can tint for light/dark mode
+Object.entries(icons).forEach(([name, icon]) => {
+ if (!icon) {
+ console.log(`Skipping ${name} - icon not found`);
+ return;
+ }
+
+ const svg = ``;
+
+ const filename = `${name}.png`;
+ fs.writeFileSync(path.join(outputDir, filename.replace('.png', '.svg')), svg);
+ console.log(`Generated ${filename.replace('.png', '.svg')}`);
+});
+
+console.log('\nAll icons generated successfully!');
diff --git a/src/api.ts b/src/api.ts
index 5f4f2b0..907097f 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -1,7 +1,10 @@
import BeeperDesktop from "@beeper/desktop-api";
-import type { AppFocusParams } from "@beeper/desktop-api/resources/app";
import { closeMainWindow, getPreferenceValues, OAuth, showHUD } from "@raycast/api";
import { OAuthService, usePromise, getAccessToken } from "@raycast/utils";
+import { readFile } from "node:fs/promises";
+import { basename } from "node:path";
+import { fileURLToPath } from "node:url";
+import { t } from "./locales";
interface Preferences {
baseURL?: string;
@@ -39,13 +42,15 @@ export function createBeeperOAuth() {
refreshTokenUrl: `${baseURL}/oauth/token`,
bodyEncoding: "url-encoded",
onAuthorize: ({ token }) => {
- // Reset client when new token is obtained
clientInstance = null;
lastAccessToken = token;
},
});
}
+/**
+ * Returns a cached BeeperDesktop client, creating a new instance when the configured base URL or access token has changed.
+ */
export function getBeeperDesktop(): BeeperDesktop {
const baseURL = getBaseURL();
const { token: accessToken } = getAccessToken();
@@ -63,17 +68,330 @@ export function getBeeperDesktop(): BeeperDesktop {
return clientInstance;
}
-export function useBeeperDesktop(fn: (client: BeeperDesktop) => Promise) {
- return usePromise(async () => fn(getBeeperDesktop()));
+/**
+ * Execute an asynchronous operation using the current BeeperDesktop client and return its managed result.
+ * The operation will be re-executed whenever any value in the args array changes.
+ */
+export function useBeeperDesktop(
+ fn: (client: BeeperDesktop, ...args: A) => Promise,
+ args?: A,
+) {
+ return usePromise((...a: A) => fn(getBeeperDesktop(), ...a), (args ?? []) as A);
}
-export const focusApp = async (params: AppFocusParams = {}) => {
+export const focusApp = async (
+ params: {
+ chatID?: string;
+ draftText?: string;
+ draftAttachmentPath?: string;
+ messageID?: string;
+ } = {},
+) => {
+ const translations = t();
try {
- await getBeeperDesktop().app.focus(params);
+ await getBeeperDesktop().post("/v1/focus", { body: params });
await closeMainWindow();
- await showHUD("Beeper Desktop focused");
+ await showHUD(translations.commands.focusApp.successMessage);
} catch (error) {
console.error("Failed to focus Beeper Desktop:", error);
- await showHUD("Failed to focus Beeper Desktop");
+ await showHUD(translations.commands.focusApp.errorMessage);
}
};
+
+// Deep-link constants
+const RAYCAST_EXTENSION_AUTHOR = "batuhan";
+const RAYCAST_EXTENSION_NAME = "beeper";
+const RAYCAST_FOCUS_COMMAND = "focus-app";
+
+export const getRaycastFocusLink = (
+ params: {
+ chatID?: string;
+ draftText?: string;
+ draftAttachmentPath?: string;
+ messageID?: string;
+ } = {},
+) => {
+ const args = Object.keys(params).length > 0 ? `?arguments=${encodeURIComponent(JSON.stringify(params))}` : "";
+ return `raycast://extensions/${RAYCAST_EXTENSION_AUTHOR}/${RAYCAST_EXTENSION_NAME}/${RAYCAST_FOCUS_COMMAND}${args}`;
+};
+
+// Types
+export type MessageAttachmentInput = {
+ uploadID: string;
+ mimeType?: string;
+ fileName?: string;
+ size?: { width?: number; height?: number };
+ duration?: number;
+ type?: "gif" | "voiceNote" | "sticker";
+};
+
+export type MessageEditInput = {
+ text: string;
+};
+
+export type AssetUploadResponse = {
+ uploadID: string;
+ mimeType?: string;
+ fileName?: string;
+ fileSize?: number;
+ width?: number;
+ height?: number;
+ duration?: number;
+ srcURL?: string;
+};
+
+export type CursorResponse = {
+ items: T[];
+ hasMore?: boolean;
+ newestCursor?: string | null;
+ oldestCursor?: string | null;
+ cursor?: string | null;
+ nextCursor?: string | null;
+};
+
+export type UnifiedSearchMessages = {
+ items?: BeeperDesktop.Message[];
+ chats?: Record;
+ hasMore?: boolean;
+ newestCursor?: string | null;
+ oldestCursor?: string | null;
+};
+
+export type GlobalSearchResponse = {
+ results?: {
+ chats?: BeeperDesktop.Chat[];
+ in_groups?: BeeperDesktop.Chat[];
+ messages?: UnifiedSearchMessages;
+ };
+};
+
+const normalizeCursorResponse = (result: {
+ items?: T[];
+ hasMore?: boolean;
+ newestCursor?: string | null;
+ oldestCursor?: string | null;
+ cursor?: string | null;
+ nextCursor?: string | null;
+}): CursorResponse => ({
+ items: result.items ?? [],
+ hasMore: result.hasMore,
+ newestCursor: result.newestCursor,
+ oldestCursor: result.oldestCursor,
+ cursor: result.cursor,
+ nextCursor: result.nextCursor,
+});
+
+// Asset helpers
+const getAccessTokenValue = () => getAccessToken().token;
+const getAuthHeaders = () => ({ Authorization: `Bearer ${getAccessTokenValue()}` });
+
+const requestJSON = async (input: RequestInfo | URL, init?: RequestInit): Promise => {
+ const response = await fetch(input, init);
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(text || response.statusText);
+ }
+ return (await response.json()) as T;
+};
+
+export const uploadAssetFromFile = async (filePath: string): Promise => {
+ const body = new FormData();
+ const fileName = basename(filePath);
+ const buffer = await readFile(filePath);
+ body.append("file", new File([buffer], fileName));
+ return requestJSON(`${getBaseURL()}/v1/assets/upload`, {
+ method: "POST",
+ headers: getAuthHeaders(),
+ body,
+ });
+};
+
+export const uploadAssetFromBase64 = async (params: {
+ content: string;
+ fileName?: string;
+ mimeType?: string;
+}): Promise => {
+ return requestJSON(`${getBaseURL()}/v1/assets/upload/base64`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ ...getAuthHeaders(),
+ },
+ body: JSON.stringify({
+ content: params.content,
+ fileName: params.fileName,
+ mimeType: params.mimeType,
+ }),
+ });
+};
+
+export const downloadAsset = async (url: string): Promise<{ srcURL: string }> => {
+ return requestJSON<{ srcURL: string }>(`${getBaseURL()}/v1/assets/download`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ ...getAuthHeaders(),
+ },
+ body: JSON.stringify({ url }),
+ });
+};
+
+export const resolveFilePathFromSrcURL = (srcURL?: string) => {
+ if (!srcURL) return undefined;
+ if (srcURL.startsWith("file://")) {
+ return fileURLToPath(srcURL);
+ }
+ return undefined;
+};
+
+export const getServeAssetURL = (url: string) => {
+ const encoded = encodeURIComponent(url);
+ return `${getBaseURL()}/v1/assets/serve?url=${encoded}`;
+};
+
+// Chat & Account API functions
+
+export const listAccounts = async (): Promise => {
+ const response = await getBeeperDesktop().get("/v1/accounts");
+ if (Array.isArray(response)) {
+ return response as BeeperDesktop.Account[];
+ }
+ if (response?.items && Array.isArray(response.items)) {
+ return response.items as BeeperDesktop.Account[];
+ }
+ return [];
+};
+
+export const searchContacts = async (accountID: string, query: string) => {
+ const response = await getBeeperDesktop().get(`/v1/accounts/${encodeURIComponent(accountID)}/contacts`, {
+ query: { query },
+ });
+ return response?.items && Array.isArray(response.items) ? (response.items as BeeperDesktop.User[]) : [];
+};
+
+export const listChats = async (params?: {
+ accountIDs?: string[];
+ cursor?: string | null;
+ direction?: "after" | "before";
+}): Promise> => {
+ const response = await getBeeperDesktop().get("/v1/chats", { query: params });
+ return normalizeCursorResponse(response);
+};
+
+export const searchChats = async (params: {
+ accountIDs?: string[];
+ cursor?: string | null;
+ direction?: "after" | "before";
+ inbox?: "primary" | "low-priority" | "archive";
+ includeMuted?: boolean;
+ lastActivityAfter?: string;
+ lastActivityBefore?: string;
+ participantQuery?: string;
+ query?: string;
+ type?: "single" | "group" | "channel" | "any";
+ unreadOnly?: boolean;
+}) => {
+ const response = await getBeeperDesktop().get("/v1/chats/search", { query: params });
+ return normalizeCursorResponse(response);
+};
+
+export const createChat = async (body: {
+ accountID: string;
+ participantIDs: string[];
+ type: "single" | "group";
+ title?: string;
+ messageText?: string;
+}) => {
+ return getBeeperDesktop().post("/v1/chats", { body });
+};
+
+export const retrieveChat = async (chatID: string, options?: { maxParticipantCount?: number | null }) => {
+ return getBeeperDesktop().get(`/v1/chats/${encodeURIComponent(chatID)}`, {
+ query: options,
+ });
+};
+
+export const archiveChat = async (chatID: string, archived?: boolean) => {
+ return getBeeperDesktop().post(`/v1/chats/${encodeURIComponent(chatID)}/archive`, {
+ body: { archived },
+ });
+};
+
+export const createChatReminder = async (
+ chatID: string,
+ reminder: { remindAtMs: number; dismissOnIncomingMessage?: boolean },
+) => {
+ return getBeeperDesktop().post(`/v1/chats/${encodeURIComponent(chatID)}/reminders`, {
+ body: reminder,
+ });
+};
+
+export const deleteChatReminder = async (chatID: string) => {
+ return getBeeperDesktop().delete(`/v1/chats/${encodeURIComponent(chatID)}/reminders`);
+};
+
+export const listChatMessages = async (
+ chatID: string,
+ params?: { cursor?: string | null; direction?: "after" | "before"; limit?: number },
+): Promise> => {
+ const response = await getBeeperDesktop().get(`/v1/chats/${encodeURIComponent(chatID)}/messages`, {
+ query: params,
+ });
+ return normalizeCursorResponse(response);
+};
+
+export const searchMessages = async (params: {
+ query?: string;
+ chatIDs?: string[];
+ sender?: "me" | "others" | string;
+ accountIDs?: string[];
+ chatType?: "group" | "single";
+ includeMuted?: boolean;
+ excludeLowPriority?: boolean | null;
+ mediaTypes?: Array<"any" | "video" | "image" | "link" | "file">;
+ dateAfter?: string;
+ dateBefore?: string;
+ cursor?: string | null;
+ direction?: "after" | "before";
+ limit?: number;
+}): Promise> => {
+ const response = await getBeeperDesktop().get("/v1/messages/search", { query: params });
+ return normalizeCursorResponse(response);
+};
+
+export const searchAll = async (params: { query: string }): Promise => {
+ return getBeeperDesktop().get("/v1/search", { query: params });
+};
+
+export const sendMessage = async (
+ chatID: string,
+ message: { text?: string; replyToMessageID?: string; attachment?: MessageAttachmentInput },
+) => {
+ return getBeeperDesktop().post(`/v1/chats/${encodeURIComponent(chatID)}/messages`, { body: message });
+};
+
+export const updateMessage = async (chatID: string, messageID: string, update: MessageEditInput) => {
+ return getBeeperDesktop().put(`/v1/chats/${encodeURIComponent(chatID)}/messages/${encodeURIComponent(messageID)}`, {
+ body: update,
+ });
+};
+
+export const downloadMessageAttachments = async (params: {
+ chatID: string;
+ messageID: string;
+ url?: string;
+}): Promise<{ success: boolean; filePath?: string; error?: string }> => {
+ if (params.url) {
+ try {
+ const response = await downloadAsset(params.url);
+ const filePath = resolveFilePathFromSrcURL(response.srcURL);
+ if (!filePath) {
+ return { success: false, error: "Downloaded asset did not return a local file path" };
+ }
+ return { success: true, filePath };
+ } catch (error) {
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
+ }
+ }
+ throw new Error("Attachment download not supported by this SDK version");
+};
diff --git a/src/chat.tsx b/src/chat.tsx
new file mode 100644
index 0000000..a5598c2
--- /dev/null
+++ b/src/chat.tsx
@@ -0,0 +1,1438 @@
+import {
+ Action,
+ ActionPanel,
+ Alert,
+ Color,
+ Detail,
+ Form,
+ Icon,
+ Keyboard,
+ List,
+ Toast,
+ confirmAlert,
+ useNavigation,
+ showHUD,
+ showToast,
+} from "@raycast/api";
+import { useCachedState, useFrecencySorting, useForm, useLocalStorage, withAccessToken } from "@raycast/utils";
+import BeeperDesktop from "@beeper/desktop-api";
+import Fuse from "fuse.js";
+import { useEffect, useMemo, useRef, useState } from "react";
+import {
+ archiveChat,
+ createBeeperOAuth,
+ createChatReminder,
+ deleteChatReminder,
+ focusApp,
+ listChatMessages,
+ retrieveChat,
+ searchChats,
+ sendMessage,
+ updateMessage,
+ getRaycastFocusLink,
+ useBeeperDesktop,
+} from "./api";
+import { formatReactionsShort, formatReactionsDetailed } from "./reactions";
+import { parseDate, getMessageID } from "./utils";
+
+export type InboxFilter = "all" | "inbox" | "low-priority" | "archive";
+export type ChatTypeFilter = "any" | "single" | "group";
+
+/** Maps the UI inbox filter value to the API's inbox parameter. */
+export const toApiInbox = (inbox: InboxFilter): "primary" | "low-priority" | "archive" | undefined =>
+ inbox === "all" ? undefined : inbox === "inbox" ? "primary" : inbox;
+
+export interface ChatFilters {
+ inbox: InboxFilter;
+ type: ChatTypeFilter;
+ unreadOnly: boolean;
+ includeMuted: boolean;
+}
+
+const recentDefaultFilters: ChatFilters = {
+ inbox: "inbox",
+ type: "any",
+ unreadOnly: false,
+ includeMuted: true,
+};
+
+const getErrorMessage = (error: unknown) => (error instanceof Error ? error.message : String(error));
+
+/** Build a preview string for a message, considering text, attachments, and reactions. */
+const getMessagePreview = (message: BeeperDesktop.Message): string => {
+ const text = message.text?.trim();
+ if (text && text.length > 0) return text;
+ const reactions = formatReactionsShort(message.reactions);
+ if (reactions) return `Reacted ${reactions}`;
+ if (message.attachments && message.attachments.length > 0) return "Attachment";
+ return "Message";
+};
+
+export type ChatInbox = "inbox" | "low-priority" | "archive";
+export type IndexedChat = { chat: BeeperDesktop.Chat; inbox: ChatInbox; searchFields: ChatSearchFields };
+export type ChatIndexState = {
+ items: IndexedChat[];
+ cursors: Record;
+ updatedAt: number;
+};
+
+const INDEXED_INBOXES: ChatInbox[] = ["inbox", "low-priority", "archive"];
+const MAX_INDEXD_CHATS_TARGET = 3000;
+const INDEX_PAGE_LIMIT = 50;
+const INDEX_MAX_PAGES = 10;
+const INDEX_MAX_PAGES_INCREMENTAL = 2;
+const INDEX_REFRESH_MAX_AGE_MS = 6 * 60 * 60 * 1000;
+const INDEX_INCREMENTAL_MIN_AGE_MS = 30 * 1000;
+const MAX_PARTICIPANTS_INDEXED = 10;
+const MAX_PARTICIPANTS_STORED = 0;
+const MAX_INDEXED_CHATS_PER_INBOX = Math.ceil(MAX_INDEXD_CHATS_TARGET / INDEXED_INBOXES.length);
+
+const emptyCursors: ChatIndexState["cursors"] = {
+ inbox: { newestCursor: null, oldestCursor: null },
+ "low-priority": { newestCursor: null, oldestCursor: null },
+ archive: { newestCursor: null, oldestCursor: null },
+};
+
+export const defaultIndexState: ChatIndexState = {
+ items: [],
+ cursors: emptyCursors,
+ updatedAt: 0,
+};
+
+const getChatTimestamp = (chat: BeeperDesktop.Chat) => {
+ if (!chat.lastActivity) return 0;
+ const ts = Date.parse(chat.lastActivity);
+ return Number.isNaN(ts) ? 0 : ts;
+};
+
+const sortIndexedChatsByActivity = (items: IndexedChat[]) =>
+ [...items].sort((a, b) => getChatTimestamp(b.chat) - getChatTimestamp(a.chat));
+
+const sortChatsByActivity = (items: BeeperDesktop.Chat[]) =>
+ [...items].sort((a, b) => getChatTimestamp(b) - getChatTimestamp(a));
+
+const mergeIndexedChats = (base: IndexedChat[], updates: IndexedChat[]) => {
+ const map = new Map();
+ for (const item of base) {
+ map.set(item.chat.id, item);
+ }
+ for (const item of updates) {
+ map.set(item.chat.id, item);
+ }
+ return Array.from(map.values());
+};
+
+const indexItemsChanged = (before: IndexedChat[], after: IndexedChat[]): boolean => {
+ if (before.length !== after.length) return true;
+ const beforeMap = new Map(before.map((i) => [i.chat.id, i]));
+ for (const item of after) {
+ const prev = beforeMap.get(item.chat.id);
+ if (!prev) return true;
+ if (
+ prev.chat.lastActivity !== item.chat.lastActivity ||
+ prev.chat.unreadCount !== item.chat.unreadCount ||
+ prev.chat.title !== item.chat.title ||
+ prev.chat.isPinned !== item.chat.isPinned ||
+ prev.chat.isMuted !== item.chat.isMuted ||
+ prev.chat.isArchived !== item.chat.isArchived ||
+ prev.inbox !== item.inbox
+ )
+ return true;
+ }
+ return false;
+};
+
+export const summarizeChatForIndex = (chat: BeeperDesktop.Chat): BeeperDesktop.Chat => ({
+ id: chat.id,
+ accountID: chat.accountID,
+ network: chat.network ?? "",
+ participants: {
+ hasMore: false,
+ items:
+ MAX_PARTICIPANTS_STORED > 0 ? (chat.participants?.items ?? []).slice(0, MAX_PARTICIPANTS_STORED) : [],
+ total: chat.participants?.total ?? 0,
+ },
+ type: chat.type,
+ unreadCount: chat.unreadCount ?? 0,
+ description: chat.description ?? undefined,
+ isArchived: chat.isArchived ?? false,
+ isMuted: chat.isMuted ?? false,
+ isPinned: chat.isPinned ?? false,
+ lastActivity: chat.lastActivity ?? undefined,
+ lastReadMessageSortKey: chat.lastReadMessageSortKey ?? undefined,
+ localChatID: chat.localChatID ?? null,
+ title: chat.title ?? null,
+});
+
+const normalizeIndexState = (state: ChatIndexState): ChatIndexState => {
+ let changed = false;
+ let items = state.items.map((item) => {
+ if (item.searchFields) return item;
+ changed = true;
+ return { ...item, searchFields: buildSearchFields(item.chat) };
+ });
+ if (items.length > MAX_INDEXD_CHATS_TARGET) {
+ items = sortIndexedChatsByActivity(items).slice(0, MAX_INDEXD_CHATS_TARGET);
+ changed = true;
+ }
+ return changed ? { ...state, items } : state;
+};
+
+const STOP_WORDS = new Set(["and"]);
+
+const normalizeSearchValue = (value: string) =>
+ value
+ .normalize("NFD")
+ .replace(/[\u0300-\u036f]/g, "")
+ .toLowerCase()
+ .replace(/[|!'^=]/g, " ")
+ .replace(/[,;!?(){}[\]"'`~#$%^&*+=<>\\]/g, " ")
+ .replace(/\s+/g, " ")
+ .trim();
+
+const parseSearchTerms = (query: string) =>
+ normalizeSearchValue(query)
+ .split(" ")
+ .map((term) => term.trim())
+ .filter((term) => term.length > 0);
+
+type ChatSearchFields = {
+ title: string;
+ network: string;
+ participants: string[];
+};
+
+interface PropertyScore {
+ minScore: number;
+ hits: number;
+}
+
+interface SearchableScore {
+ title: PropertyScore;
+ network: PropertyScore;
+ participants: PropertyScore;
+}
+
+interface SearchIndexResult {
+ id: string;
+ score: SearchableScore;
+ matchSearchTerms: Set;
+}
+
+interface SearchIndexItem {
+ id: string;
+ searchFields: ChatSearchFields;
+}
+
+interface SearchableMatch {
+ id: string;
+ title: number[];
+ network: number[];
+ participants: number[];
+ matchedSearchTerms: Set;
+}
+
+const computeScore = (scores: number[]): PropertyScore => {
+ if (!scores || scores.length === 0) {
+ return { minScore: Number.MAX_SAFE_INTEGER, hits: 0 };
+ }
+ return { minScore: Math.min(...scores), hits: scores.length };
+};
+
+const computeScoreFromMatch = (match: SearchableMatch): SearchableScore => ({
+ title: computeScore(match.title),
+ network: computeScore(match.network),
+ participants: computeScore(match.participants),
+});
+
+type SearchProperty = keyof ChatSearchFields;
+
+class ThreadSearchIndex {
+ private fuse: Fuse;
+
+ constructor(collection: SearchIndexItem[]) {
+ this.fuse = new Fuse(collection, {
+ ignoreDiacritics: true,
+ includeScore: true,
+ ignoreLocation: true,
+ ignoreFieldNorm: true,
+ threshold: 0,
+ keys: ["searchFields.title", "searchFields.network", "searchFields.participants"],
+ });
+ }
+
+ search(query: string, properties: SearchProperty[]): SearchIndexResult[] {
+ if (!query?.trim()) return [];
+ const searchTerms = parseSearchTerms(query);
+ const matches = new Map();
+
+ const getOrCreateMatch = (id: string): SearchableMatch => {
+ const existing = matches.get(id);
+ if (existing) return existing;
+ const fresh: SearchableMatch = {
+ id,
+ title: [],
+ network: [],
+ participants: [],
+ matchedSearchTerms: new Set(),
+ };
+ matches.set(id, fresh);
+ return fresh;
+ };
+
+ for (const searchTerm of searchTerms) {
+ for (const property of properties) {
+ const results = this.fuse.search({ [`searchFields.${property}`]: searchTerm } as Fuse.Expression);
+ for (const result of results) {
+ if (result.score == null) continue;
+ const match = getOrCreateMatch(result.item.id);
+ match[property].push(result.score);
+ match.matchedSearchTerms.add(searchTerm);
+ }
+ }
+ }
+
+ const requiredTerms = searchTerms.filter((term) => !STOP_WORDS.has(term));
+ const resp: SearchIndexResult[] = [];
+ for (const match of matches.values()) {
+ if (requiredTerms.length > 0 && !requiredTerms.every((term) => match.matchedSearchTerms.has(term))) {
+ continue;
+ }
+ resp.push({
+ id: match.id,
+ score: computeScoreFromMatch(match),
+ matchSearchTerms: match.matchedSearchTerms,
+ });
+ }
+ return resp;
+ }
+}
+
+export const buildSearchFields = (chat: BeeperDesktop.Chat): ChatSearchFields => {
+ const title = normalizeSearchValue(chat.title || "");
+ const network = normalizeSearchValue(chat.network || "");
+ const participants =
+ chat.participants?.items
+ ?.slice(0, MAX_PARTICIPANTS_INDEXED)
+ .map((participant) => {
+ const values = [
+ participant.fullName,
+ participant.username,
+ participant.email,
+ participant.phoneNumber ? String(participant.phoneNumber).replace(/\D/g, "") : undefined,
+ ]
+ .filter(Boolean)
+ .map((value) => normalizeSearchValue(String(value)));
+ return values.join(" ").trim();
+ })
+ .filter(Boolean) ?? [];
+
+ return { title, network, participants };
+};
+
+type ChatListViewProps = {
+ stateKey: string;
+ navigationTitle: string;
+ searchPlaceholder: string;
+ defaultFilters: ChatFilters;
+ showSmartSections?: boolean;
+ showUnreadSection?: boolean;
+ showPinnedSection?: boolean;
+};
+
+export function ChatListView({
+ stateKey,
+ navigationTitle,
+ searchPlaceholder,
+ defaultFilters,
+ showSmartSections = false,
+ showUnreadSection = true,
+ showPinnedSection = true,
+}: ChatListViewProps) {
+ const [searchText, setSearchText] = useState("");
+ const [filters, setFilters] = useCachedState(`${stateKey}:filters`, defaultFilters);
+ const [isShowingDetail, setIsShowingDetail] = useCachedState(`${stateKey}:showing-detail`, false);
+ const { value: recentChatIDs = [], setValue: setRecentChatIDs } = useLocalStorage(
+ `${stateKey}:recent-ids`,
+ [],
+ );
+ const { setValue: setLastChatID } = useLocalStorage(`${stateKey}:last-id`, null);
+ const { value: indexStateRaw = defaultIndexState, setValue: setIndexState, isLoading: isIndexLoading } =
+ useLocalStorage(`${stateKey}:index:v2`, defaultIndexState);
+
+ const trimmedQuery = searchText.trim();
+ const normalizedType = (filters.type as string) === "channel" ? "any" : filters.type;
+ const indexState = useMemo(() => normalizeIndexState(indexStateRaw ?? defaultIndexState), [indexStateRaw]);
+ const indexRef = useRef(indexState);
+ const refreshInFlight = useRef(false);
+ const initialRefreshDone = useRef(false);
+ const [isIndexRefreshing, setIsIndexRefreshing] = useState(false);
+ const [error, setError] = useState(undefined);
+
+ useEffect(() => {
+ indexRef.current = indexState;
+ }, [indexState]);
+
+ const fetchInbox = async (
+ inbox: ChatInbox | undefined,
+ mode: "full" | "incremental",
+ cursors: ChatIndexState["cursors"][ChatInbox],
+ onPage?: (payload: {
+ items: IndexedChat[];
+ newestCursor?: string | null;
+ oldestCursor?: string | null;
+ done: boolean;
+ }) => void | Promise,
+ ) => {
+ const maxPages = mode === "incremental" ? INDEX_MAX_PAGES_INCREMENTAL : INDEX_MAX_PAGES;
+ const items: IndexedChat[] = [];
+ let itemsCount = 0;
+ let cursor: string | null | undefined = undefined;
+ let newestCursor = cursors?.newestCursor ?? null;
+ let oldestCursor = cursors?.oldestCursor ?? null;
+
+ for (let page = 0; page < maxPages; page += 1) {
+ const response = await searchChats({
+ inbox: inbox ? toApiInbox(inbox) : undefined,
+ includeMuted: true,
+ type: "any",
+ limit: INDEX_PAGE_LIMIT,
+ cursor: cursor ?? undefined,
+ direction: "before",
+ });
+ const pageItems = response.items ?? [];
+ if (page === 0 && response.newestCursor) {
+ newestCursor = response.newestCursor;
+ }
+ if (response.oldestCursor) {
+ oldestCursor = response.oldestCursor;
+ }
+ if (pageItems.length === 0) break;
+ const mapped = pageItems.map((chat) => ({
+ chat: summarizeChatForIndex(chat),
+ inbox: inbox ?? (chat.isArchived ? "archive" as ChatInbox : "inbox" as ChatInbox),
+ searchFields: buildSearchFields(chat),
+ }));
+ itemsCount += mapped.length;
+ if (!onPage) {
+ items.push(...mapped);
+ }
+
+ const reachedLimit = itemsCount >= MAX_INDEXED_CHATS_PER_INBOX;
+ const done = reachedLimit || mode === "incremental" || !response.hasMore;
+ if (onPage) {
+ await onPage({
+ items: mapped,
+ newestCursor,
+ oldestCursor,
+ done,
+ });
+ }
+
+ if (done) break;
+ const nextCursor = response.oldestCursor ?? response.nextCursor ?? response.cursor;
+ if (!nextCursor) break;
+ cursor = nextCursor;
+ }
+
+ return { items, newestCursor, oldestCursor };
+ };
+
+ const refreshIndex = async (mode: "full" | "incremental" = "incremental") => {
+ if (refreshInFlight.current) return;
+ refreshInFlight.current = true;
+ const showRefreshState = mode === "full" || indexRef.current.items.length === 0;
+ if (showRefreshState) setIsIndexRefreshing(true);
+ setError(undefined);
+
+ const base = mode === "full" ? defaultIndexState : (indexRef.current ?? defaultIndexState);
+ const nextState: ChatIndexState = {
+ items: mode === "full" ? [] : [...base.items],
+ cursors: {
+ inbox: { ...base.cursors.inbox },
+ "low-priority": { ...base.cursors["low-priority"] },
+ archive: { ...base.cursors.archive },
+ },
+ updatedAt: base.updatedAt,
+ };
+
+ try {
+ let lastPersist = 0;
+ const persistIfNeeded = async (force = false) => {
+ const now = Date.now();
+ if (!force && now - lastPersist < 250) return;
+ lastPersist = now;
+ await setIndexState({ ...nextState, updatedAt: now });
+ };
+
+ for (const inbox of INDEXED_INBOXES) {
+ const result = await fetchInbox(inbox, mode, base.cursors[inbox], async (page) => {
+ nextState.items = mergeIndexedChats(nextState.items, page.items);
+ nextState.cursors[inbox] = {
+ newestCursor: page.newestCursor ?? nextState.cursors[inbox].newestCursor ?? null,
+ oldestCursor: page.oldestCursor ?? nextState.cursors[inbox].oldestCursor ?? null,
+ };
+ if (nextState.items.length > MAX_INDEXD_CHATS_TARGET * 2) {
+ nextState.items = sortIndexedChatsByActivity(nextState.items).slice(0, MAX_INDEXD_CHATS_TARGET);
+ }
+ if (mode === "full") await persistIfNeeded(page.done);
+ });
+ nextState.cursors[inbox] = {
+ newestCursor: result.newestCursor ?? nextState.cursors[inbox].newestCursor ?? null,
+ oldestCursor: result.oldestCursor ?? nextState.cursors[inbox].oldestCursor ?? null,
+ };
+ }
+
+ nextState.items = sortIndexedChatsByActivity(nextState.items).slice(0, MAX_INDEXD_CHATS_TARGET);
+ if (mode === "full" || indexItemsChanged(base.items, nextState.items)) {
+ nextState.updatedAt = Date.now();
+ await setIndexState(nextState);
+ }
+ } catch (err) {
+ setError(err);
+ } finally {
+ refreshInFlight.current = false;
+ if (showRefreshState) setIsIndexRefreshing(false);
+ }
+ };
+
+ useEffect(() => {
+ if (isIndexLoading) return;
+ if (initialRefreshDone.current) return;
+ initialRefreshDone.current = true;
+ const age = Date.now() - indexState.updatedAt;
+ if (indexState.items.length === 0 || age > INDEX_REFRESH_MAX_AGE_MS) {
+ void refreshIndex("full");
+ return;
+ }
+ if (age > INDEX_INCREMENTAL_MIN_AGE_MS) {
+ void refreshIndex("incremental");
+ }
+ }, [isIndexLoading, indexState.items.length]);
+
+ const tokens = useMemo(() => parseSearchTerms(trimmedQuery), [trimmedQuery]);
+ const normalizedQuery = useMemo(() => normalizeSearchValue(trimmedQuery), [trimmedQuery]);
+ const searchIndex = useMemo(() => {
+ if (tokens.length === 0) return null;
+ const collection = indexState.items.map((item) => ({ id: item.chat.id, searchFields: item.searchFields }));
+ return new ThreadSearchIndex(collection);
+ }, [indexState.items, tokens.length]);
+ const chats = useMemo(() => {
+ const filtered = indexState.items.filter((item) => {
+ if (filters.inbox !== "all" && item.inbox !== filters.inbox) return false;
+ if (!filters.includeMuted && item.chat.isMuted) return false;
+ if (filters.unreadOnly && (item.chat.unreadCount ?? 0) === 0) return false;
+ if (normalizedType !== "any" && item.chat.type !== normalizedType) return false;
+ return true;
+ });
+ if (tokens.length === 0) {
+ return sortChatsByActivity(filtered.map((item) => item.chat));
+ }
+
+ const now = Date.now();
+ const filteredById = new Map(filtered.map((item) => [item.chat.id, item]));
+ const results = searchIndex ? searchIndex.search(trimmedQuery, ["title", "network", "participants"]) : [];
+ const scored = results
+ .map((result) => {
+ const indexed = filteredById.get(result.id);
+ if (!indexed) return null;
+ const title = indexed.searchFields.title;
+ return {
+ chat: indexed.chat,
+ exactTitle: normalizedQuery.length > 0 && title === normalizedQuery,
+ prefixTitle: normalizedQuery.length > 0 && title.startsWith(normalizedQuery),
+ titleHits: result.score.title.hits,
+ participantHits: result.score.participants.hits,
+ networkHits: result.score.network.hits,
+ isSingle: indexed.chat.type === "single",
+ timestamp: getChatTimestamp(indexed.chat),
+ };
+ })
+ .filter(
+ (
+ item,
+ ): item is {
+ chat: BeeperDesktop.Chat;
+ exactTitle: boolean;
+ prefixTitle: boolean;
+ titleHits: number;
+ participantHits: number;
+ networkHits: number;
+ isSingle: boolean;
+ timestamp: number;
+ } => Boolean(item),
+ );
+
+ const recencyBoost = (timestamp: number) => Math.max(0, 30 - (now - timestamp) / (24 * 60 * 60 * 1000));
+
+ scored.sort((a, b) => {
+ if (a.exactTitle !== b.exactTitle) return a.exactTitle ? -1 : 1;
+ if (a.prefixTitle !== b.prefixTitle) return a.prefixTitle ? -1 : 1;
+
+ const aRecency = recencyBoost(a.timestamp);
+ const bRecency = recencyBoost(b.timestamp);
+ if (aRecency !== bRecency) return bRecency - aRecency;
+
+ if (a.titleHits !== b.titleHits) return b.titleHits - a.titleHits;
+ if (a.participantHits !== b.participantHits) return b.participantHits - a.participantHits;
+ if (a.isSingle !== b.isSingle) return a.isSingle ? -1 : 1;
+ if (a.networkHits !== b.networkHits) return b.networkHits - a.networkHits;
+ return b.timestamp - a.timestamp;
+ });
+
+ return scored.map((item) => item.chat);
+ }, [
+ filters.includeMuted,
+ filters.inbox,
+ filters.type,
+ filters.unreadOnly,
+ indexState.items,
+ normalizedQuery,
+ normalizedType,
+ searchIndex,
+ tokens,
+ trimmedQuery,
+ ]);
+
+ const frecencyInput = useMemo(() => [...chats], [chats]);
+ const {
+ data: frecencyChats = [],
+ visitItem,
+ resetRanking,
+ } = useFrecencySorting(frecencyInput, {
+ key: (chat) => chat.id,
+ });
+
+ const inboxDropdown = (
+ setFilters((prev) => ({ ...prev, inbox: value as InboxFilter }))}
+ >
+
+
+
+
+
+ );
+
+ const toggleFilter = (key: "unreadOnly" | "includeMuted") => setFilters((prev) => ({ ...prev, [key]: !prev[key] }));
+
+ const setChatType = (type: ChatTypeFilter) => setFilters((prev) => ({ ...prev, type }));
+
+ const isLoading = isIndexLoading || (isIndexRefreshing && indexState.items.length === 0);
+
+ const markChatVisited = (chat: BeeperDesktop.Chat) => {
+ if (!showSmartSections) return;
+ visitItem(chat);
+ void setLastChatID(chat.id);
+ const next = [chat.id, ...(recentChatIDs ?? []).filter((id) => id !== chat.id)].slice(0, 12);
+ void setRecentChatIDs(next);
+ };
+
+ const handleArchiveChat = async (chat: BeeperDesktop.Chat, archived: boolean) => {
+ const confirmed = await confirmAlert({
+ title: archived ? "Archive?" : "Unarchive?",
+ message: archived ? "This will move the chat to Archive." : "This will move the chat back to your Inbox.",
+ primaryAction: { title: archived ? "Archive" : "Unarchive", style: Alert.ActionStyle.Destructive },
+ });
+ if (!confirmed) return;
+
+ const toast = await showToast({
+ style: Toast.Style.Animated,
+ title: archived ? "Archiving chat" : "Unarchiving chat",
+ });
+ try {
+ await archiveChat(chat.id, archived);
+ toast.style = Toast.Style.Success;
+ toast.title = archived ? "Chat archived" : "Chat restored";
+ void refreshIndex("full");
+ } catch (err) {
+ toast.style = Toast.Style.Failure;
+ toast.title = "Chat update failed";
+ toast.message = getErrorMessage(err);
+ }
+ };
+
+ const clearReminder = async (chat: BeeperDesktop.Chat) => {
+ const confirmed = await confirmAlert({
+ title: "Dismiss Reminder?",
+ message: "This removes the reminder for this chat.",
+ primaryAction: { title: "Dismiss Reminder", style: Alert.ActionStyle.Destructive },
+ });
+ if (!confirmed) return;
+
+ const toast = await showToast({ style: Toast.Style.Animated, title: "Dismissing reminder" });
+ try {
+ await deleteChatReminder(chat.id);
+ toast.style = Toast.Style.Success;
+ toast.title = "Reminder dismissed";
+ } catch (err) {
+ toast.style = Toast.Style.Failure;
+ toast.title = "Failed to dismiss reminder";
+ toast.message = getErrorMessage(err);
+ }
+ };
+
+ const setQuickReminder = async (chat: BeeperDesktop.Chat, remindAt: Date, label: string) => {
+ const toast = await showToast({ style: Toast.Style.Animated, title: `Setting reminder (${label})` });
+ try {
+ await createChatReminder(chat.id, {
+ remindAtMs: remindAt.getTime(),
+ dismissOnIncomingMessage: false,
+ });
+ toast.style = Toast.Style.Success;
+ toast.title = "Reminder set";
+ } catch (err) {
+ toast.style = Toast.Style.Failure;
+ toast.title = "Reminder failed";
+ toast.message = getErrorMessage(err);
+ }
+ };
+
+ const pinnedChats = showPinnedSection ? chats.filter((chat) => chat.isPinned) : [];
+ const unreadChats = chats.filter((chat) => chat.unreadCount > 0 && !(showPinnedSection && chat.isPinned));
+
+
+ const pinnedIDs = new Set(pinnedChats.map((chat) => chat.id));
+ const unreadIDs = showUnreadSection ? new Set(unreadChats.map((chat) => chat.id)) : new Set();
+ const recentIDs = new Set(recentChatIDs ?? []);
+
+ const chatById = useMemo(() => new Map(chats.map((chat) => [chat.id, chat])), [chats]);
+ const recentChats = useMemo(
+ () =>
+ (recentChatIDs ?? [])
+ .map((id) => chatById.get(id))
+ .filter((chat): chat is BeeperDesktop.Chat => Boolean(chat))
+ .filter((chat) => !pinnedIDs.has(chat.id) && !unreadIDs.has(chat.id)),
+ [chatById, recentChatIDs, pinnedIDs, unreadIDs],
+ );
+
+ const frequentChats = useMemo(
+ () =>
+ frecencyChats
+ .filter((chat) => !pinnedIDs.has(chat.id) && !unreadIDs.has(chat.id) && !recentIDs.has(chat.id))
+ .slice(0, 8),
+ [frecencyChats, pinnedIDs, unreadIDs, recentIDs],
+ );
+
+ const showSections = showSmartSections && trimmedQuery.length === 0;
+
+ const otherChats = chats.filter(
+ (chat) =>
+ !pinnedIDs.has(chat.id) &&
+ !unreadIDs.has(chat.id) &&
+ (!showSections || (!recentIDs.has(chat.id) && !frequentChats.some((item) => item.id === chat.id))),
+ );
+ const showUnread = showUnreadSection && unreadChats.length > 0;
+
+ const renderChatItem = (chat: BeeperDesktop.Chat) => {
+ const lastActivity = parseDate(chat.lastActivity);
+ const openLink = getRaycastFocusLink({ chatID: chat.id });
+ const accessories = [
+ ...(chat.unreadCount > 0 ? [{ text: `${chat.unreadCount} unread` }] : []),
+ ...(chat.isPinned ? [{ icon: Icon.Pin }] : []),
+ ...(chat.isMuted ? [{ icon: Icon.SpeakerOff }] : []),
+ ...(chat.isArchived ? [{ icon: Icon.Archive }] : []),
+ ...(lastActivity ? [{ date: lastActivity }] : []),
+ ];
+
+ return (
+
+
+
+
+
+
+
+ {chat.isPinned && }
+ {chat.isMuted && (
+
+ )}
+ {chat.isArchived && (
+
+ )}
+
+ {chat.lastActivity && (
+
+ )}
+
+ }
+ />
+ ) : null
+ }
+ actions={
+
+
+ {
+ markChatVisited(chat);
+ return focusApp({ chatID: chat.id });
+ }}
+ />
+ }
+ onPush={() => markChatVisited(chat)}
+ />
+ }
+ onPush={() => markChatVisited(chat)}
+ />
+ setIsShowingDetail((prev) => !prev)}
+ />
+
+
+ {openLink && (
+
+ )}
+
+
+
+ setQuickReminder(chat, new Date(Date.now() + 60 * 60 * 1000), "1 hour")}
+ />
+ setQuickReminder(chat, new Date(Date.now() + 2 * 60 * 60 * 1000), "2 hours")}
+ />
+ {
+ const date = new Date();
+ date.setDate(date.getDate() + 1);
+ date.setHours(9, 0, 0, 0);
+ return setQuickReminder(chat, date, "tomorrow");
+ }}
+ />
+ {
+ const date = new Date();
+ date.setDate(date.getDate() + 7);
+ date.setHours(9, 0, 0, 0);
+ return setQuickReminder(chat, date, "next week");
+ }}
+ />
+ } />
+
+ clearReminder(chat)} />
+
+
+ } />
+ handleArchiveChat(chat, !chat.isArchived)}
+ />
+
+
+
+ {chat.title && }
+
+
+ refreshIndex("full")}
+ />
+ {showSmartSections && (
+ resetRanking()} />
+ )}
+ {showSmartSections && (
+ setRecentChatIDs([])} />
+ )}
+
+ toggleFilter("unreadOnly")}
+ />
+ toggleFilter("includeMuted")}
+ />
+ setChatType("any")} />
+ setChatType("single")} />
+ setChatType("group")} />
+
+
+
+ }
+ />
+ );
+ };
+
+ return (
+
+ {pinnedChats.length > 0 && {pinnedChats.map(renderChatItem)}}
+ {showUnread && {unreadChats.map(renderChatItem)}}
+ {showSections && recentChats.length > 0 && (
+ {recentChats.map(renderChatItem)}
+ )}
+ {showSections && frequentChats.length > 0 && (
+ {frequentChats.map(renderChatItem)}
+ )}
+ {otherChats.length > 0 && {otherChats.map(renderChatItem)}}
+ {!isLoading && chats.length === 0 && (
+
+ )}
+
+ );
+}
+
+export function ChatThread({ chat }: { chat: BeeperDesktop.Chat }) {
+ const [query, setQuery] = useState("");
+ const [messages, setMessages] = useState([]);
+ const [cursor, setCursor] = useState(null);
+ const [hasMore, setHasMore] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
+ const [error, setError] = useState();
+ const [isShowingDetail, setIsShowingDetail] = useCachedState("chat-thread:showing-detail", false);
+
+ const trimmedQuery = query.trim();
+
+ const PAGE_LIMIT = 50;
+
+ /** Derive the pagination cursor from the last message's sortKey. */
+ const getLastSortKey = (items: BeeperDesktop.Message[]): string | null => {
+ if (items.length === 0) return null;
+ return items[items.length - 1].sortKey ?? null;
+ };
+
+ const loadFirstPage = async (isCancelled?: () => boolean) => {
+ setIsLoading(true);
+ setError(undefined);
+
+ try {
+ const result = await listChatMessages(chat.id, { limit: PAGE_LIMIT });
+ if (isCancelled?.()) return;
+ const items = result.items ?? [];
+ setMessages(items);
+ setCursor(getLastSortKey(items));
+ setHasMore(Boolean(result.hasMore));
+ } catch (err) {
+ if (!isCancelled?.()) {
+ setError(getErrorMessage(err));
+ }
+ } finally {
+ if (!isCancelled?.()) {
+ setIsLoading(false);
+ }
+ }
+ };
+
+ const loadMore = async () => {
+ if (!cursor) {
+ await showHUD("No more messages to load");
+ return;
+ }
+ setIsLoadingMore(true);
+ try {
+ const nextPage = await listChatMessages(chat.id, { cursor, direction: "before", limit: PAGE_LIMIT });
+ const items = nextPage.items ?? [];
+ setMessages((prev) => [...prev, ...items]);
+ setCursor(getLastSortKey(items) ?? cursor);
+ setHasMore(Boolean(nextPage.hasMore));
+ if (items.length === 0) {
+ setHasMore(false);
+ await showHUD("No older messages found");
+ }
+ } catch (err) {
+ await showHUD(`Failed to load more: ${getErrorMessage(err)}`);
+ } finally {
+ setIsLoadingMore(false);
+ }
+ };
+
+ useEffect(() => {
+ let cancelled = false;
+ loadFirstPage(() => cancelled);
+ return () => {
+ cancelled = true;
+ };
+ }, [chat.id]);
+
+ const filteredMessages = useMemo(() => {
+ const nonEmpty = messages.filter((m) => !!(m.text?.trim()) || (m.attachments && m.attachments.length > 0));
+ if (!trimmedQuery) return nonEmpty;
+ const lower = trimmedQuery.toLowerCase();
+ return nonEmpty.filter((m) => m.text?.toLowerCase().includes(lower));
+ }, [messages, trimmedQuery]);
+
+ const showLoadMore = !isLoading && hasMore;
+
+ // Build a participant name lookup from message senders + chat participants
+ const nameMap = useMemo(() => {
+ const map = new Map();
+ for (const msg of messages) {
+ if (msg.senderName) map.set(msg.senderID, msg.senderName);
+ }
+ if (chat.participants?.items) {
+ for (const user of chat.participants.items) {
+ if (user.fullName) map.set(user.id, user.fullName);
+ }
+ }
+ return map;
+ }, [messages, chat.participants]);
+
+ return (
+
+ {filteredMessages.map((message) => {
+ const preview = getMessagePreview(message);
+ const reactionsShort = formatReactionsShort(message.reactions);
+ const reactionsDetailed = formatReactionsDetailed(message.reactions, nameMap);
+ const timestamp = parseDate(message.timestamp);
+ const sender = message.senderName || (message.isSender ? "You" : "Unknown");
+ const messageID = getMessageID(message);
+
+ return (
+
+ {reactionsDetailed && (
+
+ {reactionsDetailed.entries.map((e) => (
+
+ ))}
+
+ )}
+
+
+
+ {message.isSender && (
+
+
+
+ )}
+
+ }
+ />
+ ) : null
+ }
+ accessories={[
+ ...(reactionsShort ? [{ tag: { value: reactionsShort, color: Color.SecondaryText } }] : []),
+ ...(timestamp ? [{ date: timestamp }] : []),
+ ]}
+ actions={
+ setIsShowingDetail((prev) => !prev)}
+ isShowingDetail={isShowingDetail}
+ />
+ }
+ />
+ );
+ })}
+ {showLoadMore && (
+
+
+
+ }
+ />
+ )}
+ {!isLoading && filteredMessages.length === 0 && (
+
+ )}
+
+ );
+}
+
+function MessageActions({
+ chat,
+ message,
+ onRefresh,
+ onLoadMore,
+ onToggleDetail,
+ isShowingDetail,
+}: {
+ chat: BeeperDesktop.Chat;
+ message: BeeperDesktop.Message;
+ onRefresh: () => Promise;
+ onLoadMore: () => Promise;
+ onToggleDetail: () => void;
+ isShowingDetail: boolean;
+}) {
+ const messageID = getMessageID(message);
+ const messageLink = getRaycastFocusLink({ chatID: chat.id, messageID });
+
+ return (
+
+
+ focusApp({ chatID: chat.id, messageID: messageID })}
+ />
+ focusApp({ chatID: chat.id })} />
+ {messageLink && (
+
+ )}
+
+
+ }
+ />
+ } />
+ {message.isSender && message.text && (
+ }
+ />
+ )}
+
+
+
+ {message.text && }
+ {message.text && }
+ {message.text && (
+
+ )}
+
+
+
+ } />
+
+
+
+
+ );
+}
+
+function MessageDetail({ chat, message }: { chat: BeeperDesktop.Chat; message: BeeperDesktop.Message }) {
+ const messageID = getMessageID(message);
+ return (
+
+ );
+}
+
+export function ComposeMessageForm({
+ chat,
+ replyToMessageID,
+ initialText,
+}: {
+ chat: BeeperDesktop.Chat;
+ replyToMessageID?: string;
+ initialText?: string;
+}) {
+ const { pop } = useNavigation();
+ const {
+ value: draftText,
+ setValue: setDraftText,
+ removeValue: removeDraft,
+ } = useLocalStorage(`chat:draft:${chat.id}`, "");
+
+ const draftApplied = useRef(false);
+ const setDraftRef = useRef(setDraftText);
+ setDraftRef.current = setDraftText;
+
+ const { handleSubmit, itemProps, values, setValue } = useForm<{
+ text: string;
+ }>({
+ initialValues: {
+ text: initialText ?? draftText ?? "",
+ },
+ onSubmit: async (values) => {
+ const text = values.text?.trim();
+ if (!text) {
+ await showHUD("Add text to send");
+ return;
+ }
+
+ const toast = await showToast({ style: Toast.Style.Animated, title: "Sending message" });
+ try {
+ await sendMessage(chat.id, { text, replyToMessageID });
+ toast.style = Toast.Style.Success;
+ toast.title = "Message sent";
+ await removeDraft();
+ pop();
+ } catch (err) {
+ toast.style = Toast.Style.Failure;
+ toast.title = "Message failed";
+ toast.message = getErrorMessage(err);
+ }
+ },
+ });
+
+ // Apply saved draft to form once when localStorage value loads
+ useEffect(() => {
+ if (draftApplied.current || initialText) return;
+ if (draftText && values.text !== draftText) {
+ draftApplied.current = true;
+ setValue("text", draftText);
+ }
+ }, [draftText, initialText, setValue, values.text]);
+
+ // Persist form changes to localStorage (ref breaks the cycle)
+ useEffect(() => {
+ if (values.text !== undefined) {
+ void setDraftRef.current(values.text);
+ }
+ }, [values.text]);
+
+ return (
+
+ );
+}
+
+function EditMessageForm({ chat, message }: { chat: BeeperDesktop.Chat; message: BeeperDesktop.Message }) {
+ const { pop } = useNavigation();
+ const messageID = getMessageID(message);
+ const { handleSubmit, itemProps } = useForm<{ text: string }>({
+ initialValues: { text: message.text || "" },
+ onSubmit: async (values) => {
+ const text = values.text?.trim();
+ if (!text) {
+ await showHUD("Message can't be empty");
+ return;
+ }
+
+ const toast = await showToast({ style: Toast.Style.Animated, title: "Updating message" });
+ try {
+ await updateMessage(chat.id, messageID, { text });
+ toast.style = Toast.Style.Success;
+ toast.title = "Message updated";
+ pop();
+ } catch (err) {
+ toast.style = Toast.Style.Failure;
+ toast.title = "Update failed";
+ toast.message = getErrorMessage(err);
+ }
+ },
+ });
+
+ return (
+
+
+ );
+}
+
+export function ReminderForm({ chat }: { chat: BeeperDesktop.Chat }) {
+ const { pop } = useNavigation();
+ const { handleSubmit, itemProps } = useForm<{ remindAt: Date; dismissOnIncoming: boolean }>({
+ initialValues: {
+ remindAt: new Date(Date.now() + 60 * 60 * 1000),
+ dismissOnIncoming: false,
+ },
+ onSubmit: async (values) => {
+ const remindAtMs = values.remindAt.getTime();
+ const toast = await showToast({ style: Toast.Style.Animated, title: "Setting reminder" });
+ try {
+ await createChatReminder(chat.id, {
+ remindAtMs,
+ dismissOnIncomingMessage: values.dismissOnIncoming,
+ });
+ toast.style = Toast.Style.Success;
+ toast.title = "Reminder set";
+ pop();
+ } catch (err) {
+ toast.style = Toast.Style.Failure;
+ toast.title = "Reminder failed";
+ toast.message = getErrorMessage(err);
+ }
+ },
+ });
+
+ return (
+
+
+
+ );
+}
+
+export function ChatDetails({ chat }: { chat: BeeperDesktop.Chat }) {
+ const { data, isLoading } = useBeeperDesktop(async () => {
+ return retrieveChat(chat.id, { maxParticipantCount: 50 });
+ });
+
+ const participantLines = useMemo(() => {
+ const participants = data?.participants.items ?? [];
+ if (participants.length === 0) return "No participants available.";
+ return participants
+ .map((participant) => {
+ const name = participant.fullName || participant.username || participant.id;
+ return `- ${name}`;
+ })
+ .join("\n");
+ }, [data?.participants.items]);
+
+ return (
+
+ );
+}
+
+function RecentChatsCommand() {
+ return (
+
+ );
+}
+
+export default withAccessToken(createBeeperOAuth())(RecentChatsCommand);
diff --git a/src/components/ChatListItem.tsx b/src/components/ChatListItem.tsx
new file mode 100644
index 0000000..068b2b1
--- /dev/null
+++ b/src/components/ChatListItem.tsx
@@ -0,0 +1,45 @@
+import { List, ActionPanel, Action, Icon } from "@raycast/api";
+import type { ReactNode } from "react";
+import { Translations } from "../locales/en";
+import { getChatIcon } from "../utils/chatIcon";
+
+interface Chat {
+ id: string;
+ network: string;
+ type?: string;
+ participants?: {
+ items?: Array<{ isSelf?: boolean; imgURL?: string }>;
+ };
+ title?: string;
+ avatarUrl?: string;
+ onOpen?: () => void;
+ detailsTarget?: ReactNode;
+}
+
+interface ChatListItemProps {
+ chat: Chat;
+ translations: Translations;
+ accessories?: List.Item.Props["accessories"];
+ showDetails?: boolean;
+}
+
+export function ChatListItem({ chat, translations, accessories = [], showDetails = false }: ChatListItemProps) {
+ return (
+
+ chat.onOpen?.()} />
+ {showDetails && chat.detailsTarget && (
+
+ )}
+
+
+ }
+ />
+ );
+}
diff --git a/src/contacts.tsx b/src/contacts.tsx
new file mode 100644
index 0000000..9c3c72e
--- /dev/null
+++ b/src/contacts.tsx
@@ -0,0 +1,272 @@
+import { Action, ActionPanel, Icon, List, Toast, showToast, useNavigation } from "@raycast/api";
+import { useCachedPromise, withAccessToken } from "@raycast/utils";
+import { useEffect, useMemo, useRef, useState } from "react";
+import {
+ createBeeperOAuth,
+ createChat,
+ focusApp,
+ listAccounts,
+ retrieveChat,
+ searchChats,
+ searchContacts,
+} from "./api";
+import { ChatThread } from "./chat";
+import { t } from "./locales";
+import { getBeeperAppPath } from "./utils";
+
+const getContactSortName = (contact: {
+ fullName?: string;
+ username?: string;
+ phoneNumber?: string;
+ email?: string;
+ id: string;
+}) => (contact.fullName || contact.username || contact.phoneNumber || contact.email || contact.id || "").toLowerCase();
+
+const sortContacts = <
+ T extends { fullName?: string; username?: string; phoneNumber?: string; email?: string; id: string },
+>(
+ items: T[],
+) =>
+ [...items].sort((a, b) => {
+ const aName = getContactSortName(a);
+ const bName = getContactSortName(b);
+ const aEmpty = !aName;
+ const bEmpty = !bName;
+ if (aEmpty && !bEmpty) return 1;
+ if (!aEmpty && bEmpty) return -1;
+ return aName.localeCompare(bName);
+ });
+
+export function ContactsView() {
+ const translations = t();
+ const c = translations.commands.contacts;
+
+ const [query, setQuery] = useState("");
+ const { data: accounts = [], isLoading: isLoadingAccounts } = useCachedPromise(listAccounts, [], {
+ keepPreviousData: true,
+ });
+ const [accountFilter, setAccountFilter] = useState("all");
+ const { push } = useNavigation();
+ const beeperAppPath = getBeeperAppPath();
+
+ useEffect(() => {
+ if (accountFilter === "all" && accounts.length === 1) {
+ setAccountFilter(accounts[0].accountID);
+ }
+ }, [accountFilter, accounts]);
+
+ const shouldSearch = query.trim().length > 0 && accounts.length > 0;
+ const accountMap = useMemo(() => new Map(accounts.map((account) => [account.accountID, account])), [accounts]);
+ const accountsKey = useMemo(() => accounts.map((account) => account.accountID).join("|"), [accounts]);
+ const lastPartialErrorKey = useRef(null);
+
+ const {
+ data: contacts = [],
+ isLoading,
+ error,
+ } = useCachedPromise(
+ async (termInput: string, filter: string, accountsSnapshotKey: string) => {
+ if (!shouldSearch) return [];
+ const term = termInput.trim();
+ if (!term) return [];
+
+ if (filter === "all") {
+ const results = await Promise.allSettled(
+ accounts.map(async (account) => {
+ const items = await searchContacts(account.accountID, term);
+ return items.map((contact) => ({ ...contact, accountID: account.accountID }));
+ }),
+ );
+
+ const fulfilled = results.flatMap((result) => (result.status === "fulfilled" ? result.value : []));
+ const rejected = results.filter((result) => result.status === "rejected");
+
+ if (rejected.length === results.length && results.length > 0) {
+ const reason = rejected[0].reason;
+ throw reason instanceof Error ? reason : new Error(String(reason));
+ }
+
+ if (rejected.length > 0) {
+ const toastKey = `${term}-${rejected.length}-${accountsSnapshotKey}`;
+ if (lastPartialErrorKey.current !== toastKey) {
+ lastPartialErrorKey.current = toastKey;
+ await showToast({
+ style: Toast.Style.Failure,
+ title: c.partialErrorTitle,
+ message: c.partialErrorMessage,
+ });
+ }
+ }
+
+ return sortContacts(fulfilled);
+ }
+
+ const items = await searchContacts(filter, term);
+ return sortContacts(items.map((contact) => ({ ...contact, accountID: filter })));
+ },
+ [query, accountFilter, accountsKey],
+ { keepPreviousData: true },
+ );
+
+ const dropdown = (
+ setAccountFilter(value)}
+ isLoading={isLoadingAccounts}
+ >
+
+ {accounts.map((account) => (
+
+ ))}
+
+ );
+
+ const emptyView = (() => {
+ if (error && shouldSearch && !isLoading) {
+ return (
+
+
+
+ ) : null
+ }
+ />
+ );
+ }
+ if (!isLoading && contacts.length === 0) {
+ return (
+
+ );
+ }
+ return null;
+ })();
+
+ return (
+
+ {contacts.map((contact) => {
+ const account = accountMap.get((contact as { accountID?: string }).accountID || "");
+ const title = contact.fullName || contact.username || contact.id;
+ const subtitle = contact.username && contact.fullName ? contact.username : contact.email || contact.phoneNumber;
+ const accountLabel = account
+ ? `${account.network || c.accountFallback} • ${
+ account.user?.fullName ||
+ account.user?.username ||
+ account.user?.email ||
+ account.user?.phoneNumber ||
+ account.accountID
+ }`
+ : undefined;
+ return (
+
+ {
+ const selectedAccountID = (contact as { accountID?: string }).accountID;
+ if (!selectedAccountID) return;
+ const toast = await showToast({ style: Toast.Style.Animated, title: c.openingChatToast });
+ try {
+ const contactName = contact.fullName || contact.username || contact.id;
+ const existing = await searchChats({
+ accountIDs: [selectedAccountID],
+ type: "single",
+ participantQuery: contactName,
+ includeMuted: true,
+ });
+
+ let matchedChat: typeof existing.items[0] | undefined;
+ for (const candidate of existing.items ?? []) {
+ const full = await retrieveChat(candidate.id, { maxParticipantCount: 10 });
+ const hasContact = full.participants?.items?.some(
+ (p) =>
+ p.id === contact.id ||
+ (contact.username && p.username && p.username === contact.username),
+ );
+ if (hasContact) {
+ matchedChat = full;
+ break;
+ }
+ }
+
+ if (matchedChat) {
+ toast.style = Toast.Style.Success;
+ toast.title = c.chatFoundToast;
+ push();
+ } else {
+ toast.title = c.creatingChatToast;
+ const response = await createChat({
+ accountID: selectedAccountID,
+ participantIDs: [contact.id],
+ type: "single",
+ });
+ toast.style = Toast.Style.Success;
+ toast.title = c.chatCreatedToast;
+ const newChatID =
+ (response as { chatID?: string }).chatID || (response as { id?: string }).id || undefined;
+ if (newChatID) {
+ try {
+ const chat = await retrieveChat(newChatID, { maxParticipantCount: 0 });
+ push();
+ } catch {
+ // fallback: keep the list visible if chat load fails
+ }
+ }
+ }
+ } catch (error) {
+ toast.style = Toast.Style.Failure;
+ toast.title = c.openChatFailedToast;
+ toast.message = error instanceof Error ? error.message : translations.common.unknownError;
+ }
+ }}
+ />
+ focusApp()} />
+
+
+ }
+ />
+ );
+ })}
+ {emptyView}
+
+ );
+}
+
+function ContactsCommand() {
+ return ;
+}
+
+export default withAccessToken(createBeeperOAuth())(ContactsCommand);
diff --git a/src/find-chat.tsx b/src/find-chat.tsx
deleted file mode 100644
index c2a368e..0000000
--- a/src/find-chat.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { ActionPanel, Action, List, Icon } from "@raycast/api";
-import { withAccessToken } from "@raycast/utils";
-import { useState } from "react";
-import { useBeeperDesktop, createBeeperOAuth, focusApp } from "./api";
-
-function FindChatCommand() {
- const [searchText, setSearchText] = useState("");
- const { data: chats = [], isLoading } = useBeeperDesktop(async (client) => {
- const result = await client.chats.search({ query: searchText });
- return result.data;
- });
-
- return (
-
- {chats.map((chat) => (
- 0 ? [{ text: `${chat.unreadCount} unread` }] : []),
- ...(chat.isPinned ? [{ icon: Icon.Pin }] : []),
- ...(chat.isMuted ? [{ icon: Icon.SpeakerOff }] : []),
- ]}
- actions={
-
- focusApp({ chatID: chat.chatID })}
- />
-
-
- }
- />
- ))}
- {!isLoading && chats.length === 0 && (
-
- )}
-
- );
-}
-
-export default withAccessToken(createBeeperOAuth())(FindChatCommand);
diff --git a/src/focus-app.tsx b/src/focus-app.tsx
index 4dba9b0..23e1ba3 100644
--- a/src/focus-app.tsx
+++ b/src/focus-app.tsx
@@ -1,8 +1,16 @@
+import { LaunchProps } from "@raycast/api";
import { withAccessToken } from "@raycast/utils";
import { focusApp, createBeeperOAuth } from "./api";
-async function FocusAppCommand() {
- await focusApp();
+type FocusAppArguments = {
+ chatID?: string;
+ messageID?: string;
+ draftText?: string;
+ draftAttachmentPath?: string;
+};
+
+async function FocusAppCommand(props: LaunchProps<{ arguments?: FocusAppArguments }>) {
+ await focusApp(props.arguments ?? {});
}
export default withAccessToken(createBeeperOAuth())(FocusAppCommand);
diff --git a/src/hooks/useChatSearch.ts b/src/hooks/useChatSearch.ts
new file mode 100644
index 0000000..c9dc971
--- /dev/null
+++ b/src/hooks/useChatSearch.ts
@@ -0,0 +1,36 @@
+import { useBeeperDesktop } from "../api";
+
+export function useChatSearch(searchText: string, includeEmpty = false) {
+ return useBeeperDesktop(
+ async (client, query, includeEmptyResults) => {
+ if (!query && !includeEmptyResults) return [];
+
+ const allChats = [];
+ let cursor: string | null = null;
+ let hasMore = true;
+ const MAX_PAGES = 10; // Safety limit: max 10 pages * 100 = 1000 results
+ let pageCount = 0;
+
+ // For search, load more results initially (100 per batch)
+ // since users expect comprehensive results when searching
+ while (hasMore && pageCount < MAX_PAGES) {
+ const params = query
+ ? { query, limit: 100, ...(cursor ? { cursor, direction: "older" as const } : {}) }
+ : { limit: 100, ...(cursor ? { cursor, direction: "older" as const } : {}) };
+
+ const page = await client.chats.search(params);
+ allChats.push(...page.items);
+
+ cursor = page.oldestCursor;
+ hasMore = page.hasMore;
+ pageCount++;
+
+ // Early exit if we have enough results
+ if (allChats.length >= 200) break;
+ }
+
+ return allChats;
+ },
+ [searchText, includeEmpty] as [string, boolean],
+ );
+}
diff --git a/src/hooks/useMessageSearch.ts b/src/hooks/useMessageSearch.ts
new file mode 100644
index 0000000..a6a4e26
--- /dev/null
+++ b/src/hooks/useMessageSearch.ts
@@ -0,0 +1,46 @@
+import { useBeeperDesktop } from "../api";
+
+export function useMessageSearch(searchText: string) {
+ return useBeeperDesktop(
+ async (client, query) => {
+ if (!query) return [];
+ const allMessages = [];
+ // Search for messages matching the query
+ // We limit to a reasonable number or let the iterator handle it,
+ // but for a simple list view, fetching the first batch is usually enough.
+ // The API returns a PagePromise, so we can iterate.
+ for await (const message of client.messages.search({ query })) {
+ allMessages.push(message);
+ if (allMessages.length >= 50) break;
+ }
+
+ // Deduplicate chat IDs
+ const uniqueChatIDs = Array.from(new Set(allMessages.map((m) => m.chatID)));
+
+ // Fetch chats with simple concurrency control (batch size of 5)
+ const chatMap = new Map();
+ const BATCH_SIZE = 5;
+
+ for (let i = 0; i < uniqueChatIDs.length; i += BATCH_SIZE) {
+ const batch = uniqueChatIDs.slice(i, i + BATCH_SIZE);
+ await Promise.all(
+ batch.map(async (chatID) => {
+ try {
+ const chat = await client.chats.retrieve({ chatID });
+ chatMap.set(chatID, chat);
+ } catch (e) {
+ console.warn(`Failed to load chat ${chatID}`, e);
+ }
+ }),
+ );
+ }
+
+ // Map messages to include chat details
+ return allMessages.map((message) => ({
+ ...message,
+ chat: chatMap.get(message.chatID) || null,
+ }));
+ },
+ [searchText] as [string],
+ );
+}
diff --git a/src/list-accounts.tsx b/src/list-accounts.tsx
index 70adbec..d2b8068 100644
--- a/src/list-accounts.tsx
+++ b/src/list-accounts.tsx
@@ -1,16 +1,24 @@
import { ActionPanel, Detail, List, Action, Icon, Keyboard, Color } from "@raycast/api";
import { useCachedState, withAccessToken } from "@raycast/utils";
-import type { BeeperDesktop } from "@beeper/desktop-api";
import { useBeeperDesktop, createBeeperOAuth, focusApp } from "./api";
+import { t } from "./locales";
+/**
+ * Render a searchable list of Beeper Desktop accounts with an optional inline detail pane.
+ *
+ * Displays accounts fetched from Beeper Desktop and provides actions to focus the app, toggle the inline details pane, open a legacy detail view, and refresh the list. When no accounts are available an appropriate empty view is shown.
+ *
+ * @returns A Raycast List component populated with account items and an empty-state view when no accounts exist
+ */
function ListAccountsCommand() {
+ const translations = t();
const [isShowingDetail, setIsShowingDetail] = useCachedState("list-accounts:isShowingDetail", false);
const {
data: accounts,
isLoading,
revalidate,
error,
- } = useBeeperDesktop(async (client) => {
+ } = useBeeperDesktop(async (client) => {
const result = await client.accounts.list();
return result;
});
@@ -21,7 +29,7 @@ function ListAccountsCommand() {
)}
diff --git a/src/list-chats.tsx b/src/list-chats.tsx
index 76dd7d2..1cbd027 100644
--- a/src/list-chats.tsx
+++ b/src/list-chats.tsx
@@ -1,74 +1,152 @@
-import { ActionPanel, Detail, List, Action, Icon } from "@raycast/api";
-import { withAccessToken } from "@raycast/utils";
-import { useBeeperDesktop, createBeeperOAuth, focusApp } from "./api";
+import { useState, useMemo } from "react";
+import { ActionPanel, Detail, List, Action, Icon, Image, showToast, Toast } from "@raycast/api";
+import { useCachedPromise, withAccessToken } from "@raycast/utils";
+import { useBeeperDesktop, getBeeperDesktop, createBeeperOAuth, focusApp, listAccounts } from "./api";
+import { t } from "./locales";
+import { getChatIcon } from "./utils/chatIcon";
+/**
+ * Render a searchable list of Beeper chats with actions to open the chat in Beeper, view details, and copy the chat ID.
+ *
+ * The list is populated from Beeper Desktop search results filtered by the search bar; each item shows network-specific icon, title (or a localized unnamed fallback), type, and last activity when available.
+ *
+ * @returns A Raycast List component populated with chat items and an empty state when no chats are found.
+ */
function ListChatsCommand() {
- const {
- data: chats,
- isLoading,
- revalidate,
- } = useBeeperDesktop(async (client) => {
- const result = await client.chats.search();
- console.log("Fetched accounts:", JSON.stringify(result, null, 2));
- return result.data;
- });
+ const translations = t();
+ const [searchText, setSearchText] = useState("");
+ const [chats, setChats] = useState([]);
+ const [cursor, setCursor] = useState(null);
+ const [hasMore, setHasMore] = useState(true);
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
+
+ const { data: accounts = [] } = useCachedPromise(listAccounts, [], { keepPreviousData: true });
+ const accountMap = useMemo(() => new Map(accounts.map((a) => [a.accountID, a])), [accounts]);
+
+ const { isLoading } = useBeeperDesktop(
+ async (client, query) => {
+ // Reset state when search changes
+ setChats([]);
+ setCursor(null);
+ setHasMore(true);
+
+ const searchParams = query ? { query, limit: 50 } : { limit: 50 };
+ const page = await client.chats.search(searchParams);
+
+ setChats(page.items);
+ setCursor(page.oldestCursor);
+ setHasMore(page.hasMore);
+
+ return page.items;
+ },
+ [searchText],
+ );
+
+ const loadMore = async () => {
+ if (!hasMore || isLoadingMore || !cursor) return;
+
+ setIsLoadingMore(true);
+ try {
+ const client = getBeeperDesktop();
+ const searchParams = searchText
+ ? { query: searchText, limit: 50, cursor, direction: "older" as const }
+ : { limit: 50, cursor, direction: "older" as const };
+
+ const page = await client.chats.search(searchParams);
+
+ setChats((prev) => [...prev, ...page.items]);
+ setCursor(page.oldestCursor);
+ setHasMore(page.hasMore);
+ } catch (error) {
+ console.error("Failed to load more chats:", error);
+ await showToast({
+ style: Toast.Style.Failure,
+ title: translations.commands.listChats.loadMoreError || "Failed to Load More Chats",
+ message: translations.commands.listChats.loadMoreErrorMessage || "Please try again",
+ });
+ } finally {
+ setIsLoadingMore(false);
+ }
+ };
return (
-
- {(chats || []).map((chat) => (
+
+ {chats.map((chat) => {
+ const account = accountMap.get(chat.accountID);
+ const accountLabel = account
+ ? `${account.network || translations.commands.contacts.accountFallback} • ${
+ account.user?.fullName ||
+ account.user?.username ||
+ account.user?.email ||
+ account.user?.phoneNumber ||
+ account.accountID
+ }`
+ : undefined;
+ return (
0 ? [{ text: `${chat.unreadCount} unread` }] : []),
- ...(chat.isPinned ? [{ icon: Icon.Pin }] : []),
- ...(chat.isMuted ? [{ icon: Icon.SpeakerOff }] : []),
- ...(chat.isArchived ? [{ icon: Icon.AddPerson }] : []),
+ ...(accountLabel ? [{ tag: accountLabel }] : []),
+ { text: chat.type },
+ ...(chat.lastActivity ? [{ date: new Date(chat.lastActivity) }] : []),
]}
actions={
+ focusApp({ chatID: chat.id })}
+ />
}
/>
- revalidate()}
- />
- focusApp()}
- />
+
}
/>
- ))}
- {!isLoading && (!chats || chats.length === 0) && (
+ );
+ })}
+ {!isLoading && hasMore && chats.length > 0 && (
+
+
+
+ }
+ />
+ )}
+ {!isLoading && chats.length === 0 && (
)}
diff --git a/src/locales/cs.ts b/src/locales/cs.ts
new file mode 100644
index 0000000..aa1b2ad
--- /dev/null
+++ b/src/locales/cs.ts
@@ -0,0 +1,141 @@
+import { Translations } from "./en";
+
+export const cs: Translations = {
+ // Commands
+ commands: {
+ listAccounts: {
+ title: "Seznam účtů",
+ description: "Zobrazit všechny připojené Beeper účty",
+ navigationTitle: "Beeper účty",
+ emptyTitle: "Žádné účty nenalezeny",
+ emptyDescription: "Nejsou připojeny žádné Beeper účty",
+ },
+ listChats: {
+ title: "Seznam chatů",
+ description: "Zobrazit všechny vaše Beeper chaty",
+ searchPlaceholder: "Filtrovat chaty...",
+ emptyTitle: "Žádné chaty nenalezeny",
+ emptyDescription: "Zkuste upravit hledání nebo se ujistěte, že Beeper Desktop běží",
+ loadMoreError: "Nepodařilo se načíst další chaty",
+ loadMoreErrorMessage: "Zkuste to prosím znovu",
+ loading: "Načítání...",
+ loadMoreChats: "Načíst další chaty",
+ loadMoreAction: "Načíst další",
+ },
+ searchChats: {
+ title: "Hledat chaty",
+ description: "Prohledat vaše Beeper chaty",
+ searchPlaceholder: "Hledat chaty...",
+ emptyTitle: "Vyhledat chaty",
+ emptyDescription: "Začněte psát pro vyhledávání v Beeper chatech",
+ noResultsTitle: "Žádné chaty nenalezeny",
+ noResultsDescription: "Zkuste změnit hledání nebo se ujistěte, že Beeper Desktop běží",
+ },
+ unreadChats: {
+ title: "Nepřečtené chaty",
+ description: "Zobrazit všechny chaty s nepřečtenými zprávami",
+ navigationTitle: "Nepřečtené chaty",
+ searchPlaceholder: "Filtrovat nepřečtené chaty...",
+ emptyTitle: "Žádné nepřečtené zprávy",
+ emptyDescription: "Vše vyřízeno! Nemáte žádné nepřečtené chaty.",
+ errorTitle: "Nepodařilo se načíst chaty",
+ errorDescription: "Nelze se připojit k Beeper Desktop. Ujistěte se, že běží a zkuste to znovu.",
+ unreadCount: (count: number) => `${count} nepřečtených`,
+ totalCount: (count: number) => ` (${count} celkem)`,
+ quickReplyAction: "Rychlá odpověď",
+ archiveAction: "Archivovat chat",
+ archiveSuccess: "Chat archivován",
+ archiveError: "Nepodařilo se archivovat chat",
+ },
+ sendMessage: {
+ title: "Odeslat zprávu",
+ description: "Rychle odeslat zprávu do libovolného chatu",
+ navigationTitle: "Odeslat zprávu",
+ searchPlaceholder: "Hledat chat…",
+ composeAction: "Napsat zprávu",
+ noChatsFound: "Žádné chaty nenalezeny",
+ noChatsDescription: "Zkuste jiný vyhledávací dotaz.",
+ },
+ searchAll: {
+ title: "Hledat vše",
+ description: "Prohledat všechny Beeper chaty a zprávy",
+ navigationTitle: "Hledat vše",
+ searchPlaceholder: "Hledat chaty a zprávy…",
+ emptyTitle: "Prohledat Beeper",
+ emptyDescription: "Začněte psát pro vyhledávání napříč chaty a zprávami",
+ noResultsTitle: "Žádné výsledky",
+ noResultsDescription: "Zkuste jiný vyhledávací dotaz.",
+ chatsSection: "Chaty",
+ messagesSection: "Zprávy",
+ },
+ focusApp: {
+ title: "Zaměřit Beeper Desktop",
+ description: "Přenést Beeper Desktop do popředí",
+ successMessage: "Beeper Desktop zaměřen",
+ errorMessage: "Nepodařilo se zaměřit Beeper Desktop",
+ },
+ contacts: {
+ title: "Kontakty",
+ description: "Hledat kontakty napříč připojenými Beeper účty",
+ navigationTitle: "Hledat kontakty",
+ searchPlaceholder: "Hledat kontakty podle jména",
+ accountDropdownTooltip: "Účet",
+ allAccounts: "Všechny účty",
+ accountFallback: "Účet",
+ partialErrorTitle: "Část účtů selhala",
+ partialErrorMessage: "Výsledky kontaktů mohou být neúplné.",
+ searchFailedTitle: "Hledání selhalo",
+ searchFailedDescription: "Ujistěte se, že Beeper běží, a zkuste to znovu.",
+ noResultsTitle: "Žádné kontakty nenalezeny",
+ typeToSearchTitle: "Začněte psát",
+ noResultsDescription: "Zkuste jiný dotaz.",
+ typeToSearchDescription: "Zadejte jméno pro vyhledání kontaktů.",
+ openChatAction: "Otevřít chat",
+ openingChatToast: "Otevírám chat",
+ chatFoundToast: "Chat nalezen",
+ creatingChatToast: "Vytvářím chat",
+ chatCreatedToast: "Chat vytvořen",
+ openChatFailedToast: "Otevření chatu selhalo",
+ copyParticipantId: "Kopírovat ID účastníka",
+ },
+ searchMessages: {
+ title: "Hledat zprávy",
+ description: "Prohledat vaše Beeper zprávy",
+ navigationTitle: "Nedávné zprávy",
+ searchPlaceholder: "Hledat zprávy...",
+ emptyTitle: "Vyhledat zprávy",
+ emptyDescription: "Začněte psát pro vyhledávání v Beeper zprávách",
+ noResultsTitle: "Žádné zprávy nenalezeny",
+ noResultsDescription: "Zkuste změnit hledání nebo se ujistěte, že Beeper Desktop běží",
+ },
+ },
+
+ // Common
+ common: {
+ unnamedChat: "Nepojmenovaný chat",
+ unknownMessage: "Neznámá zpráva",
+ unreadCount: (count: number) => `${count} nepřečtených`,
+ unknownError: "Došlo k neznámé chybě",
+ openInBeeper: "Otevřít chat v Beeper",
+ copyChatId: "Kopírovat ID chatu",
+ copyMessageText: "Kopírovat text zprávy",
+ showDetails: "Zobrazit detaily",
+ pinned: "Připnuto",
+ muted: "Ztišeno",
+ archived: "Archivováno",
+ yes: "Ano",
+ no: "Ne",
+ details: {
+ id: "ID",
+ accountId: "ID účtu",
+ network: "Síť",
+ type: "Typ",
+ unreadCount: "Počet nepřečtených",
+ isPinned: "Připnuto",
+ isMuted: "Ztišeno",
+ isArchived: "Archivováno",
+ lastActivity: "Poslední aktivita",
+ na: "N/A",
+ },
+ },
+};
diff --git a/src/locales/en.ts b/src/locales/en.ts
new file mode 100644
index 0000000..52bb41b
--- /dev/null
+++ b/src/locales/en.ts
@@ -0,0 +1,141 @@
+export const en = {
+ // Commands
+ commands: {
+ listAccounts: {
+ title: "List Accounts",
+ description: "List all connected Beeper accounts",
+ navigationTitle: "Beeper Accounts",
+ emptyTitle: "No Accounts Found",
+ emptyDescription: "No Beeper accounts are connected",
+ },
+ listChats: {
+ title: "List Chats",
+ description: "List all your Beeper chats",
+ searchPlaceholder: "Filter chats...",
+ emptyTitle: "No chats found",
+ emptyDescription: "Try adjusting your search or make sure Beeper Desktop is running",
+ loadMoreError: "Failed to Load More Chats",
+ loadMoreErrorMessage: "Please try again",
+ loading: "Loading...",
+ loadMoreChats: "Load More Chats",
+ loadMoreAction: "Load More",
+ },
+ searchChats: {
+ title: "Search Chats",
+ description: "Search through your Beeper chats",
+ searchPlaceholder: "Search chats...",
+ emptyTitle: "Search for chats",
+ emptyDescription: "Start typing to search through your Beeper chats",
+ noResultsTitle: "No chats found",
+ noResultsDescription: "Try changing your search or ensure Beeper Desktop is running",
+ },
+ unreadChats: {
+ title: "Unread Chats",
+ description: "View all chats with unread messages",
+ navigationTitle: "Unread Chats",
+ searchPlaceholder: "Filter unread chats...",
+ emptyTitle: "No Unread Messages",
+ emptyDescription: "All caught up! You have no unread chats.",
+ errorTitle: "Failed to Load Chats",
+ errorDescription: "Could not connect to Beeper Desktop. Make sure it's running and try again.",
+ unreadCount: (count: number) => `${count} unread`,
+ totalCount: (count: number) => ` (${count} total)`,
+ quickReplyAction: "Quick Reply",
+ archiveAction: "Archive Chat",
+ archiveSuccess: "Chat archived",
+ archiveError: "Failed to archive chat",
+ },
+ sendMessage: {
+ title: "Send Message",
+ description: "Quickly send a message to any chat",
+ navigationTitle: "Send Message",
+ searchPlaceholder: "Search for a chat…",
+ composeAction: "Compose Message",
+ noChatsFound: "No Chats Found",
+ noChatsDescription: "Try a different search query.",
+ },
+ searchAll: {
+ title: "Search Everything",
+ description: "Search across all Beeper chats and messages",
+ navigationTitle: "Search Everything",
+ searchPlaceholder: "Search chats and messages…",
+ emptyTitle: "Search Beeper",
+ emptyDescription: "Type to search across all chats and messages",
+ noResultsTitle: "No Results",
+ noResultsDescription: "Try a different search query.",
+ chatsSection: "Chats",
+ messagesSection: "Messages",
+ },
+ focusApp: {
+ title: "Focus Beeper Desktop",
+ description: "Bring Beeper Desktop to the foreground",
+ successMessage: "Beeper Desktop focused",
+ errorMessage: "Failed to focus Beeper Desktop",
+ },
+ contacts: {
+ title: "Contacts",
+ description: "Search contacts across connected Beeper accounts",
+ navigationTitle: "Search Contacts",
+ searchPlaceholder: "Search contacts by name",
+ accountDropdownTooltip: "Account",
+ allAccounts: "All Accounts",
+ accountFallback: "Account",
+ partialErrorTitle: "Some accounts failed",
+ partialErrorMessage: "Contacts may be incomplete for this search.",
+ searchFailedTitle: "Search Failed",
+ searchFailedDescription: "Make sure Beeper is running and try again.",
+ noResultsTitle: "No Contacts Found",
+ typeToSearchTitle: "Type to Search",
+ noResultsDescription: "Try a different query.",
+ typeToSearchDescription: "Enter a name to search contacts.",
+ openChatAction: "Open Chat",
+ openingChatToast: "Opening chat",
+ chatFoundToast: "Chat found",
+ creatingChatToast: "Creating chat",
+ chatCreatedToast: "Chat created",
+ openChatFailedToast: "Open chat failed",
+ copyParticipantId: "Copy Participant ID",
+ },
+ searchMessages: {
+ title: "Search Messages",
+ description: "Search through your Beeper messages",
+ navigationTitle: "Recent Messages",
+ searchPlaceholder: "Search messages...",
+ emptyTitle: "Search for messages",
+ emptyDescription: "Start typing to search through your Beeper messages",
+ noResultsTitle: "No messages found",
+ noResultsDescription: "Try changing your search or ensure Beeper Desktop is running",
+ },
+ },
+
+ // Common
+ common: {
+ unnamedChat: "Unnamed Chat",
+ unknownMessage: "Unknown Message",
+ unreadCount: (count: number) => `${count} unread`,
+ unknownError: "Unknown error occurred",
+ openInBeeper: "Open Chat in Beeper",
+ copyChatId: "Copy Chat ID",
+ copyMessageText: "Copy Message Text",
+ showDetails: "Show Details",
+ pinned: "Pinned",
+ muted: "Muted",
+ archived: "Archived",
+ yes: "Yes",
+ no: "No",
+ details: {
+ id: "ID",
+ accountId: "Account ID",
+ network: "Network",
+ type: "Type",
+ unreadCount: "Unread Count",
+ isPinned: "Pinned",
+ isMuted: "Muted",
+ isArchived: "Archived",
+ lastActivity: "Last Activity",
+ na: "N/A",
+ },
+ },
+};
+
+export type Translations = typeof en;
diff --git a/src/locales/index.ts b/src/locales/index.ts
new file mode 100644
index 0000000..dfce28e
--- /dev/null
+++ b/src/locales/index.ts
@@ -0,0 +1,67 @@
+import { environment, getPreferenceValues } from "@raycast/api";
+import { en, Translations } from "./en";
+import { cs } from "./cs";
+
+const translations: Record = {
+ en,
+ cs,
+};
+
+/**
+ * Determine the active locale code to use for translations.
+ *
+ * Prefers the user's Raycast language preference when set and supported, otherwise derives the language from the Raycast environment locale, and defaults to `en` if no supported locale is found.
+ *
+ * @returns The locale code to use for translations (e.g., `en`, `cs`). `en` if no supported locale is found.
+ */
+function getLocale(): string {
+ // Try to get language from Raycast preferences first
+ const prefs = getPreferenceValues();
+ if (typeof prefs.language === "string" && translations[prefs.language]) {
+ return prefs.language;
+ }
+
+ // Fallback to Raycast environment locale
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const raycastLocale = (environment as any).locale as string | undefined;
+ if (!raycastLocale) {
+ return "en";
+ }
+ const languageCode = raycastLocale.split(/[-_]/)[0].toLowerCase();
+ return translations[languageCode] ? languageCode : "en";
+}
+
+let currentLocale = getLocale();
+let currentTranslations = translations[currentLocale];
+
+/**
+ * Get the translations for the currently active locale.
+ *
+ * @returns The `Translations` object for the active locale.
+ */
+export function t(): Translations {
+ return currentTranslations;
+}
+
+/**
+ * Switches the active locale and its translations when the given locale is supported.
+ *
+ * @param locale - Locale code (e.g., "en", "cs"); if this locale is present in the translations map the active locale and translations are updated, otherwise no change occurs.
+ */
+export function setLocale(locale: string): void {
+ if (translations[locale]) {
+ currentLocale = locale;
+ currentTranslations = translations[locale];
+ }
+}
+
+/**
+ * Get the currently active locale code.
+ *
+ * @returns The active locale code (for example `en` or `cs`).
+ */
+export function getCurrentLocale(): string {
+ return currentLocale;
+}
+
+export type { Translations };
diff --git a/src/reactions.ts b/src/reactions.ts
new file mode 100644
index 0000000..3122131
--- /dev/null
+++ b/src/reactions.ts
@@ -0,0 +1,31 @@
+import BeeperDesktop from "@beeper/desktop-api";
+
+/** Compact summary like "👍2 ❤️". */
+export const formatReactionsShort = (reactions?: BeeperDesktop.Reaction[]): string | undefined => {
+ if (!reactions || reactions.length === 0) return undefined;
+ const counts = new Map();
+ for (const r of reactions) {
+ counts.set(r.reactionKey, (counts.get(r.reactionKey) ?? 0) + 1);
+ }
+ return [...counts.entries()].map(([key, n]) => (n > 1 ? `${key}${n}` : key)).join(" ");
+};
+
+/** Detailed summary grouped by sender like "Alice 👍❤️, Bob 😂". */
+export const formatReactionsDetailed = (
+ reactions?: BeeperDesktop.Reaction[],
+ nameMap?: Map,
+): { entries: { name: string; emojis: string }[] } | undefined => {
+ if (!reactions || reactions.length === 0) return undefined;
+ const bySender = new Map();
+ for (const r of reactions) {
+ const key = r.participantID;
+ const list = bySender.get(key) ?? [];
+ list.push(r.reactionKey);
+ bySender.set(key, list);
+ }
+ const entries = [...bySender.entries()].map(([id, emojis]) => ({
+ name: nameMap?.get(id) ?? id,
+ emojis: emojis.join(""),
+ }));
+ return { entries };
+};
diff --git a/src/search-all.tsx b/src/search-all.tsx
new file mode 100644
index 0000000..c6c0cf8
--- /dev/null
+++ b/src/search-all.tsx
@@ -0,0 +1,118 @@
+import { Action, ActionPanel, Color, Icon, List } from "@raycast/api";
+import { useCachedPromise, withAccessToken } from "@raycast/utils";
+import { useState } from "react";
+import { createBeeperOAuth, focusApp, searchAll } from "./api";
+import { t } from "./locales";
+import { getChatIcon } from "./utils/chatIcon";
+import { getMessageID, parseDate } from "./utils";
+
+function SearchAllCommand() {
+ const translations = t();
+ const sa = translations.commands.searchAll;
+
+ const [searchText, setSearchText] = useState("");
+ const trimmedQuery = searchText.trim();
+
+ const { data, isLoading } = useCachedPromise(
+ async (query: string) => {
+ if (!query) return null;
+ return searchAll({ query });
+ },
+ [trimmedQuery],
+ { keepPreviousData: true },
+ );
+
+ // Merge chats + in_groups, deduplicate by id
+ const chatMap = new Map();
+ const chats = [...(data?.results?.chats ?? []), ...(data?.results?.in_groups ?? [])];
+ for (const chat of chats) chatMap.set(chat.id, chat);
+ const uniqueChats = Array.from(chatMap.values());
+
+ const messages = data?.results?.messages?.items ?? [];
+
+ const hasResults = uniqueChats.length > 0 || messages.length > 0;
+
+ return (
+
+ {!trimmedQuery && (
+
+ )}
+
+ {trimmedQuery && !isLoading && !hasResults && (
+
+ )}
+
+ {uniqueChats.length > 0 && (
+
+ {uniqueChats.map((chat) => (
+ 0 ? [{ text: translations.common.unreadCount(chat.unreadCount) }] : []),
+ ...(chat.lastActivity ? [{ date: new Date(chat.lastActivity) }] : []),
+ ]}
+ actions={
+
+ focusApp({ chatID: chat.id })}
+ />
+
+
+ }
+ />
+ ))}
+
+ )}
+
+ {messages.length > 0 && (
+
+ {messages.map((message) => {
+ const timestamp = parseDate(message.timestamp);
+ const messageID = getMessageID(message);
+ const text = message.text?.trim();
+ const preview = text ? text : "Attachment";
+ const sender = message.senderName || (message.isSender ? "You" : "Unknown");
+
+ return (
+
+ focusApp({ chatID: message.chatID, messageID })}
+ />
+ {message.text && (
+
+ )}
+
+ }
+ />
+ );
+ })}
+
+ )}
+
+ );
+}
+
+export default withAccessToken(createBeeperOAuth())(SearchAllCommand);
diff --git a/src/search-chats.tsx b/src/search-chats.tsx
index 965eab9..ec3950a 100644
--- a/src/search-chats.tsx
+++ b/src/search-chats.tsx
@@ -1,58 +1,92 @@
-import { useState } from "react";
-import { ActionPanel, Detail, List, Action, Icon } from "@raycast/api";
-import { withAccessToken } from "@raycast/utils";
-import { useBeeperDesktop, createBeeperOAuth } from "./api";
+import { List, Icon } from "@raycast/api";
+import { useCachedPromise, withAccessToken } from "@raycast/utils";
+import { useState, useMemo } from "react";
+import { createBeeperOAuth, focusApp, listAccounts } from "./api";
+import { t } from "./locales";
+import { ChatListItem } from "./components/ChatListItem";
+import { useChatSearch } from "./hooks/useChatSearch";
+/**
+ * Returns raw avatar URL for 1:1 chats, undefined for groups.
+ * Note: The URL is not sanitized here - ChatListItem handles sanitization via safeAvatarPath.
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function getAvatarUrl(chat: any): string | undefined {
+ // Only show avatar for 1:1 chats, not groups
+ if (chat.type !== "group" && chat.participants?.items && Array.isArray(chat.participants.items)) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf);
+ return otherParticipant?.imgURL;
+ }
+ return undefined;
+}
+
+/**
+ * Render a search interface that finds and displays Beeper chats matching the user's query.
+ *
+ * Shows an initial empty view when the search box is empty, performs a client-side search as the
+ * user types, and renders matching chats with network icons, unread/pinned/muted accessories, and
+ * actions to open the chat in Beeper or copy its ID.
+ *
+ * @returns The List JSX element presenting the search bar, results, and appropriate empty states.
+ */
function SearchChatsCommand() {
+ const translations = t();
const [searchText, setSearchText] = useState("");
- const { data: chats = [], isLoading } = useBeeperDesktop(async (client) => {
- const result = await client.chats.search({ query: searchText });
- return result.data;
- });
+ const { data: chats = [], isLoading } = useChatSearch(searchText);
+ const { data: accounts = [] } = useCachedPromise(listAccounts, [], { keepPreviousData: true });
+ const accountMap = useMemo(() => new Map(accounts.map((a) => [a.accountID, a])), [accounts]);
return (
-
- {chats.map((chat) => (
-
-
- }
- />
-
- }
+
+ {searchText === "" ? (
+
- ))}
- {!isLoading && chats.length === 0 && (
+ ) : !isLoading && chats.length === 0 ? (
+ ) : (
+ chats.map((chat) => {
+ const account = accountMap.get(chat.accountID);
+ const accountLabel = account
+ ? `${account.network || translations.commands.contacts.accountFallback} • ${
+ account.user?.fullName ||
+ account.user?.username ||
+ account.user?.email ||
+ account.user?.phoneNumber ||
+ account.accountID
+ }`
+ : undefined;
+ return (
+ focusApp({ chatID: chat.id }),
+ }}
+ translations={translations}
+ accessories={[
+ ...(accountLabel ? [{ tag: accountLabel }] : []),
+ ...(chat.unreadCount > 0 ? [{ text: translations.common.unreadCount(chat.unreadCount) }] : []),
+ ...(chat.isPinned ? [{ icon: Icon.Pin }] : []),
+ ...(chat.isMuted ? [{ icon: Icon.SpeakerOff }] : []),
+ ]}
+ showDetails={false}
+ />
+ );
+ })
)}
);
diff --git a/src/search-messages.tsx b/src/search-messages.tsx
new file mode 100644
index 0000000..ca4bdf1
--- /dev/null
+++ b/src/search-messages.tsx
@@ -0,0 +1,525 @@
+import {
+ Action,
+ ActionPanel,
+ Color,
+ Detail,
+ Icon,
+ Keyboard,
+ LaunchProps,
+ List,
+ openExtensionPreferences,
+} from "@raycast/api";
+import { useCachedPromise, useCachedState, withAccessToken } from "@raycast/utils";
+import BeeperDesktop from "@beeper/desktop-api";
+import { useEffect, useMemo, useRef, useState } from "react";
+import {
+ createBeeperOAuth,
+ focusApp,
+ listChatMessages,
+ retrieveChat,
+ getRaycastFocusLink,
+ searchChats,
+ searchMessages,
+ useBeeperDesktop,
+} from "./api";
+import { ChatThread, ComposeMessageForm, toApiInbox, type ChatFilters, type InboxFilter } from "./chat";
+import { formatReactionsShort, formatReactionsDetailed } from "./reactions";
+import { parseDate, getMessageID, getBeeperAppPath } from "./utils";
+import { t } from "./locales";
+
+type SenderFilter = "any" | "me" | "others";
+
+interface MessageFilters extends ChatFilters {
+ sender: SenderFilter;
+}
+
+type SearchMessagesLaunchContext = {
+ chatID?: string;
+ query?: string;
+ sender?: SenderFilter;
+};
+
+const defaultFilters: MessageFilters = {
+ inbox: "inbox",
+ type: "any",
+ unreadOnly: false,
+ sender: "any",
+ includeMuted: true,
+};
+
+function SearchMessagesCommand(props: LaunchProps<{ launchContext?: SearchMessagesLaunchContext }>) {
+ const translations = t();
+ const initialQuery = props.launchContext?.query ?? "";
+ const [searchText, setSearchText] = useState(initialQuery);
+ const [rawFilters, setFilters] = useCachedState("messages:filters:v2", defaultFilters);
+ const filters = { ...defaultFilters, ...rawFilters };
+ const [isShowingDetail, setIsShowingDetail] = useCachedState("messages:showing-detail", false);
+ const [dateAfter, setDateAfter] = useCachedState("messages:date-after", undefined);
+ const [dateBefore, setDateBefore] = useCachedState("messages:date-before", undefined);
+ const chatIDFilter = props.launchContext?.chatID;
+ const beeperAppPath = getBeeperAppPath();
+ const appliedContext = useRef(false);
+
+ useEffect(() => {
+ if (appliedContext.current) return;
+ if (!props.launchContext) return;
+ appliedContext.current = true;
+ setFilters((prev) => ({
+ ...prev,
+ sender: props.launchContext?.sender ?? prev.sender,
+ }));
+ if (props.launchContext.query) {
+ setSearchText(props.launchContext.query);
+ }
+ }, [props.launchContext, setFilters]);
+
+ const trimmedQuery = searchText.trim();
+
+ const chatFilterParams = useMemo(
+ () => ({
+ inbox: toApiInbox(filters.inbox),
+ type: filters.type !== "any" ? (filters.type as "single" | "group") : undefined,
+ includeMuted: filters.includeMuted,
+ unreadOnly: filters.unreadOnly || undefined,
+ }),
+ [filters.inbox, filters.type, filters.includeMuted, filters.unreadOnly],
+ );
+
+ const { data: inboxChatIDs, isLoading: isLoadingChats } = useCachedPromise(
+ async (params: typeof chatFilterParams) => {
+ const ids: string[] = [];
+ let cursor: string | null | undefined;
+ for (let page = 0; page < 5 && ids.length < 200; page++) {
+ const result = await searchChats({
+ ...params,
+ cursor,
+ direction: cursor ? "before" : undefined,
+ });
+ ids.push(...result.items.map((c) => c.id));
+ if (!result.hasMore || !result.oldestCursor || result.items.length === 0) break;
+ cursor = result.oldestCursor;
+ }
+ return ids;
+ },
+ [chatFilterParams],
+ { keepPreviousData: true },
+ );
+
+ const resolvedChatIDs = chatIDFilter ? [chatIDFilter] : inboxChatIDs;
+
+ const messageParams = useMemo(() => {
+ const next: Parameters[0] = {
+ includeMuted: filters.includeMuted,
+ chatType: filters.type !== "any" ? filters.type : undefined,
+ sender: filters.sender !== "any" ? filters.sender : undefined,
+ query: trimmedQuery.length > 0 ? trimmedQuery : undefined,
+ chatIDs: resolvedChatIDs,
+ dateAfter,
+ dateBefore,
+ limit: 20,
+ };
+ return next;
+ }, [resolvedChatIDs, dateAfter, dateBefore, filters.includeMuted, filters.sender, filters.type, trimmedQuery]);
+
+ const canFetchMessages = chatIDFilter ? true : inboxChatIDs !== undefined && inboxChatIDs.length > 0;
+
+ const {
+ data: rawMessages = [],
+ isLoading: isLoadingMessages,
+ revalidate,
+ error,
+ } = useCachedPromise(
+ async (input: Parameters[0]) => {
+ const maxMessages = 60;
+ const allItems: BeeperDesktop.Message[] = [];
+ let cursor: string | null | undefined;
+
+ while (allItems.length < maxMessages) {
+ const result = await searchMessages({ ...input, cursor, direction: cursor ? "before" : undefined });
+ const items = result.items ?? [];
+ allItems.push(...items);
+ if (!result.hasMore || !result.oldestCursor || items.length === 0) break;
+ cursor = result.oldestCursor;
+ }
+
+ return allItems;
+ },
+ [messageParams],
+ { execute: canFetchMessages, keepPreviousData: true },
+ );
+
+ const messages = !canFetchMessages && inboxChatIDs !== undefined ? [] : rawMessages;
+ const isLoading = isLoadingChats || isLoadingMessages;
+
+ const chatIDs = useMemo(() => Array.from(new Set(messages.map((message) => message.chatID))), [messages]);
+ const { data: chatMeta = {} } = useCachedPromise(
+ async (ids: string[]) => {
+ if (ids.length === 0) return {};
+ const entries = await Promise.all(
+ ids.slice(0, 20).map(async (chatID) => {
+ try {
+ const chat = await retrieveChat(chatID, { maxParticipantCount: 0 });
+ return [chatID, { title: chat.title || chatID, localChatID: chat.localChatID }] as const;
+ } catch {
+ return [chatID, { title: chatID }] as const;
+ }
+ }),
+ );
+ return Object.fromEntries(entries);
+ },
+ [chatIDs],
+ { keepPreviousData: true, execute: chatIDs.length > 0 },
+ );
+
+ const messageIDs = useMemo(() => new Set(messages.map((m) => m.id)), [messages]);
+ const eagerChatIDs = useMemo(() => chatIDs.slice(0, 10), [chatIDs]);
+ const lazyChatIDs = useMemo(() => chatIDs.slice(10), [chatIDs]);
+
+ const fetchReactions = async (ids: string[], msgIDs: Set) => {
+ if (ids.length === 0) return {};
+ const map: Record = {};
+ await Promise.all(
+ ids.map(async (chatID) => {
+ try {
+ const result = await listChatMessages(chatID);
+ for (const msg of result.items ?? []) {
+ if (msg.reactions?.length && msgIDs.has(msg.id)) {
+ map[msg.id] = msg.reactions;
+ }
+ }
+ } catch {
+ // ignore – reactions are best-effort
+ }
+ }),
+ );
+ return map;
+ };
+
+ const { data: eagerReactionsMap = {} } = useCachedPromise(fetchReactions, [eagerChatIDs, messageIDs], {
+ keepPreviousData: true,
+ execute: eagerChatIDs.length > 0,
+ });
+
+ const { data: lazyReactionsMap = {} } = useCachedPromise(fetchReactions, [lazyChatIDs, messageIDs], {
+ keepPreviousData: true,
+ execute: isShowingDetail && lazyChatIDs.length > 0,
+ });
+
+ const reactionsMap = useMemo(
+ () => ({ ...eagerReactionsMap, ...lazyReactionsMap }),
+ [eagerReactionsMap, lazyReactionsMap],
+ );
+
+ const updateFilters = (partial: Partial) =>
+ setFilters((prev) => ({ ...prev, ...partial }));
+
+ const inboxDropdown = (
+ setFilters((prev) => ({ ...prev, inbox: value as InboxFilter }))}
+ >
+
+
+
+
+
+ );
+
+ const nameMap = useMemo(() => {
+ const map = new Map();
+ for (const msg of messages) {
+ if (msg.senderName) map.set(msg.senderID, msg.senderName);
+ }
+ return map;
+ }, [messages]);
+
+ return (
+
+ {messages
+ .filter((m) => !!(m.text?.trim()) || (m.attachments && m.attachments.length > 0))
+ .map((message) => {
+ const text = message.text?.trim();
+ const preview = text && text.length > 0 ? text : "Attachment";
+ const enrichedReactions = reactionsMap[message.id] ?? message.reactions;
+ const reactionsShort = formatReactionsShort(enrichedReactions);
+ const reactionsDetailed = formatReactionsDetailed(enrichedReactions, nameMap);
+ const timestamp = parseDate(message.timestamp);
+ const chatInfo = (chatMeta as Record)[message.chatID];
+ const chatTitle = chatInfo?.title;
+ const sender = message.senderName || (message.isSender ? "You" : "Unknown");
+ const subtitle = chatTitle ? `${chatTitle} • ${sender}` : sender;
+ const messageID = getMessageID(message);
+ const messageLink = getRaycastFocusLink({ chatID: message.chatID, messageID });
+
+ return (
+
+ {reactionsDetailed && (
+
+ {reactionsDetailed.entries.map((e) => (
+
+ ))}
+
+ )}
+
+
+
+ {message.isSender && (
+
+
+
+ )}
+
+ }
+ />
+ ) : null
+ }
+ accessories={[
+ ...(reactionsShort ? [{ tag: { value: reactionsShort, color: Color.SecondaryText } }] : []),
+ ...(timestamp ? [{ date: timestamp }] : []),
+ ]}
+ actions={
+ setIsShowingDetail((prev) => !prev)}
+ dateAfter={dateAfter}
+ dateBefore={dateBefore}
+ setDateAfter={setDateAfter}
+ setDateBefore={setDateBefore}
+ />
+ }
+ />
+ );
+ })}
+ {!isLoading && messages.length === 0 && (
+
+ {beeperAppPath && }
+ openExtensionPreferences()}
+ />
+
+ ) : null
+ }
+ />
+ )}
+
+ );
+}
+
+function MessageSearchActions({
+ message,
+ messageID,
+ messageLink,
+ chatTitle,
+ onRefresh,
+ filters,
+ updateFilters,
+ isShowingDetail,
+ onToggleDetail,
+ dateAfter,
+ dateBefore,
+ setDateAfter,
+ setDateBefore,
+}: {
+ message: BeeperDesktop.Message;
+ messageID: string;
+ messageLink?: string;
+ chatTitle?: string;
+ onRefresh: () => void;
+ filters: MessageFilters;
+ updateFilters: (partial: Partial) => void;
+ isShowingDetail: boolean;
+ onToggleDetail: () => void;
+ dateAfter?: string;
+ dateBefore?: string;
+ setDateAfter: (value?: string) => void;
+ setDateBefore: (value?: string) => void;
+}) {
+ const translations = t();
+
+ return (
+
+
+ focusApp({ chatID: message.chatID, messageID: messageID })}
+ />
+ focusApp({ chatID: message.chatID })} />
+ {messageLink && (
+
+ )}
+
+
+ }
+ />
+ }
+ />
+
+
+
+ {message.text && }
+ {message.text && }
+ {message.text && (
+
+ )}
+
+
+
+ } />
+
+
+
+ updateFilters({ unreadOnly: !filters.unreadOnly })}
+ />
+ updateFilters({ includeMuted: !filters.includeMuted })}
+ />
+ updateFilters({ sender: "any" })} />
+ updateFilters({ sender: "me" })} />
+ updateFilters({ sender: "others" })} />
+ updateFilters({ type: "any" })} />
+ updateFilters({ type: "single" })} />
+ updateFilters({ type: "group" })} />
+ setDateAfter(date ? date.toISOString() : undefined)}
+ />
+ setDateBefore(date ? date.toISOString() : undefined)}
+ />
+ {(dateAfter || dateBefore) && (
+ {
+ setDateAfter(undefined);
+ setDateBefore(undefined);
+ }}
+ />
+ )}
+
+
+ );
+}
+
+function ChatThreadById({ chatID, fallbackTitle }: { chatID: string; fallbackTitle?: string }) {
+ const {
+ data: chat,
+ isLoading,
+ error,
+ } = useBeeperDesktop(async () => {
+ return retrieveChat(chatID, { maxParticipantCount: 0 });
+ });
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (!chat || error) {
+ return ;
+ }
+
+ return ;
+}
+
+function ComposeMessageById({ chatID, replyToMessageID }: { chatID: string; replyToMessageID?: string }) {
+ const {
+ data: chat,
+ isLoading,
+ error,
+ } = useBeeperDesktop(async () => {
+ return retrieveChat(chatID, { maxParticipantCount: 0 });
+ });
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (!chat || error) {
+ return ;
+ }
+
+ return ;
+}
+
+function MessageDetail({ message }: { message: BeeperDesktop.Message }) {
+ const messageID = getMessageID(message);
+ return (
+
+ );
+}
+
+export default withAccessToken(createBeeperOAuth())(SearchMessagesCommand);
diff --git a/src/send-message.tsx b/src/send-message.tsx
new file mode 100644
index 0000000..7a3cac0
--- /dev/null
+++ b/src/send-message.tsx
@@ -0,0 +1,116 @@
+import { Action, ActionPanel, Icon, List } from "@raycast/api";
+import { useLocalStorage, withAccessToken } from "@raycast/utils";
+import Fuse from "fuse.js";
+import { useEffect, useMemo, useRef, useState } from "react";
+import {
+ buildSearchFields,
+ ChatIndexState,
+ ComposeMessageForm,
+ defaultIndexState,
+ IndexedChat,
+ summarizeChatForIndex,
+} from "./chat";
+import { createBeeperOAuth, searchChats } from "./api";
+import { t } from "./locales";
+import { parseDate } from "./utils";
+
+const CHAT_INDEX_KEY = "chat:index:v2";
+
+function SendMessageCommand() {
+ const translations = t();
+ const sm = translations.commands.sendMessage;
+
+ const [searchText, setSearchText] = useState("");
+ const { value: indexState = defaultIndexState, setValue: setIndexState } = useLocalStorage(
+ CHAT_INDEX_KEY,
+ defaultIndexState,
+ );
+ const refreshDone = useRef(false);
+ const indexRef = useRef(indexState);
+ indexRef.current = indexState;
+
+ // Background refresh on mount — updates the index without blocking the UI
+ useEffect(() => {
+ if (refreshDone.current) return;
+ refreshDone.current = true;
+
+ searchChats({ includeMuted: true, type: "any" })
+ .then((result) => {
+ if (result.items.length === 0) return;
+ const newItems: IndexedChat[] = result.items.map((chat) => ({
+ chat: summarizeChatForIndex(chat),
+ inbox: chat.isArchived ? ("archive" as const) : ("inbox" as const),
+ searchFields: buildSearchFields(chat),
+ }));
+ const base = indexRef.current ?? defaultIndexState;
+ const map = new Map();
+ for (const item of base.items) map.set(item.chat.id, item);
+ for (const item of newItems) map.set(item.chat.id, item);
+ void setIndexState({ ...base, items: Array.from(map.values()), updatedAt: Date.now() });
+ })
+ .catch(() => {
+ // Silently ignore — existing index remains usable
+ });
+ }, []);
+
+ const allChats = indexState.items;
+
+ const fuse = useMemo(
+ () =>
+ new Fuse(allChats, {
+ keys: ["searchFields.title", "searchFields.network", "searchFields.participants"],
+ ignoreDiacritics: true,
+ includeScore: true,
+ ignoreLocation: true,
+ threshold: 0.3,
+ }),
+ [allChats],
+ );
+
+ const trimmedQuery = searchText.trim();
+
+ const chats = useMemo(() => {
+ if (!trimmedQuery) return allChats.map((item) => item.chat);
+ return fuse.search(trimmedQuery).map((result) => result.item.chat);
+ }, [allChats, fuse, trimmedQuery]);
+
+ return (
+
+ {chats.map((chat) => {
+ const lastActivity = parseDate(chat.lastActivity);
+ return (
+ 0 ? [{ text: translations.common.unreadCount(chat.unreadCount) }] : []),
+ ...(lastActivity ? [{ date: lastActivity }] : []),
+ ]}
+ actions={
+
+ }
+ />
+
+ }
+ />
+ );
+ })}
+ {allChats.length > 0 && chats.length === 0 && (
+
+ )}
+
+ );
+}
+
+export default withAccessToken(createBeeperOAuth())(SendMessageCommand);
diff --git a/src/unread-chats.tsx b/src/unread-chats.tsx
new file mode 100644
index 0000000..8a8f72f
--- /dev/null
+++ b/src/unread-chats.tsx
@@ -0,0 +1,114 @@
+import { ActionPanel, Action, List, Icon, showToast, Toast } from "@raycast/api";
+import { withAccessToken } from "@raycast/utils";
+import { archiveChat, createBeeperOAuth, focusApp, useBeeperDesktop } from "./api";
+import { ComposeMessageForm } from "./chat";
+import { t } from "./locales";
+import { getChatIcon } from "./utils/chatIcon";
+
+function UnreadChatsCommand() {
+ const translations = t();
+ const u = translations.commands.unreadChats;
+
+ const {
+ data: chats = [],
+ isLoading,
+ error,
+ revalidate,
+ } = useBeeperDesktop(async (client) => {
+ const allChats = [];
+ let cursor: string | null = null;
+ let hasMore = true;
+ const MAX_PAGES = 20;
+ let pageCount = 0;
+
+ while (hasMore && pageCount < MAX_PAGES) {
+ const searchParams = cursor
+ ? { unreadOnly: true, limit: 50, cursor, direction: "older" as const }
+ : { unreadOnly: true, limit: 50 };
+
+ const page = await client.chats.search(searchParams);
+ allChats.push(...page.items);
+
+ cursor = page.oldestCursor;
+ hasMore = page.hasMore;
+ pageCount++;
+ }
+
+ return allChats.sort((a, b) => b.unreadCount - a.unreadCount);
+ });
+
+ const totalUnread = chats.reduce((sum, chat) => sum + chat.unreadCount, 0);
+
+ const handleArchive = async (chatID: string) => {
+ const toast = await showToast({ style: Toast.Style.Animated, title: u.archiveAction });
+ try {
+ await archiveChat(chatID, true);
+ toast.style = Toast.Style.Success;
+ toast.title = u.archiveSuccess;
+ revalidate();
+ } catch {
+ toast.style = Toast.Style.Failure;
+ toast.title = u.archiveError;
+ }
+ };
+
+ return (
+ 0 ? u.totalCount(totalUnread) : ""}`}
+ >
+ {error ? (
+
+ ) : !isLoading && chats.length === 0 ? (
+
+ ) : (
+ chats.map((chat) => (
+
+ focusApp({ chatID: chat.id })}
+ />
+ }
+ />
+ handleArchive(chat.id)}
+ />
+
+
+ }
+ />
+ ))
+ )}
+
+ );
+}
+
+export default withAccessToken(createBeeperOAuth())(UnreadChatsCommand);
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..115bbac
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,18 @@
+import BeeperDesktop from "@beeper/desktop-api";
+import { existsSync } from "node:fs";
+import { homedir } from "node:os";
+import { join } from "node:path";
+
+export const parseDate = (value?: string) => {
+ if (!value) return undefined;
+ const date = new Date(value);
+ return Number.isNaN(date.getTime()) ? undefined : date;
+};
+
+export const getMessageID = (message: BeeperDesktop.Message & { messageID?: string }) =>
+ message.messageID ?? message.id;
+
+export const getBeeperAppPath = () => {
+ const candidates = ["/Applications/Beeper Desktop.app", join(homedir(), "Applications", "Beeper Desktop.app")];
+ return candidates.find((path) => existsSync(path));
+};
diff --git a/src/utils/avatar.ts b/src/utils/avatar.ts
new file mode 100644
index 0000000..ad323e2
--- /dev/null
+++ b/src/utils/avatar.ts
@@ -0,0 +1,98 @@
+import { homedir } from "os";
+import { resolve, normalize, sep } from "path";
+
+/**
+ * Returns the platform-specific allowed base directories for avatar files.
+ * Supports macOS, Windows, and Linux.
+ */
+function getAllowedAvatarBases(): string[] {
+ const home = homedir();
+ const platform = process.platform;
+
+ switch (platform) {
+ case "darwin":
+ // macOS: ~/Library/Application Support/BeeperTexts/media
+ return [normalize(resolve(home, "Library/Application Support/BeeperTexts/media"))];
+
+ case "win32":
+ // Windows: %APPDATA%/BeeperTexts/media (typically ~/AppData/Roaming/BeeperTexts/media)
+ return [normalize(resolve(home, "AppData", "Roaming", "BeeperTexts", "media"))];
+
+ case "linux":
+ default: {
+ // Linux: $XDG_DATA_HOME/BeeperTexts/media or ~/.local/share/BeeperTexts/media
+ const bases: string[] = [];
+ const xdgDataHome = process.env.XDG_DATA_HOME;
+ if (xdgDataHome) {
+ bases.push(normalize(resolve(xdgDataHome, "BeeperTexts", "media")));
+ }
+ // Always include the fallback path
+ bases.push(normalize(resolve(home, ".local", "share", "BeeperTexts", "media")));
+ return bases;
+ }
+ }
+}
+
+// Allowed base directories for avatar files (platform-specific)
+const ALLOWED_AVATAR_BASES = getAllowedAvatarBases();
+
+/**
+ * Checks if a normalized path is within any of the allowed avatar directories.
+ * Prevents prefix attacks by requiring exact match or path separator after base.
+ */
+function isPathAllowed(normalizedPath: string): boolean {
+ return ALLOWED_AVATAR_BASES.some((base) => {
+ const isExactMatch = normalizedPath === base;
+ const isSubpath = normalizedPath.startsWith(base + sep);
+ return isExactMatch || isSubpath;
+ });
+}
+
+/**
+ * Safely converts a file:// URL to a validated filesystem path.
+ * Returns undefined if the URL is invalid or points outside the allowed directory.
+ *
+ * Security measures:
+ * - Only accepts file:// URLs, rejects all other schemes
+ * - Wraps decodeURIComponent in try/catch for malformed URIs
+ * - Normalizes and resolves path to prevent path traversal
+ * - Validates path is exactly the allowed directory or a true subpath (prevents prefix attacks)
+ * - Rejects paths with null bytes
+ */
+export function safeAvatarPath(url: string): string | undefined {
+ try {
+ // Only accept file:// URLs - explicitly reject other schemes
+ if (!url.startsWith("file://")) {
+ return undefined;
+ }
+
+ // Strip the file:// prefix
+ let avatarPath = url.slice(7); // "file://".length === 7
+
+ // Decode URL encoding safely
+ try {
+ avatarPath = decodeURIComponent(avatarPath);
+ } catch {
+ // URIError: malformed URI - return undefined to fall back to network icon
+ return undefined;
+ }
+
+ // Reject paths with null bytes (could bypass string checks)
+ if (avatarPath.includes("\0")) {
+ return undefined;
+ }
+
+ // Normalize and resolve the path to prevent path traversal
+ const normalizedPath = normalize(resolve(avatarPath));
+
+ // Validate that the path is within one of the allowed avatar directories
+ if (!isPathAllowed(normalizedPath)) {
+ return undefined;
+ }
+
+ return normalizedPath;
+ } catch {
+ // Any unexpected error - fall back to network icon
+ return undefined;
+ }
+}
diff --git a/src/utils/chatIcon.ts b/src/utils/chatIcon.ts
new file mode 100644
index 0000000..0fb0b3e
--- /dev/null
+++ b/src/utils/chatIcon.ts
@@ -0,0 +1,24 @@
+import { Image, Icon } from "@raycast/api";
+import { safeAvatarPath } from "./avatar";
+import { getNetworkIcon } from "./networkIcons";
+
+/**
+ * Returns chat icon - contact avatar for DMs, network icon for groups.
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function getChatIcon(chat: any): Image.ImageLike {
+ if (!chat) return Icon.Bubble;
+
+ // For 1:1 chats, try to get the other person's avatar
+ if (chat.type !== "group" && chat.participants?.items && Array.isArray(chat.participants.items)) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const otherParticipant = chat.participants.items.find((p: any) => !p.isSelf);
+ if (otherParticipant?.imgURL) {
+ const validatedPath = safeAvatarPath(otherParticipant.imgURL);
+ if (validatedPath) {
+ return { source: validatedPath, mask: Image.Mask.Circle };
+ }
+ }
+ }
+ return getNetworkIcon(chat.network);
+}
diff --git a/src/utils/networkIcons.ts b/src/utils/networkIcons.ts
new file mode 100644
index 0000000..fb907e6
--- /dev/null
+++ b/src/utils/networkIcons.ts
@@ -0,0 +1,41 @@
+import { Icon, Image } from "@raycast/api";
+
+/**
+ * Maps network identifiers to their corresponding icon asset filenames.
+ * These filenames are relative to the extension's assets directory.
+ */
+const NETWORK_ICON_MAP: Record = {
+ slack: "slack.svg",
+ whatsapp: "whatsapp.svg",
+ telegram: "telegram.svg",
+ discord: "discord.svg",
+ instagram: "instagram.svg",
+ facebook: "facebook.svg",
+ facebookmessenger: "messenger.svg",
+ messenger: "messenger.svg",
+ signal: "signal.svg",
+ imessage: "imessage.svg",
+ twitter: "twitter.svg",
+ email: "email.svg",
+ googlemessages: "google-messages.svg",
+};
+
+/**
+ * Returns the icon for a messaging network.
+ * Falls back to a generic message icon if the network is not recognized.
+ *
+ * @param network - The network name (case-insensitive, spaces/slashes/dashes are ignored)
+ * @returns The corresponding network icon image
+ */
+export function getNetworkIcon(network: string | undefined | null): Image.ImageLike {
+ if (!network) return Icon.Message;
+ const networkLower = network.toLowerCase().replace(/[/\s-]/g, "");
+ const iconFilename = NETWORK_ICON_MAP[networkLower];
+
+ if (iconFilename) {
+ // Return an Image.Source object pointing to the asset file
+ return { source: iconFilename };
+ }
+
+ return Icon.Message;
+}