diff --git a/extensions/ghostty/.gitignore b/extensions/ghostty/.gitignore new file mode 100644 index 00000000..dd6e803c --- /dev/null +++ b/extensions/ghostty/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.log +.DS_Store diff --git a/extensions/ghostty/CHANGELOG.md b/extensions/ghostty/CHANGELOG.md new file mode 100644 index 00000000..4f45e4e2 --- /dev/null +++ b/extensions/ghostty/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## 0.1.0 + +- Initial Vicinae extension for Ghostty on Linux. +- Added commands for windows, tabs, workspaces, launch configurations, path opening, and config management. +- Added Ghostty icon and Linux/KDE-focused tab support. diff --git a/extensions/ghostty/LICENSE b/extensions/ghostty/LICENSE new file mode 100644 index 00000000..216e5684 --- /dev/null +++ b/extensions/ghostty/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 domainus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extensions/ghostty/README.md b/extensions/ghostty/README.md new file mode 100644 index 00000000..da4a6bc2 --- /dev/null +++ b/extensions/ghostty/README.md @@ -0,0 +1,54 @@ +# Ghostty for Vicinae + +Control [Ghostty](https://ghostty.org/) from Vicinae on Linux. + +## Commands + +- **New Ghostty Window** — opens a new Ghostty window. +- **New Ghostty Tab** — focuses Ghostty and sends `Ctrl+Shift+T` to create a tab. +- **Focus Ghostty Window** — lists visible Ghostty windows and focuses the selected one. +- **Open Ghostty Launch Configuration** — runs saved YAML launch configurations. +- **Open Ghostty Workspace** — scans a parent directory for Git repositories and opens one in Ghostty. +- **Open with Ghostty** — opens a provided path in Ghostty. +- **Manage Ghostty Config** — view, edit, validate, and open your Ghostty config file. + +## Requirements + +- Ghostty installed and available at `/usr/bin/ghostty`, or set the **Ghostty Binary** preference. +- `wmctrl` for focusing Ghostty windows. +- KDE/KWin `qdbus` plus `ydotool` for the **New Ghostty Tab** command on Wayland. + +Ghostty does not currently expose a stable Linux CLI action for creating a tab in an existing window. This extension therefore focuses Ghostty and sends the standard Ghostty new-tab shortcut (`Ctrl+Shift+T`). If that is not available, the command opens Ghostty and shows a failure toast. + +## Launch configurations + +Launch configurations are YAML files stored in: + +```text +~/.config/vicinae/ghostty-launch-configs +``` + +Example: + +```yaml +name: dev +windows: + - tabs: + - title: Shell + cwd: ~/Developer/my-project + commands: + - pwd + - git status +``` + +Commands in launch configurations are executed in Ghostty with `&&`. Only use launch configurations you trust. + +## Preferences + +- **Workspaces Parent Directory** — parent directory to scan for Git repositories. Defaults to `~/Developer`. +- **Workspace Scan Depth** — maximum depth for repository scanning. +- **Ghostty Binary** — path to the Ghostty executable. + +## Notes + +The window focus command uses `wmctrl`, which may be limited on some Wayland compositors. KDE Plasma works best because the tab command can use KWin scripting to focus Ghostty before sending the shortcut. diff --git a/extensions/ghostty/assets/extension-icon.png b/extensions/ghostty/assets/extension-icon.png new file mode 100644 index 00000000..b51b8d7d Binary files /dev/null and b/extensions/ghostty/assets/extension-icon.png differ diff --git a/extensions/ghostty/package-lock.json b/extensions/ghostty/package-lock.json new file mode 100644 index 00000000..30bc3756 --- /dev/null +++ b/extensions/ghostty/package-lock.json @@ -0,0 +1,603 @@ +{ + "name": "ghostty", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ghostty", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@vicinae/api": "^0.21.3", + "yaml": "^2.8.3" + }, + "devDependencies": { + "@types/node": "^24.9.2", + "@types/react": "19.0.10", + "typescript": "^5.9.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@vicinae/api": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@vicinae/api/-/api-0.21.3.tgz", + "integrity": "sha512-CgnpiL4GmOluSmKfJGWWPMSVxmCHOhNdH9uabFYpToOKBbRifmlzVEkKFkhK6wwzsSrPF19yHcrNUdFS4Iq7zA==", + "license": "ISC", + "dependencies": { + "chokidar": "^4.0.3", + "esbuild": "^0.25.2", + "react": "^19.0.0", + "zod": "^4.0.17" + }, + "bin": { + "vici": "dist/bin.js" + }, + "peerDependencies": { + "@types/node": ">=18", + "@types/react": "19.0.10" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/extensions/ghostty/package.json b/extensions/ghostty/package.json new file mode 100644 index 00000000..d8dfcc2e --- /dev/null +++ b/extensions/ghostty/package.json @@ -0,0 +1,129 @@ +{ + "$schema": "https://raw.githubusercontent.com/vicinaehq/vicinae/refs/heads/main/extra/schemas/extension.json", + "name": "ghostty", + "title": "Ghostty", + "description": "Launch windows, create tabs, open workspaces, run launch configurations, and manage Ghostty on Linux.", + "categories": [ + "Productivity", + "Developer Tools" + ], + "license": "MIT", + "author": "domainus", + "icon": "extension-icon.png", + "commands": [ + { + "name": "new-ghostty-window", + "title": "New Ghostty Window", + "description": "Launch a new Ghostty window", + "mode": "no-view" + }, + { + "name": "new-ghostty-tab", + "title": "New Ghostty Tab", + "description": "Create a new tab in the current Ghostty window using the configured Ctrl+Shift+T shortcut", + "mode": "no-view" + }, + { + "name": "search-ghostty-tab", + "title": "Focus Ghostty Window", + "subtitle": "Ghostty", + "description": "List visible Ghostty windows and focus the selected one", + "mode": "view" + }, + { + "name": "open-ghostty-launch-configuration", + "title": "Open Ghostty Launch Configuration", + "description": "Open Ghostty with a saved YAML launch configuration", + "mode": "view" + }, + { + "name": "open-ghostty-workspace", + "title": "Open Ghostty Workspace", + "description": "Find Git repositories and open them in Ghostty or with a launch configuration", + "mode": "view" + }, + { + "name": "open-with-ghostty", + "title": "Open with Ghostty", + "description": "Open a path in Ghostty", + "mode": "no-view", + "arguments": [ + { + "name": "path", + "type": "text", + "placeholder": "Path to open", + "required": false + } + ] + }, + { + "name": "view-ghostty-config", + "title": "Manage Ghostty Config", + "description": "View, edit, validate, and open the Ghostty configuration file", + "mode": "view" + } + ], + "preferences": [ + { + "name": "workspaceParentDirectory", + "type": "directory", + "required": false, + "title": "Workspaces Parent Directory", + "description": "Directory to scan for Git repositories", + "default": "~/Developer" + }, + { + "name": "workspaceScanDepth", + "type": "dropdown", + "required": true, + "title": "Workspace Scan Depth", + "description": "Max depth to scan for git repositories", + "default": "3", + "data": [ + { + "title": "2", + "value": "2" + }, + { + "title": "3", + "value": "3" + }, + { + "title": "4", + "value": "4" + }, + { + "title": "5", + "value": "5" + }, + { + "title": "6", + "value": "6" + } + ] + }, + { + "name": "ghosttyBinary", + "type": "textfield", + "required": true, + "title": "Ghostty Binary", + "description": "Path to Ghostty", + "default": "/usr/bin/ghostty" + } + ], + "scripts": { + "build": "vici build", + "dev": "vici develop", + "pretest": "tsc --outDir dist --module commonjs --target ES2022 --jsx react-jsx --esModuleInterop --skipLibCheck src/lib.ts", "test": "node --test test/*.test.mjs" + }, + "dependencies": { + "@vicinae/api": "^0.21.3", + "yaml": "^2.8.3" + }, + "devDependencies": { + "@types/node": "^24.9.2", + "@types/react": "19.0.10", + "typescript": "^5.9.3" + }, + "version": "0.1.0" +} diff --git a/extensions/ghostty/src/lib.ts b/extensions/ghostty/src/lib.ts new file mode 100644 index 00000000..ee3c5301 --- /dev/null +++ b/extensions/ghostty/src/lib.ts @@ -0,0 +1,124 @@ +import { spawn, execFileSync } from "child_process"; +import { existsSync, lstatSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs"; +import { dirname, basename, join, resolve } from "path"; +import { homedir } from "os"; +import YAML from "yaml"; + +export type LaunchPane = { cwd?: string; commands?: string[]; title?: string; layout?: LaunchPane; panes?: LaunchPane[]; split_direction?: string }; +export type LaunchTab = { title?: string; cwd?: string; commands?: string[]; layout?: LaunchPane }; +export type LaunchWindow = { title?: string; cwd?: string; tabs?: LaunchTab[] }; +export type LaunchConfig = { name?: string; windows: LaunchWindow[] }; +export type Preferences = { ghosttyBinary?: string; workspaceParentDirectory?: string; workspaceScanDepth?: string }; +export type LaunchConfigEntry = { name: string; path: string; config: LaunchConfig; error?: undefined } | { name: string; path: string; error: string; config?: undefined }; + +export const configPath = join(homedir(), ".config", "ghostty", "config"); +export const altConfigPath = join(homedir(), ".config", "ghostty", "config.ghostty"); +export const launchConfigDir = join(homedir(), ".config", "vicinae", "ghostty-launch-configs"); + +export function expandHome(path: string): string { return path.replace(/^~(?=$|\/)/, homedir()); } +export function existingConfigPath(): string { return existsSync(configPath) ? configPath : altConfigPath; } +export function ensureLaunchConfigDir(): void { mkdirSync(launchConfigDir, { recursive: true }); } +export function ghosttyBin(preferences?: Preferences): string { return preferences?.ghosttyBinary || "/usr/bin/ghostty"; } +export function defaultWorkspaceRoot(preferences?: Preferences): string { return expandHome(preferences?.workspaceParentDirectory || "~/Developer"); } + +export function commandExists(command: string): boolean { + try { execFileSync("sh", ["-lc", `command -v ${JSON.stringify(command)} >/dev/null 2>&1`], { timeout: 2000 }); return true; } catch { return false; } +} + +export function spawnGhostty(args: string[], cwd?: string, bin = "/usr/bin/ghostty"): void { + if (!existsSync(bin) && !commandExists(bin)) throw new Error(`Ghostty binary not found: ${bin}`); + const child = spawn(bin, args, { cwd: cwd && existsSync(cwd) ? cwd : homedir(), detached: true, stdio: "ignore" }); + child.unref(); +} + +export function openGhosttyWindow(cwd?: string, bin = "/usr/bin/ghostty", command?: string): void { + const args: string[] = []; + if (cwd) args.push(`--working-directory=${cwd}`); + if (command) args.push(`--initial-command=${command}`); + spawnGhostty(args, cwd, bin); +} + +export function openGhosttyNewWindow(cwd?: string, bin = "/usr/bin/ghostty"): void { + const args = ["+new-window"]; + if (cwd) args.push(`--working-directory=${cwd}`); + spawnGhostty(args, cwd, bin); +} + +export function findGitRepos(root: string, maxDepth: number): string[] { + root = expandHome(root || homedir()); + const out: string[] = []; + function walk(dir: string, depth: number) { + if (depth < 0) return; + let entries: string[] = []; + try { entries = readdirSync(dir); } catch { return; } + if (entries.includes(".git")) { out.push(dir); return; } + for (const name of entries) { + if (name.startsWith(".") && name !== ".config") continue; + const p = join(dir, name); + try { if (lstatSync(p).isDirectory()) walk(p, depth - 1); } catch {} + } + } + walk(root, maxDepth); + return out.sort((a, b) => basename(a).localeCompare(basename(b)) || a.localeCompare(b)); +} + +export function listLaunchConfigs(): LaunchConfigEntry[] { + ensureLaunchConfigDir(); + return readdirSync(launchConfigDir).filter(f => /\.ya?ml$/i.test(f)).map(file => { + const path = join(launchConfigDir, file); + try { + const config = YAML.parse(readFileSync(path, "utf8")) as LaunchConfig; + if (!config || !Array.isArray(config.windows)) throw new Error("Missing required windows list"); + return { name: config.name || basename(file).replace(/\.ya?ml$/i, ""), path, config }; + } catch (e: any) { + return { name: basename(file), path, error: e?.message || "Invalid YAML" }; + } + }); +} + +export function saveLaunchConfig(name: string, yaml: string): string { + ensureLaunchConfigDir(); + const safe = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-|-$/g, "") || "launch-config"; + const path = join(launchConfigDir, `${safe}.yaml`); + const parsed = YAML.parse(yaml); + if (!parsed || !Array.isArray(parsed.windows)) throw new Error("Launch config must include a windows list"); + writeFileSync(path, yaml); + return path; +} + +export function runLaunchConfig(config: LaunchConfig, bin = "/usr/bin/ghostty", cwdOverride?: string): void { + for (const win of config.windows || []) { + const tabs = win.tabs && win.tabs.length ? win.tabs : [{ cwd: win.cwd, commands: [] }]; + for (const tab of tabs) { + const cwd = cwdOverride || tab.cwd || win.cwd || tab.layout?.cwd || homedir(); + const commands = collectCommands(tab); + openGhosttyWindow(expandHome(cwd), bin, commands.join(" && ") || undefined); + } + } +} + +export function collectCommands(tab: LaunchTab): string[] { + const commands: string[] = []; + if (tab.commands) commands.push(...tab.commands); + function visit(p?: LaunchPane) { + if (!p) return; + if (p.commands) commands.push(...p.commands); + if (p.layout) visit(p.layout); + if (p.panes) p.panes.forEach(visit); + } + visit(tab.layout); + return commands; +} + +export function readConfig(): string { const p = existingConfigPath(); return existsSync(p) ? readFileSync(p, "utf8") : ""; } +export function writeConfig(text: string): void { const p = existingConfigPath(); mkdirSync(dirname(p), { recursive: true }); writeFileSync(p, text); } +export function validateGhosttyConfig(bin = "/usr/bin/ghostty"): { ok: boolean; message: string } { + try { const out = execFileSync(bin, ["+validate-config"], { encoding: "utf8", timeout: 10000 }); return { ok: true, message: out.trim() || "Config is valid" }; } + catch (e: any) { return { ok: false, message: ((e.stdout || "") + (e.stderr || e.message || "Validation failed")).trim() }; } +} +export function focusWindow(id: string): void { execFileSync("wmctrl", ["-ia", id], { timeout: 3000 }); } +export function listGhosttyWindows(): { id: string; title: string }[] { + const out = execFileSync("wmctrl", ["-lx"], { encoding: "utf8", timeout: 3000 }); + return out.split(/\n/).filter(l => /ghostty/i.test(l)).map(l => { const parts = l.trim().split(/\s+/); return { id: parts[0], title: parts.slice(4).join(" ") || "Ghostty" }; }); +} +export function resolveOpenPath(input?: string): string { const p = expandHome((input || "").trim() || homedir()); try { const st = lstatSync(p); return st.isDirectory() ? resolve(p) : dirname(resolve(p)); } catch { return homedir(); } } diff --git a/extensions/ghostty/src/new-ghostty-tab.ts b/extensions/ghostty/src/new-ghostty-tab.ts new file mode 100644 index 00000000..aa859248 --- /dev/null +++ b/extensions/ghostty/src/new-ghostty-tab.ts @@ -0,0 +1,55 @@ +import { execFileSync, spawn } from "child_process"; +import { existsSync, writeFileSync } from "fs"; +import { join } from "path"; +import { tmpdir } from "os"; +import { getPreferenceValues, showToast, Toast } from "@vicinae/api"; +import { ghosttyBin, type Preferences } from "./lib"; + +function focusGhosttyWithKWin(): void { + const scriptPath = join(tmpdir(), `vicinae-focus-ghostty-${process.pid}.js`); + writeFileSync(scriptPath, ` +function windows() { + if (typeof workspace.windowList === 'function') return workspace.windowList(); + if (typeof workspace.clientList === 'function') return workspace.clientList(); + return []; +} +for (const w of windows()) { + const klass = String(w.resourceClass || w.resourceName || w.windowClass || '').toLowerCase(); + const cap = String(w.caption || '').toLowerCase(); + if (klass.includes('ghostty') || cap.includes('ghostty')) { + workspace.activeWindow = w; + break; + } +} +`); + execFileSync("qdbus", ["org.kde.KWin", "/Scripting", "org.kde.kwin.Scripting.loadScript", scriptPath, `vicinae-focus-ghostty-${process.pid}`], { timeout: 2000 }); + execFileSync("qdbus", ["org.kde.KWin", "/Scripting", "org.kde.kwin.Scripting.start"], { timeout: 2000 }); +} + +function sendNewTabShortcut(): boolean { + const socket = `/run/user/${process.getuid?.() ?? 1000}/.ydotool_socket`; + if (!existsSync(socket)) return false; + execFileSync("ydotool", ["key", "29:1", "42:1", "20:1", "20:0", "42:0", "29:0"], { + env: { ...process.env, YDOTOOL_SOCKET: socket }, + timeout: 3000, + stdio: "ignore", + }); + return true; +} + +export default async function Command() { + try { + focusGhosttyWithKWin(); + Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250); + sendNewTabShortcut(); + } catch (e: any) { + try { + const bin = ghosttyBin(getPreferenceValues()); + const child = spawn(bin, [], { detached: true, stdio: "ignore", cwd: process.env.HOME || undefined }); + child.unref(); + await showToast({ style: Toast.Style.Failure, title: "Could not send new-tab shortcut", message: "Opened Ghostty instead. Check qdbus, KWin scripting, and ydotool." }); + } catch { + await showToast({ style: Toast.Style.Failure, title: "Could not create Ghostty tab", message: e?.message || String(e) }); + } + } +} diff --git a/extensions/ghostty/src/new-ghostty-window.ts b/extensions/ghostty/src/new-ghostty-window.ts new file mode 100644 index 00000000..a3261306 --- /dev/null +++ b/extensions/ghostty/src/new-ghostty-window.ts @@ -0,0 +1,10 @@ +import { getPreferenceValues, showToast, Toast } from "@vicinae/api"; +import { ghosttyBin, openGhosttyNewWindow, type Preferences } from "./lib"; + +export default async function Command() { + try { + openGhosttyNewWindow(undefined, ghosttyBin(getPreferenceValues())); + } catch (e: any) { + await showToast({ style: Toast.Style.Failure, title: "Could not open Ghostty", message: e?.message || String(e) }); + } +} diff --git a/extensions/ghostty/src/open-ghostty-launch-configuration.tsx b/extensions/ghostty/src/open-ghostty-launch-configuration.tsx new file mode 100644 index 00000000..501ddd94 --- /dev/null +++ b/extensions/ghostty/src/open-ghostty-launch-configuration.tsx @@ -0,0 +1,33 @@ +import { Action, ActionPanel, Form, Icon, List, getPreferenceValues, showToast, Toast, useNavigation } from "@vicinae/api"; +import { useEffect, useState } from "react"; +import { ghosttyBin, listLaunchConfigs, runLaunchConfig, saveLaunchConfig, launchConfigDir, type LaunchConfigEntry, type Preferences } from "./lib"; + +function AddConfig({ onSaved }: { onSaved: () => void }) { + const { pop } = useNavigation(); + async function submit(values: { name: string; yaml: string }) { + try { const path = saveLaunchConfig(values.name, values.yaml); await showToast({ style: Toast.Style.Success, title: "Saved launch config", message: path }); onSaved(); pop(); } + catch (e: any) { await showToast({ style: Toast.Style.Failure, title: "Invalid launch config", message: e?.message || String(e) }); } + } + const sample = `name: dev\nwindows:\n - tabs:\n - title: Shell\n cwd: ~/\n commands:\n - pwd\n`; + return
}> + + + ; +} + +export default function Command() { + const [configs, setConfigs] = useState([]); + const prefs = getPreferenceValues(); + const refresh = () => setConfigs(listLaunchConfigs()); + useEffect(refresh, []); + return + {configs.map((c) => + {!c.error && c.config ? { + try { runLaunchConfig(c.config, ghosttyBin(prefs)); await showToast({ style: Toast.Style.Success, title: "Opened launch configuration", message: c.name }); } + catch (e: any) { await showToast({ style: Toast.Style.Failure, title: "Could not run launch config", message: e?.message || String(e) }); } + }} /> : null} + } /> + } />)} + {configs.length === 0 ? } />} /> : null} + ; +} diff --git a/extensions/ghostty/src/open-ghostty-workspace.tsx b/extensions/ghostty/src/open-ghostty-workspace.tsx new file mode 100644 index 00000000..70cd4f0e --- /dev/null +++ b/extensions/ghostty/src/open-ghostty-workspace.tsx @@ -0,0 +1,35 @@ +import { Action, ActionPanel, Icon, List, getPreferenceValues, showToast, Toast } from "@vicinae/api"; +import { useEffect, useMemo, useState } from "react"; +import { basename } from "path"; +import { defaultWorkspaceRoot, findGitRepos, ghosttyBin, listLaunchConfigs, openGhosttyWindow, runLaunchConfig, type Preferences } from "./lib"; + +export default function Command() { + const prefs = getPreferenceValues(); + const [repos, setRepos] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const configs = useMemo(() => listLaunchConfigs(), []); + const validConfigs = configs.filter((c) => !c.error && c.config); + + const refresh = async () => { + setIsLoading(true); + try { setRepos(findGitRepos(defaultWorkspaceRoot(prefs), Number(prefs.workspaceScanDepth || 3))); } + finally { setIsLoading(false); } + }; + + useEffect(() => { refresh(); }, []); + + return + {repos.map((repo) => + { + try { openGhosttyWindow(repo, ghosttyBin(prefs)); await showToast({ style: Toast.Style.Success, title: "Opened workspace", message: repo }); } + catch (e: any) { await showToast({ style: Toast.Style.Failure, title: "Could not open workspace", message: e?.message || String(e) }); } + }} /> + {validConfigs.map((c) => { + try { runLaunchConfig(c.config!, ghosttyBin(prefs), repo); await showToast({ style: Toast.Style.Success, title: `Opened ${basename(repo)}`, message: c.name }); } + catch (e: any) { await showToast({ style: Toast.Style.Failure, title: "Could not run launch config", message: e?.message || String(e) }); } + }} />)} + + } />)} + {!isLoading && repos.length === 0 ? : null} + ; +} diff --git a/extensions/ghostty/src/open-with-ghostty.ts b/extensions/ghostty/src/open-with-ghostty.ts new file mode 100644 index 00000000..65977748 --- /dev/null +++ b/extensions/ghostty/src/open-with-ghostty.ts @@ -0,0 +1,11 @@ +import { getPreferenceValues, showToast, Toast } from "@vicinae/api"; +import { ghosttyBin, openGhosttyWindow, resolveOpenPath, type Preferences } from "./lib"; + +export default async function Command(props: { arguments?: { path?: string } }) { + const cwd = resolveOpenPath(props?.arguments?.path); + try { + openGhosttyWindow(cwd, ghosttyBin(getPreferenceValues())); + } catch (e: any) { + await showToast({ style: Toast.Style.Failure, title: "Could not open path in Ghostty", message: e?.message || String(e) }); + } +} diff --git a/extensions/ghostty/src/search-ghostty-tab.tsx b/extensions/ghostty/src/search-ghostty-tab.tsx new file mode 100644 index 00000000..462a5265 --- /dev/null +++ b/extensions/ghostty/src/search-ghostty-tab.tsx @@ -0,0 +1,26 @@ +import { Action, ActionPanel, Icon, List, showToast, Toast } from "@vicinae/api"; +import { useEffect, useState } from "react"; +import { focusWindow, listGhosttyWindows } from "./lib"; + +export default function Command() { + const [items, setItems] = useState<{ id: string; title: string }[]>([]); + const [isLoading, setIsLoading] = useState(true); + const refresh = async () => { + setIsLoading(true); + try { setItems(listGhosttyWindows()); } + catch (e: any) { setItems([]); await showToast({ style: Toast.Style.Failure, title: "Could not list Ghostty windows", message: "Install wmctrl or use a desktop that exposes windows to wmctrl." }); } + finally { setIsLoading(false); } + }; + useEffect(() => { refresh(); }, []); + + return + {items.map((w) => + { + try { focusWindow(w.id); await showToast({ style: Toast.Style.Success, title: "Focused Ghostty", message: w.title }); } + catch (e: any) { await showToast({ style: Toast.Style.Failure, title: "Could not focus window", message: e?.message || String(e) }); } + }} /> + + } />)} + {!isLoading && items.length === 0 ? : null} + ; +} diff --git a/extensions/ghostty/src/view-ghostty-config.tsx b/extensions/ghostty/src/view-ghostty-config.tsx new file mode 100644 index 00000000..99df841e --- /dev/null +++ b/extensions/ghostty/src/view-ghostty-config.tsx @@ -0,0 +1,32 @@ +import { Action, ActionPanel, Detail, Form, Icon, getPreferenceValues, showToast, Toast, useNavigation } from "@vicinae/api"; +import { useEffect, useState } from "react"; +import { existingConfigPath, ghosttyBin, readConfig, validateGhosttyConfig, writeConfig, type Preferences } from "./lib"; + +function EditConfig({ initial, onSaved }: { initial: string; onSaved: () => void }) { + const { pop } = useNavigation(); + async function submit(v: { config: string }) { + writeConfig(v.config); + await showToast({ style: Toast.Style.Success, title: "Saved Ghostty config", message: existingConfigPath() }); + onSaved(); + pop(); + } + return
}> + + ; +} + +export default function Command() { + const [text, setText] = useState(""); + const prefs = getPreferenceValues(); + const reload = () => setText(readConfig()); + useEffect(reload, []); + const md = `# Ghostty Config\n\n\`${existingConfigPath()}\`\n\n\`\`\`ini\n${text.replace(/`/g, "\\`")}\n\`\`\``; + return + } /> + { + const result = validateGhosttyConfig(ghosttyBin(prefs)); + await showToast({ style: result.ok ? Toast.Style.Success : Toast.Style.Failure, title: result.ok ? "Ghostty config is valid" : "Ghostty config is invalid", message: result.message.slice(0, 160) }); + }} /> + + } />; +} diff --git a/extensions/ghostty/test/lib.test.mjs b/extensions/ghostty/test/lib.test.mjs new file mode 100644 index 00000000..78005c43 --- /dev/null +++ b/extensions/ghostty/test/lib.test.mjs @@ -0,0 +1,26 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdirSync, rmSync } from 'node:fs'; +import { tmpdir, homedir } from 'node:os'; +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; +const mod = await import(pathToFileURL(join(process.cwd(), 'dist', 'lib.js'))); + +test('expandHome expands a leading tilde only', () => { + assert.equal(mod.expandHome('~/src'), join(homedir(), 'src')); + assert.equal(mod.expandHome('/tmp/~x'), '/tmp/~x'); +}); + +test('findGitRepos finds repo roots and does not descend into repos', () => { + const root = join(tmpdir(), `ghostty-vicinae-${Date.now()}`); + rmSync(root, { recursive: true, force: true }); + mkdirSync(join(root, 'a', '.git'), { recursive: true }); + mkdirSync(join(root, 'a', 'nested', '.git'), { recursive: true }); + mkdirSync(join(root, 'b', 'c', '.git'), { recursive: true }); + assert.deepEqual(mod.findGitRepos(root, 3).map(p => p.replace(root + '/', '')), ['a', 'c'].map(x => x === 'c' ? 'b/c' : x).sort()); + rmSync(root, { recursive: true, force: true }); +}); + +test('collectCommands flattens tab and nested pane commands', () => { + assert.deepEqual(mod.collectCommands({ commands: ['a'], layout: { commands: ['b'], panes: [{ commands: ['c'] }] } }), ['a', 'b', 'c']); +}); diff --git a/extensions/ghostty/tsconfig.json b/extensions/ghostty/tsconfig.json new file mode 100644 index 00000000..eadcdf81 --- /dev/null +++ b/extensions/ghostty/tsconfig.json @@ -0,0 +1 @@ +{"compilerOptions":{"target":"ES2022","lib":["ES2022"],"module":"commonjs","moduleResolution":"node","jsx":"react-jsx","strict":false,"esModuleInterop":true,"skipLibCheck":true,"forceConsistentCasingInFileNames":true},"include":["src/**/*"]} diff --git a/extensions/timer/package-lock.json b/extensions/timer/package-lock.json index ff489f54..501f75e5 100644 --- a/extensions/timer/package-lock.json +++ b/extensions/timer/package-lock.json @@ -17,6 +17,8 @@ }, "node_modules/@biomejs/biome": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.2.tgz", + "integrity": "sha512-8e9tzamuDycx7fdrcJ/F/GDZ8SYukc5ud6tDicjjFqURKYFSWMl0H0iXNXZEGmcmNUmABgGuHThPykcM41INgg==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -110,6 +112,8 @@ }, "node_modules/@biomejs/cli-linux-x64": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.2.tgz", + "integrity": "sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA==", "cpu": [ "x64" ], @@ -125,6 +129,8 @@ }, "node_modules/@biomejs/cli-linux-x64-musl": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.2.tgz", + "integrity": "sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA==", "cpu": [ "x64" ], @@ -430,6 +436,8 @@ }, "node_modules/@esbuild/linux-x64": { "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -588,6 +596,8 @@ }, "node_modules/@jgoz/esbuild-plugin-typecheck": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jgoz/esbuild-plugin-typecheck/-/esbuild-plugin-typecheck-4.0.4.tgz", + "integrity": "sha512-ca38NAWnE/GchWjO5m7Wbny+yMOsYkoJOboQGheCjnnu5uDxqQWJSIegN+C+CWl8K/1naI/cMfTrAfDH1oRoVQ==", "license": "MIT", "peerDependencies": { "@jgoz/esbuild-plugin-livereload": ">=2.1.4", @@ -602,6 +612,8 @@ }, "node_modules/@oclif/core": { "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.3.tgz", + "integrity": "sha512-gQCSYAtUhJilGKaSaZhqejH9X1dDu+jWQjLmtGOgN/XcKaAEPPSeT2mu1UvlvtPox1/NNRdlBcUa8KRKo2HnJQ==", "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.2", @@ -629,6 +641,8 @@ }, "node_modules/@oclif/plugin-help": { "version": "6.2.49", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.49.tgz", + "integrity": "sha512-fEsO0YU7ThtzHE1RGuoHxFu/OGlqxm7PCfFp+U1PS8sde4E0cDqjVDuv78+VKrr45LpC5lWOApj7pm3FNfHrVA==", "license": "MIT", "dependencies": { "@oclif/core": "^4" @@ -639,6 +653,8 @@ }, "node_modules/@oclif/plugin-plugins": { "version": "5.4.68", + "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-5.4.68.tgz", + "integrity": "sha512-+YZ5Afv9OiTsOIS3ODGJH2T8+CymruYQxRPXOCDQBZC0sjLb02S5yQ4OGKSCLBFpbbeBjo05C3IOBdxKzwF+wA==", "license": "MIT", "dependencies": { "@oclif/core": "^4.11.1", @@ -659,6 +675,8 @@ }, "node_modules/@types/bun": { "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.14.tgz", + "integrity": "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==", "dev": true, "license": "MIT", "dependencies": { @@ -667,6 +685,8 @@ }, "node_modules/@types/node": { "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -674,6 +694,8 @@ }, "node_modules/@types/react": { "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -681,6 +703,8 @@ }, "node_modules/@vicinae/api": { "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@vicinae/api/-/api-0.21.0.tgz", + "integrity": "sha512-3Gmew8lLF/e6vyLUTWXS2WuwQZ3mTT8xqCuWcDf7+KfDJXGb6urjY20xQY39rpGBd3+P5wcPqbcQ38JMYksgkQ==", "license": "ISC", "dependencies": { "@jgoz/esbuild-plugin-typecheck": "^4.0.3", @@ -704,6 +728,8 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "license": "MIT", "dependencies": { "type-fest": "^0.21.3" @@ -717,6 +743,8 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" @@ -724,6 +752,8 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -737,6 +767,8 @@ }, "node_modules/ansis": { "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", "license": "ISC", "engines": { "node": ">=14" @@ -744,10 +776,14 @@ }, "node_modules/async": { "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, "node_modules/balanced-match": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -755,6 +791,8 @@ }, "node_modules/brace-expansion": { "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -765,6 +803,8 @@ }, "node_modules/bun-types": { "version": "1.3.14", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.14.tgz", + "integrity": "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==", "dev": true, "license": "MIT", "dependencies": { @@ -773,6 +813,8 @@ }, "node_modules/chokidar": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -786,6 +828,8 @@ }, "node_modules/clean-stack": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", "license": "MIT", "dependencies": { "escape-string-regexp": "4.0.0" @@ -799,6 +843,8 @@ }, "node_modules/cli-spinners": { "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "license": "MIT", "engines": { "node": ">=6" @@ -809,6 +855,8 @@ }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -819,14 +867,20 @@ }, "node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/csstype": { "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -842,6 +896,8 @@ }, "node_modules/ejs": { "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" @@ -855,13 +911,16 @@ }, "node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/esbuild": { "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -899,6 +958,8 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "license": "MIT", "engines": { "node": ">=10" @@ -909,6 +970,8 @@ }, "node_modules/fdir": { "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", "engines": { "node": ">=12.0.0" @@ -924,6 +987,8 @@ }, "node_modules/filelist": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" @@ -931,6 +996,8 @@ }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -941,6 +1008,8 @@ }, "node_modules/filelist/node_modules/minimatch/node_modules/brace-expansion": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -948,10 +1017,14 @@ }, "node_modules/filelist/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/get-package-type": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "license": "MIT", "engines": { "node": ">=8.0.0" @@ -959,6 +1032,8 @@ }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", "engines": { "node": ">=8" @@ -966,6 +1041,8 @@ }, "node_modules/hosted-git-info": { "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" @@ -976,6 +1053,8 @@ }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "license": "MIT", "engines": { "node": ">=8" @@ -983,6 +1062,8 @@ }, "node_modules/is-docker": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "license": "MIT", "bin": { "is-docker": "cli.js" @@ -996,6 +1077,8 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" @@ -1003,6 +1086,8 @@ }, "node_modules/is-wsl": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "license": "MIT", "dependencies": { "is-docker": "^2.0.0" @@ -1013,6 +1098,8 @@ }, "node_modules/isexe": { "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -1020,6 +1107,8 @@ }, "node_modules/jake": { "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", "license": "Apache-2.0", "dependencies": { "async": "^3.2.6", @@ -1035,6 +1124,8 @@ }, "node_modules/lilconfig": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", "engines": { "node": ">=14" @@ -1045,10 +1136,14 @@ }, "node_modules/lru-cache": { "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, "node_modules/minimatch": { "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -1062,10 +1157,14 @@ }, "node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/npm": { "version": "11.15.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.15.0.tgz", + "integrity": "sha512-+k0tk7lRnpMUPnC7kTuU/yrV/mnFoPhJQ75VfLtZ6fwbzOVXaPsTE/Il9Pn1DHi482byMyqkHv/XsQ76mNjXLw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -1218,6 +1317,8 @@ }, "node_modules/npm-package-arg": { "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", "license": "ISC", "dependencies": { "hosted-git-info": "^7.0.0", @@ -1231,6 +1332,8 @@ }, "node_modules/npm-run-path": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "license": "MIT", "dependencies": { "path-key": "^4.0.0" @@ -2761,7 +2864,6 @@ "version": "4.0.4", "inBundle": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2854,6 +2956,8 @@ }, "node_modules/object-treeify": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-4.0.1.tgz", + "integrity": "sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==", "license": "MIT", "engines": { "node": ">= 16" @@ -2861,6 +2965,8 @@ }, "node_modules/path-key": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "license": "MIT", "engines": { "node": ">=12" @@ -2871,12 +2977,15 @@ }, "node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2886,6 +2995,8 @@ }, "node_modules/proc-log": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -2893,6 +3004,8 @@ }, "node_modules/react": { "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2900,6 +3013,8 @@ }, "node_modules/readdirp": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -2911,6 +3026,8 @@ }, "node_modules/semver": { "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2921,6 +3038,8 @@ }, "node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -2933,6 +3052,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -2943,6 +3064,8 @@ }, "node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -2956,6 +3079,8 @@ }, "node_modules/tinyglobby": { "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2970,6 +3095,8 @@ }, "node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -2980,8 +3107,9 @@ }, "node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2992,10 +3120,14 @@ }, "node_modules/undici-types": { "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/validate-npm-package-name": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -3003,6 +3135,8 @@ }, "node_modules/which": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -3016,6 +3150,8 @@ }, "node_modules/widest-line": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "license": "MIT", "dependencies": { "string-width": "^4.0.0" @@ -3026,10 +3162,14 @@ }, "node_modules/wordwrap": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "license": "MIT" }, "node_modules/wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -3045,6 +3185,8 @@ }, "node_modules/yarn": { "version": "1.22.22", + "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz", + "integrity": "sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==", "hasInstallScript": true, "license": "BSD-2-Clause", "bin": { @@ -3057,6 +3199,8 @@ }, "node_modules/zod": { "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks"