From 1c76c2de9cf799588f85dacd2caa3b6e0b785bc9 Mon Sep 17 00:00:00 2001 From: jaintp Date: Sun, 17 May 2026 09:51:26 +1000 Subject: [PATCH 1/2] feat: implement VScrot screenshot extension with multi-backend capture and annotation support. --- extensions/vscrot/.gitignore | 2 + extensions/vscrot/README.md | 147 + extensions/vscrot/assets/extension_icon.png | Bin 0 -> 87852 bytes extensions/vscrot/package-lock.json | 3928 +++++++++++++++++ extensions/vscrot/package.json | 223 + extensions/vscrot/src/all.tsx | 87 + extensions/vscrot/src/annotators/gimp.ts | 17 + extensions/vscrot/src/annotators/index.ts | 24 + extensions/vscrot/src/annotators/mspaint.ts | 16 + extensions/vscrot/src/annotators/none.ts | 11 + extensions/vscrot/src/annotators/pinta.ts | 17 + extensions/vscrot/src/annotators/satty.ts | 17 + extensions/vscrot/src/annotators/swappy.ts | 15 + extensions/vscrot/src/annotators/types.ts | 10 + extensions/vscrot/src/area.tsx | 87 + extensions/vscrot/src/backends/flameshot.ts | 19 + .../vscrot/src/backends/gnome-screenshot.ts | 21 + extensions/vscrot/src/backends/grim.ts | 33 + extensions/vscrot/src/backends/grimblast.ts | 42 + extensions/vscrot/src/backends/index.ts | 32 + extensions/vscrot/src/backends/maim.ts | 23 + .../vscrot/src/backends/screencapture.ts | 20 + .../vscrot/src/backends/screenshot-desktop.ts | 26 + extensions/vscrot/src/backends/spectacle.ts | 22 + extensions/vscrot/src/backends/types.ts | 13 + extensions/vscrot/src/backends/utils.ts | 45 + extensions/vscrot/src/backends/x11-scrot.ts | 21 + .../vscrot/src/components/CaptureList.tsx | 201 + .../vscrot/src/components/PreviewDetail.tsx | 96 + .../vscrot/src/components/ToolPicker.tsx | 98 + extensions/vscrot/src/lib/annotate.ts | 43 + extensions/vscrot/src/lib/capture.ts | 42 + extensions/vscrot/src/lib/clipboard.ts | 43 + extensions/vscrot/src/lib/dateFormat.ts | 16 + extensions/vscrot/src/lib/filesystem.ts | 57 + extensions/vscrot/src/lib/preferences.ts | 18 + extensions/vscrot/src/lib/tool-selection.ts | 41 + extensions/vscrot/src/lib/tools-database.ts | 278 ++ extensions/vscrot/src/manage-tools.tsx | 146 + extensions/vscrot/src/monitor.tsx | 152 + extensions/vscrot/src/scrot.tsx | 162 + extensions/vscrot/src/window.tsx | 87 + extensions/vscrot/tsconfig.json | 18 + 43 files changed, 6416 insertions(+) create mode 100644 extensions/vscrot/.gitignore create mode 100644 extensions/vscrot/README.md create mode 100644 extensions/vscrot/assets/extension_icon.png create mode 100644 extensions/vscrot/package-lock.json create mode 100644 extensions/vscrot/package.json create mode 100644 extensions/vscrot/src/all.tsx create mode 100644 extensions/vscrot/src/annotators/gimp.ts create mode 100644 extensions/vscrot/src/annotators/index.ts create mode 100644 extensions/vscrot/src/annotators/mspaint.ts create mode 100644 extensions/vscrot/src/annotators/none.ts create mode 100644 extensions/vscrot/src/annotators/pinta.ts create mode 100644 extensions/vscrot/src/annotators/satty.ts create mode 100644 extensions/vscrot/src/annotators/swappy.ts create mode 100644 extensions/vscrot/src/annotators/types.ts create mode 100644 extensions/vscrot/src/area.tsx create mode 100644 extensions/vscrot/src/backends/flameshot.ts create mode 100644 extensions/vscrot/src/backends/gnome-screenshot.ts create mode 100644 extensions/vscrot/src/backends/grim.ts create mode 100644 extensions/vscrot/src/backends/grimblast.ts create mode 100644 extensions/vscrot/src/backends/index.ts create mode 100644 extensions/vscrot/src/backends/maim.ts create mode 100644 extensions/vscrot/src/backends/screencapture.ts create mode 100644 extensions/vscrot/src/backends/screenshot-desktop.ts create mode 100644 extensions/vscrot/src/backends/spectacle.ts create mode 100644 extensions/vscrot/src/backends/types.ts create mode 100644 extensions/vscrot/src/backends/utils.ts create mode 100644 extensions/vscrot/src/backends/x11-scrot.ts create mode 100644 extensions/vscrot/src/components/CaptureList.tsx create mode 100644 extensions/vscrot/src/components/PreviewDetail.tsx create mode 100644 extensions/vscrot/src/components/ToolPicker.tsx create mode 100644 extensions/vscrot/src/lib/annotate.ts create mode 100644 extensions/vscrot/src/lib/capture.ts create mode 100644 extensions/vscrot/src/lib/clipboard.ts create mode 100644 extensions/vscrot/src/lib/dateFormat.ts create mode 100644 extensions/vscrot/src/lib/filesystem.ts create mode 100644 extensions/vscrot/src/lib/preferences.ts create mode 100644 extensions/vscrot/src/lib/tool-selection.ts create mode 100644 extensions/vscrot/src/lib/tools-database.ts create mode 100644 extensions/vscrot/src/manage-tools.tsx create mode 100644 extensions/vscrot/src/monitor.tsx create mode 100644 extensions/vscrot/src/scrot.tsx create mode 100644 extensions/vscrot/src/window.tsx create mode 100644 extensions/vscrot/tsconfig.json diff --git a/extensions/vscrot/.gitignore b/extensions/vscrot/.gitignore new file mode 100644 index 00000000..eb3ae205 --- /dev/null +++ b/extensions/vscrot/.gitignore @@ -0,0 +1,2 @@ +node_modules +vicinae-env.d.ts diff --git a/extensions/vscrot/README.md b/extensions/vscrot/README.md new file mode 100644 index 00000000..ab3905ca --- /dev/null +++ b/extensions/vscrot/README.md @@ -0,0 +1,147 @@ +# VScrot + +A Vicinae extension for taking, annotating, and managing screenshots across Linux (Wayland & X11), macOS, and Windows. + +--- + +## Commands + +### `scrot` - Take a Screenshot + +The main command. Opens a list of capture modes filtered to what your active tool supports. After capture, a preview lets you save, copy to clipboard, annotate, or discard. + +**Capture modes:** + +| Mode | Description | +|---|---| +| Capture Area | Interactive region selection | +| Capture Window | Active or selected window | +| Capture Monitor | A single display | +| Capture All Monitors | Full desktop (all displays merged) | + +The **Active Tools** section at the bottom of the list shows which capture and annotation tools are currently in use. Tap either entry to switch to any other installed tool - the choice is saved and persists across sessions. + +### `manage-tools` - Manage Tools + +Shows all supported capture, annotation, clipboard, and dependency tools grouped by category, with installed/not-installed status. For each missing tool: + +- The install command for your detected package manager is shown inline +- **Copy Install Command** copies it to the clipboard +- **Show All Package Managers** shows a full install table for every supported manager +- **Copy Install Script for All Missing Tools** generates a combined script for everything not yet installed + +Supported package managers: `pacman`, `apt`, `dnf`, `brew`, `winget` + +--- + +## Capture Backends + +VScrot auto-detects the best available tool in priority order. You can override this in **Extension Preferences** or by tapping **Active Tools → Capture** inside the extension. + +### Wayland + +| Tool | Modes | Notes | Link | +|---|---|---|---| +| **grimblast** | area · window · monitor · full | Hyprland-native wrapper around grim. Recommended for Hyprland. | [hyprwm/contrib](https://github.com/hyprwm/contrib) | +| **grim** + slurp | area · window · monitor · full | Compositor-agnostic Wayland grabber. Requires `slurp` for selection and `jq` for window/monitor modes. | [grim](https://sr.ht/~emersion/grim/) · [slurp](https://github.com/emersion/slurp) | +| **spectacle** | area · window · monitor · full | KDE screenshot tool. Works on Wayland and X11. | [spectacle](https://apps.kde.org/spectacle/) | +| **gnome-screenshot** | area · window · full | GNOME screenshot utility. | [gnome-screenshot](https://gitlab.gnome.org/GNOME/gnome-screenshot) | +| **flameshot** | area · full | Cross-platform. Wayland support varies by compositor - works best on KDE/GNOME Wayland. | [flameshot.org](https://flameshot.org) | + +### X11 + +| Tool | Modes | Notes | Link | +|---|---|---|---| +| **maim** + slop | area · window · full | Recommended X11 tool. Requires `slop` for area selection and `xdotool` for window capture. | [maim](https://github.com/naelstrof/maim) · [slop](https://github.com/naelstrof/slop) | +| **scrot** | area · window · full | Classic X11 capture utility. | [scrot](https://github.com/resurrecting-open-source-projects/scrot) | + +### macOS + +| Tool | Modes | Notes | Link | +|---|---|---|---| +| **screencapture** | area · window · full | Built-in macOS utility - no installation required. | - | +| **screenshot-desktop** | full · monitor | npm package bundled with the extension. Uses native OS APIs on macOS and Windows. | [screenshot-desktop](https://github.com/bencevans/screenshot-desktop) | + +### Windows + +| Tool | Modes | Notes | Link | +|---|---|---|---| +| **screenshot-desktop** | full · monitor | npm package bundled with the extension. Uses Win32 GDI - no additional install needed. | [screenshot-desktop](https://github.com/bencevans/screenshot-desktop) | +| **flameshot** | area · full | Available for Windows via the official installer. | [flameshot.org](https://flameshot.org) | + +--- + +## Annotation Tools + +After capture, the preview offers an **Annotate** action using your active annotation tool. Tools are split into two modes: + +- **Auto-reload** - the tool is launched, blocks until you close it, then the preview refreshes automatically. +- **Manual save** - the tool opens in the background; save the file yourself, then use **Refresh Preview** inside the extension. + +| Tool | Mode | Platform | Link | +|---|---|---|---| +| **satty** | Auto-reload | Linux (Wayland) | [gabm/Satty](https://github.com/gabm/Satty) | +| **swappy** | Auto-reload | Linux (Wayland) | [jtheoof/swappy](https://github.com/jtheoof/swappy) | +| **GIMP** | Manual save | Linux · macOS · Windows | [gimp.org](https://www.gimp.org) | +| **Pinta** | Manual save | Linux · macOS · Windows | [pinta-project.com](https://www.pinta-project.com) | +| **Paint** (mspaint) | Manual save | Windows (built-in) | - | +| **None** | - | All | Disables the Annotate action | + +Auto-detection priority: satty → swappy → GIMP → Pinta → Paint + +--- + +## Clipboard Tools + +Images are copied to the clipboard using platform-native tools with automatic fallback: + +| Tool | Platform | Link | +|---|---|---| +| **wl-copy** (wl-clipboard) | Linux Wayland (primary) | [wl-clipboard](https://github.com/bugaevc/wl-clipboard) | +| **xclip** | Linux X11 (fallback) | [xclip](https://github.com/astrand/xclip) | +| **pbcopy** | macOS (built-in) | - | +| PowerShell clipboard | Windows (built-in) | - | + +--- + +## Dependencies + +These are not capture tools themselves but are required by certain backends: + +| Dependency | Required by | Link | +|---|---|---| +| **slurp** | grim (area · window · monitor modes) | [slurp](https://github.com/emersion/slurp) | +| **jq** | grim (window · monitor modes via hyprctl) | [jqlang/jq](https://github.com/jqlang/jq) | +| **slop** | maim (area selection) | [slop](https://github.com/naelstrof/slop) | +| **xdotool** | maim (window capture) | [xdotool](https://github.com/jordansissel/xdotool) | +| **ImageMagick** (`identify`) | Preview metadata (image dimensions) - optional | [imagemagick.org](https://imagemagick.org) | + +--- + +## Preferences + +Preferences are the default starting point. Active tool selections made inside the extension are saved per-session via LocalStorage and override preferences. + +| Preference | Description | Default | +|---|---|---| +| **Screenshot Tool** | Capture backend. `Auto-detect` picks the first available in priority order. | `auto` | +| **Annotation Tool** | Annotation tool. `Auto-detect` picks the first available. `None` disables annotation. | `auto` | +| **Screenshot Path** | Base folder where screenshots are saved. | `~/Pictures/Screenshots` | +| **Subfolder Format** | `strftime`-style subfolder name (e.g. `%d-%m-%Y`). Leave blank to save directly in the base folder. | `%d-%m-%Y` | +| **Filename Format** | `strftime`-style filename (e.g. `Screenshot_%d-%m-%Y_%H-%M-%S`). | `Screenshot_%d-%m-%Y_%H-%M-%S` | +| **Copy to Clipboard** | Copy to clipboard automatically after capture. | `true` | +| **Save to File** | Save to file automatically after capture. | `true` | + +**Supported date tokens:** `%Y` `%m` `%d` `%H` `%M` `%S` + +--- + +## Development + +```bash +npm install +npm run dev # watch mode +npm run build # production bundle +npm run lint # type-check + lint +npm run format # biome format +``` diff --git a/extensions/vscrot/assets/extension_icon.png b/extensions/vscrot/assets/extension_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..68ff4f1ad7021efd89b4f66e6d89ad1f94d8f4cd GIT binary patch literal 87852 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.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.3.2", + "@biomejs/cli-darwin-x64": "2.3.2", + "@biomejs/cli-linux-arm64": "2.3.2", + "@biomejs/cli-linux-arm64-musl": "2.3.2", + "@biomejs/cli-linux-x64": "2.3.2", + "@biomejs/cli-linux-x64-musl": "2.3.2", + "@biomejs/cli-win32-arm64": "2.3.2", + "@biomejs/cli-win32-x64": "2.3.2" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.2.tgz", + "integrity": "sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.2.tgz", + "integrity": "sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.2.tgz", + "integrity": "sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.2.tgz", + "integrity": "sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "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" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "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" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.2.tgz", + "integrity": "sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.2.tgz", + "integrity": "sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.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/@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", + "esbuild": ">=0.25.0", + "typescript": ">= 3.5" + }, + "peerDependenciesMeta": { + "@jgoz/esbuild-plugin-livereload": { + "optional": true + } + } + }, + "node_modules/@oclif/core": { + "version": "4.10.5", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.5.tgz", + "integrity": "sha512-qcdCF7NrdWPfme6Kr34wwljRCXbCVpL1WVxiNy0Ep6vbWKjxAjFQwuhqkoyL0yjI+KdwtLcOCGn5z2yzdijc8w==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.7.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.14", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-help": { + "version": "6.2.44", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.44.tgz", + "integrity": "sha512-x03Se2LtlOOlGfTuuubt5C4Z8NHeR4zKXtVnfycuLU+2VOMu2WpsGy9nbs3nYuInuvsIY1BizjVaTjUz060Sig==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-plugins": { + "version": "5.4.60", + "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-5.4.60.tgz", + "integrity": "sha512-aTunE6bxVNDGnxwK2RkTo8m5ghFEcD2M35gM/uTC35LgQ1k4PLFYZ3yWfjhrK0LmryrL921+Cr+jwH4nzmlAWg==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4.8.0", + "ansis": "^3.17.0", + "debug": "^4.4.0", + "npm": "^10.9.7", + "npm-package-arg": "^11.0.3", + "npm-run-path": "^5.3.0", + "object-treeify": "^4.0.1", + "semver": "^7.7.4", + "validate-npm-package-name": "^5.0.1", + "which": "^4.0.0", + "yarn": "^1.22.22" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.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.20.15", + "resolved": "https://registry.npmjs.org/@vicinae/api/-/api-0.20.15.tgz", + "integrity": "sha512-7FAUgdQJ+D8krDfiAfXmxKMtqo3a/oLxC4F6IA14WSCc6Wh1NWqwNyBvmPJxxrpuNg1/eQcPfHaWqm1rZCzCMA==", + "license": "ISC", + "dependencies": { + "@jgoz/esbuild-plugin-typecheck": "^4.0.3", + "@oclif/core": "^4", + "@oclif/plugin-help": "^6", + "@oclif/plugin-plugins": "^5", + "@types/node": ">=18", + "@types/react": "19.0.10", + "chokidar": "^4.0.3", + "esbuild": "^0.25.2", + "react": "19.0.0", + "zod": "^4.0.17" + }, + "bin": { + "vici": "bin/run.js" + }, + "peerDependencies": { + "@types/node": ">=18", + "@types/react": "19.0.10" + } + }, + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + } + }, + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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" + } + }, + "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" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "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/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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "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" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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", + "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/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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/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" + } + }, + "node_modules/filelist/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/filelist/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" + } + }, + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "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" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/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/glob/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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" + } + }, + "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" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "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" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + } + }, + "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" + }, + "engines": { + "node": ">=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" + } + }, + "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", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "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" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "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": "10.9.8", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.8.tgz", + "integrity": "sha512-fYwb6ODSmHkqrJQQaCxY3M2lPf/mpgC7ik0HSzzIwG5CGtabRp4bNqikatvCoT42b5INQSqudVH0R7yVmC9hVg==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.5", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.2.0", + "@npmcli/promise-spawn": "^8.0.3", + "@npmcli/redact": "^3.2.2", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.1.1", + "abbrev": "^3.0.1", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.6.2", + "ci-info": "^4.4.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.1.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.5", + "libnpmexec": "^9.0.5", + "libnpmfund": "^6.0.5", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.5", + "libnpmpublish": "^10.0.2", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.9", + "minipass": "^7.1.3", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.5.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.1", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.2", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.4", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.4", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^7.5.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.2", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "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", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "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" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^7.5.10" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.2.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.1", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "2.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.6.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.4.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.2", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.3", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/fdir": { + "version": "6.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.5.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.2.0", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "10.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.5", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^7.5.11" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.5", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.5", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.9", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/minizlib": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.5.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.2", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^7.5.10" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/picomatch": { + "version": "4.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.4", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.23", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "7.5.11", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.4.1", + "make-fetch-happen": "^14.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.5", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "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" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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" + } + }, + "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" + } + }, + "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/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/screenshot-desktop": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/screenshot-desktop/-/screenshot-desktop-1.15.4.tgz", + "integrity": "sha512-nvYzQH+pzA9cJVKxZzde4mX1ymZoPETUp3vT5vl72460782i+ZctSbV71vu+YU/9ktAEa9nONLRLpgliXgB5LA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/bencevans" + } + ], + "license": "MIT", + "dependencies": { + "temp": "^0.9.4" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=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" + }, + "engines": { + "node": ">=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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "license": "MIT", + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "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" + } + }, + "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" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "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" + }, + "engines": { + "node": ">=8" + } + }, + "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", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "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": { + "yarn": "bin/yarn.js", + "yarnpkg": "bin/yarn.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/extensions/vscrot/package.json b/extensions/vscrot/package.json new file mode 100644 index 00000000..785e0939 --- /dev/null +++ b/extensions/vscrot/package.json @@ -0,0 +1,223 @@ +{ + "$schema": "https://raw.githubusercontent.com/vicinaehq/vicinae/refs/heads/main/extra/schemas/extension.json", + "name": "vscrot", + "title": "VScrot", + "description": "An extension to take and manage screenshots", + "categories": [ + "Productivity", + "Other" + ], + "license": "MIT", + "author": "JaINTP", + "contributors": [], + "pastContributors": [], + "icon": "extension_icon.png", + "commands": [ + { + "name": "scrot", + "title": "Scrot", + "subtitle": "Take a screenshot", + "description": "Take a screenshot", + "mode": "view" + }, + { + "name": "area", + "title": "Capture Area", + "subtitle": "Capture a selected area of your screen.", + "description": "Capture a selected area of your screen.", + "mode": "view" + }, + { + "name": "window", + "title": "Capture Window", + "subtitle": "Capture a selected window.", + "description": "Capture a selected window.", + "mode": "view" + }, + { + "name": "monitor", + "title": "Capture Monitor", + "subtitle": "Capture a selected monitor.", + "description": "Capture a selected monitor.", + "mode": "view" + }, + { + "name": "all", + "title": "Capture All Monitors", + "subtitle": "Capture all monitors.", + "description": "Capture all monitors.", + "mode": "view" + }, + { + "name": "manage-tools", + "title": "Manage Tools", + "subtitle": "Check and install screenshot tools", + "description": "View which capture, annotation, and clipboard tools are installed and copy install commands for any that are missing", + "mode": "view" + } + ], + "preferences": [ + { + "name": "screenshot_tool", + "type": "dropdown", + "title": "Screenshot Tool", + "description": "The tool used to capture screenshots. 'Auto-detect' picks the first available one.", + "default": "auto", + "required": true, + "data": [ + { + "title": "Auto-detect", + "value": "auto" + }, + { + "title": "grimblast (Hyprland)", + "value": "grimblast" + }, + { + "title": "grim + slurp (Wayland)", + "value": "grim" + }, + { + "title": "spectacle (KDE)", + "value": "spectacle" + }, + { + "title": "gnome-screenshot (GNOME)", + "value": "gnome-screenshot" + }, + { + "title": "flameshot", + "value": "flameshot" + }, + { + "title": "maim + slop (X11)", + "value": "maim" + }, + { + "title": "scrot (X11)", + "value": "scrot" + }, + { + "title": "screencapture (macOS)", + "value": "screencapture" + }, + { + "title": "screenshot-desktop (npm, Windows/macOS)", + "value": "screenshot-desktop" + } + ] + }, + { + "name": "annotation_tool", + "type": "dropdown", + "title": "Annotation Tool", + "description": "Tool used to annotate screenshots. Auto-reload tools (Satty, swappy) reload the preview automatically. Manual tools (GIMP, Pinta, Paint) require you to save and use Refresh Preview.", + "default": "auto", + "required": true, + "data": [ + { + "title": "Auto-detect", + "value": "auto" + }, + { + "title": "Satty (auto-reload)", + "value": "satty" + }, + { + "title": "swappy (auto-reload)", + "value": "swappy" + }, + { + "title": "GIMP (manual save)", + "value": "gimp" + }, + { + "title": "Pinta (manual save)", + "value": "pinta" + }, + { + "title": "Paint / mspaint (manual save)", + "value": "mspaint" + }, + { + "title": "None", + "value": "none" + } + ] + }, + { + "name": "screenshot_path", + "type": "textfield", + "title": "Screenshot Path", + "description": "Folder to save screenshots to", + "default": "~/Pictures/Screenshots", + "required": true + }, + { + "name": "use_editor", + "type": "checkbox", + "title": "Use Editor (Satty)", + "label": "Open in editor by default", + "description": "Whether to open Satty after taking a screenshot", + "default": true, + "required": false + }, + { + "name": "copy_to_clipboard", + "type": "checkbox", + "title": "Copy to Clipboard", + "label": "Copy to clipboard automatically", + "description": "Whether to copy the screenshot to the clipboard", + "default": true, + "required": false + }, + { + "name": "save_to_file", + "type": "checkbox", + "title": "Save to File", + "label": "Save to file automatically", + "description": "Whether to save the screenshot to a file", + "default": true, + "required": false + }, + { + "name": "subfolder_format", + "type": "textfield", + "title": "Subfolder Format", + "description": "Folder name format (e.g. %d-%m-%Y). Omit to save directly in screenshot path.", + "default": "%d-%m-%Y", + "required": false + }, + { + "name": "filename_format", + "type": "textfield", + "title": "Filename Format", + "description": "File name format (e.g. Screenshot_%d-%m-%Y_%H-%M-%S)", + "default": "Screenshot_%d-%m-%Y_%H-%M-%S", + "required": true + }, + { + "name": "autoclose_vicinae", + "type": "checkbox", + "title": "Autoclose Vicinae", + "label": "Close Vicinae after saving or copying", + "description": "Automatically close Vicinae after saving a screenshot to file or copying it to the clipboard", + "default": false, + "required": false + } + ], + "scripts": { + "build": "vici build", + "dev": "vici develop", + "format": "biome format --write src", + "lint": "vici lint" + }, + "dependencies": { + "@vicinae/api": "^0.20.15", + "screenshot-desktop": "^1.6.0" + }, + "devDependencies": { + "@biomejs/biome": "2.3.2", + "typescript": "^5.9.2" + } +} diff --git a/extensions/vscrot/src/all.tsx b/extensions/vscrot/src/all.tsx new file mode 100644 index 00000000..64a43ad6 --- /dev/null +++ b/extensions/vscrot/src/all.tsx @@ -0,0 +1,87 @@ +import { useState, useEffect } from "react"; +import { closeMainWindow } from "@vicinae/api"; +import { getPrefs } from "./lib/preferences"; +import { formatDateTokens } from "./lib/dateFormat"; +import { TEMP_PATH, getSavePath, saveImageFile } from "./lib/filesystem"; +import { copyToClipboard } from "./lib/clipboard"; +import { annotateWith } from "./lib/annotate"; +import { captureScreenshot } from "./lib/capture"; +import { getAnnotator } from "./annotators"; +import { resolveBackend, resolveAnnotator } from "./lib/tool-selection"; +import { PreviewDetail } from "./components/PreviewDetail"; + +export default function CaptureAll() { + const prefs = getPrefs(); + const [lastCapture, setLastCapture] = useState(null); + const [activeBackendId, setActiveBackendId] = useState(null); + const [activeAnnotatorId, setActiveAnnotatorId] = useState( + null, + ); + + const refreshPreview = () => { + setLastCapture(null); + setTimeout(() => setLastCapture(TEMP_PATH), 100); + }; + + const capture = async (backendId: string) => { + const result = await captureScreenshot("full", backendId); + if (result) setLastCapture(result); + }; + + useEffect(() => { + Promise.all([ + resolveBackend(prefs.screenshot_tool ?? "auto"), + resolveAnnotator(prefs.annotation_tool ?? "auto"), + ]).then(([backend, annotator]) => { + const bid = backend?.id ?? null; + setActiveBackendId(bid); + setActiveAnnotatorId(annotator?.id ?? null); + if (bid) capture(bid); + }); + }, []); + + const activeAnnotator = activeAnnotatorId + ? getAnnotator(activeAnnotatorId) + : null; + const annotatorLabel = + activeAnnotator && activeAnnotator.id !== "none" + ? `Annotate (${activeAnnotator.displayName})` + : null; + + const handleSave = () => { + if (!lastCapture) return; + saveImageFile(lastCapture, getSavePath(prefs)); + setLastCapture(null); + if (prefs.autoclose_vicinae) closeMainWindow(); + }; + + const handleAnnotate = async () => { + if (!lastCapture || !activeAnnotatorId) return; + const shouldReload = await annotateWith(lastCapture, activeAnnotatorId); + if (shouldReload) refreshPreview(); + }; + + if (!lastCapture) return null; + + return ( + copyToClipboard(lastCapture, prefs.autoclose_vicinae)} + onAnnotate={handleAnnotate} + onRefreshPreview={refreshPreview} + onReshoot={() => { + setLastCapture(null); + if (activeBackendId) capture(activeBackendId); + }} + onDiscard={() => setLastCapture(null)} + /> + ); +} diff --git a/extensions/vscrot/src/annotators/gimp.ts b/extensions/vscrot/src/annotators/gimp.ts new file mode 100644 index 00000000..0d209b3f --- /dev/null +++ b/extensions/vscrot/src/annotators/gimp.ts @@ -0,0 +1,17 @@ +import { exec } from "node:child_process"; +import type { AnnotatorBackend } from "./types"; +import { isCommandAvailable } from "../backends/utils"; + +export const gimpAnnotator: AnnotatorBackend = { + id: "gimp", + displayName: "GIMP", + mode: "manual", + + isAvailable: () => isCommandAvailable("gimp"), + + annotate: async (imagePath: string) => { + await new Promise((resolve) => { + exec(`gimp "${imagePath}"`, () => resolve()); + }); + }, +}; diff --git a/extensions/vscrot/src/annotators/index.ts b/extensions/vscrot/src/annotators/index.ts new file mode 100644 index 00000000..16118844 --- /dev/null +++ b/extensions/vscrot/src/annotators/index.ts @@ -0,0 +1,24 @@ +import { sattyAnnotator } from "./satty"; +import { swappyAnnotator } from "./swappy"; +import { gimpAnnotator } from "./gimp"; +import { pintaAnnotator } from "./pinta"; +import { mspaintAnnotator } from "./mspaint"; +import { noneAnnotator } from "./none"; +import type { AnnotatorBackend } from "./types"; + +export type { AnnotatorBackend, AnnotationMode } from "./types"; + +// Priority order for auto-detection (auto-reload tools first) +export const ALL_ANNOTATORS: AnnotatorBackend[] = [ + sattyAnnotator, + swappyAnnotator, + gimpAnnotator, + pintaAnnotator, + mspaintAnnotator, +]; + +export const getAnnotator = (id: string): AnnotatorBackend | null => { + if (id === "none") return noneAnnotator; + if (id === "auto") return ALL_ANNOTATORS.find((a) => a.isAvailable()) ?? null; + return ALL_ANNOTATORS.find((a) => a.id === id) ?? null; +}; diff --git a/extensions/vscrot/src/annotators/mspaint.ts b/extensions/vscrot/src/annotators/mspaint.ts new file mode 100644 index 00000000..12fa2226 --- /dev/null +++ b/extensions/vscrot/src/annotators/mspaint.ts @@ -0,0 +1,16 @@ +import { exec } from "node:child_process"; +import type { AnnotatorBackend } from "./types"; + +export const mspaintAnnotator: AnnotatorBackend = { + id: "mspaint", + displayName: "Paint", + mode: "manual", + + isAvailable: () => process.platform === "win32", + + annotate: async (imagePath: string) => { + await new Promise((resolve) => { + exec(`mspaint.exe "${imagePath}"`, () => resolve()); + }); + }, +}; diff --git a/extensions/vscrot/src/annotators/none.ts b/extensions/vscrot/src/annotators/none.ts new file mode 100644 index 00000000..8363699a --- /dev/null +++ b/extensions/vscrot/src/annotators/none.ts @@ -0,0 +1,11 @@ +import type { AnnotatorBackend } from "./types"; + +export const noneAnnotator: AnnotatorBackend = { + id: "none", + displayName: "None", + mode: "manual", + + isAvailable: () => true, + + annotate: async (_imagePath: string) => {}, +}; diff --git a/extensions/vscrot/src/annotators/pinta.ts b/extensions/vscrot/src/annotators/pinta.ts new file mode 100644 index 00000000..3e85e634 --- /dev/null +++ b/extensions/vscrot/src/annotators/pinta.ts @@ -0,0 +1,17 @@ +import { exec } from "node:child_process"; +import type { AnnotatorBackend } from "./types"; +import { isCommandAvailable } from "../backends/utils"; + +export const pintaAnnotator: AnnotatorBackend = { + id: "pinta", + displayName: "Pinta", + mode: "manual", + + isAvailable: () => isCommandAvailable("pinta"), + + annotate: async (imagePath: string) => { + await new Promise((resolve) => { + exec(`pinta "${imagePath}"`, () => resolve()); + }); + }, +}; diff --git a/extensions/vscrot/src/annotators/satty.ts b/extensions/vscrot/src/annotators/satty.ts new file mode 100644 index 00000000..086c6ba3 --- /dev/null +++ b/extensions/vscrot/src/annotators/satty.ts @@ -0,0 +1,17 @@ +import { execSync } from "node:child_process"; +import type { AnnotatorBackend } from "./types"; +import { isCommandAvailable } from "../backends/utils"; + +export const sattyAnnotator: AnnotatorBackend = { + id: "satty", + displayName: "Satty", + mode: "auto", + + isAvailable: () => isCommandAvailable("satty"), + + annotate: async (imagePath: string) => { + execSync( + `satty --filename "${imagePath}" --output-filename "${imagePath}" --early-exit`, + ); + }, +}; diff --git a/extensions/vscrot/src/annotators/swappy.ts b/extensions/vscrot/src/annotators/swappy.ts new file mode 100644 index 00000000..2ec78417 --- /dev/null +++ b/extensions/vscrot/src/annotators/swappy.ts @@ -0,0 +1,15 @@ +import { execSync } from "node:child_process"; +import type { AnnotatorBackend } from "./types"; +import { isCommandAvailable } from "../backends/utils"; + +export const swappyAnnotator: AnnotatorBackend = { + id: "swappy", + displayName: "swappy", + mode: "auto", + + isAvailable: () => isCommandAvailable("swappy"), + + annotate: async (imagePath: string) => { + execSync(`swappy -f "${imagePath}" -o "${imagePath}"`); + }, +}; diff --git a/extensions/vscrot/src/annotators/types.ts b/extensions/vscrot/src/annotators/types.ts new file mode 100644 index 00000000..98e1c017 --- /dev/null +++ b/extensions/vscrot/src/annotators/types.ts @@ -0,0 +1,10 @@ +export type AnnotationMode = "auto" | "manual"; + +export interface AnnotatorBackend { + readonly id: string; + readonly displayName: string; + /** "auto" = blocks until editor closes and writes output; "manual" = opens editor non-blocking */ + readonly mode: AnnotationMode; + isAvailable(): boolean; + annotate(imagePath: string): Promise; +} diff --git a/extensions/vscrot/src/area.tsx b/extensions/vscrot/src/area.tsx new file mode 100644 index 00000000..7bee30ab --- /dev/null +++ b/extensions/vscrot/src/area.tsx @@ -0,0 +1,87 @@ +import { useState, useEffect } from "react"; +import { closeMainWindow } from "@vicinae/api"; +import { getPrefs } from "./lib/preferences"; +import { formatDateTokens } from "./lib/dateFormat"; +import { TEMP_PATH, getSavePath, saveImageFile } from "./lib/filesystem"; +import { copyToClipboard } from "./lib/clipboard"; +import { annotateWith } from "./lib/annotate"; +import { captureScreenshot } from "./lib/capture"; +import { getAnnotator } from "./annotators"; +import { resolveBackend, resolveAnnotator } from "./lib/tool-selection"; +import { PreviewDetail } from "./components/PreviewDetail"; + +export default function CaptureArea() { + const prefs = getPrefs(); + const [lastCapture, setLastCapture] = useState(null); + const [activeBackendId, setActiveBackendId] = useState(null); + const [activeAnnotatorId, setActiveAnnotatorId] = useState( + null, + ); + + const refreshPreview = () => { + setLastCapture(null); + setTimeout(() => setLastCapture(TEMP_PATH), 100); + }; + + const capture = async (backendId: string) => { + const result = await captureScreenshot("area", backendId); + if (result) setLastCapture(result); + }; + + useEffect(() => { + Promise.all([ + resolveBackend(prefs.screenshot_tool ?? "auto"), + resolveAnnotator(prefs.annotation_tool ?? "auto"), + ]).then(([backend, annotator]) => { + const bid = backend?.id ?? null; + setActiveBackendId(bid); + setActiveAnnotatorId(annotator?.id ?? null); + if (bid) capture(bid); + }); + }, []); + + const activeAnnotator = activeAnnotatorId + ? getAnnotator(activeAnnotatorId) + : null; + const annotatorLabel = + activeAnnotator && activeAnnotator.id !== "none" + ? `Annotate (${activeAnnotator.displayName})` + : null; + + const handleSave = () => { + if (!lastCapture) return; + saveImageFile(lastCapture, getSavePath(prefs)); + setLastCapture(null); + if (prefs.autoclose_vicinae) closeMainWindow(); + }; + + const handleAnnotate = async () => { + if (!lastCapture || !activeAnnotatorId) return; + const shouldReload = await annotateWith(lastCapture, activeAnnotatorId); + if (shouldReload) refreshPreview(); + }; + + if (!lastCapture) return null; + + return ( + copyToClipboard(lastCapture, prefs.autoclose_vicinae)} + onAnnotate={handleAnnotate} + onRefreshPreview={refreshPreview} + onReshoot={() => { + setLastCapture(null); + if (activeBackendId) capture(activeBackendId); + }} + onDiscard={() => setLastCapture(null)} + /> + ); +} diff --git a/extensions/vscrot/src/backends/flameshot.ts b/extensions/vscrot/src/backends/flameshot.ts new file mode 100644 index 00000000..a994a39e --- /dev/null +++ b/extensions/vscrot/src/backends/flameshot.ts @@ -0,0 +1,19 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable } from "./utils"; + +export const flameshotBackend: CaptureBackend = { + id: "flameshot", + displayName: "flameshot", + supportedModes: ["area", "full"], + + isAvailable: () => isCommandAvailable("flameshot"), + + capture: async (mode: CaptureMode, outputPath: string) => { + if (mode === "area") { + execSync(`flameshot gui --raw > "${outputPath}"`); + } else { + execSync(`flameshot screen --raw > "${outputPath}"`); + } + }, +}; diff --git a/extensions/vscrot/src/backends/gnome-screenshot.ts b/extensions/vscrot/src/backends/gnome-screenshot.ts new file mode 100644 index 00000000..e419be0e --- /dev/null +++ b/extensions/vscrot/src/backends/gnome-screenshot.ts @@ -0,0 +1,21 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable } from "./utils"; + +export const gnomeScreenshotBackend: CaptureBackend = { + id: "gnome-screenshot", + displayName: "gnome-screenshot (GNOME)", + supportedModes: ["area", "window", "full"], + + isAvailable: () => isCommandAvailable("gnome-screenshot"), + + capture: async (mode: CaptureMode, outputPath: string) => { + if (mode === "area") { + execSync(`gnome-screenshot -a -f "${outputPath}"`); + } else if (mode === "window") { + execSync(`gnome-screenshot -w -f "${outputPath}"`); + } else { + execSync(`gnome-screenshot -f "${outputPath}"`); + } + }, +}; diff --git a/extensions/vscrot/src/backends/grim.ts b/extensions/vscrot/src/backends/grim.ts new file mode 100644 index 00000000..11c8c45b --- /dev/null +++ b/extensions/vscrot/src/backends/grim.ts @@ -0,0 +1,33 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable, selectMonitor } from "./utils"; + +export const grimBackend: CaptureBackend = { + id: "grim", + displayName: "grim + slurp (Wayland)", + supportedModes: ["area", "window", "monitor", "full"], + + isAvailable: () => isCommandAvailable("grim") && isCommandAvailable("slurp"), + + capture: async ( + mode: CaptureMode, + outputPath: string, + outputName?: string, + ) => { + let geometry = ""; + if (mode === "area") { + geometry = execSync("slurp").toString().trim(); + } else if (mode === "window") { + geometry = execSync( + `hyprctl clients -j | jq -r '.[] | select(.mapped == true) | "\\(.at[0]),\\(.at[1]) \\(.size[0])x\\(.size[1])"' | slurp -r`, + ) + .toString() + .trim(); + } else if (mode === "monitor") { + const name = outputName ?? selectMonitor(); + execSync(`grim -o "${name}" "${outputPath}"`); + return; + } + execSync(`grim ${geometry ? `-g "${geometry}"` : ""} "${outputPath}"`); + }, +}; diff --git a/extensions/vscrot/src/backends/grimblast.ts b/extensions/vscrot/src/backends/grimblast.ts new file mode 100644 index 00000000..ab5ea168 --- /dev/null +++ b/extensions/vscrot/src/backends/grimblast.ts @@ -0,0 +1,42 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable, selectMonitor } from "./utils"; + +const MODE_MAP: Record = { + area: "area", + window: "active", + monitor: "output", + full: "screen", +}; + +export const grimblastBackend: CaptureBackend = { + id: "grimblast", + displayName: "grimblast (Hyprland)", + supportedModes: ["area", "window", "monitor", "full"], + + isAvailable: () => isCommandAvailable("grimblast"), + + capture: async ( + mode: CaptureMode, + outputPath: string, + outputName?: string, + ) => { + if (mode === "monitor") { + // grimblast save output captures the focused output, which races with + // closeMainWindow() shifting focus. Use grim -o with named output instead. + const name = outputName ?? selectMonitor(); + execSync(`grim -o "${name}" "${outputPath}"`); + } else if (mode === "window") { + // grimblast save active captures whatever window has focus after Vicinae closes. + // Use hyprctl + slurp for reliable interactive window selection instead. + const geometry = execSync( + `hyprctl clients -j | jq -r '.[] | select(.mapped == true) | "\\(.at[0]),\\(.at[1]) \\(.size[0])x\\(.size[1])"' | slurp -r`, + ) + .toString() + .trim(); + execSync(`grim -g "${geometry}" "${outputPath}"`); + } else { + execSync(`grimblast save ${MODE_MAP[mode]} "${outputPath}"`); + } + }, +}; diff --git a/extensions/vscrot/src/backends/index.ts b/extensions/vscrot/src/backends/index.ts new file mode 100644 index 00000000..6e8b5ba2 --- /dev/null +++ b/extensions/vscrot/src/backends/index.ts @@ -0,0 +1,32 @@ +import { grimBackend } from "./grim"; +import { grimblastBackend } from "./grimblast"; +import { x11ScrotBackend } from "./x11-scrot"; +import { maimBackend } from "./maim"; +import { flameshotBackend } from "./flameshot"; +import { spectacleBackend } from "./spectacle"; +import { gnomeScreenshotBackend } from "./gnome-screenshot"; +import { screencaptureBackend } from "./screencapture"; +import { screenshotDesktopBackend } from "./screenshot-desktop"; +import type { CaptureBackend } from "./types"; + +export type { CaptureBackend, CaptureMode } from "./types"; + +// Priority order for auto-detection - screenshot-desktop last (native tools preferred) +export const ALL_BACKENDS: CaptureBackend[] = [ + grimblastBackend, + grimBackend, + spectacleBackend, + gnomeScreenshotBackend, + flameshotBackend, + maimBackend, + x11ScrotBackend, + screencaptureBackend, + screenshotDesktopBackend, +]; + +export const getBackend = (id: string): CaptureBackend | null => { + if (id === "auto") { + return ALL_BACKENDS.find((b) => b.isAvailable()) ?? null; + } + return ALL_BACKENDS.find((b) => b.id === id) ?? null; +}; diff --git a/extensions/vscrot/src/backends/maim.ts b/extensions/vscrot/src/backends/maim.ts new file mode 100644 index 00000000..e6c83bcf --- /dev/null +++ b/extensions/vscrot/src/backends/maim.ts @@ -0,0 +1,23 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable } from "./utils"; + +export const maimBackend: CaptureBackend = { + id: "maim", + displayName: "maim + slop (X11)", + supportedModes: ["area", "window", "full"], + + isAvailable: () => isCommandAvailable("maim"), + + capture: async (mode: CaptureMode, outputPath: string) => { + if (mode === "area") { + const geometry = execSync("slop -f '%x,%y %wx%h'").toString().trim(); + execSync(`maim -g "${geometry}" "${outputPath}"`); + } else if (mode === "window") { + const id = execSync("xdotool getactivewindow").toString().trim(); + execSync(`maim -i "${id}" "${outputPath}"`); + } else { + execSync(`maim "${outputPath}"`); + } + }, +}; diff --git a/extensions/vscrot/src/backends/screencapture.ts b/extensions/vscrot/src/backends/screencapture.ts new file mode 100644 index 00000000..9b5126ee --- /dev/null +++ b/extensions/vscrot/src/backends/screencapture.ts @@ -0,0 +1,20 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; + +export const screencaptureBackend: CaptureBackend = { + id: "screencapture", + displayName: "screencapture (macOS)", + supportedModes: ["area", "window", "full"], + + isAvailable: () => process.platform === "darwin", + + capture: async (mode: CaptureMode, outputPath: string) => { + if (mode === "area") { + execSync(`screencapture -i "${outputPath}"`); + } else if (mode === "window") { + execSync(`screencapture -w "${outputPath}"`); + } else { + execSync(`screencapture "${outputPath}"`); + } + }, +}; diff --git a/extensions/vscrot/src/backends/screenshot-desktop.ts b/extensions/vscrot/src/backends/screenshot-desktop.ts new file mode 100644 index 00000000..cc1b4f91 --- /dev/null +++ b/extensions/vscrot/src/backends/screenshot-desktop.ts @@ -0,0 +1,26 @@ +import screenshot from "screenshot-desktop"; +import type { CaptureBackend, CaptureMode } from "./types"; + +// Uses native OS APIs: screencapture (macOS), Win32 GDI (Windows). +// On Linux it falls back to ImageMagick's `import` or scrot - prefer native backends there. +export const screenshotDesktopBackend: CaptureBackend = { + id: "screenshot-desktop", + displayName: "screenshot-desktop (npm)", + supportedModes: ["full", "monitor"], + + isAvailable: () => + process.platform === "win32" || process.platform === "darwin", + + capture: async (mode: CaptureMode, outputPath: string) => { + if (mode === "monitor") { + const displays = await screenshot.listDisplays(); + await screenshot({ + screen: displays[0]?.id, + filename: outputPath, + format: "png", + }); + } else { + await screenshot({ filename: outputPath, format: "png" }); + } + }, +}; diff --git a/extensions/vscrot/src/backends/spectacle.ts b/extensions/vscrot/src/backends/spectacle.ts new file mode 100644 index 00000000..6459057a --- /dev/null +++ b/extensions/vscrot/src/backends/spectacle.ts @@ -0,0 +1,22 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable } from "./utils"; + +const FLAG_MAP: Record = { + area: "-r", + window: "-a", + monitor: "-m", + full: "-f", +}; + +export const spectacleBackend: CaptureBackend = { + id: "spectacle", + displayName: "spectacle (KDE)", + supportedModes: ["area", "window", "monitor", "full"], + + isAvailable: () => isCommandAvailable("spectacle"), + + capture: async (mode: CaptureMode, outputPath: string) => { + execSync(`spectacle ${FLAG_MAP[mode]} -b -o "${outputPath}"`); + }, +}; diff --git a/extensions/vscrot/src/backends/types.ts b/extensions/vscrot/src/backends/types.ts new file mode 100644 index 00000000..c5f67bc8 --- /dev/null +++ b/extensions/vscrot/src/backends/types.ts @@ -0,0 +1,13 @@ +export type CaptureMode = "area" | "window" | "monitor" | "full"; + +export interface CaptureBackend { + readonly id: string; + readonly displayName: string; + readonly supportedModes: CaptureMode[]; + isAvailable(): boolean; + capture( + mode: CaptureMode, + outputPath: string, + outputName?: string, + ): Promise; +} diff --git a/extensions/vscrot/src/backends/utils.ts b/extensions/vscrot/src/backends/utils.ts new file mode 100644 index 00000000..8562294c --- /dev/null +++ b/extensions/vscrot/src/backends/utils.ts @@ -0,0 +1,45 @@ +import { execSync } from "node:child_process"; + +export const isCommandAvailable = (cmd: string): boolean => { + try { + execSync(`command -v ${cmd}`, { stdio: "ignore" }); + return true; + } catch { + return false; + } +}; + +type HyprMonitor = { + x: number; + y: number; + width: number; + height: number; + scale: number; + transform: number; + name: string; +}; + +const logicalGeom = (m: HyprMonitor): string => { + const swapped = m.transform % 2 === 1; + const w = swapped + ? Math.floor(m.height / m.scale) + : Math.floor(m.width / m.scale); + const h = swapped + ? Math.floor(m.width / m.scale) + : Math.floor(m.height / m.scale); + return `${m.x},${m.y} ${w}x${h}`; +}; + +// Presents all monitors via slurp and returns the Wayland output name of the +// one the user selected. Using the name with `grim -o` lets grim handle +// transform/scale/rotation natively, avoiding geometry math bugs. +export const selectMonitor = (): string => { + const monitors: HyprMonitor[] = JSON.parse( + execSync("hyprctl monitors -j").toString(), + ); + const regions = monitors.map(logicalGeom).join("\n"); + const selected = execSync("slurp -r", { input: regions }).toString().trim(); + return ( + monitors.find((m) => logicalGeom(m) === selected)?.name ?? monitors[0].name + ); +}; diff --git a/extensions/vscrot/src/backends/x11-scrot.ts b/extensions/vscrot/src/backends/x11-scrot.ts new file mode 100644 index 00000000..55cbce79 --- /dev/null +++ b/extensions/vscrot/src/backends/x11-scrot.ts @@ -0,0 +1,21 @@ +import { execSync } from "node:child_process"; +import type { CaptureBackend, CaptureMode } from "./types"; +import { isCommandAvailable } from "./utils"; + +export const x11ScrotBackend: CaptureBackend = { + id: "scrot", + displayName: "scrot (X11)", + supportedModes: ["area", "window", "full"], + + isAvailable: () => isCommandAvailable("scrot"), + + capture: async (mode: CaptureMode, outputPath: string) => { + if (mode === "area") { + execSync(`scrot -s "${outputPath}"`); + } else if (mode === "window") { + execSync(`scrot -u "${outputPath}"`); + } else { + execSync(`scrot "${outputPath}"`); + } + }, +}; diff --git a/extensions/vscrot/src/components/CaptureList.tsx b/extensions/vscrot/src/components/CaptureList.tsx new file mode 100644 index 00000000..ac718709 --- /dev/null +++ b/extensions/vscrot/src/components/CaptureList.tsx @@ -0,0 +1,201 @@ +import { + Action, + ActionPanel, + Icon, + List, + openExtensionPreferences, +} from "@vicinae/api"; +import path from "node:path"; +import type { CaptureBackend } from "../backends/types"; +import type { AnnotatorBackend } from "../annotators/types"; +import type { CaptureMode } from "../backends/types"; +import { BackendPicker, AnnotatorPicker } from "./ToolPicker"; + +interface CaptureListProps { + recentFiles: string[]; + supportedModes: CaptureMode[]; + activeBackend: CaptureBackend | null; + activeAnnotator: AnnotatorBackend | null; + installedBackends: CaptureBackend[]; + installedAnnotators: AnnotatorBackend[]; + onCapture: (mode: CaptureMode) => void; + onRefresh: () => void; + onCopyFile: (filePath: string) => void; + onDeleteFile: (filePath: string) => void; + onSelectBackend: (id: string) => void; + onSelectAnnotator: (id: string) => void; +} + +const CAPTURE_ITEMS: { mode: CaptureMode; title: string; icon: Icon }[] = [ + { mode: "area", title: "Capture Area", icon: Icon.Crop }, + { mode: "window", title: "Capture Window", icon: Icon.AppWindow }, + { mode: "monitor", title: "Capture Monitor", icon: Icon.Monitor }, + { mode: "full", title: "Capture All Monitors", icon: Icon.Layers }, +]; + +export function CaptureList({ + recentFiles, + supportedModes, + activeBackend, + activeAnnotator, + installedBackends, + installedAnnotators, + onCapture, + onRefresh, + onCopyFile, + onDeleteFile, + onSelectBackend, + onSelectAnnotator, +}: CaptureListProps) { + const noTools = installedBackends.length === 0; + + return ( + + + + } + > + {noTools ? ( + + + + } + /> + ) : ( + <> + + {CAPTURE_ITEMS.filter((item) => + supportedModes.includes(item.mode), + ).map((item) => ( + + onCapture(item.mode)} + /> + + } + /> + ))} + + + + 1 + ? `${installedBackends.length} tools available` + : "1 tool available" + } + actions={ + + + } + /> + + + } + /> + + + } + /> + + + } + /> + + + {recentFiles.length > 0 && ( + + {recentFiles.map((file) => ( + + + + onCopyFile(file)} + /> + onDeleteFile(file)} + /> + + } + /> + ))} + + )} + + )} + + ); +} diff --git a/extensions/vscrot/src/components/PreviewDetail.tsx b/extensions/vscrot/src/components/PreviewDetail.tsx new file mode 100644 index 00000000..575c842c --- /dev/null +++ b/extensions/vscrot/src/components/PreviewDetail.tsx @@ -0,0 +1,96 @@ +import { Action, ActionPanel, Detail, Icon } from "@vicinae/api"; +import { execSync } from "node:child_process"; + +interface PreviewDetailProps { + imagePath: string; + suggestedName: string; + subfolder: string; + annotatorLabel: string | null; + onSave: () => void; + onCopy: () => void; + onAnnotate: () => void; + onRefreshPreview: () => void; + onReshoot: () => void; + onDiscard: () => void; +} + +export function PreviewDetail({ + imagePath, + suggestedName, + subfolder, + annotatorLabel, + onSave, + onCopy, + onAnnotate, + onRefreshPreview, + onReshoot, + onDiscard, +}: PreviewDetailProps) { + let dimensions = "unknown"; + try { + dimensions = execSync(`identify -format "%wx%h" "${imagePath}"`) + .toString() + .trim(); + } catch { + // imagemagick not installed - skip + } + + return ( + + + + + {annotatorLabel && ( + + )} + + + + + + + + } + metadata={ + + + + + + + } + /> + ); +} diff --git a/extensions/vscrot/src/components/ToolPicker.tsx b/extensions/vscrot/src/components/ToolPicker.tsx new file mode 100644 index 00000000..c4cbc662 --- /dev/null +++ b/extensions/vscrot/src/components/ToolPicker.tsx @@ -0,0 +1,98 @@ +import { Action, ActionPanel, Icon, List, pop } from "@vicinae/api"; +import type { CaptureBackend } from "../backends/types"; +import type { AnnotatorBackend } from "../annotators/types"; + +interface BackendPickerProps { + available: CaptureBackend[]; + currentId: string | null; + onSelect: (id: string) => void; +} + +export function BackendPicker({ + available, + currentId, + onSelect, +}: BackendPickerProps) { + return ( + + {available.map((backend) => ( + + { + onSelect(backend.id); + pop(); + }} + /> + + } + /> + ))} + + ); +} + +interface AnnotatorPickerProps { + available: AnnotatorBackend[]; + currentId: string | null; + onSelect: (id: string) => void; +} + +export function AnnotatorPicker({ + available, + currentId, + onSelect, +}: AnnotatorPickerProps) { + return ( + + + { + onSelect("none"); + pop(); + }} + /> + + } + /> + {available.map((annotator) => ( + + { + onSelect(annotator.id); + pop(); + }} + /> + + } + /> + ))} + + ); +} diff --git a/extensions/vscrot/src/lib/annotate.ts b/extensions/vscrot/src/lib/annotate.ts new file mode 100644 index 00000000..914305df --- /dev/null +++ b/extensions/vscrot/src/lib/annotate.ts @@ -0,0 +1,43 @@ +import { exec } from "node:child_process"; +import { closeMainWindow, showHUD, showToast } from "@vicinae/api"; +import { getAnnotator } from "../annotators"; + +/** + * Returns true if the preview should be reloaded (auto-mode annotator), + * false if the user is saving manually or an error occurred. + */ +export const annotateWith = async ( + imagePath: string, + toolId: string, +): Promise => { + const annotator = getAnnotator(toolId); + if (!annotator || annotator.id === "none") return false; + + if (annotator.mode === "auto") { + await closeMainWindow(); + try { + await annotator.annotate(imagePath); + } catch (e) { + console.error("Annotator failed", e); + exec("vicinae open"); + showHUD(`${annotator.displayName} failed or was cancelled`); + return false; + } + exec("vicinae open"); + return true; + } + + // Manual-save: open the editor in the background, stay in Vicinae + try { + annotator.annotate(imagePath); + } catch (e) { + console.error("Failed to launch annotator", e); + showHUD(`Failed to open ${annotator.displayName}`); + return false; + } + showToast({ + title: `Opened in ${annotator.displayName}`, + message: "Save the file there, then use Refresh Preview to reload.", + }); + return false; +}; diff --git a/extensions/vscrot/src/lib/capture.ts b/extensions/vscrot/src/lib/capture.ts new file mode 100644 index 00000000..b9f7d614 --- /dev/null +++ b/extensions/vscrot/src/lib/capture.ts @@ -0,0 +1,42 @@ +import { exec } from "node:child_process"; +import { unlinkSync, existsSync } from "node:fs"; +import { closeMainWindow, showHUD } from "@vicinae/api"; +import { TEMP_PATH } from "./filesystem"; +import { getBackend } from "../backends"; +import type { CaptureMode } from "../backends/types"; + +export const captureScreenshot = async ( + mode: CaptureMode, + toolId: string, + delay = 0, + outputName?: string, +): Promise => { + const backend = getBackend(toolId); + + if (!backend) { + showHUD( + `No screenshot tool found (configured: ${toolId}). Install one and retry.`, + ); + return null; + } + + if (!backend.supportedModes.includes(mode)) { + showHUD(`${backend.displayName} does not support "${mode}" capture`); + return null; + } + + try { + if (existsSync(TEMP_PATH)) unlinkSync(TEMP_PATH); + await closeMainWindow(); + if (delay > 0) { + await new Promise((resolve) => setTimeout(resolve, delay * 1000)); + } + await backend.capture(mode, TEMP_PATH, outputName); + exec("vicinae open"); + return TEMP_PATH; + } catch { + // User cancelled selection (e.g. ESC in slurp) or tool error - surface nothing + exec("vicinae open"); + return null; + } +}; diff --git a/extensions/vscrot/src/lib/clipboard.ts b/extensions/vscrot/src/lib/clipboard.ts new file mode 100644 index 00000000..d3d7ee1d --- /dev/null +++ b/extensions/vscrot/src/lib/clipboard.ts @@ -0,0 +1,43 @@ +import { execSync } from "node:child_process"; +import { closeMainWindow, showToast } from "@vicinae/api"; + +export const copyTextToClipboard = (text: string): void => { + const escaped = text.replace(/'/g, "'\\''"); + try { + if (process.platform === "darwin") { + execSync(`printf '%s' '${escaped}' | pbcopy`); + } else if (process.platform === "win32") { + execSync(`powershell -Command "Set-Clipboard -Value '${escaped}'"`); + } else { + try { + execSync(`printf '%s' '${escaped}' | wl-copy`); + } catch { + execSync(`printf '%s' '${escaped}' | xclip -selection clipboard`); + } + } + } catch (e) { + console.error("Failed to copy text to clipboard", e); + showHUD("Clipboard copy failed - install wl-clipboard or xclip"); + } +}; + +export const copyToClipboard = (imagePath: string, autoClose = true): void => { + if (process.platform === "darwin") { + execSync( + `osascript -e 'set the clipboard to (read (POSIX file "${imagePath}") as «class PNGf»)'`, + ); + } else if (process.platform === "win32") { + execSync( + `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::SetImage([System.Drawing.Image]::FromFile('${imagePath}'))"`, + ); + } else { + // Prefer wl-copy (Wayland), fall back to xclip (X11) + try { + execSync(`wl-copy < "${imagePath}"`); + } catch { + execSync(`xclip -selection clipboard -t image/png < "${imagePath}"`); + } + } + showToast({ title: "Copied to clipboard" }); + if (autoClose) closeMainWindow(); +}; diff --git a/extensions/vscrot/src/lib/dateFormat.ts b/extensions/vscrot/src/lib/dateFormat.ts new file mode 100644 index 00000000..7778cdd4 --- /dev/null +++ b/extensions/vscrot/src/lib/dateFormat.ts @@ -0,0 +1,16 @@ +export const formatDateTokens = (format: string): string => { + const date = new Date(); + const tokens: Record = { + "%Y": date.getFullYear().toString(), + "%m": (date.getMonth() + 1).toString().padStart(2, "0"), + "%d": date.getDate().toString().padStart(2, "0"), + "%H": date.getHours().toString().padStart(2, "0"), + "%M": date.getMinutes().toString().padStart(2, "0"), + "%S": date.getSeconds().toString().padStart(2, "0"), + }; + let result = format; + for (const [token, value] of Object.entries(tokens)) { + result = result.split(token).join(value); + } + return result; +}; diff --git a/extensions/vscrot/src/lib/filesystem.ts b/extensions/vscrot/src/lib/filesystem.ts new file mode 100644 index 00000000..b07facbc --- /dev/null +++ b/extensions/vscrot/src/lib/filesystem.ts @@ -0,0 +1,57 @@ +import fs from "node:fs"; +import path from "node:path"; +import os from "node:os"; +import { showToast } from "@vicinae/api"; +import type { Preferences } from "./preferences"; +import { expandPath } from "./preferences"; +import { formatDateTokens } from "./dateFormat"; + +export const TEMP_PATH = path.join(os.tmpdir(), "vscrot_last.png"); + +export const getSavePath = ( + prefs: Preferences, + customFilename?: string, +): string => { + const base = expandPath(prefs.screenshot_path || "~/Pictures/Screenshots"); + const subfolder = prefs.subfolder_format + ? formatDateTokens(prefs.subfolder_format) + : ""; + const filename = customFilename || formatDateTokens(prefs.filename_format); + return path.join(base, subfolder, `${filename}.png`); +}; + +export const loadRecentFiles = (saveDirBase: string): string[] => { + if (!fs.existsSync(saveDirBase)) return []; + try { + const allFiles: { path: string; mtime: number }[] = []; + const walk = (dir: string) => { + if (!fs.existsSync(dir)) return; + for (const item of fs.readdirSync(dir)) { + const fullPath = path.join(dir, item); + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + walk(fullPath); + } else if (item.endsWith(".png") || item.endsWith(".jpg")) { + allFiles.push({ path: fullPath, mtime: stat.mtimeMs }); + } + } + }; + walk(saveDirBase); + return allFiles + .sort((a, b) => b.mtime - a.mtime) + .slice(0, 20) + .map((f) => f.path); + } catch (e) { + console.error("Failed to load recent files", e); + return []; + } +}; + +export const saveImageFile = (sourcePath: string, destPath: string): void => { + const dir = path.dirname(destPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.copyFileSync(sourcePath, destPath); + showToast({ title: "Saved", message: path.basename(destPath) }); +}; diff --git a/extensions/vscrot/src/lib/preferences.ts b/extensions/vscrot/src/lib/preferences.ts new file mode 100644 index 00000000..89db5de7 --- /dev/null +++ b/extensions/vscrot/src/lib/preferences.ts @@ -0,0 +1,18 @@ +import { getPreferenceValues } from "@vicinae/api"; +import os from "node:os"; + +export interface Preferences { + screenshot_path: string; + screenshot_tool: string; + annotation_tool: string; + use_editor: boolean; + copy_to_clipboard: boolean; + save_to_file: boolean; + subfolder_format: string; + filename_format: string; + autoclose_vicinae: boolean; +} + +export const getPrefs = (): Preferences => getPreferenceValues(); + +export const expandPath = (p: string): string => p.replace(/^~/, os.homedir()); diff --git a/extensions/vscrot/src/lib/tool-selection.ts b/extensions/vscrot/src/lib/tool-selection.ts new file mode 100644 index 00000000..f771a649 --- /dev/null +++ b/extensions/vscrot/src/lib/tool-selection.ts @@ -0,0 +1,41 @@ +import { LocalStorage } from "@vicinae/api"; +import { ALL_BACKENDS, getBackend } from "../backends"; +import { ALL_ANNOTATORS, getAnnotator } from "../annotators"; +import type { CaptureBackend } from "../backends/types"; +import type { AnnotatorBackend } from "../annotators/types"; + +const CAPTURE_KEY = "active_capture_tool"; +const ANNOTATE_KEY = "active_annotation_tool"; + +export const getInstalledBackends = (): CaptureBackend[] => + ALL_BACKENDS.filter((b) => b.isAvailable()); + +export const getInstalledAnnotators = (): AnnotatorBackend[] => + ALL_ANNOTATORS.filter((a) => a.isAvailable()); + +/** Resolves the active backend: LocalStorage → preference → auto-detect. */ +export const resolveBackend = async ( + prefToolId: string, +): Promise => { + const saved = await LocalStorage.getItem(CAPTURE_KEY); + const id = saved ?? prefToolId; + const backend = getBackend(id); + // If the saved tool is no longer installed, fall back to auto-detect + if (backend && !backend.isAvailable()) return getBackend("auto"); + return backend; +}; + +/** Resolves the active annotator: LocalStorage → preference → auto-detect. */ +export const resolveAnnotator = async ( + prefToolId: string, +): Promise => { + const saved = await LocalStorage.getItem(ANNOTATE_KEY); + const id = saved ?? prefToolId; + return getAnnotator(id); +}; + +export const saveBackendChoice = (id: string): Promise => + LocalStorage.setItem(CAPTURE_KEY, id); + +export const saveAnnotatorChoice = (id: string): Promise => + LocalStorage.setItem(ANNOTATE_KEY, id); diff --git a/extensions/vscrot/src/lib/tools-database.ts b/extensions/vscrot/src/lib/tools-database.ts new file mode 100644 index 00000000..d3fe3015 --- /dev/null +++ b/extensions/vscrot/src/lib/tools-database.ts @@ -0,0 +1,278 @@ +import { execSync } from "node:child_process"; +import { isCommandAvailable } from "../backends/utils"; + +export type PackageManager = "pacman" | "apt" | "dnf" | "brew" | "winget"; + +export interface ToolInfo { + id: string; + displayName: string; + description: string; + category: "capture" | "annotate" | "clipboard" | "dependency"; + checkCommand: string | null; // null = bundled npm package, always present + packages: Partial>; + notes?: string; +} + +export const detectPackageManager = (): PackageManager | "unknown" => { + if (process.platform === "win32") return "winget"; + if (process.platform === "darwin") return "brew"; + if (isCommandAvailable("pacman")) return "pacman"; + if (isCommandAvailable("apt")) return "apt"; + if (isCommandAvailable("dnf")) return "dnf"; + return "unknown"; +}; + +export const getInstallCommand = ( + tool: ToolInfo, + pm: PackageManager, +): string | null => tool.packages[pm] ?? null; + +export const isToolInstalled = (tool: ToolInfo): boolean => { + if (tool.checkCommand === null) return true; + return isCommandAvailable(tool.checkCommand); +}; + +export const TOOLS_DATABASE: ToolInfo[] = [ + // ── Capture tools ──────────────────────────────────────────────────────── + { + id: "grimblast", + displayName: "grimblast", + description: "All-in-one capture tool for Hyprland (wraps grim + slurp)", + category: "capture", + checkCommand: "grimblast", + packages: { + pacman: "sudo pacman -S grimblast", + }, + }, + { + id: "grim", + displayName: "grim", + description: "Wayland image grabber - pair with slurp for area selection", + category: "capture", + checkCommand: "grim", + packages: { + pacman: "sudo pacman -S grim", + apt: "sudo apt install grim", + dnf: "sudo dnf install grim", + }, + }, + { + id: "spectacle", + displayName: "spectacle", + description: "KDE screenshot utility - works on Wayland and X11", + category: "capture", + checkCommand: "spectacle", + packages: { + pacman: "sudo pacman -S spectacle", + apt: "sudo apt install spectacle", + dnf: "sudo dnf install spectacle", + }, + }, + { + id: "gnome-screenshot", + displayName: "gnome-screenshot", + description: "GNOME screenshot utility", + category: "capture", + checkCommand: "gnome-screenshot", + packages: { + pacman: "sudo pacman -S gnome-screenshot", + apt: "sudo apt install gnome-screenshot", + dnf: "sudo dnf install gnome-utils", + }, + }, + { + id: "flameshot", + displayName: "flameshot", + description: "Cross-platform screenshot tool with built-in annotation GUI", + category: "capture", + checkCommand: "flameshot", + packages: { + pacman: "sudo pacman -S flameshot", + apt: "sudo apt install flameshot", + dnf: "sudo dnf install flameshot", + brew: "brew install flameshot", + winget: "winget install Ablaze.Flameshot", + }, + }, + { + id: "maim", + displayName: "maim", + description: + "Lightweight X11 screenshot tool - pair with slop for selection", + category: "capture", + checkCommand: "maim", + packages: { + pacman: "sudo pacman -S maim", + apt: "sudo apt install maim", + dnf: "sudo dnf install maim", + }, + }, + { + id: "scrot", + displayName: "scrot", + description: "Classic X11 screen capture utility", + category: "capture", + checkCommand: "scrot", + packages: { + pacman: "sudo pacman -S scrot", + apt: "sudo apt install scrot", + dnf: "sudo dnf install scrot", + brew: "brew install scrot", + }, + }, + { + id: "screenshot-desktop", + displayName: "screenshot-desktop", + description: + "npm package - uses native OS APIs (Windows/macOS only in this extension)", + category: "capture", + checkCommand: null, + packages: {}, + notes: "Bundled as an npm dependency - no installation required.", + }, + // ── Annotate tools ─────────────────────────────────────────────────────── + { + id: "satty", + displayName: "satty", + description: + "Modern Wayland-native screenshot annotation tool (auto-reload)", + category: "annotate", + checkCommand: "satty", + packages: { + pacman: "yay -S satty", + }, + notes: "AUR package - requires an AUR helper (yay, paru, etc.).", + }, + { + id: "swappy", + displayName: "swappy", + description: + "Wayland snapshot editing tool, common grim companion (auto-reload)", + category: "annotate", + checkCommand: "swappy", + packages: { + pacman: "sudo pacman -S swappy", + apt: "sudo apt install swappy", + }, + }, + { + id: "gimp", + displayName: "GIMP", + description: "Powerful cross-platform image editor (manual save)", + category: "annotate", + checkCommand: "gimp", + packages: { + pacman: "sudo pacman -S gimp", + apt: "sudo apt install gimp", + dnf: "sudo dnf install gimp", + brew: "brew install gimp", + winget: "winget install GIMP.GIMP", + }, + }, + { + id: "pinta", + displayName: "Pinta", + description: "Simple cross-platform paint/annotation tool (manual save)", + category: "annotate", + checkCommand: "pinta", + packages: { + pacman: "sudo pacman -S pinta", + apt: "sudo apt install pinta", + dnf: "sudo dnf install pinta", + brew: "brew install pinta", + }, + }, + // ── Clipboard tools ────────────────────────────────────────────────────── + { + id: "wl-clipboard", + displayName: "wl-clipboard", + description: + "Wayland clipboard utilities (wl-copy / wl-paste) - required on Wayland", + category: "clipboard", + checkCommand: "wl-copy", + packages: { + pacman: "sudo pacman -S wl-clipboard", + apt: "sudo apt install wl-clipboard", + dnf: "sudo dnf install wl-clipboard", + }, + }, + { + id: "xclip", + displayName: "xclip", + description: "X11 clipboard tool - fallback when wl-copy is unavailable", + category: "clipboard", + checkCommand: "xclip", + packages: { + pacman: "sudo pacman -S xclip", + apt: "sudo apt install xclip", + dnf: "sudo dnf install xclip", + }, + }, + // ── Dependencies ───────────────────────────────────────────────────────── + { + id: "slurp", + displayName: "slurp", + description: + "Wayland region selector - required by grim for area/window/monitor capture", + category: "dependency", + checkCommand: "slurp", + packages: { + pacman: "sudo pacman -S slurp", + apt: "sudo apt install slurp", + dnf: "sudo dnf install slurp", + }, + }, + { + id: "jq", + displayName: "jq", + description: + "JSON processor - required by grim for window/monitor selection via hyprctl", + category: "dependency", + checkCommand: "jq", + packages: { + pacman: "sudo pacman -S jq", + apt: "sudo apt install jq", + dnf: "sudo dnf install jq", + brew: "brew install jq", + winget: "winget install jqlang.jq", + }, + }, + { + id: "slop", + displayName: "slop", + description: "X11 region selector - required by maim for area selection", + category: "dependency", + checkCommand: "slop", + packages: { + pacman: "sudo pacman -S slop", + apt: "sudo apt install slop", + dnf: "sudo dnf install slop", + }, + }, + { + id: "xdotool", + displayName: "xdotool", + description: "X11 window tool - required by maim for window capture", + category: "dependency", + checkCommand: "xdotool", + packages: { + pacman: "sudo pacman -S xdotool", + apt: "sudo apt install xdotool", + dnf: "sudo dnf install xdotool", + }, + }, + { + id: "imagemagick", + displayName: "ImageMagick (identify)", + description: "Used to read image dimensions in the preview metadata", + category: "dependency", + checkCommand: "identify", + packages: { + pacman: "sudo pacman -S imagemagick", + apt: "sudo apt install imagemagick", + dnf: "sudo dnf install imagemagick", + brew: "brew install imagemagick", + winget: "winget install ImageMagick.ImageMagick", + }, + }, +]; diff --git a/extensions/vscrot/src/manage-tools.tsx b/extensions/vscrot/src/manage-tools.tsx new file mode 100644 index 00000000..655f4514 --- /dev/null +++ b/extensions/vscrot/src/manage-tools.tsx @@ -0,0 +1,146 @@ +import { Action, ActionPanel, Detail, Icon, List, showHUD } from "@vicinae/api"; +import { copyTextToClipboard } from "./lib/clipboard"; +import { + TOOLS_DATABASE, + detectPackageManager, + getInstallCommand, + isToolInstalled, + type ToolInfo, +} from "./lib/tools-database"; + +const CATEGORY_TITLES: Record = { + capture: "Capture Tools", + annotate: "Annotation Tools", + clipboard: "Clipboard Tools", + dependency: "Dependencies", +}; + +function InstallDetail({ + tool, + pm, +}: { + tool: ToolInfo; + pm: ReturnType; +}) { + const pms = ["pacman", "apt", "dnf", "brew", "winget"] as const; + const rows = pms + .map((p) => { + const cmd = tool.packages[p]; + return cmd ? `| \`${p}\` | \`${cmd}\` |` : null; + }) + .filter(Boolean) + .join("\n"); + + const notes = tool.notes ? `\n> **Note:** ${tool.notes}\n` : ""; + + const markdown = `# ${tool.displayName}\n\n${tool.description}\n${notes}\n## Install Commands\n\n| Package Manager | Command |\n|---|---|\n${rows || "| - | No package available |"}`; + + return ( + + {pm !== "unknown" && getInstallCommand(tool, pm) && ( + { + const cmd = getInstallCommand(tool, pm)!; + copyTextToClipboard(cmd); + showHUD(`Copied: ${cmd}`); + }} + /> + )} + + } + /> + ); +} + +export default function ManageTools() { + const pm = detectPackageManager(); + + const categories = [ + "capture", + "annotate", + "clipboard", + "dependency", + ] as const; + + const missingTools = TOOLS_DATABASE.filter( + (t) => !isToolInstalled(t) && t.checkCommand !== null, + ); + + const installScript = missingTools + .map((t) => + getInstallCommand(t, pm as Parameters[1]), + ) + .filter(Boolean) + .join("\n"); + + return ( + + {installScript && ( + { + copyTextToClipboard(installScript); + showHUD(`Copied install script (${missingTools.length} tools)`); + }} + /> + )} + + } + > + {categories.map((category) => { + const tools = TOOLS_DATABASE.filter((t) => t.category === category); + return ( + + {tools.map((tool) => { + const installed = isToolInstalled(tool); + const installCmd = + pm !== "unknown" ? getInstallCommand(tool, pm) : null; + + return ( + + {!installed && installCmd && ( + { + copyTextToClipboard(installCmd); + showHUD(`Copied: ${installCmd}`); + }} + /> + )} + } + /> + + } + /> + ); + })} + + ); + })} + + ); +} diff --git a/extensions/vscrot/src/monitor.tsx b/extensions/vscrot/src/monitor.tsx new file mode 100644 index 00000000..5b4b8205 --- /dev/null +++ b/extensions/vscrot/src/monitor.tsx @@ -0,0 +1,152 @@ +import { useState, useEffect } from "react"; +import { execSync } from "node:child_process"; +import { List, Action, ActionPanel, Icon, closeMainWindow } from "@vicinae/api"; +import { getPrefs } from "./lib/preferences"; +import { formatDateTokens } from "./lib/dateFormat"; +import { TEMP_PATH, getSavePath, saveImageFile } from "./lib/filesystem"; +import { copyToClipboard } from "./lib/clipboard"; +import { annotateWith } from "./lib/annotate"; +import { captureScreenshot } from "./lib/capture"; +import { getAnnotator } from "./annotators"; +import { resolveBackend, resolveAnnotator } from "./lib/tool-selection"; +import { PreviewDetail } from "./components/PreviewDetail"; + +type HyprMonitor = { name: string; description: string }; +type Phase = "loading" | "selecting" | "preview"; + +export default function CaptureMonitor() { + const prefs = getPrefs(); + const [phase, setPhase] = useState("loading"); + const [monitors, setMonitors] = useState([]); + const [lastCapture, setLastCapture] = useState(null); + const [activeBackendId, setActiveBackendId] = useState(null); + const [activeAnnotatorId, setActiveAnnotatorId] = useState( + null, + ); + + const refreshPreview = () => { + setLastCapture(null); + setTimeout(() => setLastCapture(TEMP_PATH), 100); + }; + + useEffect(() => { + (async () => { + const [backend, annotator] = await Promise.all([ + resolveBackend(prefs.screenshot_tool ?? "auto"), + resolveAnnotator(prefs.annotation_tool ?? "auto"), + ]); + + setActiveBackendId(backend?.id ?? null); + setActiveAnnotatorId(annotator?.id ?? null); + + try { + const parsed: Array<{ name: string; description: string }> = JSON.parse( + execSync("hyprctl monitors -j").toString(), + ); + if (parsed.length > 0) { + setMonitors( + parsed.map((m) => ({ name: m.name, description: m.description })), + ); + setPhase("selecting"); + return; + } + } catch { + // hyprctl unavailable — fall through to slurp-based capture + } + + // No monitor list available: fall back to slurp selection in the backend + if (backend) { + const result = await captureScreenshot("monitor", backend.id); + if (result) { + setLastCapture(result); + setPhase("preview"); + } + } + })(); + }, []); + + const captureByName = async (monitorName: string) => { + if (!activeBackendId) return; + setPhase("loading"); + const result = await captureScreenshot( + "monitor", + activeBackendId, + 0, + monitorName, + ); + if (result) { + setLastCapture(result); + setPhase("preview"); + } + }; + + const activeAnnotator = activeAnnotatorId + ? getAnnotator(activeAnnotatorId) + : null; + const annotatorLabel = + activeAnnotator && activeAnnotator.id !== "none" + ? `Annotate (${activeAnnotator.displayName})` + : null; + + const handleSave = () => { + if (!lastCapture) return; + saveImageFile(lastCapture, getSavePath(prefs)); + setLastCapture(null); + if (prefs.autoclose_vicinae) closeMainWindow(); + }; + + const handleAnnotate = async () => { + if (!lastCapture || !activeAnnotatorId) return; + const shouldReload = await annotateWith(lastCapture, activeAnnotatorId); + if (shouldReload) refreshPreview(); + }; + + if (phase === "preview" && lastCapture) { + return ( + copyToClipboard(lastCapture, prefs.autoclose_vicinae)} + onAnnotate={handleAnnotate} + onRefreshPreview={refreshPreview} + onReshoot={() => { + setLastCapture(null); + setPhase("selecting"); + }} + onDiscard={() => setLastCapture(null)} + /> + ); + } + + if (phase === "selecting") { + return ( + + {monitors.map((m) => ( + + captureByName(m.name)} + /> + + } + /> + ))} + + ); + } + + return null; +} diff --git a/extensions/vscrot/src/scrot.tsx b/extensions/vscrot/src/scrot.tsx new file mode 100644 index 00000000..44bbaaac --- /dev/null +++ b/extensions/vscrot/src/scrot.tsx @@ -0,0 +1,162 @@ +import { useState, useEffect, useMemo } from "react"; +import { closeMainWindow } from "@vicinae/api"; +import fs from "node:fs"; +import { getPrefs, expandPath } from "./lib/preferences"; +import { formatDateTokens } from "./lib/dateFormat"; +import { + TEMP_PATH, + getSavePath, + loadRecentFiles, + saveImageFile, +} from "./lib/filesystem"; +import { copyToClipboard } from "./lib/clipboard"; +import { annotateWith } from "./lib/annotate"; +import { captureScreenshot } from "./lib/capture"; +import { getBackend } from "./backends"; +import { getAnnotator } from "./annotators"; +import { + getInstalledBackends, + getInstalledAnnotators, + resolveBackend, + resolveAnnotator, + saveBackendChoice, + saveAnnotatorChoice, +} from "./lib/tool-selection"; +import { PreviewDetail } from "./components/PreviewDetail"; +import { CaptureList } from "./components/CaptureList"; +import type { CaptureMode } from "./backends/types"; + +export default function Scrot() { + const prefs = getPrefs(); + const saveDirBase = expandPath( + prefs.screenshot_path || "~/Pictures/Screenshots", + ); + + // Detected synchronously at startup - stable across renders + const installedBackends = useMemo(() => getInstalledBackends(), []); + const installedAnnotators = useMemo(() => getInstalledAnnotators(), []); + + const [recentFiles, setRecentFiles] = useState([]); + const [lastCapture, setLastCapture] = useState(null); + + // Sync initial guess from prefs so first render is instant, then + // useEffect async-corrects from LocalStorage if a saved choice exists. + const [activeBackendId, setActiveBackendId] = useState(() => { + const b = getBackend(prefs.screenshot_tool ?? "auto"); + return b?.isAvailable() ? b.id : (installedBackends[0]?.id ?? null); + }); + const [activeAnnotatorId, setActiveAnnotatorId] = useState( + () => { + const a = getAnnotator(prefs.annotation_tool ?? "auto"); + return a ? a.id : (installedAnnotators[0]?.id ?? null); + }, + ); + + const refreshRecent = () => setRecentFiles(loadRecentFiles(saveDirBase)); + const refreshPreview = () => { + setLastCapture(null); + setTimeout(() => setLastCapture(TEMP_PATH), 100); + }; + + useEffect(() => { + refreshRecent(); + resolveBackend(prefs.screenshot_tool ?? "auto").then((b) => { + if (b) setActiveBackendId(b.id); + }); + resolveAnnotator(prefs.annotation_tool ?? "auto").then((a) => { + if (a) setActiveAnnotatorId(a.id); + }); + }, []); + + const activeBackend = activeBackendId ? getBackend(activeBackendId) : null; + const supportedModes: CaptureMode[] = activeBackend?.supportedModes ?? [ + "area", + "window", + "monitor", + "full", + ]; + + const activeAnnotator = activeAnnotatorId + ? getAnnotator(activeAnnotatorId) + : null; + const annotatorLabel = + activeAnnotator && activeAnnotator.id !== "none" + ? `Annotate (${activeAnnotator.displayName})` + : null; + + const capture = async (mode: CaptureMode) => { + if (!activeBackendId) return; + const result = await captureScreenshot(mode, activeBackendId); + if (result) setLastCapture(result); + }; + + const handleSave = () => { + if (!lastCapture) return; + saveImageFile(lastCapture, getSavePath(prefs)); + refreshRecent(); + setLastCapture(null); + if (prefs.autoclose_vicinae) closeMainWindow(); + }; + + const handleAnnotate = async () => { + if (!lastCapture || !activeAnnotatorId) return; + const shouldReload = await annotateWith(lastCapture, activeAnnotatorId); + if (shouldReload) refreshPreview(); + }; + + const handleSelectBackend = async (id: string) => { + await saveBackendChoice(id); + setActiveBackendId(id); + }; + + const handleSelectAnnotator = async (id: string) => { + await saveAnnotatorChoice(id); + setActiveAnnotatorId(id); + }; + + const handleDeleteFile = (filePath: string) => { + fs.unlinkSync(filePath); + refreshRecent(); + }; + + if (lastCapture) { + return ( + copyToClipboard(lastCapture, prefs.autoclose_vicinae)} + onAnnotate={handleAnnotate} + onRefreshPreview={refreshPreview} + onReshoot={() => { + setLastCapture(null); + capture("area"); + }} + onDiscard={() => setLastCapture(null)} + /> + ); + } + + return ( + + ); +} diff --git a/extensions/vscrot/src/window.tsx b/extensions/vscrot/src/window.tsx new file mode 100644 index 00000000..e9ff3adb --- /dev/null +++ b/extensions/vscrot/src/window.tsx @@ -0,0 +1,87 @@ +import { useState, useEffect } from "react"; +import { closeMainWindow } from "@vicinae/api"; +import { getPrefs } from "./lib/preferences"; +import { formatDateTokens } from "./lib/dateFormat"; +import { TEMP_PATH, getSavePath, saveImageFile } from "./lib/filesystem"; +import { copyToClipboard } from "./lib/clipboard"; +import { annotateWith } from "./lib/annotate"; +import { captureScreenshot } from "./lib/capture"; +import { getAnnotator } from "./annotators"; +import { resolveBackend, resolveAnnotator } from "./lib/tool-selection"; +import { PreviewDetail } from "./components/PreviewDetail"; + +export default function CaptureWindow() { + const prefs = getPrefs(); + const [lastCapture, setLastCapture] = useState(null); + const [activeBackendId, setActiveBackendId] = useState(null); + const [activeAnnotatorId, setActiveAnnotatorId] = useState( + null, + ); + + const refreshPreview = () => { + setLastCapture(null); + setTimeout(() => setLastCapture(TEMP_PATH), 100); + }; + + const capture = async (backendId: string) => { + const result = await captureScreenshot("window", backendId); + if (result) setLastCapture(result); + }; + + useEffect(() => { + Promise.all([ + resolveBackend(prefs.screenshot_tool ?? "auto"), + resolveAnnotator(prefs.annotation_tool ?? "auto"), + ]).then(([backend, annotator]) => { + const bid = backend?.id ?? null; + setActiveBackendId(bid); + setActiveAnnotatorId(annotator?.id ?? null); + if (bid) capture(bid); + }); + }, []); + + const activeAnnotator = activeAnnotatorId + ? getAnnotator(activeAnnotatorId) + : null; + const annotatorLabel = + activeAnnotator && activeAnnotator.id !== "none" + ? `Annotate (${activeAnnotator.displayName})` + : null; + + const handleSave = () => { + if (!lastCapture) return; + saveImageFile(lastCapture, getSavePath(prefs)); + setLastCapture(null); + if (prefs.autoclose_vicinae) closeMainWindow(); + }; + + const handleAnnotate = async () => { + if (!lastCapture || !activeAnnotatorId) return; + const shouldReload = await annotateWith(lastCapture, activeAnnotatorId); + if (shouldReload) refreshPreview(); + }; + + if (!lastCapture) return null; + + return ( + copyToClipboard(lastCapture, prefs.autoclose_vicinae)} + onAnnotate={handleAnnotate} + onRefreshPreview={refreshPreview} + onReshoot={() => { + setLastCapture(null); + if (activeBackendId) capture(activeBackendId); + }} + onDiscard={() => setLastCapture(null)} + /> + ); +} diff --git a/extensions/vscrot/tsconfig.json b/extensions/vscrot/tsconfig.json new file mode 100644 index 00000000..8d9b6bbc --- /dev/null +++ b/extensions/vscrot/tsconfig.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 16", + "include": ["src/**/*"], + "compilerOptions": { + //"lib": ["es2020"], + "module": "commonjs", + "target": "es2020", + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "jsx": "react-jsx", + "types": ["node"] + } +} From 4139ae0ae66de6b9d4c9a78a4ce64cafb7822409 Mon Sep 17 00:00:00 2001 From: jaintp Date: Sun, 17 May 2026 11:07:35 +1000 Subject: [PATCH 2/2] fix(vscrot): Fixed all issues reported by review skill (Claude Sonnet 4.6 (Thinking)) --- extensions/vscrot/src/annotators/gimp.ts | 4 +- extensions/vscrot/src/annotators/mspaint.ts | 3 +- extensions/vscrot/src/annotators/pinta.ts | 4 +- extensions/vscrot/src/annotators/satty.ts | 5 +- extensions/vscrot/src/annotators/swappy.ts | 5 +- extensions/vscrot/src/backends/flameshot.ts | 7 +-- .../vscrot/src/backends/gnome-screenshot.ts | 9 ++-- extensions/vscrot/src/backends/grim.ts | 9 ++-- extensions/vscrot/src/backends/grimblast.ts | 11 +++-- extensions/vscrot/src/backends/maim.ts | 9 ++-- .../vscrot/src/backends/screencapture.ts | 8 ++-- extensions/vscrot/src/backends/spectacle.ts | 4 +- extensions/vscrot/src/backends/utils.ts | 3 ++ extensions/vscrot/src/backends/x11-scrot.ts | 9 ++-- .../vscrot/src/components/PreviewDetail.tsx | 3 +- extensions/vscrot/src/lib/clipboard.ts | 48 ++++++------------- extensions/vscrot/src/scrot.tsx | 26 +++++++++- 17 files changed, 94 insertions(+), 73 deletions(-) diff --git a/extensions/vscrot/src/annotators/gimp.ts b/extensions/vscrot/src/annotators/gimp.ts index 0d209b3f..b5467da1 100644 --- a/extensions/vscrot/src/annotators/gimp.ts +++ b/extensions/vscrot/src/annotators/gimp.ts @@ -1,6 +1,6 @@ import { exec } from "node:child_process"; import type { AnnotatorBackend } from "./types"; -import { isCommandAvailable } from "../backends/utils"; +import { isCommandAvailable, shellEscape } from "../backends/utils"; export const gimpAnnotator: AnnotatorBackend = { id: "gimp", @@ -11,7 +11,7 @@ export const gimpAnnotator: AnnotatorBackend = { annotate: async (imagePath: string) => { await new Promise((resolve) => { - exec(`gimp "${imagePath}"`, () => resolve()); + exec(`gimp "${shellEscape(imagePath)}"`, () => resolve()); }); }, }; diff --git a/extensions/vscrot/src/annotators/mspaint.ts b/extensions/vscrot/src/annotators/mspaint.ts index 12fa2226..0795286d 100644 --- a/extensions/vscrot/src/annotators/mspaint.ts +++ b/extensions/vscrot/src/annotators/mspaint.ts @@ -1,5 +1,6 @@ import { exec } from "node:child_process"; import type { AnnotatorBackend } from "./types"; +import { shellEscape } from "../backends/utils"; export const mspaintAnnotator: AnnotatorBackend = { id: "mspaint", @@ -10,7 +11,7 @@ export const mspaintAnnotator: AnnotatorBackend = { annotate: async (imagePath: string) => { await new Promise((resolve) => { - exec(`mspaint.exe "${imagePath}"`, () => resolve()); + exec(`mspaint.exe "${shellEscape(imagePath)}"`, () => resolve()); }); }, }; diff --git a/extensions/vscrot/src/annotators/pinta.ts b/extensions/vscrot/src/annotators/pinta.ts index 3e85e634..7da0e0bd 100644 --- a/extensions/vscrot/src/annotators/pinta.ts +++ b/extensions/vscrot/src/annotators/pinta.ts @@ -1,6 +1,6 @@ import { exec } from "node:child_process"; import type { AnnotatorBackend } from "./types"; -import { isCommandAvailable } from "../backends/utils"; +import { isCommandAvailable, shellEscape } from "../backends/utils"; export const pintaAnnotator: AnnotatorBackend = { id: "pinta", @@ -11,7 +11,7 @@ export const pintaAnnotator: AnnotatorBackend = { annotate: async (imagePath: string) => { await new Promise((resolve) => { - exec(`pinta "${imagePath}"`, () => resolve()); + exec(`pinta "${shellEscape(imagePath)}"`, () => resolve()); }); }, }; diff --git a/extensions/vscrot/src/annotators/satty.ts b/extensions/vscrot/src/annotators/satty.ts index 086c6ba3..46b8115b 100644 --- a/extensions/vscrot/src/annotators/satty.ts +++ b/extensions/vscrot/src/annotators/satty.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { AnnotatorBackend } from "./types"; -import { isCommandAvailable } from "../backends/utils"; +import { isCommandAvailable, shellEscape } from "../backends/utils"; export const sattyAnnotator: AnnotatorBackend = { id: "satty", @@ -10,8 +10,9 @@ export const sattyAnnotator: AnnotatorBackend = { isAvailable: () => isCommandAvailable("satty"), annotate: async (imagePath: string) => { + const p = shellEscape(imagePath); execSync( - `satty --filename "${imagePath}" --output-filename "${imagePath}" --early-exit`, + `satty --filename "${p}" --output-filename "${p}" --early-exit`, ); }, }; diff --git a/extensions/vscrot/src/annotators/swappy.ts b/extensions/vscrot/src/annotators/swappy.ts index 2ec78417..5d3fa5e2 100644 --- a/extensions/vscrot/src/annotators/swappy.ts +++ b/extensions/vscrot/src/annotators/swappy.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { AnnotatorBackend } from "./types"; -import { isCommandAvailable } from "../backends/utils"; +import { isCommandAvailable, shellEscape } from "../backends/utils"; export const swappyAnnotator: AnnotatorBackend = { id: "swappy", @@ -10,6 +10,7 @@ export const swappyAnnotator: AnnotatorBackend = { isAvailable: () => isCommandAvailable("swappy"), annotate: async (imagePath: string) => { - execSync(`swappy -f "${imagePath}" -o "${imagePath}"`); + const p = shellEscape(imagePath); + execSync(`swappy -f "${p}" -o "${p}"`); }, }; diff --git a/extensions/vscrot/src/backends/flameshot.ts b/extensions/vscrot/src/backends/flameshot.ts index a994a39e..e6440354 100644 --- a/extensions/vscrot/src/backends/flameshot.ts +++ b/extensions/vscrot/src/backends/flameshot.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable } from "./utils"; +import { isCommandAvailable, shellEscape } from "./utils"; export const flameshotBackend: CaptureBackend = { id: "flameshot", @@ -10,10 +10,11 @@ export const flameshotBackend: CaptureBackend = { isAvailable: () => isCommandAvailable("flameshot"), capture: async (mode: CaptureMode, outputPath: string) => { + const out = shellEscape(outputPath); if (mode === "area") { - execSync(`flameshot gui --raw > "${outputPath}"`); + execSync(`flameshot gui --raw > "${out}"`); } else { - execSync(`flameshot screen --raw > "${outputPath}"`); + execSync(`flameshot screen --raw > "${out}"`); } }, }; diff --git a/extensions/vscrot/src/backends/gnome-screenshot.ts b/extensions/vscrot/src/backends/gnome-screenshot.ts index e419be0e..bbc39081 100644 --- a/extensions/vscrot/src/backends/gnome-screenshot.ts +++ b/extensions/vscrot/src/backends/gnome-screenshot.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable } from "./utils"; +import { isCommandAvailable, shellEscape } from "./utils"; export const gnomeScreenshotBackend: CaptureBackend = { id: "gnome-screenshot", @@ -10,12 +10,13 @@ export const gnomeScreenshotBackend: CaptureBackend = { isAvailable: () => isCommandAvailable("gnome-screenshot"), capture: async (mode: CaptureMode, outputPath: string) => { + const out = shellEscape(outputPath); if (mode === "area") { - execSync(`gnome-screenshot -a -f "${outputPath}"`); + execSync(`gnome-screenshot -a -f "${out}"`); } else if (mode === "window") { - execSync(`gnome-screenshot -w -f "${outputPath}"`); + execSync(`gnome-screenshot -w -f "${out}"`); } else { - execSync(`gnome-screenshot -f "${outputPath}"`); + execSync(`gnome-screenshot -f "${out}"`); } }, }; diff --git a/extensions/vscrot/src/backends/grim.ts b/extensions/vscrot/src/backends/grim.ts index 11c8c45b..994ff1fd 100644 --- a/extensions/vscrot/src/backends/grim.ts +++ b/extensions/vscrot/src/backends/grim.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable, selectMonitor } from "./utils"; +import { isCommandAvailable, selectMonitor, shellEscape } from "./utils"; export const grimBackend: CaptureBackend = { id: "grim", @@ -14,6 +14,7 @@ export const grimBackend: CaptureBackend = { outputPath: string, outputName?: string, ) => { + const out = shellEscape(outputPath); let geometry = ""; if (mode === "area") { geometry = execSync("slurp").toString().trim(); @@ -24,10 +25,10 @@ export const grimBackend: CaptureBackend = { .toString() .trim(); } else if (mode === "monitor") { - const name = outputName ?? selectMonitor(); - execSync(`grim -o "${name}" "${outputPath}"`); + const name = shellEscape(outputName ?? selectMonitor()); + execSync(`grim -o "${name}" "${out}"`); return; } - execSync(`grim ${geometry ? `-g "${geometry}"` : ""} "${outputPath}"`); + execSync(`grim ${geometry ? `-g "${shellEscape(geometry)}"` : ""} "${out}"`); }, }; diff --git a/extensions/vscrot/src/backends/grimblast.ts b/extensions/vscrot/src/backends/grimblast.ts index ab5ea168..a9a585f6 100644 --- a/extensions/vscrot/src/backends/grimblast.ts +++ b/extensions/vscrot/src/backends/grimblast.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable, selectMonitor } from "./utils"; +import { isCommandAvailable, selectMonitor, shellEscape } from "./utils"; const MODE_MAP: Record = { area: "area", @@ -21,11 +21,12 @@ export const grimblastBackend: CaptureBackend = { outputPath: string, outputName?: string, ) => { + const out = shellEscape(outputPath); if (mode === "monitor") { // grimblast save output captures the focused output, which races with // closeMainWindow() shifting focus. Use grim -o with named output instead. - const name = outputName ?? selectMonitor(); - execSync(`grim -o "${name}" "${outputPath}"`); + const name = shellEscape(outputName ?? selectMonitor()); + execSync(`grim -o "${name}" "${out}"`); } else if (mode === "window") { // grimblast save active captures whatever window has focus after Vicinae closes. // Use hyprctl + slurp for reliable interactive window selection instead. @@ -34,9 +35,9 @@ export const grimblastBackend: CaptureBackend = { ) .toString() .trim(); - execSync(`grim -g "${geometry}" "${outputPath}"`); + execSync(`grim -g "${shellEscape(geometry)}" "${out}"`); } else { - execSync(`grimblast save ${MODE_MAP[mode]} "${outputPath}"`); + execSync(`grimblast save ${MODE_MAP[mode]} "${out}"`); } }, }; diff --git a/extensions/vscrot/src/backends/maim.ts b/extensions/vscrot/src/backends/maim.ts index e6c83bcf..39eceded 100644 --- a/extensions/vscrot/src/backends/maim.ts +++ b/extensions/vscrot/src/backends/maim.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable } from "./utils"; +import { isCommandAvailable, shellEscape } from "./utils"; export const maimBackend: CaptureBackend = { id: "maim", @@ -10,14 +10,15 @@ export const maimBackend: CaptureBackend = { isAvailable: () => isCommandAvailable("maim"), capture: async (mode: CaptureMode, outputPath: string) => { + const out = shellEscape(outputPath); if (mode === "area") { const geometry = execSync("slop -f '%x,%y %wx%h'").toString().trim(); - execSync(`maim -g "${geometry}" "${outputPath}"`); + execSync(`maim -g "${shellEscape(geometry)}" "${out}"`); } else if (mode === "window") { const id = execSync("xdotool getactivewindow").toString().trim(); - execSync(`maim -i "${id}" "${outputPath}"`); + execSync(`maim -i "${shellEscape(id)}" "${out}"`); } else { - execSync(`maim "${outputPath}"`); + execSync(`maim "${out}"`); } }, }; diff --git a/extensions/vscrot/src/backends/screencapture.ts b/extensions/vscrot/src/backends/screencapture.ts index 9b5126ee..c09f2d31 100644 --- a/extensions/vscrot/src/backends/screencapture.ts +++ b/extensions/vscrot/src/backends/screencapture.ts @@ -1,5 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; +import { shellEscape } from "./utils"; export const screencaptureBackend: CaptureBackend = { id: "screencapture", @@ -9,12 +10,13 @@ export const screencaptureBackend: CaptureBackend = { isAvailable: () => process.platform === "darwin", capture: async (mode: CaptureMode, outputPath: string) => { + const out = shellEscape(outputPath); if (mode === "area") { - execSync(`screencapture -i "${outputPath}"`); + execSync(`screencapture -i "${out}"`); } else if (mode === "window") { - execSync(`screencapture -w "${outputPath}"`); + execSync(`screencapture -w "${out}"`); } else { - execSync(`screencapture "${outputPath}"`); + execSync(`screencapture "${out}"`); } }, }; diff --git a/extensions/vscrot/src/backends/spectacle.ts b/extensions/vscrot/src/backends/spectacle.ts index 6459057a..71df95d5 100644 --- a/extensions/vscrot/src/backends/spectacle.ts +++ b/extensions/vscrot/src/backends/spectacle.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable } from "./utils"; +import { isCommandAvailable, shellEscape } from "./utils"; const FLAG_MAP: Record = { area: "-r", @@ -17,6 +17,6 @@ export const spectacleBackend: CaptureBackend = { isAvailable: () => isCommandAvailable("spectacle"), capture: async (mode: CaptureMode, outputPath: string) => { - execSync(`spectacle ${FLAG_MAP[mode]} -b -o "${outputPath}"`); + execSync(`spectacle ${FLAG_MAP[mode]} -b -o "${shellEscape(outputPath)}"`); }, }; diff --git a/extensions/vscrot/src/backends/utils.ts b/extensions/vscrot/src/backends/utils.ts index 8562294c..eab7e455 100644 --- a/extensions/vscrot/src/backends/utils.ts +++ b/extensions/vscrot/src/backends/utils.ts @@ -1,5 +1,8 @@ import { execSync } from "node:child_process"; +/** Escape a path for safe use inside double-quoted shell strings. */ +export const shellEscape = (p: string): string => p.replace(/"/g, '\\"'); + export const isCommandAvailable = (cmd: string): boolean => { try { execSync(`command -v ${cmd}`, { stdio: "ignore" }); diff --git a/extensions/vscrot/src/backends/x11-scrot.ts b/extensions/vscrot/src/backends/x11-scrot.ts index 55cbce79..1130ca83 100644 --- a/extensions/vscrot/src/backends/x11-scrot.ts +++ b/extensions/vscrot/src/backends/x11-scrot.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import type { CaptureBackend, CaptureMode } from "./types"; -import { isCommandAvailable } from "./utils"; +import { isCommandAvailable, shellEscape } from "./utils"; export const x11ScrotBackend: CaptureBackend = { id: "scrot", @@ -10,12 +10,13 @@ export const x11ScrotBackend: CaptureBackend = { isAvailable: () => isCommandAvailable("scrot"), capture: async (mode: CaptureMode, outputPath: string) => { + const out = shellEscape(outputPath); if (mode === "area") { - execSync(`scrot -s "${outputPath}"`); + execSync(`scrot -s "${out}"`); } else if (mode === "window") { - execSync(`scrot -u "${outputPath}"`); + execSync(`scrot -u "${out}"`); } else { - execSync(`scrot "${outputPath}"`); + execSync(`scrot "${out}"`); } }, }; diff --git a/extensions/vscrot/src/components/PreviewDetail.tsx b/extensions/vscrot/src/components/PreviewDetail.tsx index 575c842c..3befd72c 100644 --- a/extensions/vscrot/src/components/PreviewDetail.tsx +++ b/extensions/vscrot/src/components/PreviewDetail.tsx @@ -1,5 +1,6 @@ import { Action, ActionPanel, Detail, Icon } from "@vicinae/api"; import { execSync } from "node:child_process"; +import { shellEscape } from "../backends/utils"; interface PreviewDetailProps { imagePath: string; @@ -28,7 +29,7 @@ export function PreviewDetail({ }: PreviewDetailProps) { let dimensions = "unknown"; try { - dimensions = execSync(`identify -format "%wx%h" "${imagePath}"`) + dimensions = execSync(`identify -format "%wx%h" "${shellEscape(imagePath)}"`) .toString() .trim(); } catch { diff --git a/extensions/vscrot/src/lib/clipboard.ts b/extensions/vscrot/src/lib/clipboard.ts index d3d7ee1d..40b60e69 100644 --- a/extensions/vscrot/src/lib/clipboard.ts +++ b/extensions/vscrot/src/lib/clipboard.ts @@ -1,43 +1,25 @@ -import { execSync } from "node:child_process"; -import { closeMainWindow, showToast } from "@vicinae/api"; +import { Clipboard, closeMainWindow, showHUD, showToast } from "@vicinae/api"; -export const copyTextToClipboard = (text: string): void => { - const escaped = text.replace(/'/g, "'\\''"); +export const copyTextToClipboard = async (text: string): Promise => { try { - if (process.platform === "darwin") { - execSync(`printf '%s' '${escaped}' | pbcopy`); - } else if (process.platform === "win32") { - execSync(`powershell -Command "Set-Clipboard -Value '${escaped}'"`); - } else { - try { - execSync(`printf '%s' '${escaped}' | wl-copy`); - } catch { - execSync(`printf '%s' '${escaped}' | xclip -selection clipboard`); - } - } + await Clipboard.copy(text); } catch (e) { console.error("Failed to copy text to clipboard", e); - showHUD("Clipboard copy failed - install wl-clipboard or xclip"); + showHUD("Clipboard copy failed"); } }; -export const copyToClipboard = (imagePath: string, autoClose = true): void => { - if (process.platform === "darwin") { - execSync( - `osascript -e 'set the clipboard to (read (POSIX file "${imagePath}") as «class PNGf»)'`, - ); - } else if (process.platform === "win32") { - execSync( - `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::SetImage([System.Drawing.Image]::FromFile('${imagePath}'))"`, - ); - } else { - // Prefer wl-copy (Wayland), fall back to xclip (X11) - try { - execSync(`wl-copy < "${imagePath}"`); - } catch { - execSync(`xclip -selection clipboard -t image/png < "${imagePath}"`); - } +export const copyToClipboard = async ( + imagePath: string, + autoClose = true, +): Promise => { + try { + await Clipboard.copy({ file: imagePath }); + showToast({ title: "Copied to clipboard" }); + } catch (e) { + console.error("Failed to copy image to clipboard", e); + showHUD("Clipboard copy failed"); + return; } - showToast({ title: "Copied to clipboard" }); if (autoClose) closeMainWindow(); }; diff --git a/extensions/vscrot/src/scrot.tsx b/extensions/vscrot/src/scrot.tsx index 44bbaaac..27ff72f6 100644 --- a/extensions/vscrot/src/scrot.tsx +++ b/extensions/vscrot/src/scrot.tsx @@ -87,7 +87,31 @@ export default function Scrot() { const capture = async (mode: CaptureMode) => { if (!activeBackendId) return; const result = await captureScreenshot(mode, activeBackendId); - if (result) setLastCapture(result); + if (!result) return; + + // Auto-annotate if the preference is set and an annotator is configured + if (prefs.use_editor && activeAnnotatorId) { + const shouldReload = await annotateWith(result, activeAnnotatorId); + if (shouldReload) { + // Auto-reload tools (Satty, swappy) wrote the file back — pick it up + // Fall through to auto-copy/save with the (now annotated) file + } + } + + // Auto-copy and/or auto-save if the preferences say so + if (prefs.copy_to_clipboard) await copyToClipboard(result, false); + if (prefs.save_to_file) { + saveImageFile(result, getSavePath(prefs)); + refreshRecent(); + } + + // Close if autoclose is on and at least one auto-action ran + if ((prefs.copy_to_clipboard || prefs.save_to_file) && prefs.autoclose_vicinae) { + closeMainWindow(); + return; + } + + setLastCapture(result); }; const handleSave = () => {