diff --git a/.github/workflows/script-commands.yml b/.github/workflows/script-commands.yml index 6dd6f87d4..cd5dea05f 100644 --- a/.github/workflows/script-commands.yml +++ b/.github/workflows/script-commands.yml @@ -3,20 +3,22 @@ name: Set as Executable and Generate Documentation on: push: branches: [master] + paths-ignore: + - commands/README.md + - commands/extensions.json jobs: scriptCommands: - runs-on: macos-12 + runs-on: macos-15 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: Setup Xcode - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: 13.4.1 + uses: raycast/github-actions/setup-xcode@xcode-16.1.x - name: Setup GIT uses: raycast/github-actions/setup-git@v1.1.0 @@ -42,7 +44,7 @@ jobs: uses: ad-m/github-push-action@master with: branch: ${{ github.ref }} - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: Notify Failure if: failure() diff --git a/README.md b/README.md index a1a82d593..fcccd6f14 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@
- GitHub contributors + GitHub contributors - Twitter Follow + Twitter Follow
diff --git a/commands/README.md b/commands/README.md index 30762c431..88289b6ac 100644 --- a/commands/README.md +++ b/commands/README.md @@ -25,6 +25,8 @@ This repository contains sample commands and documentation to write your own one ### Categories +- [Ai](#ai) + - [Gemini](#gemini) - [Apps](#apps) - [Agenda](#agenda) - [Amphetamine](#amphetamine) @@ -33,6 +35,7 @@ This repository contains sample commands and documentation to write your own one - [Bear](#bear) - [BusyCal](#busycal) - [Chatgpt](#chatgpt) + - [Claude](#claude) - [Cleanshot](#cleanshot) - [Craft](#craft) - [DND Me](#dnd-me) @@ -46,6 +49,7 @@ This repository contains sample commands and documentation to write your own one - [ExpressVPN](#expressvpn) - [Fantastical](#fantastical) - [Ferdi](#ferdi) + - [Find My](#find-my) - [Focus](#focus) - [GoodLinks](#goodlinks) - [HazeOver](#hazeover) @@ -134,6 +138,7 @@ This repository contains sample commands and documentation to write your own one - [Apple Tv](#apple-tv) - [Cmus](#cmus) - [Endel](#endel) + - [Lowfi](#lowfi) - [Sonos](#sonos) - [Speaker Setup](#speaker-setup) - [Spotify](#spotify) @@ -157,11 +162,20 @@ This repository contains sample commands and documentation to write your own one - [Samsung TV](#samsung-tv) - [System](#system) - [Audio](#audio) + - [Magic Keyboard Switcher](#magic-keyboard-switcher) - [VPN](#vpn) - [Vpnutil](#vpnutil) - [Web Searches](#web-searches) - [WordPress](#wordpress) +## Ai + +#### Gemini + +| Icon | Title | Description | Author | Args | Templ | Lang | +| :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | | [Ask Gemini](ai/gemini/gemini.js) | Open Gemini in Chrome browser and submit a prompt with optional selected text as context | [Est7](https://github.com/est7) and [Nimo Beeren](https://github.com/nimobeeren) | ✅ | | | + ## Apps #### Agenda @@ -218,6 +232,12 @@ This repository contains sample commands and documentation to write your own one | :--: | ----- | ----------- | :----: | :--: | :---: | :--: | | | [chatgpt](apps/chatgpt/chatgpt-open-safari.applescript) | Open chatgpt in safari | [gintonyc](https://raycast.com/gintonyc) | | | | +#### Claude + +| Icon | Title | Description | Author | Args | Templ | Lang | +| :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | ✨ | [Ask Claude](apps/claude/claude.js) | Open Claude in Chrome browser and submit a prompt | [Nimo Beeren](https://github.com/nimobeeren) | ✅ | | | + #### Cleanshot | Icon | Title | Description | Author | Args | Templ | Lang | @@ -356,6 +376,12 @@ This repository contains sample commands and documentation to write your own one | | [Open Service by Index](apps/ferdi/ferdi-open-service-by-index.applescript) | N/A | [Jakub Lanski](https://github.com/jaklan) | ✅ | | | | | [Open Service by Name](apps/ferdi/ferdi-open-service-by-name.applescript) | N/A | [Jakub Lanski](https://github.com/jaklan) | ✅ | | | +#### Find My + +| Icon | Title | Description | Author | Args | Templ | Lang | +| :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | | [Find My Phone (Auto Sound)](apps/find-my/fmp.js) | N/A | Raycast | | | | + #### Focus | Icon | Title | Description | Author | Args | Templ | Lang | @@ -555,6 +581,7 @@ This repository contains sample commands and documentation to write your own one | | [Close Other Tabs](apps/safari/safari-close-other-tabs.applescript) | Close all tabs besides the currently active tab. | [Thomas Paul Mann](https://github.com/thomaspaulmann) | | | | | | [Close Tabs to the Left](apps/safari/safari-close-tabs-left.applescript) | Close all tabs to the left side of the currently active tab. | [Thomas Paul Mann](https://github.com/thomaspaulmann) | | | | | | [Close Tabs to the Right](apps/safari/safari-close-tabs-right.applescript) | Close all tabs to the right side of the currently active tab. | [Thomas Paul Mann](https://github.com/thomaspaulmann) | | | | + | | [Download Current URL](apps/safari/safari-download-url.applescript) | Download the currently active tab's URL. | [Michael Bianco](https://github.com/iloveitaly) | | | | | | [Duplicate Tab](apps/safari/safari-duplicate-tab.applescript) | Duplicates and opens the currently active tab. | [Thomas Paul Mann](https://github.com/thomaspaulmann) | | | | | | [Open Bing with Edge User-Agent](apps/safari/safari-bing-edge-user-agent.applescript) | Open Bing in Safari with Edge User-Agent | [smxl](https://github.com/smxl) | | | | | | [Open Safari URL in Chrome](apps/safari/safari-current-page-url-in-chrome.applescript) | Open current Safari URL in new tab in Chrome | [Dave Lehman](https://github.com/dlehman) | | | | @@ -612,7 +639,7 @@ This repository contains sample commands and documentation to write your own one | | [Connect](apps/tailscale/tailscale-connect.sh) | Connects to Tailscale | [Ross Zurowski](https://github.com/rosszurowski) | | | | | | [Disconnect](apps/tailscale/tailscale-disconnect.sh) | Disconnects from Tailscale | [Ross Zurowski](https://github.com/rosszurowski) | | | | | | [Get IP](apps/tailscale/tailscale-ip.sh) | Gets your private Tailscale IP | [Ross Zurowski](https://github.com/rosszurowski) | | | | - | | [Switch Account](apps/tailscale/tailscale-switch.sh) | Switches Tailscale networks | [Ross Zurowski](https://github.com/rosszurowski) | | | | + | | [Switch Account](apps/tailscale/tailscale-switch.sh) | Switches Tailscale networks | [Ross Zurowski](https://github.com/rosszurowski) and [Daniel Schoemer](https://github.com/quatauta) | | | | #### Terminal Translate @@ -732,6 +759,7 @@ This repository contains sample commands and documentation to write your own one | 🥽 | [Current Website to Private Browser](browsing/to-private-browsing.sh) | Open the current site in a private browser | [raulanatol](https://github.com/raulanatol) | | | | | | [Go to Outline](browsing/go-to-outine.sh) | Open the website at Outline | [Ronan Rodrigo Nunes](https://ronanrodrigo.dev) | ✅ | | | | 🌐 | [New Browser Window](browsing/new-browser-window.sh) | Open new window in default browser | [Levi Nelson](https://github.com/LeviticusNelson) | | | | + | 📚 | [Open Multiple Websites on Safari](browsing/open-multiple-websites-on-safari.template.sh) | Open multiple websites on Safari using list of URLs | [Yasutaka Nishii](https://github.com/ystknsh) | | ✅ | | | 🤖 | [Open in guest profile](browsing/open-in-guest-profile.sh) | Open current website in guest profile/mode | [JD Solanki](https://github.com/jd-solanki) | | | | | | [Open without CORS](browsing/open-chrome-without-cors.sh) | Open chrome with web security option disabled. | [Tahsin Yazkan](https://github.com/thsnyzkn) | | | | | | [Peek a link](browsing/peekalink.template.sh) | Use [Peekalink.io](https://peekalink.io) API to peek specified URL. | [Caleb Stauffer](https://github.com/crstauf) | ✅ | ✅ | | @@ -772,6 +800,7 @@ This repository contains sample commands and documentation to write your own one | Icon | Title | Description | Author | Args | Templ | Lang | | :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | 🎲 | [Random Emoji](communication/emojis/random-emoji.sh) | Copy a random emoji to the clipboard. | [Tomohiro Nishimura](https://github.com/Sixeight) | | | | | 📙 | [Search Emojis](communication/emojis/emojis-search.sh) | Search for emojis related to input. | [Caleb Stauffer](https://github.com/crstauf) | ✅ | | | | 📙 | [Search and Copy First Related Emoji](communication/emojis/emoji-copy.sh) | Copy first emoji related to input. | [Caleb Stauffer](https://github.com/crstauf) | ✅ | | | @@ -989,6 +1018,7 @@ This repository contains sample commands and documentation to write your own one | 🔐 | [Strong Password Generator](developer-utils/strong-password-generator.sh) | Generate a strong password of requested character length | [Nitin Gupta](https://twitter.com/gniting) | ✅ | | | | 🕒 | [Time Between Dates](developer-utils/time-between-dates.js) | Given two dates returns the time between them in multiple units of measure. | [Federico Miraglia](https://github.com/Mitra98t) | ✅ | | | | 🕒 | [Time Calculator](developer-utils/time-calculator.js) | Add or Subtract specified amount of time from given date. | [Federico Miraglia](https://github.com/Mitra98t) | ✅ | | | + | 🔒 | [Toggle SSH SOCKS Tunnel](developer-utils/toggle_ssh_proxy_tunnel.template.sh) | Toggles an SSH SOCKS proxy tunnel on and off. | [Andrii Barabash](https://github.com/AndriiBarabash) | | ✅ | | | 🔠 | [Transform Case](developer-utils/transform-case.sh) | Transform the case of clipboard content. Defaults to lower case if no conversion type is specified. | [Nitin Gupta](https://twitter.com/gniting) | ✅ | | | | ✂ | [Trim Git Commit Hash](developer-utils/trim-git-commit-hash.sh) | Trim full git commit hash down to seven characters. | [Caleb Stauffer](https://github.com/crstauf) | ✅ | | | | 🔗 | [Unshorten URL](developer-utils/unshorten-url.sh) | Unshortens clipboard content url and copies the result again. | [Nikita Galaiko](https://github.com/ngalaiko) | | | | @@ -1148,6 +1178,7 @@ This repository contains sample commands and documentation to write your own one | Icon | Title | Description | Author | Args | Templ | Lang | | :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | | [Calculate CAGR Percentage](math/calculate-CAGR.sh) | Calculate the CAGR between "from" and "to" values over given "years," then copy the result. | [Samuel Barton](https://github.com/samueldbarton) | ✅ | | | | 📈 | [Calculate Growth %](math/calculate-growth.sh) | Calculate percentage increase between "from" and "to" values. | [Petr Nikolaev](https://github.com/pitnikola) | ✅ | | | | 🔢 | [Generate Random Number](math/random-number.sh) | Generate a number between a given range (inclusive) and then copy the value | [Matt Gleich](https://mattglei.ch) | ✅ | | | | 🧮 | [LaTeX Calculator](math/latex-calculator.py) | Evaluate LaTeX expressions and get the product copied to your clipboard | [Matt Gleich](https://mattglei.ch) | ✅ | | | @@ -1221,6 +1252,12 @@ This repository contains sample commands and documentation to write your own one | | [Workout](media/endel/endel-scenarios-workout.sh) | Run Workout scenario at Endel | [Sergey Korobyin](https://github.com/huangsemao) | ✅ | | | | | [Yoga](media/endel/endel-scenarios-yoga.sh) | Run Yoga scenario at Endel | [Sergey Korobyin](https://github.com/huangsemao) | ✅ | | | +#### Lowfi + +| Icon | Title | Description | Author | Args | Templ | Lang | +| :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | | [Lowfi](media/lowfi/lowfi.sh) | N/A | Raycast | | | | + #### Sonos | Icon | Title | Description | Author | Args | Templ | Lang | @@ -1272,6 +1309,7 @@ This repository contains sample commands and documentation to write your own one | Icon | Title | Description | Author | Args | Templ | Lang | | :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | 🫥 | [Hide Current Application](navigation/hide-application.applescript) | Easily hide your foremost application | [Chris Bailey](https://raycast.com/that70schris) | | | | | | [JustFocus](navigation/justfocus.applescript) | N/A | Jax0rz | ✅ | | | | 📂 | [Open Applications](navigation/open-applications.sh) | Opens the Applications folder in the Finder. | Raycast | | | | | 🖥 | [Open Clipboard URL on Desktop](navigation/open-desktop-url-from-clipboard.swift) | Opens the URL from the clipboard in the desktop app. | Raycast | | | | @@ -1462,6 +1500,7 @@ This repository contains sample commands and documentation to write your own one | | [Copy Finder Selection to Clipboard](system/copy-selection-to-clipboard.applescript) | Copy contents of selected items in Finder to the clipboard. If there's more than one file selected, they will be combined and added to the clipboard. | [Felipe Turcheti](https://felipeturcheti.com) | | | | | 💁 | [Copy Last Download](system/copy-last-download.swift) | Copy the last downloaded file to the clipboard. | Raycast | | | | | 📸 | [Copy Last Screenshot](system/copy-last-screenshot.swift) | Copies the last screenshot to the clipboard. | Raycast | | | | + | 📝 | [Copy Meeting Summary](system/meeting_summary_script.swift) | Copies a summary of today's meetings to the clipboard. | Raycast | | | | | 📟 | [Copy Wi-Fi Password](system/wifi-password.sh) | Copy Wi-Fi password from current session | [Astrit Malsia](https://github.com/astrit) | | | | | 📧 | [Create Email](system/new-email.sh) | Opens default email application, and creates a new email with the given inputs. | [Brandon Escamilla](https://github.com/brandonescamilla) | ✅ | | | | 📄 | [Create New File](system/create-new-file.applescript) | Create files in the front window or desktop of the visit | [LokHsu](https://github.com/lokhsu) | | | | @@ -1486,6 +1525,7 @@ This repository contains sample commands and documentation to write your own one | 💨 | [Flush DNS](system/flush-dns.sh) | Flush DNS cache | [Felipe Turcheti](https://felipeturcheti.com) | | | | | 🔫 | [Kill AirDrop Processes](system/kill-airdrop.processes.sh) | Force kill all AirDrop processes | [Linus Salzmann](https://github.com/linus569) | | | | | ⚠️ | [Kill Running Process](system/kill-process.sh) | Force kill a running process | [Gustavo Santana](https://github.com/gumadeiras) | ✅ | | | + | 🚫 | [Kill a process on PORT](system/kill-a-process-on-port.sh) | Kill running processes on the given ports | [aaqifshafi](https://github.com/aaqifshafi) | ✅ | | | | 📈 | [Largest CPU Process](system/largest-cpu-process.sh) | Report process with largest system CPU usage. | [Caleb Stauffer](https://github.com/crstauf) | | | | | 📈 | [Largest RAM Process](system/largest-ram-process.sh) | Report process with largest system RAM usage. | [Caleb Stauffer](https://github.com/crstauf) | | | | | 🔋 | [Low Power Mode](system/Low Power Mode.sh) | N/A | [Kailash Yellareddy](https://github.com/kyellareddy) | | | | @@ -1493,10 +1533,11 @@ This repository contains sample commands and documentation to write your own one | 🌵 | [Modify File Extension](system/modify-extension.sh) | Batch modify the file in the current directory or the specified extension of the selected file | [LokHsu](https://github.com/lokhsu) | ✅ | | | | 🗑 | [Move Desktop to Trash](system/move-desktop-to-trash.applescript) | Empty the desktop. | [Seypopi](https://github.com/Seypopi) | | | | | | [Move Downloads to Trash](system/move-downloads-to-trash.applescript) | N/A | Jax0rz | | | | - | | [Network Info](system/Network Info.sh) | N/A | [Kailash Yellareddy](https://github.com/kyellareddy) | | | | + | 🛜 | [Network Info](system/Network Info.sh) | N/A | [Kailash Yellareddy](https://github.com/kyellareddy) | | | | | 🌐 | [Network Quality](system/network-quality.sh) | N/A | [Archie Lacoin](https://github.com/pomdtr) and [LanikSJ](https://github.com/LanikSJ) | | | | | 📶 | [Network Status](system/network-status.sh) | Get current network connections. | [Alexandru Turcanu](https://github.com/Pondorasti) | | | | | 🖼 | [OCR](system/ocr.swift) | Use macOS Vision API Identification pictures, if it contain a QR code, Copy the QR code content to the clipboard, If do not include QR codes, identify text content and supplement to clipboard | [zhe](https://github.com/wmszhe) | | | | + | 🔑 | [One-Time Password](system/otp.sh) | Get One-Time Password (OTP) from Apple Password Manager | Angelos Michalopoulos | ✅ | | | | 📁 | [Open Folder](system/open-folder.sh) | Open a folder on macOS | [Bin Hua](https://github.com/hzb) | ✅ | | | | 📋 | [Open Image](system/open-image-from-clipboard.sh) | Open Image from Clipboard in Preview for OCR or other purposes. | [xxchan](https://github.com/xxchan) | | | | | 💁‍♂️ | [Open Last Download](system/open-last-download.swift) | Opens the last downloaded file. | Raycast | | | | @@ -1504,6 +1545,7 @@ This repository contains sample commands and documentation to write your own one | 💥 | [Quit All Applications](system/quit-all-apps.swift) | Quits all running applications except Finder and Raycast. | Raycast | | | | | 💥 | [Quit app](system/quit-app.swift) | Quits an app, by name or process id. | [Roland Leth](https://runtimesharks.com) | ✅ | | | | 🖼️ | [Refresh Wallpaper](system/wallpaper-refresh.applescript) | Apply a random image from the [wallpaper directory](https://support.apple.com/guide/mac-help/change-your-desktop-picture-mchlp3013/mac) for the main display's current [Space](https://support.apple.com/guide/mac-help/work-in-multiple-spaces-mh14112/mac). | [Caleb Stauffer](https://github.com/crstauf) | | | | + | 📂 | [Rename Video](system/rename-videos-pictures.py) | This is a simple Python script for recursively renaming video and picture files within a directory. Type the root directory's absolute path, and it will scan all the video and picture files in it and rename them according to the folder where they are located as the format `--`. | [StepaniaH](https://github.com/StepaniaH) | ✅ | | | | 🚀 | [Reset Launchpad](system/reset-launchpad.sh) | Resets the macOS Launchpad to its default state | [Zach Dawson](https://raycast.com/zdawz) | | | | | ♻️ | [Restart](system/restart.applescript) | Restarts computer. | Raycast | | | | | 💀 | [Restart the Dock](system/restart-dock.sh) | Restart the Dock | [Jordi Clement](https://github.com/jordicl) | | | | @@ -1515,6 +1557,7 @@ This repository contains sample commands and documentation to write your own one | 🧭 | [Set Frontmost App as Default Browser](system/default-browser-front-most-app.applescript) | Set Frontmost Web Browser as Default Browser. | [Yohanes Bandung Bondowoso](https://github.com/ybbond) | ✅ | | | | 🛌 | [Shut Down](system/shutdown.applescript) | Shuts down computer. | Raycast | | | | | 😴 | [Sleep](system/sleep.applescript) | Puts computer to sleep. | Raycast | | | | + | 😴 | [Sleep Timer](system/sleep-timer.applescript) | Put your Mac to sleep (in X minutes). | [AndriiBarabash](https://github.com/AndriiBarabash) | ✅ | | | | 🧬 | [Toggle .DS_Store](system/toggle-ds-store.applescript) | A script command to enable and disable .DS_Store | [Astrit](https://github.com/astrit) | | | | | 🔋 | [Toggle Battery Charging](system/toggle-battery-charging.sh) | Toggle charging the battery when it is plugged in. When turned off, it will always use the charger instead of the battery; when turned on, it will go to automatic mode (decide based on your settings and daily charging routine). | [Amir Hossein SamadiPour](https://github.com/SamadiPour) | | | | | | [Toggle Bluetooth](system/bluetooth.template.applescript) | Toggle your Bluetooth connection. | [Vincent Dörig](https://github.com/vincentdoerig) | | ✅ | | @@ -1526,11 +1569,13 @@ This repository contains sample commands and documentation to write your own one | 👓 | [Toggle Hidden Files](system/toggle-hidden-files.applescript) | Show and hide hidden files/folders which starts with "." (dot), i.e: .bash_rc, .ssh | [Thiago Holanda](https://twitter.com/tholanda) | | | | | 🐚 | [Toggle Lid Sleep](system/toggle-lid-sleep.sh) | Prevent sleep from closing laptop lid (clamshell mode) | [Ivan Rybalko](https://github.com/ivribalko) | | | | | 🖱 | [Toggle Natural Scrolling](system/toggle-natural-scrolling.applescript) | Script Command to change natural trackpad/mouse scrolling setting. Reverting the setting value each time. | [Wiley Marques](https://twitter.com/wileymarques) | | | | + | 🖱 | [Toggle Natural Scrolling (macOS 15+)](system/toggle-natural-scrolling-macos15.applescript) | Toggle natural trackpad/mouse scrolling setting for macOS 15.6.1+ | [Raphael-KR](https://github.com/Raphael-KR) | | | | | 🌘 | [Toggle Night Shift](system/nightshift.sh) | Toggle Night Shift mode (until tomorrow/sunrise). Required [nightlight](https://github.com/smudge/nightlight) | [BhEaN](https://github.com/bhean) | | | | | 🌗 | [Toggle System Appearance](system/toggle-system-appearance.applescript) | Script Command to switch between the system appearance, light and dark mode. | [Thiago Holanda](https://twitter.com/tholanda) | | | | | | [Toggle Wi-Fi](system/wifi.template.applescript) | Toggle your Wi-Fi connection. | [Vincent Dörig](https://github.com/vincentdoerig) | | ✅ | | | 😴 | [Turn Off Do Not Disturb](system/dnd-off.sh) | Turn off "do not disturb" mode. Does [not work on Big Sur](https://github.com/sindresorhus/do-not-disturb-cli/issues/2). | [Caleb Stauffer](https://github.com/crstauf) | | | | | 😴 | [Turn On Do Not Disturb](system/dnd-on.sh) | Turn on "do not disturb" mode. Does [not work on Big Sur](https://github.com/sindresorhus/do-not-disturb-cli/issues/2). | [Caleb Stauffer](https://github.com/crstauf) | | | | + | 📋 | [Type Clipboard](system/paste-clipboard.sh) | Takes your clipboard then types each character in the clipboard | [AlexGadd](https://raycast.com/AlexGadd) | | | | | 🗑 | [Uninstall with AppCleaner](system/uninstall-with-appcleaner.applescript) | Uninstall applications with AppCleaner | [Felipe Turcheti](https://felipeturcheti.com) | ✅ | | | | ♻️ | [Update Community Scripts](system/update-scripts-command.template.py) | Updates community Script Commands to their last available version from the GitHub repository. | [Quentin Eude](https://www.github.com/qeude) | | ✅ | | @@ -1549,6 +1594,12 @@ This repository contains sample commands and documentation to write your own one | 🎙 | [Toggle Microphone](system/audio/toggle-mic.applescript) | Toggles microphone. | [Matthew Morek](https://github.com/matthewmorek) | | | | | 🔔 | [Toggle Mute Notifcation Sounds](system/audio/toggle-mute-notification-sounds.applescript) | Toggles notification sounds. | [Annie Ma](http://www.anniema.co/) | | | | +#### Magic Keyboard Switcher + +| Icon | Title | Description | Author | Args | Templ | Lang | +| :--: | ----- | ----------- | :----: | :--: | :---: | :--: | + | | [Magic Keyboard switcher](system/magic-keyboard-switcher/magic-keyboard-switcher.template.sh) | Switch a single magic keyboard between computers | [blastik](https://github.com/blastik) | | ✅ | | + #### VPN | Icon | Title | Description | Author | Args | Templ | Lang | @@ -1598,7 +1649,8 @@ This repository contains sample commands and documentation to write your own one | | [Search PHP Docs](web-searches/search-php-docs.sh) | Search [PHP official documentation](https://www.php.net/docs.php). | [Caleb Stauffer](https://github.com/crstauf) | ✅ | | | | | [Search Python 3 Documentation](web-searches/search-python3-docs.sh) | Search Python 3 Documentation | [Lucas Costa](https://github.com/lucasrcosta) | ✅ | | | | | [Search Python Package Index (PyPI)](web-searches/search-pypi.sh) | Search Python Package Index (PyPI) | [Lucas Costa](https://github.com/lucasrcosta) | ✅ | | | - | | [Search Script Command](web-searches/search-script-command-marketplace.sh) | Search for Script Commands in the [Unofficial Raycast Script Commands Marketplace](https://scriptcommands.com). | [Alexandru Turcanu](https://github.com/Pondorasti) | ✅ | | | + | | [Search Rust Documentation](web-searches/search-rust-docs.sh) | Search Rust documentation | [lemorage](https://raycast.com/lemorage) | ✅ | | | + | | [Search Script Command](web-searches/search-script-command-marketplace.sh) | Search for Script Commands in the [Unofficial Raycast Script Commands Marketplace](https://scriptcommands.alexandru.so). | [Alexandru Turcanu](https://github.com/Pondorasti) | ✅ | | | | | [Search Swift Code](web-searches/search-swift-github.sh) | N/A | Raycast | ✅ | | | | | [Search Unsplash](web-searches/search-unsplash.sh) | Search [Unsplash](https://unsplash.com). | [Caleb Stauffer](https://github.com/crstauf) | ✅ | | | | | [Search WP Engine Installs](web-searches/search-wpengine-installs.sh) | Search [WP Engine](https://wpengine.com) installs. | [Caleb Stauffer](https://github.com/crstauf) | ✅ | | | diff --git a/commands/ai/gemini/gemini.js b/commands/ai/gemini/gemini.js new file mode 100755 index 000000000..20eaa42d8 --- /dev/null +++ b/commands/ai/gemini/gemini.js @@ -0,0 +1,231 @@ +#!/usr/bin/env node + +// Dependencies: +// This script requires the following software to be installed: +// - `node` https://nodejs.org +// - `chrome-cli` https://github.com/prasmussen/chrome-cli +// Install via homebrew: `brew install node chrome-cli` + +// This script needs to run JavaScript in your browser, which requires your permission. +// To do so, open Chrome and find the menu bar item: +// View > Developer > Allow JavaScript from Apple Events + +// Required parameters: +// @raycast.schemaVersion 1 +// @raycast.title Ask Gemini +// @raycast.mode silent +// @raycast.packageName Gemini + +// Optional parameters: +// @raycast.icon ./images/icon-gemini.svg +// @raycast.argument1 { "type": "text", "placeholder": "Selected Text", "optional": true } +// @raycast.argument2 { "type": "text", "placeholder": "Prompt"} + +// Documentation: +// @raycast.description Open Gemini in Chrome browser and submit a prompt with optional selected text as context +// @raycast.author Est7 +// @raycast.authorURL https://github.com/est7 + +// @raycast.author Nimo Beeren +// @raycast.authorURL https://github.com/nimobeeren + +const { execSync } = require("child_process"); + +const selectedText = process.argv[2] || ""; // Get the text from "Selected Text" argument, or use an empty string if argument is empty. +const prompt = process.argv[3]; + +process.env.OUTPUT_FORMAT = "json"; + +/** Escape a string so that it can be used in JavaScript code when wrapped in double quotes. */ +function escapeJsString(str) { + return str.replaceAll(`\\`, `\\\\`).replaceAll(`"`, `\\"`); +} + +/** Escape a string so that it can be used in a shell command when wrapped in single quotes. */ +function escapeShellString(str) { + return str.replaceAll(`'`, `'"'"'`); +} + +// used to wait for Chrome to activate. +function sleep(ms) { + const start = Date.now(); + while (Date.now() - start < ms) {} +} + +/** + * Verifies that all required dependencies are installed. + * Exits the process with an error message if any dependency is missing. + */ +function checkDependencies() { + // Check if Google Chrome is installed + try { + execSync("osascript -e 'tell application \"Google Chrome\" to get version'" , { stdio: "ignore" }); + } catch { + try { + execSync("osascript -e 'tell application \"Chrome\" to get version'" , { stdio: "ignore" }); + } catch { + console.error("Google Chrome is required to run this script"); + process.exit(1); + } + } + + // Check if chrome-cli is installed + try { + execSync("which chrome-cli"); + } catch { + console.error( + "chrome-cli is required to run this script (https://github.com/prasmussen/chrome-cli)" + ); + process.exit(1); + } +} + +// Verify all dependencies are installed +checkDependencies(); + +// Bring Chrome to the foreground first. +try { + // Try to activate Chrome through AppleScript, supporting different possible application names. + execSync("osascript -e 'tell application \"Google Chrome\" to activate'", { + stdio: "ignore", + }); +} catch (e) { + try { + // If the first naming method fails, try possible alternatives. + execSync("osascript -e 'tell application \"Chrome\" to activate'", { + stdio: "ignore", + }); + } catch (err) { + console.error( + "Unable to activate Chrome browser, continue with other operations", + ); + } +} + +// Give Chrome a little time to make sure it is activated +sleep(300); + +// Find the Gemini tab if one is already open +let tabs = JSON.parse(execSync("chrome-cli list tabs")).tabs; +let geminiTab = tabs.find((tab) => + tab.url.startsWith("https://gemini.google.com/"), +); + +// If there is a Gemini tab open, get its info. Otherwise, open Gemini in a new window. +let geminiTabInfo; +if (geminiTab) { + // Focus on existing tags, do not refresh the page + execSync(`chrome-cli activate -t ${geminiTab.id}`); + // Get tab info + geminiTabInfo = JSON.parse(execSync(`chrome-cli info -t ${geminiTab.id}`)); +} else { + // Open a Gemini session in a new tab, focus it and return the tab info + geminiTabInfo = JSON.parse( + execSync("chrome-cli open 'https://gemini.google.com/app'"), + ); +} + +// Wait for the tab to be loaded, then execute the script +let interval = setInterval(() => { + if (geminiTabInfo.loading) { + geminiTabInfo = JSON.parse( + execSync(`chrome-cli info -t ${geminiTabInfo.id}`), + ); + } else { + clearInterval(interval); + executeScript(); + } +}, 100); + +function executeScript() { + const script = async function (selectedText, prompt) { + // Wait for prompt element to be on the page + let promptElement; + await new Promise((resolve) => { + let interval = setInterval(() => { + promptElement = document.querySelector( + 'div[aria-label="Enter a prompt here"]', + ); + if (promptElement) { + clearInterval(interval); + resolve(); + } + }, 100); + }); + + // Prepare the final text + let finalText = ""; + if (selectedText && selectedText.trim() !== "") { + finalText += `${selectedText}\n\n${prompt}`; + } else { + finalText = prompt; + } + + // Focus the input element first + promptElement.focus(); + + // Check if there's existing content + const hasExistingContent = promptElement.textContent.trim() !== ""; + + // Clear existing content if needed - safely without innerHTML + if (!hasExistingContent) { + // If empty, we'll just add our content + // No need to clear anything + } else { + // If we want to append to existing content, add a newline + // Create a new paragraph for separation + const selection = window.getSelection(); + const range = document.createRange(); + + // Move cursor to the end of existing content + range.selectNodeContents(promptElement); + range.collapse(false); // false means collapse to end + selection.removeAllRanges(); + selection.addRange(range); + + // Insert two newlines to separate content + document.execCommand("insertText", false, "\n\n"); + } + + // Insert the content using execCommand which is safer than innerHTML + // Split by newlines and insert with proper paragraph formatting + const paragraphs = finalText.split("\n"); + paragraphs.forEach((paragraph, index) => { + if (index > 0) { + // Insert newline between paragraphs (not before the first one) + document.execCommand("insertText", false, "\n"); + } + + // Insert the paragraph text + document.execCommand("insertText", false, paragraph || "\u200B"); + }); + + // Trigger input event to notify Gemini of changes + const inputEvent = new Event("input", { bubbles: true }); + promptElement.dispatchEvent(inputEvent); + + // Ensure cursor is at the end and visible + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(promptElement); + range.collapse(false); // false means collapse to end + selection.removeAllRanges(); + selection.addRange(range); + + // Scroll to make cursor visible + promptElement.scrollTop = promptElement.scrollHeight; + + // Additional scroll after a short delay to ensure visibility + setTimeout(() => { + promptElement.scrollTop = promptElement.scrollHeight; + }, 100); + }; + + const functionString = escapeShellString(script.toString()); + const selectedTextString = escapeShellString(escapeJsString(selectedText)); + const promptString = escapeShellString(escapeJsString(prompt)); + + execSync( + `chrome-cli execute '(${functionString})(\"${selectedTextString}\", \"${promptString}\")' -t ${geminiTabInfo.id}`, + ); +} diff --git a/commands/ai/gemini/images/icon-gemini.svg b/commands/ai/gemini/images/icon-gemini.svg new file mode 100644 index 000000000..4545c8331 --- /dev/null +++ b/commands/ai/gemini/images/icon-gemini.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/commands/apps/claude/claude.js b/commands/apps/claude/claude.js new file mode 100755 index 000000000..f9adb66c8 --- /dev/null +++ b/commands/apps/claude/claude.js @@ -0,0 +1,127 @@ +#!/usr/bin/env node + +// Dependencies: +// This script requires the following software to be installed: +// - `node` https://nodejs.org +// - `chrome-cli` https://github.com/prasmussen/chrome-cli +// Install via homebrew: `brew install node chrome-cli` + +// This script needs to run JavaScript in your browser, which requires your permission. +// To do so, open Chrome and find the menu bar item: +// View > Developer > Allow JavaScript from Apple Events + +// Required parameters: +// @raycast.schemaVersion 1 +// @raycast.title Ask Claude +// @raycast.mode silent +// @raycast.packageName Claude + +// Optional parameters: +// @raycast.icon ✨ +// @raycast.argument1 { "type": "text", "placeholder": "Prompt"} + +// Documentation: +// @raycast.description Open Claude in Chrome browser and submit a prompt +// @raycast.author Nimo Beeren +// @raycast.authorURL https://github.com/nimobeeren + +const { execSync } = require("child_process"); + +const prompt = process.argv[2]; + +process.env.OUTPUT_FORMAT = "json"; + +/** Escape a string so that it can be used in JavaScript code when wrapped in double quotes. */ +function escapeJsString(str) { + return str.replaceAll(`\\`, `\\\\`).replaceAll(`"`, `\\"`); +} + +/** Escape a string so that it can be used in a shell command when wrapped in single quotes. */ +function escapeShellString(str) { + return str.replaceAll(`'`, `'"'"'`); +} + +try { + execSync("which chrome-cli"); +} catch { + console.error( + "chrome-cli is required to run this script (https://github.com/prasmussen/chrome-cli)" + ); + process.exit(1); +} + +// Find the Claude tab if one is already open +let tabs = JSON.parse(execSync("chrome-cli list tabs")).tabs; +let claudeTab = tabs.find((tab) => tab.url.startsWith("https://claude.ai/")); + +// If there is a Claude tab open, get its info. Otherwise, open Claude in a new +// window. +let claudeTabInfo; +if (claudeTab) { + // Open a new Claude session in the existing tab and focus it + execSync(`chrome-cli open 'https://claude.ai/new' -t ${claudeTab.id}`); + // Get tab info + claudeTabInfo = JSON.parse(execSync(`chrome-cli info -t ${claudeTab.id}`)); +} else { + // Open a Claude session in a new tab, focus it and return the tab info + claudeTabInfo = JSON.parse( + execSync("chrome-cli open 'https://claude.ai/new'") + ); +} + +// Wait for the tab to be loaded, then execute the script +let interval = setInterval(() => { + if (claudeTabInfo.loading) { + claudeTabInfo = JSON.parse( + execSync(`chrome-cli info -t ${claudeTabInfo.id}`) + ); + } else { + clearInterval(interval); + executeScript(); + } +}, 100); + +function executeScript() { + const script = async function (prompt) { + // Wait for prompt element to be on the page + let promptElement; + await new Promise((resolve) => { + let interval = setInterval(() => { + promptElement = document.querySelector( + '[aria-label="Write your prompt to Claude"] > div' + ); + if (promptElement) { + clearInterval(interval); + resolve(); + } + }, 100); + }); + + // Set the prompt + const p = document.createElement("p"); + p.textContent = prompt; + promptElement.replaceChildren(p); + + // Wait for submit button to be on the page + let submitButton; + await new Promise((resolve) => { + let interval = setInterval(() => { + submitButton = document.querySelector('[aria-label="Send Message"]'); + if (submitButton) { + clearInterval(interval); + resolve(); + } + }, 100); + }); + + // Submit the prompt + submitButton.click(); + }; + + const functionString = escapeShellString(script.toString()); + const promptString = escapeShellString(escapeJsString(prompt)); + + execSync( + `chrome-cli execute '(${functionString})("${promptString}")' -t ${claudeTabInfo.id}` + ); +} diff --git a/commands/apps/find-my/.env.example b/commands/apps/find-my/.env.example new file mode 100644 index 000000000..6e3a6c985 --- /dev/null +++ b/commands/apps/find-my/.env.example @@ -0,0 +1,3 @@ +ICLOUD_EMAIL= +ICLOUD_PASSWORD= +DEVICE= \ No newline at end of file diff --git a/commands/apps/find-my/README.md b/commands/apps/find-my/README.md new file mode 100644 index 000000000..55057353e --- /dev/null +++ b/commands/apps/find-my/README.md @@ -0,0 +1,58 @@ +# Find My Phone Raycast + +I lose my phone often, so I made this. + +This is a Node.js script that uses Playwright to automate iCloud Find My and trigger a sound on your Apple device. It's perfect for people like me who misplace their phones and want a quick, automated way to make them ring—with Raycast. + +## Usage + +```sh +git clone https://github.com/vsvaidya27/fmp-raycast +cd fmp-raycast +npm install +cp .env.example .env +# Edit .env with your real credentials +chmod +x fmp.js +``` + +### Add to Raycast + +1. Open Raycast and go to **Extensions**. +2. Click **Add**. +3. Select **Add Script Directory**. +4. Choose the `fmp-raycast` directory you just cloned. +5. Set fmp as your alias for calling the script. + +Now you can trigger the script directly from Raycast! + +## How it works + +- Automates login to iCloud Find My using Playwright +- Selects your device by name +- Triggers the "Play Sound" feature to help you locate your device + +**Note:** You'll need to provide your iCloud credentials and device name in the `.env` file. Sometimes a 2FA check may pop up, but usually they allow you to bypass this and manual intervention is not neccesary (since you often need the very thing you are trying to find for 2FA). + +--- + +## 🛠 Troubleshooting + +### If you get the error `env: node: No such file or directory` + +Raycast uses a limited shell environment, so it may not find your Node.js install. + +To fix it: + +1. Run this in Terminal to find your full Node path: + ```bash + which node + ``` +2. Edit the top of `fmp.js`: + Replace: + ```sh + #!/usr/bin/env node + ``` + With: + ```sh + #!/full/path/to/node + ``` diff --git a/commands/apps/find-my/fmp.js b/commands/apps/find-my/fmp.js new file mode 100755 index 000000000..e672b032b --- /dev/null +++ b/commands/apps/find-my/fmp.js @@ -0,0 +1,62 @@ +#!/usr/bin/env node + +// Required parameters: +// @raycast.schemaVersion 1 +// @raycast.title Find My Phone (Auto Sound) +// @raycast.mode silent +// @raycast.packageName FindMy +// @raycast.icon images/find-my-icon.png + +require('dotenv').config(); +const { chromium } = require('playwright'); + +(async () => { + const browser = await chromium.launch({ headless: false }); + const page = await browser.newPage(); + + // 1. Go to iCloud Find Devices + await page.goto('https://www.icloud.com/find'); + + // 2. Click "Sign In" + await page.waitForSelector('text=Sign In', { timeout: 20000 }); + await page.click('text=Sign In'); + + // 3. Wait for the Apple login iframe to appear + const frameLocator = page.frameLocator('iframe[name="aid-auth-widget"]'); + + // 4. Wait for the email/phone input inside the iframe + await frameLocator.getByRole('textbox', { name: 'Email or Phone Number' }).waitFor({ timeout: 20000 }); + + // 5. Fill in the email/phone + await frameLocator.getByRole('textbox', { name: 'Email or Phone Number' }).fill(process.env.ICLOUD_EMAIL); + + // 6. Click the right-arrow button + await frameLocator.getByRole('button').first().click(); + + // 7. Wait for the "Continue with Password" button to appear, then click it + await frameLocator.getByRole('button', { name: /Continue with Password/i }).waitFor({ timeout: 20000 }); + await frameLocator.getByRole('button', { name: /Continue with Password/i }).click(); + + // 8. Wait for password input + await frameLocator.getByRole('textbox', { name: 'Password' }).waitFor({ timeout: 20000 }); + await frameLocator.getByRole('textbox', { name: 'Password' }).fill(process.env.ICLOUD_PASSWORD); + + // 9. Click the arrow button to continue + await frameLocator.getByRole('button').first().click(); + + // 10. Wait for 2FA if needed; Hopefully not because this kinda defeats the purpose of automation even though its prob for the best security-wise :( + console.log('If prompted, please complete 2FA in the browser window. :('); + + // 12. Click on your iPhone using the precise selector inside the second iframe + await page.locator('iframe').nth(1).contentFrame().getByTitle(process.env.DEVICE).getByTestId('show-device-name').click(); + + // 13. Wait for the device details panel to load + await page.waitForTimeout(2000); + + // 14. Click the "Play Sound" button + await page.locator('iframe').nth(1).contentFrame().getByRole('button', { name: 'Play Sound' }).click(); + + console.log('✅ Play Sound triggered for ' + process.env.DEVICE + '!'); + await browser.close(); +})(); + diff --git a/commands/apps/find-my/images/find-my-icon.png b/commands/apps/find-my/images/find-my-icon.png new file mode 100644 index 000000000..0862bfec8 Binary files /dev/null and b/commands/apps/find-my/images/find-my-icon.png differ diff --git a/commands/apps/find-my/package.json b/commands/apps/find-my/package.json new file mode 100644 index 000000000..6e278971c --- /dev/null +++ b/commands/apps/find-my/package.json @@ -0,0 +1,16 @@ +{ + "name": "findmyraycast", + "version": "1.0.0", + "main": "fmp.js", + "scripts": { + "start": "node fmp.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "dotenv": "^16.5.0", + "playwright": "^1.52.0" + } +} diff --git a/commands/apps/safari/safari-download-url.applescript b/commands/apps/safari/safari-download-url.applescript new file mode 100755 index 000000000..8fd9a716a --- /dev/null +++ b/commands/apps/safari/safari-download-url.applescript @@ -0,0 +1,28 @@ +#!/usr/bin/osascript + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Download Current URL +# @raycast.mode silent + +# Optional parameters: +# @raycast.packageName Safari +# @raycast.icon images/safari.png + +# @Documentation: +# @raycast.author Michael Bianco +# @raycast.authorURL https://github.com/iloveitaly +# @raycast.description Download the currently active tab's URL. + +tell application "Safari" + activate + set currentTab to current tab of window 1 + set theURL to URL of currentTab +end tell + +-- Simulate pressing Option key and clicking the link +tell application "System Events" + keystroke "l" using {command down} -- Select the address bar (Cmd + L) + delay 0.5 + keystroke return using {option down} -- Press Option + Return to download the file +end tell diff --git a/commands/apps/tailscale/tailscale-switch.sh b/commands/apps/tailscale/tailscale-switch.sh index ad40265e9..e174480c8 100755 --- a/commands/apps/tailscale/tailscale-switch.sh +++ b/commands/apps/tailscale/tailscale-switch.sh @@ -15,9 +15,15 @@ # @Documentation: # @raycast.description Switches Tailscale networks + +# Original author # @raycast.author Ross Zurowski # @raycast.authorURL https://github.com/rosszurowski +# Contributor +# @raycast.author Daniel Schoemer +# @raycast.authorURL https://github.com/quatauta + ts="" if command -v tailscale &> /dev/null; then @@ -29,12 +35,11 @@ else exit 1 fi -$ts logout -$ts up +"${ts}" switch --list | # List all Tailscale accounts " " +grep -vF -e 'ID' -e '*' | # Omit the header line and the current account marked with "*" +awk '{ print $1 }' | # Print only the account ID of not-connected accounts +head -n1 | # Print only the first not-connected account +xargs -r -n1 "${ts}" switch # Switch to the selected account -if command -v jq &> /dev/null; then - account=$($ts status --json | jq -r '.User[(.Self.UserID | tostring)].LoginName') - echo "Connected as $account" -else - echo "Connected" -fi +tailnet="$("${ts}" switch --list | awk '/\*/ { print $2 }')" +echo "Switched to ${tailnet}" diff --git a/commands/browsing/open-multiple-websites-on-safari.template.sh b/commands/browsing/open-multiple-websites-on-safari.template.sh new file mode 100755 index 000000000..0f16d29fe --- /dev/null +++ b/commands/browsing/open-multiple-websites-on-safari.template.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Open Multiple Websites on Safari +# @raycast.mode silent + +# Optional parameters: +# @raycast.icon 📚 +# @raycast.packageName Browsing + +# Documentation: +# @raycast.description Open multiple websites on Safari using list of URLs +# @raycast.author Yasutaka Nishii +# @raycast.authorURL https://github.com/ystknsh + +# Set list of URLs +urls=( + "https://example.com/" + "https://example.org/" + "https://example.net/" + "https://example.jp/" + "https://example.io/" + "https://example.ai/" +) + +# Make AppleScript commands +applescript_command="tell application \"Safari\" + make new document with properties {URL:\"${urls[0]}\"} + tell window 1" + +for ((i=1; i<${#urls[@]}; i++)); do + applescript_command+=" + make new tab with properties {URL:\"${urls[$i]}\"}" +done + +applescript_command+=" + end tell +end tell" + +# Execute AppleScript +osascript -e "$applescript_command" + +# Set Safari window to front(Optional) +osascript < /dev/null; then + echo "jq command is required (https://github.com/jqlang/jq)."; + exit 1; +fi + +IFS=$'\n' read -d '' -r -a EMOJIS < <(curl -s https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json | jq -r '.[] | .emoji' && printf '\0') +EMOJI="${EMOJIS[$RANDOM % ${#EMOJIS[@]}]}" +echo -n "$EMOJI" | pbcopy +echo "$EMOJI" diff --git a/commands/conversions/rich-text-clipboard-to-markdown.sh b/commands/conversions/rich-text-clipboard-to-markdown.sh index 7cde8493f..5bf7d5d1d 100755 --- a/commands/conversions/rich-text-clipboard-to-markdown.sh +++ b/commands/conversions/rich-text-clipboard-to-markdown.sh @@ -20,4 +20,4 @@ if ! command -v pandoc &> /dev/null; then fi export LC_CTYPE=UTF-8 -osascript -e 'the clipboard as "HTML"'| perl -ne 'print chr foreach unpack("C*",pack("H*",substr($_,11,-3)))' | pandoc --from=html --to=gfm | pbcopy +osascript -e 'the clipboard as «class RTF »' | perl -ne 'print chr foreach unpack("C*",pack("H*",substr($_,11,-3)))' | pandoc --from=rtf --to=gfm | pbcopy diff --git a/commands/developer-utils/aws/amazon-s3-download.sh b/commands/developer-utils/aws/amazon-s3-download.sh index 502a9529c..fcd54b887 100755 --- a/commands/developer-utils/aws/amazon-s3-download.sh +++ b/commands/developer-utils/aws/amazon-s3-download.sh @@ -11,7 +11,7 @@ # Documentation: # @raycast.description Download from Amazon S3 via URL -# @raycast.argument1 { "type": "text", "placeholder": "S3 URL" } +# @raycast.argument1 { "type": "text", "placeholder": "s3://bucket/key" } # @raycast.author Chris Cook # @raycast.authorURL https://github.com/zirkelc @@ -26,64 +26,110 @@ fi # Try matching different S3 URL patterns # https://dev.to/aws-builders/format-and-parse-amazon-s3-url-5e10 -if [[ $URL =~ ^https?://s3\.([a-z0-9-]+)\.amazonaws\.com/([^/]+)/(.*)$ ]]; then +if [[ $URL =~ ^https?://s3\.([a-z0-9-]+)\.amazonaws\.com/([^/]+)(/(.*))?$ ]]; then # Regional hostname with path-style + # Example: https://s3.us-east-1.amazonaws.com/bucket/key BUCKET="${BASH_REMATCH[2]}" - KEY="${BASH_REMATCH[3]}" -elif [[ $URL =~ ^https?://([^/]+)\.s3\.([a-z0-9-]+)\.amazonaws\.com/(.*)$ ]]; then + KEY="${BASH_REMATCH[4]}" +elif [[ $URL =~ ^https?://([^/]+)\.s3\.([a-z0-9-]+)\.amazonaws\.com(/(.*))?$ ]]; then # Regional hostname with virtual-hosted-style + # Example: https://bucket.s3.us-east-1.amazonaws.com/key BUCKET="${BASH_REMATCH[1]}" - KEY="${BASH_REMATCH[3]}" -elif [[ $URL =~ ^https?://s3\.amazonaws\.com/([^/]+)/(.*)$ ]]; then + KEY="${BASH_REMATCH[4]}" +elif [[ $URL =~ ^https?://s3\.amazonaws\.com/([^/]+)(/(.*))?$ ]]; then # Legacy hostname with path-style + # Example: https://s3.amazonaws.com/bucket/key BUCKET="${BASH_REMATCH[1]}" - KEY="${BASH_REMATCH[2]}" -elif [[ $URL =~ ^https?://([^/]+)\.s3\.amazonaws\.com/(.*)$ ]]; then + KEY="${BASH_REMATCH[3]}" +elif [[ $URL =~ ^https?://([^/]+)\.s3\.amazonaws\.com(/(.*))?$ ]]; then # Legacy hostname with virtual-hosted-style + # Example: https://bucket.s3.amazonaws.com/key BUCKET="${BASH_REMATCH[1]}" - KEY="${BASH_REMATCH[2]}" -elif [[ $URL =~ ^s3://([^/]+)/(.*)$ ]]; then + KEY="${BASH_REMATCH[3]}" +elif [[ $URL =~ ^s3://([^/]+)(/(.*))?$ ]]; then # S3 URI + # Example: s3://bucket/key BUCKET="${BASH_REMATCH[1]}" - KEY="${BASH_REMATCH[2]}" + KEY="${BASH_REMATCH[3]}" else echo "Invalid URL: $URL" echo "URL must match recognized S3 patterns." + echo "Patterns:" + echo "- Global S3 URI: s3://bucket/key" + echo "- Regional Path-Style URL: https://s3.region.amazonaws.com/bucket/key" + echo "- Regional Virtual-Hosted-Style URL: https://bucket.s3.region.amazonaws.com/key" + echo "- Legacy Path-Style URL: https://s3.amazonaws.com/bucket/key" + echo "- Legacy Virtual-Hosted-Style URL: https://bucket.s3.amazonaws.com/key" exit 1 fi -# Check for empty bucket or key -if [[ -z "$BUCKET" || -z "$KEY" ]]; then - echo "Error extracting bucket and key from URL: $URL" - exit 1 -fi +# Trim leading slash from KEY if present +KEY="${KEY#/}" -# Remove trailing slash if present and set recursive download flag -RECURSIVE="" -if [[ "$KEY" =~ .*/$ ]]; then - KEY="${KEY%/}" # Remove trailing slash for directory key - RECURSIVE="--recursive" +# Check for empty bucket +if [[ -z "$BUCKET" ]]; then + echo "Error extracting bucket from URL: $URL" + exit 1 fi -# Set download path DOWNLOAD_FOLDER="$HOME/Downloads" -DOWNLOAD_PATH="$DOWNLOAD_FOLDER/$KEY" -if [[ -n "$RECURSIVE" ]]; then - echo "Downloading directory $URL to $DOWNLOAD_PATH..." -else - echo "Downloading file $URL to $DOWNLOAD_PATH..." +# Ensure download folder exists +if [ ! -d "$DOWNLOAD_FOLDER" ]; then + echo "Download folder does not exist" + exit 1 fi # Print bucket and key echo "Bucket: $BUCKET" -echo "Key: $KEY" +echo "Key: ${KEY:-}" +echo "Download: $DOWNLOAD_FOLDER" +echo "" -# Use recursive if necessary -if aws s3 cp "s3://$BUCKET/$KEY" "$DOWNLOAD_PATH" $RECURSIVE; then - echo "Downloaded successfully." - open "$DOWNLOAD_FOLDER" +if [ -z "$KEY" ]; then + # No key provided, download entire bucket + DOWNLOAD_PATH="$DOWNLOAD_FOLDER/$BUCKET" + echo "Downloading entire bucket s3://$BUCKET to $DOWNLOAD_PATH..." + RECURSIVE="--recursive" else - echo "Download failed." - exit 1 + # Check if the key ends with a slash, indicating it's likely a directory + if [[ "$KEY" == */ ]]; then + DOWNLOAD_PATH="$DOWNLOAD_FOLDER/$BUCKET/$KEY" + echo "Downloading directory s3://$BUCKET/$KEY to $DOWNLOAD_PATH..." + RECURSIVE="--recursive" + else + # Check if the object exists as a file + if aws s3api head-object --bucket "$BUCKET" --key "$KEY" &>/dev/null; then + # It's a file + FILENAME=$(basename "$KEY") + DOWNLOAD_PATH="$DOWNLOAD_FOLDER/$FILENAME" + echo "Downloading file s3://$BUCKET/$KEY to $DOWNLOAD_PATH..." + RECURSIVE="" + else + # It might be a directory without a trailing slash or it doesn't exist + # Try to list objects with this prefix + if aws s3api list-objects-v2 --bucket "$BUCKET" --prefix "$KEY/" --max-items 1 --query 'Contents[0].Key' --output text &>/dev/null; then + # It's a directory + DOWNLOAD_PATH="$DOWNLOAD_FOLDER/$BUCKET/$KEY" + echo "Downloading directory s3://$BUCKET/$KEY to $DOWNLOAD_PATH..." + RECURSIVE="--recursive" + else + echo "Error: No file or directory found at s3://$BUCKET/$KEY" + exit 1 + fi + fi + fi +fi + +# Perform the download +if aws s3 cp "s3://$BUCKET/${KEY:+$KEY}" "$DOWNLOAD_PATH" $RECURSIVE; then + echo "Downloaded successfully." + if [[ -n "$RECURSIVE" ]]; then + open "$DOWNLOAD_FOLDER/$BUCKET" + else + open "$DOWNLOAD_FOLDER" + fi +else + echo "Download failed. Error code: $?" + exit 1 fi diff --git a/commands/developer-utils/toggle_ssh_proxy_tunnel.template.sh b/commands/developer-utils/toggle_ssh_proxy_tunnel.template.sh new file mode 100755 index 000000000..80f8e054d --- /dev/null +++ b/commands/developer-utils/toggle_ssh_proxy_tunnel.template.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Raycast Script Command +# +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Toggle SSH SOCKS Tunnel +# @raycast.mode silent +# +# Optional parameters: +# @raycast.icon 🔒 +# @raycast.packageName Developer Utilities +# +# Documentation: +# @raycast.description Toggles an SSH SOCKS proxy tunnel on and off. +# @raycast.author Andrii Barabash +# @raycast.authorURL https://github.com/AndriiBarabash + +# --- Configuration --- +# Replace these with your own values +SSH_USER="" +SSH_HOST="" +SSH_PORT="" +INTERFACE="" # e.g., "Wi-Fi" or "Ethernet" +PROXY_PORT="" # e.g., 1080 +# --- End Configuration --- + +if pgrep -f "ssh -D $PROXY_PORT" >/dev/null; then + echo "SSH SOCKS tunnel is running. Turning it off..." + + # Kill the SSH process + pkill -f "ssh -D $PROXY_PORT" + + # Disable the SOCKS proxy + networksetup -setsocksfirewallproxystate "$INTERFACE" off + + echo "Tunnel and proxy disabled." +else + echo "SSH SOCKS tunnel is not running. Turning it on..." + + # Start the SSH tunnel + ssh -D "$PROXY_PORT" -f -C -q -N -p "$SSH_PORT" "$SSH_USER"@"$SSH_HOST" + + # Enable the SOCKS proxy + networksetup -setsocksfirewallproxy "$INTERFACE" 127.0.0.1 "$PROXY_PORT" + networksetup -setsocksfirewallproxystate "$INTERFACE" on + + echo "Tunnel and proxy enabled." +fi diff --git a/commands/extensions.json b/commands/extensions.json index 823fc5313..a245b43c1 100644 --- a/commands/extensions.json +++ b/commands/extensions.json @@ -331,7 +331,7 @@ "refreshTime" : null, "schemaVersion" : 1, "title" : "Rich Text to Markdown", - "updatedAt" : "2021-07-23T17:21:58-04:00" + "updatedAt" : "2024-08-28T20:37:53+08:00" }, { "authors" : null, @@ -1294,6 +1294,34 @@ "title" : "Go to Outline", "updatedAt" : "2021-02-19T19:09:37-03:00" }, + { + "authors" : [ + { + "name" : "Yasutaka Nishii", + "url" : "https:\/\/github.com\/ystknsh" + } + ], + "createdAt" : "2024-08-29T16:26:42+09:00", + "currentDirectoryPath" : null, + "description" : "Open multiple websites on Safari using list of URLs", + "filename" : "open-multiple-websites-on-safari.template.sh", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "📚" + }, + "identifier" : "be278c484ca03c82e901a345ff6e6b72", + "isTemplate" : true, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Browsing", + "path" : "browsing\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Open Multiple Websites on Safari", + "updatedAt" : "2024-09-03T11:24:41Z" + }, { "authors" : [ { @@ -2150,6 +2178,34 @@ "schemaVersion" : 1, "title" : "Open Downloads", "updatedAt" : "2021-04-28T11:35:25-04:00" + }, + { + "authors" : [ + { + "name" : "Chris Bailey", + "url" : "https:\/\/raycast.com\/that70schris" + } + ], + "createdAt" : "2024-08-13T00:40:38-04:00", + "currentDirectoryPath" : null, + "description" : "Easily hide your foremost application", + "filename" : "hide-application.applescript", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "🫥" + }, + "identifier" : "47f61692f25fabb33a1748ded306439c", + "isTemplate" : false, + "language" : "applescript", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Navigation", + "path" : "navigation\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Hide Current Application", + "updatedAt" : "2024-08-13T00:40:38-04:00" } ] }, @@ -3971,6 +4027,31 @@ "title" : "LaTeX Calculator", "updatedAt" : "2022-02-04T10:14:08Z" }, + { + "authors" : [ + { + "name" : "Samuel Barton", + "url" : "https:\/\/github.com\/samueldbarton" + } + ], + "createdAt" : "2024-11-18T09:32:56-06:00", + "currentDirectoryPath" : null, + "description" : "Calculate the CAGR between \"from\" and \"to\" values over given \"years,\" then copy the result.", + "filename" : "calculate-CAGR.sh", + "hasArguments" : true, + "icon" : null, + "identifier" : "5acd1b95964396ecfbe1aee4841ddb0a", + "isTemplate" : false, + "language" : "bash", + "mode" : "compact", + "needsConfirmation" : null, + "packageName" : "Math", + "path" : "math\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Calculate CAGR Percentage", + "updatedAt" : "2024-11-18T09:32:56-06:00" + }, { "authors" : [ { @@ -4900,6 +4981,34 @@ "title" : "Uninstall with AppCleaner", "updatedAt" : "2021-03-18T14:47:46-03:00" }, + { + "authors" : [ + { + "name" : "Raphael-KR", + "url" : "https:\/\/github.com\/Raphael-KR" + } + ], + "createdAt" : "2025-08-23T18:49:50+09:00", + "currentDirectoryPath" : null, + "description" : "Toggle natural trackpad\/mouse scrolling setting for macOS 15.6.1+", + "filename" : "toggle-natural-scrolling-macos15.applescript", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "🖱" + }, + "identifier" : "3ccaeabe09d36538f01f592bc7b3e51e", + "isTemplate" : false, + "language" : "applescript", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "System", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Toggle Natural Scrolling (macOS 15+)", + "updatedAt" : "2025-08-25T09:08:49Z" + }, { "authors" : [ { @@ -4954,7 +5063,7 @@ "refreshTime" : null, "schemaVersion" : 1, "title" : "Dismiss Notifications", - "updatedAt" : "2023-05-08T06:42:50Z" + "updatedAt" : "2025-05-18T15:56:50-07:00" }, { "authors" : [ @@ -5954,6 +6063,34 @@ "title" : "Capture Screen Selection to Clipboard", "updatedAt" : "2021-08-07T19:28:35-07:00" }, + { + "authors" : [ + { + "name" : "StepaniaH", + "url" : "https:\/\/github.com\/StepaniaH" + } + ], + "createdAt" : "2024-12-16T23:44:40+08:00", + "currentDirectoryPath" : null, + "description" : "This is a simple Python script for recursively renaming video and picture files within a directory. Type the root directory's absolute path, and it will scan all the video and picture files in it and rename them according to the folder where they are located as the format `--`.", + "filename" : "rename-videos-pictures.py", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : "📂" + }, + "identifier" : "360d8e4290996f7b2b40dc6e388e30f2", + "isTemplate" : false, + "language" : "python", + "mode" : "fullOutput", + "needsConfirmation" : null, + "packageName" : "Rename Videos Pictures", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Rename Video", + "updatedAt" : "2024-12-18T23:56:00+08:00" + }, { "authors" : [ { @@ -6066,6 +6203,34 @@ "title" : "Minimize All Windows", "updatedAt" : "2022-02-21T17:47:42+01:00" }, + { + "authors" : [ + { + "name" : "AndriiBarabash", + "url" : "https:\/\/github.com\/AndriiBarabash" + } + ], + "createdAt" : "2025-03-10T00:16:14+01:00", + "currentDirectoryPath" : null, + "description" : "Put your Mac to sleep (in X minutes).", + "filename" : "sleep-timer.applescript", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : "😴" + }, + "identifier" : "ff43a32961d94da6001ad37ab79dd032", + "isTemplate" : false, + "language" : "applescript", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "System", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Sleep Timer", + "updatedAt" : "2025-03-10T10:57:38+01:00" + }, { "authors" : [ { @@ -6643,6 +6808,34 @@ "title" : "CPU Usage", "updatedAt" : "2024-04-22T06:25:56Z" }, + { + "authors" : [ + { + "name" : "aaqifshafi", + "url" : "https:\/\/github.com\/aaqifshafi" + } + ], + "createdAt" : "2025-10-24T17:32:55+05:30", + "currentDirectoryPath" : null, + "description" : "Kill running processes on the given ports", + "filename" : "kill-a-process-on-port.sh", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : "🚫" + }, + "identifier" : "71d24aef7a640392ef09a32800fd05e3", + "isTemplate" : false, + "language" : "bash", + "mode" : "compact", + "needsConfirmation" : null, + "packageName" : "Kill A Process On Port", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Kill a process on PORT", + "updatedAt" : "2025-10-24T17:32:55+05:30" + }, { "authors" : [ { @@ -6671,6 +6864,34 @@ "title" : "Flush DNS", "updatedAt" : "2021-06-14T14:36:05-03:00" }, + { + "authors" : [ + { + "name" : "Angelos Michalopoulos", + "url" : null + } + ], + "createdAt" : "2024-11-18T17:33:08+02:00", + "currentDirectoryPath" : null, + "description" : "Get One-Time Password (OTP) from Apple Password Manager", + "filename" : "otp.sh", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : "🔑" + }, + "identifier" : "7115fa08d182688291508d427a6ce246", + "isTemplate" : false, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "System", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "One-Time Password", + "updatedAt" : "2024-11-18T17:33:08+02:00" + }, { "authors" : [ { @@ -7019,6 +7240,29 @@ "title" : "Network Status", "updatedAt" : "2021-03-11T11:38:48-08:00" }, + { + "authors" : null, + "createdAt" : "2025-09-05T10:08:41-05:00", + "currentDirectoryPath" : null, + "description" : "Copies a summary of today's meetings to the clipboard.", + "filename" : "meeting_summary_script.swift", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "📝" + }, + "identifier" : "3542086084234d14d5e94041c7c2915b", + "isTemplate" : false, + "language" : "swift", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "System", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Copy Meeting Summary", + "updatedAt" : "2025-09-05T10:08:41-05:00" + }, { "authors" : null, "createdAt" : "2020-09-30T12:56:01+01:00", @@ -7070,6 +7314,34 @@ "title" : "Toggle Battery Charging", "updatedAt" : "2022-08-18T12:53:36Z" }, + { + "authors" : [ + { + "name" : "AlexGadd", + "url" : "https:\/\/raycast.com\/AlexGadd" + } + ], + "createdAt" : "2025-10-25T01:03:45+13:00", + "currentDirectoryPath" : null, + "description" : "Takes your clipboard then types each character in the clipboard", + "filename" : "paste-clipboard.sh", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "📋" + }, + "identifier" : "b6b6c23a4d07188552b17cba337131f8", + "isTemplate" : false, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Type Clipboard in Search", + "path" : "system\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Type Clipboard", + "updatedAt" : "2025-10-24T12:04:57Z" + }, { "authors" : null, "createdAt" : "2020-09-30T12:56:01+01:00", @@ -7269,6 +7541,41 @@ } ] }, + { + "name" : "Magic Keyboard Switcher", + "path" : "magic-keyboard-switcher", + "readme" : "system\/magic-keyboard-switcher\/README.md", + "scriptCommands" : [ + { + "authors" : [ + { + "name" : "blastik", + "url" : "https:\/\/github.com\/blastik" + } + ], + "createdAt" : "2025-09-24T17:50:16+02:00", + "currentDirectoryPath" : null, + "description" : "Switch a single magic keyboard between computers", + "filename" : "magic-keyboard-switcher.template.sh", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "images\/logo.png" + }, + "identifier" : "ebd6d04790272dd639e870be745a66db", + "isTemplate" : true, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "System", + "path" : "system\/magic-keyboard-switcher\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Magic Keyboard switcher", + "updatedAt" : "2025-09-24T17:59:07+02:00" + } + ] + }, { "name" : "Vpnutil", "path" : "vpnutil", @@ -7709,6 +8016,53 @@ } ] }, + { + "name" : "Ai", + "path" : "ai", + "scriptCommands" : [ + + ], + "subGroups" : [ + { + "name" : "Gemini", + "path" : "gemini", + "scriptCommands" : [ + { + "authors" : [ + { + "name" : "Est7", + "url" : "https:\/\/github.com\/est7" + }, + { + "name" : "Nimo Beeren", + "url" : "https:\/\/github.com\/nimobeeren" + } + ], + "createdAt" : "2025-04-24T11:38:46+08:00", + "currentDirectoryPath" : null, + "description" : "Open Gemini in Chrome browser and submit a prompt with optional selected text as context", + "filename" : "gemini.js", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : ".\/images\/icon-gemini.svg" + }, + "identifier" : "2a9c8b6698663cc8834ace7501bc9bec", + "isTemplate" : false, + "language" : "node", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Gemini", + "path" : "ai\/gemini\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Ask Gemini", + "updatedAt" : "2025-05-13T08:32:11Z" + } + ] + } + ] + }, { "name" : "Developer Utils", "path" : "developer-utils", @@ -8001,6 +8355,34 @@ "title" : "Is It Up?", "updatedAt" : "2023-02-21T22:56:29-05:00" }, + { + "authors" : [ + { + "name" : "Andrii Barabash", + "url" : "https:\/\/github.com\/AndriiBarabash" + } + ], + "createdAt" : "2025-09-27T20:40:28+02:00", + "currentDirectoryPath" : null, + "description" : "Toggles an SSH SOCKS proxy tunnel on and off.", + "filename" : "toggle_ssh_proxy_tunnel.template.sh", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "🔒" + }, + "identifier" : "aae74278107ffa5f34b3733eb2c8a813", + "isTemplate" : true, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Developer Utilities", + "path" : "developer-utils\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Toggle SSH SOCKS Tunnel", + "updatedAt" : "2025-09-29T13:44:50Z" + }, { "authors" : [ { @@ -10631,7 +11013,7 @@ "refreshTime" : null, "schemaVersion" : 1, "title" : "S3 Download", - "updatedAt" : "2024-06-14T12:34:01Z" + "updatedAt" : "2024-09-17T11:36:59+02:00" }, { "authors" : [ @@ -12109,6 +12491,34 @@ "schemaVersion" : 1, "title" : "Search and Copy First Related Emoji", "updatedAt" : "2020-11-28T12:02:29-05:00" + }, + { + "authors" : [ + { + "name" : "Tomohiro Nishimura", + "url" : "https:\/\/github.com\/Sixeight" + } + ], + "createdAt" : "2025-01-23T18:29:53+09:00", + "currentDirectoryPath" : null, + "description" : "Copy a random emoji to the clipboard.", + "filename" : "random-emoji.sh", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "🎲" + }, + "identifier" : "52479cec0740642d40a8902ee5efa1fe", + "isTemplate" : false, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Emojis", + "path" : "communication\/emojis\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Random Emoji", + "updatedAt" : "2025-03-25T19:27:06+09:00" } ] }, @@ -13691,7 +14101,7 @@ "refreshTime" : null, "schemaVersion" : 1, "title" : "Search Google Maps", - "updatedAt" : "2022-11-17T10:35:57+01:00" + "updatedAt" : "2024-12-05T12:33:50+01:00" }, { "authors" : [ @@ -13702,7 +14112,7 @@ ], "createdAt" : "2021-07-13T21:10:54-07:00", "currentDirectoryPath" : null, - "description" : "Search for Script Commands in the [Unofficial Raycast Script Commands Marketplace](https:\/\/scriptcommands.com).", + "description" : "Search for Script Commands in the [Unofficial Raycast Script Commands Marketplace](https:\/\/scriptcommands.alexandru.so).", "filename" : "search-script-command-marketplace.sh", "hasArguments" : true, "icon" : { @@ -13719,7 +14129,7 @@ "refreshTime" : null, "schemaVersion" : 1, "title" : "Search Script Command", - "updatedAt" : "2024-02-19T19:37:34-08:00" + "updatedAt" : "2024-12-04T11:52:04+01:00" }, { "authors" : [ @@ -13833,6 +14243,34 @@ "title" : "Search LinkedIn", "updatedAt" : "2021-01-24T20:18:06Z" }, + { + "authors" : [ + { + "name" : "lemorage", + "url" : "https:\/\/raycast.com\/lemorage" + } + ], + "createdAt" : "2024-09-19T08:42:28+08:00", + "currentDirectoryPath" : null, + "description" : "Search Rust documentation", + "filename" : "search-rust-docs.sh", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : "images\/rust.png" + }, + "identifier" : "2f4472d80148aaadabf915529d6f68c9", + "isTemplate" : false, + "language" : "bash", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Web Searches", + "path" : "web-searches\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Search Rust Documentation", + "updatedAt" : "2024-09-19T08:42:28+08:00" + }, { "authors" : [ { @@ -15545,6 +15983,36 @@ } ] }, + { + "name" : "Find My", + "path" : "find-my", + "readme" : "apps\/find-my\/README.md", + "scriptCommands" : [ + { + "authors" : null, + "createdAt" : "2025-06-12T06:53:33-07:00", + "currentDirectoryPath" : null, + "description" : null, + "filename" : "fmp.js", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "images\/find-my-icon.png" + }, + "identifier" : "206851d53c58c074472f7acb20ceeb08", + "isTemplate" : false, + "language" : "node", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "FindMy", + "path" : "apps\/find-my\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Find My Phone (Auto Sound)", + "updatedAt" : "2025-06-12T06:53:33-07:00" + } + ] + }, { "name" : "Bear", "path" : "bear", @@ -15781,6 +16249,40 @@ } ] }, + { + "name" : "Claude", + "path" : "claude", + "scriptCommands" : [ + { + "authors" : [ + { + "name" : "Nimo Beeren", + "url" : "https:\/\/github.com\/nimobeeren" + } + ], + "createdAt" : "2024-08-07T21:01:27+02:00", + "currentDirectoryPath" : null, + "description" : "Open Claude in Chrome browser and submit a prompt", + "filename" : "claude.js", + "hasArguments" : true, + "icon" : { + "dark" : null, + "light" : "✨" + }, + "identifier" : "cd9356ee79829cf478bdcd127c673a0c", + "isTemplate" : false, + "language" : "node", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Claude", + "path" : "apps\/claude\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Ask Claude", + "updatedAt" : "2024-08-30T13:09:27Z" + } + ] + }, { "name" : "TunnelBlick", "path" : "tunnelblick", @@ -17835,6 +18337,34 @@ "title" : "Add Item to Reading List", "updatedAt" : "2021-03-09T15:09:02Z" }, + { + "authors" : [ + { + "name" : "Michael Bianco", + "url" : "https:\/\/github.com\/iloveitaly" + } + ], + "createdAt" : "2024-08-21T08:30:36-06:00", + "currentDirectoryPath" : null, + "description" : "Download the currently active tab's URL.", + "filename" : "safari-download-url.applescript", + "hasArguments" : false, + "icon" : { + "dark" : null, + "light" : "images\/safari.png" + }, + "identifier" : "3fe3287d74734aa1adaf454aa6be4dce", + "isTemplate" : false, + "language" : "applescript", + "mode" : "silent", + "needsConfirmation" : null, + "packageName" : "Safari", + "path" : "apps\/safari\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Download Current URL", + "updatedAt" : "2024-08-30T13:02:53Z" + }, { "authors" : [ { @@ -18347,6 +18877,10 @@ { "name" : "Ross Zurowski", "url" : "https:\/\/github.com\/rosszurowski" + }, + { + "name" : "Daniel Schoemer", + "url" : "https:\/\/github.com\/quatauta" } ], "createdAt" : "2021-06-14T15:33:18-04:00", @@ -18368,7 +18902,7 @@ "refreshTime" : null, "schemaVersion" : 1, "title" : "Switch Account", - "updatedAt" : "2021-06-14T15:33:18-04:00" + "updatedAt" : "2024-08-19T14:49:32+02:00" } ] }, @@ -23277,6 +23811,32 @@ } ] }, + { + "name" : "Lowfi", + "path" : "lowfi", + "scriptCommands" : [ + { + "authors" : null, + "createdAt" : "2024-11-24T04:45:52+01:00", + "currentDirectoryPath" : null, + "description" : null, + "filename" : "lowfi.sh", + "hasArguments" : false, + "icon" : null, + "identifier" : "afcc05d7a53abe6543c98f59f23bb7e5", + "isTemplate" : false, + "language" : "bash", + "mode" : "compact", + "needsConfirmation" : null, + "packageName" : "Music", + "path" : "media\/lowfi\/", + "refreshTime" : null, + "schemaVersion" : 1, + "title" : "Lowfi", + "updatedAt" : "2024-12-11T20:08:12Z" + } + ] + }, { "name" : "Apple Tv", "path" : "apple-tv", @@ -23702,6 +24262,16 @@ "icon" : "icon-swift.png", "name" : "swift" }, + { + "displayName" : "Python", + "icon" : "icon-python.png", + "name" : "python" + }, + { + "displayName" : "Ruby", + "icon" : "icon-ruby.png", + "name" : "ruby" + }, { "displayName" : "AppleScript", "icon" : "icon-applescript.png", @@ -23711,21 +24281,11 @@ "displayName" : "Node", "icon" : "icon-nodejs.png", "name" : "node" - }, - { - "displayName" : "Ruby", - "icon" : "icon-ruby.png", - "name" : "ruby" - }, - { - "displayName" : "Python", - "icon" : "icon-python.png", - "name" : "python" } ], "metadata" : [ ], - "totalScriptCommands" : 824, - "updatedAt" : "2024-08-05T15:50:30Z" + "totalScriptCommands" : 843, + "updatedAt" : "2025-10-24T12:06:29Z" } \ No newline at end of file diff --git a/commands/math/calculate-CAGR.sh b/commands/math/calculate-CAGR.sh new file mode 100755 index 000000000..671876a94 --- /dev/null +++ b/commands/math/calculate-CAGR.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Calculate CAGR Percentage +# @raycast.mode compact +# @raycast.packageName Math + +# Optional parameters: +# @raycast.argument1 { "type": "text", "placeholder": "From" } +# @raycast.argument2 { "type": "text", "placeholder": "To" } +# @raycast.argument3 { "type": "text", "placeholder": "Years" } + +# Documentation: +# @raycast.author Samuel Barton +# @raycast.authorURL https://github.com/samueldbarton +# @raycast.description Calculate the CAGR between "from" and "to" values over given "years," then copy the result. + +CAGR=$(echo "scale=4; e( l($2 / $1) / $3 ) - 1" | bc -l) +CAGR=$(echo "scale=2; $CAGR * 100" | bc -l) +CAGR=$(printf "%.2f" "$CAGR") +echo "$CAGR" | pbcopy +echo "The CAGR from $1 to $2 over $3 years is $CAGR%. CAGR copied to clipboard." \ No newline at end of file diff --git a/commands/media/lowfi/lowfi.sh b/commands/media/lowfi/lowfi.sh new file mode 100755 index 000000000..04248cf5c --- /dev/null +++ b/commands/media/lowfi/lowfi.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Note: lowfi and WezTerm required +# +# Install lowfi at https://github.com/talwat/lowfi +# or by cargo: cargo install lowfi +# +# Install WezTerm at https://wezfurlong.org/wezterm/install/macos.html +# or by homebrew: brew install --cask wezterm + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Lowfi +# @raycast.mode compact +# @raycast.packageName Music + +# Optional parameters: +# @raycast.icon + +export PATH="$HOME/.cargo/bin:$PATH" + +# Check if lowfi is already running +if pgrep -f "lowfi$" > /dev/null; then + echo "Found existing lowfi process" + + # Use AppleScript to find and focus the lowfi window + osascript -e ' + -- First activate WezTerm application + tell application "WezTerm" to activate + + -- Then focus the specific window + tell application "System Events" + tell process "wezterm-gui" + -- Get all windows + set allWindows to every window + repeat with w in allWindows + if title of w contains "lowfi" then + -- Try multiple methods to bring window to front + set frontmost to true + set value of attribute "AXMain" of w to true + perform action "AXRaise" of w + set position of w to {0, 40} + -- Click the window to ensure focus + click w + return "Found and focused lowfi window" + end if + end repeat + end tell + end tell + return "Could not find lowfi window" + ' +else + echo "Starting new lowfi instance" + /Applications/WezTerm.app/Contents/MacOS/wezterm start -- lowfi & +fi diff --git a/commands/navigation/hide-application.applescript b/commands/navigation/hide-application.applescript new file mode 100755 index 000000000..f9574a4fd --- /dev/null +++ b/commands/navigation/hide-application.applescript @@ -0,0 +1,18 @@ +#!/usr/bin/osascript + +# @raycast.schemaVersion 1 +# @raycast.author Chris Bailey +# @raycast.authorURL https://raycast.com/that70schris +# @raycast.description Easily hide your foremost application +# @raycast.packageName Navigation +# @raycast.title Hide Current Application +# @raycast.mode silent +# @raycast.icon 🫥 + +tell application "System Events" + tell (first process whose frontmost is true) + set _name to displayed name + end tell + + set visible of application process _name to false +end tell diff --git a/commands/system/dismiss-notifications.applescript b/commands/system/dismiss-notifications.applescript index 5b3b18ed3..2c3c2716b 100755 --- a/commands/system/dismiss-notifications.applescript +++ b/commands/system/dismiss-notifications.applescript @@ -14,18 +14,36 @@ # @raycast.author benyn # @raycast.authorURL github.com/benyn -tell application "System Events" - tell process "NotificationCenter" - if not (window "Notification Center" exists) then return - set alertGroups to groups of first UI element of first scroll area of first group of window "Notification Center" - repeat with aGroup in alertGroups - try - perform (first action of aGroup whose name contains "Close" or name contains "Clear") - on error errMsg - log errMsg - end try +tell application "System Events" to tell process "NotificationCenter" + -- Exit if there are no visible notifications. + if not (window "Notification Center" exists) then return + + -- `notificationContainer` refers to the UI element (of class `group`) holding notifications and can be either: + -- - A single, individual notification, or + -- - A collection of individual notifications and groups of stacked notifications. + set notificationContainer to a reference to group 1 of scroll area 1 of group 1 of group 1 of window "Notification Center" + + -- If it is a collection, close notifications and groups in reverse order to avoid index changes. + set notificationGroups to a reference to groups of notificationContainer + repeat with i from (number of notificationGroups) to 1 by -1 + set g to item i of notificationGroups + repeat with a in (actions of g whose description is "Close" or description starts with "Clear") + -- Ignore errors that happen if the last remaining item is an individual notification, + -- and `notificationCenter` is no longer a `group` of `group`s. + -- The final remaining notification will be closed in the second repeat statement below. + ignoring application responses + perform a + end ignoring end repeat - -- Show no message on success - return "" - end tell + end repeat + + -- Close the `notificationContainer` itself. This handles: + -- - A single, individual notification that was `notificationContainer` from the start, or + -- - The last remaining individual notification after the loop above. + repeat with a in (actions of notificationContainer whose description is "Close" or description starts with "Clear") + perform a + end repeat end tell + +-- Prevent Raycast from displaying the successful result message. +return diff --git a/commands/system/kill-a-process-on-port.sh b/commands/system/kill-a-process-on-port.sh new file mode 100755 index 000000000..c97f38f87 --- /dev/null +++ b/commands/system/kill-a-process-on-port.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Kill a process on PORT +# @raycast.mode compact + +# Optional parameters: +# @raycast.icon 🚫 +# @raycast.argument1 { "type": "text", "placeholder": "Ports (e.g. 3000 5000)" } + +# Documentation: +# @raycast.description Kill running processes on the given ports +# @raycast.author aaqifshafi +# @raycast.authorURL https://github.com/aaqifshafi + +if [ $# -eq 0 ]; then + echo "Provide at least one port number." + exit 1 +fi + +for port in "$@" +do + pid=$(lsof -ti tcp:$port) + if [ -n "$pid" ]; then + kill -9 $pid + echo "Killed process $pid on port $port" + else + echo "No process found on port $port" + fi +done diff --git a/commands/system/magic-keyboard-switcher/README.md b/commands/system/magic-keyboard-switcher/README.md new file mode 100644 index 000000000..a6ce88ad3 --- /dev/null +++ b/commands/system/magic-keyboard-switcher/README.md @@ -0,0 +1,21 @@ +# magic-keyboard-switcher +Script command to easility switch the bluetooth connectivity of a single Magic Keyboard between several computers. + +## How to setup + +1. Install [blueutil](https://github.com/toy/blueutil). You can use [brew](https://brew.sh/) - `brew install blueutil` +2. Find out your Magic Keyboard bluetooth MAC address. With the keyboard connected, keep the Option key pressed while you click on your status bar bluetooth icon. +
+ Bluetooth menu +
+ +3. Open the script file, set your Magic Keyboard bluetooth MAC address in the `BTMAC` variable and review the blueutil binary location set in the `BIN` variable. +4. Remove `.template` from the file name. +5. Say Ok when prompted about giving Bluetooth permissions to Raycast after executing the script command. + +## How to switch the Magic Keyboard between computers + +It's desirable to have Raycast and this script command installed in both computers. + +First, run the script command in the one that has the keyboard currently connected. It will disconnect it and make it discoverable. +Second, run the script command in the other computer. It should connect it. diff --git a/commands/system/magic-keyboard-switcher/images/bluetooth menu.png b/commands/system/magic-keyboard-switcher/images/bluetooth menu.png new file mode 100644 index 000000000..de5d4070a Binary files /dev/null and b/commands/system/magic-keyboard-switcher/images/bluetooth menu.png differ diff --git a/commands/system/magic-keyboard-switcher/images/logo.png b/commands/system/magic-keyboard-switcher/images/logo.png new file mode 100644 index 000000000..200a262fe Binary files /dev/null and b/commands/system/magic-keyboard-switcher/images/logo.png differ diff --git a/commands/system/magic-keyboard-switcher/magic-keyboard-switcher.template.sh b/commands/system/magic-keyboard-switcher/magic-keyboard-switcher.template.sh new file mode 100755 index 000000000..e27d2b939 --- /dev/null +++ b/commands/system/magic-keyboard-switcher/magic-keyboard-switcher.template.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Dependency: This script requires `blueutil` cli installed: https://github.com/toy/blueutil +# Install via homebrew: `brew install blueutil` + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Magic Keyboard switcher +# @raycast.mode silent + +# Optional parameters: +# @raycast.icon images/logo.png +# @raycast.packageName System + +# Documentation: +# @raycast.author blastik +# @raycast.authorURL https://github.com/blastik +# @raycast.description Switch a single magic keyboard between computers + +# blueutil location +BIN=/opt/homebrew/bin/blueutil + +# Your Magic Keyboard MAC Address +BTMAC='XX:XX:XX:XX:XX:XX' + +CMD_VAL="$($BIN --is-connected $BTMAC)" +CMD_UNPAIR="$BIN --unpair $BTMAC" +CMD_PAIR="$BIN --pair $BTMAC" +CMD_CONN="$BIN --connect $BTMAC" + +if ! command -v blueutil &> /dev/null; then + echo "blueutil command is required (https://github.com/toy/blueutil)."; + exit 1; +fi + +if [[ "$CMD_VAL" -eq 1 ]]; then + echo "Connected to $BTMAC" + echo "Going to disconnect $BTMAC" + $($CMD_UNPAIR) + if [[ $? -eq 0 ]]; then + echo "Disconnected from $BTMAC" + else + echo "Failed to disconnect from $BTMAC" + exit 1 + fi +else + echo "Not connected to $BTMAC" + $($CMD_PAIR) + sleep 1 + $($CMD_CONN) + if [[ $? -eq 0 ]]; then + echo "Connected to $BTMAC" + else + echo "Failed to connect to $BTMAC" + exit 1 + fi +fi diff --git a/commands/system/meeting_summary_script.swift b/commands/system/meeting_summary_script.swift new file mode 100755 index 000000000..57053400c --- /dev/null +++ b/commands/system/meeting_summary_script.swift @@ -0,0 +1,92 @@ +#!/usr/bin/swift + +// Required parameters: +// @raycast.schemaVersion 1 +// @raycast.title Copy Meeting Summary +// @raycast.mode silent +// @raycast.packageName System +// +// Optional parameters: +// @raycast.icon 📝 +// +// Documentation: +// @raycast.description Copies a summary of today's meetings to the clipboard. + +import AppKit +import EventKit + +let now = Date() +let calendar: Calendar = .current + +do { + let today: (startDate: Date, endDate: Date, events: [EKEvent]) + + do { + // Retrieve the range for today (from 00:00 to 23:59) + today.startDate = calendar.startOfDay(for: now) + today.endDate = calendar.date(byAdding: .day, value: 1, to: today.startDate)! + + // Retrieve all the events for today + let store = EKEventStore() + let predicate = store.predicateForEvents(withStart: today.startDate, end: today.endDate, calendars: nil) + today.events = store.events(matching: predicate) + .filter { !$0.isAllDay } // Exclude all-day events + .sorted { $0.startDate < $1.startDate } // Sort by start time + } + + let summary: String + if today.events.isEmpty { + summary = "No meetings scheduled for today." + } else { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .full + dateFormatter.timeStyle = .none + let todayString = dateFormatter.string(from: now) + + let timeFormatter = DateFormatter() + timeFormatter.dateStyle = .none + timeFormatter.timeStyle = .short + timeFormatter.timeZone = TimeZone.current + + let meetingList = today.events.map { event in + let startTime = timeFormatter.string(from: event.startDate) + let title = event.title ?? "Untitled Event" + let timezone = timeFormatter.timeZone.abbreviation() ?? "" + + var meetingInfo = timezone.isEmpty ? "• \(startTime): \(title)" : "• \(startTime) \(timezone): \(title)" + + // Add location if available + if let location = event.location, !location.isEmpty { + meetingInfo += " (\(location))" + } + + // Add notes if available (first line only to keep it concise) + if let notes = event.notes, !notes.isEmpty { + let firstLine = notes.components(separatedBy: .newlines).first ?? "" + let cleanedNotes = firstLine.trimmingCharacters(in: .whitespacesAndNewlines) + // Filter out junk characters (like the -::~: pattern) + let validNotes = cleanedNotes.filter { char in + char.isLetter || char.isNumber || char.isWhitespace || char.isPunctuation && !"-:~".contains(char) + } + if !validNotes.isEmpty && validNotes.count <= 100 && !validNotes.allSatisfy({ "-:~".contains($0) }) { + meetingInfo += "\n Notes: \(String(validNotes))" + } + } + + return meetingInfo + }.joined(separator: "\n") + + let meetingCount = today.events.count + + summary = """ +Meetings: +\(meetingList) +""" + } + + NSPasteboard.general.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) + NSPasteboard.general.setString(summary, forType: NSPasteboard.PasteboardType.string) + print("Copied meeting summary to clipboard") +} catch { + print("Error retrieving calendar events: \(error)") +} \ No newline at end of file diff --git a/commands/system/otp.sh b/commands/system/otp.sh new file mode 100755 index 000000000..830ba3ded --- /dev/null +++ b/commands/system/otp.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Dependency: This script requires `apw`, `jq` and `awk` to be installed and in $PATH +# +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title One-Time Password +# @raycast.mode silent +# +# Optional parameters: +# @raycast.icon 🔑 +# @raycast.packageName System +# @raycast.argument1 { "type": "text", "placeholder": "Domain" } +# @raycast.argument2 { "type": "text", "placeholder": "Username Index", "optional": true } +# +# @raycast.description Get One-Time Password (OTP) from Apple Password Manager +# @raycast.author Angelos Michalopoulos +# @raycase.authorURL https://github.com/miagg + +if ! command -v apw &> /dev/null || ! command -v jq &> /dev/null || ! command -v awk &> /dev/null; then + echo "This function requires apw, jq and awk to be installed" + exit 1 +fi +UINDEX=$((${2:-1} - 1)) +CODES=$(apw otp get "$1" 2>/dev/null) +STATUS=$? +# ✋ If return code 9, not authenticated, run apw auth +if [ $STATUS -eq 9 ]; then + echo "Please authenticate first by running 'apw auth'" + exit 1 +fi +# ✋ If return code 3, domain not found, alert user +if [ $STATUS -eq 3 ]; then + echo "Domain $1 not found" + exit 1 +fi +# Grab available OTP codes for domain +CODES_COUNT=$(echo $CODES | jq '.results | length') +if [ $CODES_COUNT -gt 1 ]; then + CODE=$(echo $CODES | jq -r ".results[$UINDEX].code") + USERNAME=$(echo $CODES | jq -r ".results[$UINDEX].username") + if [ "$CODE" == "null" ]; then + echo "Please provide an index between 1 and $CODES_COUNT" + exit 1 + fi +else + CODE=$(echo $CODES | jq -r '.results[0].code') + USERNAME=$(echo $CODES | jq -r ".results[0].username") +fi +echo $CODE | pbcopy +echo "OTP code for $USERNAME copied to clipboard" \ No newline at end of file diff --git a/commands/system/paste-clipboard.sh b/commands/system/paste-clipboard.sh new file mode 100755 index 000000000..b2b738d0d --- /dev/null +++ b/commands/system/paste-clipboard.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Type Clipboard +# @raycast.mode silent + +# Optional parameters: +# @raycast.icon 📋 +# @raycast.packageName Type Clipboard in Search + +# Documentation: +# @raycast.description Takes your clipboard then types each character in the clipboard +# @raycast.author AlexGadd +# @raycast.authorURL https://raycast.com/AlexGadd + +osascript -e 'set clipboardContent to the clipboard' \ +-e 'set charCount to count of characters of clipboardContent' \ +-e 'tell application "System Events"' \ +-e ' repeat with i from 1 to charCount' \ +-e ' set theChar to character i of clipboardContent' \ +-e ' keystroke theChar' \ +-e ' end repeat' \ +-e 'end tell' diff --git a/commands/system/rename-videos-pictures.py b/commands/system/rename-videos-pictures.py new file mode 100755 index 000000000..f4f25c862 --- /dev/null +++ b/commands/system/rename-videos-pictures.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Rename Video +# @raycast.mode fullOutput + +# Optional parameters: +# @raycast.icon 📂 +# @raycast.argument1 { "type": "text", "placeholder": "Directory's absolute path", "optional": false } +# @raycast.argument2 { "type": "text", "placeholder": "Type (video/image/all)", "optional": true } +# @raycast.argument3 { "type": "text", "placeholder": "Dry Run (true/false)", "optional": true } + +# Documentation: +# @raycast.description This is a simple Python script for recursively renaming video and picture files within a directory. Type the root directory's absolute path, and it will scan all the video and picture files in it and rename them according to the folder where they are located as the format `--`. +# @raycast.author StepaniaH +# @raycast.authorURL https://github.com/StepaniaH + +import os +import sys +import datetime +import argparse +import re + +class RenameFilesAsDate: + def __init__(self, root_directory): + self.root_directory = os.path.expanduser(root_directory) + self.error_files = {} + self.video_extensions = ('.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v', '.3gp') + self.image_extensions = ('.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.svg', '.heic') + + def rename_files(self, file_type='all', dry_run=False): + current_date = datetime.datetime.now().strftime("%m%d") + self._process_directory(self.root_directory, current_date, file_type, dry_run) + + if self.error_files: + print("\nThe following files could not be renamed:") + for original_path, error in self.error_files.items(): + print(f"{original_path}: {error}") + + def _is_already_renamed(self, filename, current_date): + """Check if the file has been named according to the target format""" + base_name = os.path.splitext(filename)[0] + pattern = re.compile(f'^.*-{current_date}-\\d+$') + return bool(pattern.match(base_name)) + + def _get_max_sequence_number(self, directory, current_date): + """Get the largest serial number existing in the current directory""" + max_seq = 0 + pattern = re.compile(f'^.*-{current_date}-(\\d+)$') + + for entry in os.listdir(directory): + match = pattern.match(os.path.splitext(entry)[0]) + if match: + seq_num = int(match.group(1)) + max_seq = max(max_seq, seq_num) + return max_seq + + def _process_directory(self, directory, current_date, file_type, dry_run): + folder_name = os.path.basename(directory) + supported_extensions = self._get_supported_extensions(file_type) + + # First collect the files that need to be renamed + files_to_rename = [] + for entry in os.listdir(directory): + entry_path = os.path.join(directory, entry) + + if os.path.isfile(entry_path): + if entry.lower().endswith(supported_extensions): + if not self._is_already_renamed(entry, current_date): + files_to_rename.append(entry) + elif os.path.isdir(entry_path): + self._process_directory(entry_path, current_date, file_type, dry_run) + + if files_to_rename: + # Get the largest serial number existing in the current directory + count = self._get_max_sequence_number(directory, current_date) + 1 + total_files = len(files_to_rename) + num_digits = len(str(total_files + count - 1)) + + # Rename collected files + for entry in sorted(files_to_rename): # Sort to ensure consistent rename order + entry_path = os.path.join(directory, entry) + new_name = f"{folder_name}-{current_date}-{count:0{num_digits}d}{os.path.splitext(entry)[1].lower()}" + new_path = os.path.join(directory, new_name) + + if dry_run: + print(f"Would rename: {entry} -> {new_name}") + else: + try: + os.rename(entry_path, new_path) + print(f"Renamed: {entry} -> {new_name}") + except OSError as e: + self.error_files[entry_path] = f"Rename failed: {str(e)}" + continue + count += 1 + + def _get_supported_extensions(self, file_type): + if file_type == 'video': + return self.video_extensions + elif file_type == 'image': + return self.image_extensions + return self.video_extensions + self.image_extensions + +def main(): + args = sys.argv[1:] + + directory = None + file_type = 'all' + dry_run = False + + if len(args) >= 1: + directory = args[0] + if len(args) >= 2 and args[1]: + file_type = args[1] + if len(args) >= 3 and args[2]: + dry_run = args[2].lower() == 'true' + + if not directory: + print("Error: Directory argument is required.") + sys.exit(1) + + renamer = RenameFilesAsDate(directory) + renamer.rename_files(file_type, dry_run) + +if __name__ == '__main__': + main() diff --git a/commands/system/sleep-timer.applescript b/commands/system/sleep-timer.applescript new file mode 100755 index 000000000..8cd9ae4ee --- /dev/null +++ b/commands/system/sleep-timer.applescript @@ -0,0 +1,22 @@ +#!/usr/bin/osascript + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Sleep Timer +# @raycast.mode silent +# @raycast.description Put your Mac to sleep (in X minutes). +# @raycast.packageName System + +# Optional parameters: +# @raycast.icon 😴 +# @raycast.argument1 { "optional": true, "type": "text", "placeholder": "(in) minutes" } + +# Documentation: +# @raycast.author AndriiBarabash +# @raycast.authorURL https://github.com/AndriiBarabash + +on run argv + delay (item 1 of argv) * 60 + tell application "Finder" to sleep +end run + diff --git a/commands/system/toggle-natural-scrolling-macos15.applescript b/commands/system/toggle-natural-scrolling-macos15.applescript new file mode 100755 index 000000000..710b7e435 --- /dev/null +++ b/commands/system/toggle-natural-scrolling-macos15.applescript @@ -0,0 +1,69 @@ +#!/usr/bin/osascript + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Toggle Natural Scrolling (macOS 15+) +# @raycast.mode silent +# @raycast.packageName System + +# Optional parameters: +# @raycast.icon 🖱 +# @raycast.author Raphael-KR +# @raycast.authorURL https://github.com/Raphael-KR +# @raycast.description Toggle natural trackpad/mouse scrolling setting for macOS 15.6.1+ + +set macVersion to system version of (system info) + +if macVersion is greater than or equal to "15" then + try + set isNaturalScrolling to (do shell script "defaults read .GlobalPreferences 'com.apple.swipescrolldirection' 2>/dev/null || echo '1'") + + if isNaturalScrolling is "1" then + do shell script "defaults write .GlobalPreferences 'com.apple.swipescrolldirection' -bool NO" + display notification "Natural Scrolling: OFF" with title "Trackpad Settings" + else + do shell script "defaults write .GlobalPreferences 'com.apple.swipescrolldirection' -bool YES" + display notification "Natural Scrolling: ON" with title "Trackpad Settings" + end if + + do shell script "/System/Library/PrivateFrameworks/SystemAdministration.framework/Resources/activateSettings -u" + + on error e + display notification "Using GUI method as fallback" with title "Trackpad Settings" + + tell application "System Settings" + activate + set current pane to pane "com.apple.preference.trackpad" + end tell + + delay 0.6 + + tell application "System Events" + tell process "System Settings" + click radio button 2 of tab group 1 of window "Trackpad" + click checkbox 1 of tab group 1 of window "Trackpad" + end tell + end tell + + tell application "System Settings" to quit + end try + +else + display notification "For macOS 14 and earlier, use the original script" with title "Compatibility Note" + + tell application "System Settings" + activate + set current pane to pane "com.apple.preference.trackpad" + end tell + + delay 0.6 + + tell application "System Events" + tell process "System Settings" + click radio button 2 of tab group 1 of window "Trackpad" + click checkbox 1 of tab group 1 of window "Trackpad" + end tell + end tell + + tell application "System Settings" to quit +end if diff --git a/commands/web-searches/google-maps.sh b/commands/web-searches/google-maps.sh index 345205af0..d856d504b 100755 --- a/commands/web-searches/google-maps.sh +++ b/commands/web-searches/google-maps.sh @@ -14,10 +14,14 @@ first_argument=${1// /+} second_argument=${2// /+} -if [ "$1" = "" ]; then - open "https://www.google.com/maps" -elif [ "$2" = "" ]; then - open "https://www.google.com/maps/search/$first_argument" +if [ "$1" != "" ]; then + if [ "$2" = "" ]; then + open "https://www.google.com/maps/dir/?api=1&origin=$first_argument" + else + open "https://www.google.com/maps/dir/?api=1&origin=$first_argument&destination=$second_argument" + fi +elif [ "$1" = "" ] && [ "$2" != "" ]; then + open "https://www.google.com/maps/dir/?api=1&origin=Current+Location&destination=$second_argument" else - open "https://www.google.com/maps/dir/$first_argument/$second_argument" + open "https://www.google.com/maps" fi diff --git a/commands/web-searches/images/rust.png b/commands/web-searches/images/rust.png new file mode 100644 index 000000000..1eaa7709d Binary files /dev/null and b/commands/web-searches/images/rust.png differ diff --git a/commands/web-searches/search-rust-docs.sh b/commands/web-searches/search-rust-docs.sh new file mode 100755 index 000000000..7c28cf948 --- /dev/null +++ b/commands/web-searches/search-rust-docs.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Required parameters: +# @raycast.schemaVersion 1 +# @raycast.title Search Rust Documentation +# @raycast.mode silent +# +# Optional parameters: +# @raycast.packageName Web Searches +# @raycast.icon images/rust.png +# @raycast.argument1 { "type": "text", "placeholder": "Name", "percentEncoded": true } +# +# Example query: +# Searching for `expect` will open this link in your browser: +# `https://doc.rust-lang.org/std/?search=expect`. +# +# Documentation: +# @raycast.author lemorage +# @raycast.authorURL https://raycast.com/lemorage +# @raycast.description Search Rust documentation + +query=$(echo "$1" | sed 's/ /%20/g') + +open "https://doc.rust-lang.org/std/?search=$query" diff --git a/commands/web-searches/search-script-command-marketplace.sh b/commands/web-searches/search-script-command-marketplace.sh index 9d0997592..5280ec224 100755 --- a/commands/web-searches/search-script-command-marketplace.sh +++ b/commands/web-searches/search-script-command-marketplace.sh @@ -9,7 +9,7 @@ # @raycast.icon images/marketplace-logo.png # @raycast.argument1 { "type": "text", "placeholder": "query"} # @raycast.packageName Web Searches -# @raycast.description Search for Script Commands in the [Unofficial Raycast Script Commands Marketplace](https://scriptcommands.com). +# @raycast.description Search for Script Commands in the [Unofficial Raycast Script Commands Marketplace](https://scriptcommands.alexandru.so). # Documentation: # @raycast.author Alexandru Turcanu diff --git a/documentation/OUTPUTMODES.md b/documentation/OUTPUTMODES.md index cd98677fb..b4f2f0ca2 100644 --- a/documentation/OUTPUTMODES.md +++ b/documentation/OUTPUTMODES.md @@ -23,7 +23,7 @@ In `inline` mode, the first line of output will be directly shown in the command Please note that long-running tasks generating a lot of partial data are not supported for `compact`, `silent`, and `inline` modes. For example, the `zip` command generates a lot of partial logs when compressing folders with many files. Scripts using `zip` won't work on `compact`, `silent`, and `inline`; but they will work in `fullOutput`. To make it work in the other modes you need to use the `zip -q` flag. -# ANSII Supported Colors 🎨 +# ANSI Supported Colors 🎨 We support colors for `inline` and `fullOutput` mode scripts for you to customize generated output by changing its background and foreground color.