diff --git a/.claude-context.md b/.claude-context.md index 21d5e990..97e20f6b 100644 --- a/.claude-context.md +++ b/.claude-context.md @@ -1,9 +1,11 @@ # Claude Context: Detaching Tauri from Yaak ## Goal + Make Yaak runnable as a standalone CLI without Tauri as a dependency. The core Rust crates in `crates/` should be usable independently, while Tauri-specific code lives in `crates-tauri/`. ## Project Structure + ``` crates/ # Core crates - should NOT depend on Tauri crates-tauri/ # Tauri-specific crates (yaak-app-client, yaak-tauri-utils, etc.) @@ -13,11 +15,13 @@ crates-cli/ # CLI crate (yaak-cli) ## Completed Work ### 1. Folder Restructure + - Moved Tauri-dependent app code to `crates-tauri/yaak-app-client/` - Created `crates-tauri/yaak-tauri-utils/` for shared Tauri utilities (window traits, api_client, error handling) - Created `crates-cli/yaak-cli/` for the standalone CLI ### 2. Decoupled Crates (no longer depend on Tauri) + - **yaak-models**: Uses `init_standalone()` pattern for CLI database access - **yaak-http**: Removed Tauri plugin, HttpConnectionManager initialized in yaak-app setup - **yaak-common**: Only contains Tauri-free utilities (serde, platform) @@ -25,6 +29,7 @@ crates-cli/ # CLI crate (yaak-cli) - **yaak-grpc**: Replaced AppHandle with GrpcConfig struct, uses tokio::process::Command instead of Tauri sidecar ### 3. CLI Implementation + - Basic CLI at `crates-cli/yaak-cli/src/main.rs` - Commands: workspaces, requests, send (by ID), get (ad-hoc URL), create - Uses same database as Tauri app via `yaak_models::init_standalone()` @@ -32,12 +37,14 @@ crates-cli/ # CLI crate (yaak-cli) ## Remaining Work ### Crates Still Depending on Tauri (in `crates/`) + 1. **yaak-git** (3 files) - Moderate complexity 2. **yaak-plugins** (13 files) - **Hardest** - deeply integrated with Tauri for plugin-to-window communication 3. **yaak-sync** (4 files) - Moderate complexity 4. **yaak-ws** (5 files) - Moderate complexity ### Pattern for Decoupling + 1. Remove Tauri plugin `init()` function from the crate 2. Move commands to `yaak-app/src/commands.rs` or keep inline in `lib.rs` 3. Move extension traits (e.g., `SomethingManagerExt`) to yaak-app or yaak-tauri-utils @@ -47,6 +54,7 @@ crates-cli/ # CLI crate (yaak-cli) 7. Replace `tauri::async_runtime::block_on` with `tokio::runtime::Handle::current().block_on()` ## Key Files + - `crates-tauri/yaak-app-client/src/lib.rs` - Main Tauri app, setup block initializes managers - `crates-tauri/yaak-app-client/src/commands.rs` - Migrated Tauri commands - `crates-tauri/yaak-app-client/src/models_ext.rs` - Database plugin and extension traits @@ -54,9 +62,11 @@ crates-cli/ # CLI crate (yaak-cli) - `crates/yaak-models/src/lib.rs` - Contains `init_standalone()` for CLI usage ## Git Branch + Working on `detach-tauri` branch. ## Recent Commits + ``` c40cff40 Remove Tauri dependencies from yaak-crypto and yaak-grpc df495f1d Move Tauri utilities from yaak-common to yaak-tauri-utils @@ -67,6 +77,7 @@ e718a5f1 Refactor models_ext to use init_standalone from yaak-models ``` ## Testing + - Run `cargo check -p ` to verify a crate builds without Tauri - Run `npm run client:dev` to test the Tauri app still works - Run `cargo run -p yaak-cli -- --help` to test the CLI diff --git a/.claude/commands/release/generate-release-notes.md b/.claude/commands/release/generate-release-notes.md index 97fba5bc..a2e98b4a 100644 --- a/.claude/commands/release/generate-release-notes.md +++ b/.claude/commands/release/generate-release-notes.md @@ -8,7 +8,7 @@ Generate formatted release notes for Yaak releases by analyzing git history and ## What to do 1. Identifies the version tag and previous version -2. Retrieves all commits between versions +2. Retrieves all commits between versions - If the version is a beta version, it retrieves commits between the beta version and previous beta version - If the version is a stable version, it retrieves commits between the stable version and the previous stable version 3. Fetches PR descriptions for linked issues to find: diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..9b77ea71 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 33c88d65..7286aa20 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -11,6 +11,7 @@ - [ ] I added or updated tests when reasonable. Approved feedback item (required if not a bug fix or small-scope improvement): + ## Related diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf32f4a3..55216e09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,17 +14,20 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: voidzero-dev/setup-vp@v1 + with: + node-version: "24" + cache: true - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: shared-key: ci cache-on-failure: true - - run: npm ci + - run: vp install - run: npm run bootstrap - run: npm run lint - name: Run JS Tests - run: npm test + run: vp test - name: Run Rust Tests run: cargo test --all diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index d300267f..9471a059 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -47,4 +47,3 @@ jobs: # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://code.claude.com/docs/en/cli-reference for available options # claude_args: '--allowed-tools Bash(gh pr:*)' - diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index f7a51f54..15ad3070 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -50,8 +50,11 @@ jobs: - name: Checkout yaakapp/app uses: actions/checkout@v4 - - name: Setup Node - uses: actions/setup-node@v4 + - name: Setup Vite+ + uses: voidzero-dev/setup-vp@v1 + with: + node-version: "24" + cache: true - name: install Rust stable uses: dtolnay/rust-toolchain@stable @@ -87,13 +90,13 @@ jobs: echo $dir >> $env:GITHUB_PATH & $exe --version - - run: npm ci + - run: vp install - run: npm run bootstrap env: YAAK_TARGET_ARCH: ${{ matrix.yaak_arch }} - run: npm run lint - name: Run JS Tests - run: npm test + run: vp test - name: Run Rust Tests run: cargo test --all --exclude yaak-cli diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml index 457565e8..008b34c8 100644 --- a/.github/workflows/sponsors.yml +++ b/.github/workflows/sponsors.yml @@ -16,23 +16,23 @@ jobs: uses: JamesIves/github-sponsors-readme-action@v1 with: token: ${{ secrets.SPONSORS_PAT }} - file: 'README.md' + file: "README.md" maximum: 1999 template: 'User avatar: {{{ login }}}  ' active-only: false include-private: true - marker: 'sponsors-base' + marker: "sponsors-base" - name: Generate Sponsors uses: JamesIves/github-sponsors-readme-action@v1 with: token: ${{ secrets.SPONSORS_PAT }} - file: 'README.md' + file: "README.md" minimum: 2000 template: 'User avatar: {{{ login }}}  ' active-only: false include-private: true - marker: 'sponsors-premium' + marker: "sponsors-premium" # ⚠️ Note: You can use any deployment step here to automatically push the README # changes back to your branch. @@ -41,4 +41,4 @@ jobs: with: branch: main force: false - folder: '.' + folder: "." diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..d845d9d8 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24.14.0 diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..dc68961c --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +# vite-plugin-wasm has not yet declared Vite 8 in its peerDependencies +legacy-peer-deps=true diff --git a/.oxfmtignore b/.oxfmtignore new file mode 100644 index 00000000..7d38738a --- /dev/null +++ b/.oxfmtignore @@ -0,0 +1,2 @@ +**/bindings/** +crates/yaak-templates/pkg/** diff --git a/.husky/post-checkout b/.vite-hooks/post-checkout similarity index 100% rename from .husky/post-checkout rename to .vite-hooks/post-checkout diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit new file mode 100644 index 00000000..05b9080e --- /dev/null +++ b/.vite-hooks/pre-commit @@ -0,0 +1 @@ +vp lint diff --git a/.vscode/extensions.json b/.vscode/extensions.json index df9e35ce..b2b5eb73 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["biomejs.biome", "rust-lang.rust-analyzer", "bradlc.vscode-tailwindcss"] + "recommendations": [ + "rust-lang.rust-analyzer", + "bradlc.vscode-tailwindcss", + "VoidZero.vite-plus-extension-pack" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index ce1fe758..b8065359 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,8 @@ { - "editor.defaultFormatter": "biomejs.biome", + "editor.defaultFormatter": "oxc.oxc-vscode", "editor.formatOnSave": true, - "biome.enabled": true, - "biome.lint.format.enable": true + "editor.formatOnSaveMode": "file", + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "explicit" + } } diff --git a/Cargo.toml b/Cargo.toml index 30af53cc..48090c3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,38 +1,38 @@ [workspace] resolver = "2" members = [ - "crates/yaak", - # Common/foundation crates - "crates/common/yaak-database", - "crates/common/yaak-rpc", - # Shared crates (no Tauri dependency) - "crates/yaak-core", - "crates/yaak-common", - "crates/yaak-crypto", - "crates/yaak-git", - "crates/yaak-grpc", - "crates/yaak-http", - "crates/yaak-models", - "crates/yaak-plugins", - "crates/yaak-sse", - "crates/yaak-sync", - "crates/yaak-templates", - "crates/yaak-tls", - "crates/yaak-ws", - "crates/yaak-api", - "crates/yaak-proxy", - # Proxy-specific crates - "crates-proxy/yaak-proxy-lib", - # CLI crates - "crates-cli/yaak-cli", - # Tauri-specific crates - "crates-tauri/yaak-app-client", - "crates-tauri/yaak-app-proxy", - "crates-tauri/yaak-fonts", - "crates-tauri/yaak-license", - "crates-tauri/yaak-mac-window", - "crates-tauri/yaak-tauri-utils", - "crates-tauri/yaak-window", + "crates/yaak", + # Common/foundation crates + "crates/common/yaak-database", + "crates/common/yaak-rpc", + # Shared crates (no Tauri dependency) + "crates/yaak-core", + "crates/yaak-common", + "crates/yaak-crypto", + "crates/yaak-git", + "crates/yaak-grpc", + "crates/yaak-http", + "crates/yaak-models", + "crates/yaak-plugins", + "crates/yaak-sse", + "crates/yaak-sync", + "crates/yaak-templates", + "crates/yaak-tls", + "crates/yaak-ws", + "crates/yaak-api", + "crates/yaak-proxy", + # Proxy-specific crates + "crates-proxy/yaak-proxy-lib", + # CLI crates + "crates-cli/yaak-cli", + # Tauri-specific crates + "crates-tauri/yaak-app-client", + "crates-tauri/yaak-app-proxy", + "crates-tauri/yaak-fonts", + "crates-tauri/yaak-license", + "crates-tauri/yaak-mac-window", + "crates-tauri/yaak-tauri-utils", + "crates-tauri/yaak-window", ] [workspace.dependencies] diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 77ba2cf9..cf5683a2 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,24 +1,26 @@ # Developer Setup -Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so +Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so uses Rust and HTML/CSS/JS for the main application but there is also a plugin system powered by a Node.js sidecar that communicates to the app over gRPC. -Because of the moving parts, there are a few setup steps required before development can +Because of the moving parts, there are a few setup steps required before development can begin. ## Prerequisites Make sure you have the following tools installed: -- [Node.js](https://nodejs.org/en/download/package-manager) +- [Node.js](https://nodejs.org/en/download/package-manager) (v24+) - [Rust](https://www.rust-lang.org/tools/install) +- [Vite+](https://vite.dev/guide/vite-plus) (`vp` CLI) Check the installations with the following commands: ```shell node -v npm -v +vp --version rustc --version ``` @@ -45,12 +47,12 @@ npm start ## SQLite Migrations New migrations can be created from the `src-tauri/` directory: - + ```shell npm run migration ``` -Rerun the app to apply the migrations. +Rerun the app to apply the migrations. _Note: For safety, development builds use a separate database location from production builds._ @@ -61,9 +63,9 @@ _Note: For safety, development builds use a separate database location from prod lezer-generator components/core/Editor//.grammar > components/core/Editor//.ts ``` -## Linting & Formatting +## Linting and Formatting -This repo uses Biome for linting and formatting (replacing ESLint + Prettier). +This repo uses [Vite+](https://vite.dev/guide/vite-plus) for linting (oxlint) and formatting (oxfmt). - Lint the entire repo: @@ -71,12 +73,6 @@ This repo uses Biome for linting and formatting (replacing ESLint + Prettier). npm run lint ``` -- Auto-fix lint issues where possible: - -```sh -npm run lint:fix -``` - - Format code: ```sh @@ -84,5 +80,7 @@ npm run format ``` Notes: -- Many workspace packages also expose the same scripts (`lint`, `lint:fix`, and `format`). -- TypeScript type-checking still runs separately via `tsc --noEmit` in relevant packages. + +- A pre-commit hook runs `vp lint` automatically on commit. +- Some workspace packages also run `tsc --noEmit` for type-checking. +- VS Code users should install the recommended extensions for format-on-save support. diff --git a/README.md b/README.md index abe75b9f..980d6048 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@


- -

User avatar: MVST-Solutions  User avatar: dharsanb  User avatar: railwayapp  User avatar: caseyamcl  User avatar: bytebase  User avatar:   

@@ -27,12 +25,10 @@ ![Yaak API Client](https://yaak.app/static/screenshot.png) - ## Features -Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it. -Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in. - +Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it. +Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in. ### 🌐 Work with any API @@ -41,21 +37,23 @@ Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight - Filter and inspect responses with JSONPath or XPath. ### 🔐 Stay secure + - Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication. -- Secure sensitive values with encrypted secrets. +- Secure sensitive values with encrypted secrets. - Store secrets in your OS keychain. ### ☁️ Organize & collaborate + - Group requests into workspaces and nested folders. - Use environment variables to switch between dev, staging, and prod. - Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox. ### 🧩 Extend & customize + - Insert dynamic values like UUIDs or timestamps with template tags. - Pick from built-in themes or build your own. - Create plugins to extend authentication, template tags, or the UI. - ## Contribution Policy > [!IMPORTANT] diff --git a/apps/yaak-client/commands/commands.tsx b/apps/yaak-client/commands/commands.tsx index 9a75b5fd..e1b09484 100644 --- a/apps/yaak-client/commands/commands.tsx +++ b/apps/yaak-client/commands/commands.tsx @@ -1,6 +1,6 @@ -import { createWorkspaceModel, type Folder, modelTypeLabel } from '@yaakapp-internal/models'; -import { applySync, calculateSync } from '@yaakapp-internal/sync'; -import { Button } from '../components/core/Button'; +import { createWorkspaceModel, type Folder, modelTypeLabel } from "@yaakapp-internal/models"; +import { applySync, calculateSync } from "@yaakapp-internal/sync"; +import { Button } from "../components/core/Button"; import { Banner, InlineCode, @@ -11,21 +11,21 @@ import { TableHeaderCell, TableRow, TruncatedWideTableCell, -} from '@yaakapp-internal/ui'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; -import { pluralizeCount } from '../lib/pluralize'; -import { showPrompt } from '../lib/prompt'; -import { resolvedModelNameWithFolders } from '../lib/resolvedModelName'; +} from "@yaakapp-internal/ui"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; +import { pluralizeCount } from "../lib/pluralize"; +import { showPrompt } from "../lib/prompt"; +import { resolvedModelNameWithFolders } from "../lib/resolvedModelName"; export const createFolder = createFastMutation< string | null, void, - Partial> + Partial> >({ - mutationKey: ['create_folder'], + mutationKey: ["create_folder"], mutationFn: async (patch) => { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) { @@ -34,12 +34,12 @@ export const createFolder = createFastMutation< if (!patch.name) { const name = await showPrompt({ - id: 'new-folder', - label: 'Name', - defaultValue: 'Folder', - title: 'New Folder', - confirmText: 'Create', - placeholder: 'Name', + id: "new-folder", + label: "Name", + defaultValue: "Folder", + title: "New Folder", + confirmText: "Create", + placeholder: "Name", }); if (name == null) return null; @@ -47,7 +47,7 @@ export const createFolder = createFastMutation< } patch.sortPriority = patch.sortPriority || -Date.now(); - const id = await createWorkspaceModel({ model: 'folder', workspaceId, ...patch }); + const id = await createWorkspaceModel({ model: "folder", workspaceId, ...patch }); return id; }, }); @@ -61,12 +61,12 @@ export const syncWorkspace = createFastMutation< mutationFn: async ({ workspaceId, syncDir, force }) => { const ops = (await calculateSync(workspaceId, syncDir)) ?? []; if (ops.length === 0) { - console.log('Nothing to sync', workspaceId, syncDir); + console.log("Nothing to sync", workspaceId, syncDir); return; } - console.log('Syncing workspace', workspaceId, syncDir, ops); + console.log("Syncing workspace", workspaceId, syncDir, ops); - const dbOps = ops.filter((o) => o.type.startsWith('db')); + const dbOps = ops.filter((o) => o.type.startsWith("db")); if (dbOps.length === 0) { await applySync(workspaceId, syncDir, ops); @@ -74,10 +74,10 @@ export const syncWorkspace = createFastMutation< } const isDeletingWorkspace = ops.some( - (o) => o.type === 'dbDelete' && o.model.model === 'workspace', + (o) => o.type === "dbDelete" && o.model.model === "workspace", ); - console.log('Directory changes detected', { dbOps, ops }); + console.log("Directory changes detected", { dbOps, ops }); if (force) { await applySync(workspaceId, syncDir, ops); @@ -85,9 +85,9 @@ export const syncWorkspace = createFastMutation< } showDialog({ - id: 'commit-sync', - title: 'Changes Detected', - size: 'md', + id: "commit-sync", + title: "Changes Detected", + size: "md", render: ({ hide }) => (
)}

- {pluralizeCount('file', dbOps.length)} in the directory{' '} - {dbOps.length === 1 ? 'has' : 'have'} changed. Do you want to update your workspace? + {pluralizeCount("file", dbOps.length)} in the directory{" "} + {dbOps.length === 1 ? "has" : "have"} changed. Do you want to update your workspace?

@@ -123,20 +123,20 @@ export const syncWorkspace = createFastMutation< let color: string; let model: string; - if (op.type === 'dbCreate') { - label = 'create'; + if (op.type === "dbCreate") { + label = "create"; name = resolvedModelNameWithFolders(op.fs.model); - color = 'text-success'; + color = "text-success"; model = modelTypeLabel(op.fs.model); - } else if (op.type === 'dbUpdate') { - label = 'update'; + } else if (op.type === "dbUpdate") { + label = "update"; name = resolvedModelNameWithFolders(op.fs.model); - color = 'text-info'; + color = "text-info"; model = modelTypeLabel(op.fs.model); - } else if (op.type === 'dbDelete') { - label = 'delete'; + } else if (op.type === "dbDelete") { + label = "delete"; name = resolvedModelNameWithFolders(op.model); - color = 'text-danger'; + color = "text-danger"; model = modelTypeLabel(op.model); } else { return null; diff --git a/apps/yaak-client/commands/createEnvironment.tsx b/apps/yaak-client/commands/createEnvironment.tsx index 1dcc5675..80f68e10 100644 --- a/apps/yaak-client/commands/createEnvironment.tsx +++ b/apps/yaak-client/commands/createEnvironment.tsx @@ -1,33 +1,33 @@ -import type { Environment } from '@yaakapp-internal/models'; -import { CreateEnvironmentDialog } from '../components/CreateEnvironmentDialog'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; -import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; +import type { Environment } from "@yaakapp-internal/models"; +import { CreateEnvironmentDialog } from "../components/CreateEnvironmentDialog"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; +import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams"; export const createSubEnvironmentAndActivate = createFastMutation< string | null, unknown, Environment | null >({ - mutationKey: ['create_environment'], + mutationKey: ["create_environment"], mutationFn: async (baseEnvironment) => { if (baseEnvironment == null) { - throw new Error('No base environment passed'); + throw new Error("No base environment passed"); } const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) { - throw new Error('Cannot create environment when no active workspace'); + throw new Error("Cannot create environment when no active workspace"); } return new Promise((resolve) => { showDialog({ - id: 'new-environment', - title: 'New Environment', - description: 'Create multiple environments with different sets of variables', - size: 'sm', + id: "new-environment", + title: "New Environment", + description: "Create multiple environments with different sets of variables", + size: "sm", onClose: () => resolve(null), render: ({ hide }) => ( cmdDeleteWebsocketConnections(request.id), }); diff --git a/apps/yaak-client/commands/moveToWorkspace.tsx b/apps/yaak-client/commands/moveToWorkspace.tsx index 63672b4f..3428a580 100644 --- a/apps/yaak-client/commands/moveToWorkspace.tsx +++ b/apps/yaak-client/commands/moveToWorkspace.tsx @@ -1,28 +1,26 @@ -import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; +import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; -import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { pluralizeCount } from '../lib/pluralize'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; +import { MoveToWorkspaceDialog } from "../components/MoveToWorkspaceDialog"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { pluralizeCount } from "../lib/pluralize"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; export const moveToWorkspace = createFastMutation({ - mutationKey: ['move_workspace'], + mutationKey: ["move_workspace"], mutationFn: async (requests: (HttpRequest | GrpcRequest | WebsocketRequest)[]) => { const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (activeWorkspaceId == null) return; if (requests.length === 0) return; const title = - requests.length === 1 - ? 'Move Request' - : `Move ${pluralizeCount('Request', requests.length)}`; + requests.length === 1 ? "Move Request" : `Move ${pluralizeCount("Request", requests.length)}`; showDialog({ - id: 'change-workspace', + id: "change-workspace", title, - size: 'sm', + size: "sm", render: ({ hide }) => ( , }); diff --git a/apps/yaak-client/commands/openSettings.tsx b/apps/yaak-client/commands/openSettings.tsx index 099e4bf1..0db60659 100644 --- a/apps/yaak-client/commands/openSettings.tsx +++ b/apps/yaak-client/commands/openSettings.tsx @@ -1,29 +1,29 @@ -import type { SettingsTab } from '../components/Settings/Settings'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { jotaiStore } from '../lib/jotai'; -import { router } from '../lib/router'; -import { invokeCmd } from '../lib/tauri'; +import type { SettingsTab } from "../components/Settings/Settings"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { jotaiStore } from "../lib/jotai"; +import { router } from "../lib/router"; +import { invokeCmd } from "../lib/tauri"; // Allow tab with optional subtab (e.g., "plugins:installed") type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}` | null; export const openSettings = createFastMutation({ - mutationKey: ['open_settings'], + mutationKey: ["open_settings"], mutationFn: async (tab) => { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) return; const location = router.buildLocation({ - to: '/workspaces/$workspaceId/settings', + to: "/workspaces/$workspaceId/settings", params: { workspaceId }, search: { tab: (tab ?? undefined) as SettingsTab | undefined }, }); - await invokeCmd('cmd_new_child_window', { + await invokeCmd("cmd_new_child_window", { url: location.href, - label: 'settings', - title: 'Yaak Settings', + label: "settings", + title: "Yaak Settings", innerSize: [750, 600], }); }, diff --git a/apps/yaak-client/commands/openWorkspaceFromSyncDir.tsx b/apps/yaak-client/commands/openWorkspaceFromSyncDir.tsx index 83426ce9..9c5b6d2f 100644 --- a/apps/yaak-client/commands/openWorkspaceFromSyncDir.tsx +++ b/apps/yaak-client/commands/openWorkspaceFromSyncDir.tsx @@ -1,7 +1,7 @@ -import { applySync, calculateSyncFsOnly } from '@yaakapp-internal/sync'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { showSimpleAlert } from '../lib/alert'; -import { router } from '../lib/router'; +import { applySync, calculateSyncFsOnly } from "@yaakapp-internal/sync"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { showSimpleAlert } from "../lib/alert"; +import { router } from "../lib/router"; export const openWorkspaceFromSyncDir = createFastMutation({ mutationKey: [], @@ -9,18 +9,18 @@ export const openWorkspaceFromSyncDir = createFastMutation({ const ops = await calculateSyncFsOnly(dir); const workspace = ops - .map((o) => (o.type === 'dbCreate' && o.fs.model.type === 'workspace' ? o.fs.model : null)) + .map((o) => (o.type === "dbCreate" && o.fs.model.type === "workspace" ? o.fs.model : null)) .filter((m) => m)[0]; if (workspace == null) { - showSimpleAlert('Failed to Open', 'No workspace found in directory'); + showSimpleAlert("Failed to Open", "No workspace found in directory"); return; } await applySync(workspace.id, dir, ops); - router.navigate({ - to: '/workspaces/$workspaceId', + await router.navigate({ + to: "/workspaces/$workspaceId", params: { workspaceId: workspace.id }, }); }, diff --git a/apps/yaak-client/commands/openWorkspaceSettings.tsx b/apps/yaak-client/commands/openWorkspaceSettings.tsx index 2ad53fbe..4f68f87c 100644 --- a/apps/yaak-client/commands/openWorkspaceSettings.tsx +++ b/apps/yaak-client/commands/openWorkspaceSettings.tsx @@ -1,16 +1,16 @@ -import type { WorkspaceSettingsTab } from '../components/WorkspaceSettingsDialog'; -import { WorkspaceSettingsDialog } from '../components/WorkspaceSettingsDialog'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; +import type { WorkspaceSettingsTab } from "../components/WorkspaceSettingsDialog"; +import { WorkspaceSettingsDialog } from "../components/WorkspaceSettingsDialog"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; export function openWorkspaceSettings(tab?: WorkspaceSettingsTab) { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) return; showDialog({ - id: 'workspace-settings', - size: 'md', - className: 'h-[calc(100vh-5rem)] !max-h-[40rem]', + id: "workspace-settings", + size: "md", + className: "h-[calc(100vh-5rem)] !max-h-[40rem]", noPadding: true, render: ({ hide }) => ( diff --git a/apps/yaak-client/commands/switchWorkspace.tsx b/apps/yaak-client/commands/switchWorkspace.tsx index 461b4922..56356148 100644 --- a/apps/yaak-client/commands/switchWorkspace.tsx +++ b/apps/yaak-client/commands/switchWorkspace.tsx @@ -1,9 +1,9 @@ -import { createFastMutation } from '../hooks/useFastMutation'; -import { getRecentCookieJars } from '../hooks/useRecentCookieJars'; -import { getRecentEnvironments } from '../hooks/useRecentEnvironments'; -import { getRecentRequests } from '../hooks/useRecentRequests'; -import { router } from '../lib/router'; -import { invokeCmd } from '../lib/tauri'; +import { createFastMutation } from "../hooks/useFastMutation"; +import { getRecentCookieJars } from "../hooks/useRecentCookieJars"; +import { getRecentEnvironments } from "../hooks/useRecentEnvironments"; +import { getRecentRequests } from "../hooks/useRecentRequests"; +import { router } from "../lib/router"; +import { invokeCmd } from "../lib/tauri"; export const switchWorkspace = createFastMutation< void, @@ -13,7 +13,7 @@ export const switchWorkspace = createFastMutation< inNewWindow: boolean; } >({ - mutationKey: ['open_workspace'], + mutationKey: ["open_workspace"], mutationFn: async ({ workspaceId, inNewWindow }) => { const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? undefined; const requestId = (await getRecentRequests(workspaceId))[0] ?? undefined; @@ -26,16 +26,16 @@ export const switchWorkspace = createFastMutation< if (inNewWindow) { const location = router.buildLocation({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId }, search, }); - await invokeCmd('cmd_new_main_window', { url: location.href }); + await invokeCmd("cmd_new_main_window", { url: location.href }); return; } await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId }, search, }); diff --git a/apps/yaak-client/components/BinaryFileEditor.tsx b/apps/yaak-client/components/BinaryFileEditor.tsx index 7069249c..7fc3d458 100644 --- a/apps/yaak-client/components/BinaryFileEditor.tsx +++ b/apps/yaak-client/components/BinaryFileEditor.tsx @@ -1,15 +1,15 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { Banner, HStack, InlineCode, VStack } from '@yaakapp-internal/ui'; -import mime from 'mime'; -import { useKeyValue } from '../hooks/useKeyValue'; -import { Button } from './core/Button'; -import { SelectFile } from './SelectFile'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { Banner, HStack, InlineCode, VStack } from "@yaakapp-internal/ui"; +import mime from "mime"; +import { useKeyValue } from "../hooks/useKeyValue"; +import { Button } from "./core/Button"; +import { SelectFile } from "./SelectFile"; type Props = { requestId: string; contentType: string | null; - body: HttpRequest['body']; - onChange: (body: HttpRequest['body']) => void; + body: HttpRequest["body"]; + onChange: (body: HttpRequest["body"]) => void; onChangeContentType: (contentType: string | null) => void; }; @@ -21,8 +21,8 @@ export function BinaryFileEditor({ requestId, }: Props) { const ignoreContentType = useKeyValue({ - namespace: 'global', - key: ['ignore_content_type', requestId], + namespace: "global", + key: ["ignore_content_type", requestId], fallback: false, }); @@ -31,8 +31,8 @@ export function BinaryFileEditor({ onChange({ filePath: filePath ?? undefined }); }; - const filePath = typeof body.filePath === 'string' ? body.filePath : null; - const mimeType = mime.getType(filePath ?? '') ?? 'application/octet-stream'; + const filePath = typeof body.filePath === "string" ? body.filePath : null; + const mimeType = mime.getType(filePath ?? "") ?? "application/octet-stream"; return ( diff --git a/apps/yaak-client/components/CargoFeature.tsx b/apps/yaak-client/components/CargoFeature.tsx index 0186c35b..7a5d1fe5 100644 --- a/apps/yaak-client/components/CargoFeature.tsx +++ b/apps/yaak-client/components/CargoFeature.tsx @@ -1,12 +1,12 @@ -import type { ReactNode } from 'react'; -import { appInfo } from '../lib/appInfo'; +import type { ReactNode } from "react"; +import { appInfo } from "../lib/appInfo"; interface Props { children: ReactNode; - feature: 'updater' | 'license'; + feature: "updater" | "license"; } -const featureMap: Record = { +const featureMap: Record = { updater: appInfo.featureUpdater, license: appInfo.featureLicense, }; diff --git a/apps/yaak-client/components/CloneGitRepositoryDialog.tsx b/apps/yaak-client/components/CloneGitRepositoryDialog.tsx index f43e3ff3..3b3e2af2 100644 --- a/apps/yaak-client/components/CloneGitRepositoryDialog.tsx +++ b/apps/yaak-client/components/CloneGitRepositoryDialog.tsx @@ -1,15 +1,15 @@ -import { open } from '@tauri-apps/plugin-dialog'; -import { gitClone } from '@yaakapp-internal/git'; -import { Banner, VStack } from '@yaakapp-internal/ui'; -import { useState } from 'react'; -import { openWorkspaceFromSyncDir } from '../commands/openWorkspaceFromSyncDir'; -import { appInfo } from '../lib/appInfo'; -import { showErrorToast } from '../lib/toast'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { IconButton } from './core/IconButton'; -import { PlainInput } from './core/PlainInput'; -import { promptCredentials } from './git/credentials'; +import { open } from "@tauri-apps/plugin-dialog"; +import { gitClone } from "@yaakapp-internal/git"; +import { Banner, VStack } from "@yaakapp-internal/ui"; +import { useState } from "react"; +import { openWorkspaceFromSyncDir } from "../commands/openWorkspaceFromSyncDir"; +import { appInfo } from "../lib/appInfo"; +import { showErrorToast } from "../lib/toast"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { IconButton } from "./core/IconButton"; +import { PlainInput } from "./core/PlainInput"; +import { promptCredentials } from "./git/credentials"; interface Props { hide: () => void; @@ -17,15 +17,15 @@ interface Props { // Detect path separator from an existing path (defaults to /) function getPathSeparator(path: string): string { - return path.includes('\\') ? '\\' : '/'; + return path.includes("\\") ? "\\" : "/"; } export function CloneGitRepositoryDialog({ hide }: Props) { - const [url, setUrl] = useState(''); + const [url, setUrl] = useState(""); const [baseDirectory, setBaseDirectory] = useState(appInfo.defaultProjectDir); const [directoryOverride, setDirectoryOverride] = useState(null); const [hasSubdirectory, setHasSubdirectory] = useState(false); - const [subdirectory, setSubdirectory] = useState(''); + const [subdirectory, setSubdirectory] = useState(""); const [isCloning, setIsCloning] = useState(false); const [error, setError] = useState(null); @@ -38,7 +38,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) { const handleSelectDirectory = async () => { const dir = await open({ - title: 'Select Directory', + title: "Select Directory", directory: true, multiple: false, }); @@ -58,9 +58,9 @@ export function CloneGitRepositoryDialog({ hide }: Props) { try { const result = await gitClone(url, directory, promptCredentials); - if (result.type === 'needs_credentials') { + if (result.type === "needs_credentials") { setError( - result.error ?? 'Authentication failed. Please check your credentials and try again.', + result.error ?? "Authentication failed. Please check your credentials and try again.", ); return; } @@ -72,8 +72,8 @@ export function CloneGitRepositoryDialog({ hide }: Props) { } catch (err) { setError(String(err)); showErrorToast({ - id: 'git-clone-error', - title: 'Clone Failed', + id: "git-clone-error", + title: "Clone Failed", message: String(err), }); } finally { @@ -136,7 +136,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) { disabled={!url || !directory || isCloning} isLoading={isCloning} > - {isCloning ? 'Cloning...' : 'Clone Repository'} + {isCloning ? "Cloning..." : "Clone Repository"} ); @@ -156,5 +156,5 @@ function extractRepoName(url: string): string { if (sshMatch?.[1]) { return sshMatch[1]; } - return ''; + return ""; } diff --git a/apps/yaak-client/components/ColorIndicator.tsx b/apps/yaak-client/components/ColorIndicator.tsx index 931fabcf..60cdf14a 100644 --- a/apps/yaak-client/components/ColorIndicator.tsx +++ b/apps/yaak-client/components/ColorIndicator.tsx @@ -1,5 +1,5 @@ -import classNames from 'classnames'; -import type { CSSProperties } from 'react'; +import classNames from "classnames"; +import type { CSSProperties } from "react"; interface Props { color: string | null; @@ -11,7 +11,7 @@ export function ColorIndicator({ color, onClick, className }: Props) { const style: CSSProperties = { backgroundColor: color ?? undefined }; const finalClassName = classNames( className, - 'inline-block w-[0.75em] h-[0.75em] rounded-full mr-1.5 border border-transparent flex-shrink-0', + "inline-block w-[0.75em] h-[0.75em] rounded-full mr-1.5 border border-transparent flex-shrink-0", ); if (onClick) { @@ -20,7 +20,7 @@ export function ColorIndicator({ color, onClick, className }: Props) { type="button" onClick={onClick} style={style} - className={classNames(finalClassName, 'hover:border-text')} + className={classNames(finalClassName, "hover:border-text")} /> ); } diff --git a/apps/yaak-client/components/CommandPaletteDialog.tsx b/apps/yaak-client/components/CommandPaletteDialog.tsx index dced21ce..eec1d3b9 100644 --- a/apps/yaak-client/components/CommandPaletteDialog.tsx +++ b/apps/yaak-client/components/CommandPaletteDialog.tsx @@ -1,8 +1,8 @@ -import { workspacesAtom } from '@yaakapp-internal/models'; -import { Heading, Icon, useDebouncedState } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import { fuzzyFilter } from 'fuzzbunny'; -import { useAtomValue } from 'jotai'; +import { workspacesAtom } from "@yaakapp-internal/models"; +import { Heading, Icon, useDebouncedState } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import { fuzzyFilter } from "fuzzbunny"; +import { useAtomValue } from "jotai"; import { Fragment, type KeyboardEvent, @@ -11,45 +11,45 @@ import { useMemo, useRef, useState, -} from 'react'; -import { createFolder } from '../commands/commands'; -import { createSubEnvironmentAndActivate } from '../commands/createEnvironment'; -import { openSettings } from '../commands/openSettings'; -import { switchWorkspace } from '../commands/switchWorkspace'; -import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; -import { useActiveEnvironment } from '../hooks/useActiveEnvironment'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { useAllRequests } from '../hooks/useAllRequests'; -import { useCreateWorkspace } from '../hooks/useCreateWorkspace'; -import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown'; -import { useGrpcRequestActions } from '../hooks/useGrpcRequestActions'; -import type { HotkeyAction } from '../hooks/useHotKey'; -import { useHttpRequestActions } from '../hooks/useHttpRequestActions'; -import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; -import { useRecentRequests } from '../hooks/useRecentRequests'; -import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces'; -import { useScrollIntoView } from '../hooks/useScrollIntoView'; -import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; -import { useSidebarHidden } from '../hooks/useSidebarHidden'; -import { appInfo } from '../lib/appInfo'; -import { copyToClipboard } from '../lib/copy'; -import { createRequestAndNavigate } from '../lib/createRequestAndNavigate'; -import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm'; -import { showDialog } from '../lib/dialog'; -import { editEnvironment } from '../lib/editEnvironment'; -import { renameModelWithPrompt } from '../lib/renameModelWithPrompt'; +} from "react"; +import { createFolder } from "../commands/commands"; +import { createSubEnvironmentAndActivate } from "../commands/createEnvironment"; +import { openSettings } from "../commands/openSettings"; +import { switchWorkspace } from "../commands/switchWorkspace"; +import { useActiveCookieJar } from "../hooks/useActiveCookieJar"; +import { useActiveEnvironment } from "../hooks/useActiveEnvironment"; +import { useActiveRequest } from "../hooks/useActiveRequest"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { useAllRequests } from "../hooks/useAllRequests"; +import { useCreateWorkspace } from "../hooks/useCreateWorkspace"; +import { useEnvironmentsBreakdown } from "../hooks/useEnvironmentsBreakdown"; +import { useGrpcRequestActions } from "../hooks/useGrpcRequestActions"; +import type { HotkeyAction } from "../hooks/useHotKey"; +import { useHttpRequestActions } from "../hooks/useHttpRequestActions"; +import { useRecentEnvironments } from "../hooks/useRecentEnvironments"; +import { useRecentRequests } from "../hooks/useRecentRequests"; +import { useRecentWorkspaces } from "../hooks/useRecentWorkspaces"; +import { useScrollIntoView } from "../hooks/useScrollIntoView"; +import { useSendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest"; +import { useSidebarHidden } from "../hooks/useSidebarHidden"; +import { appInfo } from "../lib/appInfo"; +import { copyToClipboard } from "../lib/copy"; +import { createRequestAndNavigate } from "../lib/createRequestAndNavigate"; +import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm"; +import { showDialog } from "../lib/dialog"; +import { editEnvironment } from "../lib/editEnvironment"; +import { renameModelWithPrompt } from "../lib/renameModelWithPrompt"; import { resolvedModelNameWithFolders, resolvedModelNameWithFoldersArray, -} from '../lib/resolvedModelName'; -import { router } from '../lib/router'; -import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; -import { CookieDialog } from './CookieDialog'; -import { Button } from './core/Button'; -import { Hotkey } from './core/Hotkey'; -import { HttpMethodTag } from './core/HttpMethodTag'; -import { PlainInput } from './core/PlainInput'; +} from "../lib/resolvedModelName"; +import { router } from "../lib/router"; +import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams"; +import { CookieDialog } from "./CookieDialog"; +import { Button } from "./core/Button"; +import { Hotkey } from "./core/Hotkey"; +import { HttpMethodTag } from "./core/HttpMethodTag"; +import { PlainInput } from "./core/PlainInput"; interface CommandPaletteGroup { key: string; @@ -66,7 +66,7 @@ type CommandPaletteItem = { const MAX_PER_GROUP = 8; export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { - const [command, setCommand] = useDebouncedState('', 150); + const [command, setCommand] = useDebouncedState("", 150); const [selectedItemKey, setSelectedItemKey] = useState(null); const activeEnvironment = useActiveEnvironment(); const httpRequestActions = useHttpRequestActions(); @@ -94,79 +94,79 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const commands: CommandPaletteItem[] = [ { - key: 'settings.open', - label: 'Open Settings', - action: 'settings.show', + key: "settings.open", + label: "Open Settings", + action: "settings.show", onSelect: () => openSettings.mutate(null), }, { - key: 'app.create', - label: 'Create Workspace', + key: "app.create", + label: "Create Workspace", onSelect: createWorkspace, }, { - key: 'model.create', - label: 'Create HTTP Request', - onSelect: () => createRequestAndNavigate({ model: 'http_request', workspaceId }), + key: "model.create", + label: "Create HTTP Request", + onSelect: () => createRequestAndNavigate({ model: "http_request", workspaceId }), }, { - key: 'grpc_request.create', - label: 'Create GRPC Request', - onSelect: () => createRequestAndNavigate({ model: 'grpc_request', workspaceId }), + key: "grpc_request.create", + label: "Create GRPC Request", + onSelect: () => createRequestAndNavigate({ model: "grpc_request", workspaceId }), }, { - key: 'websocket_request.create', - label: 'Create Websocket Request', - onSelect: () => createRequestAndNavigate({ model: 'websocket_request', workspaceId }), + key: "websocket_request.create", + label: "Create Websocket Request", + onSelect: () => createRequestAndNavigate({ model: "websocket_request", workspaceId }), }, { - key: 'folder.create', - label: 'Create Folder', + key: "folder.create", + label: "Create Folder", onSelect: () => createFolder.mutate({}), }, { - key: 'cookies.show', - label: 'Show Cookies', + key: "cookies.show", + label: "Show Cookies", onSelect: async () => { showDialog({ - id: 'cookies', - title: 'Manage Cookies', - size: 'full', + id: "cookies", + title: "Manage Cookies", + size: "full", render: () => , }); }, }, { - key: 'environment.edit', - label: 'Edit Environment', - action: 'environment_editor.toggle', + key: "environment.edit", + label: "Edit Environment", + action: "environment_editor.toggle", onSelect: () => editEnvironment(activeEnvironment), }, { - key: 'environment.create', - label: 'Create Environment', + key: "environment.create", + label: "Create Environment", onSelect: () => createSubEnvironmentAndActivate.mutate(baseEnvironment), }, { - key: 'sidebar.toggle', - label: 'Toggle Sidebar', - action: 'sidebar.focus', + key: "sidebar.toggle", + label: "Toggle Sidebar", + action: "sidebar.focus", onSelect: () => setSidebarHidden((h) => !h), }, ]; - if (activeRequest?.model === 'http_request') { + if (activeRequest?.model === "http_request") { commands.push({ - key: 'request.send', - action: 'request.send', - label: 'Send Request', + key: "request.send", + action: "request.send", + label: "Send Request", onSelect: () => sendRequest(activeRequest.id), }); if (appInfo.cliVersion != null) { commands.push({ - key: 'request.copy_cli_send', + key: "request.copy_cli_send", searchText: `copy cli send yaak request send ${activeRequest.id}`, - label: 'Copy CLI Send Command', + label: "Copy CLI Send Command", onSelect: () => copyToClipboard(`yaak request send ${activeRequest.id}`), }); } @@ -179,7 +179,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { }); } - if (activeRequest?.model === 'grpc_request') { + if (activeRequest?.model === "grpc_request") { grpcRequestActions.forEach((a, i) => { commands.push({ key: `grpc_request_action.${i}`, @@ -191,21 +191,21 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { if (activeRequest != null) { commands.push({ - key: 'http_request.rename', - label: 'Rename Request', + key: "http_request.rename", + label: "Rename Request", onSelect: () => renameModelWithPrompt(activeRequest), }); commands.push({ - key: 'sidebar.selected.delete', - label: 'Delete Request', + key: "sidebar.selected.delete", + label: "Delete Request", onSelect: () => deleteModelWithConfirm(activeRequest), }); } return commands.sort((a, b) => - ('searchText' in a ? a.searchText : a.label).localeCompare( - 'searchText' in b ? b.searchText : b.label, + ("searchText" in a ? a.searchText : a.label).localeCompare( + "searchText" in b ? b.searchText : b.label, ), ); }, [ @@ -282,14 +282,14 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const groups = useMemo(() => { const actionsGroup: CommandPaletteGroup = { - key: 'actions', - label: 'Actions', + key: "actions", + label: "Actions", items: workspaceCommands, }; const requestGroup: CommandPaletteGroup = { - key: 'requests', - label: 'Switch Request', + key: "requests", + label: "Switch Request", items: [], }; @@ -303,14 +303,14 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { {resolvedModelNameWithFoldersArray(r).map((name, i, all) => ( {i !== 0 && } -
{name}
+
{name}
))} ), onSelect: async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: r.workspaceId }, search: (prev) => ({ ...prev, request_id: r.id }), }); @@ -319,8 +319,8 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { } const environmentGroup: CommandPaletteGroup = { - key: 'environments', - label: 'Switch Environment', + key: "environments", + label: "Switch Environment", items: [], }; @@ -336,8 +336,8 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { } const workspaceGroup: CommandPaletteGroup = { - key: 'workspaces', - label: 'Switch Workspace', + key: "workspaces", + label: "Switch Workspace", items: [], }; @@ -365,10 +365,10 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { ? fuzzyFilter( allItems.map((i) => ({ ...i, - filterBy: 'searchText' in i ? i.searchText : i.label, + filterBy: "searchText" in i ? i.searchText : i.label, })), command, - { fields: ['filterBy'] }, + { fields: ["filterBy"] }, ) .sort((a, b) => b.score - a.score) .map((v) => v.item) @@ -406,13 +406,13 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const handleKeyDown = useCallback( (e: KeyboardEvent) => { const index = filteredAllItems.findIndex((v) => v.key === selectedItem?.key); - if (e.key === 'ArrowDown' || (e.ctrlKey && e.key === 'n')) { + if (e.key === "ArrowDown" || (e.ctrlKey && e.key === "n")) { const next = filteredAllItems[index + 1] ?? filteredAllItems[0]; setSelectedItemKey(next?.key ?? null); - } else if (e.key === 'ArrowUp' || (e.ctrlKey && e.key === 'k')) { + } else if (e.key === "ArrowUp" || (e.ctrlKey && e.key === "k")) { const prev = filteredAllItems[index - 1] ?? filteredAllItems[filteredAllItems.length - 1]; setSelectedItemKey(prev?.key ?? null); - } else if (e.key === 'Enter') { + } else if (e.key === "Enter") { const selected = filteredAllItems[index]; setSelectedItemKey(selected?.key ?? null); if (selected) { @@ -489,10 +489,10 @@ function CommandPaletteItem({ color="custom" justify="start" className={classNames( - 'w-full h-sm flex items-center rounded px-1.5', - 'hover:text-text', - active && 'bg-surface-highlight', - !active && 'text-text-subtle', + "w-full h-sm flex items-center rounded px-1.5", + "hover:text-text", + active && "bg-surface-highlight", + !active && "text-text-subtle", )} > {children} diff --git a/apps/yaak-client/components/ConfirmLargeRequestBody.tsx b/apps/yaak-client/components/ConfirmLargeRequestBody.tsx index 443b35f8..f8acf535 100644 --- a/apps/yaak-client/components/ConfirmLargeRequestBody.tsx +++ b/apps/yaak-client/components/ConfirmLargeRequestBody.tsx @@ -1,12 +1,12 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import { Banner, HStack, InlineCode } from '@yaakapp-internal/ui'; -import type { ReactNode } from 'react'; -import { useToggle } from '../hooks/useToggle'; -import { showConfirm } from '../lib/confirm'; -import { Button } from './core/Button'; -import { Link } from './core/Link'; -import { SizeTag } from './core/SizeTag'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import { Banner, HStack, InlineCode } from "@yaakapp-internal/ui"; +import type { ReactNode } from "react"; +import { useToggle } from "../hooks/useToggle"; +import { showConfirm } from "../lib/confirm"; +import { Button } from "./core/Button"; +import { Link } from "./core/Link"; +import { SizeTag } from "./core/SizeTag"; interface Props { children: ReactNode; @@ -29,17 +29,17 @@ export function ConfirmLargeRequestBody({ children, request }: Props) { return (

- Rendering content over{' '} + Rendering content over{" "} - {' '} + {" "} may impact performance.

- See{' '} + See{" "} Working With Large Values - {' '} + {" "} for tips.

@@ -53,13 +53,13 @@ export function ConfirmLargeRequestBody({ children, request }: Props) { onClick={async () => { const confirm = await showConfirm({ id: `delete-body-${request.id}`, - confirmText: 'Delete Body', - title: 'Delete Body Text', - description: 'Are you sure you want to delete the request body text?', - color: 'danger', + confirmText: "Delete Body", + title: "Delete Body Text", + description: "Are you sure you want to delete the request body text?", + color: "danger", }); if (confirm) { - await patchModel(request, { body: { ...request.body, text: '' } }); + await patchModel(request, { body: { ...request.body, text: "" } }); } }} > diff --git a/apps/yaak-client/components/ConfirmLargeResponse.tsx b/apps/yaak-client/components/ConfirmLargeResponse.tsx index 59e1d8a5..90bb2c85 100644 --- a/apps/yaak-client/components/ConfirmLargeResponse.tsx +++ b/apps/yaak-client/components/ConfirmLargeResponse.tsx @@ -1,14 +1,14 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { Banner, HStack, InlineCode } from '@yaakapp-internal/ui'; -import { type ReactNode, useMemo } from 'react'; -import { useSaveResponse } from '../hooks/useSaveResponse'; -import { useToggle } from '../hooks/useToggle'; -import { isProbablyTextContentType } from '../lib/contentType'; -import { getContentTypeFromHeaders } from '../lib/model_util'; -import { getResponseBodyText } from '../lib/responseBody'; -import { CopyButton } from './CopyButton'; -import { Button } from './core/Button'; -import { SizeTag } from './core/SizeTag'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { Banner, HStack, InlineCode } from "@yaakapp-internal/ui"; +import { type ReactNode, useMemo } from "react"; +import { useSaveResponse } from "../hooks/useSaveResponse"; +import { useToggle } from "../hooks/useToggle"; +import { isProbablyTextContentType } from "../lib/contentType"; +import { getContentTypeFromHeaders } from "../lib/model_util"; +import { getResponseBodyText } from "../lib/responseBody"; +import { CopyButton } from "./CopyButton"; +import { Button } from "./core/Button"; +import { SizeTag } from "./core/SizeTag"; interface Props { children: ReactNode; @@ -31,10 +31,10 @@ export function ConfirmLargeResponse({ children, response }: Props) { return (

- Showing responses over{' '} + Showing responses over{" "} - {' '} + {" "} may impact performance

diff --git a/apps/yaak-client/components/ConfirmLargeResponseRequest.tsx b/apps/yaak-client/components/ConfirmLargeResponseRequest.tsx index eed0208f..92525f70 100644 --- a/apps/yaak-client/components/ConfirmLargeResponseRequest.tsx +++ b/apps/yaak-client/components/ConfirmLargeResponseRequest.tsx @@ -1,13 +1,13 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { Banner, HStack, InlineCode } from '@yaakapp-internal/ui'; -import { type ReactNode, useMemo } from 'react'; -import { getRequestBodyText as getHttpResponseRequestBodyText } from '../hooks/useHttpRequestBody'; -import { useToggle } from '../hooks/useToggle'; -import { isProbablyTextContentType } from '../lib/contentType'; -import { getContentTypeFromHeaders } from '../lib/model_util'; -import { CopyButton } from './CopyButton'; -import { Button } from './core/Button'; -import { SizeTag } from './core/SizeTag'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { Banner, HStack, InlineCode } from "@yaakapp-internal/ui"; +import { type ReactNode, useMemo } from "react"; +import { getRequestBodyText as getHttpResponseRequestBodyText } from "../hooks/useHttpRequestBody"; +import { useToggle } from "../hooks/useToggle"; +import { isProbablyTextContentType } from "../lib/contentType"; +import { getContentTypeFromHeaders } from "../lib/model_util"; +import { CopyButton } from "./CopyButton"; +import { Button } from "./core/Button"; +import { SizeTag } from "./core/SizeTag"; interface Props { children: ReactNode; @@ -29,10 +29,10 @@ export function ConfirmLargeResponseRequest({ children, response }: Props) { return (

- Showing content over{' '} + Showing content over{" "} - {' '} + {" "} may impact performance

@@ -44,7 +44,7 @@ export function ConfirmLargeResponseRequest({ children, response }: Props) { color="secondary" variant="border" size="xs" - text={() => getHttpResponseRequestBodyText(response).then((d) => d?.bodyText ?? '')} + text={() => getHttpResponseRequestBodyText(response).then((d) => d?.bodyText ?? "")} /> )} diff --git a/apps/yaak-client/components/CookieDialog.tsx b/apps/yaak-client/components/CookieDialog.tsx index bd50957c..6c8062c5 100644 --- a/apps/yaak-client/components/CookieDialog.tsx +++ b/apps/yaak-client/components/CookieDialog.tsx @@ -1,9 +1,9 @@ -import type { Cookie } from '@yaakapp-internal/models'; -import { cookieJarsAtom, patchModel } from '@yaakapp-internal/models'; -import { useAtomValue } from 'jotai'; -import { cookieDomain } from '../lib/model_util'; -import { Banner, InlineCode } from '@yaakapp-internal/ui'; -import { IconButton } from './core/IconButton'; +import type { Cookie } from "@yaakapp-internal/models"; +import { cookieJarsAtom, patchModel } from "@yaakapp-internal/models"; +import { useAtomValue } from "jotai"; +import { cookieDomain } from "../lib/model_util"; +import { Banner, InlineCode } from "@yaakapp-internal/ui"; +import { IconButton } from "./core/IconButton"; interface Props { cookieJarId: string | null; diff --git a/apps/yaak-client/components/CookieDropdown.tsx b/apps/yaak-client/components/CookieDropdown.tsx index b33cd54b..7c64d6c8 100644 --- a/apps/yaak-client/components/CookieDropdown.tsx +++ b/apps/yaak-client/components/CookieDropdown.tsx @@ -1,16 +1,16 @@ -import { cookieJarsAtom, patchModel } from '@yaakapp-internal/models'; -import { useAtomValue } from 'jotai'; -import { memo, useMemo } from 'react'; -import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; -import { useCreateCookieJar } from '../hooks/useCreateCookieJar'; -import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm'; -import { showDialog } from '../lib/dialog'; -import { showPrompt } from '../lib/prompt'; -import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; -import { CookieDialog } from './CookieDialog'; -import { Dropdown, type DropdownItem } from './core/Dropdown'; -import { Icon, InlineCode } from '@yaakapp-internal/ui'; -import { IconButton } from './core/IconButton'; +import { cookieJarsAtom, patchModel } from "@yaakapp-internal/models"; +import { useAtomValue } from "jotai"; +import { memo, useMemo } from "react"; +import { useActiveCookieJar } from "../hooks/useActiveCookieJar"; +import { useCreateCookieJar } from "../hooks/useCreateCookieJar"; +import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm"; +import { showDialog } from "../lib/dialog"; +import { showPrompt } from "../lib/prompt"; +import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams"; +import { CookieDialog } from "./CookieDialog"; +import { Dropdown, type DropdownItem } from "./core/Dropdown"; +import { Icon, InlineCode } from "@yaakapp-internal/ui"; +import { IconButton } from "./core/IconButton"; export const CookieDropdown = memo(function CookieDropdown() { const activeCookieJar = useActiveCookieJar(); @@ -22,44 +22,44 @@ export const CookieDropdown = memo(function CookieDropdown() { ...(cookieJars ?? []).map((j) => ({ key: j.id, label: j.name, - leftSlot: , + leftSlot: , onSelect: () => { setWorkspaceSearchParams({ cookie_jar_id: j.id }); }, })), ...(((cookieJars ?? []).length > 0 && activeCookieJar != null ? [ - { type: 'separator', label: activeCookieJar.name }, + { type: "separator", label: activeCookieJar.name }, { - key: 'manage', - label: 'Manage Cookies', + key: "manage", + label: "Manage Cookies", leftSlot: , onSelect: () => { if (activeCookieJar == null) return; showDialog({ - id: 'cookies', - title: 'Manage Cookies', - size: 'full', + id: "cookies", + title: "Manage Cookies", + size: "full", render: () => , }); }, }, { - key: 'rename', - label: 'Rename', + key: "rename", + label: "Rename", leftSlot: , onSelect: async () => { const name = await showPrompt({ - id: 'rename-cookie-jar', - title: 'Rename Cookie Jar', + id: "rename-cookie-jar", + title: "Rename Cookie Jar", description: ( <> Enter a new name for {activeCookieJar?.name} ), - label: 'Name', - confirmText: 'Save', - placeholder: 'New name', + label: "Name", + confirmText: "Save", + placeholder: "New name", defaultValue: activeCookieJar?.name, }); if (name == null) return; @@ -69,9 +69,9 @@ export const CookieDropdown = memo(function CookieDropdown() { ...(((cookieJars ?? []).length > 1 // Never delete the last one ? [ { - label: 'Delete', + label: "Delete", leftSlot: , - color: 'danger', + color: "danger", onSelect: async () => { await deleteModelWithConfirm(activeCookieJar); }, @@ -80,10 +80,10 @@ export const CookieDropdown = memo(function CookieDropdown() { : []) as DropdownItem[]), ] : []) as DropdownItem[]), - { type: 'separator' }, + { type: "separator" }, { - key: 'create-cookie-jar', - label: 'New Cookie Jar', + key: "create-cookie-jar", + label: "New Cookie Jar", leftSlot: , onSelect: () => createCookieJar.mutate(), }, diff --git a/apps/yaak-client/components/CopyButton.tsx b/apps/yaak-client/components/CopyButton.tsx index 4a068b9d..2a0da485 100644 --- a/apps/yaak-client/components/CopyButton.tsx +++ b/apps/yaak-client/components/CopyButton.tsx @@ -1,10 +1,10 @@ -import { useTimedBoolean } from '@yaakapp-internal/ui'; -import { copyToClipboard } from '../lib/copy'; -import { showToast } from '../lib/toast'; -import type { ButtonProps } from './core/Button'; -import { Button } from './core/Button'; +import { useTimedBoolean } from "@yaakapp-internal/ui"; +import { copyToClipboard } from "../lib/copy"; +import { showToast } from "../lib/toast"; +import type { ButtonProps } from "./core/Button"; +import { Button } from "./core/Button"; -interface Props extends Omit { +interface Props extends Omit { text: string | (() => Promise); } @@ -14,12 +14,12 @@ export function CopyButton({ text, ...props }: Props) { ); } diff --git a/apps/yaak-client/components/CopyIconButton.tsx b/apps/yaak-client/components/CopyIconButton.tsx index 84e318c0..a97a032c 100644 --- a/apps/yaak-client/components/CopyIconButton.tsx +++ b/apps/yaak-client/components/CopyIconButton.tsx @@ -1,8 +1,8 @@ -import { IconButton, type IconButtonProps, useTimedBoolean } from '@yaakapp-internal/ui'; -import { copyToClipboard } from '../lib/copy'; -import { showToast } from '../lib/toast'; +import { IconButton, type IconButtonProps, useTimedBoolean } from "@yaakapp-internal/ui"; +import { copyToClipboard } from "../lib/copy"; +import { showToast } from "../lib/toast"; -interface Props extends Omit { +interface Props extends Omit { text: string | (() => Promise); } @@ -11,15 +11,15 @@ export function CopyIconButton({ text, ...props }: Props) { return ( { - const content = typeof text === 'function' ? await text() : text; + const content = typeof text === "function" ? await text() : text; if (content == null) { showToast({ - id: 'failed-to-copy', - color: 'danger', - message: 'Failed to copy', + id: "failed-to-copy", + color: "danger", + message: "Failed to copy", }); } else { copyToClipboard(content, { disableToast: true }); diff --git a/apps/yaak-client/components/CreateDropdown.tsx b/apps/yaak-client/components/CreateDropdown.tsx index 7241381d..9fa82376 100644 --- a/apps/yaak-client/components/CreateDropdown.tsx +++ b/apps/yaak-client/components/CreateDropdown.tsx @@ -1,8 +1,8 @@ -import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems'; -import type { DropdownProps } from './core/Dropdown'; -import { Dropdown } from './core/Dropdown'; +import { useCreateDropdownItems } from "../hooks/useCreateDropdownItems"; +import type { DropdownProps } from "./core/Dropdown"; +import { Dropdown } from "./core/Dropdown"; -interface Props extends Omit { +interface Props extends Omit { hideFolder?: boolean; } @@ -10,7 +10,7 @@ export function CreateDropdown({ hideFolder, children, ...props }: Props) { const getItems = useCreateDropdownItems({ hideFolder, hideIcons: true, - folderId: 'active-folder', + folderId: "active-folder", }); return ( diff --git a/apps/yaak-client/components/CreateEnvironmentDialog.tsx b/apps/yaak-client/components/CreateEnvironmentDialog.tsx index 7a0534b3..1436617a 100644 --- a/apps/yaak-client/components/CreateEnvironmentDialog.tsx +++ b/apps/yaak-client/components/CreateEnvironmentDialog.tsx @@ -1,12 +1,12 @@ -import { createWorkspaceModel } from '@yaakapp-internal/models'; -import { useState } from 'react'; -import { useToggle } from '../hooks/useToggle'; -import { ColorIndicator } from './ColorIndicator'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { ColorPickerWithThemeColors } from './core/ColorPicker'; -import { Label } from './core/Label'; -import { PlainInput } from './core/PlainInput'; +import { createWorkspaceModel } from "@yaakapp-internal/models"; +import { useState } from "react"; +import { useToggle } from "../hooks/useToggle"; +import { ColorIndicator } from "./ColorIndicator"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { ColorPickerWithThemeColors } from "./core/ColorPicker"; +import { Label } from "./core/Label"; +import { PlainInput } from "./core/PlainInput"; interface Props { onCreate: (id: string) => void; @@ -15,7 +15,7 @@ interface Props { } export function CreateEnvironmentDialog({ workspaceId, hide, onCreate }: Props) { - const [name, setName] = useState(''); + const [name, setName] = useState(""); const [color, setColor] = useState(null); const [sharable, toggleSharable] = useToggle(false); return ( @@ -24,13 +24,13 @@ export function CreateEnvironmentDialog({ workspaceId, hide, onCreate }: Props) onSubmit={async (e) => { e.preventDefault(); const id = await createWorkspaceModel({ - model: 'environment', + model: "environment", name, color, variables: [], public: sharable, workspaceId, - parentModel: 'environment', + parentModel: "environment", }); hide(); onCreate(id); diff --git a/apps/yaak-client/components/CreateWorkspaceDialog.tsx b/apps/yaak-client/components/CreateWorkspaceDialog.tsx index f91c007a..339206d5 100644 --- a/apps/yaak-client/components/CreateWorkspaceDialog.tsx +++ b/apps/yaak-client/components/CreateWorkspaceDialog.tsx @@ -1,26 +1,26 @@ -import { gitMutations } from '@yaakapp-internal/git'; -import type { WorkspaceMeta } from '@yaakapp-internal/models'; -import { createGlobalModel, updateModel } from '@yaakapp-internal/models'; -import { VStack } from '@yaakapp-internal/ui'; -import { useState } from 'react'; -import { router } from '../lib/router'; -import { setupOrConfigureEncryption } from '../lib/setupOrConfigureEncryption'; -import { invokeCmd } from '../lib/tauri'; -import { showErrorToast } from '../lib/toast'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { Label } from './core/Label'; -import { PlainInput } from './core/PlainInput'; -import { EncryptionHelp } from './EncryptionHelp'; -import { gitCallbacks } from './git/callbacks'; -import { SyncToFilesystemSetting } from './SyncToFilesystemSetting'; +import { gitMutations } from "@yaakapp-internal/git"; +import type { WorkspaceMeta } from "@yaakapp-internal/models"; +import { createGlobalModel, updateModel } from "@yaakapp-internal/models"; +import { VStack } from "@yaakapp-internal/ui"; +import { useState } from "react"; +import { router } from "../lib/router"; +import { setupOrConfigureEncryption } from "../lib/setupOrConfigureEncryption"; +import { invokeCmd } from "../lib/tauri"; +import { showErrorToast } from "../lib/toast"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { Label } from "./core/Label"; +import { PlainInput } from "./core/PlainInput"; +import { EncryptionHelp } from "./EncryptionHelp"; +import { gitCallbacks } from "./git/callbacks"; +import { SyncToFilesystemSetting } from "./SyncToFilesystemSetting"; interface Props { hide: () => void; } export function CreateWorkspaceDialog({ hide }: Props) { - const [name, setName] = useState(''); + const [name, setName] = useState(""); const [syncConfig, setSyncConfig] = useState<{ filePath: string | null; initGit?: boolean; @@ -34,12 +34,12 @@ export function CreateWorkspaceDialog({ hide }: Props) { className="pb-3" onSubmit={async (e) => { e.preventDefault(); - const workspaceId = await createGlobalModel({ model: 'workspace', name }); + const workspaceId = await createGlobalModel({ model: "workspace", name }); if (workspaceId == null) return; // Do getWorkspaceMeta instead of naively creating one because it might have // been created already when the store refreshes the workspace meta after - const workspaceMeta = await invokeCmd('cmd_get_workspace_meta', { + const workspaceMeta = await invokeCmd("cmd_get_workspace_meta", { workspaceId, }); await updateModel({ @@ -52,8 +52,8 @@ export function CreateWorkspaceDialog({ hide }: Props) { .init.mutateAsync() .catch((err) => { showErrorToast({ - id: 'git-init-error', - title: 'Error initializing Git', + id: "git-init-error", + title: "Error initializing Git", message: String(err), }); }); @@ -61,7 +61,7 @@ export function CreateWorkspaceDialog({ hide }: Props) { // Navigate to workspace await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId }, }); diff --git a/apps/yaak-client/components/Dialogs.tsx b/apps/yaak-client/components/Dialogs.tsx index bf8b72ce..297cf2f2 100644 --- a/apps/yaak-client/components/Dialogs.tsx +++ b/apps/yaak-client/components/Dialogs.tsx @@ -1,14 +1,14 @@ -import { useAtomValue } from 'jotai'; -import type { ComponentType } from 'react'; -import { useCallback } from 'react'; -import { dialogsAtom, hideDialog } from '../lib/dialog'; -import { Dialog, type DialogProps } from './core/Dialog'; -import { ErrorBoundary } from './ErrorBoundary'; +import { useAtomValue } from "jotai"; +import type { ComponentType } from "react"; +import { useCallback } from "react"; +import { dialogsAtom, hideDialog } from "../lib/dialog"; +import { Dialog, type DialogProps } from "./core/Dialog"; +import { ErrorBoundary } from "./ErrorBoundary"; export type DialogInstance = { id: string; render: ComponentType<{ hide: () => void }>; -} & Omit; +} & Omit; export function Dialogs() { const dialogs = useAtomValue(dialogsAtom); diff --git a/apps/yaak-client/components/DnsOverridesEditor.tsx b/apps/yaak-client/components/DnsOverridesEditor.tsx index 9faed861..45a4c246 100644 --- a/apps/yaak-client/components/DnsOverridesEditor.tsx +++ b/apps/yaak-client/components/DnsOverridesEditor.tsx @@ -1,5 +1,5 @@ -import type { DnsOverride, Workspace } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; +import type { DnsOverride, Workspace } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; import { HStack, Table, @@ -9,12 +9,12 @@ import { TableHeaderCell, TableRow, VStack, -} from '@yaakapp-internal/ui'; -import { useCallback, useId, useMemo } from 'react'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { IconButton } from './core/IconButton'; -import { PlainInput } from './core/PlainInput'; +} from "@yaakapp-internal/ui"; +import { useCallback, useId, useMemo } from "react"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { IconButton } from "./core/IconButton"; +import { PlainInput } from "./core/PlainInput"; interface Props { workspace: Workspace; @@ -44,8 +44,8 @@ export function DnsOverridesEditor({ workspace }: Props) { const handleAdd = useCallback(() => { const newOverride: DnsOverride = { - hostname: '', - ipv4: [''], + hostname: "", + ipv4: [""], ipv6: [], enabled: true, }; @@ -73,7 +73,7 @@ export function DnsOverridesEditor({ workspace }: Props) { return (
- Override DNS resolution for specific hostnames. This works like{' '} + Override DNS resolution for specific hostnames. This works like{" "} /etc/hosts but only for requests made from this workspace.
@@ -118,15 +118,15 @@ interface DnsOverrideRowProps { } function DnsOverrideRow({ override, onUpdate, onDelete }: DnsOverrideRowProps) { - const ipv4Value = override.ipv4.join(', '); - const ipv6Value = override.ipv6.join(', '); + const ipv4Value = override.ipv4.join(", "); + const ipv6Value = override.ipv6.join(", "); return ( onUpdate({ enabled })} /> @@ -151,7 +151,7 @@ function DnsOverrideRow({ override, onUpdate, onDelete }: DnsOverrideRowProps) { onChange={(value) => onUpdate({ ipv4: value - .split(',') + .split(",") .map((s) => s.trim()) .filter(Boolean), }) @@ -168,7 +168,7 @@ function DnsOverrideRow({ override, onUpdate, onDelete }: DnsOverrideRowProps) { onChange={(value) => onUpdate({ ipv6: value - .split(',') + .split(",") .map((s) => s.trim()) .filter(Boolean), }) diff --git a/apps/yaak-client/components/DropMarker.tsx b/apps/yaak-client/components/DropMarker.tsx index 18547781..f3a1f8dd 100644 --- a/apps/yaak-client/components/DropMarker.tsx +++ b/apps/yaak-client/components/DropMarker.tsx @@ -1,30 +1,30 @@ -import classNames from 'classnames'; -import type { CSSProperties } from 'react'; -import { memo } from 'react'; +import classNames from "classnames"; +import type { CSSProperties } from "react"; +import { memo } from "react"; interface Props { className?: string; style?: CSSProperties; - orientation?: 'horizontal' | 'vertical'; + orientation?: "horizontal" | "vertical"; } export const DropMarker = memo( - function DropMarker({ className, style, orientation = 'horizontal' }: Props) { + function DropMarker({ className, style, orientation = "horizontal" }: Props) { return (
diff --git a/apps/yaak-client/components/DynamicForm.tsx b/apps/yaak-client/components/DynamicForm.tsx index e3cad5d5..575b07a9 100644 --- a/apps/yaak-client/components/DynamicForm.tsx +++ b/apps/yaak-client/components/DynamicForm.tsx @@ -1,5 +1,5 @@ -import type { Folder, HttpRequest } from '@yaakapp-internal/models'; -import { foldersAtom, httpRequestsAtom } from '@yaakapp-internal/models'; +import type { Folder, HttpRequest } from "@yaakapp-internal/models"; +import { foldersAtom, httpRequestsAtom } from "@yaakapp-internal/models"; import type { FormInput, FormInputCheckbox, @@ -10,32 +10,32 @@ import type { FormInputSelect, FormInputText, JsonPrimitive, -} from '@yaakapp-internal/plugins'; -import { Banner, VStack } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import { useCallback, useEffect, useMemo } from 'react'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { useRandomKey } from '../hooks/useRandomKey'; -import { capitalize } from '../lib/capitalize'; -import { showDialog } from '../lib/dialog'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { Checkbox } from './core/Checkbox'; -import { DetailsBanner } from './core/DetailsBanner'; -import { Editor } from './core/Editor/LazyEditor'; -import { IconButton } from './core/IconButton'; -import type { InputProps } from './core/Input'; -import { Input } from './core/Input'; -import { Label } from './core/Label'; -import type { Pair } from './core/PairEditor'; -import { PairEditor } from './core/PairEditor'; -import { PlainInput } from './core/PlainInput'; -import { Select } from './core/Select'; -import { Markdown } from './Markdown'; -import { SelectFile } from './SelectFile'; +} from "@yaakapp-internal/plugins"; +import { Banner, VStack } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import { useCallback, useEffect, useMemo } from "react"; +import { useActiveRequest } from "../hooks/useActiveRequest"; +import { useRandomKey } from "../hooks/useRandomKey"; +import { capitalize } from "../lib/capitalize"; +import { showDialog } from "../lib/dialog"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { Checkbox } from "./core/Checkbox"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { Editor } from "./core/Editor/LazyEditor"; +import { IconButton } from "./core/IconButton"; +import type { InputProps } from "./core/Input"; +import { Input } from "./core/Input"; +import { Label } from "./core/Label"; +import type { Pair } from "./core/PairEditor"; +import { PairEditor } from "./core/PairEditor"; +import { PlainInput } from "./core/PlainInput"; +import { Select } from "./core/Select"; +import { Markdown } from "./Markdown"; +import { SelectFile } from "./SelectFile"; -export const DYNAMIC_FORM_NULL_ARG = '__NULL__'; -const INPUT_SIZE = 'sm'; +export const DYNAMIC_FORM_NULL_ARG = "__NULL__"; +const INPUT_SIZE = "sm"; interface Props { inputs: FormInput[] | undefined | null; @@ -74,7 +74,7 @@ export function DynamicForm>({ autocompleteFunctions={autocompleteFunctions} autocompleteVariables={autocompleteVariables} data={data} - className={classNames(className, 'pb-4')} // Pad the bottom to look nice + className={classNames(className, "pb-4")} // Pad the bottom to look nice /> ); } @@ -88,8 +88,8 @@ function FormInputsStack>({ space={3} className={classNames( className, - 'h-full overflow-auto', - 'pr-1', // A bit of space between inputs and scrollbar + "h-full overflow-auto", + "pr-1", // A bit of space between inputs and scrollbar )} > @@ -99,7 +99,7 @@ function FormInputsStack>({ type FormInputsProps = Pick< Props, - 'inputs' | 'autocompleteFunctions' | 'autocompleteVariables' | 'stateKey' | 'data' + "inputs" | "autocompleteFunctions" | "autocompleteVariables" | "stateKey" | "data" > & { setDataAttr: (name: string, value: JsonPrimitive) => void; disabled?: boolean; @@ -117,16 +117,16 @@ function FormInputs>({ return ( <> {inputs?.map((input, i) => { - if ('hidden' in input && input.hidden) { + if ("hidden" in input && input.hidden) { return null; } - if ('disabled' in input && disabled != null) { + if ("disabled" in input && disabled != null) { input.disabled = disabled; } switch (input.type) { - case 'select': + case "select": return ( >({ } /> ); - case 'text': + case "text": return ( >({ autocompleteVariables={autocompleteVariables || false} onChange={(v) => setDataAttr(input.name, v)} value={ - data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '') + data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? "") } /> ); - case 'editor': + case "editor": return ( >({ autocompleteVariables={autocompleteVariables || false} onChange={(v) => setDataAttr(input.name, v)} value={ - data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '') + data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? "") } /> ); - case 'checkbox': + case "checkbox": return ( >({ value={data[input.name] != null ? data[input.name] === true : false} /> ); - case 'http_request': + case "http_request": return ( >({ value={data[input.name] != null ? String(data[input.name]) : DYNAMIC_FORM_NULL_ARG} /> ); - case 'file': + case "file": return ( >({ } /> ); - case 'accordion': + case "accordion": if (!hasVisibleInputs(input.inputs)) { return null; } @@ -204,7 +204,7 @@ function FormInputs>({
>({
); - case 'h_stack': + case "h_stack": if (!hasVisibleInputs(input.inputs)) { return null; } @@ -237,7 +237,7 @@ function FormInputs>({ />
); - case 'banner': + case "banner": if (!hasVisibleInputs(input.inputs)) { return null; } @@ -245,7 +245,7 @@ function FormInputs>({ >({ /> ); - case 'markdown': + case "markdown": return {input.content}; - case 'key_value': + case "key_value": return ( >({ stateKey={stateKey} onChange={(v) => setDataAttr(input.name, v)} value={ - data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '[]') + data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? "[]") } /> ); @@ -300,12 +300,12 @@ function TextArg({ onChange, name: arg.name, multiLine: arg.multiLine, - className: arg.multiLine ? 'min-h-[4rem]' : undefined, + className: arg.multiLine ? "min-h-[4rem]" : undefined, defaultValue: value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value, required: !arg.optional, disabled: arg.disabled, help: arg.description, - type: arg.password ? 'password' : 'text', + type: arg.password ? "password" : "text", label: arg.label ?? arg.name, size: INPUT_SIZE, hideLabel: arg.hideLabel ?? arg.label == null, @@ -357,9 +357,9 @@ function EditorArg({
@@ -389,10 +389,10 @@ function EditorArg({ title="Pop out to large editor" onClick={() => { showDialog({ - id: 'id', - size: 'full', - title: arg.readOnly ? 'View Value' : 'Edit Value', - className: '!max-w-[50rem] !max-h-[60rem]', + id: "id", + size: "full", + title: arg.readOnly ? "View Value" : "Edit Value", + className: "!max-w-[50rem] !max-h-[60rem]", description: arg.label && (
))} @@ -155,8 +155,8 @@ function ExportDataDialogContent({ disabled={noneSelected} onClick={() => handleExport()} > - Export{' '} - {pluralizeCount('Workspace', numSelected, { omitSingle: true, noneWord: 'Nothing' })} + Export{" "} + {pluralizeCount("Workspace", numSelected, { omitSingle: true, noneWord: "Nothing" })} diff --git a/apps/yaak-client/components/FolderLayout.tsx b/apps/yaak-client/components/FolderLayout.tsx index 3038523f..34e2f41e 100644 --- a/apps/yaak-client/components/FolderLayout.tsx +++ b/apps/yaak-client/components/FolderLayout.tsx @@ -1,24 +1,24 @@ -import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; -import { foldersAtom } from '@yaakapp-internal/models'; -import { Heading, HStack, Icon, LoadingIcon } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import type { CSSProperties, ReactNode } from 'react'; -import { useCallback, useMemo } from 'react'; -import { allRequestsAtom } from '../hooks/useAllRequests'; -import { useFolderActions } from '../hooks/useFolderActions'; -import { useLatestHttpResponse } from '../hooks/useLatestHttpResponse'; -import { sendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; -import { showDialog } from '../lib/dialog'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { router } from '../lib/router'; -import { Button } from './core/Button'; -import { HttpResponseDurationTag } from './core/HttpResponseDurationTag'; -import { HttpStatusTag } from './core/HttpStatusTag'; -import { IconButton } from './core/IconButton'; -import { Separator } from './core/Separator'; -import { SizeTag } from './core/SizeTag'; -import { HttpResponsePane } from './HttpResponsePane'; +import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; +import { foldersAtom } from "@yaakapp-internal/models"; +import { Heading, HStack, Icon, LoadingIcon } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import type { CSSProperties, ReactNode } from "react"; +import { useCallback, useMemo } from "react"; +import { allRequestsAtom } from "../hooks/useAllRequests"; +import { useFolderActions } from "../hooks/useFolderActions"; +import { useLatestHttpResponse } from "../hooks/useLatestHttpResponse"; +import { sendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest"; +import { showDialog } from "../lib/dialog"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { router } from "../lib/router"; +import { Button } from "./core/Button"; +import { HttpResponseDurationTag } from "./core/HttpResponseDurationTag"; +import { HttpStatusTag } from "./core/HttpStatusTag"; +import { IconButton } from "./core/IconButton"; +import { Separator } from "./core/Separator"; +import { SizeTag } from "./core/SizeTag"; +import { HttpResponsePane } from "./HttpResponsePane"; interface Props { folder: Folder; @@ -30,7 +30,7 @@ export function FolderLayout({ folder, style }: Props) { const requests = useAtomValue(allRequestsAtom); const folderActions = useFolderActions(); const sendAllAction = useMemo( - () => folderActions.find((a) => a.label === 'Send All'), + () => folderActions.find((a) => a.label === "Send All"), [folderActions], ); @@ -75,13 +75,13 @@ export function FolderLayout({ folder, style }: Props) { function ChildCard({ child }: { child: Folder | HttpRequest | GrpcRequest | WebsocketRequest }) { let card: ReactNode; - if (child.model === 'folder') { + if (child.model === "folder") { card = ; - } else if (child.model === 'http_request') { + } else if (child.model === "http_request") { card = ; - } else if (child.model === 'grpc_request') { + } else if (child.model === "grpc_request") { card = ; - } else if (child.model === 'websocket_request') { + } else if (child.model === "websocket_request") { card = ; } else { card =
Unknown model
; @@ -89,7 +89,7 @@ function ChildCard({ child }: { child: Folder | HttpRequest | GrpcRequest | Webs const navigate = useCallback(async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: child.workspaceId }, search: (prev) => ({ ...prev, request_id: child.id }), }); @@ -98,12 +98,12 @@ function ChildCard({ child }: { child: Folder | HttpRequest | GrpcRequest | Webs return (
- {child.model === 'folder' && } + {child.model === "folder" && } {resolvedModelName(child)} @@ -140,7 +140,7 @@ function FolderCard({ folder }: { folder: Folder }) { color="primary" onClick={async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: folder.workspaceId }, search: (prev) => { return { ...prev, request_id: null, folder_id: folder.id }; @@ -174,10 +174,10 @@ function HttpRequestCard({ request }: { request: HttpRequest }) { onClick={(e) => { e.stopPropagation(); showDialog({ - id: 'response-preview', - title: 'Response Preview', - size: 'md', - className: 'h-full', + id: "response-preview", + title: "Response Preview", + size: "md", + className: "h-full", render: () => { return ; }, @@ -188,12 +188,12 @@ function HttpRequestCard({ request }: { request: HttpRequest }) { space={2} alignItems="center" className={classNames( - 'cursor-default select-none', - 'whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm hide-scrollbars', - 'font-mono text-editor border rounded px-1.5 py-0.5 truncate w-full', + "cursor-default select-none", + "whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm hide-scrollbars", + "font-mono text-editor border rounded px-1.5 py-0.5 truncate w-full", )} > - {latestResponse.state !== 'closed' && } + {latestResponse.state !== "closed" && } diff --git a/apps/yaak-client/components/FolderSettingsDialog.tsx b/apps/yaak-client/components/FolderSettingsDialog.tsx index b3786edc..ef306f70 100644 --- a/apps/yaak-client/components/FolderSettingsDialog.tsx +++ b/apps/yaak-client/components/FolderSettingsDialog.tsx @@ -1,36 +1,36 @@ -import { createWorkspaceModel, foldersAtom, patchModel } from '@yaakapp-internal/models'; -import { HStack, Icon, InlineCode, VStack } from '@yaakapp-internal/ui'; -import { useAtomValue } from 'jotai'; -import { Fragment, useMemo } from 'react'; -import { useAuthTab } from '../hooks/useAuthTab'; -import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown'; -import { useHeadersTab } from '../hooks/useHeadersTab'; -import { useInheritedHeaders } from '../hooks/useInheritedHeaders'; -import { useModelAncestors } from '../hooks/useModelAncestors'; -import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm'; -import { hideDialog } from '../lib/dialog'; -import { CopyIconButton } from './CopyIconButton'; -import { Button } from './core/Button'; -import { CountBadge } from './core/CountBadge'; -import { Input } from './core/Input'; -import { Link } from './core/Link'; -import type { TabItem } from './core/Tabs/Tabs'; -import { TabContent, Tabs } from './core/Tabs/Tabs'; -import { EmptyStateText } from './EmptyStateText'; -import { EnvironmentEditor } from './EnvironmentEditor'; -import { HeadersEditor } from './HeadersEditor'; -import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; -import { MarkdownEditor } from './MarkdownEditor'; +import { createWorkspaceModel, foldersAtom, patchModel } from "@yaakapp-internal/models"; +import { HStack, Icon, InlineCode, VStack } from "@yaakapp-internal/ui"; +import { useAtomValue } from "jotai"; +import { Fragment, useMemo } from "react"; +import { useAuthTab } from "../hooks/useAuthTab"; +import { useEnvironmentsBreakdown } from "../hooks/useEnvironmentsBreakdown"; +import { useHeadersTab } from "../hooks/useHeadersTab"; +import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; +import { useModelAncestors } from "../hooks/useModelAncestors"; +import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm"; +import { hideDialog } from "../lib/dialog"; +import { CopyIconButton } from "./CopyIconButton"; +import { Button } from "./core/Button"; +import { CountBadge } from "./core/CountBadge"; +import { Input } from "./core/Input"; +import { Link } from "./core/Link"; +import type { TabItem } from "./core/Tabs/Tabs"; +import { TabContent, Tabs } from "./core/Tabs/Tabs"; +import { EmptyStateText } from "./EmptyStateText"; +import { EnvironmentEditor } from "./EnvironmentEditor"; +import { HeadersEditor } from "./HeadersEditor"; +import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; +import { MarkdownEditor } from "./MarkdownEditor"; interface Props { folderId: string | null; tab?: FolderSettingsTab; } -const TAB_AUTH = 'auth'; -const TAB_HEADERS = 'headers'; -const TAB_VARIABLES = 'variables'; -const TAB_GENERAL = 'general'; +const TAB_AUTH = "auth"; +const TAB_HEADERS = "headers"; +const TAB_VARIABLES = "variables"; +const TAB_GENERAL = "general"; export type FolderSettingsTab = | typeof TAB_AUTH @@ -48,7 +48,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { const inheritedHeaders = useInheritedHeaders(folder); const environments = useEnvironmentsBreakdown(); const folderEnvironment = environments.allEnvironments.find( - (e) => e.parentModel === 'folder' && e.parentId === folderId, + (e) => e.parentModel === "folder" && e.parentId === folderId, ); const numVars = (folderEnvironment?.variables ?? []).filter((v) => v.name).length; @@ -58,13 +58,13 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { return [ { value: TAB_GENERAL, - label: 'General', + label: "General", }, ...headersTab, ...authTab, { value: TAB_VARIABLES, - label: 'Variables', + label: "Variables", rightSlot: numVars > 0 ? : null, }, ]; @@ -128,7 +128,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { onClick={async () => { const didDelete = await deleteModelWithConfirm(folder); if (didDelete) { - hideDialog('folder-settings'); + hideDialog("folder-settings"); } }} color="danger" @@ -164,10 +164,10 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {

- Override{' '} + Override{" "} Variables - {' '} + {" "} for requests within this folder.

, ], diff --git a/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx b/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx index 15225493..872a7b3e 100644 --- a/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx +++ b/apps/yaak-client/components/GrpcProtoSelectionDialog.tsx @@ -1,13 +1,13 @@ -import { open } from '@tauri-apps/plugin-dialog'; -import type { GrpcRequest } from '@yaakapp-internal/models'; -import { Banner, HStack, Icon, InlineCode, VStack } from '@yaakapp-internal/ui'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { useGrpc } from '../hooks/useGrpc'; -import { useGrpcProtoFiles } from '../hooks/useGrpcProtoFiles'; -import { pluralizeCount } from '../lib/pluralize'; -import { Button } from './core/Button'; -import { IconButton } from './core/IconButton'; -import { Link } from './core/Link'; +import { open } from "@tauri-apps/plugin-dialog"; +import type { GrpcRequest } from "@yaakapp-internal/models"; +import { Banner, HStack, Icon, InlineCode, VStack } from "@yaakapp-internal/ui"; +import { useActiveRequest } from "../hooks/useActiveRequest"; +import { useGrpc } from "../hooks/useGrpc"; +import { useGrpcProtoFiles } from "../hooks/useGrpcProtoFiles"; +import { pluralizeCount } from "../lib/pluralize"; +import { Button } from "./core/Button"; +import { IconButton } from "./core/IconButton"; +import { Link } from "./core/Link"; interface Props { onDone: () => void; @@ -15,7 +15,7 @@ interface Props { export function GrpcProtoSelectionDialog(props: Props) { const request = useActiveRequest(); - if (request?.model !== 'grpc_request') return null; + if (request?.model !== "grpc_request") return null; return ; } @@ -46,9 +46,9 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp variant="border" onClick={async () => { const selected = await open({ - title: 'Select Proto Files', + title: "Select Proto Files", multiple: true, - filters: [{ name: 'Proto Files', extensions: ['proto'] }], + filters: [{ name: "Proto Files", extensions: ["proto"] }], }); if (selected == null) return; @@ -64,7 +64,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp color="primary" onClick={async () => { const selected = await open({ - title: 'Select Proto Directory', + title: "Select Proto Directory", directory: true, }); if (selected == null) return; @@ -89,7 +89,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp {reflectError && (

- Reflection failed on URL {request.url || 'n/a'} + Reflection failed on URL {request.url || "n/a"}

{reflectError.trim()}

@@ -97,16 +97,16 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp {!serverReflection && services != null && services.length > 0 && (

- Found services{' '} + Found services{" "} {services?.slice(0, 5).map((s, i) => { return ( - + {s.name} - {i === services.length - 1 ? '' : i === services.length - 2 ? ' and ' : ', '} + {i === services.length - 1 ? "" : i === services.length - 2 ? " and " : ", "} ); })} - {services?.length > 5 && pluralizeCount('other', services?.length - 5)} + {services?.length > 5 && pluralizeCount("other", services?.length - 5)}

)} @@ -116,13 +116,13 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp Server reflection found services {services?.map((s, i) => { return ( - + {s.name} - {i === services.length - 1 ? '' : i === services.length - 2 ? ' and ' : ', '} + {i === services.length - 1 ? "" : i === services.length - 2 ? " and " : ", "} ); })} - . You can override this schema by manually selecting *.proto{' '} + . You can override this schema by manually selecting *.proto{" "} files.

@@ -139,16 +139,16 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
{protoFiles.map((f, i) => { - const parts = f.split('/'); + const parts = f.split("/"); return ( // biome-ignore lint/suspicious/noArrayIndexKey: none
({ ...prev, [w.id]: !prev[w.id] })) } > - {w.name} {w.id === activeWorkspace.id ? '(current workspace)' : ''} + {w.name} {w.id === activeWorkspace.id ? "(current workspace)" : ""}
- + - {parts.length > 3 && '.../'} - {parts.slice(-3).join('/')} + {parts.length > 3 && ".../"} + {parts.slice(-3).join("/")} - {request.url} doesn't implement{' '} + {request.url} doesn't implement{" "} Server Reflection - {' '} + {" "} . Please manually add the .proto file to get started. )} diff --git a/apps/yaak-client/components/GrpcRequestPane.tsx b/apps/yaak-client/components/GrpcRequestPane.tsx index b24c931b..408e5da0 100644 --- a/apps/yaak-client/components/GrpcRequestPane.tsx +++ b/apps/yaak-client/components/GrpcRequestPane.tsx @@ -1,26 +1,26 @@ -import { type GrpcRequest, type HttpRequestHeader, patchModel } from '@yaakapp-internal/models'; -import { HStack, Icon, useContainerSize, VStack } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import type { CSSProperties } from 'react'; -import { useCallback, useMemo, useRef } from 'react'; -import { useAuthTab } from '../hooks/useAuthTab'; -import type { ReflectResponseService } from '../hooks/useGrpc'; -import { useHeadersTab } from '../hooks/useHeadersTab'; -import { useInheritedHeaders } from '../hooks/useInheritedHeaders'; -import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { Button } from './core/Button'; -import { CountBadge } from './core/CountBadge'; -import { IconButton } from './core/IconButton'; -import { PlainInput } from './core/PlainInput'; -import { RadioDropdown } from './core/RadioDropdown'; -import type { TabItem } from './core/Tabs/Tabs'; -import { TabContent, Tabs } from './core/Tabs/Tabs'; -import { GrpcEditor } from './GrpcEditor'; -import { HeadersEditor } from './HeadersEditor'; -import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; -import { MarkdownEditor } from './MarkdownEditor'; -import { UrlBar } from './UrlBar'; +import { type GrpcRequest, type HttpRequestHeader, patchModel } from "@yaakapp-internal/models"; +import { HStack, Icon, useContainerSize, VStack } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import type { CSSProperties } from "react"; +import { useCallback, useMemo, useRef } from "react"; +import { useAuthTab } from "../hooks/useAuthTab"; +import type { ReflectResponseService } from "../hooks/useGrpc"; +import { useHeadersTab } from "../hooks/useHeadersTab"; +import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; +import { useRequestUpdateKey } from "../hooks/useRequestUpdateKey"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { Button } from "./core/Button"; +import { CountBadge } from "./core/CountBadge"; +import { IconButton } from "./core/IconButton"; +import { PlainInput } from "./core/PlainInput"; +import { RadioDropdown } from "./core/RadioDropdown"; +import type { TabItem } from "./core/Tabs/Tabs"; +import { TabContent, Tabs } from "./core/Tabs/Tabs"; +import { GrpcEditor } from "./GrpcEditor"; +import { HeadersEditor } from "./HeadersEditor"; +import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; +import { MarkdownEditor } from "./MarkdownEditor"; +import { UrlBar } from "./UrlBar"; interface Props { style?: CSSProperties; @@ -30,12 +30,12 @@ interface Props { reflectionError?: string; reflectionLoading?: boolean; methodType: - | 'unary' - | 'client_streaming' - | 'server_streaming' - | 'streaming' - | 'no-schema' - | 'no-method'; + | "unary" + | "client_streaming" + | "server_streaming" + | "streaming" + | "no-schema" + | "no-method"; isStreaming: boolean; onCommit: () => void; onCancel: () => void; @@ -44,10 +44,10 @@ interface Props { services: ReflectResponseService[] | null; } -const TAB_MESSAGE = 'message'; -const TAB_METADATA = 'metadata'; -const TAB_AUTH = 'auth'; -const TAB_DESCRIPTION = 'description'; +const TAB_MESSAGE = "message"; +const TAB_METADATA = "metadata"; +const TAB_AUTH = "auth"; +const TAB_DESCRIPTION = "description"; export function GrpcRequestPane({ style, @@ -64,7 +64,7 @@ export function GrpcRequestPane({ onSend, }: Props) { const authTab = useAuthTab(TAB_AUTH, activeRequest); - const metadataTab = useHeadersTab(TAB_METADATA, activeRequest, 'Metadata'); + const metadataTab = useHeadersTab(TAB_METADATA, activeRequest, "Metadata"); const inheritedHeaders = useInheritedHeaders(activeRequest); const forceUpdateKey = useRequestUpdateKey(activeRequest.id ?? null); @@ -85,18 +85,18 @@ export function GrpcRequestPane({ const options = services?.flatMap((s) => s.methods.map((m) => ({ - label: `${s.name.split('.').pop() ?? s.name}/${m.name}`, + label: `${s.name.split(".").pop() ?? s.name}/${m.name}`, value: `${s.name}/${m.name}`, })), ) ?? []; - const value = `${activeRequest?.service ?? ''}/${activeRequest?.method ?? ''}`; + const value = `${activeRequest?.service ?? ""}/${activeRequest?.method ?? ""}`; return { value, options }; }, [activeRequest?.method, activeRequest?.service, services]); const handleChangeService = useCallback( async (v: string) => { - const [serviceName, methodName] = v.split('/', 2); - if (serviceName == null || methodName == null) throw new Error('Should never happen'); + const [serviceName, methodName] = v.split("/", 2); + if (serviceName == null || methodName == null) throw new Error("Should never happen"); await patchModel(activeRequest, { service: serviceName, method: methodName, @@ -110,9 +110,9 @@ export function GrpcRequestPane({ if (activeRequest.service == null || activeRequest.method == null) { alert({ - id: 'grpc-invalid-service-method', - title: 'Error', - body: 'Service or method not selected', + id: "grpc-invalid-service-method", + title: "Error", + body: "Service or method not selected", }); } onGo(); @@ -125,12 +125,12 @@ export function GrpcRequestPane({ const tabs: TabItem[] = useMemo( () => [ - { value: TAB_MESSAGE, label: 'Message' }, + { value: TAB_MESSAGE, label: "Message" }, ...metadataTab, ...authTab, { value: TAB_DESCRIPTION, - label: 'Info', + label: "Info", rightSlot: activeRequest.description && , }, ], @@ -152,14 +152,14 @@ export function GrpcRequestPane({
0 && paneWidth < 400 && '!grid-cols-1', + "grid grid-cols-[minmax(0,1fr)_auto] gap-1.5", + paneWidth === 0 && "opacity-0", + paneWidth > 0 && paneWidth < 400 && "!grid-cols-1", )} > ({ label: o.label, value: o.value, - type: 'default', + type: "default", shortLabel: o.label, }))} itemsAfter={[ { - label: 'Refresh', - type: 'default', + label: "Refresh", + type: "default", leftSlot: , }, ]} @@ -193,14 +193,14 @@ export function GrpcRequestPane({ rightSlot={} disabled={isStreaming || services == null} className={classNames( - 'font-mono text-editor min-w-[5rem] !ring-0', - paneWidth < 400 && 'flex-1', + "font-mono text-editor min-w-[5rem] !ring-0", + paneWidth < 400 && "flex-1", )} > - {select.options.find((o) => o.value === select.value)?.label ?? 'No Schema'} + {select.options.find((o) => o.value === select.value)?.label ?? "No Schema"} - {methodType === 'client_streaming' || methodType === 'streaming' ? ( + {methodType === "client_streaming" || methodType === "streaming" ? ( <> {isStreaming && ( <> @@ -223,26 +223,26 @@ export function GrpcRequestPane({ ) : ( )} diff --git a/apps/yaak-client/components/GrpcResponsePane.tsx b/apps/yaak-client/components/GrpcResponsePane.tsx index cbbf33b3..5a6f422f 100644 --- a/apps/yaak-client/components/GrpcResponsePane.tsx +++ b/apps/yaak-client/components/GrpcResponsePane.tsx @@ -1,36 +1,36 @@ -import type { GrpcEvent, GrpcRequest } from '@yaakapp-internal/models'; -import { HStack, Icon, type IconProps, LoadingIcon, VStack } from '@yaakapp-internal/ui'; -import { useAtomValue, useSetAtom } from 'jotai'; -import type { CSSProperties } from 'react'; -import { useEffect, useMemo, useState } from 'react'; +import type { GrpcEvent, GrpcRequest } from "@yaakapp-internal/models"; +import { HStack, Icon, type IconProps, LoadingIcon, VStack } from "@yaakapp-internal/ui"; +import { useAtomValue, useSetAtom } from "jotai"; +import type { CSSProperties } from "react"; +import { useEffect, useMemo, useState } from "react"; import { activeGrpcConnectionAtom, activeGrpcConnections, pinnedGrpcConnectionIdAtom, useGrpcEvents, -} from '../hooks/usePinnedGrpcConnection'; -import { useStateWithDeps } from '../hooks/useStateWithDeps'; -import { Button } from './core/Button'; -import { Editor } from './core/Editor/LazyEditor'; -import { EventDetailHeader, EventViewer } from './core/EventViewer'; -import { EventViewerRow } from './core/EventViewerRow'; -import { HotkeyList } from './core/HotkeyList'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; -import { EmptyStateText } from './EmptyStateText'; -import { ErrorBoundary } from './ErrorBoundary'; -import { RecentGrpcConnectionsDropdown } from './RecentGrpcConnectionsDropdown'; +} from "../hooks/usePinnedGrpcConnection"; +import { useStateWithDeps } from "../hooks/useStateWithDeps"; +import { Button } from "./core/Button"; +import { Editor } from "./core/Editor/LazyEditor"; +import { EventDetailHeader, EventViewer } from "./core/EventViewer"; +import { EventViewerRow } from "./core/EventViewerRow"; +import { HotkeyList } from "./core/HotkeyList"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; +import { EmptyStateText } from "./EmptyStateText"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { RecentGrpcConnectionsDropdown } from "./RecentGrpcConnectionsDropdown"; interface Props { style?: CSSProperties; className?: string; activeRequest: GrpcRequest; methodType: - | 'unary' - | 'client_streaming' - | 'server_streaming' - | 'streaming' - | 'no-schema' - | 'no-method'; + | "unary" + | "client_streaming" + | "server_streaming" + | "streaming" + | "no-schema" + | "no-method"; } export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { @@ -50,10 +50,10 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { // Set the active message to the first message received if unary // biome-ignore lint/correctness/useExhaustiveDependencies: none useEffect(() => { - if (events.length === 0 || activeEvent != null || methodType !== 'unary') { + if (events.length === 0 || activeEvent != null || methodType !== "unary") { return; } - const firstServerMessageIndex = events.findIndex((m) => m.eventType === 'server_message'); + const firstServerMessageIndex = events.findIndex((m) => m.eventType === "server_message"); if (firstServerMessageIndex !== -1) { setActiveEventIndex(firstServerMessageIndex); } @@ -61,7 +61,7 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { if (activeConnection == null) { return ( - + ); } @@ -69,7 +69,7 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { {events.length} Messages - {activeConnection.state !== 'closed' && ( + {activeConnection.state !== "closed" && ( )} @@ -155,8 +155,8 @@ function GrpcEventDetail({ setShowingLarge: (v: boolean) => void; onClose: () => void; }) { - if (event.eventType === 'client_message' || event.eventType === 'server_message') { - const title = `Message ${event.eventType === 'client_message' ? 'Sent' : 'Received'}`; + if (event.eventType === "client_message" || event.eventType === "server_message") { + const title = `Message ${event.eventType === "client_message" ? "Sent" : "Received"}`; return (
@@ -190,7 +190,7 @@ function GrpcEventDetail({ ) : ( {Object.keys(event.metadata).length === 0 ? ( - No {event.eventType === 'connection_end' ? 'trailers' : 'metadata'} + No {event.eventType === "connection_end" ? "trailers" : "metadata"} ) : ( @@ -229,20 +229,20 @@ function GrpcEventDetail({ } function getEventDisplay( - eventType: GrpcEvent['eventType'], - status: GrpcEvent['status'], -): { icon: IconProps['icon']; color: IconProps['color']; title: string } { - if (eventType === 'server_message') { - return { icon: 'arrow_big_down_dash', color: 'info', title: 'Server message' }; + eventType: GrpcEvent["eventType"], + status: GrpcEvent["status"], +): { icon: IconProps["icon"]; color: IconProps["color"]; title: string } { + if (eventType === "server_message") { + return { icon: "arrow_big_down_dash", color: "info", title: "Server message" }; } - if (eventType === 'client_message') { - return { icon: 'arrow_big_up_dash', color: 'primary', title: 'Client message' }; + if (eventType === "client_message") { + return { icon: "arrow_big_up_dash", color: "primary", title: "Client message" }; } - if (eventType === 'error' || (status != null && status > 0)) { - return { icon: 'alert_triangle', color: 'danger', title: 'Error' }; + if (eventType === "error" || (status != null && status > 0)) { + return { icon: "alert_triangle", color: "danger", title: "Error" }; } - if (eventType === 'connection_end') { - return { icon: 'check', color: 'success', title: 'Connection response' }; + if (eventType === "connection_end") { + return { icon: "check", color: "success", title: "Connection response" }; } - return { icon: 'info', color: undefined, title: 'Event' }; + return { icon: "info", color: undefined, title: "Event" }; } diff --git a/apps/yaak-client/components/HeadersEditor.tsx b/apps/yaak-client/components/HeadersEditor.tsx index d5d2f0ca..790ca958 100644 --- a/apps/yaak-client/components/HeadersEditor.tsx +++ b/apps/yaak-client/components/HeadersEditor.tsx @@ -1,19 +1,19 @@ -import type { HttpRequestHeader } from '@yaakapp-internal/models'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import { HStack } from '@yaakapp-internal/ui'; -import { charsets } from '../lib/data/charsets'; -import { connections } from '../lib/data/connections'; -import { encodings } from '../lib/data/encodings'; -import { headerNames } from '../lib/data/headerNames'; -import { mimeTypes } from '../lib/data/mimetypes'; -import { CountBadge } from './core/CountBadge'; -import { DetailsBanner } from './core/DetailsBanner'; -import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; -import type { InputProps } from './core/Input'; -import type { Pair, PairEditorProps } from './core/PairEditor'; -import { PairEditorRow } from './core/PairEditor'; -import { ensurePairId } from './core/PairEditor.util'; -import { PairOrBulkEditor } from './core/PairOrBulkEditor'; +import type { HttpRequestHeader } from "@yaakapp-internal/models"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import { HStack } from "@yaakapp-internal/ui"; +import { charsets } from "../lib/data/charsets"; +import { connections } from "../lib/data/connections"; +import { encodings } from "../lib/data/encodings"; +import { headerNames } from "../lib/data/headerNames"; +import { mimeTypes } from "../lib/data/mimetypes"; +import { CountBadge } from "./core/CountBadge"; +import { DetailsBanner } from "./core/DetailsBanner"; +import type { GenericCompletionConfig } from "./core/Editor/genericCompletion"; +import type { InputProps } from "./core/Input"; +import type { Pair, PairEditorProps } from "./core/PairEditor"; +import { PairEditorRow } from "./core/PairEditor"; +import { ensurePairId } from "./core/PairEditor.util"; +import { PairOrBulkEditor } from "./core/PairOrBulkEditor"; type Props = { forceUpdateKey: string; @@ -29,7 +29,7 @@ export function HeadersEditor({ stateKey, headers, inheritedHeaders, - inheritedHeadersLabel = 'Inherited', + inheritedHeadersLabel = "Inherited", onChange, forceUpdateKey, }: Props) { @@ -50,8 +50,8 @@ export function HeadersEditor({
{hasInheritedHeaders && ( @@ -106,28 +106,28 @@ export function HeadersEditor({ const MIN_MATCH = 3; const headerOptionsMap: Record = { - 'content-type': mimeTypes, - accept: ['*/*', ...mimeTypes], - 'accept-encoding': encodings, + "content-type": mimeTypes, + accept: ["*/*", ...mimeTypes], + "accept-encoding": encodings, connection: connections, - 'accept-charset': charsets, + "accept-charset": charsets, }; -const valueType = (pair: Pair): InputProps['type'] => { +const valueType = (pair: Pair): InputProps["type"] => { const name = pair.name.toLowerCase().trim(); if ( - name.includes('authorization') || - name.includes('api-key') || - name.includes('access-token') || - name.includes('auth') || - name.includes('secret') || - name.includes('token') || - name === 'cookie' || - name === 'set-cookie' + name.includes("authorization") || + name.includes("api-key") || + name.includes("access-token") || + name.includes("auth") || + name.includes("secret") || + name.includes("token") || + name === "cookie" || + name === "set-cookie" ) { - return 'password'; + return "password"; } - return 'text'; + return "text"; }; const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefined => { @@ -135,19 +135,19 @@ const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefi const options: GenericCompletionOption[] = headerOptionsMap[name]?.map((o) => ({ label: o, - type: 'constant', + type: "constant", boost: 1, // Put above other completions })) ?? []; return { minMatch: MIN_MATCH, options }; }; -const nameAutocomplete: PairEditorProps['nameAutocomplete'] = { +const nameAutocomplete: PairEditorProps["nameAutocomplete"] = { minMatch: MIN_MATCH, options: headerNames.map((t) => - typeof t === 'string' + typeof t === "string" ? { label: t, - type: 'constant', + type: "constant", boost: 1, // Put above other completions } : { @@ -158,11 +158,11 @@ const nameAutocomplete: PairEditorProps['nameAutocomplete'] = { }; const validateHttpHeader = (v: string) => { - if (v === '') { + if (v === "") { return true; } // Template strings are not allowed so we replace them with a valid example string - const withoutTemplateStrings = v.replace(/\$\{\[\s*[^\]\s]+\s*]}/gi, '123'); + const withoutTemplateStrings = v.replace(/\$\{\[\s*[^\]\s]+\s*]}/gi, "123"); return withoutTemplateStrings.match(/^[a-zA-Z0-9-_]+$/) !== null; }; diff --git a/apps/yaak-client/components/HttpAuthenticationEditor.tsx b/apps/yaak-client/components/HttpAuthenticationEditor.tsx index 6bc9ab0d..ec08c115 100644 --- a/apps/yaak-client/components/HttpAuthenticationEditor.tsx +++ b/apps/yaak-client/components/HttpAuthenticationEditor.tsx @@ -4,23 +4,23 @@ import type { HttpRequest, WebsocketRequest, Workspace, -} from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import { HStack, Icon, InlineCode } from '@yaakapp-internal/ui'; -import { useCallback } from 'react'; -import { openFolderSettings } from '../commands/openFolderSettings'; -import { openWorkspaceSettings } from '../commands/openWorkspaceSettings'; -import { useHttpAuthenticationConfig } from '../hooks/useHttpAuthenticationConfig'; -import { useInheritedAuthentication } from '../hooks/useInheritedAuthentication'; -import { useRenderTemplate } from '../hooks/useRenderTemplate'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { Dropdown, type DropdownItem } from './core/Dropdown'; -import { IconButton } from './core/IconButton'; -import { Input, type InputProps } from './core/Input'; -import { Link } from './core/Link'; -import { SegmentedControl } from './core/SegmentedControl'; -import { DynamicForm } from './DynamicForm'; -import { EmptyStateText } from './EmptyStateText'; +} from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import { HStack, Icon, InlineCode } from "@yaakapp-internal/ui"; +import { useCallback } from "react"; +import { openFolderSettings } from "../commands/openFolderSettings"; +import { openWorkspaceSettings } from "../commands/openWorkspaceSettings"; +import { useHttpAuthenticationConfig } from "../hooks/useHttpAuthenticationConfig"; +import { useInheritedAuthentication } from "../hooks/useInheritedAuthentication"; +import { useRenderTemplate } from "../hooks/useRenderTemplate"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { Dropdown, type DropdownItem } from "./core/Dropdown"; +import { IconButton } from "./core/IconButton"; +import { Input, type InputProps } from "./core/Input"; +import { Link } from "./core/Link"; +import { SegmentedControl } from "./core/SegmentedControl"; +import { DynamicForm } from "./DynamicForm"; +import { EmptyStateText } from "./EmptyStateText"; interface Props { model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace; @@ -39,7 +39,7 @@ export function HttpAuthenticationEditor({ model }: Props) { [model], ); - if (model.authenticationType === 'none') { + if (model.authenticationType === "none") { return No authentication; } @@ -54,7 +54,7 @@ export function HttpAuthenticationEditor({ model }: Props) { } if (inheritedAuth == null) { - if (model.model === 'workspace' || model.model === 'folder') { + if (model.model === "workspace" || model.model === "folder") { return (

@@ -67,24 +67,24 @@ export function HttpAuthenticationEditor({ model }: Props) { return No authentication; } - if (inheritedAuth.authenticationType === 'none') { + if (inheritedAuth.authenticationType === "none") { return No authentication; } const wasAuthInherited = inheritedAuth?.id !== model.id; if (wasAuthInherited) { const name = resolvedModelName(inheritedAuth); - const cta = inheritedAuth.model === 'workspace' ? 'Workspace' : name; + const cta = inheritedAuth.model === "workspace" ? "Workspace" : name; return (

- Inherited from{' '} + Inherited from{" "} - ) : activeResponse.state === 'closed' && + ) : activeResponse.state === "closed" && (activeResponse.contentLength ?? 0) === 0 ? ( Empty - ) : mimeType?.match(/^text\/event-stream/i) && viewMode === 'pretty' ? ( + ) : mimeType?.match(/^text\/event-stream/i) && viewMode === "pretty" ? ( ) : mimeType?.match(/^image\/svg/) ? ( @@ -291,17 +291,17 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) { ) : mimeType?.match(/^video/i) ? ( - ) : mimeType?.match(/^multipart/i) && viewMode === 'pretty' ? ( + ) : mimeType?.match(/^multipart/i) && viewMode === "pretty" ? ( ) : mimeType?.match(/pdf/i) ? ( - ) : mimeType?.match(/csv|tab-separated/i) && viewMode === 'pretty' ? ( + ) : mimeType?.match(/csv|tab-separated/i) && viewMode === "pretty" ? ( ) : ( )} @@ -339,7 +339,7 @@ function getRedirectDropWarning( const droppedHeaders = new Set(); for (const e of events) { const event = e.event; - if (event.type !== 'redirect') { + if (event.type !== "redirect") { continue; } @@ -370,12 +370,12 @@ function pushHeaderName(headers: Set, headerName: string): void { function getRedirectWarningLabel(warning: RedirectDropWarning): string { if (warning.droppedBodyCount > 0 && warning.droppedHeaders.length > 0) { - return 'Dropped body and headers'; + return "Dropped body and headers"; } if (warning.droppedBodyCount > 0) { - return 'Dropped body'; + return "Dropped body"; } - return 'Dropped headers'; + return "Dropped headers"; } function EnsureCompleteResponse({ @@ -390,7 +390,7 @@ function EnsureCompleteResponse({ } // Wait until the response has been fully-downloaded - if (response.state !== 'closed') { + if (response.state !== "closed") { return ( @@ -421,7 +421,7 @@ function HttpMultipartViewer({ response }: { response: HttpResponse }) { if (body.data == null) return null; const contentTypeHeader = getContentTypeFromHeaders(response.headers); - const boundary = contentTypeHeader?.split('boundary=')[1] ?? 'unknown'; + const boundary = contentTypeHeader?.split("boundary=")[1] ?? "unknown"; return ; } diff --git a/apps/yaak-client/components/HttpResponseTimeline.tsx b/apps/yaak-client/components/HttpResponseTimeline.tsx index 82ac9f70..8ad5e3aa 100644 --- a/apps/yaak-client/components/HttpResponseTimeline.tsx +++ b/apps/yaak-client/components/HttpResponseTimeline.tsx @@ -2,16 +2,16 @@ import type { HttpResponse, HttpResponseEvent, HttpResponseEventData, -} from '@yaakapp-internal/models'; -import { type ReactNode, useMemo, useState } from 'react'; -import { useHttpResponseEvents } from '../hooks/useHttpResponseEvents'; -import { Editor } from './core/Editor/LazyEditor'; -import { type EventDetailAction, EventDetailHeader, EventViewer } from './core/EventViewer'; -import { EventViewerRow } from './core/EventViewerRow'; -import { HttpStatusTagRaw } from './core/HttpStatusTag'; -import { Icon, type IconProps } from '@yaakapp-internal/ui'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; -import type { TimelineViewMode } from './HttpResponsePane'; +} from "@yaakapp-internal/models"; +import { type ReactNode, useMemo, useState } from "react"; +import { useHttpResponseEvents } from "../hooks/useHttpResponseEvents"; +import { Editor } from "./core/Editor/LazyEditor"; +import { type EventDetailAction, EventDetailHeader, EventViewer } from "./core/EventViewer"; +import { EventViewerRow } from "./core/EventViewerRow"; +import { HttpStatusTagRaw } from "./core/HttpStatusTag"; +import { Icon, type IconProps } from "@yaakapp-internal/ui"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; +import type { TimelineViewMode } from "./HttpResponsePane"; interface Props { response: HttpResponse; @@ -28,12 +28,12 @@ function Inner({ response, viewMode }: Props) { // Generate plain text representation of all events (with prefixes for timeline view) const plainText = useMemo(() => { - if (!events || events.length === 0) return ''; - return events.map((event) => formatEventText(event.event, true)).join('\n'); + if (!events || events.length === 0) return ""; + return events.map((event) => formatEventText(event.event, true)).join("\n"); }, [events]); // Plain text view - show all events as text in an editor - if (viewMode === 'text') { + if (viewMode === "text") { if (isLoading) { return

Loading events...
; } else if (error) { @@ -98,8 +98,8 @@ function EventDetails({ const actions: EventDetailAction[] = [ { - key: 'toggle-raw', - label: showRaw ? 'Formatted' : 'Text', + key: "toggle-raw", + label: showRaw ? "Formatted" : "Text", onClick: () => setShowRaw(!showRaw), }, ]; @@ -107,24 +107,24 @@ function EventDetails({ // Determine the title based on event type const title = (() => { switch (e.type) { - case 'header_up': - return 'Header Sent'; - case 'header_down': - return 'Header Received'; - case 'send_url': - return 'Request'; - case 'receive_url': - return 'Response'; - case 'redirect': - return 'Redirect'; - case 'setting': - return 'Apply Setting'; - case 'chunk_sent': - return 'Data Sent'; - case 'chunk_received': - return 'Data Received'; - case 'dns_resolved': - return e.overridden ? 'DNS Override' : 'DNS Resolution'; + case "header_up": + return "Header Sent"; + case "header_down": + return "Header Received"; + case "send_url": + return "Request"; + case "receive_url": + return "Response"; + case "redirect": + return "Redirect"; + case "setting": + return "Apply Setting"; + case "chunk_sent": + return "Data Sent"; + case "chunk_received": + return "Data Received"; + case "dns_resolved": + return e.overridden ? "DNS Override" : "DNS Resolution"; default: return label; } @@ -139,7 +139,7 @@ function EventDetails({ } // Headers - show name and value - if (e.type === 'header_up' || e.type === 'header_down') { + if (e.type === "header_up" || e.type === "header_down") { return ( {e.name} @@ -149,13 +149,13 @@ function EventDetails({ } // Request URL - show all URL parts separately - if (e.type === 'send_url') { - const auth = e.username || e.password ? `${e.username}:${e.password}@` : ''; + if (e.type === "send_url") { + const auth = e.username || e.password ? `${e.username}:${e.password}@` : ""; const isDefaultPort = - (e.scheme === 'http' && e.port === 80) || (e.scheme === 'https' && e.port === 443); - const portStr = isDefaultPort ? '' : `:${e.port}`; - const query = e.query ? `?${e.query}` : ''; - const fragment = e.fragment ? `#${e.fragment}` : ''; + (e.scheme === "http" && e.port === 80) || (e.scheme === "https" && e.port === 443); + const portStr = isDefaultPort ? "" : `:${e.port}`; + const query = e.query ? `?${e.query}` : ""; + const fragment = e.fragment ? `#${e.fragment}` : ""; const fullUrl = `${e.scheme}://${auth}${e.host}${portStr}${e.path}${query}${fragment}`; return ( @@ -174,7 +174,7 @@ function EventDetails({ } // Response status - show version and status separately - if (e.type === 'receive_url') { + if (e.type === "receive_url") { return ( {e.version} @@ -186,7 +186,7 @@ function EventDetails({ } // Redirect - show status, URL, and behavior - if (e.type === 'redirect') { + if (e.type === "redirect") { const droppedHeaders = e.dropped_headers ?? []; return ( @@ -195,18 +195,18 @@ function EventDetails({ {e.url} - {e.behavior === 'drop_body' ? 'Drop body, change to GET' : 'Preserve method and body'} + {e.behavior === "drop_body" ? "Drop body, change to GET" : "Preserve method and body"} - {e.dropped_body ? 'Yes' : 'No'} + {e.dropped_body ? "Yes" : "No"} - {droppedHeaders.length > 0 ? droppedHeaders.join(', ') : '--'} + {droppedHeaders.length > 0 ? droppedHeaders.join(", ") : "--"} ); } // Settings - show as key/value - if (e.type === 'setting') { + if (e.type === "setting") { return ( {e.name} @@ -216,16 +216,16 @@ function EventDetails({ } // Chunks - show formatted bytes - if (e.type === 'chunk_sent' || e.type === 'chunk_received') { + if (e.type === "chunk_sent" || e.type === "chunk_received") { return
{formatBytes(e.bytes)}
; } // DNS Resolution - show hostname, addresses, and timing - if (e.type === 'dns_resolved') { + if (e.type === "dns_resolved") { return ( {e.hostname} - {e.addresses.join(', ')} + {e.addresses.join(", ")} {e.overridden ? ( -- @@ -255,57 +255,57 @@ function EventDetails({ ); } -type EventTextParts = { prefix: '>' | '<' | '*'; text: string }; +type EventTextParts = { prefix: ">" | "<" | "*"; text: string }; /** Get the prefix and text for an event */ function getEventTextParts(event: HttpResponseEventData): EventTextParts { switch (event.type) { - case 'send_url': + case "send_url": return { - prefix: '>', - text: `${event.method} ${event.path}${event.query ? `?${event.query}` : ''}${event.fragment ? `#${event.fragment}` : ''}`, + prefix: ">", + text: `${event.method} ${event.path}${event.query ? `?${event.query}` : ""}${event.fragment ? `#${event.fragment}` : ""}`, }; - case 'receive_url': - return { prefix: '<', text: `${event.version} ${event.status}` }; - case 'header_up': - return { prefix: '>', text: `${event.name}: ${event.value}` }; - case 'header_down': - return { prefix: '<', text: `${event.name}: ${event.value}` }; - case 'redirect': { - const behavior = event.behavior === 'drop_body' ? 'drop body' : 'preserve'; + case "receive_url": + return { prefix: "<", text: `${event.version} ${event.status}` }; + case "header_up": + return { prefix: ">", text: `${event.name}: ${event.value}` }; + case "header_down": + return { prefix: "<", text: `${event.name}: ${event.value}` }; + case "redirect": { + const behavior = event.behavior === "drop_body" ? "drop body" : "preserve"; const droppedHeaders = event.dropped_headers ?? []; const dropped = [ - event.dropped_body ? 'body dropped' : null, - droppedHeaders.length > 0 ? `headers dropped: ${droppedHeaders.join(', ')}` : null, + event.dropped_body ? "body dropped" : null, + droppedHeaders.length > 0 ? `headers dropped: ${droppedHeaders.join(", ")}` : null, ] .filter(Boolean) - .join(', '); + .join(", "); return { - prefix: '*', - text: `Redirect ${event.status} -> ${event.url} (${behavior}${dropped ? `, ${dropped}` : ''})`, + prefix: "*", + text: `Redirect ${event.status} -> ${event.url} (${behavior}${dropped ? `, ${dropped}` : ""})`, }; } - case 'setting': - return { prefix: '*', text: `Setting ${event.name}=${event.value}` }; - case 'info': - return { prefix: '*', text: event.message }; - case 'chunk_sent': - return { prefix: '*', text: `[${formatBytes(event.bytes)} sent]` }; - case 'chunk_received': - return { prefix: '*', text: `[${formatBytes(event.bytes)} received]` }; - case 'dns_resolved': + case "setting": + return { prefix: "*", text: `Setting ${event.name}=${event.value}` }; + case "info": + return { prefix: "*", text: event.message }; + case "chunk_sent": + return { prefix: "*", text: `[${formatBytes(event.bytes)} sent]` }; + case "chunk_received": + return { prefix: "*", text: `[${formatBytes(event.bytes)} received]` }; + case "dns_resolved": if (event.overridden) { return { - prefix: '*', - text: `DNS override ${event.hostname} -> ${event.addresses.join(', ')}`, + prefix: "*", + text: `DNS override ${event.hostname} -> ${event.addresses.join(", ")}`, }; } return { - prefix: '*', - text: `DNS resolved ${event.hostname} to ${event.addresses.join(', ')} (${event.duration}ms)`, + prefix: "*", + text: `DNS resolved ${event.hostname} to ${event.addresses.join(", ")} (${event.duration}ms)`, }; default: - return { prefix: '*', text: '[unknown event]' }; + return { prefix: "*", text: "[unknown event]" }; } } @@ -316,103 +316,103 @@ function formatEventText(event: HttpResponseEventData, includePrefix: boolean): } type EventDisplay = { - icon: IconProps['icon']; - color: IconProps['color']; + icon: IconProps["icon"]; + color: IconProps["color"]; label: string; summary: ReactNode; }; function getEventDisplay(event: HttpResponseEventData): EventDisplay { switch (event.type) { - case 'setting': + case "setting": return { - icon: 'settings', - color: 'secondary', - label: 'Setting', + icon: "settings", + color: "secondary", + label: "Setting", summary: `${event.name} = ${event.value}`, }; - case 'info': + case "info": return { - icon: 'info', - color: 'secondary', - label: 'Info', + icon: "info", + color: "secondary", + label: "Info", summary: event.message, }; - case 'redirect': { + case "redirect": { const droppedHeaders = event.dropped_headers ?? []; const dropped = [ - event.dropped_body ? 'drop body' : null, + event.dropped_body ? "drop body" : null, droppedHeaders.length > 0 - ? `drop ${droppedHeaders.length} ${droppedHeaders.length === 1 ? 'header' : 'headers'}` + ? `drop ${droppedHeaders.length} ${droppedHeaders.length === 1 ? "header" : "headers"}` : null, ] .filter(Boolean) - .join(', '); + .join(", "); return { - icon: 'arrow_big_right_dash', - color: 'success', - label: 'Redirect', - summary: `Redirecting ${event.status} ${event.url}${dropped ? ` (${dropped})` : ''}`, + icon: "arrow_big_right_dash", + color: "success", + label: "Redirect", + summary: `Redirecting ${event.status} ${event.url}${dropped ? ` (${dropped})` : ""}`, }; } - case 'send_url': + case "send_url": return { - icon: 'arrow_big_up_dash', - color: 'primary', - label: 'Request', - summary: `${event.method} ${event.path}${event.query ? `?${event.query}` : ''}${event.fragment ? `#${event.fragment}` : ''}`, + icon: "arrow_big_up_dash", + color: "primary", + label: "Request", + summary: `${event.method} ${event.path}${event.query ? `?${event.query}` : ""}${event.fragment ? `#${event.fragment}` : ""}`, }; - case 'receive_url': + case "receive_url": return { - icon: 'arrow_big_down_dash', - color: 'info', - label: 'Response', + icon: "arrow_big_down_dash", + color: "info", + label: "Response", summary: `${event.version} ${event.status}`, }; - case 'header_up': + case "header_up": return { - icon: 'arrow_big_up_dash', - color: 'primary', - label: 'Header', + icon: "arrow_big_up_dash", + color: "primary", + label: "Header", summary: `${event.name}: ${event.value}`, }; - case 'header_down': + case "header_down": return { - icon: 'arrow_big_down_dash', - color: 'info', - label: 'Header', + icon: "arrow_big_down_dash", + color: "info", + label: "Header", summary: `${event.name}: ${event.value}`, }; - case 'chunk_sent': + case "chunk_sent": return { - icon: 'info', - color: 'secondary', - label: 'Chunk', + icon: "info", + color: "secondary", + label: "Chunk", summary: `${formatBytes(event.bytes)} chunk sent`, }; - case 'chunk_received': + case "chunk_received": return { - icon: 'info', - color: 'secondary', - label: 'Chunk', + icon: "info", + color: "secondary", + label: "Chunk", summary: `${formatBytes(event.bytes)} chunk received`, }; - case 'dns_resolved': + case "dns_resolved": return { - icon: 'globe', - color: event.overridden ? 'success' : 'secondary', - label: event.overridden ? 'DNS Override' : 'DNS', + icon: "globe", + color: event.overridden ? "success" : "secondary", + label: event.overridden ? "DNS Override" : "DNS", summary: event.overridden - ? `${event.hostname} → ${event.addresses.join(', ')} (overridden)` - : `${event.hostname} → ${event.addresses.join(', ')} (${event.duration}ms)`, + ? `${event.hostname} → ${event.addresses.join(", ")} (overridden)` + : `${event.hostname} → ${event.addresses.join(", ")} (${event.duration}ms)`, }; default: return { - icon: 'info', - color: 'secondary', - label: 'Unknown', - summary: 'Unknown event', + icon: "info", + color: "secondary", + label: "Unknown", + summary: "Unknown event", }; } } diff --git a/apps/yaak-client/components/ImportCurlButton.tsx b/apps/yaak-client/components/ImportCurlButton.tsx index a86d0d0d..c0de6b2d 100644 --- a/apps/yaak-client/components/ImportCurlButton.tsx +++ b/apps/yaak-client/components/ImportCurlButton.tsx @@ -1,14 +1,14 @@ -import { clear, readText } from '@tauri-apps/plugin-clipboard-manager'; -import * as m from 'motion/react-m'; -import { useEffect, useState } from 'react'; -import { useImportCurl } from '../hooks/useImportCurl'; -import { useWindowFocus } from '../hooks/useWindowFocus'; -import { Button } from './core/Button'; -import { Icon } from '@yaakapp-internal/ui'; +import { clear, readText } from "@tauri-apps/plugin-clipboard-manager"; +import * as m from "motion/react-m"; +import { useEffect, useState } from "react"; +import { useImportCurl } from "../hooks/useImportCurl"; +import { useWindowFocus } from "../hooks/useWindowFocus"; +import { Button } from "./core/Button"; +import { Icon } from "@yaakapp-internal/ui"; export function ImportCurlButton() { const focused = useWindowFocus(); - const [clipboardText, setClipboardText] = useState(''); + const [clipboardText, setClipboardText] = useState(""); const importCurl = useImportCurl(); const [isLoading, setIsLoading] = useState(false); @@ -18,7 +18,7 @@ export function ImportCurlButton() { readText().then(setClipboardText); }, [focused]); - if (!clipboardText?.trim().startsWith('curl ')) { + if (!clipboardText?.trim().startsWith("curl ")) { return null; } @@ -41,9 +41,9 @@ export function ImportCurlButton() { try { await importCurl.mutateAsync({ command: clipboardText }); await clear(); // Clear the clipboard so the button goes away - setClipboardText(''); + setClipboardText(""); } catch (e) { - console.log('Failed to import curl', e); + console.log("Failed to import curl", e); } finally { setIsLoading(false); } diff --git a/apps/yaak-client/components/ImportDataDialog.tsx b/apps/yaak-client/components/ImportDataDialog.tsx index d3f80405..4c676c1c 100644 --- a/apps/yaak-client/components/ImportDataDialog.tsx +++ b/apps/yaak-client/components/ImportDataDialog.tsx @@ -1,8 +1,8 @@ -import { VStack } from '@yaakapp-internal/ui'; -import { useState } from 'react'; -import { useLocalStorage } from 'react-use'; -import { Button } from './core/Button'; -import { SelectFile } from './SelectFile'; +import { VStack } from "@yaakapp-internal/ui"; +import { useState } from "react"; +import { useLocalStorage } from "react-use"; +import { Button } from "./core/Button"; +import { SelectFile } from "./SelectFile"; interface Props { importData: (filePath: string) => Promise; @@ -10,7 +10,7 @@ interface Props { export function ImportDataDialog({ importData }: Props) { const [isLoading, setIsLoading] = useState(false); - const [filePath, setFilePath] = useLocalStorage('importFilePath', null); + const [filePath, setFilePath] = useLocalStorage("importFilePath", null); return ( @@ -45,7 +45,7 @@ export function ImportDataDialog({ importData }: Props) { } }} > - {isLoading ? 'Importing' : 'Import'} + {isLoading ? "Importing" : "Import"} )} diff --git a/apps/yaak-client/components/IsDev.tsx b/apps/yaak-client/components/IsDev.tsx index d850c5e3..6cc9ad6c 100644 --- a/apps/yaak-client/components/IsDev.tsx +++ b/apps/yaak-client/components/IsDev.tsx @@ -1,5 +1,5 @@ -import type { ReactNode } from 'react'; -import { appInfo } from '../lib/appInfo'; +import type { ReactNode } from "react"; +import { appInfo } from "../lib/appInfo"; interface Props { children: ReactNode; diff --git a/apps/yaak-client/components/JsonBodyEditor.tsx b/apps/yaak-client/components/JsonBodyEditor.tsx index 5f1d8765..cdcef87a 100644 --- a/apps/yaak-client/components/JsonBodyEditor.tsx +++ b/apps/yaak-client/components/JsonBodyEditor.tsx @@ -1,21 +1,21 @@ -import { linter } from '@codemirror/lint'; -import type { HttpRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import { Banner, Icon } from '@yaakapp-internal/ui'; -import { useCallback, useMemo } from 'react'; -import { useKeyValue } from '../hooks/useKeyValue'; -import { textLikelyContainsJsonComments } from '../lib/jsonComments'; -import type { DropdownItem } from './core/Dropdown'; -import { Dropdown } from './core/Dropdown'; -import type { EditorProps } from './core/Editor/Editor'; -import { jsonParseLinter } from './core/Editor/json-lint'; -import { Editor } from './core/Editor/LazyEditor'; -import { IconButton } from './core/IconButton'; -import { IconTooltip } from './core/IconTooltip'; +import { linter } from "@codemirror/lint"; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import { Banner, Icon } from "@yaakapp-internal/ui"; +import { useCallback, useMemo } from "react"; +import { useKeyValue } from "../hooks/useKeyValue"; +import { textLikelyContainsJsonComments } from "../lib/jsonComments"; +import type { DropdownItem } from "./core/Dropdown"; +import { Dropdown } from "./core/Dropdown"; +import type { EditorProps } from "./core/Editor/Editor"; +import { jsonParseLinter } from "./core/Editor/json-lint"; +import { Editor } from "./core/Editor/LazyEditor"; +import { IconButton } from "./core/IconButton"; +import { IconTooltip } from "./core/IconTooltip"; interface Props { forceUpdateKey: string; - heightMode: EditorProps['heightMode']; + heightMode: EditorProps["heightMode"]; request: HttpRequest; } @@ -40,13 +40,13 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { ); const hasComments = useMemo( - () => textLikelyContainsJsonComments(request.body?.text ?? ''), + () => textLikelyContainsJsonComments(request.body?.text ?? ""), [request.body?.text], ); const { value: bannerDismissed, set: setBannerDismissed } = useKeyValue({ - namespace: 'no_sync', - key: ['json-fix-3', request.workspaceId], + namespace: "no_sync", + key: ["json-fix-3", request.workspaceId], fallback: false, }); @@ -68,8 +68,8 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { const showBanner = hasComments && autoFix && !bannerDismissed; - const stripMessage = 'Automatically strip comments and trailing commas before sending'; - const actions = useMemo( + const stripMessage = "Automatically strip comments and trailing commas before sending"; + const actions = useMemo( () => [ showBanner && ( @@ -85,12 +85,12 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { items={ [ { - label: 'Automatically Fix JSON', + label: "Automatically Fix JSON", keepOpenOnSelect: true, onSelect: handleToggleAutoFix, rightSlot: , leftSlot: ( - + ), }, ] satisfies DropdownItem[] @@ -110,7 +110,7 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { autocompleteVariables placeholder="..." heightMode={heightMode} - defaultValue={`${request.body?.text ?? ''}`} + defaultValue={`${request.body?.text ?? ""}`} language="json" onChange={handleChange} stateKey={`json.${request.id}`} diff --git a/apps/yaak-client/components/KeyboardShortcutsDialog.tsx b/apps/yaak-client/components/KeyboardShortcutsDialog.tsx index e6a245ef..194437ef 100644 --- a/apps/yaak-client/components/KeyboardShortcutsDialog.tsx +++ b/apps/yaak-client/components/KeyboardShortcutsDialog.tsx @@ -1,5 +1,5 @@ -import { hotkeyActions } from '../hooks/useHotKey'; -import { HotkeyList } from './core/HotkeyList'; +import { hotkeyActions } from "../hooks/useHotKey"; +import { HotkeyList } from "./core/HotkeyList"; export function KeyboardShortcutsDialog() { return ( diff --git a/apps/yaak-client/components/LicenseBadge.tsx b/apps/yaak-client/components/LicenseBadge.tsx index d1d9d47a..1326db20 100644 --- a/apps/yaak-client/components/LicenseBadge.tsx +++ b/apps/yaak-client/components/LicenseBadge.tsx @@ -1,62 +1,62 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; -import type { LicenseCheckStatus } from '@yaakapp-internal/license'; -import { useLicense } from '@yaakapp-internal/license'; -import { settingsAtom } from '@yaakapp-internal/models'; -import { differenceInCalendarDays } from 'date-fns'; -import { formatDate } from 'date-fns/format'; -import { useAtomValue } from 'jotai'; -import type { ReactNode } from 'react'; -import { openSettings } from '../commands/openSettings'; -import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage'; -import { jotaiStore } from '../lib/jotai'; -import { CargoFeature } from './CargoFeature'; -import type { ButtonProps } from './core/Button'; -import { Dropdown, type DropdownItem } from './core/Dropdown'; -import { Icon } from '@yaakapp-internal/ui'; -import { PillButton } from './core/PillButton'; +import { openUrl } from "@tauri-apps/plugin-opener"; +import type { LicenseCheckStatus } from "@yaakapp-internal/license"; +import { useLicense } from "@yaakapp-internal/license"; +import { settingsAtom } from "@yaakapp-internal/models"; +import { differenceInCalendarDays } from "date-fns"; +import { formatDate } from "date-fns/format"; +import { useAtomValue } from "jotai"; +import type { ReactNode } from "react"; +import { openSettings } from "../commands/openSettings"; +import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage"; +import { jotaiStore } from "../lib/jotai"; +import { CargoFeature } from "./CargoFeature"; +import type { ButtonProps } from "./core/Button"; +import { Dropdown, type DropdownItem } from "./core/Dropdown"; +import { Icon } from "@yaakapp-internal/ui"; +import { PillButton } from "./core/PillButton"; -const dismissedAtom = atomWithKVStorage('dismissed_license_expired', null); +const dismissedAtom = atomWithKVStorage("dismissed_license_expired", null); function getDetail( data: LicenseCheckStatus, dismissedExpired: string | null, -): { label: ReactNode; color: ButtonProps['color']; options?: DropdownItem[] } | null | undefined { +): { label: ReactNode; color: ButtonProps["color"]; options?: DropdownItem[] } | null | undefined { const dismissedAt = dismissedExpired ? new Date(dismissedExpired).getTime() : null; switch (data.status) { - case 'active': + case "active": return null; - case 'personal_use': - return { label: 'Personal Use', color: 'notice' }; - case 'trialing': - return { label: 'Commercial Trial', color: 'secondary' }; - case 'error': - return { label: 'Error', color: 'danger' }; - case 'inactive': - return { label: 'Personal Use', color: 'notice' }; - case 'past_due': - return { label: 'Past Due', color: 'danger' }; - case 'expired': + case "personal_use": + return { label: "Personal Use", color: "notice" }; + case "trialing": + return { label: "Commercial Trial", color: "secondary" }; + case "error": + return { label: "Error", color: "danger" }; + case "inactive": + return { label: "Personal Use", color: "notice" }; + case "past_due": + return { label: "Past Due", color: "danger" }; + case "expired": // Don't show the expired message if it's been less than 14 days since the last dismissal if (dismissedAt && differenceInCalendarDays(new Date(), dismissedAt) < 14) { return null; } return { - color: 'notice', - label: data.data.changes > 0 ? 'Updates Paused' : 'License Expired', + color: "notice", + label: data.data.changes > 0 ? "Updates Paused" : "License Expired", options: [ { label: `${data.data.changes} New Updates`, - color: 'success', + color: "success", leftSlot: , rightSlot: , hidden: data.data.changes === 0 || data.data.changesUrl == null, - onSelect: () => openUrl(data.data.changesUrl ?? ''), + onSelect: () => openUrl(data.data.changesUrl ?? ""), }, { - type: 'separator', - label: `License expired ${formatDate(data.data.periodEnd, 'MMM dd, yyyy')}`, + type: "separator", + label: `License expired ${formatDate(data.data.periodEnd, "MMM dd, yyyy")}`, }, { label:
Renew License
, @@ -66,12 +66,12 @@ function getDetail( onSelect: () => openUrl(data.data.billingUrl), }, { - label: 'Enter License Key', + label: "Enter License Key", leftSlot: , hidden: data.data.changesUrl == null, onSelect: openLicenseDialog, }, - { type: 'separator' }, + { type: "separator" }, { label: Remind me Later, leftSlot: , @@ -135,5 +135,5 @@ function LicenseBadgeCmp() { } function openLicenseDialog() { - openSettings.mutate('license'); + openSettings.mutate("license"); } diff --git a/apps/yaak-client/components/LocalImage.tsx b/apps/yaak-client/components/LocalImage.tsx index 340218f5..5c4f2719 100644 --- a/apps/yaak-client/components/LocalImage.tsx +++ b/apps/yaak-client/components/LocalImage.tsx @@ -1,7 +1,7 @@ -import { useQuery } from '@tanstack/react-query'; -import { convertFileSrc } from '@tauri-apps/api/core'; -import { resolveResource } from '@tauri-apps/api/path'; -import classNames from 'classnames'; +import { useQuery } from "@tanstack/react-query"; +import { convertFileSrc } from "@tauri-apps/api/core"; +import { resolveResource } from "@tauri-apps/api/path"; +import classNames from "classnames"; interface Props { src: string; @@ -10,7 +10,7 @@ interface Props { export function LocalImage({ src: srcPath, className }: Props) { const src = useQuery({ - queryKey: ['local-image', srcPath], + queryKey: ["local-image", srcPath], queryFn: async () => { const p = await resolveResource(srcPath); return convertFileSrc(p); @@ -23,8 +23,8 @@ export function LocalImage({ src: srcPath, className }: Props) { alt="Response preview" className={classNames( className, - 'transition-opacity', - src.data == null ? 'opacity-0' : 'opacity-100', + "transition-opacity", + src.data == null ? "opacity-0" : "opacity-100", )} /> ); diff --git a/apps/yaak-client/components/Markdown.tsx b/apps/yaak-client/components/Markdown.tsx index ab6df5df..1bc0834d 100644 --- a/apps/yaak-client/components/Markdown.tsx +++ b/apps/yaak-client/components/Markdown.tsx @@ -1,9 +1,9 @@ -import type { CSSProperties } from 'react'; -import ReactMarkdown, { type Components } from 'react-markdown'; -import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'; -import remarkGfm from 'remark-gfm'; -import { ErrorBoundary } from './ErrorBoundary'; -import { Prose } from './Prose'; +import type { CSSProperties } from "react"; +import ReactMarkdown, { type Components } from "react-markdown"; +import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"; +import remarkGfm from "remark-gfm"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { Prose } from "./Prose"; interface Props { children: string | null; @@ -30,48 +30,48 @@ const prismTheme = { }, // Syntax tokens - comment: { color: 'var(--textSubtle)' }, - prolog: { color: 'var(--textSubtle)' }, - doctype: { color: 'var(--textSubtle)' }, - cdata: { color: 'var(--textSubtle)' }, + comment: { color: "var(--textSubtle)" }, + prolog: { color: "var(--textSubtle)" }, + doctype: { color: "var(--textSubtle)" }, + cdata: { color: "var(--textSubtle)" }, - punctuation: { color: 'var(--textSubtle)' }, + punctuation: { color: "var(--textSubtle)" }, - property: { color: 'var(--primary)' }, - 'attr-name': { color: 'var(--primary)' }, + property: { color: "var(--primary)" }, + "attr-name": { color: "var(--primary)" }, - string: { color: 'var(--notice)' }, - char: { color: 'var(--notice)' }, + string: { color: "var(--notice)" }, + char: { color: "var(--notice)" }, - number: { color: 'var(--info)' }, - constant: { color: 'var(--info)' }, - symbol: { color: 'var(--info)' }, + number: { color: "var(--info)" }, + constant: { color: "var(--info)" }, + symbol: { color: "var(--info)" }, - boolean: { color: 'var(--warning)' }, - 'attr-value': { color: 'var(--warning)' }, + boolean: { color: "var(--warning)" }, + "attr-value": { color: "var(--warning)" }, - variable: { color: 'var(--success)' }, + variable: { color: "var(--success)" }, - tag: { color: 'var(--info)' }, - operator: { color: 'var(--danger)' }, - keyword: { color: 'var(--danger)' }, - function: { color: 'var(--success)' }, - 'class-name': { color: 'var(--primary)' }, - builtin: { color: 'var(--danger)' }, - selector: { color: 'var(--danger)' }, - inserted: { color: 'var(--success)' }, - deleted: { color: 'var(--danger)' }, - regex: { color: 'var(--warning)' }, + tag: { color: "var(--info)" }, + operator: { color: "var(--danger)" }, + keyword: { color: "var(--danger)" }, + function: { color: "var(--success)" }, + "class-name": { color: "var(--primary)" }, + builtin: { color: "var(--danger)" }, + selector: { color: "var(--danger)" }, + inserted: { color: "var(--success)" }, + deleted: { color: "var(--danger)" }, + regex: { color: "var(--warning)" }, - important: { color: 'var(--danger)', fontWeight: 'bold' }, - italic: { fontStyle: 'italic' }, - bold: { fontWeight: 'bold' }, - entity: { cursor: 'help' }, + important: { color: "var(--danger)", fontWeight: "bold" }, + italic: { fontStyle: "italic" }, + bold: { fontWeight: "bold" }, + entity: { cursor: "help" }, }; const lineStyle: CSSProperties = { - paddingRight: '1.5em', - paddingLeft: '0', + paddingRight: "1.5em", + paddingLeft: "0", opacity: 0.5, }; @@ -91,7 +91,7 @@ const markdownComponents: Partial = { const { children, className, ref, ...extraProps } = props; extraProps.node = undefined; - const match = /language-(\w+)/.exec(className || ''); + const match = /language-(\w+)/.exec(className || ""); return match ? ( = { language={match[1]} style={prismTheme} > - {String(children).replace(/\n$/, '')} + {String(children as string).replace(/\n$/, "")} ) : ( diff --git a/apps/yaak-client/components/MarkdownEditor.tsx b/apps/yaak-client/components/MarkdownEditor.tsx index e51acd0a..5da836a2 100644 --- a/apps/yaak-client/components/MarkdownEditor.tsx +++ b/apps/yaak-client/components/MarkdownEditor.tsx @@ -1,13 +1,13 @@ -import classNames from 'classnames'; -import { useRef, useState } from 'react'; -import type { EditorProps } from './core/Editor/Editor'; -import { Editor } from './core/Editor/LazyEditor'; -import { SegmentedControl } from './core/SegmentedControl'; -import { Markdown } from './Markdown'; +import classNames from "classnames"; +import { useRef, useState } from "react"; +import type { EditorProps } from "./core/Editor/Editor"; +import { Editor } from "./core/Editor/LazyEditor"; +import { SegmentedControl } from "./core/SegmentedControl"; +import { Markdown } from "./Markdown"; -type ViewMode = 'edit' | 'preview'; +type ViewMode = "edit" | "preview"; -interface Props extends Pick { +interface Props extends Pick { placeholder: string; className?: string; editorClassName?: string; @@ -25,7 +25,7 @@ export function MarkdownEditor({ forceUpdateKey, ...editorProps }: Props) { - const [viewMode, setViewMode] = useState(defaultValue ? 'preview' : 'edit'); + const [viewMode, setViewMode] = useState(defaultValue ? "preview" : "edit"); const containerRef = useRef(null); @@ -33,7 +33,7 @@ export function MarkdownEditor({ ); - const contents = viewMode === 'preview' ? preview : editor; + const contents = viewMode === "preview" ? preview : editor; return (
@@ -73,8 +73,8 @@ export function MarkdownEditor({ value={viewMode} className="opacity-0 group-focus-within/markdown:opacity-100 group-hover/markdown:opacity-100" options={[ - { icon: 'eye', label: 'Preview mode', value: 'preview' }, - { icon: 'pencil', label: 'Edit mode', value: 'edit' }, + { icon: "eye", label: "Preview mode", value: "preview" }, + { icon: "pencil", label: "Edit mode", value: "edit" }, ]} />
diff --git a/apps/yaak-client/components/MoveToWorkspaceDialog.tsx b/apps/yaak-client/components/MoveToWorkspaceDialog.tsx index 3415379e..20b3986d 100644 --- a/apps/yaak-client/components/MoveToWorkspaceDialog.tsx +++ b/apps/yaak-client/components/MoveToWorkspaceDialog.tsx @@ -1,14 +1,14 @@ -import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; -import { patchModel, workspacesAtom } from '@yaakapp-internal/models'; -import { InlineCode, VStack } from '@yaakapp-internal/ui'; -import { useAtomValue } from 'jotai'; -import { useState } from 'react'; -import { pluralizeCount } from '../lib/pluralize'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { router } from '../lib/router'; -import { showToast } from '../lib/toast'; -import { Button } from './core/Button'; -import { Select } from './core/Select'; +import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; +import { patchModel, workspacesAtom } from "@yaakapp-internal/models"; +import { InlineCode, VStack } from "@yaakapp-internal/ui"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import { pluralizeCount } from "../lib/pluralize"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { router } from "../lib/router"; +import { showToast } from "../lib/toast"; +import { Button } from "./core/Button"; +import { Select } from "./core/Select"; interface Props { activeWorkspaceId: string; @@ -49,17 +49,17 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P // Hide after a moment, to give time for requests to disappear setTimeout(onDone, 100); showToast({ - id: 'workspace-moved', + id: "workspace-moved", message: requests.length === 1 && requests[0] != null ? ( <> - {resolvedModelName(requests[0])} moved to{' '} - {targetWorkspace?.name ?? 'unknown'} + {resolvedModelName(requests[0])} moved to{" "} + {targetWorkspace?.name ?? "unknown"} ) : ( <> - {pluralizeCount('request', requests.length)} moved to{' '} - {targetWorkspace?.name ?? 'unknown'} + {pluralizeCount("request", requests.length)} moved to{" "} + {targetWorkspace?.name ?? "unknown"} ), action: ({ hide }) => ( @@ -69,7 +69,7 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P className="mr-auto min-w-[5rem]" onClick={async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: selectedWorkspaceId }, }); hide(); @@ -81,7 +81,7 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P }); }} > - {requests.length === 1 ? 'Move' : `Move ${pluralizeCount('Request', requests.length)}`} + {requests.length === 1 ? "Move" : `Move ${pluralizeCount("Request", requests.length)}`} ); diff --git a/apps/yaak-client/components/Prose.tsx b/apps/yaak-client/components/Prose.tsx index ab8be45b..883aef46 100644 --- a/apps/yaak-client/components/Prose.tsx +++ b/apps/yaak-client/components/Prose.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; -import type { ReactNode } from 'react'; -import './Prose.css'; +import classNames from "classnames"; +import type { ReactNode } from "react"; +import "./Prose.css"; interface Props { children: ReactNode; @@ -8,5 +8,5 @@ interface Props { } export function Prose({ className, ...props }: Props) { - return
; + return
; } diff --git a/apps/yaak-client/components/RecentGrpcConnectionsDropdown.tsx b/apps/yaak-client/components/RecentGrpcConnectionsDropdown.tsx index f083a207..e48af5d7 100644 --- a/apps/yaak-client/components/RecentGrpcConnectionsDropdown.tsx +++ b/apps/yaak-client/components/RecentGrpcConnectionsDropdown.tsx @@ -1,11 +1,11 @@ -import type { GrpcConnection } from '@yaakapp-internal/models'; -import { deleteModel } from '@yaakapp-internal/models'; -import { HStack, Icon } from '@yaakapp-internal/ui'; -import { formatDistanceToNowStrict } from 'date-fns'; -import { useDeleteGrpcConnections } from '../hooks/useDeleteGrpcConnections'; -import { pluralizeCount } from '../lib/pluralize'; -import { Dropdown } from './core/Dropdown'; -import { IconButton } from './core/IconButton'; +import type { GrpcConnection } from "@yaakapp-internal/models"; +import { deleteModel } from "@yaakapp-internal/models"; +import { HStack, Icon } from "@yaakapp-internal/ui"; +import { formatDistanceToNowStrict } from "date-fns"; +import { useDeleteGrpcConnections } from "../hooks/useDeleteGrpcConnections"; +import { pluralizeCount } from "../lib/pluralize"; +import { Dropdown } from "./core/Dropdown"; +import { IconButton } from "./core/IconButton"; interface Props { connections: GrpcConnection[]; @@ -19,27 +19,27 @@ export function RecentGrpcConnectionsDropdown({ onPinnedConnectionId, }: Props) { const deleteAllConnections = useDeleteGrpcConnections(activeConnection?.requestId); - const latestConnectionId = connections[0]?.id ?? 'n/a'; + const latestConnectionId = connections[0]?.id ?? "n/a"; return ( deleteModel(activeConnection), disabled: connections.length === 0, }, { - label: `Clear ${pluralizeCount('Connection', connections.length)}`, + label: `Clear ${pluralizeCount("Connection", connections.length)}`, onSelect: deleteAllConnections.mutate, hidden: connections.length <= 1, disabled: connections.length === 0, }, - { type: 'separator', label: 'History' }, + { type: "separator", label: "History" }, ...connections.map((c) => ({ label: ( - {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{' '} + {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{" "} {c.elapsed}ms ), @@ -50,7 +50,7 @@ export function RecentGrpcConnectionsDropdown({ > , hidden: responses.length === 0 || !!activeResponse.error, - disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100, + disabled: activeResponse.state !== "closed" && activeResponse.status >= 100, }, { - label: 'Copy Body', + label: "Copy Body", onSelect: copyResponse.mutate, leftSlot: , hidden: responses.length === 0 || !!activeResponse.error, - disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100, + disabled: activeResponse.state !== "closed" && activeResponse.status >= 100, }, { - label: 'Delete', + label: "Delete", leftSlot: , onSelect: () => deleteModel(activeResponse), }, { - label: 'Unpin Response', + label: "Unpin Response", onSelect: () => onPinnedResponseId(activeResponse.id), leftSlot: , hidden: latestResponseId === activeResponse.id, disabled: responses.length === 0, }, - { type: 'separator', label: 'History' }, + { type: "separator", label: "History" }, { - label: `Delete ${responses.length} ${pluralize('Response', responses.length)}`, + label: `Delete ${responses.length} ${pluralize("Response", responses.length)}`, onSelect: deleteAllResponses.mutate, hidden: responses.length === 0, disabled: responses.length === 0, }, - { type: 'separator' }, + { type: "separator" }, ...responses.map((r: HttpResponse) => ({ label: ( - {' '} - {r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'} + {" "} + {r.elapsed >= 0 ? `${r.elapsed}ms` : "n/a"} ), leftSlot: activeResponse?.id === r.id ? : , @@ -78,7 +78,7 @@ export const RecentHttpResponsesDropdown = function ResponsePane({ > { + useKeyboardEvent("keyup", "Control", () => { if (dropdownRef.current?.isOpen) { dropdownRef.current?.select?.(); } }); - useHotKey('switcher.prev', () => { + useHotKey("switcher.prev", () => { if (!dropdownRef.current?.isOpen) { // Select the second because the first is the current request dropdownRef.current?.open(1); @@ -41,7 +41,7 @@ export function RecentRequestsDropdown({ className }: Props) { } }); - useHotKey('switcher.next', () => { + useHotKey("switcher.next", () => { if (!dropdownRef.current?.isOpen) dropdownRef.current?.open(); dropdownRef.current?.prev?.(); }); @@ -61,7 +61,7 @@ export function RecentRequestsDropdown({ className }: Props) { leftSlot: , onSelect: async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: activeWorkspaceId }, search: (prev) => ({ ...prev, request_id: request.id }), }); @@ -73,8 +73,8 @@ export function RecentRequestsDropdown({ className }: Props) { if (recentRequestItems.length === 0) { return [ { - key: 'no-recent-requests', - label: 'No recent requests', + key: "no-recent-requests", + label: "No recent requests", disabled: true, }, ]; @@ -90,8 +90,8 @@ export function RecentRequestsDropdown({ className }: Props) { hotkeyAction="switcher.toggle" className={classNames( className, - 'truncate pointer-events-auto', - activeRequest == null && 'text-text-subtlest italic', + "truncate pointer-events-auto", + activeRequest == null && "text-text-subtlest italic", )} > {resolvedModelName(activeRequest)} diff --git a/apps/yaak-client/components/RecentWebsocketConnectionsDropdown.tsx b/apps/yaak-client/components/RecentWebsocketConnectionsDropdown.tsx index d2f7a14c..5f3a4f5c 100644 --- a/apps/yaak-client/components/RecentWebsocketConnectionsDropdown.tsx +++ b/apps/yaak-client/components/RecentWebsocketConnectionsDropdown.tsx @@ -1,11 +1,11 @@ -import type { WebsocketConnection } from '@yaakapp-internal/models'; -import { deleteModel, getModel } from '@yaakapp-internal/models'; -import { HStack, Icon } from '@yaakapp-internal/ui'; -import { formatDistanceToNowStrict } from 'date-fns'; -import { deleteWebsocketConnections } from '../commands/deleteWebsocketConnections'; -import { pluralizeCount } from '../lib/pluralize'; -import { Dropdown } from './core/Dropdown'; -import { IconButton } from './core/IconButton'; +import type { WebsocketConnection } from "@yaakapp-internal/models"; +import { deleteModel, getModel } from "@yaakapp-internal/models"; +import { HStack, Icon } from "@yaakapp-internal/ui"; +import { formatDistanceToNowStrict } from "date-fns"; +import { deleteWebsocketConnections } from "../commands/deleteWebsocketConnections"; +import { pluralizeCount } from "../lib/pluralize"; +import { Dropdown } from "./core/Dropdown"; +import { IconButton } from "./core/IconButton"; interface Props { connections: WebsocketConnection[]; @@ -18,20 +18,20 @@ export function RecentWebsocketConnectionsDropdown({ connections, onPinnedConnectionId, }: Props) { - const latestConnectionId = connections[0]?.id ?? 'n/a'; + const latestConnectionId = connections[0]?.id ?? "n/a"; return ( deleteModel(activeConnection), disabled: connections.length === 0, }, { - label: `Clear ${pluralizeCount('Connection', connections.length)}`, + label: `Clear ${pluralizeCount("Connection", connections.length)}`, onSelect: () => { - const request = getModel('websocket_request', activeConnection.requestId); + const request = getModel("websocket_request", activeConnection.requestId); if (request != null) { deleteWebsocketConnections.mutate(request); } @@ -39,11 +39,11 @@ export function RecentWebsocketConnectionsDropdown({ hidden: connections.length <= 1, disabled: connections.length === 0, }, - { type: 'separator', label: 'History' }, + { type: "separator", label: "History" }, ...connections.map((c) => ({ label: ( - {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{' '} + {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{" "} {c.elapsed}ms ), @@ -54,7 +54,7 @@ export function RecentWebsocketConnectionsDropdown({ > { if (workspaces.length === 0 || recentWorkspaces == null) { - console.log('No workspaces found to redirect to. Skipping.', { + console.log("No workspaces found to redirect to. Skipping.", { workspaces, recentWorkspaces, }); return; } - (async () => { - const workspaceId = recentWorkspaces[0] ?? workspaces[0]?.id ?? 'n/a'; - const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? null; - const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? null; - const requestId = (await getRecentRequests(workspaceId))[0] ?? null; - const params = { workspaceId }; - const search = { - cookie_jar_id: cookieJarId, - environment_id: environmentId, - request_id: requestId, - }; + fireAndForget( + (async () => { + const workspaceId = recentWorkspaces[0] ?? workspaces[0]?.id ?? "n/a"; + const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? null; + const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? null; + const requestId = (await getRecentRequests(workspaceId))[0] ?? null; + const params = { workspaceId }; + const search = { + cookie_jar_id: cookieJarId, + environment_id: environmentId, + request_id: requestId, + }; - console.log('Redirecting to workspace', params, search); - await router.navigate({ to: '/workspaces/$workspaceId', params, search }); - })(); + console.log("Redirecting to workspace", params, search); + await router.navigate({ to: "/workspaces/$workspaceId", params, search }); + })(), + ); }, [recentWorkspaces, workspaces, workspaces.length]); return null; diff --git a/apps/yaak-client/components/RequestBodyViewer.tsx b/apps/yaak-client/components/RequestBodyViewer.tsx index ef17f802..a5a77825 100644 --- a/apps/yaak-client/components/RequestBodyViewer.tsx +++ b/apps/yaak-client/components/RequestBodyViewer.tsx @@ -1,20 +1,20 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { lazy, Suspense } from 'react'; -import { useHttpRequestBody } from '../hooks/useHttpRequestBody'; -import { getMimeTypeFromContentType, languageFromContentType } from '../lib/contentType'; -import { LoadingIcon } from '@yaakapp-internal/ui'; -import { EmptyStateText } from './EmptyStateText'; -import { AudioViewer } from './responseViewers/AudioViewer'; -import { CsvViewer } from './responseViewers/CsvViewer'; -import { ImageViewer } from './responseViewers/ImageViewer'; -import { MultipartViewer } from './responseViewers/MultipartViewer'; -import { SvgViewer } from './responseViewers/SvgViewer'; -import { TextViewer } from './responseViewers/TextViewer'; -import { VideoViewer } from './responseViewers/VideoViewer'; -import { WebPageViewer } from './responseViewers/WebPageViewer'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { lazy, Suspense } from "react"; +import { useHttpRequestBody } from "../hooks/useHttpRequestBody"; +import { getMimeTypeFromContentType, languageFromContentType } from "../lib/contentType"; +import { LoadingIcon } from "@yaakapp-internal/ui"; +import { EmptyStateText } from "./EmptyStateText"; +import { AudioViewer } from "./responseViewers/AudioViewer"; +import { CsvViewer } from "./responseViewers/CsvViewer"; +import { ImageViewer } from "./responseViewers/ImageViewer"; +import { MultipartViewer } from "./responseViewers/MultipartViewer"; +import { SvgViewer } from "./responseViewers/SvgViewer"; +import { TextViewer } from "./responseViewers/TextViewer"; +import { VideoViewer } from "./responseViewers/VideoViewer"; +import { WebPageViewer } from "./responseViewers/WebPageViewer"; const PdfViewer = lazy(() => - import('./responseViewers/PdfViewer').then((m) => ({ default: m.PdfViewer })), + import("./responseViewers/PdfViewer").then((m) => ({ default: m.PdfViewer })), ); interface Props { @@ -48,7 +48,7 @@ function RequestBodyViewerInner({ response }: Props) { // Try to detect language from content-type header that was sent const contentTypeHeader = response.requestHeaders.find( - (h) => h.name.toLowerCase() === 'content-type', + (h) => h.name.toLowerCase() === "content-type", ); const contentType = contentTypeHeader?.value ?? null; const mimeType = contentType ? getMimeTypeFromContentType(contentType).essence : null; @@ -56,7 +56,7 @@ function RequestBodyViewerInner({ response }: Props) { // Route to appropriate viewer based on content type if (mimeType?.match(/^multipart/i)) { - const boundary = contentType?.split('boundary=')[1] ?? 'unknown'; + const boundary = contentType?.split("boundary=")[1] ?? "unknown"; // Create a copy because parseMultipart may detach the buffer const bodyCopy = new Uint8Array(body); return ( diff --git a/apps/yaak-client/components/RequestMethodDropdown.tsx b/apps/yaak-client/components/RequestMethodDropdown.tsx index 48af35e6..8754750e 100644 --- a/apps/yaak-client/components/RequestMethodDropdown.tsx +++ b/apps/yaak-client/components/RequestMethodDropdown.tsx @@ -1,14 +1,14 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { memo, useCallback, useMemo } from 'react'; -import { showPrompt } from '../lib/prompt'; -import { Button } from './core/Button'; -import type { DropdownItem } from './core/Dropdown'; -import { HttpMethodTag, HttpMethodTagRaw } from './core/HttpMethodTag'; -import { Icon } from '@yaakapp-internal/ui'; -import type { RadioDropdownItem } from './core/RadioDropdown'; -import { RadioDropdown } from './core/RadioDropdown'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { memo, useCallback, useMemo } from "react"; +import { showPrompt } from "../lib/prompt"; +import { Button } from "./core/Button"; +import type { DropdownItem } from "./core/Dropdown"; +import { HttpMethodTag, HttpMethodTagRaw } from "./core/HttpMethodTag"; +import { Icon } from "@yaakapp-internal/ui"; +import type { RadioDropdownItem } from "./core/RadioDropdown"; +import { RadioDropdown } from "./core/RadioDropdown"; type Props = { request: HttpRequest; @@ -16,14 +16,14 @@ type Props = { }; const radioItems: RadioDropdownItem[] = [ - 'GET', - 'PUT', - 'POST', - 'PATCH', - 'DELETE', - 'OPTIONS', - 'QUERY', - 'HEAD', + "GET", + "PUT", + "POST", + "PATCH", + "DELETE", + "OPTIONS", + "QUERY", + "HEAD", ].map((m) => ({ value: m, label: , @@ -43,17 +43,17 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({ const itemsAfter = useMemo( () => [ { - key: 'custom', - label: 'CUSTOM', + key: "custom", + label: "CUSTOM", leftSlot: , onSelect: async () => { const newMethod = await showPrompt({ - id: 'custom-method', - label: 'Http Method', - title: 'Custom Method', - confirmText: 'Save', - description: 'Enter a custom method name', - placeholder: 'CUSTOM', + id: "custom-method", + label: "Http Method", + title: "Custom Method", + confirmText: "Save", + description: "Enter a custom method name", + placeholder: "CUSTOM", }); if (newMethod == null) return; await handleChange(newMethod); @@ -70,7 +70,7 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({ itemsAfter={itemsAfter} onChange={handleChange} > - diff --git a/apps/yaak-client/components/ResponseCookies.tsx b/apps/yaak-client/components/ResponseCookies.tsx index 5e217a9c..65ecad04 100644 --- a/apps/yaak-client/components/ResponseCookies.tsx +++ b/apps/yaak-client/components/ResponseCookies.tsx @@ -1,11 +1,11 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useMemo } from 'react'; -import type { JSX } from 'react/jsx-runtime'; -import { useHttpResponseEvents } from '../hooks/useHttpResponseEvents'; -import { CountBadge } from './core/CountBadge'; -import { DetailsBanner } from './core/DetailsBanner'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useMemo } from "react"; +import type { JSX } from "react/jsx-runtime"; +import { useHttpResponseEvents } from "../hooks/useHttpResponseEvents"; +import { CountBadge } from "./core/CountBadge"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; interface Props { response: HttpResponse; @@ -26,37 +26,37 @@ interface ParsedCookie { function parseCookieHeader(cookieHeader: string): Array<{ name: string; value: string }> { // Parse "Cookie: name=value; name2=value2" format - return cookieHeader.split(';').map((pair) => { - const [name = '', ...valueParts] = pair.split('='); + return cookieHeader.split(";").map((pair) => { + const [name = "", ...valueParts] = pair.split("="); return { name: name.trim(), - value: valueParts.join('=').trim(), + value: valueParts.join("=").trim(), }; }); } function parseSetCookieHeader(setCookieHeader: string): ParsedCookie { // Parse "Set-Cookie: name=value; Domain=...; Path=..." format - const parts = setCookieHeader.split(';').map((p) => p.trim()); - const [nameValue = '', ...attributes] = parts; - const [name = '', ...valueParts] = nameValue.split('='); + const parts = setCookieHeader.split(";").map((p) => p.trim()); + const [nameValue = "", ...attributes] = parts; + const [name = "", ...valueParts] = nameValue.split("="); const cookie: ParsedCookie = { name: name.trim(), - value: valueParts.join('=').trim(), + value: valueParts.join("=").trim(), }; for (const attr of attributes) { - const [key = '', val] = attr.split('=').map((s) => s.trim()); + const [key = "", val] = attr.split("=").map((s) => s.trim()); const lowerKey = key.toLowerCase(); - if (lowerKey === 'domain') cookie.domain = val; - else if (lowerKey === 'path') cookie.path = val; - else if (lowerKey === 'expires') cookie.expires = val; - else if (lowerKey === 'max-age') cookie.maxAge = val; - else if (lowerKey === 'secure') cookie.secure = true; - else if (lowerKey === 'httponly') cookie.httpOnly = true; - else if (lowerKey === 'samesite') cookie.sameSite = val; + if (lowerKey === "domain") cookie.domain = val; + else if (lowerKey === "path") cookie.path = val; + else if (lowerKey === "expires") cookie.expires = val; + else if (lowerKey === "max-age") cookie.maxAge = val; + else if (lowerKey === "secure") cookie.secure = true; + else if (lowerKey === "httponly") cookie.httpOnly = true; + else if (lowerKey === "samesite") cookie.sameSite = val; } // Detect if cookie is being deleted @@ -94,7 +94,7 @@ export function ResponseCookies({ response }: Props) { const e = event.event; // Cookie headers sent (header_up with name=cookie) - if (e.type === 'header_up' && e.name.toLowerCase() === 'cookie') { + if (e.type === "header_up" && e.name.toLowerCase() === "cookie") { const cookies = parseCookieHeader(e.value); for (const cookie of cookies) { sentMap.set(cookie.name, cookie); @@ -102,7 +102,7 @@ export function ResponseCookies({ response }: Props) { } // Set-Cookie headers received (header_down with name=set-cookie) - if (e.type === 'header_down' && e.name.toLowerCase() === 'set-cookie') { + if (e.type === "header_down" && e.name.toLowerCase() === "set-cookie") { const cookie = parseSetCookieHeader(e.value); receivedMap.set(cookie.name, cookie); } @@ -130,7 +130,7 @@ export function ResponseCookies({ response }: Props) { ) : ( {sentCookies.map((cookie, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: none + // oxlint-disable-next-line react/no-array-index-key {cookie.value} @@ -153,13 +153,13 @@ export function ResponseCookies({ response }: Props) { ) : (
{receivedCookies.map((cookie, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: none + // oxlint-disable-next-line react/no-array-index-key
{cookie.name} diff --git a/apps/yaak-client/components/ResponseHeaders.tsx b/apps/yaak-client/components/ResponseHeaders.tsx index 67f7e665..4f0b64c6 100644 --- a/apps/yaak-client/components/ResponseHeaders.tsx +++ b/apps/yaak-client/components/ResponseHeaders.tsx @@ -1,10 +1,10 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; -import type { HttpResponse } from '@yaakapp-internal/models'; -import { useMemo } from 'react'; -import { CountBadge } from './core/CountBadge'; -import { DetailsBanner } from './core/DetailsBanner'; -import { IconButton } from './core/IconButton'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; +import { openUrl } from "@tauri-apps/plugin-opener"; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { useMemo } from "react"; +import { CountBadge } from "./core/CountBadge"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { IconButton } from "./core/IconButton"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; interface Props { response: HttpResponse; @@ -62,7 +62,7 @@ export function ResponseHeaders({ response }: Props) { ) : ( {requestHeaders.map((h, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: none + // oxlint-disable-next-line react/no-array-index-key {h.value} @@ -84,7 +84,7 @@ export function ResponseHeaders({ response }: Props) { ) : ( {responseHeaders.map((h, i) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: none + // oxlint-disable-next-line react/no-array-index-key {h.value} diff --git a/apps/yaak-client/components/ResponseInfo.tsx b/apps/yaak-client/components/ResponseInfo.tsx index dcb68816..865ce6fa 100644 --- a/apps/yaak-client/components/ResponseInfo.tsx +++ b/apps/yaak-client/components/ResponseInfo.tsx @@ -1,7 +1,7 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; -import type { HttpResponse } from '@yaakapp-internal/models'; -import { IconButton } from './core/IconButton'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; +import { openUrl } from "@tauri-apps/plugin-opener"; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { IconButton } from "./core/IconButton"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; interface Props { response: HttpResponse; diff --git a/apps/yaak-client/components/RouteError.tsx b/apps/yaak-client/components/RouteError.tsx index a843c89a..0e6ba1a7 100644 --- a/apps/yaak-client/components/RouteError.tsx +++ b/apps/yaak-client/components/RouteError.tsx @@ -1,13 +1,13 @@ -import { Button, FormattedError, Heading, VStack } from '@yaakapp-internal/ui'; -import { DetailsBanner } from './core/DetailsBanner'; +import { Button, FormattedError, Heading, VStack } from "@yaakapp-internal/ui"; +import { DetailsBanner } from "./core/DetailsBanner"; export default function RouteError({ error }: { error: unknown }) { - console.log('Error', error); + console.log("Error", error); const stringified = JSON.stringify(error); // biome-ignore lint/suspicious/noExplicitAny: none const message = (error as any).message ?? stringified; const stack = - typeof error === 'object' && error != null && 'stack' in error ? String(error.stack) : null; + typeof error === "object" && error != null && "stack" in error ? String(error.stack) : null; return (
@@ -28,7 +28,7 @@ export default function RouteError({ error }: { error: unknown }) { @@ -270,8 +270,8 @@ TemplateFunctionDialog.show = ( const initialTokens = parseTemplate(tagValue); showDialog({ id: `template-function-${Math.random()}`, // Allow multiple at once - size: 'md', - className: 'h-[60rem]', + size: "md", + className: "h-[60rem]", noPadding: true, title: {fn.name}(…), description: fn.description, diff --git a/apps/yaak-client/components/Toasts.tsx b/apps/yaak-client/components/Toasts.tsx index fe499f31..046f1919 100644 --- a/apps/yaak-client/components/Toasts.tsx +++ b/apps/yaak-client/components/Toasts.tsx @@ -1,18 +1,18 @@ -import { useAtomValue } from 'jotai'; -import { AnimatePresence } from 'motion/react'; -import type { ReactNode } from 'react'; -import { hideToast, toastsAtom } from '../lib/toast'; -import { Toast, type ToastProps } from './core/Toast'; -import { ErrorBoundary } from './ErrorBoundary'; -import { Portal } from '@yaakapp-internal/ui'; +import { useAtomValue } from "jotai"; +import { AnimatePresence } from "motion/react"; +import type { ReactNode } from "react"; +import { hideToast, toastsAtom } from "../lib/toast"; +import { Toast, type ToastProps } from "./core/Toast"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { Portal } from "@yaakapp-internal/ui"; export type ToastInstance = { id: string; uniqueKey: string; message: ReactNode; timeout: 3000 | 5000 | 8000 | (number & {}) | null; - onClose?: ToastProps['onClose']; -} & Omit; + onClose?: ToastProps["onClose"]; +} & Omit; export const Toasts = () => { const toasts = useAtomValue(toastsAtom); diff --git a/apps/yaak-client/components/UrlBar.tsx b/apps/yaak-client/components/UrlBar.tsx index e53b6681..98bfd242 100644 --- a/apps/yaak-client/components/UrlBar.tsx +++ b/apps/yaak-client/components/UrlBar.tsx @@ -1,29 +1,29 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import type { IconProps } from '@yaakapp-internal/ui'; -import { HStack } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import type { FormEvent, ReactNode } from 'react'; -import { memo, useCallback, useRef, useState } from 'react'; -import { useHotKey } from '../hooks/useHotKey'; -import { IconButton } from './core/IconButton'; -import type { InputHandle, InputProps } from './core/Input'; -import { Input } from './core/Input'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import type { IconProps } from "@yaakapp-internal/ui"; +import { HStack } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import type { FormEvent, ReactNode } from "react"; +import { memo, useCallback, useRef, useState } from "react"; +import { useHotKey } from "../hooks/useHotKey"; +import { IconButton } from "./core/IconButton"; +import type { InputHandle, InputProps } from "./core/Input"; +import { Input } from "./core/Input"; -type Props = Pick & { +type Props = Pick & { className?: string; placeholder: string; onSend: () => void; onUrlChange: (url: string) => void; onPaste?: (v: string) => void; - onPasteOverwrite?: InputProps['onPasteOverwrite']; + onPasteOverwrite?: InputProps["onPasteOverwrite"]; onCancel: () => void; - submitIcon?: IconProps['icon'] | null; + submitIcon?: IconProps["icon"] | null; isLoading: boolean; forceUpdateKey: string; rightSlot?: ReactNode; leftSlot?: ReactNode; - autocomplete?: InputProps['autocomplete']; - stateKey: InputProps['stateKey']; + autocomplete?: InputProps["autocomplete"]; + stateKey: InputProps["stateKey"]; }; export const UrlBar = memo(function UrlBar({ @@ -36,7 +36,7 @@ export const UrlBar = memo(function UrlBar({ onCancel, onPaste, onPasteOverwrite, - submitIcon = 'send_horizontal', + submitIcon = "send_horizontal", autocomplete, leftSlot, rightSlot, @@ -50,7 +50,7 @@ export const UrlBar = memo(function UrlBar({ inputRef.current = h; }, []); - useHotKey('url_bar.focus', () => { + useHotKey("url_bar.focus", () => { inputRef.current?.selectAll(); }); @@ -61,7 +61,7 @@ export const UrlBar = memo(function UrlBar({ }; return ( - + { // Prevent the button from taking focus diff --git a/apps/yaak-client/components/UrlParameterEditor.tsx b/apps/yaak-client/components/UrlParameterEditor.tsx index d3394d0f..c08607ea 100644 --- a/apps/yaak-client/components/UrlParameterEditor.tsx +++ b/apps/yaak-client/components/UrlParameterEditor.tsx @@ -1,15 +1,15 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { VStack } from '@yaakapp-internal/ui'; -import { useCallback, useRef } from 'react'; -import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor'; -import type { PairEditorHandle, PairEditorProps } from './core/PairEditor'; -import { PairOrBulkEditor } from './core/PairOrBulkEditor'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { VStack } from "@yaakapp-internal/ui"; +import { useCallback, useRef } from "react"; +import { useRequestEditor, useRequestEditorEvent } from "../hooks/useRequestEditor"; +import type { PairEditorHandle, PairEditorProps } from "./core/PairEditor"; +import { PairOrBulkEditor } from "./core/PairOrBulkEditor"; type Props = { forceUpdateKey: string; - pairs: HttpRequest['headers']; - stateKey: PairEditorProps['stateKey']; - onChange: (headers: HttpRequest['urlParameters']) => void; + pairs: HttpRequest["headers"]; + stateKey: PairEditorProps["stateKey"]; + onChange: (headers: HttpRequest["urlParameters"]) => void; }; export function UrlParametersEditor({ pairs, forceUpdateKey, onChange, stateKey }: Props) { @@ -21,7 +21,7 @@ export function UrlParametersEditor({ pairs, forceUpdateKey, onChange, stateKey const [{ urlParametersKey }] = useRequestEditor(); useRequestEditorEvent( - 'request_params.focus_value', + "request_params.focus_value", (name) => { const pair = pairs.find((p) => p.name === name); if (pair?.id != null) { diff --git a/apps/yaak-client/components/WebsocketRequestLayout.tsx b/apps/yaak-client/components/WebsocketRequestLayout.tsx index 6d11a8ef..16e6b3e2 100644 --- a/apps/yaak-client/components/WebsocketRequestLayout.tsx +++ b/apps/yaak-client/components/WebsocketRequestLayout.tsx @@ -1,13 +1,13 @@ -import type { WebsocketRequest } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import type { CSSProperties } from 'react'; +import type { WebsocketRequest } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import type { CSSProperties } from "react"; -import { SplitLayout } from '@yaakapp-internal/ui'; -import { activeWorkspaceAtom } from '../hooks/useActiveWorkspace'; -import { workspaceLayoutAtom } from '../lib/atoms'; -import { WebsocketRequestPane } from './WebsocketRequestPane'; -import { WebsocketResponsePane } from './WebsocketResponsePane'; +import { SplitLayout } from "@yaakapp-internal/ui"; +import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace"; +import { workspaceLayoutAtom } from "../lib/atoms"; +import { WebsocketRequestPane } from "./WebsocketRequestPane"; +import { WebsocketResponsePane } from "./WebsocketResponsePane"; interface Props { activeRequest: WebsocketRequest; @@ -17,7 +17,7 @@ interface Props { export function WebsocketRequestLayout({ activeRequest, style }: Props) { const workspaceLayout = useAtomValue(workspaceLayoutAtom); const activeWorkspace = useAtomValue(activeWorkspaceAtom); - const wsId = activeWorkspace?.id ?? 'n/a'; + const wsId = activeWorkspace?.id ?? "n/a"; return ( )} secondSlot={({ style }) => (
diff --git a/apps/yaak-client/components/WebsocketRequestPane.tsx b/apps/yaak-client/components/WebsocketRequestPane.tsx index 3627c20c..9dd86e68 100644 --- a/apps/yaak-client/components/WebsocketRequestPane.tsx +++ b/apps/yaak-client/components/WebsocketRequestPane.tsx @@ -1,41 +1,41 @@ -import type { WebsocketRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import { closeWebsocket, connectWebsocket, sendWebsocket } from '@yaakapp-internal/ws'; -import classNames from 'classnames'; -import { atom, useAtomValue } from 'jotai'; -import type { CSSProperties } from 'react'; -import { useCallback, useMemo, useRef } from 'react'; -import { getActiveCookieJar } from '../hooks/useActiveCookieJar'; -import { getActiveEnvironment } from '../hooks/useActiveEnvironment'; -import { activeRequestIdAtom } from '../hooks/useActiveRequestId'; -import { allRequestsAtom } from '../hooks/useAllRequests'; -import { useAuthTab } from '../hooks/useAuthTab'; -import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse'; -import { useHeadersTab } from '../hooks/useHeadersTab'; -import { useInheritedHeaders } from '../hooks/useInheritedHeaders'; -import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse'; -import { activeWebsocketConnectionAtom } from '../hooks/usePinnedWebsocketConnection'; -import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor'; -import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; -import { deepEqualAtom } from '../lib/atoms'; -import { languageFromContentType } from '../lib/contentType'; -import { generateId } from '../lib/generateId'; -import { prepareImportQuerystring } from '../lib/prepareImportQuerystring'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { CountBadge } from './core/CountBadge'; -import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; -import { Editor } from './core/Editor/LazyEditor'; -import { IconButton } from './core/IconButton'; -import type { Pair } from './core/PairEditor'; -import { PlainInput } from './core/PlainInput'; -import type { TabItem, TabsRef } from './core/Tabs/Tabs'; -import { setActiveTab, TabContent, Tabs } from './core/Tabs/Tabs'; -import { HeadersEditor } from './HeadersEditor'; -import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; -import { MarkdownEditor } from './MarkdownEditor'; -import { UrlBar } from './UrlBar'; -import { UrlParametersEditor } from './UrlParameterEditor'; +import type { WebsocketRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import { closeWebsocket, connectWebsocket, sendWebsocket } from "@yaakapp-internal/ws"; +import classNames from "classnames"; +import { atom, useAtomValue } from "jotai"; +import type { CSSProperties } from "react"; +import { useCallback, useMemo, useRef } from "react"; +import { getActiveCookieJar } from "../hooks/useActiveCookieJar"; +import { getActiveEnvironment } from "../hooks/useActiveEnvironment"; +import { activeRequestIdAtom } from "../hooks/useActiveRequestId"; +import { allRequestsAtom } from "../hooks/useAllRequests"; +import { useAuthTab } from "../hooks/useAuthTab"; +import { useCancelHttpResponse } from "../hooks/useCancelHttpResponse"; +import { useHeadersTab } from "../hooks/useHeadersTab"; +import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; +import { usePinnedHttpResponse } from "../hooks/usePinnedHttpResponse"; +import { activeWebsocketConnectionAtom } from "../hooks/usePinnedWebsocketConnection"; +import { useRequestEditor, useRequestEditorEvent } from "../hooks/useRequestEditor"; +import { useRequestUpdateKey } from "../hooks/useRequestUpdateKey"; +import { deepEqualAtom } from "../lib/atoms"; +import { languageFromContentType } from "../lib/contentType"; +import { generateId } from "../lib/generateId"; +import { prepareImportQuerystring } from "../lib/prepareImportQuerystring"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { CountBadge } from "./core/CountBadge"; +import type { GenericCompletionConfig } from "./core/Editor/genericCompletion"; +import { Editor } from "./core/Editor/LazyEditor"; +import { IconButton } from "./core/IconButton"; +import type { Pair } from "./core/PairEditor"; +import { PlainInput } from "./core/PlainInput"; +import type { TabItem, TabsRef } from "./core/Tabs/Tabs"; +import { setActiveTab, TabContent, Tabs } from "./core/Tabs/Tabs"; +import { HeadersEditor } from "./HeadersEditor"; +import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; +import { MarkdownEditor } from "./MarkdownEditor"; +import { UrlBar } from "./UrlBar"; +import { UrlParametersEditor } from "./UrlParameterEditor"; interface Props { style: CSSProperties; @@ -44,19 +44,19 @@ interface Props { activeRequest: WebsocketRequest; } -const TAB_MESSAGE = 'message'; -const TAB_PARAMS = 'params'; -const TAB_HEADERS = 'headers'; -const TAB_AUTH = 'auth'; -const TAB_DESCRIPTION = 'description'; -const TABS_STORAGE_KEY = 'websocket_request_tabs'; +const TAB_MESSAGE = "message"; +const TAB_PARAMS = "params"; +const TAB_HEADERS = "headers"; +const TAB_AUTH = "auth"; +const TAB_DESCRIPTION = "description"; +const TABS_STORAGE_KEY = "websocket_request_tabs"; const nonActiveRequestUrlsAtom = atom((get) => { const activeRequestId = get(activeRequestIdAtom); const requests = get(allRequestsAtom); return requests .filter((r) => r.id !== activeRequestId) - .map((r): GenericCompletionOption => ({ type: 'constant', label: r.url })); + .map((r): GenericCompletionOption => ({ type: "constant", label: r.url })); }); const memoNotActiveRequestUrlsAtom = deepEqualAtom(nonActiveRequestUrlsAtom); @@ -71,13 +71,17 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque const inheritedHeaders = useInheritedHeaders(activeRequest); // Listen for event to focus the params tab (e.g., when clicking a :param in the URL) - useRequestEditorEvent('request_pane.focus_tab', () => { - tabsRef.current?.setActiveTab(TAB_PARAMS); - }, []); + useRequestEditorEvent( + "request_pane.focus_tab", + () => { + tabsRef.current?.setActiveTab(TAB_PARAMS); + }, + [], + ); const { urlParameterPairs, urlParametersKey } = useMemo(() => { const placeholderNames = Array.from(activeRequest.url.matchAll(/\/(:[^/]+)/g)).map( - (m) => m[1] ?? '', + (m) => m[1] ?? "", ); const nonEmptyParameters = activeRequest.urlParameters.filter((p) => p.name || p.value); const items: Pair[] = [...nonEmptyParameters]; @@ -86,28 +90,28 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque if (item) { item.readOnlyName = true; } else { - items.push({ name, value: '', enabled: true, readOnlyName: true, id: generateId() }); + items.push({ name, value: "", enabled: true, readOnlyName: true, id: generateId() }); } } - return { urlParameterPairs: items, urlParametersKey: placeholderNames.join(',') }; + return { urlParameterPairs: items, urlParametersKey: placeholderNames.join(",") }; }, [activeRequest.url, activeRequest.urlParameters]); const tabs = useMemo(() => { return [ { value: TAB_MESSAGE, - label: 'Message', + label: "Message", } as TabItem, { value: TAB_PARAMS, rightSlot: , - label: 'Params', + label: "Params", }, ...headersTab, ...authTab, { value: TAB_DESCRIPTION, - label: 'Info', + label: "Info", }, ]; }, [authTab, headersTab, urlParameterPairs.length]); @@ -125,8 +129,8 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque autocompleteUrls.length > 0 ? autocompleteUrls : [ - { label: 'http://', type: 'constant' }, - { label: 'https://', type: 'constant' }, + { label: "http://", type: "constant" }, + { label: "https://", type: "constant" }, ], }), [autocompleteUrls], @@ -184,12 +188,12 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque const messageLanguage = languageFromContentType(null, activeRequest.message); - const isLoading = connection !== null && connection.state !== 'closed'; + const isLoading = connection !== null && connection.state !== "closed"; return (
{activeRequest && ( <> @@ -198,7 +202,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque stateKey={`url.${activeRequest.id}`} key={forceUpdateKey + urlKey} url={activeRequest.url} - submitIcon={isLoading ? 'send_horizontal' : 'arrow_up_down'} + submitIcon={isLoading ? "send_horizontal" : "arrow_up_down"} rightSlot={ isLoading && (
patchModel(activeRequest, { message })} diff --git a/apps/yaak-client/components/WebsocketResponsePane.tsx b/apps/yaak-client/components/WebsocketResponsePane.tsx index 66f263be..e3284db2 100644 --- a/apps/yaak-client/components/WebsocketResponsePane.tsx +++ b/apps/yaak-client/components/WebsocketResponsePane.tsx @@ -1,26 +1,26 @@ -import type { WebsocketEvent, WebsocketRequest } from '@yaakapp-internal/models'; -import { HStack, Icon, LoadingIcon, VStack } from '@yaakapp-internal/ui'; -import { hexy } from 'hexy'; -import { useAtomValue } from 'jotai'; -import { useMemo, useState } from 'react'; -import { useFormatText } from '../hooks/useFormatText'; +import type { WebsocketEvent, WebsocketRequest } from "@yaakapp-internal/models"; +import { HStack, Icon, LoadingIcon, VStack } from "@yaakapp-internal/ui"; +import { hexy } from "hexy"; +import { useAtomValue } from "jotai"; +import { useMemo, useState } from "react"; +import { useFormatText } from "../hooks/useFormatText"; import { activeWebsocketConnectionAtom, activeWebsocketConnectionsAtom, setPinnedWebsocketConnectionId, useWebsocketEvents, -} from '../hooks/usePinnedWebsocketConnection'; -import { useStateWithDeps } from '../hooks/useStateWithDeps'; -import { languageFromContentType } from '../lib/contentType'; -import { Button } from './core/Button'; -import { Editor } from './core/Editor/LazyEditor'; -import { type EventDetailAction, EventDetailHeader, EventViewer } from './core/EventViewer'; -import { EventViewerRow } from './core/EventViewerRow'; -import { HotkeyList } from './core/HotkeyList'; -import { WebsocketStatusTag } from './core/WebsocketStatusTag'; -import { EmptyStateText } from './EmptyStateText'; -import { ErrorBoundary } from './ErrorBoundary'; -import { RecentWebsocketConnectionsDropdown } from './RecentWebsocketConnectionsDropdown'; +} from "../hooks/usePinnedWebsocketConnection"; +import { useStateWithDeps } from "../hooks/useStateWithDeps"; +import { languageFromContentType } from "../lib/contentType"; +import { Button } from "./core/Button"; +import { Editor } from "./core/Editor/LazyEditor"; +import { type EventDetailAction, EventDetailHeader, EventViewer } from "./core/EventViewer"; +import { EventViewerRow } from "./core/EventViewerRow"; +import { HotkeyList } from "./core/HotkeyList"; +import { WebsocketStatusTag } from "./core/WebsocketStatusTag"; +import { EmptyStateText } from "./EmptyStateText"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { RecentWebsocketConnectionsDropdown } from "./RecentWebsocketConnectionsDropdown"; interface Props { activeRequest: WebsocketRequest; @@ -37,14 +37,14 @@ export function WebsocketResponsePane({ activeRequest }: Props) { if (activeConnection == null) { return ( - + ); } const header = ( - {activeConnection.state !== 'closed' && ( + {activeConnection.state !== "closed" && ( )} @@ -76,7 +76,7 @@ export function WebsocketResponsePane({ activeRequest }: Props) { renderDetail={({ event, index, onClose }) => ( setHexDumps({ ...hexDumps, [index]: v })} showLarge={showLarge} showingLarge={showingLarge} @@ -101,25 +101,25 @@ function WebsocketEventRow({ }) { const { message: messageBytes, isServer, messageType } = event; const message = messageBytes - ? new TextDecoder('utf-8').decode(Uint8Array.from(messageBytes)) - : ''; + ? new TextDecoder("utf-8").decode(Uint8Array.from(messageBytes)) + : ""; const iconColor = - messageType === 'close' || messageType === 'open' ? 'secondary' : isServer ? 'info' : 'primary'; + messageType === "close" || messageType === "open" ? "secondary" : isServer ? "info" : "primary"; const icon = - messageType === 'close' || messageType === 'open' - ? 'info' + messageType === "close" || messageType === "open" + ? "info" : isServer - ? 'arrow_big_down_dash' - : 'arrow_big_up_dash'; + ? "arrow_big_down_dash" + : "arrow_big_up_dash"; const content = - messageType === 'close' ? ( - 'Disconnected from server' - ) : messageType === 'open' ? ( - 'Connected to server' - ) : message === '' ? ( + messageType === "close" ? ( + "Disconnected from server" + ) : messageType === "open" ? ( + "Connected to server" + ) : message === "" ? ( No content ) : ( {message.slice(0, 1000)} @@ -157,27 +157,27 @@ function WebsocketEventDetail({ }) { const message = useMemo(() => { if (hexDump) { - return event.message ? hexy(event.message) : ''; + return event.message ? hexy(event.message) : ""; } - return event.message ? new TextDecoder('utf-8').decode(Uint8Array.from(event.message)) : ''; + return event.message ? new TextDecoder("utf-8").decode(Uint8Array.from(event.message)) : ""; }, [event.message, hexDump]); const language = languageFromContentType(null, message); const formattedMessage = useFormatText({ language, text: message, pretty: true }); const title = - event.messageType === 'close' - ? 'Connection Closed' - : event.messageType === 'open' - ? 'Connection Open' - : `Message ${event.isServer ? 'Received' : 'Sent'}`; + event.messageType === "close" + ? "Connection Closed" + : event.messageType === "open" + ? "Connection Open" + : `Message ${event.isServer ? "Received" : "Sent"}`; const actions: EventDetailAction[] = - message !== '' + message !== "" ? [ { - key: 'toggle-hexdump', - label: hexDump ? 'Show Message' : 'Show Hexdump', + key: "toggle-hexdump", + label: hexDump ? "Show Message" : "Show Hexdump", onClick: () => setHexDump(!hexDump), }, ] @@ -218,7 +218,7 @@ function WebsocketEventDetail({ ) : ( ; } - if (activeRequest?.model === 'websocket_request') { + if (activeRequest?.model === "websocket_request") { return ; } - if (activeRequest?.model === 'http_request') { + if (activeRequest?.model === "http_request") { return ; } if (activeFolder != null) { @@ -183,7 +183,7 @@ function WorkspaceBody() { return ( ); diff --git a/apps/yaak-client/components/WorkspaceEncryptionSetting.tsx b/apps/yaak-client/components/WorkspaceEncryptionSetting.tsx index 1740dec0..a1571827 100644 --- a/apps/yaak-client/components/WorkspaceEncryptionSetting.tsx +++ b/apps/yaak-client/components/WorkspaceEncryptionSetting.tsx @@ -3,27 +3,27 @@ import { enableEncryption, revealWorkspaceKey, setWorkspaceKey, -} from '@yaakapp-internal/crypto'; -import type { WorkspaceMeta } from '@yaakapp-internal/models'; -import { Banner, HStack, VStack } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import { useEffect, useState } from 'react'; -import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { useStateWithDeps } from '../hooks/useStateWithDeps'; -import { showConfirm } from '../lib/confirm'; -import { CopyIconButton } from './CopyIconButton'; -import type { ButtonProps } from './core/Button'; -import { Button } from './core/Button'; -import { IconButton } from './core/IconButton'; -import { IconTooltip } from './core/IconTooltip'; -import { Label } from './core/Label'; -import { PlainInput } from './core/PlainInput'; -import { EncryptionHelp } from './EncryptionHelp'; +} from "@yaakapp-internal/crypto"; +import type { WorkspaceMeta } from "@yaakapp-internal/models"; +import { Banner, HStack, VStack } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; +import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { useStateWithDeps } from "../hooks/useStateWithDeps"; +import { showConfirm } from "../lib/confirm"; +import { CopyIconButton } from "./CopyIconButton"; +import type { ButtonProps } from "./core/Button"; +import { Button } from "./core/Button"; +import { IconButton } from "./core/IconButton"; +import { IconTooltip } from "./core/IconTooltip"; +import { Label } from "./core/Label"; +import { PlainInput } from "./core/PlainInput"; +import { EncryptionHelp } from "./EncryptionHelp"; interface Props { - size?: ButtonProps['size']; + size?: ButtonProps["size"]; expanded?: boolean; onDone?: () => void; onEnabledEncryption?: () => void; @@ -118,7 +118,7 @@ export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEn
))}
- {selectedColor === 'custom' && ( + {selectedColor === "custom" && ( <> color.match(/#[0-9a-fA-F]{6}$/) !== null} /> diff --git a/apps/yaak-client/components/core/Confirm.tsx b/apps/yaak-client/components/core/Confirm.tsx index a7c0b1f5..bc464900 100644 --- a/apps/yaak-client/components/core/Confirm.tsx +++ b/apps/yaak-client/components/core/Confirm.tsx @@ -1,10 +1,10 @@ -import type { Color } from '@yaakapp-internal/plugins'; -import { HStack } from '@yaakapp-internal/ui'; -import type { FormEvent } from 'react'; -import { useState } from 'react'; -import { CopyIconButton } from '../CopyIconButton'; -import { Button } from './Button'; -import { PlainInput } from './PlainInput'; +import type { Color } from "@yaakapp-internal/plugins"; +import { HStack } from "@yaakapp-internal/ui"; +import type { FormEvent } from "react"; +import { useState } from "react"; +import { CopyIconButton } from "../CopyIconButton"; +import { Button } from "./Button"; +import { PlainInput } from "./PlainInput"; export interface ConfirmProps { onHide: () => void; @@ -19,9 +19,9 @@ export function Confirm({ onResult, confirmText, requireTyping, - color = 'primary', + color = "primary", }: ConfirmProps) { - const [confirm, setConfirm] = useState(''); + const [confirm, setConfirm] = useState(""); const handleHide = () => { onResult(false); onHide(); @@ -63,7 +63,7 @@ export function Confirm({ )} ); } interface MenuItemHotKeyProps { action: HotkeyAction | undefined; - onSelect: MenuItemProps['onSelect']; - item: MenuItemProps['item']; + onSelect: MenuItemProps["onSelect"]; + item: MenuItemProps["item"]; } function MenuItemHotKey({ action, onSelect, item }: MenuItemHotKeyProps) { diff --git a/apps/yaak-client/components/core/Editor/BetterMatchDecorator.ts b/apps/yaak-client/components/core/Editor/BetterMatchDecorator.ts index 5a6e963d..ff9ac078 100644 --- a/apps/yaak-client/components/core/Editor/BetterMatchDecorator.ts +++ b/apps/yaak-client/components/core/Editor/BetterMatchDecorator.ts @@ -1,4 +1,4 @@ -import { type DecorationSet, MatchDecorator, type ViewUpdate } from '@codemirror/view'; +import { type DecorationSet, MatchDecorator, type ViewUpdate } from "@codemirror/view"; /** * This is a custom MatchDecorator that will not decorate a match if the selection is inside it diff --git a/apps/yaak-client/components/core/Editor/DiffViewer.tsx b/apps/yaak-client/components/core/Editor/DiffViewer.tsx index d8d4dfb9..f36f28ee 100644 --- a/apps/yaak-client/components/core/Editor/DiffViewer.tsx +++ b/apps/yaak-client/components/core/Editor/DiffViewer.tsx @@ -1,11 +1,11 @@ -import { yaml } from '@codemirror/lang-yaml'; -import { syntaxHighlighting } from '@codemirror/language'; -import { MergeView } from '@codemirror/merge'; -import { EditorView } from '@codemirror/view'; -import classNames from 'classnames'; -import { useEffect, useRef } from 'react'; -import './DiffViewer.css'; -import { readonlyExtensions, syntaxHighlightStyle } from './extensions'; +import { yaml } from "@codemirror/lang-yaml"; +import { syntaxHighlighting } from "@codemirror/language"; +import { MergeView } from "@codemirror/merge"; +import { EditorView } from "@codemirror/view"; +import classNames from "classnames"; +import { useEffect, useRef } from "react"; +import "./DiffViewer.css"; +import { readonlyExtensions, syntaxHighlightStyle } from "./extensions"; interface Props { /** Original/previous version (left side) */ @@ -45,7 +45,7 @@ export function DiffViewer({ original, modified, className }: Props) { collapseUnchanged: { margin: 2, minSize: 3 }, highlightChanges: false, gutter: true, - orientation: 'a-b', + orientation: "a-b", revertControls: undefined, }); @@ -58,7 +58,7 @@ export function DiffViewer({ original, modified, className }: Props) { return (
); } diff --git a/apps/yaak-client/components/core/Editor/Editor.css b/apps/yaak-client/components/core/Editor/Editor.css index b7183b10..6b80aa03 100644 --- a/apps/yaak-client/components/core/Editor/Editor.css +++ b/apps/yaak-client/components/core/Editor/Editor.css @@ -438,7 +438,7 @@ } input.cm-textfield { - @apply cursor-text; + @apply cursor-text; } .cm-search label { diff --git a/apps/yaak-client/components/core/Editor/Editor.tsx b/apps/yaak-client/components/core/Editor/Editor.tsx index d435ad69..b3ee5d3b 100644 --- a/apps/yaak-client/components/core/Editor/Editor.tsx +++ b/apps/yaak-client/components/core/Editor/Editor.tsx @@ -1,22 +1,22 @@ -import { startCompletion } from '@codemirror/autocomplete'; -import { defaultKeymap, historyField, indentWithTab } from '@codemirror/commands'; -import { foldState, forceParsing } from '@codemirror/language'; -import type { EditorStateConfig, Extension } from '@codemirror/state'; -import { Compartment, EditorState } from '@codemirror/state'; -import { EditorView, keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view'; -import { emacs } from '@replit/codemirror-emacs'; -import { vim } from '@replit/codemirror-vim'; +import { startCompletion } from "@codemirror/autocomplete"; +import { defaultKeymap, historyField, indentWithTab } from "@codemirror/commands"; +import { foldState, forceParsing } from "@codemirror/language"; +import type { EditorStateConfig, Extension } from "@codemirror/state"; +import { Compartment, EditorState } from "@codemirror/state"; +import { EditorView, keymap, placeholder as placeholderExt, tooltips } from "@codemirror/view"; +import { emacs } from "@replit/codemirror-emacs"; +import { vim } from "@replit/codemirror-vim"; -import { vscodeKeymap } from '@replit/codemirror-vscode-keymap'; -import type { EditorKeymap } from '@yaakapp-internal/models'; -import { settingsAtom } from '@yaakapp-internal/models'; -import type { EditorLanguage, TemplateFunction } from '@yaakapp-internal/plugins'; -import { HStack } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import type { GraphQLSchema } from 'graphql'; -import { useAtomValue } from 'jotai'; -import { md5 } from 'js-md5'; -import type { ReactNode, RefObject } from 'react'; +import { vscodeKeymap } from "@replit/codemirror-vscode-keymap"; +import type { EditorKeymap } from "@yaakapp-internal/models"; +import { settingsAtom } from "@yaakapp-internal/models"; +import type { EditorLanguage, TemplateFunction } from "@yaakapp-internal/plugins"; +import { HStack } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import type { GraphQLSchema } from "graphql"; +import { useAtomValue } from "jotai"; +import { md5 } from "js-md5"; +import type { ReactNode, RefObject } from "react"; import { Children, cloneElement, @@ -26,31 +26,31 @@ import { useLayoutEffect, useMemo, useRef, -} from 'react'; -import { activeEnvironmentAtom } from '../../../hooks/useActiveEnvironment'; -import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables'; -import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables'; -import { eventMatchesHotkey } from '../../../hooks/useHotKey'; -import { useRequestEditor } from '../../../hooks/useRequestEditor'; -import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplateFunctions'; -import { editEnvironment } from '../../../lib/editEnvironment'; -import { tryFormatJson, tryFormatXml } from '../../../lib/formatters'; -import { jotaiStore } from '../../../lib/jotai'; -import { withEncryptionEnabled } from '../../../lib/setupOrConfigureEncryption'; -import { TemplateFunctionDialog } from '../../TemplateFunctionDialog'; -import { IconButton } from '../IconButton'; -import './Editor.css'; +} from "react"; +import { activeEnvironmentAtom } from "../../../hooks/useActiveEnvironment"; +import type { WrappedEnvironmentVariable } from "../../../hooks/useEnvironmentVariables"; +import { useEnvironmentVariables } from "../../../hooks/useEnvironmentVariables"; +import { eventMatchesHotkey } from "../../../hooks/useHotKey"; +import { useRequestEditor } from "../../../hooks/useRequestEditor"; +import { useTemplateFunctionCompletionOptions } from "../../../hooks/useTemplateFunctions"; +import { editEnvironment } from "../../../lib/editEnvironment"; +import { tryFormatJson, tryFormatXml } from "../../../lib/formatters"; +import { jotaiStore } from "../../../lib/jotai"; +import { withEncryptionEnabled } from "../../../lib/setupOrConfigureEncryption"; +import { TemplateFunctionDialog } from "../../TemplateFunctionDialog"; +import { IconButton } from "../IconButton"; +import "./Editor.css"; import { baseExtensions, getLanguageExtension, multiLineExtensions, readonlyExtensions, -} from './extensions'; -import type { GenericCompletionConfig } from './genericCompletion'; -import { singleLineExtensions } from './singleLine'; +} from "./extensions"; +import type { GenericCompletionConfig } from "./genericCompletion"; +import { singleLineExtensions } from "./singleLine"; // VSCode's Tab actions mess with the single-line editor tab actions, so remove it. -const vsCodeWithoutTab = vscodeKeymap.filter((k) => k.key !== 'Tab'); +const vsCodeWithoutTab = vscodeKeymap.filter((k) => k.key !== "Tab"); const keymapExtensions: Record = { vim: vim(), @@ -74,10 +74,10 @@ export interface EditorProps { forcedEnvironmentId?: string; forceUpdateKey?: string | number; format?: (v: string) => Promise; - heightMode?: 'auto' | 'full'; + heightMode?: "auto" | "full"; hideGutter?: boolean; id?: string; - language?: EditorLanguage | 'pairs' | 'url' | 'timeline' | null; + language?: EditorLanguage | "pairs" | "url" | "timeline" | null; lintExtension?: Extension; graphQLSchema?: GraphQLSchema | null; onBlur?: () => void; @@ -92,7 +92,7 @@ export interface EditorProps { containerOnly?: boolean; stateKey: string | null; tooltipContainer?: HTMLElement; - type?: 'text' | 'password'; + type?: "text" | "password"; wrapLines?: boolean; setRef?: (view: EditorView | null) => void; } @@ -147,7 +147,7 @@ function EditorInner({ const useTemplating = !!(autocompleteFunctions || autocompleteVariables || autocomplete); const environmentVariables = useMemo(() => { if (!autocompleteVariables) return emptyVariables; - return typeof autocompleteVariables === 'function' + return typeof autocompleteVariables === "function" ? allEnvironmentVariables.filter(autocompleteVariables) : allEnvironmentVariables; }, [allEnvironmentVariables, autocompleteVariables]); @@ -163,18 +163,18 @@ function EditorInner({ if ( singleLine || language == null || - language === 'text' || - language === 'url' || - language === 'pairs' + language === "text" || + language === "url" || + language === "pairs" ) { disableTabIndent = true; } if (format == null && !readOnly) { format = - language === 'json' + language === "json" ? tryFormatJson - : language === 'xml' || language === 'html' + : language === "xml" || language === "html" ? tryFormatXml : undefined; } @@ -182,37 +182,37 @@ function EditorInner({ const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null); // Use ref so we can update the handler without re-initializing the editor - const handleChange = useRef(onChange); + const handleChange = useRef(onChange); useEffect(() => { handleChange.current = onChange; }, [onChange]); // Use ref so we can update the handler without re-initializing the editor - const handlePaste = useRef(onPaste); + const handlePaste = useRef(onPaste); useEffect(() => { handlePaste.current = onPaste; }, [onPaste]); // Use ref so we can update the handler without re-initializing the editor - const handlePasteOverwrite = useRef(onPasteOverwrite); + const handlePasteOverwrite = useRef(onPasteOverwrite); useEffect(() => { handlePasteOverwrite.current = onPasteOverwrite; }, [onPasteOverwrite]); // Use ref so we can update the handler without re-initializing the editor - const handleFocus = useRef(onFocus); + const handleFocus = useRef(onFocus); useEffect(() => { handleFocus.current = onFocus; }, [onFocus]); // Use ref so we can update the handler without re-initializing the editor - const handleBlur = useRef(onBlur); + const handleBlur = useRef(onBlur); useEffect(() => { handleBlur.current = onBlur; }, [onBlur]); // Use ref so we can update the handler without re-initializing the editor - const handleKeyDown = useRef(onKeyDown); + const handleKeyDown = useRef(onKeyDown); useEffect(() => { handleKeyDown.current = onKeyDown; }, [onKeyDown]); @@ -236,10 +236,10 @@ function EditorInner({ if (cm.current === null) return; const current = keymapCompartment.current.get(cm.current.view.state) ?? []; // PERF: This is expensive with hundreds of editors on screen, so only do it when necessary - if (settings.editorKeymap === 'default' && current === keymapExtensions.default) return; // Nothing to do - if (settings.editorKeymap === 'vim' && current === keymapExtensions.vim) return; // Nothing to do - if (settings.editorKeymap === 'vscode' && current === keymapExtensions.vscode) return; // Nothing to do - if (settings.editorKeymap === 'emacs' && current === keymapExtensions.emacs) return; // Nothing to do + if (settings.editorKeymap === "default" && current === keymapExtensions.default) return; // Nothing to do + if (settings.editorKeymap === "vim" && current === keymapExtensions.vim) return; // Nothing to do + if (settings.editorKeymap === "vscode" && current === keymapExtensions.vscode) return; // Nothing to do + if (settings.editorKeymap === "emacs" && current === keymapExtensions.emacs) return; // Nothing to do const ext = keymapExtensions[settings.editorKeymap] ?? keymapExtensions.default; const effects = keymapCompartment.current.reconfigure(ext); @@ -289,7 +289,7 @@ function EditorInner({ TemplateFunctionDialog.show(fn, tagValue, startPos, cm.current.view); }; - if (fn.name === 'secure') { + if (fn.name === "secure") { withEncryptionEnabled(show); } else { show(); @@ -309,7 +309,7 @@ function EditorInner({ const onClickMissingVariable = useCallback(async (name: string) => { const activeEnvironment = jotaiStore.get(activeEnvironmentAtom); await editEnvironment(activeEnvironment, { - addOrFocusVariable: { name, value: '', enabled: true }, + addOrFocusVariable: { name, value: "", enabled: true }, }); }, []); @@ -414,9 +414,9 @@ function EditorInner({ : []), ]; - const cachedJsonState = getCachedEditorState(defaultValue ?? '', stateKey); + const cachedJsonState = getCachedEditorState(defaultValue ?? "", stateKey); - const doc = `${defaultValue ?? ''}`; + const doc = `${defaultValue ?? ""}`; const config: EditorStateConfig = { extensions, doc }; const state = cachedJsonState @@ -439,7 +439,7 @@ function EditorInner({ } setRef?.(view); } catch (e) { - console.log('Failed to initialize Codemirror', e); + console.log("Failed to initialize Codemirror", e); } }, [forceUpdateKey], @@ -449,7 +449,7 @@ function EditorInner({ useEffect( function updateReadOnlyEditor() { if (readOnly && cm.current?.view != null) { - updateContents(cm.current.view, defaultValue || ''); + updateContents(cm.current.view, defaultValue || ""); } }, [defaultValue, readOnly], @@ -460,7 +460,7 @@ function EditorInner({ function updateNonFocusedEditor() { const notFocused = !cm.current?.view.hasFocus; if (notFocused && cm.current != null) { - updateContents(cm.current.view, defaultValue || ''); + updateContents(cm.current.view, defaultValue || ""); } }, [defaultValue], @@ -470,7 +470,7 @@ function EditorInner({ const decoratedActions = useMemo(() => { const results = []; const actionClassName = classNames( - 'bg-surface transition-opacity transform-gpu opacity-0 group-hover:opacity-100 hover:!opacity-100 shadow', + "bg-surface transition-opacity transform-gpu opacity-0 group-hover:opacity-100 hover:!opacity-100 shadow", ); if (format) { @@ -517,12 +517,12 @@ function EditorInner({ ref={initEditorRef} className={classNames( className, - 'cm-wrapper text-base', - disabled && 'opacity-disabled', - type === 'password' && 'cm-obscure-text', - heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height', - singleLine ? 'cm-singleline' : 'cm-multiline', - readOnly && 'cm-readonly', + "cm-wrapper text-base", + disabled && "opacity-disabled", + type === "password" && "cm-obscure-text", + heightMode === "auto" ? "cm-auto-height" : "cm-full-height", + singleLine ? "cm-singleline" : "cm-multiline", + readOnly && "cm-readonly", )} /> ); @@ -539,8 +539,8 @@ function EditorInner({ space={1} justifyContent="end" className={classNames( - 'absolute bottom-2 left-0 right-0', - 'pointer-events-none', // No pointer events, so we don't block the editor + "absolute bottom-2 left-0 right-0", + "pointer-events-none", // No pointer events, so we don't block the editor )} > {decoratedActions} @@ -562,20 +562,20 @@ function getExtensions({ onFocus, onBlur, onKeyDown, -}: Pick & { - stateKey: EditorProps['stateKey']; +}: Pick & { + stateKey: EditorProps["stateKey"]; container: HTMLDivElement | null; - onChange: RefObject; - onPaste: RefObject; - onPasteOverwrite: RefObject; - onFocus: RefObject; - onBlur: RefObject; - onKeyDown: RefObject; + onChange: RefObject; + onPaste: RefObject; + onPasteOverwrite: RefObject; + onFocus: RefObject; + onBlur: RefObject; + onKeyDown: RefObject; }) { // TODO: Ensure tooltips render inside the dialog if we are in one. const parent = container?.closest('[role="dialog"]') ?? - document.querySelector('#cm-portal') ?? + document.querySelector("#cm-portal") ?? undefined; return [ @@ -589,7 +589,7 @@ function getExtensions({ }, keydown: (e, view) => { // Check if the hotkey matches the editor.autocomplete action - if (eventMatchesHotkey(e, 'editor.autocomplete')) { + if (eventMatchesHotkey(e, "editor.autocomplete")) { e.preventDefault(); startCompletion(view); return true; @@ -597,7 +597,7 @@ function getExtensions({ onKeyDown.current?.(e); }, paste: (e, v) => { - const textData = e.clipboardData?.getData('text/plain') ?? ''; + const textData = e.clipboardData?.getData("text/plain") ?? ""; onPaste.current?.(textData); if (v.state.selection.main.from === 0 && v.state.selection.main.to === v.state.doc.length) { onPasteOverwrite.current?.(e, textData); @@ -605,7 +605,7 @@ function getExtensions({ }, }), tooltips({ parent }), - keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap), + keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== "Enter") : defaultKeymap), ...(singleLine ? [singleLineExtensions()] : []), ...(!singleLine ? multiLineExtensions({ hideGutter }) : []), ...(readOnly ? readonlyExtensions : []), @@ -627,10 +627,10 @@ function getExtensions({ } const placeholderElFromText = (text: string | undefined) => { - const el = document.createElement('div'); + const el = document.createElement("div"); // Default to because codemirror needs it for sizing. I'm not sure why, but probably something // to do with how Yaak "hacks" it with CSS for single line input. - el.innerHTML = text ? text.replaceAll('\n', '
') : ' '; + el.innerHTML = text ? text.replaceAll("\n", "
") : " "; return el; }; @@ -646,7 +646,7 @@ function saveCachedEditorState(stateKey: string | null, state: EditorState | nul try { sessionStorage.setItem(computeFullStateKey(stateKey), JSON.stringify(stateObj)); } catch (err) { - console.log('Failed to save to editor state', stateKey, err); + console.log("Failed to save to editor state", stateKey, err); } } @@ -667,7 +667,7 @@ function getCachedEditorState(doc: string, stateKey: string | null) { state.doc = doc; return state; } catch (err) { - console.log('Failed to restore editor storage', stateKey, err); + console.log("Failed to restore editor storage", stateKey, err); } return null; diff --git a/apps/yaak-client/components/core/Editor/LazyEditor.tsx b/apps/yaak-client/components/core/Editor/LazyEditor.tsx index c2ae1ab1..93a2706f 100644 --- a/apps/yaak-client/components/core/Editor/LazyEditor.tsx +++ b/apps/yaak-client/components/core/Editor/LazyEditor.tsx @@ -1,7 +1,7 @@ -import { lazy, Suspense } from 'react'; -import type { EditorProps } from './Editor'; +import { lazy, Suspense } from "react"; +import type { EditorProps } from "./Editor"; -const Editor_ = lazy(() => import('./Editor').then((m) => ({ default: m.Editor }))); +const Editor_ = lazy(() => import("./Editor").then((m) => ({ default: m.Editor }))); export function Editor(props: EditorProps) { return ( diff --git a/apps/yaak-client/components/core/Editor/extensions.ts b/apps/yaak-client/components/core/Editor/extensions.ts index 8db6750d..ec9d46ce 100644 --- a/apps/yaak-client/components/core/Editor/extensions.ts +++ b/apps/yaak-client/components/core/Editor/extensions.ts @@ -3,15 +3,15 @@ import { closeBrackets, closeBracketsKeymap, completionKeymap, -} from '@codemirror/autocomplete'; -import { history, historyKeymap } from '@codemirror/commands'; -import { go } from '@codemirror/lang-go'; -import { java } from '@codemirror/lang-java'; -import { javascript } from '@codemirror/lang-javascript'; -import { markdown } from '@codemirror/lang-markdown'; -import { php } from '@codemirror/lang-php'; -import { python } from '@codemirror/lang-python'; -import { xml } from '@codemirror/lang-xml'; +} from "@codemirror/autocomplete"; +import { history, historyKeymap } from "@codemirror/commands"; +import { go } from "@codemirror/lang-go"; +import { java } from "@codemirror/lang-java"; +import { javascript } from "@codemirror/lang-javascript"; +import { markdown } from "@codemirror/lang-markdown"; +import { php } from "@codemirror/lang-php"; +import { python } from "@codemirror/lang-python"; +import { xml } from "@codemirror/lang-xml"; import { bracketMatching, codeFolding, @@ -22,20 +22,20 @@ import { LanguageSupport, StreamLanguage, syntaxHighlighting, -} from '@codemirror/language'; -import { c, csharp, kotlin, objectiveC } from '@codemirror/legacy-modes/mode/clike'; -import { clojure } from '@codemirror/legacy-modes/mode/clojure'; -import { http } from '@codemirror/legacy-modes/mode/http'; -import { oCaml } from '@codemirror/legacy-modes/mode/mllike'; -import { powerShell } from '@codemirror/legacy-modes/mode/powershell'; -import { r } from '@codemirror/legacy-modes/mode/r'; -import { ruby } from '@codemirror/legacy-modes/mode/ruby'; -import { shell } from '@codemirror/legacy-modes/mode/shell'; -import { swift } from '@codemirror/legacy-modes/mode/swift'; -import { linter, lintGutter, lintKeymap } from '@codemirror/lint'; -import { search, searchKeymap } from '@codemirror/search'; -import type { Extension } from '@codemirror/state'; -import { EditorState } from '@codemirror/state'; +} from "@codemirror/language"; +import { c, csharp, kotlin, objectiveC } from "@codemirror/legacy-modes/mode/clike"; +import { clojure } from "@codemirror/legacy-modes/mode/clojure"; +import { http } from "@codemirror/legacy-modes/mode/http"; +import { oCaml } from "@codemirror/legacy-modes/mode/mllike"; +import { powerShell } from "@codemirror/legacy-modes/mode/powershell"; +import { r } from "@codemirror/legacy-modes/mode/r"; +import { ruby } from "@codemirror/legacy-modes/mode/ruby"; +import { shell } from "@codemirror/legacy-modes/mode/shell"; +import { swift } from "@codemirror/legacy-modes/mode/swift"; +import { linter, lintGutter, lintKeymap } from "@codemirror/lint"; +import { search, searchKeymap } from "@codemirror/search"; +import type { Extension } from "@codemirror/state"; +import { EditorState } from "@codemirror/state"; import { crosshairCursor, drawSelection, @@ -46,51 +46,51 @@ import { keymap, lineNumbers, rectangularSelection, -} from '@codemirror/view'; -import { tags as t } from '@lezer/highlight'; -import { jsonc, jsoncLanguage } from '@shopify/lang-jsonc'; -import { graphql } from 'cm6-graphql'; -import type { GraphQLSchema } from 'graphql'; -import { activeRequestIdAtom } from '../../../hooks/useActiveRequestId'; -import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables'; -import { jotaiStore } from '../../../lib/jotai'; -import { renderMarkdown } from '../../../lib/markdown'; -import { pluralizeCount } from '../../../lib/pluralize'; -import { showGraphQLDocExplorerAtom } from '../../graphql/graphqlAtoms'; -import type { EditorProps } from './Editor'; -import { jsonParseLinter } from './json-lint'; -import { pairs } from './pairs/extension'; -import { searchMatchCount } from './searchMatchCount'; -import { text } from './text/extension'; -import { timeline } from './timeline/extension'; -import type { TwigCompletionOption } from './twig/completion'; -import { twig } from './twig/extension'; -import { pathParametersPlugin } from './twig/pathParameters'; -import { url } from './url/extension'; +} from "@codemirror/view"; +import { tags as t } from "@lezer/highlight"; +import { jsonc, jsoncLanguage } from "@shopify/lang-jsonc"; +import { graphql } from "cm6-graphql"; +import type { GraphQLSchema } from "graphql"; +import { activeRequestIdAtom } from "../../../hooks/useActiveRequestId"; +import type { WrappedEnvironmentVariable } from "../../../hooks/useEnvironmentVariables"; +import { jotaiStore } from "../../../lib/jotai"; +import { renderMarkdown } from "../../../lib/markdown"; +import { pluralizeCount } from "../../../lib/pluralize"; +import { showGraphQLDocExplorerAtom } from "../../graphql/graphqlAtoms"; +import type { EditorProps } from "./Editor"; +import { jsonParseLinter } from "./json-lint"; +import { pairs } from "./pairs/extension"; +import { searchMatchCount } from "./searchMatchCount"; +import { text } from "./text/extension"; +import { timeline } from "./timeline/extension"; +import type { TwigCompletionOption } from "./twig/completion"; +import { twig } from "./twig/extension"; +import { pathParametersPlugin } from "./twig/pathParameters"; +import { url } from "./url/extension"; export const syntaxHighlightStyle = HighlightStyle.define([ { tag: [t.documentMeta, t.blockComment, t.lineComment, t.docComment, t.comment], - color: 'var(--textSubtlest)', + color: "var(--textSubtlest)", }, { tag: [t.emphasis], - textDecoration: 'underline', + textDecoration: "underline", }, { tag: [t.angleBracket, t.paren, t.bracket, t.squareBracket, t.brace, t.separator, t.punctuation], - color: 'var(--textSubtle)', + color: "var(--textSubtle)", }, { tag: [t.link, t.name, t.tagName, t.angleBracket, t.docString, t.number], - color: 'var(--info)', + color: "var(--info)", }, - { tag: [t.variableName], color: 'var(--success)' }, - { tag: [t.bool], color: 'var(--warning)' }, - { tag: [t.attributeName, t.propertyName], color: 'var(--primary)' }, - { tag: [t.attributeValue], color: 'var(--warning)' }, - { tag: [t.string], color: 'var(--notice)' }, - { tag: [t.atom, t.meta, t.operator, t.bool, t.null, t.keyword], color: 'var(--danger)' }, + { tag: [t.variableName], color: "var(--success)" }, + { tag: [t.bool], color: "var(--warning)" }, + { tag: [t.attributeName, t.propertyName], color: "var(--primary)" }, + { tag: [t.attributeValue], color: "var(--warning)" }, + { tag: [t.string], color: "var(--notice)" }, + { tag: [t.atom, t.meta, t.operator, t.bool, t.null, t.keyword], color: "var(--danger)" }, ]); const syntaxTheme = EditorView.theme({}, { dark: true }); @@ -102,7 +102,7 @@ const legacyLang = (mode: Parameters[0]) => { }; const syntaxExtensions: Record< - NonNullable, + NonNullable, null | (() => LanguageSupport) > = { graphql: null, @@ -134,11 +134,11 @@ const syntaxExtensions: Record< swift: legacyLang(swift), }; -const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ['json', 'javascript', 'graphql']; +const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ["json", "javascript", "graphql"]; export function getLanguageExtension({ useTemplating, - language = 'text', + language = "text", lintExtension, environmentVariables, autocomplete, @@ -156,10 +156,10 @@ export function getLanguageExtension({ onClickPathParameter: (name: string) => void; completionOptions: TwigCompletionOption[]; graphQLSchema: GraphQLSchema | null; -} & Pick) { +} & Pick) { const extraExtensions: Extension[] = []; - if (language === 'url') { + if (language === "url") { extraExtensions.push(pathParametersPlugin(onClickPathParameter)); } @@ -169,13 +169,13 @@ export function getLanguageExtension({ } // GraphQL is a special exception - if (language === 'graphql') { + if (language === "graphql") { return [ graphql(graphQLSchema ?? undefined, { async onCompletionInfoRender(gqlCompletionItem): Promise { if (!gqlCompletionItem.documentation) return null; const innerHTML = await renderMarkdown(gqlCompletionItem.documentation); - const span = document.createElement('span'); + const span = document.createElement("span"); span.innerHTML = innerHTML; return span; }, @@ -192,11 +192,11 @@ export function getLanguageExtension({ ]; } - if (language === 'json') { + if (language === "json") { extraExtensions.push(lintExtension ?? linter(jsonParseLinter())); extraExtensions.push( jsoncLanguage.data.of({ - commentTokens: { line: '//', block: { open: '/*', close: '*/' } }, + commentTokens: { line: "//", block: { open: "/*", close: "*/" } }, }), ); if (!hideGutter) { @@ -205,7 +205,7 @@ export function getLanguageExtension({ } const maybeBase = language ? syntaxExtensions[language] : null; - const base = typeof maybeBase === 'function' ? maybeBase() : null; + const base = typeof maybeBase === "function" ? maybeBase() : null; if (base == null) { return []; } @@ -229,10 +229,10 @@ export function getLanguageExtension({ // Filter out autocomplete start triggers from completionKeymap since we handle it via configurable hotkeys. // Keep navigation keys (ArrowUp/Down, Enter, Escape, etc.) but remove startCompletion bindings. const filteredCompletionKeymap = completionKeymap.filter((binding) => { - const key = binding.key?.toLowerCase() ?? ''; - const mac = (binding as { mac?: string }).mac?.toLowerCase() ?? ''; + const key = binding.key?.toLowerCase() ?? ""; + const mac = (binding as { mac?: string }).mac?.toLowerCase() ?? ""; // Filter out Ctrl-Space and Mac-specific autocomplete triggers (Alt-`, Alt-i) - const isStartTrigger = key.includes('space') || mac.includes('alt-') || mac.includes('`'); + const isStartTrigger = key.includes("space") || mac.includes("alt-") || mac.includes("`"); return !isStartTrigger; }); @@ -242,7 +242,7 @@ export const baseExtensions = [ dropCursor(), drawSelection(), autocompletion({ - tooltipClass: () => 'x-theme-menu', + tooltipClass: () => "x-theme-menu", closeOnBlur: true, // Set to `false` for debugging in devtools without closing it defaultKeymap: false, // We handle the trigger via configurable hotkeys compareCompletions: (a, b) => { @@ -257,7 +257,7 @@ export const baseExtensions = [ export const readonlyExtensions = [ EditorState.readOnly.of(true), - EditorView.contentAttributes.of({ tabindex: '-1' }), + EditorView.contentAttributes.of({ tabindex: "-1" }), ]; export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => [ @@ -269,11 +269,11 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => lineNumbers(), foldGutter({ markerDOM: (open) => { - const el = document.createElement('div'); - el.classList.add('fold-gutter-icon'); + const el = document.createElement("div"); + el.classList.add("fold-gutter-icon"); el.tabIndex = -1; if (open) { - el.setAttribute('data-open', ''); + el.setAttribute("data-open", ""); } return el; }, @@ -281,12 +281,12 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => ], codeFolding({ placeholderDOM(_view, onclick, prepared) { - const el = document.createElement('span'); + const el = document.createElement("span"); el.onclick = onclick; - el.className = 'cm-foldPlaceholder'; - el.innerText = prepared || '…'; - el.title = 'unfold'; - el.ariaLabel = 'folded code'; + el.className = "cm-foldPlaceholder"; + el.innerText = prepared || "…"; + el.title = "unfold"; + el.ariaLabel = "folded code"; return el; }, /** @@ -295,15 +295,15 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => */ preparePlaceholder(state, range) { let count: number | undefined; - let startToken = '{'; - let endToken = '}'; + let startToken = "{"; + let endToken = "}"; const prevLine = state.doc.lineAt(range.from).text; - const isArray = prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{'); + const isArray = prevLine.lastIndexOf("[") > prevLine.lastIndexOf("{"); if (isArray) { - startToken = '['; - endToken = ']'; + startToken = "["; + endToken = "]"; } const internal = state.sliceDoc(range.from, range.to); @@ -317,7 +317,7 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => } if (count !== undefined) { - const label = isArray ? 'item' : 'key'; + const label = isArray ? "item" : "key"; return pluralizeCount(label, count); } }, diff --git a/apps/yaak-client/components/core/Editor/filter/extension.ts b/apps/yaak-client/components/core/Editor/filter/extension.ts index 580f1511..88f6c4bb 100644 --- a/apps/yaak-client/components/core/Editor/filter/extension.ts +++ b/apps/yaak-client/components/core/Editor/filter/extension.ts @@ -1,8 +1,8 @@ -import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete'; -import { autocompletion, startCompletion } from '@codemirror/autocomplete'; -import { LanguageSupport, LRLanguage, syntaxTree } from '@codemirror/language'; -import type { SyntaxNode } from '@lezer/common'; -import { parser } from './filter'; +import type { Completion, CompletionContext, CompletionResult } from "@codemirror/autocomplete"; +import { autocompletion, startCompletion } from "@codemirror/autocomplete"; +import { LanguageSupport, LRLanguage, syntaxTree } from "@codemirror/language"; +import type { SyntaxNode } from "@lezer/common"; +import { parser } from "./filter"; export interface FieldDef { name: string; @@ -43,7 +43,7 @@ function inPhrase(ctx: CompletionContext): boolean { // Lezer node names from your grammar: Phrase is the quoted token let n: SyntaxNode | null = syntaxTree(ctx.state).resolveInner(ctx.pos, -1); while (n) { - if (n.name === 'Phrase') return true; + if (n.name === "Phrase") return true; n = n.parent; } return false; @@ -53,7 +53,7 @@ function inPhrase(ctx: CompletionContext): boolean { function inUnclosedQuote(doc: string, pos: number): boolean { let quotes = 0; for (let i = 0; i < pos; i++) { - if (doc[i] === '"' && doc[i - 1] !== '\\') quotes++; + if (doc[i] === '"' && doc[i - 1] !== "\\") quotes++; } return quotes % 2 === 1; // odd = inside an open quote } @@ -64,13 +64,13 @@ function inUnclosedQuote(doc: string, pos: number): boolean { * - Otherwise, we're in a field name or bare term position. */ function contextInfo(stateDoc: string, pos: number) { - const lastColon = stateDoc.lastIndexOf(':', pos - 1); + const lastColon = stateDoc.lastIndexOf(":", pos - 1); const lastBoundary = Math.max( - stateDoc.lastIndexOf(' ', pos - 1), - stateDoc.lastIndexOf('\t', pos - 1), - stateDoc.lastIndexOf('\n', pos - 1), - stateDoc.lastIndexOf('(', pos - 1), - stateDoc.lastIndexOf(')', pos - 1), + stateDoc.lastIndexOf(" ", pos - 1), + stateDoc.lastIndexOf("\t", pos - 1), + stateDoc.lastIndexOf("\n", pos - 1), + stateDoc.lastIndexOf("(", pos - 1), + stateDoc.lastIndexOf(")", pos - 1), ); const inValue = lastColon > lastBoundary; @@ -96,7 +96,7 @@ function contextInfo(stateDoc: string, pos: number) { function fieldNameCompletions(fieldNames: string[]): Completion[] { return fieldNames.map((name) => ({ label: name, - type: 'property', + type: "property", apply: (view, _completion, from, to) => { // Insert "name:" (leave cursor right after colon) view.dispatch({ @@ -117,7 +117,7 @@ function fieldValueCompletions( return vals.map((v) => ({ label: v.match(IDENT_ONLY) ? v : `"${v}"`, displayLabel: v, - type: 'constant', + type: "constant", })); } @@ -169,7 +169,7 @@ function makeCompletionSource(opts: FilterOptions) { } const language = LRLanguage.define({ - name: 'filter', + name: "filter", parser, languageData: { autocompletion: {}, diff --git a/apps/yaak-client/components/core/Editor/filter/filter.ts b/apps/yaak-client/components/core/Editor/filter/filter.ts index 70a2310a..f7a1dae3 100644 --- a/apps/yaak-client/components/core/Editor/filter/filter.ts +++ b/apps/yaak-client/components/core/Editor/filter/filter.ts @@ -1,20 +1,20 @@ -// biome-ignore-all lint: Disable for generated file +/* oxlint-disable */ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "%QOVQPOOPeOPOOOVQPO'#CfOjQPO'#ChO!XQPO'#CgOOQO'#Cc'#CcOVQPO'#CaOOQO'#Ca'#CaO!oQPO'#C`O!|QPO'#C_OOQO'#C^'#C^QOQPOOPOOO'#Cp'#CpP#XOPO)C>jO#`QPO,59QO#eQPO,59ROOQO,58{,58{OVQPO'#CqOOQO'#Cq'#CqO#mQPO,58zOVQPO'#CrO#zQPO,58yPOOO-E6n-E6nOOQO1G.l1G.lOOQO'#Cm'#CmOOQO'#Ck'#CkOOQO1G.m1G.mOOQO,59],59]OOQO-E6o-E6oOOQO,59^,59^OOQO-E6p-E6p", stateData: - '$]~OiPQ~OUUOXQO]RO`TO~Oi[O~OUaXXaX]aX^[X`aXbaXcaXgaXWaX~O^_O~OUUOXQO]RO`TObaO~OcSXgSXWSX~P!^OcdOgRXWRX~Oi[O~Qh]WgO~O]hO`iO~OcSagSaWSa~P!^OcdOgRaWRa~OUbc]c~', - goto: '#hgPPhnryP!YPP!c!c!lPP!uP!xPP#U#[#bQZOR^QTYOQSXOQRmdUWOQdQ`USbWcRka_VOQUWacd_TOQUWacd_SOQUWacdRj_^TOQUWacdRi_Q]PRf]QcWRlcQeXRne', + "$]~OiPQ~OUUOXQO]RO`TO~Oi[O~OUaXXaX]aX^[X`aXbaXcaXgaXWaX~O^_O~OUUOXQO]RO`TObaO~OcSXgSXWSX~P!^OcdOgRXWRX~Oi[O~Qh]WgO~O]hO`iO~OcSagSaWSa~P!^OcdOgRaWRa~OUbc]c~", + goto: "#hgPPhnryP!YPP!c!c!lPP!uP!xPP#U#[#bQZOR^QTYOQSXOQRmdUWOQdQ`USbWcRka_VOQUWacd_TOQUWacd_SOQUWacdRj_^TOQUWacdRi_Q]PRf]QcWRlcQeXRne", nodeNames: - '⚠ Query Expr OrExpr AndExpr Unary Not Primary RParen LParen Group Field FieldName Word Colon FieldValue Phrase Term And Or', + "⚠ Query Expr OrExpr AndExpr Unary Not Primary RParen LParen Group Field FieldName Word Colon FieldValue Phrase Term And Or", maxTerm: 25, nodeProps: [ - ['openedBy', 8, 'LParen'], - ['closedBy', 9, 'RParen'], + ["openedBy", 8, "LParen"], + ["closedBy", 9, "RParen"], ], propSources: [highlight], skippedNodes: [0, 20], diff --git a/apps/yaak-client/components/core/Editor/filter/highlight.ts b/apps/yaak-client/components/core/Editor/filter/highlight.ts index 6529fda9..0491e5bb 100644 --- a/apps/yaak-client/components/core/Editor/filter/highlight.ts +++ b/apps/yaak-client/components/core/Editor/filter/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ // Boolean operators @@ -16,6 +16,6 @@ export const highlight = styleTags({ Phrase: t.string, // "quoted string" // Fields - 'FieldName/Word': t.attributeName, - 'FieldValue/Term/Word': t.attributeValue, + "FieldName/Word": t.attributeName, + "FieldValue/Term/Word": t.attributeValue, }); diff --git a/apps/yaak-client/components/core/Editor/filter/query.ts b/apps/yaak-client/components/core/Editor/filter/query.ts index 0299e70e..6c3eb5be 100644 --- a/apps/yaak-client/components/core/Editor/filter/query.ts +++ b/apps/yaak-client/components/core/Editor/filter/query.ts @@ -1,33 +1,33 @@ // query.ts // A tiny query language parser with NOT/AND/OR, parentheses, phrases, negation, and field:value. -import { fuzzyMatch } from 'fuzzbunny'; +import { fuzzyMatch } from "fuzzbunny"; ///////////////////////// // AST ///////////////////////// export type Ast = - | { type: 'Term'; value: string } // foo - | { type: 'Phrase'; value: string } // "hi there" - | { type: 'Field'; field: string; value: string } // method:POST or title:"exact phrase" - | { type: 'Not'; node: Ast } // -foo or NOT foo - | { type: 'And'; left: Ast; right: Ast } // a AND b - | { type: 'Or'; left: Ast; right: Ast }; // a OR b + | { type: "Term"; value: string } // foo + | { type: "Phrase"; value: string } // "hi there" + | { type: "Field"; field: string; value: string } // method:POST or title:"exact phrase" + | { type: "Not"; node: Ast } // -foo or NOT foo + | { type: "And"; left: Ast; right: Ast } // a AND b + | { type: "Or"; left: Ast; right: Ast }; // a OR b ///////////////////////// // Tokenizer ///////////////////////// type Tok = - | { kind: 'LPAREN' } - | { kind: 'RPAREN' } - | { kind: 'AND' } - | { kind: 'OR' } - | { kind: 'NOT' } // explicit NOT - | { kind: 'MINUS' } // unary minus before term/phrase/paren group - | { kind: 'COLON' } - | { kind: 'WORD'; text: string } // bareword (unquoted) - | { kind: 'PHRASE'; text: string } // "quoted phrase" - | { kind: 'EOF' }; + | { kind: "LPAREN" } + | { kind: "RPAREN" } + | { kind: "AND" } + | { kind: "OR" } + | { kind: "NOT" } // explicit NOT + | { kind: "MINUS" } // unary minus before term/phrase/paren group + | { kind: "COLON" } + | { kind: "WORD"; text: string } // bareword (unquoted) + | { kind: "PHRASE"; text: string } // "quoted phrase" + | { kind: "EOF" }; const isSpace = (c: string) => /\s/.test(c); const isIdent = (c: string) => /[A-Za-z0-9_\-./]/.test(c); @@ -37,11 +37,11 @@ export function tokenize(input: string): Tok[] { let i = 0; const n = input.length; - const peek = () => input[i] ?? ''; + const peek = () => input[i] ?? ""; const advance = () => input[i++]; const readWord = () => { - let s = ''; + let s = ""; while (i < n && isIdent(peek())) s += advance(); return s; }; @@ -49,11 +49,11 @@ export function tokenize(input: string): Tok[] { const readPhrase = () => { // assumes current char is opening quote advance(); // consume opening " - let s = ''; + let s = ""; while (i < n) { const c = advance(); if (c === `"`) break; - if (c === '\\' && i < n) { + if (c === "\\" && i < n) { // escape \" and \\ (simple) const next = advance(); s += next; @@ -72,28 +72,28 @@ export function tokenize(input: string): Tok[] { continue; } - if (c === '(') { - toks.push({ kind: 'LPAREN' }); + if (c === "(") { + toks.push({ kind: "LPAREN" }); i++; continue; } - if (c === ')') { - toks.push({ kind: 'RPAREN' }); + if (c === ")") { + toks.push({ kind: "RPAREN" }); i++; continue; } - if (c === ':') { - toks.push({ kind: 'COLON' }); + if (c === ":") { + toks.push({ kind: "COLON" }); i++; continue; } if (c === `"`) { const text = readPhrase(); - toks.push({ kind: 'PHRASE', text }); + toks.push({ kind: "PHRASE", text }); continue; } - if (c === '-') { - toks.push({ kind: 'MINUS' }); + if (c === "-") { + toks.push({ kind: "MINUS" }); i++; continue; } @@ -102,10 +102,10 @@ export function tokenize(input: string): Tok[] { if (isIdent(c)) { const w = readWord(); const upper = w.toUpperCase(); - if (upper === 'AND') toks.push({ kind: 'AND' }); - else if (upper === 'OR') toks.push({ kind: 'OR' }); - else if (upper === 'NOT') toks.push({ kind: 'NOT' }); - else toks.push({ kind: 'WORD', text: w }); + if (upper === "AND") toks.push({ kind: "AND" }); + else if (upper === "OR") toks.push({ kind: "OR" }); + else if (upper === "NOT") toks.push({ kind: "NOT" }); + else toks.push({ kind: "WORD", text: w }); continue; } @@ -113,7 +113,7 @@ export function tokenize(input: string): Tok[] { i++; } - toks.push({ kind: 'EOF' }); + toks.push({ kind: "EOF" }); return toks; } @@ -122,20 +122,20 @@ class Parser { constructor(private toks: Tok[]) {} private peek(): Tok { - return this.toks[this.i] ?? { kind: 'EOF' }; + return this.toks[this.i] ?? { kind: "EOF" }; } private advance(): Tok { - return this.toks[this.i++] ?? { kind: 'EOF' }; + return this.toks[this.i++] ?? { kind: "EOF" }; } - private at(kind: Tok['kind']) { + private at(kind: Tok["kind"]) { return this.peek().kind === kind; } // Top-level: parse OR-precedence chain, allowing implicit AND. parse(): Ast | null { - if (this.at('EOF')) return null; + if (this.at("EOF")) return null; const expr = this.parseOr(); - if (!this.at('EOF')) { + if (!this.at("EOF")) { // Optionally, consume remaining tokens or throw } return expr; @@ -144,10 +144,10 @@ class Parser { // Precedence: NOT (highest), AND, OR (lowest) private parseOr(): Ast { let node = this.parseAnd(); - while (this.at('OR')) { + while (this.at("OR")) { this.advance(); const rhs = this.parseAnd(); - node = { type: 'Or', left: node, right: rhs }; + node = { type: "Or", left: node, right: rhs }; } return node; } @@ -155,31 +155,31 @@ class Parser { private parseAnd(): Ast { let node = this.parseUnary(); // Implicit AND: if next token starts a primary, treat as AND. - while (this.at('AND') || this.startsPrimary()) { - if (this.at('AND')) this.advance(); + while (this.at("AND") || this.startsPrimary()) { + if (this.at("AND")) this.advance(); const rhs = this.parseUnary(); - node = { type: 'And', left: node, right: rhs }; + node = { type: "And", left: node, right: rhs }; } return node; } private parseUnary(): Ast { - if (this.at('NOT') || this.at('MINUS')) { + if (this.at("NOT") || this.at("MINUS")) { this.advance(); const node = this.parseUnary(); - return { type: 'Not', node }; + return { type: "Not", node }; } return this.parsePrimaryOrField(); } private startsPrimary(): boolean { const k = this.peek().kind; - return k === 'WORD' || k === 'PHRASE' || k === 'LPAREN' || k === 'MINUS' || k === 'NOT'; + return k === "WORD" || k === "PHRASE" || k === "LPAREN" || k === "MINUS" || k === "NOT"; } private parsePrimaryOrField(): Ast { // Parenthesized group - if (this.at('LPAREN')) { + if (this.at("LPAREN")) { this.advance(); const inside = this.parseOr(); // if (!this.at('RPAREN')) throw new Error("Missing closing ')'"); @@ -188,59 +188,59 @@ class Parser { } // Phrase - if (this.at('PHRASE')) { - const t = this.advance() as Extract; - return { type: 'Phrase', value: t.text }; + if (this.at("PHRASE")) { + const t = this.advance() as Extract; + return { type: "Phrase", value: t.text }; } // Field or bare word - if (this.at('WORD')) { - const wordTok = this.advance() as Extract; + if (this.at("WORD")) { + const wordTok = this.advance() as Extract; - if (this.at('COLON')) { + if (this.at("COLON")) { // field:value or field:"phrase" this.advance(); // : let value: string; - if (this.at('PHRASE')) { - const p = this.advance() as Extract; + if (this.at("PHRASE")) { + const p = this.advance() as Extract; value = p.text; - } else if (this.at('WORD')) { - const w = this.advance() as Extract; + } else if (this.at("WORD")) { + const w = this.advance() as Extract; value = w.text; } else { // Anything else after colon is treated literally as a single Term token. const t = this.advance(); value = tokText(t); } - return { type: 'Field', field: wordTok.text, value }; + return { type: "Field", field: wordTok.text, value }; } // plain term - return { type: 'Term', value: wordTok.text }; + return { type: "Term", value: wordTok.text }; } - const w = this.advance() as Extract; - return { type: 'Phrase', value: 'text' in w ? w.text : '' }; + const w = this.advance() as Extract; + return { type: "Phrase", value: "text" in w ? w.text : "" }; } } function tokText(t: Tok): string { - if ('text' in t) return t.text; + if ("text" in t) return t.text; switch (t.kind) { - case 'COLON': - return ':'; - case 'LPAREN': - return '('; - case 'RPAREN': - return ')'; + case "COLON": + return ":"; + case "LPAREN": + return "("; + case "RPAREN": + return ")"; default: - return ''; + return ""; } } export function parseQuery(q: string): Ast | null { - if (q.trim() === '') return null; + if (q.trim() === "") return null; const toks = tokenize(q); const parser = new Parser(toks); return parser.parse(); @@ -251,45 +251,45 @@ export type Doc = { fields?: Record; }; -type Technique = 'substring' | 'fuzzy' | 'strict'; +type Technique = "substring" | "fuzzy" | "strict"; function includes(hay: string | undefined, needle: string, technique: Technique): boolean { if (!hay || !needle) return false; - if (technique === 'strict') return hay === needle; - if (technique === 'fuzzy') return !!fuzzyMatch(hay, needle); + if (technique === "strict") return hay === needle; + if (technique === "fuzzy") return !!fuzzyMatch(hay, needle); return hay.indexOf(needle) !== -1; } export function evaluate(ast: Ast | null, doc: Doc): boolean { if (!ast) return true; // Match everything if no query is provided - const text = (doc.text ?? '').toLowerCase(); + const text = (doc.text ?? "").toLowerCase(); const fieldsNorm: Record = {}; for (const [k, v] of Object.entries(doc.fields ?? {})) { - if (!(typeof v === 'string' || Array.isArray(v))) continue; + if (!(typeof v === "string" || Array.isArray(v))) continue; fieldsNorm[k.toLowerCase()] = Array.isArray(v) - ? v.filter((v) => typeof v === 'string').map((s) => s.toLowerCase()) - : [String(v ?? '').toLowerCase()]; + ? v.filter((v) => typeof v === "string").map((s) => s.toLowerCase()) + : [String(v ?? "").toLowerCase()]; } const evalNode = (node: Ast): boolean => { switch (node.type) { - case 'Term': - return includes(text, node.value.toLowerCase(), 'fuzzy'); - case 'Phrase': + case "Term": + return includes(text, node.value.toLowerCase(), "fuzzy"); + case "Phrase": // Quoted phrases match exactly - return includes(text, node.value.toLowerCase(), 'substring'); - case 'Field': { + return includes(text, node.value.toLowerCase(), "substring"); + case "Field": { const vals = fieldsNorm[node.field.toLowerCase()] ?? []; if (vals.length === 0) return false; - return vals.some((v) => includes(v, node.value.toLowerCase(), 'substring')); + return vals.some((v) => includes(v, node.value.toLowerCase(), "substring")); } - case 'Not': + case "Not": return !evalNode(node.node); - case 'And': + case "And": return evalNode(node.left) && evalNode(node.right); - case 'Or': + case "Or": return evalNode(node.left) || evalNode(node.right); } }; diff --git a/apps/yaak-client/components/core/Editor/genericCompletion.ts b/apps/yaak-client/components/core/Editor/genericCompletion.ts index bfdecef2..4eaef118 100644 --- a/apps/yaak-client/components/core/Editor/genericCompletion.ts +++ b/apps/yaak-client/components/core/Editor/genericCompletion.ts @@ -1,6 +1,6 @@ -import type { CompletionContext } from '@codemirror/autocomplete'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import { defaultBoost } from './twig/completion'; +import type { CompletionContext } from "@codemirror/autocomplete"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import { defaultBoost } from "./twig/completion"; export interface GenericCompletionConfig { minMatch?: number; diff --git a/apps/yaak-client/components/core/Editor/hyperlink/extension.ts b/apps/yaak-client/components/core/Editor/hyperlink/extension.ts index e8c995be..7f1f1a96 100644 --- a/apps/yaak-client/components/core/Editor/hyperlink/extension.ts +++ b/apps/yaak-client/components/core/Editor/hyperlink/extension.ts @@ -1,9 +1,9 @@ -import type { DecorationSet, ViewUpdate } from '@codemirror/view'; -import { Decoration, EditorView, hoverTooltip, MatchDecorator, ViewPlugin } from '@codemirror/view'; -import { activeWorkspaceIdAtom } from '../../../../hooks/useActiveWorkspace'; -import { copyToClipboard } from '../../../../lib/copy'; -import { createRequestAndNavigate } from '../../../../lib/createRequestAndNavigate'; -import { jotaiStore } from '../../../../lib/jotai'; +import type { DecorationSet, ViewUpdate } from "@codemirror/view"; +import { Decoration, EditorView, hoverTooltip, MatchDecorator, ViewPlugin } from "@codemirror/view"; +import { activeWorkspaceIdAtom } from "../../../../hooks/useActiveWorkspace"; +import { copyToClipboard } from "../../../../lib/copy"; +import { createRequestAndNavigate } from "../../../../lib/createRequestAndNavigate"; +import { jotaiStore } from "../../../../lib/jotai"; const REGEX = /(https?:\/\/([-a-zA-Z0-9@:%._+*~#=]{1,256})+(\.[a-zA-Z0-9()]{1,6})?\b([-a-zA-Z0-9()@:%_+*.~#?&/={}[\]]*))/g; @@ -14,7 +14,7 @@ const tooltip = hoverTooltip( let match: RegExpExecArray | null; let found: { start: number; end: number } | null = null; - // biome-ignore lint/suspicious/noAssignInExpressions: none + // oxlint-disable-next-line no-cond-assign while ((match = REGEX.exec(text))) { const start = from + match.index; const end = start + match[0].length; @@ -39,26 +39,26 @@ const tooltip = hoverTooltip( create() { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); const link = text.substring(found?.start - from, found?.end - from); - const dom = document.createElement('div'); + const dom = document.createElement("div"); - const $open = document.createElement('a'); - $open.textContent = 'Open in browser'; + const $open = document.createElement("a"); + $open.textContent = "Open in browser"; $open.href = link; - $open.target = '_blank'; - $open.rel = 'noopener noreferrer'; + $open.target = "_blank"; + $open.rel = "noopener noreferrer"; - const $copy = document.createElement('button'); - $copy.textContent = 'Copy to clipboard'; - $copy.addEventListener('click', () => { + const $copy = document.createElement("button"); + $copy.textContent = "Copy to clipboard"; + $copy.addEventListener("click", () => { copyToClipboard(link); }); - const $create = document.createElement('button'); - $create.textContent = 'Create new request'; - $create.addEventListener('click', async () => { + const $create = document.createElement("button"); + $create.textContent = "Create new request"; + $create.addEventListener("click", async () => { await createRequestAndNavigate({ - model: 'http_request', - workspaceId: workspaceId ?? 'n/a', + model: "http_request", + workspaceId: workspaceId ?? "n/a", url: link, }); }); @@ -94,12 +94,12 @@ const decorator = () => { const groupMatch = match[1]; if (groupMatch == null) { // Should never happen, but make TS happy - console.warn('Group match was empty', match); + console.warn("Group match was empty", match); return Decoration.replace({}); } return Decoration.mark({ - class: 'hyperlink-widget', + class: "hyperlink-widget", }); }, }); diff --git a/apps/yaak-client/components/core/Editor/json-lint.ts b/apps/yaak-client/components/core/Editor/json-lint.ts index 32652f15..4353bd76 100644 --- a/apps/yaak-client/components/core/Editor/json-lint.ts +++ b/apps/yaak-client/components/core/Editor/json-lint.ts @@ -1,6 +1,6 @@ -import type { Diagnostic } from '@codemirror/lint'; -import type { EditorView } from '@codemirror/view'; -import { parse as jsonLintParse } from '@prantlf/jsonlint'; +import type { Diagnostic } from "@codemirror/lint"; +import type { EditorView } from "@codemirror/view"; +import { parse as jsonLintParse } from "@prantlf/jsonlint"; const TEMPLATE_SYNTAX_REGEX = /\$\{\[[\s\S]*?]}/g; @@ -15,14 +15,14 @@ export function jsonParseLinter(options?: JsonLintOptions) { const doc = view.state.doc.toString(); // We need lint to not break on stuff like {"foo:" ${[ ... ]}} so we'll replace all template // syntax with repeating `1` characters, so it's valid JSON and the position is still correct. - const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, (m) => '1'.repeat(m.length)); + const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, (m) => "1".repeat(m.length)); jsonLintParse(escapedDoc, { - mode: (options?.allowComments ?? true) ? 'cjson' : 'json', + mode: (options?.allowComments ?? true) ? "cjson" : "json", ignoreTrailingCommas: options?.allowTrailingCommas ?? false, }); - // biome-ignore lint/suspicious/noExplicitAny: none + // oxlint-disable-next-line no-explicit-any } catch (err: any) { - if (!('location' in err)) { + if (!("location" in err)) { return []; } @@ -33,7 +33,7 @@ export function jsonParseLinter(options?: JsonLintOptions) { { from: err.location.start.offset, to: err.location.start.offset, - severity: 'error', + severity: "error", message: err.message, }, ]; diff --git a/apps/yaak-client/components/core/Editor/pairs/extension.ts b/apps/yaak-client/components/core/Editor/pairs/extension.ts index cf4f20f7..823e9b30 100644 --- a/apps/yaak-client/components/core/Editor/pairs/extension.ts +++ b/apps/yaak-client/components/core/Editor/pairs/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './pairs'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./pairs"; const language = LRLanguage.define({ - name: 'pairs', + name: "pairs", parser, languageData: {}, }); diff --git a/apps/yaak-client/components/core/Editor/pairs/highlight.ts b/apps/yaak-client/components/core/Editor/pairs/highlight.ts index 13fb4b79..954deff9 100644 --- a/apps/yaak-client/components/core/Editor/pairs/highlight.ts +++ b/apps/yaak-client/components/core/Editor/pairs/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ Sep: t.bracket, diff --git a/apps/yaak-client/components/core/Editor/pairs/pairs.ts b/apps/yaak-client/components/core/Editor/pairs/pairs.ts index 968a6cd3..f5cc1403 100644 --- a/apps/yaak-client/components/core/Editor/pairs/pairs.ts +++ b/apps/yaak-client/components/core/Editor/pairs/pairs.ts @@ -1,12 +1,12 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "zQQOPOOOVOQO'#CaQQOPOOO[OSO,58{OOOO-E6_-E6_OaOQO1G.gOOOO7+$R7+$R", - stateData: 'f~OQPO~ORRO~OSTO~OVUO~O', - goto: ']UPPPPPVQQORSQ', - nodeNames: '⚠ pairs Key Sep Value', + stateData: "f~OQPO~ORRO~OSTO~OVUO~O", + goto: "]UPPPPPVQQORSQ", + nodeNames: "⚠ pairs Key Sep Value", maxTerm: 7, propSources: [highlight], skippedNodes: [0], @@ -17,13 +17,13 @@ export const parser = LRParser.deserialize({ topRules: { pairs: [0, 1] }, tokenPrec: 0, termNames: { - '0': '⚠', - '1': '@top', - '2': 'Key', - '3': 'Sep', - '4': 'Value', - '5': '(Key Sep Value "\\n")+', - '6': '␄', - '7': '"\\n"', + "0": "⚠", + "1": "@top", + "2": "Key", + "3": "Sep", + "4": "Value", + "5": '(Key Sep Value "\\n")+', + "6": "␄", + "7": '"\\n"', }, }); diff --git a/apps/yaak-client/components/core/Editor/searchMatchCount.ts b/apps/yaak-client/components/core/Editor/searchMatchCount.ts index 79b0937a..b2069191 100644 --- a/apps/yaak-client/components/core/Editor/searchMatchCount.ts +++ b/apps/yaak-client/components/core/Editor/searchMatchCount.ts @@ -1,6 +1,6 @@ -import { getSearchQuery, searchPanelOpen } from '@codemirror/search'; -import type { Extension } from '@codemirror/state'; -import { type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view'; +import { getSearchQuery, searchPanelOpen } from "@codemirror/search"; +import type { Extension } from "@codemirror/state"; +import { type EditorView, ViewPlugin, type ViewUpdate } from "@codemirror/view"; /** * A CodeMirror extension that displays the total number of search matches @@ -41,7 +41,7 @@ export function searchMatchCount(): Extension { if (!query.search) { if (this.countEl) { - this.countEl.textContent = '0/0'; + this.countEl.textContent = "0/0"; } return; } @@ -64,7 +64,7 @@ export function searchMatchCount(): Extension { if (count > MAX_COUNT) { this.countEl.textContent = `${MAX_COUNT}+`; } else if (count === 0) { - this.countEl.textContent = '0/0'; + this.countEl.textContent = "0/0"; } else if (currentIndex > 0) { this.countEl.textContent = `${currentIndex}/${count}`; } else { @@ -75,7 +75,7 @@ export function searchMatchCount(): Extension { private ensureCountEl() { // Find the search panel in the editor DOM - const panel = this.view.dom.querySelector('.cm-search'); + const panel = this.view.dom.querySelector(".cm-search"); if (!panel) { this.countEl = null; return; @@ -85,11 +85,11 @@ export function searchMatchCount(): Extension { return; // Already attached } - this.countEl = document.createElement('span'); - this.countEl.className = 'cm-search-match-count'; + this.countEl = document.createElement("span"); + this.countEl.className = "cm-search-match-count"; // Reorder: insert prev button, then next button, then count after the search input - const searchInput = panel.querySelector('input'); + const searchInput = panel.querySelector("input"); const prevBtn = panel.querySelector('button[name="prev"]'); const nextBtn = panel.querySelector('button[name="next"]'); if (searchInput && searchInput.parentElement === panel) { diff --git a/apps/yaak-client/components/core/Editor/singleLine.ts b/apps/yaak-client/components/core/Editor/singleLine.ts index 7b040d7c..20c4b14b 100644 --- a/apps/yaak-client/components/core/Editor/singleLine.ts +++ b/apps/yaak-client/components/core/Editor/singleLine.ts @@ -1,5 +1,5 @@ -import type { Extension, TransactionSpec } from '@codemirror/state'; -import { EditorSelection, EditorState, Transaction } from '@codemirror/state'; +import type { Extension, TransactionSpec } from "@codemirror/state"; +import { EditorSelection, EditorState, Transaction } from "@codemirror/state"; /** * A CodeMirror extension that forces single-line input by stripping @@ -17,14 +17,14 @@ import { EditorSelection, EditorState, Transaction } from '@codemirror/state'; export function singleLineExtensions(): Extension { return EditorState.transactionFilter.of( (tr: Transaction): TransactionSpec | readonly TransactionSpec[] => { - if (!tr.isUserEvent('input') || tr.isUserEvent('input.type.compose')) return tr; + if (!tr.isUserEvent("input") || tr.isUserEvent("input.type.compose")) return tr; const changes: { from: number; to: number; insert: string }[] = []; tr.changes.iterChanges((_fromA, toA, fromB, _toB, inserted) => { - let insert = ''; + let insert = ""; for (const line of inserted.iterLines()) { - insert += line.replace(/\n/g, ''); + insert += line.replace(/\n/g, ""); } if (insert !== inserted.toString()) { diff --git a/apps/yaak-client/components/core/Editor/text/extension.ts b/apps/yaak-client/components/core/Editor/text/extension.ts index 8af165f4..a398f2ad 100644 --- a/apps/yaak-client/components/core/Editor/text/extension.ts +++ b/apps/yaak-client/components/core/Editor/text/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './text'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./text"; export const textLanguage = LRLanguage.define({ - name: 'text', + name: "text", parser, languageData: {}, }); diff --git a/apps/yaak-client/components/core/Editor/text/text.ts b/apps/yaak-client/components/core/Editor/text/text.ts index e2e986c0..b09b79c4 100644 --- a/apps/yaak-client/components/core/Editor/text/text.ts +++ b/apps/yaak-client/components/core/Editor/text/text.ts @@ -1,11 +1,11 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; +import { LRParser } from "@lezer/lr"; export const parser = LRParser.deserialize({ version: 14, - states: '[OQOPOOQOOOOO', - stateData: 'V~OQPO~O', - goto: 'QPP', - nodeNames: '⚠ Template Text', + states: "[OQOPOOQOOOOO", + stateData: "V~OQPO~O", + goto: "QPP", + nodeNames: "⚠ Template Text", maxTerm: 3, skippedNodes: [0], repeatNodeCount: 0, diff --git a/apps/yaak-client/components/core/Editor/timeline/extension.ts b/apps/yaak-client/components/core/Editor/timeline/extension.ts index 74d20b93..b1a00412 100644 --- a/apps/yaak-client/components/core/Editor/timeline/extension.ts +++ b/apps/yaak-client/components/core/Editor/timeline/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './timeline'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./timeline"; export const timelineLanguage = LRLanguage.define({ - name: 'timeline', + name: "timeline", parser, languageData: {}, }); diff --git a/apps/yaak-client/components/core/Editor/timeline/highlight.ts b/apps/yaak-client/components/core/Editor/timeline/highlight.ts index b872957b..6f8c9e9a 100644 --- a/apps/yaak-client/components/core/Editor/timeline/highlight.ts +++ b/apps/yaak-client/components/core/Editor/timeline/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ OutgoingText: t.propertyName, // > lines - primary color (matches timeline icons) diff --git a/apps/yaak-client/components/core/Editor/timeline/timeline.terms.ts b/apps/yaak-client/components/core/Editor/timeline/timeline.terms.ts index 1b3bff48..8f03d420 100644 --- a/apps/yaak-client/components/core/Editor/timeline/timeline.terms.ts +++ b/apps/yaak-client/components/core/Editor/timeline/timeline.terms.ts @@ -1,6 +1,5 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -export const - Timeline = 1, +export const Timeline = 1, OutgoingLine = 2, OutgoingText = 3, Newline = 4, @@ -9,4 +8,4 @@ export const InfoLine = 7, InfoText = 8, PlainLine = 9, - PlainText = 10 + PlainText = 10; diff --git a/apps/yaak-client/components/core/Editor/timeline/timeline.ts b/apps/yaak-client/components/core/Editor/timeline/timeline.ts index 235578d6..fee8020d 100644 --- a/apps/yaak-client/components/core/Editor/timeline/timeline.ts +++ b/apps/yaak-client/components/core/Editor/timeline/timeline.ts @@ -1,18 +1,21 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import {LRParser} from "@lezer/lr" -import {highlight} from "./highlight" +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, - states: "!pQQOPOOO`OPO'#C^OeOPO'#CaOjOPO'#CcOoOPO'#CeOOOO'#Ci'#CiOOOO'#Cg'#CgQQOPOOOOOO,58x,58xOOOO,58{,58{OOOO,58},58}OOOO,59P,59POOOO-E6e-E6e", + states: + "!pQQOPOOO`OPO'#C^OeOPO'#CaOjOPO'#CcOoOPO'#CeOOOO'#Ci'#CiOOOO'#Cg'#CgQQOPOOOOOO,58x,58xOOOO,58{,58{OOOO,58},58}OOOO,59P,59POOOO-E6e-E6e", stateData: "z~ORPOUQOWROYSO~OSWO~OSXO~OSYO~OSZO~ORUWYW~", goto: "m^PP_PP_P_P_PcPiTTOVQVOR[VTUOV", - nodeNames: "⚠ Timeline OutgoingLine OutgoingText Newline IncomingLine IncomingText InfoLine InfoText PlainLine PlainText", + nodeNames: + "⚠ Timeline OutgoingLine OutgoingText Newline IncomingLine IncomingText InfoLine InfoText PlainLine PlainText", maxTerm: 13, propSources: [highlight], skippedNodes: [0], repeatNodeCount: 1, - tokenData: "%h~RZOYtYZ!]Zztz{!b{!^t!^!_#d!_!`t!`!a$f!a;'St;'S;=`!V<%lOt~ySY~OYtZ;'St;'S;=`!V<%lOt~!YP;=`<%lt~!bOS~~!gUY~OYtZptpq!yq;'St;'S;=`!V<%lOt~#QSW~Y~OY!yZ;'S!y;'S;=`#^<%lO!y~#aP;=`<%l!y~#iUY~OYtZptpq#{q;'St;'S;=`!V<%lOt~$SSU~Y~OY#{Z;'S#{;'S;=`$`<%lO#{~$cP;=`<%l#{~$kUY~OYtZptpq$}q;'St;'S;=`!V<%lOt~%USR~Y~OY$}Z;'S$};'S;=`%b<%lO$}~%eP;=`<%l$}", + tokenData: + "%h~RZOYtYZ!]Zztz{!b{!^t!^!_#d!_!`t!`!a$f!a;'St;'S;=`!V<%lOt~ySY~OYtZ;'St;'S;=`!V<%lOt~!YP;=`<%lt~!bOS~~!gUY~OYtZptpq!yq;'St;'S;=`!V<%lOt~#QSW~Y~OY!yZ;'S!y;'S;=`#^<%lO!y~#aP;=`<%l!y~#iUY~OYtZptpq#{q;'St;'S;=`!V<%lOt~$SSU~Y~OY#{Z;'S#{;'S;=`$`<%lO#{~$cP;=`<%l#{~$kUY~OYtZptpq$}q;'St;'S;=`!V<%lOt~%USR~Y~OY$}Z;'S$};'S;=`%b<%lO$}~%eP;=`<%l$}", tokenizers: [0], - topRules: {"Timeline":[0,1]}, - tokenPrec: 36 -}) + topRules: { Timeline: [0, 1] }, + tokenPrec: 36, +}); diff --git a/apps/yaak-client/components/core/Editor/twig/completion.ts b/apps/yaak-client/components/core/Editor/twig/completion.ts index 5c9bdde2..39793223 100644 --- a/apps/yaak-client/components/core/Editor/twig/completion.ts +++ b/apps/yaak-client/components/core/Editor/twig/completion.ts @@ -1,20 +1,20 @@ -import type { Completion, CompletionContext } from '@codemirror/autocomplete'; -import { startCompletion } from '@codemirror/autocomplete'; -import type { TemplateFunction } from '@yaakapp-internal/plugins'; +import type { Completion, CompletionContext } from "@codemirror/autocomplete"; +import { startCompletion } from "@codemirror/autocomplete"; +import type { TemplateFunction } from "@yaakapp-internal/plugins"; -const openTag = '${[ '; -const closeTag = ' ]}'; +const openTag = "${[ "; +const closeTag = " ]}"; export type TwigCompletionOptionVariable = { - type: 'variable'; + type: "variable"; }; export type TwigCompletionOptionNamespace = { - type: 'namespace'; + type: "namespace"; }; export type TwigCompletionOptionFunction = TemplateFunction & { - type: 'function'; + type: "function"; }; export type TwigCompletionOption = ( @@ -50,17 +50,17 @@ export function twigCompletion({ options }: TwigCompletionConfig) { const completions: Completion[] = options .flatMap((o): Completion[] => { - const matchSegments = toMatch.text.replace(/^\$/, '').split('.'); - const optionSegments = o.name.split('.'); + const matchSegments = toMatch.text.replace(/^\$/, "").split("."); + const optionSegments = o.name.split("."); // If not on the last segment, only complete the namespace if (matchSegments.length < optionSegments.length) { - const prefix = optionSegments.slice(0, matchSegments.length).join('.'); + const prefix = optionSegments.slice(0, matchSegments.length).join("."); return [ { label: `${prefix}.*`, - type: 'namespace', - detail: 'namespace', + type: "namespace", + detail: "namespace", apply: (view, _completion, from, to) => { const insert = `${prefix}.`; view.dispatch({ @@ -75,13 +75,13 @@ export function twigCompletion({ options }: TwigCompletionConfig) { } // If on the last segment, wrap the entire tag - const inner = o.type === 'function' ? `${o.name}()` : o.name; + const inner = o.type === "function" ? `${o.name}()` : o.name; return [ { label: o.name, info: o.description, detail: o.type, - type: o.type === 'variable' ? 'variable' : 'function', + type: o.type === "variable" ? "variable" : "function", apply: (view, _completion, from, to) => { const insert = openTag + inner + closeTag; view.dispatch({ @@ -94,7 +94,7 @@ export function twigCompletion({ options }: TwigCompletionConfig) { }) .filter((v) => v != null); - const uniqueCompletions = uniqueBy(completions, 'label'); + const uniqueCompletions = uniqueBy(completions, "label"); const sortedCompletions = uniqueCompletions.sort((a, b) => { const boostDiff = defaultBoost(b) - defaultBoost(a); if (boostDiff !== 0) return boostDiff; @@ -119,9 +119,9 @@ export function uniqueBy(arr: T[], key: K): T[] { } export function defaultBoost(o: Completion) { - if (o.type === 'variable') return 4; - if (o.type === 'constant') return 3; - if (o.type === 'function') return 2; - if (o.type === 'namespace') return 1; + if (o.type === "variable") return 4; + if (o.type === "constant") return 3; + if (o.type === "function") return 2; + if (o.type === "namespace") return 1; return 0; } diff --git a/apps/yaak-client/components/core/Editor/twig/extension.ts b/apps/yaak-client/components/core/Editor/twig/extension.ts index 45aeaf43..bbd4c8cf 100644 --- a/apps/yaak-client/components/core/Editor/twig/extension.ts +++ b/apps/yaak-client/components/core/Editor/twig/extension.ts @@ -1,15 +1,15 @@ -import type { LanguageSupport } from '@codemirror/language'; -import { LRLanguage } from '@codemirror/language'; -import type { Extension } from '@codemirror/state'; -import { parseMixed } from '@lezer/common'; -import type { WrappedEnvironmentVariable } from '../../../../hooks/useEnvironmentVariables'; -import type { GenericCompletionConfig } from '../genericCompletion'; -import { genericCompletion } from '../genericCompletion'; -import { textLanguage } from '../text/extension'; -import type { TwigCompletionOption } from './completion'; -import { twigCompletion } from './completion'; -import { templateTagsPlugin } from './templateTags'; -import { parser as twigParser } from './twig'; +import type { LanguageSupport } from "@codemirror/language"; +import { LRLanguage } from "@codemirror/language"; +import type { Extension } from "@codemirror/state"; +import { parseMixed } from "@lezer/common"; +import type { WrappedEnvironmentVariable } from "../../../../hooks/useEnvironmentVariables"; +import type { GenericCompletionConfig } from "../genericCompletion"; +import { genericCompletion } from "../genericCompletion"; +import { textLanguage } from "../text/extension"; +import type { TwigCompletionOption } from "./completion"; +import { twigCompletion } from "./completion"; +import { templateTagsPlugin } from "./templateTags"; +import { parser as twigParser } from "./twig"; export function twig({ base, @@ -35,7 +35,7 @@ export function twig({ environmentVariables.map((v) => ({ name: v.variable.name, value: v.variable.value, - type: 'variable', + type: "variable", label: v.variable.name, description: `Inherited from ${v.source}`, onClick: (rawTag: string, startPos: number) => onClickVariable(v, rawTag, startPos), @@ -74,12 +74,12 @@ function mixLanguage(base: LanguageSupport): LRLanguage { return { parser: base.language.parser, - overlay: (node) => node.type.name === 'Text', + overlay: (node) => node.type.name === "Text", }; }), }); - const language = LRLanguage.define({ name: 'twig', parser }); + const language = LRLanguage.define({ name: "twig", parser }); mixedLanguagesCache[base.language.name] = language; return language; } diff --git a/apps/yaak-client/components/core/Editor/twig/highlight.ts b/apps/yaak-client/components/core/Editor/twig/highlight.ts index b91d6ed9..d3a614ec 100644 --- a/apps/yaak-client/components/core/Editor/twig/highlight.ts +++ b/apps/yaak-client/components/core/Editor/twig/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ TagOpen: t.bracket, diff --git a/apps/yaak-client/components/core/Editor/twig/pathParameters.ts b/apps/yaak-client/components/core/Editor/twig/pathParameters.ts index e32f5f6d..dc226adc 100644 --- a/apps/yaak-client/components/core/Editor/twig/pathParameters.ts +++ b/apps/yaak-client/components/core/Editor/twig/pathParameters.ts @@ -1,7 +1,7 @@ -import { syntaxTree } from '@codemirror/language'; -import type { Range } from '@codemirror/state'; -import type { DecorationSet, ViewUpdate } from '@codemirror/view'; -import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view'; +import { syntaxTree } from "@codemirror/language"; +import type { Range } from "@codemirror/state"; +import type { DecorationSet, ViewUpdate } from "@codemirror/view"; +import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view"; class PathPlaceholderWidget extends WidgetType { readonly #clickListenerCallback: () => void; @@ -22,15 +22,15 @@ class PathPlaceholderWidget extends WidgetType { } toDOM() { - const elt = document.createElement('span'); - elt.className = 'x-theme-templateTag x-theme-templateTag--secondary template-tag'; + const elt = document.createElement("span"); + elt.className = "x-theme-templateTag x-theme-templateTag--secondary template-tag"; elt.textContent = this.rawText; - elt.addEventListener('click', this.#clickListenerCallback); + elt.addEventListener("click", this.#clickListenerCallback); return elt; } destroy(dom: HTMLElement) { - dom.removeEventListener('click', this.#clickListenerCallback); + dom.removeEventListener("click", this.#clickListenerCallback); super.destroy(dom); } @@ -50,14 +50,14 @@ function pathParameters( from, to, enter(node) { - if (node.name === 'Text') { + if (node.name === "Text") { // Find the `url` node and then jump into it to find the placeholders for (let i = node.from; i < node.to; i++) { const innerTree = syntaxTree(view.state).resolveInner(i); - if (innerTree.node.name === 'url') { + if (innerTree.node.name === "url") { innerTree.toTree().iterate({ enter(node) { - if (node.name !== 'Placeholder') return; + if (node.name !== "Placeholder") return; const globalFrom = innerTree.node.from + node.from; const globalTo = innerTree.node.from + node.to; const rawText = view.state.doc.sliceString(globalFrom, globalTo); diff --git a/apps/yaak-client/components/core/Editor/twig/templateTags.ts b/apps/yaak-client/components/core/Editor/twig/templateTags.ts index 41701fad..4ca45113 100644 --- a/apps/yaak-client/components/core/Editor/twig/templateTags.ts +++ b/apps/yaak-client/components/core/Editor/twig/templateTags.ts @@ -1,13 +1,13 @@ -import { syntaxTree } from '@codemirror/language'; -import type { Range } from '@codemirror/state'; -import type { DecorationSet, ViewUpdate } from '@codemirror/view'; -import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view'; -import type { SyntaxNodeRef } from '@lezer/common'; -import { applyFormInputDefaults, validateTemplateFunctionArgs } from '@yaakapp-internal/lib'; -import type { FormInput, JsonPrimitive, TemplateFunction } from '@yaakapp-internal/plugins'; -import { parseTemplate } from '@yaakapp-internal/templates'; -import type { TwigCompletionOption } from './completion'; -import { collectArgumentValues } from './util'; +import { syntaxTree } from "@codemirror/language"; +import type { Range } from "@codemirror/state"; +import type { DecorationSet, ViewUpdate } from "@codemirror/view"; +import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view"; +import type { SyntaxNodeRef } from "@lezer/common"; +import { applyFormInputDefaults, validateTemplateFunctionArgs } from "@yaakapp-internal/lib"; +import type { FormInput, JsonPrimitive, TemplateFunction } from "@yaakapp-internal/plugins"; +import { parseTemplate } from "@yaakapp-internal/templates"; +import type { TwigCompletionOption } from "./completion"; +import { collectArgumentValues } from "./util"; class TemplateTagWidget extends WidgetType { readonly #clickListenerCallback: () => void; @@ -34,24 +34,24 @@ class TemplateTagWidget extends WidgetType { } toDOM() { - const elt = document.createElement('span'); + const elt = document.createElement("span"); elt.className = `x-theme-templateTag template-tag ${ this.option.invalid - ? 'x-theme-templateTag--danger' - : this.option.type === 'variable' - ? 'x-theme-templateTag--primary' - : 'x-theme-templateTag--info' + ? "x-theme-templateTag--danger" + : this.option.type === "variable" + ? "x-theme-templateTag--primary" + : "x-theme-templateTag--info" }`; - elt.title = this.option.invalid ? 'Not Found' : (this.option.value ?? ''); - elt.setAttribute('data-tag-type', this.option.type); - if (typeof this.option.label === 'string') elt.textContent = this.option.label; + elt.title = this.option.invalid ? "Not Found" : (this.option.value ?? ""); + elt.setAttribute("data-tag-type", this.option.type); + if (typeof this.option.label === "string") elt.textContent = this.option.label; else elt.appendChild(this.option.label); - elt.addEventListener('click', this.#clickListenerCallback); + elt.addEventListener("click", this.#clickListenerCallback); return elt; } destroy(dom: HTMLElement) { - dom.removeEventListener('click', this.#clickListenerCallback); + dom.removeEventListener("click", this.#clickListenerCallback); super.destroy(dom); } @@ -72,34 +72,34 @@ function templateTags( from, to, enter(node) { - if (node.name === 'Tag') { + if (node.name === "Tag") { // Don't decorate if the cursor is inside the match if (isSelectionInsideNode(view, node)) return; const rawTag = view.state.doc.sliceString(node.from, node.to); // TODO: Search `node.tree` instead of using Regex here - const inner = rawTag.replace(/^\$\{\[\s*/, '').replace(/\s*]}$/, ''); + const inner = rawTag.replace(/^\$\{\[\s*/, "").replace(/\s*]}$/, ""); let name = inner.match(/([\w.]+)[(]/)?.[1] ?? inner; - if (inner.includes('\n')) { + if (inner.includes("\n")) { return; } // The beta named the function `Response` but was changed in stable. // Keep this here for a while because there's no easy way to migrate - if (name === 'Response') { - name = 'response'; + if (name === "Response") { + name = "response"; } let option = options.find( - (o) => o.name === name || (o.type === 'function' && o.aliases?.includes(name)), + (o) => o.name === name || (o.type === "function" && o.aliases?.includes(name)), ); if (option == null) { const from = node.from; // Cache here so the reference doesn't change option = { - type: 'variable', + type: "variable", invalid: true, name: inner, value: null, @@ -110,7 +110,7 @@ function templateTags( }; } - if (option.type === 'function') { + if (option.type === "function") { const tokens = parseTemplate(rawTag); const rawValues = collectArgumentValues(tokens, option); const values = applyFormInputDefaults(option.args, rawValues); @@ -175,49 +175,49 @@ function makeFunctionLabel( ): HTMLElement | string { if (fn.args.length === 0) return fn.name; - const $outer = document.createElement('span'); - $outer.className = 'fn'; - const $bOpen = document.createElement('span'); - $bOpen.className = 'fn-bracket'; - $bOpen.textContent = '('; + const $outer = document.createElement("span"); + $outer.className = "fn"; + const $bOpen = document.createElement("span"); + $bOpen.className = "fn-bracket"; + $bOpen.textContent = "("; $outer.appendChild(document.createTextNode(fn.name)); $outer.appendChild($bOpen); - const $inner = document.createElement('span'); - $inner.className = 'fn-inner'; - $inner.title = ''; + const $inner = document.createElement("span"); + $inner.className = "fn-inner"; + $inner.title = ""; fn.previewArgs?.forEach((name: string, i: number, all: string[]) => { - const v = String(values[name] || ''); + const v = String(values[name] || ""); if (!v) return; if (all.length > 1) { - const $c = document.createElement('span'); - $c.className = 'fn-arg-name'; + const $c = document.createElement("span"); + $c.className = "fn-arg-name"; $c.textContent = i > 0 ? `, ${name}=` : `${name}=`; $inner.appendChild($c); } - const $v = document.createElement('span'); - $v.className = 'fn-arg-value'; - $v.textContent = v.includes(' ') ? `'${v}'` : v; + const $v = document.createElement("span"); + $v.className = "fn-arg-value"; + $v.textContent = v.includes(" ") ? `'${v}'` : v; $inner.appendChild($v); }); fn.args.forEach((a: FormInput, i: number) => { - if (!('name' in a)) return; + if (!("name" in a)) return; const v = values[a.name]; if (v == null) return; - if (i > 0) $inner.title += '\n'; + if (i > 0) $inner.title += "\n"; $inner.title += `${a.name} = ${JSON.stringify(v)}`; }); if ($inner.childNodes.length === 0) { - $inner.appendChild(document.createTextNode('…')); + $inner.appendChild(document.createTextNode("…")); } $outer.appendChild($inner); - const $bClose = document.createElement('span'); - $bClose.className = 'fn-bracket'; - $bClose.textContent = ')'; + const $bClose = document.createElement("span"); + $bClose.className = "fn-bracket"; + $bClose.textContent = ")"; $outer.appendChild($bClose); return $outer; diff --git a/apps/yaak-client/components/core/Editor/twig/twig.test.ts b/apps/yaak-client/components/core/Editor/twig/twig.test.ts index c000face..2ce95f01 100644 --- a/apps/yaak-client/components/core/Editor/twig/twig.test.ts +++ b/apps/yaak-client/components/core/Editor/twig/twig.test.ts @@ -1,14 +1,14 @@ -// biome-ignore-all lint/suspicious/noTemplateCurlyInString: We're testing this, specifically +/* oxlint-disable no-template-curly-in-string */ -import { describe, expect, test } from 'vitest'; -import { parser } from './twig'; +import { describe, expect, test } from "vite-plus/test"; +import { parser } from "./twig"; function getNodeNames(input: string): string[] { const tree = parser.parse(input); const nodes: string[] = []; const cursor = tree.cursor(); do { - if (cursor.name !== 'Template') { + if (cursor.name !== "Template") { nodes.push(cursor.name); } } while (cursor.next()); @@ -16,93 +16,93 @@ function getNodeNames(input: string): string[] { } function hasTag(input: string): boolean { - return getNodeNames(input).includes('Tag'); + return getNodeNames(input).includes("Tag"); } function hasError(input: string): boolean { - return getNodeNames(input).includes('⚠'); + return getNodeNames(input).includes("⚠"); } -describe('twig grammar', () => { - describe('${[var]} format (valid template tags)', () => { - test('parses simple variable as Tag', () => { - expect(hasTag('${[var]}')).toBe(true); - expect(hasError('${[var]}')).toBe(false); +describe("twig grammar", () => { + describe("${[var]} format (valid template tags)", () => { + test("parses simple variable as Tag", () => { + expect(hasTag("${[var]}")).toBe(true); + expect(hasError("${[var]}")).toBe(false); }); - test('parses variable with whitespace as Tag', () => { - expect(hasTag('${[ var ]}')).toBe(true); - expect(hasError('${[ var ]}')).toBe(false); + test("parses variable with whitespace as Tag", () => { + expect(hasTag("${[ var ]}")).toBe(true); + expect(hasError("${[ var ]}")).toBe(false); }); - test('parses embedded variable as Tag', () => { - expect(hasTag('hello ${[name]} world')).toBe(true); - expect(hasError('hello ${[name]} world')).toBe(false); + test("parses embedded variable as Tag", () => { + expect(hasTag("hello ${[name]} world")).toBe(true); + expect(hasError("hello ${[name]} world")).toBe(false); }); - test('parses function call as Tag', () => { - expect(hasTag('${[fn()]}')).toBe(true); - expect(hasError('${[fn()]}')).toBe(false); + test("parses function call as Tag", () => { + expect(hasTag("${[fn()]}")).toBe(true); + expect(hasError("${[fn()]}")).toBe(false); }); }); - describe('${var} format (should be plain text, not tags)', () => { - test('parses ${var} as plain Text without errors', () => { - expect(hasTag('${var}')).toBe(false); - expect(hasError('${var}')).toBe(false); + describe("${var} format (should be plain text, not tags)", () => { + test("parses ${var} as plain Text without errors", () => { + expect(hasTag("${var}")).toBe(false); + expect(hasError("${var}")).toBe(false); }); - test('parses embedded ${var} as plain Text', () => { - expect(hasTag('hello ${name} world')).toBe(false); - expect(hasError('hello ${name} world')).toBe(false); + test("parses embedded ${var} as plain Text", () => { + expect(hasTag("hello ${name} world")).toBe(false); + expect(hasError("hello ${name} world")).toBe(false); }); - test('parses JSON with ${var} as plain Text', () => { + test("parses JSON with ${var} as plain Text", () => { const json = '{"key": "${value}"}'; expect(hasTag(json)).toBe(false); expect(hasError(json)).toBe(false); }); - test('parses multiple ${var} as plain Text', () => { - expect(hasTag('${a} and ${b}')).toBe(false); - expect(hasError('${a} and ${b}')).toBe(false); + test("parses multiple ${var} as plain Text", () => { + expect(hasTag("${a} and ${b}")).toBe(false); + expect(hasError("${a} and ${b}")).toBe(false); }); }); - describe('mixed content', () => { - test('distinguishes ${var} from ${[var]} in same string', () => { - const input = '${plain} and ${[tag]}'; + describe("mixed content", () => { + test("distinguishes ${var} from ${[var]} in same string", () => { + const input = "${plain} and ${[tag]}"; expect(hasTag(input)).toBe(true); expect(hasError(input)).toBe(false); }); - test('parses JSON with ${[var]} as having Tag', () => { + test("parses JSON with ${[var]} as having Tag", () => { const json = '{"key": "${[value]}"}'; expect(hasTag(json)).toBe(true); expect(hasError(json)).toBe(false); }); }); - describe('edge cases', () => { - test('handles $ at end of string', () => { - expect(hasError('hello$')).toBe(false); - expect(hasTag('hello$')).toBe(false); + describe("edge cases", () => { + test("handles $ at end of string", () => { + expect(hasError("hello$")).toBe(false); + expect(hasTag("hello$")).toBe(false); }); - test('handles ${ at end of string without crash', () => { + test("handles ${ at end of string without crash", () => { // Incomplete syntax may produce errors, but should not crash - expect(() => parser.parse('hello${')).not.toThrow(); + expect(() => parser.parse("hello${")).not.toThrow(); }); - test('handles ${[ without closing without crash', () => { + test("handles ${[ without closing without crash", () => { // Unclosed tag may produce partial match, but should not crash - expect(() => parser.parse('${[unclosed')).not.toThrow(); + expect(() => parser.parse("${[unclosed")).not.toThrow(); }); - test('handles empty ${[]}', () => { + test("handles empty ${[]}", () => { // Empty tags may or may not be valid depending on grammar // Just ensure no crash - expect(() => parser.parse('${[]}')).not.toThrow(); + expect(() => parser.parse("${[]}")).not.toThrow(); }); }); }); diff --git a/apps/yaak-client/components/core/Editor/twig/twig.ts b/apps/yaak-client/components/core/Editor/twig/twig.ts index e124c988..78af862c 100644 --- a/apps/yaak-client/components/core/Editor/twig/twig.ts +++ b/apps/yaak-client/components/core/Editor/twig/twig.ts @@ -1,20 +1,20 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LocalTokenGroup, LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LocalTokenGroup, LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "!^QQOPOOOOOO'#C_'#C_OYOQO'#C^OOOO'#Cc'#CcQQOPOOOOOO'#Cd'#CdO_OQO,58xOOOO-E6a-E6aOOOO-E6b-E6bOOOO1G.d1G.d", - stateData: 'g~OUROYPO~OSTO~OSTOTXO~O', - goto: 'nXPPY^PPPbhTROSTQOSQSORVSQUQRWU', - nodeNames: '⚠ Template Tag TagOpen TagContent TagClose Text', + stateData: "g~OUROYPO~OSTO~OSTOTXO~O", + goto: "nXPPY^PPPbhTROSTQOSQSORVSQUQRWU", + nodeNames: "⚠ Template Tag TagOpen TagContent TagClose Text", maxTerm: 10, propSources: [highlight], skippedNodes: [0], repeatNodeCount: 2, tokenData: "#{~RTOtbtu!zu;'Sb;'S;=`!o<%lOb~gTU~Otbtuvu;'Sb;'S;=`!o<%lOb~yVO#ob#o#p!`#p;'Sb;'S;=`!o<%l~b~Ob~~!u~!cSO!}b#O;'Sb;'S;=`!o<%lOb~!rP;=`<%lb~!zOU~~!}VO#ob#o#p#d#p;'Sb;'S;=`!o<%l~b~Ob~~!u~#gTO!}b!}#O#v#O;'Sb;'S;=`!o<%lOb~#{OY~", - tokenizers: [1, new LocalTokenGroup('b~RP#P#QU~XP#q#r[~aOT~~', 17, 4)], + tokenizers: [1, new LocalTokenGroup("b~RP#P#QU~XP#q#r[~aOT~~", 17, 4)], topRules: { Template: [0, 1] }, tokenPrec: 0, }); diff --git a/apps/yaak-client/components/core/Editor/twig/util.ts b/apps/yaak-client/components/core/Editor/twig/util.ts index 37d6efd1..330ea851 100644 --- a/apps/yaak-client/components/core/Editor/twig/util.ts +++ b/apps/yaak-client/components/core/Editor/twig/util.ts @@ -1,5 +1,5 @@ -import type { FormInput, TemplateFunction } from '@yaakapp-internal/plugins'; -import type { Tokens } from '@yaakapp-internal/templates'; +import type { FormInput, TemplateFunction } from "@yaakapp-internal/plugins"; +import type { Tokens } from "@yaakapp-internal/templates"; /** * Process the initial tokens from the template and merge those with the default values pulled from @@ -8,21 +8,21 @@ import type { Tokens } from '@yaakapp-internal/templates'; export function collectArgumentValues(initialTokens: Tokens, templateFunction: TemplateFunction) { const initial: Record = {}; const initialArgs = - initialTokens.tokens[0]?.type === 'tag' && initialTokens.tokens[0]?.val.type === 'fn' + initialTokens.tokens[0]?.type === "tag" && initialTokens.tokens[0]?.val.type === "fn" ? initialTokens.tokens[0]?.val.args : []; const processArg = (arg: FormInput) => { - if ('inputs' in arg && arg.inputs) { + if ("inputs" in arg && arg.inputs) { arg.inputs.forEach(processArg); } - if (!('name' in arg)) return; + if (!("name" in arg)) return; const initialArg = initialArgs.find((a) => a.name === arg.name); const initialArgValue = - initialArg?.value.type === 'str' + initialArg?.value.type === "str" ? initialArg?.value.text - : initialArg?.value.type === 'bool' + : initialArg?.value.type === "bool" ? initialArg.value.value : undefined; const value = initialArgValue ?? arg.defaultValue; diff --git a/apps/yaak-client/components/core/Editor/url/completion.ts b/apps/yaak-client/components/core/Editor/url/completion.ts index 40acefa3..0b6612e1 100644 --- a/apps/yaak-client/components/core/Editor/url/completion.ts +++ b/apps/yaak-client/components/core/Editor/url/completion.ts @@ -1,9 +1,9 @@ -import { genericCompletion } from '../genericCompletion'; +import { genericCompletion } from "../genericCompletion"; export const completions = genericCompletion({ options: [ - { label: 'http://', type: 'constant' }, - { label: 'https://', type: 'constant' }, + { label: "http://", type: "constant" }, + { label: "https://", type: "constant" }, ], minMatch: 1, }); diff --git a/apps/yaak-client/components/core/Editor/url/extension.ts b/apps/yaak-client/components/core/Editor/url/extension.ts index a46fc1d8..e900b823 100644 --- a/apps/yaak-client/components/core/Editor/url/extension.ts +++ b/apps/yaak-client/components/core/Editor/url/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './url'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./url"; const urlLanguage = LRLanguage.define({ - name: 'url', + name: "url", parser, languageData: {}, }); diff --git a/apps/yaak-client/components/core/Editor/url/highlight.ts b/apps/yaak-client/components/core/Editor/url/highlight.ts index 85467f4f..69c8ac9b 100644 --- a/apps/yaak-client/components/core/Editor/url/highlight.ts +++ b/apps/yaak-client/components/core/Editor/url/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ Protocol: t.comment, diff --git a/apps/yaak-client/components/core/Editor/url/url.ts b/apps/yaak-client/components/core/Editor/url/url.ts index ca17436c..ad421be6 100644 --- a/apps/yaak-client/components/core/Editor/url/url.ts +++ b/apps/yaak-client/components/core/Editor/url/url.ts @@ -1,13 +1,13 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "!|OQOPOOQYOPOOOTOPOOObOQO'#CdOjOPO'#C`OuOSO'#CcQOOOOOQ]OPOOOOOO,59O,59OOOOO-E6b-E6bOzOPO,58}O!SOSO'#CeO!XOPO1G.iOOOO,59P,59POOOO-E6c-E6c", - stateData: '!g~OQQORPO~OZRO[TO~OTWOUWO~OZROYSX[SX~O]YO~O^ZOYVa~O]]O~O^ZOYVi~OQRTUT~', - goto: 'nYPPPPZPP^bhRVPTUPVQSPRXSQ[YR^[', - nodeNames: '⚠ url Protocol Host Path Placeholder PathSegment Query', + stateData: "!g~OQQORPO~OZRO[TO~OTWOUWO~OZROYSX[SX~O]YO~O^ZOYVa~O]]O~O^ZOYVi~OQRTUT~", + goto: "nYPPPPZPP^bhRVPTUPVQSPRXSQ[YR^[", + nodeNames: "⚠ url Protocol Host Path Placeholder PathSegment Query", maxTerm: 14, propSources: [highlight], skippedNodes: [0], diff --git a/apps/yaak-client/components/core/EventViewer.tsx b/apps/yaak-client/components/core/EventViewer.tsx index 1716ff85..91413926 100644 --- a/apps/yaak-client/components/core/EventViewer.tsx +++ b/apps/yaak-client/components/core/EventViewer.tsx @@ -1,15 +1,15 @@ -import type { Virtualizer } from '@tanstack/react-virtual'; -import { Banner, HStack, SplitLayout } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import { format } from 'date-fns'; -import type { ReactNode } from 'react'; -import { useCallback, useMemo, useRef, useState } from 'react'; -import { useEventViewerKeyboard } from '../../hooks/useEventViewerKeyboard'; -import { CopyIconButton } from '../CopyIconButton'; -import { AutoScroller } from './AutoScroller'; -import { Button } from './Button'; -import { IconButton } from './IconButton'; -import { Separator } from './Separator'; +import type { Virtualizer } from "@tanstack/react-virtual"; +import { Banner, HStack, SplitLayout } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import { format } from "date-fns"; +import type { ReactNode } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; +import { useEventViewerKeyboard } from "../../hooks/useEventViewerKeyboard"; +import { CopyIconButton } from "../CopyIconButton"; +import { AutoScroller } from "./AutoScroller"; +import { Button } from "./Button"; +import { IconButton } from "./IconButton"; +import { Separator } from "./Separator"; interface EventViewerProps { /** Array of events to display */ @@ -68,8 +68,8 @@ export function EventViewer({ defaultRatio = 0.4, enableKeyboardNav = true, isLoading = false, - loadingMessage = 'Loading events...', - emptyMessage = 'No events recorded', + loadingMessage = "Loading events...", + emptyMessage = "No events recorded", onActiveIndexChange, }: EventViewerProps) { const [activeIndex, setActiveIndexInternal] = useState(null); @@ -80,7 +80,7 @@ export function EventViewer({ (indexOrUpdater: number | null | ((prev: number | null) => number | null)) => { setActiveIndexInternal((prev) => { const newIndex = - typeof indexOrUpdater === 'function' ? indexOrUpdater(prev) : indexOrUpdater; + typeof indexOrUpdater === "function" ? indexOrUpdater(prev) : indexOrUpdater; onActiveIndexChange?.(newIndex); return newIndex; }); @@ -127,7 +127,7 @@ export function EventViewer({ setIsPanelOpen(true); // Scroll to ensure selected item is visible after panel opens requestAnimationFrame(() => { - virtualizerRef.current?.scrollToIndex(index, { align: 'auto' }); + virtualizerRef.current?.scrollToIndex(index, { align: "auto" }); }); }, [setActiveIndex], @@ -230,7 +230,7 @@ export function EventDetailHeader({ copyText, onClose, }: EventDetailHeaderProps) { - const formattedTime = timestamp ? format(new Date(`${timestamp}Z`), 'HH:mm:ss.SSS') : null; + const formattedTime = timestamp ? format(new Date(`${timestamp}Z`), "HH:mm:ss.SSS") : null; return (
@@ -255,7 +255,7 @@ export function EventDetailHeader({ className={classNames( copyText != null || formattedTime || - ((actions ?? []).length > 0 && 'border-l border-l-surface-highlight ml-2 pl-3'), + ((actions ?? []).length > 0 && "border-l border-l-surface-highlight ml-2 pl-3"), )} > {icon}
{content}
- {timestamp &&
{format(`${timestamp}Z`, 'HH:mm:ss.SSS')}
} + {timestamp &&
{format(`${timestamp}Z`, "HH:mm:ss.SSS")}
}
); diff --git a/apps/yaak-client/components/core/Hotkey.tsx b/apps/yaak-client/components/core/Hotkey.tsx index 95e6910f..485d13d5 100644 --- a/apps/yaak-client/components/core/Hotkey.tsx +++ b/apps/yaak-client/components/core/Hotkey.tsx @@ -1,12 +1,12 @@ -import { HStack } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import type { HotkeyAction } from '../../hooks/useHotKey'; -import { useFormattedHotkey } from '../../hooks/useHotKey'; +import { HStack } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import type { HotkeyAction } from "../../hooks/useHotKey"; +import { useFormattedHotkey } from "../../hooks/useHotKey"; interface Props { action: HotkeyAction | null; className?: string; - variant?: 'text' | 'with-bg'; + variant?: "text" | "with-bg"; } export function Hotkey({ action, className, variant }: Props) { @@ -21,7 +21,7 @@ export function Hotkey({ action, className, variant }: Props) { interface HotkeyRawProps { labelParts: string[]; className?: string; - variant?: 'text' | 'with-bg'; + variant?: "text" | "with-bg"; } export function HotkeyRaw({ labelParts, className, variant }: HotkeyRawProps) { @@ -29,9 +29,9 @@ export function HotkeyRaw({ labelParts, className, variant }: HotkeyRawProps) { {labelParts.map((char, index) => ( diff --git a/apps/yaak-client/components/core/HotkeyLabel.tsx b/apps/yaak-client/components/core/HotkeyLabel.tsx index a4a6fb84..ab2e7360 100644 --- a/apps/yaak-client/components/core/HotkeyLabel.tsx +++ b/apps/yaak-client/components/core/HotkeyLabel.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; -import type { HotkeyAction } from '../../hooks/useHotKey'; -import { useHotkeyLabel } from '../../hooks/useHotKey'; +import classNames from "classnames"; +import type { HotkeyAction } from "../../hooks/useHotKey"; +import { useHotkeyLabel } from "../../hooks/useHotKey"; interface Props { action: HotkeyAction; @@ -10,6 +10,6 @@ interface Props { export function HotkeyLabel({ action, className }: Props) { const label = useHotkeyLabel(action); return ( - {label} + {label} ); } diff --git a/apps/yaak-client/components/core/HotkeyList.tsx b/apps/yaak-client/components/core/HotkeyList.tsx index 06719d27..202dd059 100644 --- a/apps/yaak-client/components/core/HotkeyList.tsx +++ b/apps/yaak-client/components/core/HotkeyList.tsx @@ -1,9 +1,9 @@ -import classNames from 'classnames'; -import type { ReactNode } from 'react'; -import { Fragment } from 'react'; -import type { HotkeyAction } from '../../hooks/useHotKey'; -import { Hotkey } from './Hotkey'; -import { HotkeyLabel } from './HotkeyLabel'; +import classNames from "classnames"; +import type { ReactNode } from "react"; +import { Fragment } from "react"; +import type { HotkeyAction } from "../../hooks/useHotKey"; +import { Hotkey } from "./Hotkey"; +import { HotkeyLabel } from "./HotkeyLabel"; interface Props { hotkeys: HotkeyAction[]; @@ -13,7 +13,7 @@ interface Props { export const HotkeyList = ({ hotkeys, bottomSlot, className }: Props) => { return ( -
+
{hotkeys.map((hotkey) => ( diff --git a/apps/yaak-client/components/core/HttpMethodTag.tsx b/apps/yaak-client/components/core/HttpMethodTag.tsx index dfed18a5..057971e2 100644 --- a/apps/yaak-client/components/core/HttpMethodTag.tsx +++ b/apps/yaak-client/components/core/HttpMethodTag.tsx @@ -1,8 +1,8 @@ -import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; -import { settingsAtom } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import { memo } from 'react'; +import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; +import { settingsAtom } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import { memo } from "react"; interface Props { request: HttpRequest | GrpcRequest | WebsocketRequest; @@ -12,27 +12,32 @@ interface Props { } const methodNames: Record = { - get: 'GET', - put: 'PUT', - post: 'POST', - patch: 'PTCH', - delete: 'DELE', - options: 'OPTN', - head: 'HEAD', - query: 'QURY', - graphql: 'GQL', - grpc: 'GRPC', - websocket: 'WS', + get: "GET", + put: "PUT", + post: "POST", + patch: "PTCH", + delete: "DELE", + options: "OPTN", + head: "HEAD", + query: "QURY", + graphql: "GQL", + grpc: "GRPC", + websocket: "WS", }; -export const HttpMethodTag = memo(function HttpMethodTag({ request, className, short, noAlias }: Props) { +export const HttpMethodTag = memo(function HttpMethodTag({ + request, + className, + short, + noAlias, +}: Props) { const method = - request.model === 'http_request' && (request.bodyType === 'graphql' && !noAlias) - ? 'graphql' - : request.model === 'grpc_request' - ? 'grpc' - : request.model === 'websocket_request' - ? 'websocket' + request.model === "http_request" && request.bodyType === "graphql" && !noAlias + ? "graphql" + : request.model === "grpc_request" + ? "grpc" + : request.model === "websocket_request" + ? "websocket" : request.method; return ; @@ -52,7 +57,7 @@ export function HttpMethodTagRaw({ let label = method.toUpperCase(); if (short) { label = methodNames[method.toLowerCase()] ?? method.slice(0, 4); - label = label.padStart(4, ' '); + label = label.padStart(4, " "); } const m = method.toUpperCase(); @@ -64,20 +69,20 @@ export function HttpMethodTagRaw({ {label} diff --git a/apps/yaak-client/components/core/HttpResponseDurationTag.tsx b/apps/yaak-client/components/core/HttpResponseDurationTag.tsx index 21bae26c..6c7c2ad2 100644 --- a/apps/yaak-client/components/core/HttpResponseDurationTag.tsx +++ b/apps/yaak-client/components/core/HttpResponseDurationTag.tsx @@ -1,5 +1,5 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { useEffect, useRef, useState } from 'react'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { useEffect, useRef, useState } from "react"; interface Props { response: HttpResponse; @@ -12,17 +12,17 @@ export function HttpResponseDurationTag({ response }: Props) { // Calculate the duration of the response for use when the response hasn't finished yet useEffect(() => { clearInterval(timeout.current); - if (response.state === 'closed') return; + if (response.state === "closed") return; timeout.current = setInterval(() => { setFallbackElapsed(Date.now() - new Date(`${response.createdAt}Z`).getTime()); }, 100); return () => clearInterval(timeout.current); }, [response.createdAt, response.state]); - const dnsValue = response.elapsedDns > 0 ? formatMillis(response.elapsedDns) : '--'; + const dnsValue = response.elapsedDns > 0 ? formatMillis(response.elapsedDns) : "--"; const title = `DNS: ${dnsValue}\nHEADER: ${formatMillis(response.elapsedHeaders)}\nTOTAL: ${formatMillis(response.elapsed)}`; - const elapsed = response.state === 'closed' ? response.elapsed : fallbackElapsed; + const elapsed = response.state === "closed" ? response.elapsed : fallbackElapsed; return ( diff --git a/apps/yaak-client/components/core/HttpStatusTag.tsx b/apps/yaak-client/components/core/HttpStatusTag.tsx index 7a56752a..a705f520 100644 --- a/apps/yaak-client/components/core/HttpStatusTag.tsx +++ b/apps/yaak-client/components/core/HttpStatusTag.tsx @@ -1,5 +1,5 @@ -import type { HttpResponse, HttpResponseState } from '@yaakapp-internal/models'; -import classNames from 'classnames'; +import type { HttpResponse, HttpResponseState } from "@yaakapp-internal/models"; +import classNames from "classnames"; interface Props { response: HttpResponse; @@ -20,35 +20,35 @@ export function HttpStatusTagRaw({ showReason, statusReason, short, -}: Omit & { +}: Omit & { status: number | string; state?: HttpResponseState; statusReason?: string | null; }) { let colorClass: string; let label = `${status}`; - const statusN = typeof status === 'number' ? status : parseInt(status, 10); + const statusN = typeof status === "number" ? status : parseInt(status, 10); - if (state === 'initialized') { - label = short ? 'CONN' : 'CONNECTING'; - colorClass = 'text-text-subtle'; + if (state === "initialized") { + label = short ? "CONN" : "CONNECTING"; + colorClass = "text-text-subtle"; } else if (statusN < 100) { - label = short ? 'ERR' : 'ERROR'; - colorClass = 'text-danger'; + label = short ? "ERR" : "ERROR"; + colorClass = "text-danger"; } else if (statusN < 200) { - colorClass = 'text-info'; + colorClass = "text-info"; } else if (statusN < 300) { - colorClass = 'text-success'; + colorClass = "text-success"; } else if (statusN < 400) { - colorClass = 'text-primary'; + colorClass = "text-primary"; } else if (statusN < 500) { - colorClass = 'text-warning'; + colorClass = "text-warning"; } else { - colorClass = 'text-danger'; + colorClass = "text-danger"; } return ( - + {label} {showReason && statusReason} ); diff --git a/apps/yaak-client/components/core/IconButton.tsx b/apps/yaak-client/components/core/IconButton.tsx index be36ded2..26b642a6 100644 --- a/apps/yaak-client/components/core/IconButton.tsx +++ b/apps/yaak-client/components/core/IconButton.tsx @@ -1,10 +1,10 @@ import { IconButton as BaseIconButton, type IconButtonProps as BaseIconButtonProps, -} from '@yaakapp-internal/ui'; -import { forwardRef, useImperativeHandle, useRef } from 'react'; -import type { HotkeyAction } from '../../hooks/useHotKey'; -import { useFormattedHotkey, useHotKey } from '../../hooks/useHotKey'; +} from "@yaakapp-internal/ui"; +import { forwardRef, useImperativeHandle, useRef } from "react"; +import type { HotkeyAction } from "../../hooks/useHotKey"; +import { useFormattedHotkey, useHotKey } from "../../hooks/useHotKey"; export type IconButtonProps = BaseIconButtonProps & { hotkeyAction?: HotkeyAction; @@ -16,8 +16,8 @@ export const IconButton = forwardRef(functio { hotkeyAction, hotkeyPriority, hotkeyLabelOnly, title, ...props }: IconButtonProps, ref, ) { - const hotkeyTrigger = useFormattedHotkey(hotkeyAction ?? null)?.join(''); - const fullTitle = hotkeyTrigger ? `${title ?? ''} ${hotkeyTrigger}`.trim() : title; + const hotkeyTrigger = useFormattedHotkey(hotkeyAction ?? null)?.join(""); + const fullTitle = hotkeyTrigger ? `${title ?? ""} ${hotkeyTrigger}`.trim() : title; const buttonRef = useRef(null); useImperativeHandle( diff --git a/apps/yaak-client/components/core/IconTooltip.tsx b/apps/yaak-client/components/core/IconTooltip.tsx index 0b442317..3886048e 100644 --- a/apps/yaak-client/components/core/IconTooltip.tsx +++ b/apps/yaak-client/components/core/IconTooltip.tsx @@ -1,18 +1,18 @@ -import { Icon, type IconProps } from '@yaakapp-internal/ui'; -import type { TooltipProps } from './Tooltip'; -import { Tooltip } from './Tooltip'; +import { Icon, type IconProps } from "@yaakapp-internal/ui"; +import type { TooltipProps } from "./Tooltip"; +import { Tooltip } from "./Tooltip"; -type Props = Omit & { - icon?: IconProps['icon']; - iconSize?: IconProps['size']; - iconColor?: IconProps['color']; +type Props = Omit & { + icon?: IconProps["icon"]; + iconSize?: IconProps["size"]; + iconColor?: IconProps["color"]; className?: string; tabIndex?: number; }; export function IconTooltip({ content, - icon = 'info', + icon = "info", iconColor, iconSize, ...tooltipProps diff --git a/apps/yaak-client/components/core/Input.tsx b/apps/yaak-client/components/core/Input.tsx index c165d044..c636daf3 100644 --- a/apps/yaak-client/components/core/Input.tsx +++ b/apps/yaak-client/components/core/Input.tsx @@ -1,45 +1,45 @@ -import type { EditorView } from '@codemirror/view'; -import type { Color } from '@yaakapp-internal/plugins'; -import { HStack, Icon, type IconProps } from '@yaakapp-internal/ui'; -import classNames from 'classnames'; -import type { ReactNode } from 'react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { createFastMutation } from '../../hooks/useFastMutation'; -import { useIsEncryptionEnabled } from '../../hooks/useIsEncryptionEnabled'; -import { useStateWithDeps } from '../../hooks/useStateWithDeps'; -import { copyToClipboard } from '../../lib/copy'; +import type { EditorView } from "@codemirror/view"; +import type { Color } from "@yaakapp-internal/plugins"; +import { HStack, Icon, type IconProps } from "@yaakapp-internal/ui"; +import classNames from "classnames"; +import type { ReactNode } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { createFastMutation } from "../../hooks/useFastMutation"; +import { useIsEncryptionEnabled } from "../../hooks/useIsEncryptionEnabled"; +import { useStateWithDeps } from "../../hooks/useStateWithDeps"; +import { copyToClipboard } from "../../lib/copy"; import { analyzeTemplate, convertTemplateToInsecure, convertTemplateToSecure, -} from '../../lib/encryption'; -import { generateId } from '../../lib/generateId'; +} from "../../lib/encryption"; +import { generateId } from "../../lib/generateId"; import { setupOrConfigureEncryption, withEncryptionEnabled, -} from '../../lib/setupOrConfigureEncryption'; -import { Button } from './Button'; -import type { DropdownItem } from './Dropdown'; -import { Dropdown } from './Dropdown'; -import type { EditorProps } from './Editor/Editor'; -import { Editor } from './Editor/LazyEditor'; -import { IconButton } from './IconButton'; -import { IconTooltip } from './IconTooltip'; -import { Label } from './Label'; +} from "../../lib/setupOrConfigureEncryption"; +import { Button } from "./Button"; +import type { DropdownItem } from "./Dropdown"; +import { Dropdown } from "./Dropdown"; +import type { EditorProps } from "./Editor/Editor"; +import { Editor } from "./Editor/LazyEditor"; +import { IconButton } from "./IconButton"; +import { IconTooltip } from "./IconTooltip"; +import { Label } from "./Label"; export type InputProps = Pick< EditorProps, - | 'language' - | 'autocomplete' - | 'forcedEnvironmentId' - | 'forceUpdateKey' - | 'disabled' - | 'autoFocus' - | 'autoSelect' - | 'autocompleteVariables' - | 'autocompleteFunctions' - | 'onKeyDown' - | 'readOnly' + | "language" + | "autocomplete" + | "forcedEnvironmentId" + | "forceUpdateKey" + | "disabled" + | "autoFocus" + | "autoSelect" + | "autocompleteVariables" + | "autocompleteFunctions" + | "onKeyDown" + | "readOnly" > & { className?: string; containerClassName?: string; @@ -51,7 +51,7 @@ export type InputProps = Pick< help?: ReactNode; label: ReactNode; labelClassName?: string; - labelPosition?: 'top' | 'left'; + labelPosition?: "top" | "left"; leftSlot?: ReactNode; multiLine?: boolean; name?: string; @@ -59,15 +59,15 @@ export type InputProps = Pick< onChange?: (value: string) => void; onFocus?: () => void; onPaste?: (value: string) => void; - onPasteOverwrite?: EditorProps['onPasteOverwrite']; + onPasteOverwrite?: EditorProps["onPasteOverwrite"]; placeholder?: string; required?: boolean; rightSlot?: ReactNode; - size?: '2xs' | 'xs' | 'sm' | 'md' | 'auto'; - stateKey: EditorProps['stateKey']; - extraExtensions?: EditorProps['extraExtensions']; + size?: "2xs" | "xs" | "sm" | "md" | "auto"; + stateKey: EditorProps["stateKey"]; + extraExtensions?: EditorProps["extraExtensions"]; tint?: Color; - type?: 'text' | 'password'; + type?: "text" | "password"; validate?: boolean | ((v: string) => boolean); wrapLines?: boolean; setRef?: (h: InputHandle | null) => void; @@ -78,13 +78,13 @@ export interface InputHandle { isFocused: () => boolean; value: () => string; selectAll: () => void; - dispatch: EditorView['dispatch']; + dispatch: EditorView["dispatch"]; } export function Input({ type, ...props }: InputProps) { // If it's a password and template functions are supported (ie. secure(...)) then // use the encrypted input component. - if (type === 'password' && props.autocompleteFunctions) { + if (type === "password" && props.autocompleteFunctions) { return ; } return ; @@ -103,7 +103,7 @@ function BaseInput({ inputWrapperClassName, label, labelClassName, - labelPosition = 'top', + labelPosition = "top", leftSlot, multiLine, onBlur, @@ -115,17 +115,17 @@ function BaseInput({ readOnly, required, rightSlot, - size = 'md', + size = "md", stateKey, tint, - type = 'text', + type = "text", validate, wrapLines, setRef, ...props }: InputProps) { const [focused, setFocused] = useState(false); - const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]); + const [obscured, setObscured] = useStateWithDeps(type === "password", [type]); const [hasChanged, setHasChanged] = useStateWithDeps(false, [forceUpdateKey]); const editorRef = useRef(null); const skipNextFocus = useRef(false); @@ -140,7 +140,7 @@ function BaseInput({ editorRef.current.dispatch({ selection: { anchor, head: anchor }, scrollIntoView: true }); }, isFocused: () => editorRef.current?.hasFocus ?? false, - value: () => editorRef.current?.state.doc.toString() ?? '', + value: () => editorRef.current?.state.doc.toString() ?? "", dispatch: (...args) => { // biome-ignore lint/suspicious/noExplicitAny: none editorRef.current?.dispatch(...(args as any)); @@ -168,9 +168,9 @@ function BaseInput({ const fn = () => { skipNextFocus.current = true; }; - window.addEventListener('focus', fn); + window.addEventListener("focus", fn); return () => { - window.removeEventListener('focus', fn); + window.removeEventListener("focus", fn); }; }, []); @@ -201,13 +201,13 @@ function BaseInput({ const id = useRef(`input-${generateId()}`); const editorClassName = classNames( className, - '!bg-transparent min-w-0 h-auto w-full focus:outline-none placeholder:text-placeholder', + "!bg-transparent min-w-0 h-auto w-full focus:outline-none placeholder:text-placeholder", ); const isValid = useMemo(() => { - if (required && !validateRequire(defaultValue ?? '')) return false; - if (typeof validate === 'boolean') return validate; - if (typeof validate === 'function' && !validate(defaultValue ?? '')) return false; + if (required && !validateRequire(defaultValue ?? "")) return false; + if (typeof validate === "boolean") return validate; + if (typeof validate === "function" && !validate(defaultValue ?? "")) return false; return true; }, [required, defaultValue, validate]); @@ -224,12 +224,12 @@ function BaseInput({ // Submit the nearest form on Enter key press const handleKeyDown = useCallback( (e: KeyboardEvent) => { - if (e.key !== 'Enter') return; + if (e.key !== "Enter") return; - const form = wrapperRef.current?.closest('form'); + const form = wrapperRef.current?.closest("form"); if (!isValid || form == null) return; - form?.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true })); + form?.dispatchEvent(new Event("submit", { cancelable: true, bubbles: true })); }, [isValid], ); @@ -238,11 +238,11 @@ function BaseInput({