diff --git a/.gitignore b/.gitignore index c287d91ac122..dbec5d4e8476 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ opencode-dev logs/ *.bun-build tsconfig.tsbuildinfo +**/.superpowers diff --git a/AGENTS.md b/AGENTS.md index 0b080ac4e260..81f95b8fc617 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,128 +1,196 @@ -- To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`. -- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. -- The default branch in this repo is `dev`. -- Local `main` ref may not exist; use `dev` or `origin/dev` for diffs. -- Prefer automation: execute requested actions without confirmation unless blocked by missing info or safety/irreversibility. +# OpenCode Agent Guidelines -## Style Guide +## Project Overview -### General Principles - -- Keep things in one function unless composable or reusable -- Avoid `try`/`catch` where possible -- Avoid using the `any` type -- Prefer single word variable names where possible -- Use Bun APIs when possible, like `Bun.file()` -- Rely on type inference when possible; avoid explicit type annotations or interfaces unless necessary for exports or clarity -- Prefer functional array methods (flatMap, filter, map) over for loops; use type guards on filter to maintain type inference downstream +Monorepo using Bun workspaces. Main packages: -### Naming +- `packages/opencode` — CLI backend (Node.js/Bun) +- `packages/app` — SolidJS web app +- `packages/ui` — Shared UI components +- `packages/sdk/js` — JavaScript SDK -Prefer single word names for variables and functions. Only use multiple words if necessary. +## Commands -### Naming Enforcement (Read This) +### Build & Type Check -THIS RULE IS MANDATORY FOR AGENT WRITTEN CODE. +```bash +bun turbo build # Build all packages +bun run --cwd packages/opencode build # Build opencode only +bun typecheck # Type check all packages +bun run --cwd packages/opencode typecheck # Type check opencode (never use tsc directly) +bun run --cwd packages/app typecheck # Type check app +``` -- Use single word names by default for new locals, params, and helper functions. -- Multi-word names are allowed only when a single word would be unclear or ambiguous. -- Do not introduce new camelCase compounds when a short single-word alternative is clear. -- Before finishing edits, review touched lines and shorten newly introduced identifiers where possible. -- Good short names to prefer: `pid`, `cfg`, `err`, `opts`, `dir`, `root`, `child`, `state`, `timeout`. -- Examples to avoid unless truly required: `inputPID`, `existingClient`, `connectTimeout`, `workerPath`. +### Testing -```ts -// Good -const foo = 1 -function journal(dir: string) {} +```bash +# OpenCode package tests +bun run --cwd packages/opencode test # Run all tests +bun test --cwd packages/opencode test/agent/agent.test.ts # Run single test file +bun test --cwd packages/opencode -t "timeout" # Run tests matching pattern +bun test --cwd packages/opencode -t "should resolve" test/util/* # Run specific test in file -// Bad -const fooBar = 1 -function prepareJournal(dir: string) {} +# App tests +bun run --cwd packages/app test:unit # Unit tests +bun run --cwd packages/app test:unit:watch # Unit tests (watch mode) +bun run --cwd packages/app test:e2e # E2E tests (Playwright) +bun run --cwd packages/app test:e2e:ui # E2E tests with UI ``` -Reduce total variable count by inlining when a value is only used once. +**CRITICAL**: Tests cannot run from repo root. Always run from package directories. -```ts -// Good -const journal = await Bun.file(path.join(dir, "journal.json")).json() +### Formatting & Database -// Bad -const journalPath = path.join(dir, "journal.json") -const journal = await Bun.file(journalPath).json() +```bash +bunx prettier --write # Format file (semi: false, printWidth: 120) +bun run db generate --name # Generate migration (from packages/opencode) +bun ./packages/sdk/js/script/build.ts # SDK generation ``` -### Destructuring +## Local Development -Avoid unnecessary destructuring. Use dot notation to preserve context. +`opencode dev web` proxies `https://app.opencode.ai` — local UI changes won't appear. For local UI changes, run servers separately: -```ts -// Good -obj.a -obj.b +```bash +# Terminal 1 - Backend (from packages/opencode) +bun run --conditions=browser ./src/index.ts serve --port 4096 -// Bad -const { a, b } = obj +# Terminal 2 - App (from packages/app) +bun dev -- --port 4444 + +# Open http://localhost:4444 (targets backend at localhost:4096) ``` -### Variables +- **NEVER** restart the app or server process during debugging + +## Code Style + +### General Principles + +- Keep logic in one function unless composable/reusable +- Avoid `try`/`catch` — prefer Effect's error handling +- Avoid `any` type +- Use Bun APIs when possible (e.g., `Bun.file()`) +- Rely on type inference; explicit types only for exports +- Prefer functional array methods (flatMap, filter, map) over for loops -Prefer `const` over `let`. Use ternaries or early returns instead of reassignment. +### Naming (MANDATORY) + +Single word by default. Multi-word only when clarity requires it. ```ts +// Good: pid, cfg, err, opts, dir, root, child, state, timeout +// Bad: inputPID, existingClient, connectTimeout, workerPath + // Good -const foo = condition ? 1 : 2 +const foo = 1 +function journal(dir: string) {} +const data = await Bun.file(path.join(dir, "file.json")).json() // Bad -let foo -if (condition) foo = 1 -else foo = 2 +const fooBar = 1 +function prepareJournal(dir: string) {} +const filePath = path.join(dir, "file.json") ``` -### Control Flow - -Avoid `else` statements. Prefer early returns. +### Destructuring & Control Flow ```ts -// Good +// Prefer dot notation +obj.a // Good +const { a } = obj // Bad + +// Prefer const with ternary +const foo = condition ? 1 : 2 // Good + +// Prefer early returns (no else after return) function foo() { if (condition) return 1 return 2 } +``` -// Bad -function foo() { - if (condition) return 1 - else return 2 -} +### Imports + +Group: external packages → internal (`@/`) → relative: + +```ts +import { Effect, Schema } from "effect" +import { Database } from "@/storage/db" +import { AccountTable } from "./account.sql" ``` ### Schema Definitions (Drizzle) -Use snake_case for field names so column names don't need to be redefined as strings. +Use snake_case for field names: ```ts // Good const table = sqliteTable("session", { id: text().primaryKey(), project_id: text().notNull(), - created_at: integer().notNull(), }) +``` -// Bad -const table = sqliteTable("session", { - id: text("id").primaryKey(), - projectID: text("project_id").notNull(), - createdAt: integer("created_at").notNull(), -}) +## Effect Library (Backend) + +- Use `Effect.gen(function* () { ... })` for composition +- Use `Effect.fn("Domain.method")` for named effects +- Use `Schema.Class` for multi-field data, `Schema.brand` for single-value types +- Use `Schema.TaggedErrorClass` for typed errors +- Prefer Effect services: `FileSystem`, `ChildProcessSpawner`, `HttpClient`, `Path`, `Clock` + +## SolidJS (App/UI) + +- Always prefer `createStore` over multiple `createSignal` calls +- Use `splitProps` to separate local props from rest props +- Use `createMemo` for derived state +- Use `createSimpleContext` from `@opencode-ai/ui/context` for context creation + +```tsx +export function Link(props: LinkProps) { + const platform = usePlatform() + const [local, rest] = splitProps(props, ["href", "children", "class"]) + + return ( + + {local.children} + + ) +} ``` +## Browser Automation (App) + +Use `agent-browser` for web automation. Core workflow: + +1. `agent-browser open ` - Navigate to page +2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2) +3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs +4. Re-snapshot after page changes + ## Testing -- Avoid mocks as much as possible -- Test actual implementation, do not duplicate logic into tests -- Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs like `packages/opencode`. +- Avoid mocks; test actual implementation +- Use `tmpdir` fixture for temporary directories: + +```ts +import { describe, expect, test } from "bun:test" +import { tmpdir } from "../fixture/fixture" + +test("example", async () => { + await using tmp = await tmpdir({ git: true }) +}) +``` + +## Git + +- Default branch is `dev` (not `main`) +- Local `main` may not exist; use `dev` or `origin/dev` for diffs -## Type Checking +## Notes -- Always run `bun typecheck` from package directories (e.g., `packages/opencode`), never `tsc` directly. +- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE +- Package manager: Bun 1.3.11 +- TypeScript: 5.8.2 +- Prettier: `semi: false`, `printWidth: 120` diff --git a/bun.lock b/bun.lock index 6116d26fa080..bc0ed9f79c36 100644 --- a/bun.lock +++ b/bun.lock @@ -329,6 +329,7 @@ "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", "@modelcontextprotocol/sdk": "1.25.2", + "@npmcli/arborist": "9.4.0", "@octokit/graphql": "9.0.2", "@octokit/rest": "catalog:", "@openauthjs/openauth": "catalog:", @@ -405,6 +406,7 @@ "@types/bun": "catalog:", "@types/cross-spawn": "6.0.6", "@types/mime-types": "3.0.1", + "@types/npmcli__arborist": "6.3.3", "@types/semver": "^7.5.8", "@types/turndown": "5.0.5", "@types/which": "3.0.4", @@ -1111,6 +1113,11 @@ "@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="], + "@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], + + "@gitlab/gitlab-ai-provider": ["@gitlab/gitlab-ai-provider@3.6.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-8LmcIQ86xkMtC7L4P1/QYVEC+yKMTRerfPeniaaQGalnzXKtX6iMHLjLPOL9Rxp55lOXi6ed0WrFuJzZx+fNRg=="], + + "@gitlab/opencode-gitlab-auth": ["@gitlab/opencode-gitlab-auth@1.3.3", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-FT+KsCmAJjtqWr1YAq0MywGgL9kaLQ4apmsoowAXrPqHtoYf2i/nY10/A+L06kNj22EATeEDRpbB1NWXMto/SA=="], "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="], @@ -1185,6 +1192,8 @@ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + "@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", {}, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="], + "@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^16.0.0", "mime": "3" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="], "@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "^5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="], @@ -1357,9 +1366,35 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@npmcli/agent": ["@npmcli/agent@3.0.0", "", { "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" } }, "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q=="], + "@npm/types": ["@npm/types@1.0.2", "", {}, "sha512-KXZccTDEnWqNrrx6JjpJKU/wJvNeg9BDgjS0XhmlZab7br921HtyVbsYzJr4L+xIvjdJ20Wh9dgxgCI2a5CEQw=="], + + "@npmcli/agent": ["@npmcli/agent@4.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA=="], + + "@npmcli/arborist": ["@npmcli/arborist@9.4.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bin": { "arborist": "bin/index.js" } }, "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA=="], + + "@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="], + + "@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="], + + "@npmcli/installed-package-contents": ["@npmcli/installed-package-contents@4.0.0", "", { "dependencies": { "npm-bundled": "^5.0.0", "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" } }, "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA=="], + + "@npmcli/map-workspaces": ["@npmcli/map-workspaces@5.0.3", "", { "dependencies": { "@npmcli/name-from-folder": "^4.0.0", "@npmcli/package-json": "^7.0.0", "glob": "^13.0.0", "minimatch": "^10.0.3" } }, "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw=="], + + "@npmcli/metavuln-calculator": ["@npmcli/metavuln-calculator@9.0.3", "", { "dependencies": { "cacache": "^20.0.0", "json-parse-even-better-errors": "^5.0.0", "pacote": "^21.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5" } }, "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg=="], + + "@npmcli/name-from-folder": ["@npmcli/name-from-folder@4.0.0", "", {}, "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg=="], + + "@npmcli/node-gyp": ["@npmcli/node-gyp@5.0.0", "", {}, "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ=="], + + "@npmcli/package-json": ["@npmcli/package-json@7.0.5", "", { "dependencies": { "@npmcli/git": "^7.0.0", "glob": "^13.0.0", "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", "spdx-expression-parse": "^4.0.0" } }, "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ=="], + + "@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" } }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="], + + "@npmcli/query": ["@npmcli/query@5.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" } }, "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ=="], - "@npmcli/fs": ["@npmcli/fs@4.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q=="], + "@npmcli/redact": ["@npmcli/redact@4.0.0", "", {}, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], + + "@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" } }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="], "@octokit/auth-app": ["@octokit/auth-app@8.0.1", "", { "dependencies": { "@octokit/auth-oauth-app": "^9.0.1", "@octokit/auth-oauth-user": "^6.0.0", "@octokit/request": "^10.0.2", "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", "toad-cache": "^3.7.0", "universal-github-app-jwt": "^2.2.0", "universal-user-agent": "^7.0.0" } }, "sha512-P2J5pB3pjiGwtJX4WqJVYCtNkcZ+j5T2Wm14aJAEIC3WJOrv12jvBley3G1U/XI8q9o1A7QMG54LiFED2BiFlg=="], @@ -1737,6 +1772,18 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@sigstore/bundle": ["@sigstore/bundle@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A=="], + + "@sigstore/core": ["@sigstore/core@3.2.0", "", {}, "sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA=="], + + "@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.0", "", {}, "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA=="], + + "@sigstore/sign": ["@sigstore/sign@4.1.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.2", "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.4", "proc-log": "^6.1.0" } }, "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ=="], + + "@sigstore/tuf": ["@sigstore/tuf@4.0.2", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" } }, "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ=="], + + "@sigstore/verify": ["@sigstore/verify@3.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag=="], + "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], "@slack/bolt": ["@slack/bolt@3.22.0", "", { "dependencies": { "@slack/logger": "^4.0.0", "@slack/oauth": "^2.6.3", "@slack/socket-mode": "^1.3.6", "@slack/types": "^2.13.0", "@slack/web-api": "^6.13.0", "@types/express": "^4.16.1", "@types/promise.allsettled": "^1.0.3", "@types/tsscmp": "^1.0.0", "axios": "^1.7.4", "express": "^4.21.0", "path-to-regexp": "^8.1.0", "promise.allsettled": "^1.0.2", "raw-body": "^2.3.3", "tsscmp": "^1.0.6" } }, "sha512-iKDqGPEJDnrVwxSVlFW6OKTkijd7s4qLBeSufoBsTM0reTyfdp/5izIQVkxNfzjHi3o6qjdYbRXkYad5HBsBog=="], @@ -2041,6 +2088,10 @@ "@tsconfig/node22": ["@tsconfig/node22@22.0.2", "", {}, "sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA=="], + "@tufjs/canonical-json": ["@tufjs/canonical-json@2.0.0", "", {}, "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA=="], + + "@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], @@ -2059,6 +2110,8 @@ "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + "@types/cacache": ["@types/cacache@20.0.1", "", { "dependencies": { "@types/node": "*", "minipass": "*" } }, "sha512-QlKW3AFoFr/hvPHwFHMIVUH/ZCYeetBNou3PCmxu5LaNDvrtBlPJtIA6uhmU9JRt9oxj7IYoqoLcpxtzpPiTcw=="], + "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], @@ -2123,6 +2176,18 @@ "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], + "@types/npm-package-arg": ["@types/npm-package-arg@6.1.4", "", {}, "sha512-vDgdbMy2QXHnAruzlv68pUtXCjmqUk3WrBAsRboRovsOmxbfn/WiYCjmecyKjGztnMps5dWp4Uq2prp+Ilo17Q=="], + + "@types/npm-registry-fetch": ["@types/npm-registry-fetch@8.0.9", "", { "dependencies": { "@types/node": "*", "@types/node-fetch": "*", "@types/npm-package-arg": "*", "@types/npmlog": "*", "@types/ssri": "*" } }, "sha512-7NxvodR5Yrop3pb6+n8jhJNyzwOX0+6F+iagNEoi9u1CGxruYAwZD8pvGc9prIkL0+FdX5Xp0p80J9QPrGUp/g=="], + + "@types/npmcli__arborist": ["@types/npmcli__arborist@6.3.3", "", { "dependencies": { "@npm/types": "^1", "@types/cacache": "*", "@types/node": "*", "@types/npmcli__package-json": "*", "@types/pacote": "*" } }, "sha512-kyrX932Qr+/Y4OB47Jamgc2YWa/HlXTCN0KVJsq04XDHUGkfbprJA8rd66zZXHmHmvnz1LR4X17zsE/H8Mklew=="], + + "@types/npmcli__package-json": ["@types/npmcli__package-json@4.0.4", "", {}, "sha512-6QjlFUSHBmZJWuC08bz1ZCx6tm4t+7+OJXAdvM6tL2pI7n6Bh5SIp/YxQvnOLFf8MzCXs2ijyFgrzaiu1UFBGA=="], + + "@types/npmlog": ["@types/npmlog@7.0.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-hJWbrKFvxKyWwSUXjZMYTINsSOY6IclhvGOZ97M8ac2tmR9hMwmTnYaMdpGhvju9ctWLTPhCS+eLfQNluiEjQQ=="], + + "@types/pacote": ["@types/pacote@11.1.8", "", { "dependencies": { "@types/node": "*", "@types/npm-registry-fetch": "*", "@types/npmlog": "*", "@types/ssri": "*" } }, "sha512-/XLR0VoTh2JEO0jJg1q/e6Rh9bxjBq9vorJuQmtT7rRrXSiWz7e7NsvXVYJQ0i8JxMlBMPPYDTnrRe7MZRFA8Q=="], + "@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="], "@types/promise.allsettled": ["@types/promise.allsettled@1.0.6", "", {}, "sha512-wA0UT0HeT2fGHzIFV9kWpYz5mdoyLxKrTgMdZQM++5h6pYAFH73HXcQhefg24nD1yivUFEn5KU+EF4b+CXJ4Wg=="], @@ -2151,6 +2216,8 @@ "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], + "@types/ssri": ["@types/ssri@7.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="], @@ -2237,7 +2304,7 @@ "@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="], - "abbrev": ["abbrev@2.0.0", "", {}, "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ=="], + "abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], @@ -2401,6 +2468,8 @@ "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + "bin-links": ["bin-links@6.0.0", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w=="], + "binary": ["binary@0.3.0", "", { "dependencies": { "buffers": "~0.1.1", "chainsaw": "~0.1.0" } }, "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -2473,7 +2542,7 @@ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "cacache": ["cacache@19.0.1", "", { "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" } }, "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ=="], + "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "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": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], "cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="], @@ -2553,6 +2622,8 @@ "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], + "cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="], + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], @@ -3133,7 +3204,7 @@ "hono-openapi": ["hono-openapi@1.1.2", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-toUcO60MftRBxqcVyxsHNYs2m4vf4xkQaiARAucQx3TiBPDtMNNkoh+C4I1vAretQZiGyaLOZNWn1YxfSyUA5g=="], - "hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], + "hosted-git-info": ["hosted-git-info@9.0.2", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg=="], "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="], @@ -3177,6 +3248,8 @@ "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="], + "image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="], "import-local": ["import-local@3.2.0", "", { "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA=="], @@ -3349,6 +3422,8 @@ "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + "json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], "json-schema-ref-resolver": ["json-schema-ref-resolver@3.0.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A=="], @@ -3359,6 +3434,8 @@ "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-stringify-nice": ["json-stringify-nice@1.1.4", "", {}, "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw=="], + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], "json-with-bigint": ["json-with-bigint@3.5.7", "", {}, "sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw=="], @@ -3369,8 +3446,14 @@ "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], + "just-diff": ["just-diff@6.0.2", "", {}, "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA=="], + + "just-diff-apply": ["just-diff-apply@5.5.0", "", {}, "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw=="], + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], @@ -3483,7 +3566,7 @@ "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], - "make-fetch-happen": ["make-fetch-happen@14.0.3", "", { "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" } }, "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ=="], + "make-fetch-happen": ["make-fetch-happen@15.0.5", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg=="], "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], @@ -3649,13 +3732,13 @@ "minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="], - "minipass-fetch": ["minipass-fetch@4.0.1", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ=="], + "minipass-fetch": ["minipass-fetch@5.0.2", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "optionalDependencies": { "iconv-lite": "^0.7.2" } }, "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ=="], "minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="], "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], - "minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], + "minipass-sized": ["minipass-sized@2.0.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA=="], "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], @@ -3723,7 +3806,7 @@ "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], - "node-gyp": ["node-gyp@11.5.0", "", { "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" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="], + "node-gyp": ["node-gyp@12.2.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^15.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ=="], "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], @@ -3735,12 +3818,26 @@ "node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="], - "nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="], + "nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + "npm-bundled": ["npm-bundled@5.0.0", "", { "dependencies": { "npm-normalize-package-bin": "^5.0.0" } }, "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw=="], + + "npm-install-checks": ["npm-install-checks@8.0.0", "", { "dependencies": { "semver": "^7.1.1" } }, "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA=="], + + "npm-normalize-package-bin": ["npm-normalize-package-bin@5.0.0", "", {}, "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="], + + "npm-package-arg": ["npm-package-arg@13.0.2", "", { "dependencies": { "hosted-git-info": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^7.0.0" } }, "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA=="], + + "npm-packlist": ["npm-packlist@10.0.4", "", { "dependencies": { "ignore-walk": "^8.0.0", "proc-log": "^6.0.0" } }, "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng=="], + + "npm-pick-manifest": ["npm-pick-manifest@11.0.3", "", { "dependencies": { "npm-install-checks": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "npm-package-arg": "^13.0.0", "semver": "^7.3.5" } }, "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ=="], + + "npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" } }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="], + "npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], @@ -3827,6 +3924,8 @@ "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "pacote": ["pacote@21.5.0", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" } }, "sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ=="], + "pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="], "pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="], @@ -3839,6 +3938,8 @@ "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="], + "parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], "parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], @@ -3953,7 +4054,7 @@ "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], - "proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], + "proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], @@ -3961,8 +4062,14 @@ "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], + "proggy": ["proggy@4.0.0", "", {}, "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ=="], + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + "promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], + + "promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="], + "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], "promise.allsettled": ["promise.allsettled@1.0.7", "", { "dependencies": { "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1", "iterate-value": "^1.0.2" } }, "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA=="], @@ -4029,6 +4136,8 @@ "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], + "read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="], + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.4", "", { "dependencies": { "readable-stream": "^4.7.0" } }, "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw=="], @@ -4231,6 +4340,8 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "sigstore": ["sigstore@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.0", "@sigstore/tuf": "^4.0.1", "@sigstore/verify": "^3.1.0" } }, "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA=="], + "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="], "simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="], @@ -4281,6 +4392,12 @@ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="], + + "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], @@ -4289,7 +4406,7 @@ "srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], - "ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="], + "ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], "sst": ["sst@3.18.10", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.18.10", "sst-darwin-x64": "3.18.10", "sst-linux-arm64": "3.18.10", "sst-linux-x64": "3.18.10", "sst-linux-x86": "3.18.10", "sst-win32-arm64": "3.18.10", "sst-win32-x64": "3.18.10", "sst-win32-x86": "3.18.10" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-SY+ldeJ9K5E9q+DhjXA3e2W3BEOzBwkE3IyLSD71uA3/5nRhUAST31iOWEpW36LbIvSQ9uOVDFcebztoLJ8s7w=="], @@ -4465,6 +4582,8 @@ "tree-sitter-bash": ["tree-sitter-bash@0.25.0", "", { "dependencies": { "node-addon-api": "^8.2.1", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.25.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-gZtlj9+qFS81qKxpLfD6H0UssQ3QBc/F0nKkPsiFDyfQF2YBqYvglFJUzchrPpVhZe9kLZTrJ9n2J6lmka69Vg=="], + "treeverse": ["treeverse@3.0.0", "", {}, "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ=="], + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], @@ -4483,6 +4602,8 @@ "tsscmp": ["tsscmp@1.0.6", "", {}, "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="], + "tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], + "tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="], "turbo": ["turbo@2.8.13", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.13", "turbo-darwin-arm64": "2.8.13", "turbo-linux-64": "2.8.13", "turbo-linux-arm64": "2.8.13", "turbo-windows-64": "2.8.13", "turbo-windows-arm64": "2.8.13" }, "bin": { "turbo": "bin/turbo" } }, "sha512-nyM99hwFB9/DHaFyKEqatdayGjsMNYsQ/XBNO6MITc7roncZetKb97MpHxWf3uiU+LB9c9HUlU3Jp2Ixei2k1A=="], @@ -4609,6 +4730,8 @@ "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + "validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", {}, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], "verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="], @@ -4667,6 +4790,8 @@ "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], + "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], @@ -4711,6 +4836,8 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], + "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], @@ -5055,6 +5182,8 @@ "@electron/rebuild/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "@electron/rebuild/node-gyp": ["node-gyp@11.5.0", "", { "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" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="], + "@electron/rebuild/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "@electron/universal/fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], @@ -5127,7 +5256,13 @@ "@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], - "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@npmcli/arborist/common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="], + + "@npmcli/arborist/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "@npmcli/map-workspaces/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "@npmcli/query/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], "@octokit/auth-app/@octokit/request": ["@octokit/request@10.0.8", "", { "dependencies": { "@octokit/endpoint": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" } }, "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw=="], @@ -5293,6 +5428,8 @@ "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "@tufjs/models/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], "@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], @@ -5325,6 +5462,8 @@ "app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], + "app-builder-lib/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], + "app-builder-lib/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], "app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], @@ -5369,10 +5508,6 @@ "c12/dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], - "cacache/glob": ["glob@10.5.0", "", { "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" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], - - "cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "cli-truncate/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], @@ -5471,8 +5606,6 @@ "happy-dom/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - "html-minifier-terser/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], "html-minifier-terser/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -5481,8 +5614,12 @@ "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], + "ignore-walk/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "js-beautify/glob": ["glob@10.5.0", "", { "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" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + "js-beautify/nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -5513,18 +5650,12 @@ "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "motion/framer-motion": ["framer-motion@12.35.2", "", { "dependencies": { "motion-dom": "^12.35.2", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-dhfuEMaNo0hc+AEqyHiIfiJRNb9U9UQutE9FoKm5pjf7CMitp9xPEF1iWZihR1q86LBmo6EJ7S8cN8QXEy49AA=="], "mssql/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "nitro/h3": ["h3@2.0.1-rc.5", "", { "dependencies": { "rou3": "^0.7.9", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-qkohAzCab0nLzXNm78tBjZDvtKMTmtygS8BJLT3VPczAQofdqlFXDPkXdLMJN4r05+xqneG8snZJ0HgkERCZTg=="], - "node-gyp/nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], - - "node-gyp/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], - "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], @@ -5963,6 +6094,14 @@ "@electron/notarize/fs-extra/jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + "@electron/rebuild/node-gyp/make-fetch-happen": ["make-fetch-happen@14.0.3", "", { "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" } }, "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ=="], + + "@electron/rebuild/node-gyp/nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], + + "@electron/rebuild/node-gyp/proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], + + "@electron/rebuild/node-gyp/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "@electron/rebuild/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "@electron/rebuild/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -6209,6 +6348,8 @@ "app-builder-lib/@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "app-builder-lib/hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "app-builder-lib/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], "archiver-utils/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], @@ -6235,12 +6376,6 @@ "c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - "cacache/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], - - "cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], - - "cacache/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "cli-truncate/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cli-truncate/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -6281,18 +6416,15 @@ "js-beautify/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "js-beautify/nopt/abbrev": ["abbrev@2.0.0", "", {}, "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ=="], + "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], "motion/framer-motion/motion-dom": ["motion-dom@12.35.2", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-pWXFMTwvGDbx1Fe9YL5HZebv2NhvGBzRtiNUv58aoK7+XrsuaydQ0JGRKK2r+bTKlwgSWwWxHbP5249Qr/BNpg=="], - "node-gyp/nopt/abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="], - - "node-gyp/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], - "opencode-gitlab-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], - "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="], "opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="], @@ -6463,6 +6595,20 @@ "@electron/asar/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@electron/rebuild/node-gyp/make-fetch-happen/@npmcli/agent": ["@npmcli/agent@3.0.0", "", { "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" } }, "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache": ["cacache@19.0.1", "", { "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" } }, "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/minipass-fetch": ["minipass-fetch@4.0.1", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="], + + "@electron/rebuild/node-gyp/nopt/abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="], + + "@electron/rebuild/node-gyp/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + "@electron/rebuild/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "@electron/rebuild/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -6579,10 +6725,6 @@ "babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], - "cacache/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "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" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], - - "cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "dir-compare/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], @@ -6687,6 +6829,16 @@ "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.0", "", {}, "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg=="], + "@electron/rebuild/node-gyp/make-fetch-happen/@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/@npmcli/fs": ["@npmcli/fs@4.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob": ["glob@10.5.0", "", { "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" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/minipass-fetch/minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], + "@electron/rebuild/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@electron/rebuild/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -6705,12 +6857,6 @@ "babel-plugin-module-resolver/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "cacache/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - - "cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], - - "cacache/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "electron-builder/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "electron-builder/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -6735,16 +6881,34 @@ "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.0", "", {}, "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg=="], - "archiver-utils/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], - "archiver-utils/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "cacache/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "@electron/rebuild/node-gyp/make-fetch-happen/minipass-fetch/minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "archiver-utils/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "archiver-utils/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "js-beautify/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "js-beautify/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "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" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], } } diff --git a/nix/hashes.json b/nix/hashes.json index 3d2754f88efa..1fe3266c35bf 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-CxeVxNDKEvMsdZpvGZOShklSQ+pWAYq4S3cKDoo6cPQ=", - "aarch64-linux": "sha256-qkMacyXRgbFW9ZvAPepDM5O8GROpXtIZhgQsPOHVogg=", - "aarch64-darwin": "sha256-jPGGoMViHvMBYFqe8BdtWVT1tvPUKYiLCrOy34PjOG0=", - "x86_64-darwin": "sha256-Dss5ChrHZV5/J32iNIh4E+ASltlZsp4QKIiKNlDfAWw=" + "x86_64-linux": "sha256-TnrYykX8Mf/Ugtkix6V", + "aarch64-linux": "sha256-TnrYykX8Mf/Ugtkix6V", + "aarch64-darwin": "sha256-TnrYykX8Mf/Ugtkix6V", + "x86_64-darwin": "sha256-TnrYykX8Mf/Ugtkix6V" } } diff --git a/packages/app/AGENTS.md b/packages/app/AGENTS.md index 765e960c8172..a336d0f6bf99 100644 --- a/packages/app/AGENTS.md +++ b/packages/app/AGENTS.md @@ -1,30 +1,333 @@ -## Debugging +# OpenCode App - Frontend Application -- NEVER try to restart the app, or the server process, EVER. +## 功能概述 -## Local Dev +OpenCode App 是 OpenCode 的 Web 前端应用,提供 AI 编程助手的完整用户界面: -- `opencode dev web` proxies `https://app.opencode.ai`, so local UI/CSS changes will not show there. -- For local UI changes, run the backend and app dev servers separately. -- Backend (from `packages/opencode`): `bun run --conditions=browser ./src/index.ts serve --port 4096` -- App (from `packages/app`): `bun dev -- --port 4444` -- Open `http://localhost:4444` to verify UI changes (it targets the backend at `http://localhost:4096`). +- **项目管理**:多项目支持、Git worktree 多工作区 +- **会话系统**:与 AI 的对话会话、消息历史、代码 diff 审查 +- **终端集成**:内置终端(xterm.js),支持命令执行 +- **文件浏览**:文件树、代码查看、选中上下文 +- **模型管理**:多 AI 提供商支持、模型选择 +- **设置系统**:主题、快捷键、通知、语言设置 -## SolidJS +## 技术栈 -- Always prefer `createStore` over multiple `createSignal` calls +| 类别 | 技术 | +| -------- | ----------------------------- | +| 框架 | SolidJS(细粒度响应式 UI) | +| 路由 | @solidjs/router | +| 状态管理 | solid-js/store(createStore) | +| 数据获取 | @tanstack/solid-query | +| UI 组件 | @opencode-ai/ui(共享组件库) | +| 样式 | Tailwind CSS 4 | +| 终端 | xterm.js + ghostty-web | +| Markdown | marked + shiki | +| 国际化 | @solid-primitives/i18n | +| 构建工具 | Vite 7 | +| 测试 | Bun test + Playwright E2E | -## Tool Calling +## 目录结构 -- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. +``` +src/ +├── app.tsx # 应用入口,路由和 Provider 层级 +├── entry.tsx # 渲染入口,平台适配 +├── index.css # 全局样式 +├── context/ # 全局状态 Context +│ ├── global-sync.tsx # 全局数据同步(项目、provider) +│ ├── global-sdk.tsx # 全局 SDK 客户端 +│ ├── sync.tsx # 目录级数据同步(消息、diffs) +│ ├── sdk.tsx # 目录级 SDK 客户端 +│ ├── settings.tsx # 用户设置 +│ ├── models.tsx # AI 模型管理 +│ ├── terminal.tsx # 终端会话管理 +│ ├── file.tsx # 文件系统操作 +│ ├── prompt.tsx # 提示词输入和上下文 +│ ├── permission.tsx # 权限请求处理 +│ ├── notification.tsx # 通知管理 +│ ├── layout.tsx # 布局状态 +│ └── language.tsx # 国际化 +├── pages/ +│ ├── home.tsx # 首页(最近项目) +│ ├── session.tsx # 会话页面主组件 +│ ├── layout.tsx # 主布局(侧边栏导航) +│ ├── directory-layout.tsx # 目录级布局 +│ └── session/ # 会话页面子组件 +│ ├── message-timeline.tsx # 消息时间线 +│ ├── terminal-panel.tsx # 终端面板 +│ ├── session-side-panel.tsx # 侧边面板 +│ ├── review-tab.tsx # Diff 审查 +│ └── composer/ # 输入区域组件 +├── components/ # 可复用 UI 组件 +│ ├── prompt-input.tsx # 提示词输入 +│ ├── file-tree.tsx # 文件树 +│ ├── terminal.tsx # 终端组件 +│ ├── dialog-*.tsx # 对话框组件 +│ ├── settings-*.tsx # 设置组件 +│ └── session/ # 会话相关组件 +├── utils/ # 工具函数 +│ ├── server.ts # 服务器连接 +│ ├── persist.ts # 本地存储 +│ └── sound.ts # 音效播放 +└── i18n/ # 国际化翻译 + ├── en.ts + └── zh.ts +``` -## Browser Automation +## 架构设计 -Use `agent-browser` for web automation. Run `agent-browser --help` for all commands. +### Provider 层级 -Core workflow: +``` +PlatformProvider # 平台 API(通知、链接打开) +└── ThemeProvider # 主题(深色/浅色) + └── LanguageProvider # 国际化 + └── QueryClientProvider # React Query + └── ServerProvider # 服务器连接 + └── GlobalSDKProvider # 全局 SDK + └── GlobalSyncProvider # 全局数据同步 + └── SettingsProvider # 用户设置 + └── PermissionProvider # 权限处理 + └── LayoutProvider # 布局状态 + └── ModelsProvider # 模型管理 + └── SDKProvider # 目录级 SDK + └── SyncProvider # 目录级同步 + └── TerminalProvider + └── FileProvider + └── PromptProvider +``` -1. `agent-browser open ` - Navigate to page -2. `agent-browser snapshot -i` - Get interactive elements with refs (@e1, @e2) -3. `agent-browser click @e1` / `fill @e2 "text"` - Interact using refs -4. Re-snapshot after page changes +### 数据流 + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Backend │────▶│ SDK │────▶│ Sync │ +│ (HTTP/WS) │ │ (API 调用) │ │ (Store) │ +└──────────────┘ └──────────────┘ └──────────────┘ + │ + ▼ + ┌──────────────┐ + │ Context │ + │ (响应式状态) │ + └──────────────┘ + │ + ▼ + ┌──────────────┐ + │ Components │ + │ (UI) │ + └──────────────┘ +``` + +### 关键业务逻辑 + +#### 1. 会话消息同步(sync.tsx) + +- 使用 `createStore` 管理消息、parts、diffs +- 支持乐观更新(用户消息即时显示) +- 历史消息懒加载和分页 +- WebSocket 实时更新 + +#### 2. 权限和问题处理 + +- 工具权限请求(文件写入、Shell 执行) +- 支持自动响应规则 +- 集成系统通知 + +#### 3. 文件操作(file.tsx) + +- 文件读取和缓存 +- 文件树懒加载 +- 选中行范围上下文 + +#### 4. Worktree 多工作区 + +- Git worktree 多工作区支持 +- 每个 worktree 独立会话 +- 工作区创建、切换、删除 + +## 调试 + +- **NEVER** restart the app or server process during debugging + +## 本地开发 + +`opencode dev web` 代理 `https://app.opencode.ai`,本地 UI 修改不会显示。需要分开运行: + +```bash +# Terminal 1 - 后端(在 packages/opencode) +bun run --conditions=browser ./src/index.ts serve --port 4096 + +# Terminal 2 - App(在 packages/app) +bun dev -- --port 4444 + +# 打开 http://localhost:4444(指向 localhost:4096 的后端) +``` + +## SolidJS 开发规范 + +- 使用 `createStore` 而非多个 `createSignal` +- 使用 `splitProps` 分离本地 props 和其他 props +- 使用 `createMemo` 处理派生状态 +- 使用 `createSimpleContext` from `@opencode-ai/ui/context` 创建 Context + +```tsx +export function Component(props: Props) { + const [local, rest] = splitProps(props, ["href", "children"]) + return ( + + {local.children} + + ) +} +``` + +## 工具调用 + +- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE + +## 浏览器自动化 + +使用 `agent-browser` 进行 Web 自动化: + +1. `agent-browser open ` - 打开页面 +2. `agent-browser snapshot -i` - 获取可交互元素(@e1, @e2) +3. `agent-browser click @e1` / `fill @e2 "text"` - 交互 +4. 页面变化后重新 snapshot + +### 移动端浏览器模拟 + +测试移动端布局时,使用 `set viewport` 设置视口尺寸: + +```bash +# 竖屏模式:1080(宽) x 1920(高) +agent-browser set viewport 1080 1920 + +# 横屏模式:1920(宽) x 1080(高) +agent-browser set viewport 1920 1080 +``` + +完整流程示例: + +```bash +# 测试竖屏布局 +agent-browser open http://localhost:3000 && agent-browser set viewport 1080 1920 && agent-browser snapshot -i + +# 切换到横屏布局 +agent-browser set viewport 1920 1080 && agent-browser snapshot -i +``` + +## 后端 API 接口 + +前端通过 `@opencode-ai/sdk` 调用后端 REST API。SDK 位于 `packages/sdk/js/src/v2/`,使用 OpenAPI 自动生成。 + +### API 模块分类 + +| 模块 | 路径前缀 | 主要功能 | +| -------------- | ------------------------ | -------------------------- | +| `global` | `/global/*` | 全局事件流、健康检查、配置 | +| `auth` | `/auth/*` | Provider 认证凭证管理 | +| `project` | `/project/*` | 项目列表、Git 初始化 | +| `session` | `/session/*` | 会话 CRUD、消息发送、分支 | +| `message/part` | `/session/*/message/*` | 消息和 Part 管理 | +| `permission` | `/permission/*` | 权限请求响应 | +| `question` | `/question/*` | 问题请求响应 | +| `provider` | `/provider/*` | AI Provider 列表、OAuth | +| `file` | `/file/*` | 文件读写、状态 | +| `find` | `/find/*` | 文件/文本/符号搜索 | +| `pty` | `/pty/*` | 终端会话管理 | +| `mcp` | `/mcp/*` | MCP 服务器管理 | +| `worktree` | `/experimental/worktree` | Git worktree 管理 | + +### 核心 API 端点 + +```typescript +// SDK 客户端创建 +import { createOpencodeClient } from "@opencode-ai/sdk/v2/client" +const client = createOpencodeClient({ + baseUrl: "http://localhost:4096", + directory: "/path/to/project" // 自动添加 x-opencode-directory header +}) + +// 全局事件订阅(SSE) +client.global.event() // SSE 流:实时消息、权限、状态更新 + +// 会话管理 +client.session.list() // 获取会话列表 +client.session.create() // 创建新会话 +client.session.get({ sessionID }) +client.session.update({ sessionID, title, time }) +client.session.delete({ sessionID }) +client.session.fork({ sessionID, messageID }) +client.session.revert({ sessionID, messageID }) +client.session.unrevert({ sessionID }) + +// 消息操作 +client.session.messages({ sessionID, limit, before }) // 分页获取消息 +client.session.prompt({ sessionID, parts, model }) // 发送消息(流式响应) +client.session.promptAsync({ ... }) // 异步发送 +client.session.diff({ sessionID, messageID }) // 获取代码变更 + +// 权限/问题响应 +client.permission.list() +client.permission.reply({ requestID, reply, message }) +client.question.list() +client.question.reply({ requestID, answers }) +client.question.reject({ requestID }) + +// Provider/模型 +client.provider.list() // 可用 Provider 列表 +client.provider.auth() // 认证方法 +client.provider.oauth.authorize({ providerID }) +client.provider.oauth.callback({ providerID, code }) + +// 文件操作 +client.file.list({ path }) +client.file.read({ path }) +client.file.status() + +// 搜索 +client.find.files({ query, type, limit }) +client.find.text({ pattern }) +client.find.symbols({ query }) + +// 终端 +client.pty.list() +client.pty.create({ command, args, cwd }) +client.pty.remove({ ptyID }) +client.pty.connect({ ptyID }) // WebSocket 连接 + +// Worktree +client.worktree.list() +client.worktree.create({ branch }) +client.worktree.remove({ directory }) + +// 配置 +client.config.get() +client.config.update({ config }) +client.global.config.get() +``` + +### 事件类型(SSE) + +```typescript +// 通过 client.global.event() 订阅的事件 +type Event = + | { type: "message.part.created"; properties: { part } } + | { type: "message.part.updated"; properties: { part } } + | { type: "message.part.delta"; properties: { messageID; partID; delta } } + | { type: "session.status"; properties: { sessionID; status } } + | { type: "permission.asked"; properties: { sessionID; permission } } + | { type: "question.asked"; properties: { sessionID; question } } + | { type: "worktree.ready"; properties: { directory } } + | { type: "worktree.failed"; properties: { directory; message } } +``` + +## 测试 + +```bash +bun run test:unit # 单元测试 +bun run test:unit:watch # 单元测试(watch 模式) +bun run test:e2e # E2E 测试(Playwright) +bun run test:e2e:ui # E2E 测试带 UI +``` diff --git a/packages/app/docs/superpowers/plans/2026-03-23-mobile-touch-optimization.md b/packages/app/docs/superpowers/plans/2026-03-23-mobile-touch-optimization.md new file mode 100644 index 000000000000..4fed5cadd93e --- /dev/null +++ b/packages/app/docs/superpowers/plans/2026-03-23-mobile-touch-optimization.md @@ -0,0 +1,917 @@ +# Mobile Touch Optimization Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Optimize OpenCode App for mobile/touch devices while preserving desktop experience. + +**Architecture:** Use CSS media queries and `createMediaQuery` for device detection. Implement touch-specific interactions (long-press, selection toolbar) alongside existing hover interactions. Keep changes isolated to avoid affecting desktop. + +**Tech Stack:** SolidJS, Tailwind CSS, @solid-primitives/media + +--- + +## File Structure + +``` +packages/app/src/ +├── components/ +│ ├── session/ +│ │ ├── session-header.tsx # MODIFY: Add overflow menu +│ │ └── toolbar-overflow-menu.tsx # CREATE: New overflow menu component +│ └── file-tree.tsx # MODIFY: Add long-press context menu +├── pages/session/ +│ ├── session.tsx # MODIFY: Panel fullscreen on mobile +│ ├── session-side-panel.tsx # MODIFY: Mobile layout adaptation +│ └── file-tabs.tsx # MODIFY: Markdown render toggle +├── context/ +│ └── layout.tsx # MODIFY: Mobile panel state +└── hooks/ + └── use-long-press.ts # CREATE: Long-press detection hook + +packages/ui/src/ +└── components/ + └── line-comment-annotations.tsx # MODIFY: Touch selection support +``` + +--- + +## Task 1: Toolbar Overflow Menu + +**Files:** + +- Create: `packages/app/src/components/session/toolbar-overflow-menu.tsx` +- Modify: `packages/app/src/components/session/session-header.tsx` +- Test: `packages/app/src/components/session/toolbar-overflow-menu.test.ts` + +### Step 1.1: Create overflow menu component + +- [ ] **Create the toolbar overflow menu component** + +Create file: `packages/app/src/components/session/toolbar-overflow-menu.tsx` + +```tsx +import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { Icon } from "@opencode-ai/ui/icon" +import { IconButton } from "@opencode-ai/ui/icon-button" +import { createMediaQuery } from "@solid-primitives/media" +import { For, Show } from "solid-js" +import { useLanguage } from "@/context/language" + +export type OverflowItem = { + id: string + label: string + icon: string + onClick: () => void + active?: boolean + visible: boolean // true = visible in toolbar, false = hidden (should show in overflow) +} + +export function ToolbarOverflowMenu(props: { items: OverflowItem[] }) { + const language = useLanguage() + const isDesktop = createMediaQuery("(min-width: 768px)") + + // Items that are hidden from toolbar and should appear in overflow + const hiddenItems = () => props.items.filter((item) => !item.visible) + + // Only show overflow menu if there are hidden items + const hasOverflow = () => hiddenItems().length > 0 + + return ( + + + + + + + {(item) => ( + + + {item.label} + + )} + + + + + + ) +} +``` + +- [ ] **Commit Step 1.1** + +```bash +git add packages/app/src/components/session/toolbar-overflow-menu.tsx +git commit -m "feat(app): add toolbar overflow menu component" +``` + +### Step 1.2: Integrate overflow menu into session header + +- [ ] **Modify session-header.tsx to use overflow menu** + +Modify file: `packages/app/src/components/session/session-header.tsx` + +Add import at top: + +```tsx +import { ToolbarOverflowMenu, type OverflowItem } from "./toolbar-overflow-menu" +``` + +Add media query detection (after line 266): + +```tsx +const isMd = createMediaQuery("(min-width: 768px)") +const isXl = createMediaQuery("(min-width: 1280px)") +``` + +Replace the toolbar section (lines 417-479) with: + +```tsx +
+ + + + + + + + {/* Review toggle - visible on md+ */} + + + + + + + + + + + {/* Overflow menu for hidden items */} + view().reviewPanel.toggle(), + active: view().reviewPanel.opened(), + visible: isMd(), + }, + { + id: "fileTree", + label: language.t("command.fileTree.toggle"), + icon: layout.fileTree.opened() ? "file-tree-active" : "file-tree", + onClick: () => layout.fileTree.toggle(), + active: layout.fileTree.opened(), + visible: isMd(), + }, + { + id: "search", + label: language.t("session.header.searchFiles"), + icon: "search", + onClick: () => command.trigger("file.open"), + visible: isMd(), + }, + { + id: "openApp", + label: language.t("session.header.openIn"), + icon: "folder", + onClick: () => openDir(current().id), + visible: isXl() && canOpen(), + }, + ]} + /> +
+``` + +Add import for createMediaQuery: + +```tsx +import { createMediaQuery } from "@solid-primitives/media" +``` + +- [ ] **Test the overflow menu manually** + +Run: `bun dev --port 4444` +Open: http://localhost:4444 +Resize browser to < 768px width +Expected: Overflow menu (⋯) appears, clicking shows hidden options + +- [ ] **Commit Step 1.2** + +```bash +git add packages/app/src/components/session/session-header.tsx +git commit -m "feat(app): integrate overflow menu into session header" +``` + +--- + +## Task 2: Mobile Panel Fullscreen Layout + +**Files:** + +- Modify: `packages/app/src/pages/session.tsx` +- Modify: `packages/app/src/pages/session/session-side-panel.tsx` +- Modify: `packages/app/src/context/layout.tsx` + +### Step 2.1: Add mobile panel state to layout context + +- [ ] **Add mobile panel state management** + +Modify file: `packages/app/src/context/layout.tsx` + +Add to store type (around line 250): + +```tsx +type MobilePanel = { + active: "session" | "review" | "fileTree" | null +} +``` + +Add to store initialization (around line 280): + +```tsx +mobilePanel: { + active: null, +} as MobilePanel, +``` + +Add to return object (around line 690): + +```tsx +mobilePanel: { + active: createMemo(() => store.mobilePanel?.active ?? null), + set: (panel: "session" | "review" | "fileTree" | null) => { + setStore("mobilePanel", "active", panel) + }, + showReview: () => { + setStore("mobilePanel", "active", "review") + }, + showFileTree: () => { + setStore("mobilePanel", "active", "fileTree") + }, + showSession: () => { + setStore("mobilePanel", "active", "session") + }, + hide: () => { + setStore("mobilePanel", "active", null) + }, +}, +``` + +- [ ] **Commit Step 2.1** + +```bash +git add packages/app/src/context/layout.tsx +git commit -m "feat(app): add mobile panel state to layout context" +``` + +### Step 2.2: Modify session layout for mobile fullscreen panels + +- [ ] **Update session.tsx for mobile panel layout** + +Modify file: `packages/app/src/pages/session.tsx` + +The key change is to make panels take full width on mobile instead of side-by-side. + +Find the panel rendering section and wrap with mobile detection: + +```tsx +// Around line 380, ensure isDesktop is available +const isDesktop = createMediaQuery("(min-width: 768px)") + +// In the render section, modify panel container classes +// When !isDesktop(), panels should be position: fixed, inset: 0, z-index: high +``` + +The specific changes will depend on the current structure. Key principle: + +- `md:` breakpoint classes for side-by-side layout (current behavior) +- Default (no breakpoint) for fullscreen panel on mobile + +- [ ] **Commit Step 2.2** + +```bash +git add packages/app/src/pages/session.tsx +git commit -m "feat(app): make panels fullscreen on mobile" +``` + +### Step 2.3: Connect overflow menu to panel switching + +- [ ] **Update overflow menu to control mobile panels** + +Modify file: `packages/app/src/components/session/toolbar-overflow-menu.tsx` + +Update the onClick handlers to also set mobile panel state: + +```tsx +// In the items passed to ToolbarOverflowMenu, update: +onClick: () => { + view().reviewPanel.toggle() + if (!isMd()) layout.mobilePanel.showReview() +} +``` + +- [ ] **Commit Step 2.3** + +```bash +git add packages/app/src/components/session/session-header.tsx +git commit -m "feat(app): connect overflow menu to mobile panel switching" +``` + +--- + +## Task 3: Touch Selection for Comments + +**Files:** + +- Modify: `packages/ui/src/components/line-comment-annotations.tsx` +- Create: `packages/ui/src/components/touch-selection-toolbar.tsx` + +### Step 3.1: Create touch selection toolbar component + +- [ ] **Create touch selection toolbar** + +Create file: `packages/ui/src/components/touch-selection-toolbar.tsx` + +```tsx +import { createSignal, createEffect, onCleanup, Show } from "solid-js" +import { IconButton } from "./icon-button" + +type Position = { top: number; left: number } + +export function useTouchSelection() { + const [hasSelection, setHasSelection] = createSignal(false) + const [position, setPosition] = createSignal(null) + + const checkSelection = () => { + const selection = window.getSelection() + if (!selection || selection.isCollapsed) { + setHasSelection(false) + setPosition(null) + return + } + + const range = selection.getRangeAt(0) + const rect = range.getBoundingClientRect() + + setHasSelection(true) + setPosition({ + top: rect.top - 40, // Position above selection + left: rect.left + rect.width / 2, + }) + } + + const handleSelectionChange = () => { + // Delay to allow selection to complete + setTimeout(checkSelection, 10) + } + + const handleTouchEnd = () => { + setTimeout(checkSelection, 100) + } + + createEffect(() => { + document.addEventListener("selectionchange", handleSelectionChange) + document.addEventListener("touchend", handleTouchEnd) + + onCleanup(() => { + document.removeEventListener("selectionchange", handleSelectionChange) + document.removeEventListener("touchend", handleTouchEnd) + }) + }) + + return { + hasSelection, + position, + clearSelection: () => { + window.getSelection()?.removeAllRanges() + setHasSelection(false) + setPosition(null) + }, + } +} + +export function TouchSelectionToolbar(props: { + position: Position | null + onAddComment: () => void + onClose: () => void +}) { + return ( + + {(pos) => ( +
+ { + props.onAddComment() + props.onClose() + }} + aria-label="Add comment" + /> +
+ )} +
+ ) +} +``` + +- [ ] **Commit Step 3.1** + +```bash +git add packages/ui/src/components/touch-selection-toolbar.tsx +git commit -m "feat(ui): add touch selection toolbar component" +``` + +### Step 3.2: Integrate touch selection with line comments + +- [ ] **Modify line-comment-annotations.tsx for touch support** + +Modify file: `packages/ui/src/components/line-comment-annotations.tsx` + +Add touch detection and selection toolbar: + +```tsx +import { createMediaQuery } from "@solid-primitives/media" +import { TouchSelectionToolbar, useTouchSelection } from "./touch-selection-toolbar" + +// In component body: +const isTouch = createMediaQuery("(hover: none)") +const touchSelection = useTouchSelection() + +// In render, add touch toolbar when on touch device: + + { + // Trigger comment dialog with current selection + commentsUi.onLineSelected(getCurrentSelection()) + }} + onClose={touchSelection.clearSelection} + /> + +``` + +- [ ] **Commit Step 3.2** + +```bash +git add packages/ui/src/components/line-comment-annotations.tsx +git commit -m "feat(ui): integrate touch selection with line comments" +``` + +--- + +## Task 4: Long-Press Context Menu for File Tree + +**Files:** + +- Create: `packages/app/src/hooks/use-long-press.ts` +- Modify: `packages/app/src/components/file-tree.tsx` + +### Step 4.1: Create long-press hook + +- [ ] **Create use-long-press hook** + +Create file: `packages/app/src/hooks/use-long-press.ts` + +```tsx +import { createSignal, onCleanup } from "solid-js" + +type LongPressOptions = { + delay?: number + onLongPress: () => void + onTouchStart?: () => void + onTouchEnd?: () => void +} + +export function useLongPress(options: LongPressOptions) { + const { delay = 500, onLongPress, onTouchStart, onTouchEnd } = options + const [isLongPress, setIsLongPress] = createSignal(false) + + let timeoutId: ReturnType | undefined + let startPos = { x: 0, y: 0 } + + const handleTouchStart = (e: TouchEvent) => { + const touch = e.touches[0] + startPos = { x: touch.clientX, y: touch.clientY } + + onTouchStart?.() + + timeoutId = setTimeout(() => { + setIsLongPress(true) + onLongPress() + // Optional: haptic feedback + if (navigator.vibrate) { + navigator.vibrate(50) + } + }, delay) + } + + const handleTouchMove = (e: TouchEvent) => { + if (!timeoutId) return + + const touch = e.touches[0] + const dx = Math.abs(touch.clientX - startPos.x) + const dy = Math.abs(touch.clientY - startPos.y) + + // Cancel if moved more than 10px + if (dx > 10 || dy > 10) { + clearTimeout(timeoutId) + timeoutId = undefined + } + } + + const handleTouchEnd = () => { + if (timeoutId) { + clearTimeout(timeoutId) + timeoutId = undefined + } + onTouchEnd?.() + setIsLongPress(false) + } + + const bind = { + ontouchstart: handleTouchStart, + ontouchmove: handleTouchMove, + ontouchend: handleTouchEnd, + } + + onCleanup(() => { + if (timeoutId) { + clearTimeout(timeoutId) + } + }) + + return { + isLongPress, + bind, + } +} +``` + +- [ ] **Commit Step 4.1** + +```bash +git add packages/app/src/hooks/use-long-press.ts +git commit -m "feat(app): add use-long-press hook" +``` + +### Step 4.2: Add context menu to file tree items + +- [ ] **Modify file-tree.tsx to add long-press menu** + +Modify file: `packages/app/src/components/file-tree.tsx` + +Add imports: + +```tsx +import { useLongPress } from "@/hooks/use-long-press" +import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { usePrompt } from "@/context/prompt" +import { useLanguage } from "@/context/language" +import { createMediaQuery } from "@solid-primitives/media" +``` + +Add context menu to file items (find the file item render and add): + +```tsx +// In FileItem component or equivalent +const prompt = usePrompt() +const language = useLanguage() +const isTouch = createMediaQuery("(hover: none)") +const [contextMenuOpen, setContextMenuOpen] = createSignal(false) + +const { bind } = useLongPress({ + onLongPress: () => { + if (isTouch() && props.node.type === "file") { + setContextMenuOpen(true) + } + }, +}) + +// In render, wrap file item with context menu: +
+ {/* existing file item content */} + + + + +
+``` + +- [ ] **Add i18n key for "Add to Conversation"** + +Add to `packages/app/src/i18n/en.ts`: + +```tsx +"fileTree.addToConversation": "Add to conversation", +``` + +Add to `packages/app/src/i18n/zh.ts`: + +```tsx +"fileTree.addToConversation": "添加到对话", +``` + +- [ ] **Commit Step 4.2** + +```bash +git add packages/app/src/components/file-tree.tsx packages/app/src/i18n/en.ts packages/app/src/i18n/zh.ts +git commit -m "feat(app): add long-press context menu to file tree" +``` + +--- + +## Task 5: Markdown Render Toggle + +**Files:** + +- Modify: `packages/app/src/pages/session/file-tabs.tsx` +- Create: `packages/app/src/components/markdown-view-toggle.tsx` + +### Step 5.1: Create markdown view toggle component + +- [ ] **Create markdown view toggle** + +Create file: `packages/app/src/components/markdown-view-toggle.tsx` + +```tsx +import { Button } from "@opencode-ai/ui/button" +import { useLanguage } from "@/context/language" + +export type MarkdownViewMode = "rendered" | "source" + +export function MarkdownViewToggle(props: { mode: MarkdownViewMode; onChange: (mode: MarkdownViewMode) => void }) { + const language = useLanguage() + + return ( +
+ + +
+ ) +} +``` + +- [ ] **Add i18n keys** + +Add to `packages/app/src/i18n/en.ts`: + +```tsx +"markdown.view.rendered": "Rendered", +"markdown.view.source": "Source", +``` + +Add to `packages/app/src/i18n/zh.ts`: + +```tsx +"markdown.view.rendered": "渲染", +"markdown.view.source": "源码", +``` + +- [ ] **Commit Step 5.1** + +```bash +git add packages/app/src/components/markdown-view-toggle.tsx packages/app/src/i18n/en.ts packages/app/src/i18n/zh.ts +git commit -m "feat(app): add markdown view toggle component" +``` + +### Step 5.2: Integrate markdown toggle into file tabs + +- [ ] **Modify file-tabs.tsx to support markdown rendering** + +Modify file: `packages/app/src/pages/session/file-tabs.tsx` + +Add imports: + +```tsx +import { Markdown } from "@opencode-ai/ui/markdown" +import { MarkdownViewToggle, type MarkdownViewMode } from "@/components/markdown-view-toggle" +import { createStore } from "solid-js/store" +``` + +Add markdown detection and state: + +```tsx +// In FileTabContent component +const isMarkdown = createMemo(() => { + const p = path() + if (!p) return false + return p.endsWith(".md") || p.endsWith(".markdown") +}) + +const [mdViewMode, setMdViewMode] = createStore>({}) + +const currentMdMode = createMemo(() => { + const p = path() + if (!p) return "rendered" + return mdViewMode[p] ?? "rendered" +}) +``` + +Modify render function to handle markdown: + +```tsx +const renderFile = (source: string) => ( +
+ +
+ { + const p = path() + if (p) setMdViewMode(p, mode) + }} + /> +
+
+ + + } + > +
+ +
+
+
+) +``` + +- [ ] **Commit Step 5.2** + +```bash +git add packages/app/src/pages/session/file-tabs.tsx +git commit -m "feat(app): add markdown render/source toggle to file tabs" +``` + +--- + +## Task 6: Integration Testing + +### Step 6.1: Manual testing checklist + +- [ ] **Test overflow menu** + +1. Open app on desktop (> 768px) +2. Verify all toolbar buttons visible +3. Resize to < 768px +4. Verify overflow menu appears with hidden options +5. Click overflow items, verify functionality + +- [ ] **Test mobile panel layout** + +1. Open app on mobile/< 768px +2. Open review panel - verify fullscreen +3. Open file tree - verify fullscreen +4. Switch between panels via overflow menu + +- [ ] **Test touch selection** + +1. On touch device, open code file +2. Select text by long-press and drag +3. Verify "+" toolbar appears near selection +4. Tap "+", verify comment dialog opens + +- [ ] **Test long-press file menu** + +1. On touch device, open file tree +2. Long-press a file item (500ms) +3. Verify context menu appears +4. Tap "Add to conversation" +5. Verify file reference appears in chat input + +- [ ] **Test markdown toggle** + +1. Open a .md file +2. Verify rendered view shows by default +3. Click "Source" button +4. Verify raw markdown shown +5. Click "Rendered" button +6. Verify rendered view returns + +### Step 6.2: Commit final changes + +```bash +git add -A +git commit -m "feat(app): complete mobile touch optimization + +- Add toolbar overflow menu for hidden buttons +- Make panels fullscreen on mobile +- Add touch selection toolbar for comments +- Add long-press context menu for file tree +- Add markdown render/source toggle" +``` + +--- + +## Summary + +| Task | Description | Files Changed | +| ---- | ----------------------- | --------------------- | +| 1 | Toolbar Overflow Menu | 2 created, 1 modified | +| 2 | Mobile Panel Fullscreen | 3 modified | +| 3 | Touch Selection | 1 created, 1 modified | +| 4 | Long-Press Menu | 1 created, 1 modified | +| 5 | Markdown Toggle | 1 created, 1 modified | +| 6 | Integration Testing | Manual verification | diff --git a/packages/app/docs/superpowers/specs/2026-03-23-mobile-touch-optimization-design.md b/packages/app/docs/superpowers/specs/2026-03-23-mobile-touch-optimization-design.md new file mode 100644 index 000000000000..2075876dd9e4 --- /dev/null +++ b/packages/app/docs/superpowers/specs/2026-03-23-mobile-touch-optimization-design.md @@ -0,0 +1,204 @@ +# OpenCode App 移动端触屏优化设计 + +**日期**: 2026-03-23 +**状态**: 待审查 +**作者**: AI Agent + 用户协作 + +--- + +## 概述 + +优化 OpenCode App 的移动端/触屏体验,在保持桌面端功能完整的前提下,解决以下 5 个核心痛点。 + +--- + +## 背景 + +当前 OpenCode App 主要面向桌面端设计,在移动端/触屏设备上存在以下问题: + +1. 工具栏按钮在窄屏时直接消失,无法访问 +2. 面板在窄屏时分割显示,空间不足 +3. 选中代码添加评论功能依赖悬停,触屏无法使用 +4. 拖拽文件到 chat 的功能在触屏无法使用 +5. Markdown 文件只显示源码,缺少渲染视图 + +--- + +## 设计目标 + +- 保持桌面端功能完整,不破坏现有体验 +- 移动端/触屏交互符合平台习惯 +- 实现成本可控,复用现有组件和模式 + +--- + +## 痛点与方案 + +### 痛点 1:工具栏溢出处理 + +**问题**: 当屏幕宽度小于断点时,工具栏按钮直接隐藏,用户无法访问这些功能。 + +**现有断点**: + +- `md: 768px` - 搜索文件、审查、文件树按钮隐藏 +- `xl: 1280px` - 打开应用按钮隐藏 + +**方案**: 固定断点 + 溢出菜单 + +- 保持现有 Tailwind 断点逻辑 +- 在工具栏最右侧添加"更多"按钮 (⋯) +- 当按钮因断点隐藏时,自动出现在溢出菜单中 +- 点击溢出菜单项触发对应功能 + +**实现要点**: + +- 修改 `packages/app/src/components/session/session-header.tsx` +- 新增溢出菜单组件,动态检测隐藏的按钮 +- 菜单项包含:搜索文件、审查面板、文件树、打开应用 + +--- + +### 痛点 2:面板布局不适配 + +**问题**: 窄屏时多个面板分割显示,每个面板空间不足。 + +**方案**: 移动端面板全屏化 + +- 当 `width < 768px` 时,面板(审查、文件树)采用全屏模式 +- 不再与主内容区并排显示 +- 通过痛点 1 的溢出菜单切换面板 +- 每次只显示一个全屏面板或主内容区 + +**实现要点**: + +- 修改 `packages/app/src/pages/session.tsx` 中的面板布局逻辑 +- 调整 `packages/app/src/pages/session/session-side-panel.tsx` +- 新增移动端布局状态管理 + +--- + +### 痛点 3:选中添加评论 (触屏) + +**问题**: 桌面端通过悬停显示 "+" 按钮添加评论,触屏无悬停能力。 + +**方案**: 选中后浮动工具栏 + +- 用户在代码区选中文本后,在选区附近显示浮动 "+" 按钮 +- 点击 "+" 按钮弹出评论对话框 +- 行为与桌面端一致 + +**实现要点**: + +- 修改 `packages/ui` 中的行评论相关组件 +- 检测触屏设备 (`hover: none`),使用不同交互模式 +- 利用 `selectionchange` 事件监听选中状态 +- 浮动按钮定位在选区上方/下方 + +--- + +### 痛点 4:拖拽文件引用 (触屏) + +**问题**: 桌面端可拖拽文件到 chat 区域添加引用,触屏无法拖拽。 + +**方案**: 长按菜单 + +- 在文件树项目上长按,弹出上下文菜单 +- 菜单包含"添加到对话"选项 +- 点击后添加文件引用到 chat 输入区 + +**实现要点**: + +- 修改 `packages/app/src/components/file-tree.tsx` +- 使用 `touchstart`/`touchend` 实现长按检测 (约 500ms) +- 复用现有上下文菜单组件 +- 调用 `prompt.context.add()` 添加文件引用 + +--- + +### 痛点 5:Markdown 渲染切换 + +**问题**: Markdown 文件只显示源码,缺少渲染视图。 + +**方案**: 自动渲染 + 手动切换 + +- `.md` 文件默认显示渲染后的 HTML 视图 +- 在文件查看区提供"渲染/源码"切换按钮 +- 渲染能力与 GitHub 相当(使用现有 `@opencode-ai/ui/markdown` 组件) + +**实现要点**: + +- 修改 `packages/app/src/pages/session/file-tabs.tsx` +- 检测 `.md` 文件扩展名 +- 渲染视图复用 `Markdown` 组件 +- 添加视图模式状态和切换 UI + +**GitHub 级渲染支持**: + +- GFM (GitHub Flavored Markdown) +- 代码块语法高亮 +- 表格、任务列表 +- 自动链接 + +--- + +## 断点策略 + +沿用 Tailwind CSS 默认断点: + +| 断点 | 宽度 | 行为 | +| ---- | ------ | -------------------------- | +| `sm` | 640px | - | +| `md` | 768px | 面板全屏化、工具栏溢出开始 | +| `lg` | 1024px | - | +| `xl` | 1280px | 打开应用按钮显示 | + +--- + +## 触屏检测 + +使用 CSS Media Query 检测触屏设备: + +```css +@media (hover: none) { + /* 触屏专用样式 */ +} +``` + +JavaScript 检测 (已有实现): + +```typescript +const touch = createMediaQuery("(hover: none)") +``` + +--- + +## 文件变更预估 + +| 文件 | 变更类型 | 说明 | +| ------------------------ | -------- | ----------------- | +| `session-header.tsx` | 修改 | 添加溢出菜单 | +| `session.tsx` | 修改 | 面板全屏化布局 | +| `session-side-panel.tsx` | 修改 | 移动端适配 | +| `file-tabs.tsx` | 修改 | Markdown 渲染切换 | +| `file-tree.tsx` | 修改 | 长按菜单 | +| `line-comment-*` (ui) | 修改 | 触屏选区交互 | + +--- + +## 风险与缓解 + +| 风险 | 缓解措施 | +| -------------- | -------------------------------------- | +| 破坏桌面端体验 | 所有改动通过媒体查询隔离,仅影响移动端 | +| 长按误触 | 设置合理阈值 (500ms),提供触觉反馈 | +| 渲染性能 | Markdown 渲染使用现有缓存机制 | + +--- + +## 成功标准 + +1. 窄屏下所有工具栏功能可通过溢出菜单访问 +2. 面板在移动端以全屏模式显示,切换流畅 +3. 触屏选中代码后可添加评论 +4. 触屏可通过长按添加文件引用 +5. Markdown 文件可切换渲染/源码视图 diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 930832fb6555..5ea4de85d574 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -1,6 +1,9 @@ import { useFile } from "@/context/file" import { encodeFilePath } from "@/context/file/path" +import { useLanguage } from "@/context/language" +import { usePrompt } from "@/context/prompt" import { Collapsible } from "@opencode-ai/ui/collapsible" +import { ContextMenu } from "@opencode-ai/ui/context-menu" import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" import { @@ -108,20 +111,61 @@ const withFileDragImage = (event: DragEvent) => { setTimeout(() => document.body.removeChild(image), 0) } -const FileTreeNode = ( - p: ParentProps & - ComponentProps<"div"> & - ComponentProps<"button"> & { - node: FileNode - level: number - active?: string - nodeClass?: string - draggable: boolean - kinds?: ReadonlyMap - marks?: Set - as?: "div" | "button" - }, -) => { +type FileTreeItemProps = ParentProps & + ComponentProps<"div"> & + ComponentProps<"button"> & { + node: FileNode + level: number + active?: string + nodeClass?: string + draggable: boolean + kinds?: ReadonlyMap + marks?: Set + as?: "div" | "button" + } + +function FileTreeItemWithContextMenu(props: FileTreeItemProps & { onFileClick?: (file: FileNode) => void }) { + const prompt = usePrompt() + const t = useLanguage().t + + const handleAddToContext = () => { + prompt.context.add({ type: "file", path: props.node.path }) + } + + return ( + + + props.onFileClick?.(props.node)} + > + {props.children} + + + + + + + {t("fileTree.addToConversation")} + + + + + ) +} + +const FileTreeNode = (p: FileTreeItemProps) => { const [local, rest] = splitProps(p, [ "node", "level", @@ -452,7 +496,7 @@ export default function FileTree(props: { - props.onFileClick?.(node)} + onFileClick={props.onFileClick} >
@@ -496,7 +539,7 @@ export default function FileTree(props: { - + ) diff --git a/packages/app/src/components/markdown-view-toggle.tsx b/packages/app/src/components/markdown-view-toggle.tsx new file mode 100644 index 000000000000..d23d56e31922 --- /dev/null +++ b/packages/app/src/components/markdown-view-toggle.tsx @@ -0,0 +1,29 @@ +import { Button } from "@opencode-ai/ui/button" +import { useLanguage } from "@/context/language" + +export type MarkdownViewMode = "rendered" | "source" + +export function MarkdownViewToggle(props: { mode: MarkdownViewMode; onChange: (mode: MarkdownViewMode) => void }) { + const language = useLanguage() + + return ( +
+ + +
+ ) +} diff --git a/packages/app/src/components/session/find-assistant-messages.test.ts b/packages/app/src/components/session/find-assistant-messages.test.ts new file mode 100644 index 000000000000..ab944e2cc9f1 --- /dev/null +++ b/packages/app/src/components/session/find-assistant-messages.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, test } from "bun:test" +import type { Message } from "@opencode-ai/sdk/v2/client" +import { findAssistantMessages } from "@opencode-ai/ui/find-assistant-messages" + +function user(id: string): Message { + return { + id, + role: "user", + sessionID: "session-1", + time: { created: 1 }, + } as unknown as Message +} + +function assistant(id: string, parentID: string): Message { + return { + id, + role: "assistant", + sessionID: "session-1", + parentID, + time: { created: 1 }, + } as unknown as Message +} + +describe("findAssistantMessages", () => { + test("normal ordering: assistant after user in array → found via forward scan", () => { + const messages = [user("u1"), assistant("a1", "u1")] + const result = findAssistantMessages(messages, 0, "u1") + expect(result).toHaveLength(1) + expect(result[0].id).toBe("a1") + }) + + test("clock skew: assistant before user in array → found via backward scan", () => { + // When client clock is ahead, user ID sorts after assistant ID, + // so assistant appears earlier in the ID-sorted message array + const messages = [assistant("a1", "u1"), user("u1")] + const result = findAssistantMessages(messages, 1, "u1") + expect(result).toHaveLength(1) + expect(result[0].id).toBe("a1") + }) + + test("no assistant messages → returns empty array", () => { + const messages = [user("u1"), user("u2")] + const result = findAssistantMessages(messages, 0, "u1") + expect(result).toHaveLength(0) + }) + + test("multiple assistant messages with matching parentID → all found", () => { + const messages = [user("u1"), assistant("a1", "u1"), assistant("a2", "u1")] + const result = findAssistantMessages(messages, 0, "u1") + expect(result).toHaveLength(2) + expect(result[0].id).toBe("a1") + expect(result[1].id).toBe("a2") + }) + + test("does not return assistant messages with different parentID", () => { + const messages = [user("u1"), assistant("a1", "u1"), assistant("a2", "other")] + const result = findAssistantMessages(messages, 0, "u1") + expect(result).toHaveLength(1) + expect(result[0].id).toBe("a1") + }) + + test("stops forward scan at next user message", () => { + const messages = [user("u1"), assistant("a1", "u1"), user("u2"), assistant("a2", "u1")] + const result = findAssistantMessages(messages, 0, "u1") + expect(result).toHaveLength(1) + expect(result[0].id).toBe("a1") + }) + + test("stops backward scan at previous user message", () => { + const messages = [assistant("a0", "u1"), user("u0"), assistant("a1", "u1"), user("u1")] + const result = findAssistantMessages(messages, 3, "u1") + expect(result).toHaveLength(1) + expect(result[0].id).toBe("a1") + }) + + test("invalid index returns empty array", () => { + const messages = [user("u1")] + expect(findAssistantMessages(messages, -1, "u1")).toHaveLength(0) + expect(findAssistantMessages(messages, 5, "u1")).toHaveLength(0) + }) +}) diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 495b32340580..4073bdeda7d4 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -8,6 +8,7 @@ import { Spinner } from "@opencode-ai/ui/spinner" import { showToast } from "@opencode-ai/ui/toast" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { getFilename } from "@opencode-ai/util/path" +import { createMediaQuery } from "@solid-primitives/media" import { createEffect, createMemo, For, onCleanup, Show } from "solid-js" import { createStore } from "solid-js/store" import { Portal } from "solid-js/web" @@ -24,6 +25,7 @@ import { messageAgentColor } from "@/utils/agent" import { decode64 } from "@/utils/base64" import { Persist, persisted } from "@/utils/persist" import { StatusPopover } from "../status-popover" +import { ToolbarOverflowMenu, type OverflowItem } from "./toolbar-overflow-menu" const OPEN_APPS = [ "vscode", @@ -265,6 +267,9 @@ export function SessionHeader() { const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center")) const rightMount = createMemo(() => document.getElementById("opencode-titlebar-right")) + const isMd = createMediaQuery("(min-width: 768px)") + const isXl = createMediaQuery("(min-width: 1280px)") + return ( <> @@ -415,6 +420,17 @@ export function SessionHeader() {
+ + + + @@ -434,48 +450,95 @@ export function SessionHeader() { - + + +
+ + + { + view().reviewPanel.toggle() + if (!isMd()) { + layout.mobilePanel.showReview() + } + }, + active: view().reviewPanel.opened(), + visible: isMd(), + }, + { + id: "fileTree", + label: language.t("command.fileTree.toggle"), + icon: layout.fileTree.opened() ? "file-tree-active" : "file-tree", + onClick: () => { + layout.fileTree.toggle() + if (!isMd()) { + layout.mobilePanel.showFileTree() + } + }, + active: layout.fileTree.opened(), + visible: isMd(), + }, + { + id: "search", + label: language.t("session.header.searchFiles"), + icon: "magnifying-glass", + onClick: () => command.trigger("file.open"), + visible: isMd(), + }, + { + id: "openApp", + label: language.t("session.header.openIn"), + icon: "folder", + onClick: () => openDir(current().id), + visible: isXl(), + }, + ]} + /> diff --git a/packages/app/src/components/session/toolbar-overflow-menu.tsx b/packages/app/src/components/session/toolbar-overflow-menu.tsx new file mode 100644 index 000000000000..1a4fc27d8a52 --- /dev/null +++ b/packages/app/src/components/session/toolbar-overflow-menu.tsx @@ -0,0 +1,48 @@ +import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { Icon, type IconProps } from "@opencode-ai/ui/icon" +import { IconButton } from "@opencode-ai/ui/icon-button" +import { For, Show } from "solid-js" +import { useLanguage } from "@/context/language" + +export type OverflowItem = { + id: string + label: string + icon: IconProps["name"] + onClick: () => void + active?: boolean + visible: boolean +} + +export function ToolbarOverflowMenu(props: { items: OverflowItem[] }) { + const language = useLanguage() + + const hiddenItems = () => props.items.filter((item) => !item.visible) + + const hasOverflow = () => hiddenItems().length > 0 + + return ( + + + + + + + {(item) => ( + + + {item.label} + + )} + + + + + + ) +} diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 77de1a73ce69..ed563af9ec22 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -287,6 +287,9 @@ export function Titlebar() {
+
+ BETA +
diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index 78928118d72a..cc9c50a8868b 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -43,6 +43,10 @@ type SessionView = { pendingMessageAt?: number } +type MobilePanel = { + active: "review" | "fileTree" | "file" | null +} + type TabHandoff = { dir: string id: string @@ -253,6 +257,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( mobileSidebar: { opened: false, }, + mobilePanel: { + active: null, + } as MobilePanel, sessionTabs: {} as Record, sessionView: {} as Record, handoff: { @@ -688,6 +695,24 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("mobileSidebar", "opened", (x) => !x) }, }, + mobilePanel: { + active: createMemo(() => store.mobilePanel?.active ?? null), + set: (panel: "review" | "fileTree" | "file" | null) => { + setStore("mobilePanel", "active", panel) + }, + showReview: () => { + setStore("mobilePanel", "active", "review") + }, + showFileTree: () => { + setStore("mobilePanel", "active", "fileTree") + }, + showFile: () => { + setStore("mobilePanel", "active", "file") + }, + hide: () => { + setStore("mobilePanel", "active", null) + }, + }, pendingMessage: { set(sessionKey: string, messageID: string) { const at = Date.now() diff --git a/packages/app/src/hooks/use-long-press.ts b/packages/app/src/hooks/use-long-press.ts new file mode 100644 index 000000000000..8ac6f7c89fb4 --- /dev/null +++ b/packages/app/src/hooks/use-long-press.ts @@ -0,0 +1,70 @@ +import { createSignal, onCleanup } from "solid-js" + +type LongPressOptions = { + delay?: number + onLongPress: () => void + onTouchStart?: () => void + onTouchEnd?: () => void +} + +export function useLongPress(options: LongPressOptions) { + const { delay = 500, onLongPress, onTouchStart, onTouchEnd } = options + const [isLongPress, setIsLongPress] = createSignal(false) + + let timeoutId: ReturnType | undefined + let startPos = { x: 0, y: 0 } + + const handleTouchStart = (e: TouchEvent) => { + const touch = e.touches[0] + startPos = { x: touch.clientX, y: touch.clientY } + + onTouchStart?.() + + timeoutId = setTimeout(() => { + setIsLongPress(true) + onLongPress() + if (navigator.vibrate) { + navigator.vibrate(50) + } + }, delay) + } + + const handleTouchMove = (e: TouchEvent) => { + if (!timeoutId) return + + const touch = e.touches[0] + const dx = Math.abs(touch.clientX - startPos.x) + const dy = Math.abs(touch.clientY - startPos.y) + + if (dx > 10 || dy > 10) { + clearTimeout(timeoutId) + timeoutId = undefined + } + } + + const handleTouchEnd = () => { + if (timeoutId) { + clearTimeout(timeoutId) + timeoutId = undefined + } + onTouchEnd?.() + setIsLongPress(false) + } + + const bind = { + ontouchstart: handleTouchStart, + ontouchmove: handleTouchMove, + ontouchend: handleTouchEnd, + } + + onCleanup(() => { + if (timeoutId) { + clearTimeout(timeoutId) + } + }) + + return { + isLongPress, + bind, + } +} diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 579b740d3aee..f12fa438469f 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -519,6 +519,7 @@ export const dict = { "home.empty.title": "No recent projects", "home.empty.description": "Get started by opening a local project", + "session.backToChat": "Back to chat", "session.tab.session": "Session", "session.tab.review": "Review", "session.tab.context": "Context", @@ -945,4 +946,9 @@ export const dict = { "workspace.reset.archived.one": "1 session will be archived.", "workspace.reset.archived.many": "{{count}} sessions will be archived.", "workspace.reset.note": "This will reset the workspace to match the default branch.", + + "fileTree.addToConversation": "Add to conversation", + + "markdown.view.rendered": "Rendered", + "markdown.view.source": "Source", } diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index cf64ca9b2c54..081aecb98141 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -496,6 +496,7 @@ export const dict = { "home.empty.title": "没有最近项目", "home.empty.description": "通过打开本地项目开始使用", + "session.backToChat": "回到聊天", "session.tab.session": "会话", "session.tab.review": "审查", "session.tab.context": "上下文", @@ -927,4 +928,9 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "创建持久化项目图标失败", "error.childStore.storeCreateFailed": "创建存储失败", "terminal.connectionLost.abnormalClose": "WebSocket 异常关闭:{{code}}", + + "fileTree.addToConversation": "添加到对话", + + "markdown.view.rendered": "渲染", + "markdown.view.source": "源码", } satisfies Partial> diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 428826f6ad97..25b703ca6c9a 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -47,6 +47,7 @@ import { type DiffStyle, SessionReviewTab, type SessionReviewTabProps } from "@/ import { useSessionLayout } from "@/pages/session/session-layout" import { syncSessionModel } from "@/pages/session/session-model-helpers" import { SessionSidePanel } from "@/pages/session/session-side-panel" +import { MobileFullscreenPanel } from "@/pages/session/mobile-fullscreen-panel" import { TerminalPanel } from "@/pages/session/terminal-panel" import { useSessionCommands } from "@/pages/session/use-session-commands" import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll" @@ -1822,6 +1823,16 @@ export default function Page() { reviewSnap={ui.reviewSnap} size={size} /> + + + layout.mobilePanel.hide()} + onShowFile={() => layout.mobilePanel.showFile()} + onShowFileTree={() => layout.mobilePanel.showFileTree()} + reviewPanel={reviewPanel} + /> +
diff --git a/packages/app/src/pages/session/composer/session-question-dock.tsx b/packages/app/src/pages/session/composer/session-question-dock.tsx index 7ba07b15d0c6..9dd890893432 100644 --- a/packages/app/src/pages/session/composer/session-question-dock.tsx +++ b/packages/app/src/pages/session/composer/session-question-dock.tsx @@ -4,6 +4,7 @@ import { useMutation } from "@tanstack/solid-query" import { Button } from "@opencode-ai/ui/button" import { DockPrompt } from "@opencode-ai/ui/dock-prompt" import { Icon } from "@opencode-ai/ui/icon" +import { IconButton } from "@opencode-ai/ui/icon-button" import { showToast } from "@opencode-ai/ui/toast" import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2" import { useLanguage } from "@/context/language" @@ -25,6 +26,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit custom: cached?.custom ?? ([] as string[]), customOn: cached?.customOn ?? ([] as boolean[]), editing: false, + collapsed: false, }) let root: HTMLDivElement | undefined @@ -35,6 +37,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit const input = createMemo(() => store.custom[store.tab] ?? "") const on = createMemo(() => store.customOn[store.tab] === true) const multi = createMemo(() => question()?.multiple === true) + const picked = createMemo(() => store.answers[store.tab]?.length ?? 0) const summary = createMemo(() => { const n = Math.min(store.tab + 1, total()) @@ -43,6 +46,8 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit const last = createMemo(() => store.tab >= total() - 1) + const fold = () => setStore("collapsed", (value) => !value) + const customUpdate = (value: string, selected: boolean = on()) => { const prev = input().trim() const next = value.trim() @@ -261,9 +266,21 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit kind="question" ref={(el) => (root = el)} header={ - <> +
{ + if (event.key !== "Enter" && event.key !== " ") return + event.preventDefault() + fold() + }} + >
{summary()}
-
+
{(_, i) => (
- +
+ { + event.preventDefault() + event.stopPropagation() + }} + onClick={(event) => { + event.stopPropagation() + fold() + }} + aria-label={store.collapsed ? language.t("session.todo.expand") : language.t("session.todo.collapse")} + /> +
+
} footer={ <> @@ -301,56 +343,121 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } > -
{question()?.question}
- {language.t("ui.question.singleHint")}
}> -
{language.t("ui.question.multiHint")}
+
{ + if (!store.collapsed) return + if (event.key !== "Enter" && event.key !== " ") return + event.preventDefault() + fold() + }} + > + {question()?.question} +
+ 0}> +
+ {picked()} answer{picked() === 1 ? "" : "s"} selected +
-
- - {(opt, i) => { - const picked = () => store.answers[store.tab]?.includes(opt.label) ?? false - return ( + }> +
{language.t("ui.question.multiHint")}
+ +
+ + {(opt, i) => { + const picked = () => store.answers[store.tab]?.includes(opt.label) ?? false + return ( + + ) + }} + + + selectOption(i())} + onClick={customOpen} > -