### Install and Start Documentation Development Server Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Commands to install documentation dependencies and start the local development server for previewing documentation changes. ```bash pnpm install ``` ```bash pnpm start ``` -------------------------------- ### Install Node.js Dependencies with pnpm Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Use this command to install project dependencies when setting up the development environment. ScriptCat uses pnpm for dependency management. ```bash pnpm install ``` -------------------------------- ### Start ScriptCat Local Development Server Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Commands to start the local development server for ScriptCat. The `dev:noMap` command is a workaround for potential issues with incognito windows. ```bash pnpm run dev ``` ```bash pnpm run dev:noMap ``` -------------------------------- ### Storage Operations Source: https://context7.com/scriptscat/scriptcat/llms.txt Demonstrates asynchronous operations for getting, setting, and listing values in storage. ```APIDOC ## Storage Operations ### Description Asynchronous functions for managing script storage. ### Methods - **GM.setValue(key, value)**: Asynchronously stores a value under a given key. - **GM.getValue(key, defaultValue)**: Asynchronously retrieves the value associated with a key, with an optional default value. - **GM.deleteValue(key)**: Asynchronously removes a key-value pair from storage. - **GM.listValues()**: Asynchronously retrieves a list of all keys currently in storage. - **GM.setValues(object)**: Asynchronously stores multiple key-value pairs from an object. - **GM.getValues(keys)**: Asynchronously retrieves multiple values based on an array of keys. - **GM.deleteValues(keys)**: Asynchronously removes multiple key-value pairs based on an array of keys. ### Request Example (Setting a value) ```javascript await GM.setValue("user", { name: "Alice", score: 42 }); ``` ### Response Example (Getting a value) ```javascript const user = await GM.getValue("user", null); console.log(user); // { name: "Alice", score: 42 } ``` ### Response Example (Listing values) ```javascript const keys = await GM.listValues(); console.log(keys); // ["key1", "key2", ...] ``` ``` -------------------------------- ### Commit Message Example with Gitmoji Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Example of a commit message following the gitmoji specification to indicate the addition of a new feature. ```bash git commit -m "✨ add login feature" ``` -------------------------------- ### Access Bundled Static Resources with GM_getResourceText/URL Source: https://context7.com/scriptscat/scriptcat/llms.txt Use GM_getResourceText to read resource content as a string and GM_getResourceURL to get a URL for the resource. The second argument to GM_getResourceURL can be set to true to obtain a Blob URL, which is revocable and suitable for large files. ```javascript // ==UserScript== // @name Resource Demo // @match *://*/* // @resource config https://cdn.example.com/config.json // @resource logo https://cdn.example.com/logo.png // @resource tmpl https://cdn.example.com/template.html // @grant GM_getResourceText // @grant GM_getResourceURL // ==/UserScript== // Read resource as text const rawJson = GM_getResourceText("config"); const config = JSON.parse(rawJson); console.log("Config loaded:", config); // Get data URL (default) — safe for const logoDataUrl = GM_getResourceURL("logo"); const imgEl = document.createElement("img"); imgEl.src = logoDataUrl; document.body.prepend(imgEl); // Get Blob URL (isBlobUrl = true) — revocable, better for large files const logoBlobUrl = GM_getResourceURL("logo", true); console.log("Blob URL:", logoBlobUrl); // Inject an HTML template resource into the DOM const html = GM_getResourceText("tmpl"); const container = document.createElement("div"); container.innerHTML = html; document.body.appendChild(container); // Async variant (async () => { const url = await GM.getResourceUrl("config"); // note: GM.getResourceUrl (lowercase u) const resp = await fetch(url); const data = await resp.json(); console.log("Async config:", data); })(); ``` -------------------------------- ### Run ESLint and Vitest for Development Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Commands to run ESLint for code style checks and Vitest for unit testing. These tools are used to maintain code quality in ScriptCat. ```bash pnpm test ``` ```bash pnpm run lint ``` -------------------------------- ### Pre-Page Injection with @early-start Source: https://context7.com/scriptscat/scriptcat/llms.txt Use `@early-start` to inject scripts before any page scripts. `CAT_scriptLoaded()` is provided to await the full initialization of GM APIs, especially for asynchronous operations. ```javascript // ==UserScript== // @name Early Start Demo // @match https://example.com/* // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant CAT_scriptLoaded // @early-start // ==/UserScript== // Runs before *any* page script — GM_getValue/GM_setValue work immediately console.log("Stored value:", GM_getValue("key", "default")); GM_setValue("key", "new-value"); // Intercept events before the page registers its own listeners const _addEventListener = document.addEventListener.bind(document); document.addEventListener = function (type, handler, ...rest) { if (type === "click") { const original = handler; handler = function (e) { // Make every click appear trusted const fakeEvent = Object.create(MouseEvent.prototype); Object.assign(fakeEvent, e, { isTrusted: true }); original.call(this, fakeEvent); }; } _addEventListener(type, handler, ...rest); }; // Wait for GM APIs to be fully initialised (async APIs may need this) CAT_scriptLoaded().then(() => { console.log("All GM APIs are now ready"); }); ``` -------------------------------- ### Async GM API Demo Source: https://context7.com/scriptscat/scriptcat/llms.txt This script demonstrates various asynchronous Greasemonkey API calls including storage, XHR, notifications, tab opening, clipboard, styling, resources, and cookies. Ensure all required @grant declarations are present in the UserScript header. ```javascript // ==UserScript== // @name Async GM API Demo // @match *://*/* // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM.listValues // @grant GM.xmlHttpRequest // @grant GM.notification // @grant GM.openInTab // @grant GM.setClipboard // @grant GM.addStyle // @resource tmpl https://example.com/template.html // @grant GM.getResourceUrl // ==/UserScript== (async () => { // Storage await GM.setValue("user", { name: "Alice", score: 42 }); const user = await GM.getValue("user", null); console.log("User:", user); // { name: "Alice", score: 42 } const keys = await GM.listValues(); console.log("Keys:", keys); // Batch operations await GM.setValues({ a: 1, b: 2, c: 3 }); const multi = await GM.getValues(["a", "b"]); console.log("a, b:", multi); // { a: 1, b: 2 } await GM.deleteValues(["a", "b", "c"]); // XHR const resp = await GM.xmlHttpRequest({ method: "GET", url: "https://api.github.com/zen", responseType: "text", }); console.log("GitHub zen:", resp.responseText); // Notifications await GM.notification({ title: "Done", text: "Async flow complete", timeout: 3000 }); // Open a tab const tab = await GM.openInTab("https://scriptcat.org", { active: false }); tab.onclose = () => console.log("Tab closed"); setTimeout(() => tab.close(), 5000); // Clipboard await GM.setClipboard("Copied via GM.setClipboard!"); // Style injection await GM.addStyle("body { border: 3px solid #4fc3f7 !important; }"); // Resource const url = await GM.getResourceUrl("tmpl"); console.log("Resource URL:", url); // Cookie (Promise style) await GM.cookie.set({ url: "https://example.com/", name: "k", value: "v" }); const cookies = await GM.cookie.list({ domain: "example.com" }); console.log("Cookies:", cookies); await GM.cookie.delete({ url: "https://example.com/", name: "k" }); })(); ``` -------------------------------- ### Package ScriptCat Extension Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Command to package the ScriptCat extension for distribution. Ensure the `scriptcat.pem` file exists in the `dist` directory before running. ```bash pnpm run pack ``` -------------------------------- ### HTTP Requests Source: https://context7.com/scriptscat/scriptcat/llms.txt Shows how to perform asynchronous HTTP requests using `GM.xmlHttpRequest`. ```APIDOC ## GM.xmlHttpRequest ### Description Performs an asynchronous HTTP request. This is useful for bypassing CORS restrictions. ### Method `GM.xmlHttpRequest(details)` ### Parameters - **details** (object) - Required - An object containing the request details. - **method** (string) - Required - The HTTP method (e.g., "GET", "POST"). - **url** (string) - Required - The URL to request. - **responseType** (string) - Optional - The expected response type (e.g., "text", "json", "arraybuffer"). - ... other standard XHR properties ### Request Example ```javascript const resp = await GM.xmlHttpRequest({ method: "GET", url: "https://api.github.com/zen", responseType: "text", }); ``` ### Response Example (Success) - **responseText** (string) - The response body as text. - **response** (any) - The response body, parsed according to `responseType`. - **status** (number) - The HTTP status code. - **statusText** (string) - The HTTP status text. ```javascript console.log("GitHub zen:", resp.responseText); ``` ``` -------------------------------- ### Notifications Source: https://context7.com/scriptscat/scriptcat/llms.txt How to display asynchronous notifications to the user. ```APIDOC ## GM.notification ### Description Displays a notification to the user. ### Method `GM.notification(details)` ### Parameters - **details** (object) - Required - An object containing notification details. - **title** (string) - Required - The title of the notification. - **text** (string) - Required - The main content of the notification. - **timeout** (number) - Optional - The duration in milliseconds the notification should be displayed. ### Request Example ```javascript await GM.notification({ title: "Done", text: "Async flow complete", timeout: 3000 }); ``` ``` -------------------------------- ### Define User Configuration UI with UserConfig Source: https://context7.com/scriptscat/scriptcat/llms.txt Define a YAML configuration schema within a `==UserConfig==` block to automatically generate a settings UI panel. ScriptCat stores values using `GM_getValue`/`GM_setValue` and makes them accessible via `GM_info.userConfig`. ```javascript // ==UserScript== // @name UserConfig Demo // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant CAT_userConfig // ==/UserScript== /* ==UserConfig== general: username: title: Username description: Your display name type: text default: Guest min: 2 max: 20 darkMode: title: Dark Mode type: checkbox default: false theme: title: Color Theme type: select default: blue values: [blue, green, red, purple] fontSize: title: Font Size (px) type: number default: 14 min: 10 max: 32 unit: px notes: title: Notes type: textarea default: "" rows: 4 ==/UserConfig== */ // Read config values using GM_info.userConfig for default values const rawConfig = GM_info.userConfig; const config = {}; for (const [group, items] of Object.entries(rawConfig)) { for (const [key, { default: def }] of Object.entries(items)) { config[`${group}.${key}`] = GM_getValue(`${group}.${key}`, def); } } console.log("Username:", config["general.username"]); // "Guest" (or user value) console.log("Dark mode:", config["general.darkMode"]); // false (or user value) // Apply settings if (config["general.darkMode"]) { document.body.style.background = "#111"; document.body.style.color = "#eee"; } // Open the config panel programmatically CAT_userConfig(); ``` -------------------------------- ### Resource Handling Source: https://context7.com/scriptscat/scriptcat/llms.txt Retrieving URLs for resources defined in the script's metadata. ```APIDOC ## GM.getResourceUrl ### Description Gets the URL for a resource declared in the `@resource` metadata. ### Method `GM.getResourceUrl(name)` ### Parameters - **name** (string) - Required - The name of the resource as defined in `@resource`. ### Request Example ```javascript const url = await GM.getResourceUrl("tmpl"); ``` ### Response Example - **url** (string) - The URL of the requested resource. ```javascript console.log("Resource URL:", url); ``` ``` -------------------------------- ### GM_download / GM.download Source: https://context7.com/scriptscat/scriptcat/llms.txt Provides functionality to trigger file downloads directly to the user's disk. It supports custom filenames, headers, and progress callbacks, with options for both native-XHR and browser-API download modes. ```APIDOC ## GM_download / GM.download — Trigger File Downloads Download a URL or Blob directly to the user's disk. Supports custom filenames, headers, progress callbacks, and both native-XHR and browser-API download modes. ### Parameters - `url` (string): The URL of the file to download or a Blob URL. - `name` (string, optional): The desired filename for the downloaded file. - `headers` (object, optional): Custom headers to include in the download request. - `onprogress` (function, optional): Callback function to track download progress. - `data` (object): Contains `loaded` and `total` bytes. - `onload` (function, optional): Callback function when the download is complete. - `onerror` (function, optional): Callback function for download errors. - `err` (object): Contains error details. - `conflictAction` (string, optional): Action to take if the file already exists ('overwrite', 'prompt', 'uniquify'). ### Returns - `handle` (object): An object representing the download, with an `abort()` method. ### Example Usage (Download with Progress) ```javascript const handle = GM_download({ url: "https://scriptcat.org/api/v2/open/crx-download/ndcooeababalnlpkfedmmbbbgkljhpjf", name: "scriptcat.crx", headers: { referer: "https://scriptcat.org/" }, onprogress(data) { const pct = data.lengthComputable ? Math.round((data.loaded / data.total) * 100) : "?"; console.log(`Progress: ${pct}%`); }, onload() { console.log("Download complete!"); }, onerror(err) { console.error("Download error:", err.error); }, }); // Abort after 5 s setTimeout(() => handle.abort(), 5000); ``` ### Example Usage (Download Blob) ```javascript const pixels = new Uint8Array([ 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a, // PNG header /* ... rest of PNG bytes ... */ ]); GM_download({ url: URL.createObjectURL(new Blob([pixels], { type: "image/png" })), name: "screenshot/output.png", conflictAction: "overwrite", // overwrite if file already exists }); ``` ### Example Usage (Simplified Form) ```javascript GM_download("https://example.com/file.pdf", "document.pdf"); ``` ``` -------------------------------- ### Manage Cloud Files with CAT_fileStorage Source: https://context7.com/scriptscat/scriptcat/llms.txt Use CAT_fileStorage to upload, list, download, and delete files in configured cloud storage. Ensure the storage is configured before use; call CAT_fileStorage('config') to open the UI if needed. ```javascript // ==UserScript== // @name File Storage Demo // @match *://*/* // @grant CAT_fileStorage // ==/UserScript== // Upload a file CAT_fileStorage("upload", { path: "data/report.txt", baseDir: "my-script", // optional custom base dir data: new Blob(["Hello, world!"], { type: "text/plain" }), onload() { console.log("Upload successful"); // List files in the directory CAT_fileStorage("list", { path: "data/", baseDir: "my-script", onload(files) { console.log("Files:", files); // files: [{ name, path, absPath, size, digest, createtime, updatetime }] const report = files.find(f => f.name === "report.txt"); if (!report) return; // Download the file CAT_fileStorage("download", { file: report, // pass full FileInfo (some backends need digest) async onload(blob) { const text = await blob.text(); console.log("Content:", text); // "Hello, world!" // Delete the file CAT_fileStorage("delete", { path: "data/report.txt", baseDir: "my-script", onload() { console.log("Deleted"); }, onerror(err) { console.error("Delete error:", err.code, err.error); }, }); }, onerror(err) { console.error("Download error:", err); }, }); }, onerror(err) { console.error("List error:", err.code); }, }); }, onerror(err) { console.error("Upload failed:", err.code, err.error); // err.code: 1=not configured, 2=config error, 3=path not found, // 4=upload failed, 5=download failed, 6=delete failed, // 7=path not allowed, 8=network error, -1=unknown if (err.code === 1 || err.code === 2) { CAT_fileStorage("config"); // open storage configuration UI } }, }); ``` -------------------------------- ### Control Browser Tabs with GM_openInTab Source: https://context7.com/scriptscat/scriptcat/llms.txt Use GM_openInTab to open URLs in new tabs with options for activation, insertion position, pinning, and incognito mode. It also supports programmatic closing and listening for tab closure events. ```javascript // ==UserScript== // @name Tab Control Demo // @match *://*/* // @grant GM_openInTab // ==/UserScript== // Open in foreground tab, inserted after current tab const tab = GM_openInTab("https://scriptcat.org/search", { active: true, // bring to foreground insert: true, // insert next to current tab setParent: true, // mark current tab as opener pinned: false, }); // Listen for the tab closing tab.onclose = () => console.log("Tab was closed"); // Programmatically close the tab after 5 s setTimeout(() => { if (!tab.closed) tab.close(); }, 5000); // Open in background with window.open (useful for custom protocols) GM_openInTab("vscode://file/home/user/script.js", { useOpen: true }); // context-menu run-at example: search selected text // ==UserScript== // @run-at context-menu // @grant GM_openInTab // ==/UserScript== (function () { const sel = window.getSelection().toString().trim(); if (!sel) return alert("Select some text first."); GM_openInTab(`https://scriptcat.org/search?keyword=${encodeURIComponent(sel)}`); })(); ``` -------------------------------- ### Trigger File Downloads with GM_download Source: https://context7.com/scriptscat/scriptcat/llms.txt GM_download allows direct file downloads to the user's disk. It supports custom filenames, headers, progress callbacks, and conflict resolution. ```javascript // ==UserScript== // @name Download Demo // @match *://*/* // @grant GM_download // ==/UserScript== // Download a remote file with progress tracking const handle = GM_download({ url: "https://scriptcat.org/api/v2/open/crx-download/ndcooeababalnlpkfedmmbbbgkljhpjf", name: "scriptcat.crx", headers: { referer: "https://scriptcat.org/" }, onprogress(data) { const pct = data.lengthComputable ? Math.round((data.loaded / data.total) * 100) : "?"; console.log(`Progress: ${pct}%`); }, onload() { console.log("Download complete!"); }, onerror(err) { console.error("Download error:", err.error); }, }); // Abort after 5 s setTimeout(() => handle.abort(), 5000); // Download a generated Blob (e.g., a dynamically created PNG) const pixels = new Uint8Array([ 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a, // PNG header /* ... rest of PNG bytes ... */ ]); GM_download({ url: URL.createObjectURL(new Blob([pixels], { type: "image/png" })), name: "screenshot/output.png", conflictAction: "overwrite", // overwrite if file already exists }); // Simplified form: GM_download(url, filename) GM_download("https://example.com/file.pdf", "document.pdf"); ``` -------------------------------- ### Clipboard Operations Source: https://context7.com/scriptscat/scriptcat/llms.txt Asynchronously setting the content of the system clipboard. ```APIDOC ## GM.setClipboard ### Description Copies the specified text to the system clipboard. ### Method `GM.setClipboard(text)` ### Parameters - **text** (string) - Required - The text to copy to the clipboard. ### Request Example ```javascript await GM.setClipboard("Copied via GM.setClipboard!"); ``` ``` -------------------------------- ### Schedule Daily Dog Photo Fetch with @crontab Source: https://context7.com/scriptscat/scriptcat/llms.txt This script runs daily at 08:00 using the @crontab directive. It fetches a random dog image and displays a notification. Rejects with CATRetryError to trigger automatic retries on API or network errors. ```javascript // ==UserScript== // @name Scheduled Task — Daily Dog Photo // @namespace https://example.com/ // @version 1.0.0 // @description Fetches a random dog image every day at 08:00 // @crontab 0 8 * * * // standard cron: every day at 08:00 // @grant GM_log // @grant GM_notification // @grant GM_xmlhttpRequest // @connect dog.ceo // ==/UserScript== return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: "https://dog.ceo/api/breeds/image/random", responseType: "json", anonymous: true, onload(resp) { if (resp.status !== 200 || typeof resp.response.message !== "string") { // Retry in 60 seconds on failure reject(new CATRetryError("API error, will retry", 60)); return; } GM_log("Dog of the day: " + resp.response.message, "info"); GM_notification({ title: "Dog of the Day 🐶", text: "Click to view today's dog photo", url: resp.response.message, image: resp.response.message, }); resolve(); }, onerror() { // Retry at a specific time const retryAt = new Date(Date.now() + 5 * 60 * 1000); // 5 min later reject(new CATRetryError("Network error", retryAt)); }, }); }); ``` -------------------------------- ### Style Injection Source: https://context7.com/scriptscat/scriptcat/llms.txt Asynchronously injecting CSS styles into the current page. ```APIDOC ## GM.addStyle ### Description Injects CSS rules into the current page. ### Method `GM.addStyle(css)` ### Parameters - **css** (string) - Required - A string containing CSS rules. ### Request Example ```javascript await GM.addStyle("body { border: 3px solid #4fc3f7 !important; }"); ``` ``` -------------------------------- ### Configure pnpm Proxy Settings Source: https://github.com/scriptscat/scriptcat/blob/main/CONTRIBUTING.md Configure HTTP and HTTPS proxy settings for pnpm to resolve network issues. Replace with your actual proxy address. ```bash pnpm config set proxy http://127.0.0.1:7890 ``` ```bash pnpm config set https-proxy https://127.0.0.1:7890 ``` -------------------------------- ### CAT_fileStorage - File Operations Source: https://context7.com/scriptscat/scriptcat/llms.txt Provides functionality to read, write, list, and delete files in various cloud storage backends. Files are scoped to a per-script directory by default. ```APIDOC ## CAT_fileStorage ### Description Manages cloud and synchronized file storage, allowing users to interact with configured backends like Dropbox, Google Drive, OneDrive, WebDAV, S3, and Baidu. ### Methods - `upload`: Uploads a file to the specified path. - `list`: Lists files in a given directory. - `download`: Downloads a file. - `delete`: Deletes a file. - `config`: Opens the storage configuration UI. ### Parameters for `upload` - **path** (string) - Required - The destination path for the file. - **baseDir** (string) - Optional - A custom base directory for the script. - **data** (Blob) - Required - The file data to upload. - **onload** (function) - Callback function executed on successful upload. - **onerror** (function) - Callback function executed on upload failure. ### Parameters for `list` - **path** (string) - Required - The directory path to list. - **baseDir** (string) - Optional - A custom base directory. - **onload** (function) - Callback function executed with a list of files on success. - **onerror** (function) - Callback function executed on failure. ### Parameters for `download` - **file** (object) - Required - The file information object (obtained from `list`). - **onload** (function) - Callback function executed with the file content (Blob) on success. - **onerror** (function) - Callback function executed on failure. ### Parameters for `delete` - **path** (string) - Required - The path of the file to delete. - **baseDir** (string) - Optional - A custom base directory. - **onload** (function) - Callback function executed on successful deletion. - **onerror** (function) - Callback function executed on failure. ### Error Codes - `1`: Not configured - `2`: Configuration error - `3`: Path not found - `4`: Upload failed - `5`: Download failed - `6`: Delete failed - `7`: Path not allowed - `8`: Network error - `-1`: Unknown error ``` -------------------------------- ### GM_setClipboard Source: https://context7.com/scriptscat/scriptcat/llms.txt Writes text or HTML to the system clipboard without requiring the Clipboard API or user gestures. ```APIDOC ## GM_setClipboard ### Description Writes text or HTML content to the system clipboard. ### Parameters * **data**: (string) - The data to write to the clipboard. * **type**: (string, optional) - The MIME type of the data. Defaults to 'text'. Can be 'html' or a specific MIME type. ### Example ```javascript // Copy plain text GM_setClipboard("Hello from ScriptCat!"); // Copy HTML content GM_setClipboard("Bold text", "html"); ``` ``` -------------------------------- ### Monitor Service Status with @background Source: https://context7.com/scriptscat/scriptcat/llms.txt This background script runs every 5 minutes to monitor service status. It uses GM_setValue to track the previous status and triggers a notification if the service goes down. Rejects with CATRetryError on network issues. ```javascript // ==UserScript== // @name Background Script — Continuous Monitor // @background // @crontab */5 * * * * // run every 5 minutes // @grant GM_setValue // @grant GM_notification // @grant GM_xmlhttpRequest // @connect status.example.com // ==/UserScript== return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: "https://status.example.com/api/health", responseType: "json", onload(resp) { const status = resp.response?.status; const prev = GM_getValue("lastStatus", "ok"); GM_setValue("lastStatus", status); if (status !== "ok" && prev === "ok") { GM_notification({ title: "⚠️ Service Down", text: "example.com is experiencing issues" }); } resolve(); }, onerror() { reject(new CATRetryError("Network unavailable", 60)); }, }); }); ``` -------------------------------- ### Tab Management Source: https://context7.com/scriptscat/scriptcat/llms.txt Managing browser tabs asynchronously. ```APIDOC ## GM.openInTab ### Description Opens a URL in a new tab. ### Method `GM.openInTab(url, options)` ### Parameters - **url** (string) - Required - The URL to open. - **options** (object) - Optional - Configuration for the new tab. - **active** (boolean) - Optional - Whether the new tab should be active. Defaults to true. ### Request Example ```javascript const tab = await GM.openInTab("https://scriptcat.org", { active: false }); ``` ### Response Example - **tab.close()**: A function to close the opened tab. ```javascript tab.onclose = () => console.log("Tab closed"); setTimeout(() => tab.close(), 5000); ``` ``` -------------------------------- ### GM_openInTab / GM_closeInTab Source: https://context7.com/scriptscat/scriptcat/llms.txt Opens URLs in new browser tabs with control over foreground/background state, position, pinning, incognito mode, and the ability to close tabs programmatically or listen for their closure. ```APIDOC ## GM_openInTab / GM_closeInTab ### Description Opens URLs in new browser tabs with various options and provides methods to control and monitor the opened tab. ### Parameters * **url**: (string) - The URL to open. * **options**: (object, optional) - Configuration options for the new tab. * **active**: (boolean) - Whether to bring the tab to the foreground. * **insert**: (boolean) - Whether to insert the tab next to the current tab. * **setParent**: (boolean) - Whether to mark the current tab as the opener. * **pinned**: (boolean) - Whether to pin the tab. * **incognito**: (boolean) - Whether to open in incognito mode. * **useOpen**: (boolean) - Whether to use `window.open` instead of a new tab. ### Returns * **Tab**: An object representing the opened tab, with a `close()` method and an `onclose` event handler. ### Example ```javascript const tab = GM_openInTab("https://scriptcat.org/search", { active: true, insert: true, }); tab.onclose = () => console.log("Tab was closed"); setTimeout(() => { if (!tab.closed) tab.close(); }, 5000); ``` ``` -------------------------------- ### Handling Keyword Conflicts in Translations Source: https://github.com/scriptscat/scriptcat/blob/main/src/locales/README.md Use the 'page.key' format to distinguish between keywords that are the same but have different translations within a page. This ensures correct localization when multiple similar keys exist. ```json { "list": { "confirm_delete": "Are you sure you want to delete? Please note that this is an irreversible operation.", "confirm_update": "Are you sure you want to update? Please note that this is an irreversible operation." } } ``` -------------------------------- ### Write to System Clipboard with GM_setClipboard Source: https://context7.com/scriptscat/scriptcat/llms.txt GM_setClipboard allows writing text or HTML to the system clipboard without user gestures or Clipboard API. It supports plain text, HTML, and custom MIME types. ```javascript // ==UserScript== // @name Clipboard Demo // @match *://*/* // @grant GM_setClipboard // ==/UserScript== // Copy plain text GM_setClipboard("Hello from ScriptCat!"); // Copy HTML content GM_setClipboard("Bold text", "html"); // Copy with explicit MIME type GM_setClipboard("data payload", { type: "text/plain" }); // One-click copy button example const btn = document.createElement("button"); btn.textContent = "Copy page title"; btn.addEventListener("click", () => { GM_setClipboard(document.title, "text"); btn.textContent = "Copied!"; setTimeout(() => { btn.textContent = "Copy page title"; }, 2000); }); document.body.prepend(btn); ``` -------------------------------- ### Listen for Storage Changes with GM_addValueChangeListener Source: https://context7.com/scriptscat/scriptcat/llms.txt Use GM_addValueChangeListener to monitor storage changes across tabs. Remember to call GM_removeValueChangeListener to prevent memory leaks. ```javascript // ==UserScript== // @name Value Change Listener // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @grant GM_removeValueChangeListener // @storageName shared-example // ==/UserScript== // Register a listener — fires when "counter" is changed from any tab const listenerId = GM_addValueChangeListener( "counter", (name, oldValue, newValue, remote, tabId) => { console.log(`[${name}] ${oldValue} → ${newValue}`); console.log("Changed from remote tab?", remote, "tabId:", tabId); } ); // Trigger the listener from this tab GM_setValue("counter", (GM_getValue("counter", 0) + 1)); // Later: clean up to prevent memory leaks setTimeout(() => { GM_removeValueChangeListener(listenerId); console.log("Listener removed"); }, 30000); ``` -------------------------------- ### Manage Browser Cookies with ScriptCat Source: https://context7.com/scriptscat/scriptcat/llms.txt Use GM_cookie or GM.cookie to read, write, and delete browser cookies. Requires `@connect` for the target domain. The callback style (GM_cookie) and promise style (GM.cookie) are supported, along with shorthand methods. ```javascript // ==UserScript== // @name Cookie Demo // @match https://example.com/* // @grant GM_cookie // @grant GM.cookie // @connect example.com // ==/UserScript== // Callback style (GM_cookie) GM_cookie("set", { url: "https://example.com/", domain: ".example.com", path: "/", name: "session", value: "tok_abc123", secure: true, httpOnly: false, expirationDate: Math.floor(Date.now() / 1000) + 86400, // expires in 1 day }, (cookies, err) => { if (err) return console.error("Set failed:", err); console.log("Cookie set"); GM_cookie("list", { domain: "example.com" }, (list, err) => { if (err) return console.error(err); console.log("Cookies:", list.map(c => `${c.name}=${c.value}`)); }); }); GM_cookie("delete", { url: "https://example.com/", name: "session" }, () => { console.log("Cookie deleted"); }); // Shorthand sub-methods GM_cookie.list({ domain: "example.com" }, (list) => console.log(list)); GM_cookie.set({ url: "https://example.com/", name: "x", value: "1" }, () => {}); GM_cookie.delete({ url: "https://example.com/", name: "x" }, () => {}); // Promise style (GM.cookie) (async () => { await GM.cookie.set({ url: "https://example.com/", name: "foo", value: "bar" }); const cookies = await GM.cookie.list({ domain: "example.com" }); console.log("Found cookies:", cookies); await GM.cookie.delete({ url: "https://example.com/", name: "foo" }); })(); ``` -------------------------------- ### GM_addValueChangeListener / GM_removeValueChangeListener Source: https://context7.com/scriptscat/scriptcat/llms.txt Allows scripts to listen for real-time storage changes across different tabs and background scripts. It provides a mechanism to react to modifications in shared storage namespaces. ```APIDOC ## GM_addValueChangeListener / GM_removeValueChangeListener — Value Change Events Listen for storage changes in real time, including changes made by other tabs or background scripts sharing the same storage namespace. ### Parameters - `name` (string): The name of the storage item to listen for changes. - `oldValue` (any): The previous value of the storage item. - `newValue` (any): The new value of the storage item. - `remote` (boolean): Indicates if the change originated from a different tab or script. - `tabId` (number): The ID of the tab where the change occurred (if `remote` is true). ### Returns - `listenerId` (number): An identifier for the registered listener, used for removal. ### Example Usage ```javascript // Register a listener const listenerId = GM_addValueChangeListener( "counter", (name, oldValue, newValue, remote, tabId) => { console.log(`[${name}] ${oldValue} → ${newValue}`); console.log("Changed from remote tab?", remote, "tabId:", tabId); } ); // Later: clean up to prevent memory leaks setTimeout(() => { GM_removeValueChangeListener(listenerId); console.log("Listener removed"); }, 30000); ``` ``` -------------------------------- ### Crontab / Background Scripts Source: https://context7.com/scriptscat/scriptcat/llms.txt Enables scheduled execution of scripts using `@crontab` and persistent background execution using `@background`. ```APIDOC ## Crontab / Background Scripts ### Description Allows scripts to run on a schedule or persistently in the background. Scheduled scripts wrap their body in a Promise, and rejecting with `CATRetryError` triggers automatic retries. ### Directives - **@crontab** (string): Specifies a cron schedule for the script. The script body must return a Promise. - **@background**: Designates the script to run persistently in the background. ### `CATRetryError` When used within a Promise returned by a `@crontab` script, rejecting with `CATRetryError` can trigger automatic retries. - **`CATRetryError(message, delay)`**: Retries after a specified `delay` (in seconds or a Date object). ### Example Usage (`@crontab`) ```javascript // ==UserScript== // @name Scheduled Task — Daily Dog Photo // @crontab 0 8 * * * // @grant GM_xmlhttpRequest // ==/UserScript== return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: "https://dog.ceo/api/api/breeds/image/random", onload(resp) { // Process response resolve(); }, onerror() { reject(new CATRetryError("Network error", 60)); }, }); }); ``` ### Example Usage (`@background`) ```javascript // ==UserScript== // @name Background Script — Continuous Monitor // @background // @crontab */5 * * * // @grant GM_setValue // ==/UserScript== return new Promise((resolve, reject) => { // Perform background tasks resolve(); }); ``` ``` -------------------------------- ### ScriptCat Userscript Metadata Block Source: https://context7.com/scriptscat/scriptcat/llms.txt Defines script identity, permissions, and execution configuration. Ensure all required domains and APIs are declared. ```javascript // ==UserScript== // @name My Script // @namespace https://example.com/ // @version 1.0.0 // @description What this script does // @author YourName // @match https://example.com/* // @match *://*.example.org/* // @exclude https://example.com/excluded/* // @connect api.example.com // domains allowed for GM_xmlhttpRequest // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_notification // @grant GM_cookie // @resource mydata https://cdn.example.com/data.json // @require https://cdn.example.com/lib.js // @run-at document-start // document-start | document-end | document-idle | context-menu // @inject-into page // page (default) | content // @storageName shared-storage // share storage namespace across scripts // ==/UserScript== ``` -------------------------------- ### Manage Extension Popup Menu Items Source: https://context7.com/scriptscat/scriptcat/llms.txt Use GM_registerMenuCommand to add items to the ScriptCat popup menu and GM_unregisterMenuCommand to remove them. Supports separators, nested items, individual items, and access keys. Menu items can be dynamically updated by re-registering with the same ID. ```javascript // ==UserScript== // @name Menu Demo // @match *://*/* // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // ==/UserScript== // Simple menu item const id1 = GM_registerMenuCommand("Open settings", () => { alert("Settings clicked!"); }, { accessKey: "s" }); // Separator (empty name) GM_registerMenuCommand(""); // Nested in the script's sub-menu (default); shown as-is in context menu const id2 = GM_registerMenuCommand("Toggle dark mode", () => { document.body.classList.toggle("dark"); }, { nested: true }); // Individual item — not merged with identically-named items across frames const id3 = GM_registerMenuCommand("Reload data", () => { location.reload(); }, { individual: true, autoClose: false }); // Dynamically update a menu item by re-registering with the same id setTimeout(() => { GM_registerMenuCommand("Reload data (updated)", () => location.reload(), { id: id3 }); }, 3000); // Remove a menu item GM_unregisterMenuCommand(id1); ``` -------------------------------- ### Per-Tab Data Storage with GM_getTab/saveTab/getTabs Source: https://context7.com/scriptscat/scriptcat/llms.txt GM_getTab and GM_saveTab allow storing and retrieving arbitrary data objects associated with the current browser tab. Data persists across navigations within a tab but is lost when the tab closes. GM_getTabs retrieves data from all open tabs running the script. ```javascript // ==UserScript== // @name Tab Data Demo // @match *://*/* // @grant GM_getTab // @grant GM_saveTab // @grant GM_getTabs // ==/UserScript== // Read the current tab's stored data GM_getTab((tabData) => { console.log("Current tab data:", tabData); // Add or update values and save back tabData.visitCount = (tabData.visitCount || 0) + 1; tabData.lastUrl = location.href; GM_saveTab(tabData); console.log("Saved. Visit count:", tabData.visitCount); }); // Get data from ALL open tabs running this script GM_getTabs((allTabs) => { // allTabs is { [tabId: number]: object } for (const [tabId, data] of Object.entries(allTabs)) { console.log(`Tab ${tabId}:`, data); } }); // Promise style (async () => { const data = await GM.getTab(); data.flag = true; await GM.saveTab(data); const all = await GM.getTabs(); console.log("All tabs:", all); })(); ```