mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-25 02:41:21 +01:00
Compare commits
53 Commits
dependabot
...
wip/yaak-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2000c86d8 | ||
|
|
7577846369 | ||
|
|
903f57a415 | ||
|
|
d0f1708017 | ||
|
|
ee69db0f12 | ||
|
|
7314aedc71 | ||
|
|
3c4035097a | ||
|
|
77ec87ea17 | ||
|
|
4d792c7f9f | ||
|
|
d253d1605c | ||
|
|
bc8a449b8a | ||
|
|
7fbce4e808 | ||
|
|
f91f40e3a1 | ||
|
|
024b0a3cd3 | ||
|
|
9e0a708011 | ||
|
|
d8ce5c9d1a | ||
|
|
f7ff964fe5 | ||
|
|
cc504e0a1c | ||
|
|
47f0daabff | ||
|
|
87e60372fe | ||
|
|
7e7faa69df | ||
|
|
0b7705d915 | ||
|
|
5e3ef70d93 | ||
|
|
4968237ece | ||
|
|
568a1b80ed | ||
|
|
d4a6735881 | ||
|
|
0c52fd03e2 | ||
|
|
3e7d04b2f3 | ||
|
|
6600116b1a | ||
|
|
7be53ca330 | ||
|
|
f51f72a332 | ||
|
|
90365f0723 | ||
|
|
e87c3291e7 | ||
|
|
a0442fb42b | ||
|
|
12ece44197 | ||
|
|
4c041e68a9 | ||
|
|
6534421733 | ||
|
|
6e11894f79 | ||
|
|
96a22c68f2 | ||
|
|
0a616eb5e2 | ||
|
|
7382287bef | ||
|
|
a5433fbc74 | ||
|
|
6f8c4c06bb | ||
|
|
4c37e62146 | ||
|
|
cf28229f5f | ||
|
|
3586c8fe24 | ||
|
|
d99898f39b | ||
|
|
ff6686f982 | ||
|
|
6f9e4ada15 | ||
|
|
fd100330a6 | ||
|
|
6915778c06 | ||
|
|
e26705f016 | ||
|
|
32f22aad67 |
@@ -8,7 +8,7 @@ Make Yaak runnable as a standalone CLI without Tauri as a dependency. The core R
|
||||
|
||||
```
|
||||
crates/ # Core crates - should NOT depend on Tauri
|
||||
crates-tauri/ # Tauri-specific crates (yaak-app, yaak-tauri-utils, etc.)
|
||||
crates-tauri/ # Tauri-specific crates (yaak-app-client, yaak-tauri-utils, etc.)
|
||||
crates-cli/ # CLI crate (yaak-cli)
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ crates-cli/ # CLI crate (yaak-cli)
|
||||
|
||||
### 1. Folder Restructure
|
||||
|
||||
- Moved Tauri-dependent app code to `crates-tauri/yaak-app/`
|
||||
- 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
|
||||
|
||||
@@ -50,14 +50,14 @@ crates-cli/ # CLI crate (yaak-cli)
|
||||
3. Move extension traits (e.g., `SomethingManagerExt`) to yaak-app or yaak-tauri-utils
|
||||
4. Initialize managers in yaak-app's `.setup()` block
|
||||
5. Remove `tauri` from Cargo.toml dependencies
|
||||
6. Update `crates-tauri/yaak-app/capabilities/default.json` to remove the plugin permission
|
||||
6. Update `crates-tauri/yaak-app-client/capabilities/default.json` to remove the plugin permission
|
||||
7. Replace `tauri::async_runtime::block_on` with `tokio::runtime::Handle::current().block_on()`
|
||||
|
||||
## Key Files
|
||||
|
||||
- `crates-tauri/yaak-app/src/lib.rs` - Main Tauri app, setup block initializes managers
|
||||
- `crates-tauri/yaak-app/src/commands.rs` - Migrated Tauri commands
|
||||
- `crates-tauri/yaak-app/src/models_ext.rs` - Database plugin and extension traits
|
||||
- `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
|
||||
- `crates-tauri/yaak-tauri-utils/src/window.rs` - WorkspaceWindowTrait for window state
|
||||
- `crates/yaak-models/src/lib.rs` - Contains `init_standalone()` for CLI usage
|
||||
|
||||
@@ -79,5 +79,5 @@ e718a5f1 Refactor models_ext to use init_standalone from yaak-models
|
||||
## Testing
|
||||
|
||||
- Run `cargo check -p <crate>` to verify a crate builds without Tauri
|
||||
- Run `npm run app-dev` to test the Tauri app still works
|
||||
- Run `npm run client:dev` to test the Tauri app still works
|
||||
- Run `cargo run -p yaak-cli -- --help` to test the CLI
|
||||
|
||||
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,5 +1,5 @@
|
||||
crates-tauri/yaak-app/vendored/**/* linguist-generated=true
|
||||
crates-tauri/yaak-app/gen/schemas/**/* linguist-generated=true
|
||||
crates-tauri/yaak-app-client/vendored/**/* linguist-generated=true
|
||||
crates-tauri/yaak-app-client/gen/schemas/**/* linguist-generated=true
|
||||
**/bindings/* linguist-generated=true
|
||||
crates/yaak-templates/pkg/* linguist-generated=true
|
||||
|
||||
|
||||
8
.github/workflows/release-app.yml
vendored
8
.github/workflows/release-app.yml
vendored
@@ -125,8 +125,8 @@ jobs:
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
# Sign vendored binaries with hardened runtime and their specific entitlements
|
||||
codesign --force --options runtime --entitlements crates-tauri/yaak-app/macos/entitlements.yaakprotoc.plist --sign "$APPLE_SIGNING_IDENTITY" crates-tauri/yaak-app/vendored/protoc/yaakprotoc || true
|
||||
codesign --force --options runtime --entitlements crates-tauri/yaak-app/macos/entitlements.yaaknode.plist --sign "$APPLE_SIGNING_IDENTITY" crates-tauri/yaak-app/vendored/node/yaaknode || true
|
||||
codesign --force --options runtime --entitlements crates-tauri/yaak-app-client/macos/entitlements.yaakprotoc.plist --sign "$APPLE_SIGNING_IDENTITY" crates-tauri/yaak-app-client/vendored/protoc/yaakprotoc || true
|
||||
codesign --force --options runtime --entitlements crates-tauri/yaak-app-client/macos/entitlements.yaaknode.plist --sign "$APPLE_SIGNING_IDENTITY" crates-tauri/yaak-app-client/vendored/node/yaaknode || true
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
releaseBody: "[Changelog __VERSION__](https://yaak.app/blog/__VERSION__)"
|
||||
releaseDraft: true
|
||||
prerelease: true
|
||||
args: "${{ matrix.args }} --config ./crates-tauri/yaak-app/tauri.release.conf.json"
|
||||
args: "${{ matrix.args }} --config ./crates-tauri/yaak-app-client/tauri.release.conf.json"
|
||||
|
||||
# Build a per-machine NSIS installer for enterprise deployment (PDQ, SCCM, Intune)
|
||||
- name: Build and upload machine-wide installer (Windows only)
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
run: |
|
||||
Get-ChildItem -Recurse -Path target -File -Filter "*.exe.sig" | Remove-Item -Force
|
||||
npx tauri bundle ${{ matrix.args }} --bundles nsis --config ./crates-tauri/yaak-app/tauri.release.conf.json --config '{"bundle":{"createUpdaterArtifacts":true,"windows":{"nsis":{"installMode":"perMachine"}}}}'
|
||||
npx tauri bundle ${{ matrix.args }} --bundles nsis --config ./crates-tauri/yaak-app-client/tauri.release.conf.json --config '{"bundle":{"createUpdaterArtifacts":true,"windows":{"nsis":{"installMode":"perMachine"}}}}'
|
||||
$setup = Get-ChildItem -Recurse -Path target -Filter "*setup*.exe" | Select-Object -First 1
|
||||
$setupSig = "$($setup.FullName).sig"
|
||||
$dest = $setup.FullName -replace '-setup\.exe$', '-setup-machine.exe'
|
||||
|
||||
6
.github/workflows/release-cli-npm.yml
vendored
6
.github/workflows/release-cli-npm.yml
vendored
@@ -45,8 +45,8 @@ jobs:
|
||||
with:
|
||||
name: vendored-assets
|
||||
path: |
|
||||
crates-tauri/yaak-app/vendored/plugin-runtime/index.cjs
|
||||
crates-tauri/yaak-app/vendored/plugins
|
||||
crates-tauri/yaak-app-client/vendored/plugin-runtime/index.cjs
|
||||
crates-tauri/yaak-app-client/vendored/plugins
|
||||
if-no-files-found: error
|
||||
|
||||
build-binaries:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: vendored-assets
|
||||
path: crates-tauri/yaak-app/vendored
|
||||
path: crates-tauri/yaak-app-client/vendored
|
||||
|
||||
- name: Set CLI build version
|
||||
shell: bash
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -39,7 +39,8 @@ codebook.toml
|
||||
target
|
||||
|
||||
# Per-worktree Tauri config (generated by post-checkout hook)
|
||||
crates-tauri/yaak-app/tauri.worktree.conf.json
|
||||
crates-tauri/yaak-app-client/tauri.worktree.conf.json
|
||||
crates-tauri/yaak-app-proxy/tauri.worktree.conf.json
|
||||
|
||||
# Tauri auto-generated permission files
|
||||
**/permissions/autogenerated
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
**/bindings/**
|
||||
**/routeTree.gen.ts
|
||||
crates/yaak-templates/pkg/**
|
||||
|
||||
157
Cargo.lock
generated
157
Cargo.lock
generated
@@ -488,6 +488,28 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf"
|
||||
dependencies = [
|
||||
"aws-lc-sys",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-sys"
|
||||
version = "0.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cmake",
|
||||
"dunce",
|
||||
"fs_extra",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.9"
|
||||
@@ -2209,6 +2231,12 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
@@ -5132,6 +5160,16 @@ dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
@@ -5984,6 +6022,19 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"yasna",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.12"
|
||||
@@ -6735,6 +6786,8 @@ version = "0.23.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log 0.4.29",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -6803,10 +6856,11 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.10"
|
||||
version = "0.103.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
|
||||
checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
@@ -10245,7 +10299,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-app"
|
||||
name = "yaak-app-client"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"charset",
|
||||
@@ -10303,9 +10357,25 @@ dependencies = [
|
||||
"yaak-tauri-utils",
|
||||
"yaak-templates",
|
||||
"yaak-tls",
|
||||
"yaak-window",
|
||||
"yaak-ws",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-app-proxy"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"log 0.4.29",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-os",
|
||||
"yaak-mac-window",
|
||||
"yaak-proxy-lib",
|
||||
"yaak-rpc",
|
||||
"yaak-window",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-cli"
|
||||
version = "0.1.0"
|
||||
@@ -10375,6 +10445,25 @@ dependencies = [
|
||||
"yaak-models",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-database"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"include_dir",
|
||||
"log 0.4.29",
|
||||
"nanoid",
|
||||
"r2d2",
|
||||
"r2d2_sqlite",
|
||||
"rusqlite",
|
||||
"sea-query",
|
||||
"sea-query-rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-fonts"
|
||||
version = "0.1.0"
|
||||
@@ -10447,6 +10536,7 @@ dependencies = [
|
||||
"hyper-util",
|
||||
"log 0.4.29",
|
||||
"mime_guess",
|
||||
"native-tls",
|
||||
"regex 1.11.1",
|
||||
"reqwest",
|
||||
"serde",
|
||||
@@ -10516,6 +10606,7 @@ dependencies = [
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs",
|
||||
"yaak-core",
|
||||
"yaak-database",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10547,6 +10638,52 @@ dependencies = [
|
||||
"zip-extract",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-proxy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"pem",
|
||||
"rcgen",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-proxy-lib"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"include_dir",
|
||||
"log 0.4.29",
|
||||
"r2d2",
|
||||
"r2d2_sqlite",
|
||||
"rusqlite",
|
||||
"sea-query",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"ts-rs",
|
||||
"yaak-database",
|
||||
"yaak-proxy",
|
||||
"yaak-rpc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-rpc"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"log 0.4.29",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-sse"
|
||||
version = "0.1.0"
|
||||
@@ -10612,6 +10749,17 @@ dependencies = [
|
||||
"yaak-models",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-window"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log 0.4.29",
|
||||
"md5 0.8.0",
|
||||
"rand 0.9.1",
|
||||
"tauri",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-ws"
|
||||
version = "0.1.0"
|
||||
@@ -10643,6 +10791,9 @@ name = "yasna"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
||||
dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
|
||||
19
Cargo.toml
19
Cargo.toml
@@ -2,6 +2,9 @@
|
||||
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",
|
||||
@@ -17,14 +20,19 @@ members = [
|
||||
"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",
|
||||
"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]
|
||||
@@ -47,6 +55,10 @@ thiserror = "2.0.17"
|
||||
tokio = "1.48.0"
|
||||
ts-rs = "11.1.0"
|
||||
|
||||
# Internal crates - common/foundation
|
||||
yaak-database = { path = "crates/common/yaak-database" }
|
||||
yaak-rpc = { path = "crates/common/yaak-rpc" }
|
||||
|
||||
# Internal crates - shared
|
||||
yaak-core = { path = "crates/yaak-core" }
|
||||
yaak = { path = "crates/yaak" }
|
||||
@@ -63,12 +75,17 @@ yaak-templates = { path = "crates/yaak-templates" }
|
||||
yaak-tls = { path = "crates/yaak-tls" }
|
||||
yaak-ws = { path = "crates/yaak-ws" }
|
||||
yaak-api = { path = "crates/yaak-api" }
|
||||
yaak-proxy = { path = "crates/yaak-proxy" }
|
||||
|
||||
# Internal crates - proxy
|
||||
yaak-proxy-lib = { path = "crates-proxy/yaak-proxy-lib" }
|
||||
|
||||
# Internal crates - Tauri-specific
|
||||
yaak-fonts = { path = "crates-tauri/yaak-fonts" }
|
||||
yaak-license = { path = "crates-tauri/yaak-license" }
|
||||
yaak-mac-window = { path = "crates-tauri/yaak-mac-window" }
|
||||
yaak-tauri-utils = { path = "crates-tauri/yaak-tauri-utils" }
|
||||
yaak-window = { path = "crates-tauri/yaak-window" }
|
||||
|
||||
[profile.release]
|
||||
strip = false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/JamesIves/github-sponsors-readme-action">
|
||||
<img width="200px" src="https://github.com/mountain-loop/yaak/raw/main/crates-tauri/yaak-app/icons/icon.png">
|
||||
<img width="200px" src="https://github.com/mountain-loop/yaak/raw/main/crates-tauri/yaak-app-client/icons/icon.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createWorkspaceModel, type Folder, modelTypeLabel } from "@yaakapp-internal/models";
|
||||
import { applySync, calculateSync } from "@yaakapp-internal/sync";
|
||||
import { Banner } from "../components/core/Banner";
|
||||
import { Button } from "../components/core/Button";
|
||||
import { InlineCode } from "../components/core/InlineCode";
|
||||
import {
|
||||
Banner,
|
||||
InlineCode,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
TruncatedWideTableCell,
|
||||
} from "../components/core/Table";
|
||||
} from "@yaakapp-internal/ui";
|
||||
import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
||||
import { createFastMutation } from "../hooks/useFastMutation";
|
||||
import { showDialog } from "../lib/dialog";
|
||||
@@ -1,10 +1,8 @@
|
||||
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 { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
import { SelectFile } from "./SelectFile";
|
||||
|
||||
type Props = {
|
||||
@@ -1,15 +1,14 @@
|
||||
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 { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { Checkbox } from "./core/Checkbox";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
import { VStack } from "./core/Stacks";
|
||||
import { promptCredentials } from "./git/credentials";
|
||||
|
||||
interface Props {
|
||||
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -21,7 +22,6 @@ import { useActiveRequest } from "../hooks/useActiveRequest";
|
||||
import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
||||
import { useAllRequests } from "../hooks/useAllRequests";
|
||||
import { useCreateWorkspace } from "../hooks/useCreateWorkspace";
|
||||
import { useDebouncedState } from "../hooks/useDebouncedState";
|
||||
import { useEnvironmentsBreakdown } from "../hooks/useEnvironmentsBreakdown";
|
||||
import { useGrpcRequestActions } from "../hooks/useGrpcRequestActions";
|
||||
import type { HotkeyAction } from "../hooks/useHotKey";
|
||||
@@ -47,10 +47,8 @@ import { router } from "../lib/router";
|
||||
import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams";
|
||||
import { CookieDialog } from "./CookieDialog";
|
||||
import { Button } from "./core/Button";
|
||||
import { Heading } from "./core/Heading";
|
||||
import { Hotkey } from "./core/Hotkey";
|
||||
import { HttpMethodTag } from "./core/HttpMethodTag";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
|
||||
interface CommandPaletteGroup {
|
||||
@@ -1,14 +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 { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { Link } from "./core/Link";
|
||||
import { SizeTag } from "./core/SizeTag";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -6,11 +7,8 @@ import { isProbablyTextContentType } from "../lib/contentType";
|
||||
import { getContentTypeFromHeaders } from "../lib/model_util";
|
||||
import { getResponseBodyText } from "../lib/responseBody";
|
||||
import { CopyButton } from "./CopyButton";
|
||||
import { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { SizeTag } from "./core/SizeTag";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
@@ -1,15 +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 { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { SizeTag } from "./core/SizeTag";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
@@ -2,9 +2,8 @@ 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 } from "./core/Banner";
|
||||
import { Banner, InlineCode } from "@yaakapp-internal/ui";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
|
||||
interface Props {
|
||||
cookieJarId: string | null;
|
||||
@@ -9,9 +9,8 @@ import { showPrompt } from "../lib/prompt";
|
||||
import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams";
|
||||
import { CookieDialog } from "./CookieDialog";
|
||||
import { Dropdown, type DropdownItem } from "./core/Dropdown";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { Icon, InlineCode } from "@yaakapp-internal/ui";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
|
||||
export const CookieDropdown = memo(function CookieDropdown() {
|
||||
const activeCookieJar = useActiveCookieJar();
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useTimedBoolean } from "../hooks/useTimedBoolean";
|
||||
import { useTimedBoolean } from "@yaakapp-internal/ui";
|
||||
import { copyToClipboard } from "../lib/copy";
|
||||
import { showToast } from "../lib/toast";
|
||||
import type { ButtonProps } from "./core/Button";
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useTimedBoolean } from "../hooks/useTimedBoolean";
|
||||
import { IconButton, type IconButtonProps, useTimedBoolean } from "@yaakapp-internal/ui";
|
||||
import { copyToClipboard } from "../lib/copy";
|
||||
import { showToast } from "../lib/toast";
|
||||
import type { IconButtonProps } from "./core/IconButton";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
|
||||
interface Props extends Omit<IconButtonProps, "onClick" | "icon"> {
|
||||
text: string | (() => Promise<string | null>);
|
||||
@@ -1,6 +1,7 @@
|
||||
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";
|
||||
@@ -10,7 +11,6 @@ import { Button } from "./core/Button";
|
||||
import { Checkbox } from "./core/Checkbox";
|
||||
import { Label } from "./core/Label";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
import { VStack } from "./core/Stacks";
|
||||
import { EncryptionHelp } from "./EncryptionHelp";
|
||||
import { gitCallbacks } from "./git/callbacks";
|
||||
import { SyncToFilesystemSetting } from "./SyncToFilesystemSetting";
|
||||
@@ -1,13 +1,21 @@
|
||||
import type { DnsOverride, Workspace } from "@yaakapp-internal/models";
|
||||
import { patchModel } from "@yaakapp-internal/models";
|
||||
import { useCallback, useId, useMemo } from "react";
|
||||
import { fireAndForget } from "../lib/fireAndForget";
|
||||
import {
|
||||
HStack,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
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";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "./core/Table";
|
||||
|
||||
interface Props {
|
||||
workspace: Workspace;
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
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";
|
||||
@@ -19,7 +20,6 @@ import { useRandomKey } from "../hooks/useRandomKey";
|
||||
import { capitalize } from "../lib/capitalize";
|
||||
import { showDialog } from "../lib/dialog";
|
||||
import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { Banner } from "./core/Banner";
|
||||
import { Checkbox } from "./core/Checkbox";
|
||||
import { DetailsBanner } from "./core/DetailsBanner";
|
||||
import { Editor } from "./core/Editor/LazyEditor";
|
||||
@@ -31,7 +31,6 @@ import type { Pair } from "./core/PairEditor";
|
||||
import { PairEditor } from "./core/PairEditor";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
import { Select } from "./core/Select";
|
||||
import { VStack } from "./core/Stacks";
|
||||
import { Markdown } from "./Markdown";
|
||||
import { SelectFile } from "./SelectFile";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VStack } from "./core/Stacks";
|
||||
import { VStack } from "@yaakapp-internal/ui";
|
||||
|
||||
export function EncryptionHelp() {
|
||||
return (
|
||||
@@ -8,7 +8,7 @@ import type { ButtonProps } from "./core/Button";
|
||||
import { Button } from "./core/Button";
|
||||
import type { DropdownItem } from "./core/Dropdown";
|
||||
import { Dropdown } from "./core/Dropdown";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { Icon } from "@yaakapp-internal/ui";
|
||||
import { EnvironmentColorIndicator } from "./EnvironmentColorIndicator";
|
||||
|
||||
type Props = {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { ColorIndicator } from "./ColorIndicator";
|
||||
import { Banner } from "./core/Banner";
|
||||
import { Banner } from "@yaakapp-internal/ui";
|
||||
import { Button } from "./core/Button";
|
||||
import { ColorPickerWithThemeColors } from "./core/ColorPicker";
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
import type { Environment, Workspace } from "@yaakapp-internal/models";
|
||||
import { duplicateModel, patchModel } from "@yaakapp-internal/models";
|
||||
import type { TreeHandle, TreeNode, TreeProps } from "@yaakapp-internal/ui";
|
||||
import { Banner, Icon, InlineCode, SplitLayout, Tree } from "@yaakapp-internal/ui";
|
||||
import { atom, useAtomValue } from "jotai";
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { atomFamily } from "jotai/utils";
|
||||
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
||||
import { createSubEnvironmentAndActivate } from "../commands/createEnvironment";
|
||||
import { activeWorkspaceAtom, activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
||||
import {
|
||||
environmentsBreakdownAtom,
|
||||
useEnvironmentsBreakdown,
|
||||
} from "../hooks/useEnvironmentsBreakdown";
|
||||
import { useHotKey } from "../hooks/useHotKey";
|
||||
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
|
||||
import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm";
|
||||
import { fireAndForget } from "../lib/fireAndForget";
|
||||
import { jotaiStore } from "../lib/jotai";
|
||||
import { isBaseEnvironment, isSubEnvironment } from "../lib/model_util";
|
||||
import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { showColorPicker } from "../lib/showColorPicker";
|
||||
import { Banner } from "./core/Banner";
|
||||
import type { ContextMenuProps, DropdownItem } from "./core/Dropdown";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { ContextMenu } from "./core/Dropdown";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { IconTooltip } from "./core/IconTooltip";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import type { PairEditorHandle } from "./core/PairEditor";
|
||||
import { SplitLayout } from "./core/SplitLayout";
|
||||
import type { TreeNode } from "./core/tree/common";
|
||||
import type { TreeHandle, TreeProps } from "./core/tree/Tree";
|
||||
import { Tree } from "./core/tree/Tree";
|
||||
import { EnvironmentColorIndicator } from "./EnvironmentColorIndicator";
|
||||
import { EnvironmentEditor } from "./EnvironmentEditor";
|
||||
import { EnvironmentSharableTooltip } from "./EnvironmentSharableTooltip";
|
||||
|
||||
const collapsedFamily = atomFamily((treeId: string) => {
|
||||
const key = ["env_collapsed", treeId ?? "n/a"];
|
||||
return atomWithKVStorage<Record<string, boolean>>(key, {});
|
||||
});
|
||||
|
||||
interface Props {
|
||||
initialEnvironmentId: string | null;
|
||||
setRef?: (ref: PairEditorHandle | null) => void;
|
||||
@@ -49,7 +53,7 @@ export function EnvironmentEditDialog({ initialEnvironmentId, setRef }: Props) {
|
||||
|
||||
return (
|
||||
<SplitLayout
|
||||
name="env_editor"
|
||||
storageKey="env_editor"
|
||||
defaultRatio={0.75}
|
||||
layout="horizontal"
|
||||
className="gap-0"
|
||||
@@ -113,7 +117,7 @@ function EnvironmentEditDialogSidebar({
|
||||
const treeRef = useRef<TreeHandle>(null);
|
||||
const { baseEnvironment, baseEnvironments } = useEnvironmentsBreakdown();
|
||||
|
||||
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
||||
// oxlint-disable-next-line react-hooks/exhaustive-deps -- none
|
||||
useLayoutEffect(() => {
|
||||
if (selectedEnvironmentId == null) return;
|
||||
treeRef.current?.selectItem(selectedEnvironmentId);
|
||||
@@ -130,44 +134,60 @@ function EnvironmentEditDialogSidebar({
|
||||
[baseEnvironment?.id, selectedEnvironmentId, setSelectedEnvironmentId],
|
||||
);
|
||||
|
||||
const actions = useMemo(() => {
|
||||
const enable = () => treeRef.current?.hasFocus() ?? false;
|
||||
const treeHasFocus = useCallback(() => treeRef.current?.hasFocus() ?? false, []);
|
||||
|
||||
const actions = {
|
||||
"sidebar.selected.rename": {
|
||||
enable,
|
||||
allowDefault: true,
|
||||
priority: 100,
|
||||
cb: async (items: TreeModel[]) => {
|
||||
const item = items[0];
|
||||
if (items.length === 1 && item != null) {
|
||||
treeRef.current?.renameItem(item.id);
|
||||
}
|
||||
},
|
||||
},
|
||||
"sidebar.selected.delete": {
|
||||
priority: 100,
|
||||
enable,
|
||||
cb: (items: TreeModel[]) => deleteModelWithConfirm(items),
|
||||
},
|
||||
"sidebar.selected.duplicate": {
|
||||
priority: 100,
|
||||
enable,
|
||||
cb: async (items: TreeModel[]) => {
|
||||
if (items.length === 1 && items[0]) {
|
||||
const item = items[0];
|
||||
const newId = await duplicateModel(item);
|
||||
setSelectedEnvironmentId(newId);
|
||||
} else {
|
||||
await Promise.all(items.map(duplicateModel));
|
||||
}
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
return actions;
|
||||
}, [setSelectedEnvironmentId]);
|
||||
const getSelectedTreeModels = useCallback(
|
||||
() => treeRef.current?.getSelectedItems() as TreeModel[] | undefined,
|
||||
[],
|
||||
);
|
||||
|
||||
const hotkeys = useMemo<TreeProps<TreeModel>["hotkeys"]>(() => ({ actions }), [actions]);
|
||||
const handleRenameSelected = useCallback(() => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items?.length === 1 && items[0] != null) {
|
||||
treeRef.current?.renameItem(items[0].id);
|
||||
}
|
||||
}, [getSelectedTreeModels]);
|
||||
|
||||
const handleDeleteSelected = useCallback(
|
||||
(items: TreeModel[]) => deleteModelWithConfirm(items),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleDuplicateSelected = useCallback(
|
||||
async (items: TreeModel[]) => {
|
||||
if (items.length === 1 && items[0]) {
|
||||
const newId = await duplicateModel(items[0]);
|
||||
setSelectedEnvironmentId(newId);
|
||||
} else {
|
||||
await Promise.all(items.map(duplicateModel));
|
||||
}
|
||||
},
|
||||
[setSelectedEnvironmentId],
|
||||
);
|
||||
|
||||
useHotKey("sidebar.selected.rename", handleRenameSelected, {
|
||||
enable: treeHasFocus,
|
||||
allowDefault: true,
|
||||
priority: 100,
|
||||
});
|
||||
useHotKey(
|
||||
"sidebar.selected.delete",
|
||||
useCallback(() => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) {
|
||||
fireAndForget(handleDeleteSelected(items));
|
||||
}
|
||||
}, [getSelectedTreeModels, handleDeleteSelected]),
|
||||
{ enable: treeHasFocus, priority: 100 },
|
||||
);
|
||||
useHotKey(
|
||||
"sidebar.selected.duplicate",
|
||||
useCallback(async () => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) await handleDuplicateSelected(items);
|
||||
}, [getSelectedTreeModels, handleDuplicateSelected]),
|
||||
{ enable: treeHasFocus, priority: 100 },
|
||||
);
|
||||
|
||||
const getContextMenu = useCallback(
|
||||
(items: TreeModel[]): ContextMenuProps["items"] => {
|
||||
@@ -196,12 +216,10 @@ function EnvironmentEditDialogSidebar({
|
||||
hidden: isBaseEnvironment(environment) || !singleEnvironment,
|
||||
hotKeyAction: "sidebar.selected.rename",
|
||||
hotKeyLabelOnly: true,
|
||||
onSelect: async () => {
|
||||
onSelect: () => {
|
||||
// Not sure why this is needed, but without it the
|
||||
// edit input blurs immediately after opening.
|
||||
requestAnimationFrame(() => {
|
||||
fireAndForget(actions["sidebar.selected.rename"].cb(items));
|
||||
});
|
||||
requestAnimationFrame(() => handleRenameSelected());
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -210,7 +228,7 @@ function EnvironmentEditDialogSidebar({
|
||||
hidden: isBaseEnvironment(environment),
|
||||
hotKeyAction: "sidebar.selected.duplicate",
|
||||
hotKeyLabelOnly: true,
|
||||
onSelect: () => actions["sidebar.selected.duplicate"].cb(items),
|
||||
onSelect: () => handleDuplicateSelected(items),
|
||||
},
|
||||
{
|
||||
label: environment.color ? "Change Color" : "Assign Color",
|
||||
@@ -246,7 +264,12 @@ function EnvironmentEditDialogSidebar({
|
||||
|
||||
return menuItems;
|
||||
},
|
||||
[actions, baseEnvironments.length, handleDeleteEnvironment],
|
||||
[
|
||||
baseEnvironments.length,
|
||||
handleDeleteEnvironment,
|
||||
handleDuplicateSelected,
|
||||
handleRenameSelected,
|
||||
],
|
||||
);
|
||||
|
||||
const handleDragEnd = useCallback(async function handleDragEnd({
|
||||
@@ -293,6 +316,13 @@ function EnvironmentEditDialogSidebar({
|
||||
[setSelectedEnvironmentId],
|
||||
);
|
||||
|
||||
const renderContextMenuFn = useCallback<NonNullable<TreeProps<TreeModel>["renderContextMenu"]>>(
|
||||
({ items, position, onClose }) => (
|
||||
<ContextMenu items={items as DropdownItem[]} triggerPosition={position} onClose={onClose} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const tree = useAtomValue(treeAtom);
|
||||
return (
|
||||
<aside className="x-theme-sidebar h-full w-full min-w-0 grid overflow-y-auto border-r border-border-subtle ">
|
||||
@@ -301,10 +331,11 @@ function EnvironmentEditDialogSidebar({
|
||||
<Tree
|
||||
ref={treeRef}
|
||||
treeId={treeId}
|
||||
collapsedAtom={collapsedFamily(treeId)}
|
||||
className="px-2 pb-10"
|
||||
hotkeys={hotkeys}
|
||||
root={tree}
|
||||
getContextMenu={getContextMenu}
|
||||
renderContextMenu={renderContextMenuFn}
|
||||
onDragEnd={handleDragEnd}
|
||||
getItemKey={(i) => `${i.id}::${i.name}`}
|
||||
ItemLeftSlotInner={ItemLeftSlotInner}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Environment } from "@yaakapp-internal/models";
|
||||
import { patchModel } from "@yaakapp-internal/models";
|
||||
import type { GenericCompletionOption } from "@yaakapp-internal/plugins";
|
||||
import { Heading } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useEnvironmentsBreakdown } from "../hooks/useEnvironmentsBreakdown";
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
} from "../lib/setupOrConfigureEncryption";
|
||||
import { DismissibleBanner } from "./core/DismissibleBanner";
|
||||
import type { GenericCompletionConfig } from "./core/Editor/genericCompletion";
|
||||
import { Heading } from "./core/Heading";
|
||||
import type { PairEditorHandle, PairWithId } from "./core/PairEditor";
|
||||
import { ensurePairId } from "./core/PairEditor.util";
|
||||
import { PairOrBulkEditor } from "./core/PairOrBulkEditor";
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Banner, Button, InlineCode } from "@yaakapp-internal/ui";
|
||||
import type { ErrorInfo, ReactNode } from "react";
|
||||
import { Component, useEffect } from "react";
|
||||
import { showDialog } from "../lib/dialog";
|
||||
import { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import RouteError from "./RouteError";
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
@@ -1,6 +1,7 @@
|
||||
import { save } from "@tauri-apps/plugin-dialog";
|
||||
import type { Workspace } from "@yaakapp-internal/models";
|
||||
import { workspacesAtom } from "@yaakapp-internal/models";
|
||||
import { HStack, VStack } from "@yaakapp-internal/ui";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import slugify from "slugify";
|
||||
@@ -11,7 +12,6 @@ import { Button } from "./core/Button";
|
||||
import { Checkbox } from "./core/Checkbox";
|
||||
import { DetailsBanner } from "./core/DetailsBanner";
|
||||
import { Link } from "./core/Link";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
onHide: () => void;
|
||||
@@ -1,5 +1,6 @@
|
||||
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";
|
||||
@@ -8,20 +9,15 @@ import { allRequestsAtom } from "../hooks/useAllRequests";
|
||||
import { useFolderActions } from "../hooks/useFolderActions";
|
||||
import { useLatestHttpResponse } from "../hooks/useLatestHttpResponse";
|
||||
import { sendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest";
|
||||
import { fireAndForget } from "../lib/fireAndForget";
|
||||
import { showDialog } from "../lib/dialog";
|
||||
import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { router } from "../lib/router";
|
||||
import { Button } from "./core/Button";
|
||||
import { Heading } from "./core/Heading";
|
||||
import { HttpResponseDurationTag } from "./core/HttpResponseDurationTag";
|
||||
import { HttpStatusTag } from "./core/HttpStatusTag";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { LoadingIcon } from "./core/LoadingIcon";
|
||||
import { Separator } from "./core/Separator";
|
||||
import { SizeTag } from "./core/SizeTag";
|
||||
import { HStack } from "./core/Stacks";
|
||||
import { HttpResponsePane } from "./HttpResponsePane";
|
||||
|
||||
interface Props {
|
||||
@@ -46,7 +42,7 @@ export function FolderLayout({ folder, style }: Props) {
|
||||
}, [folder.id, folders, requests]);
|
||||
|
||||
const handleSendAll = useCallback(() => {
|
||||
if (sendAllAction) fireAndForget(sendAllAction.call(folder));
|
||||
void sendAllAction?.call(folder);
|
||||
}, [sendAllAction, folder]);
|
||||
|
||||
return (
|
||||
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -11,11 +12,8 @@ import { hideDialog } from "../lib/dialog";
|
||||
import { CopyIconButton } from "./CopyIconButton";
|
||||
import { Button } from "./core/Button";
|
||||
import { CountBadge } from "./core/CountBadge";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { Input } from "./core/Input";
|
||||
import { Link } from "./core/Link";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
import type { TabItem } from "./core/Tabs/Tabs";
|
||||
import { TabContent, Tabs } from "./core/Tabs/Tabs";
|
||||
import { EmptyStateText } from "./EmptyStateText";
|
||||
@@ -7,10 +7,10 @@ import { useActiveRequest } from "../hooks/useActiveRequest";
|
||||
import { useGrpc } from "../hooks/useGrpc";
|
||||
import { useGrpcProtoFiles } from "../hooks/useGrpcProtoFiles";
|
||||
import { activeGrpcConnectionAtom, useGrpcEvents } from "../hooks/usePinnedGrpcConnection";
|
||||
import { Banner, SplitLayout } from "@yaakapp-internal/ui";
|
||||
import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace";
|
||||
import { workspaceLayoutAtom } from "../lib/atoms";
|
||||
import { Banner } from "./core/Banner";
|
||||
import { HotkeyList } from "./core/HotkeyList";
|
||||
import { SplitLayout } from "./core/SplitLayout";
|
||||
import { GrpcRequestPane } from "./GrpcRequestPane";
|
||||
import { GrpcResponsePane } from "./GrpcResponsePane";
|
||||
|
||||
@@ -22,6 +22,8 @@ const emptyArray: string[] = [];
|
||||
|
||||
export function GrpcConnectionLayout({ style }: Props) {
|
||||
const workspaceLayout = useAtomValue(workspaceLayoutAtom);
|
||||
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
|
||||
const wsId = activeWorkspace?.id ?? "n/a";
|
||||
const activeRequest = useActiveRequest("grpc_request");
|
||||
const activeConnection = useAtomValue(activeGrpcConnectionAtom);
|
||||
const grpcEvents = useGrpcEvents(activeConnection?.id ?? null);
|
||||
@@ -79,7 +81,7 @@ export function GrpcConnectionLayout({ style }: Props) {
|
||||
|
||||
return (
|
||||
<SplitLayout
|
||||
name="grpc_layout"
|
||||
storageKey={`grpc_layout::${wsId}`}
|
||||
className="p-3 gap-1.5"
|
||||
style={style}
|
||||
layout={workspaceLayout}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { jsoncLanguage } from "@shopify/lang-jsonc";
|
||||
import { linter } from "@codemirror/lint";
|
||||
import type { EditorView } from "@codemirror/view";
|
||||
import { jsoncLanguage } from "@shopify/lang-jsonc";
|
||||
import type { GrpcRequest } from "@yaakapp-internal/models";
|
||||
import { FormattedError, InlineCode, VStack } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import {
|
||||
handleRefresh,
|
||||
@@ -18,9 +19,6 @@ import { pluralizeCount } from "../lib/pluralize";
|
||||
import { Button } from "./core/Button";
|
||||
import type { EditorProps } from "./core/Editor/Editor";
|
||||
import { Editor } from "./core/Editor/LazyEditor";
|
||||
import { FormattedError } from "./core/FormattedError";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { VStack } from "./core/Stacks";
|
||||
import { GrpcProtoSelectionDialog } from "./GrpcProtoSelectionDialog";
|
||||
|
||||
type Props = Pick<EditorProps, "heightMode" | "onChange" | "className" | "forceUpdateKey"> & {
|
||||
@@ -1,16 +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 { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { Link } from "./core/Link";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
onDone: () => void;
|
||||
@@ -30,7 +27,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
|
||||
const services = grpc.reflect.data;
|
||||
const serverReflection = protoFiles.length === 0 && services != null;
|
||||
let reflectError = grpc.reflect.error ?? null;
|
||||
const reflectionUnimplemented = `${reflectError}`.match(/unimplemented/i);
|
||||
const reflectionUnimplemented = String(reflectError).match(/unimplemented/i);
|
||||
|
||||
if (reflectionUnimplemented) {
|
||||
reflectError = null;
|
||||
@@ -143,8 +140,8 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
{protoFiles.map((f, i) => {
|
||||
const parts = f.split("/");
|
||||
// oxlint-disable-next-line no-array-index-key -- none
|
||||
return (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<tr key={f + i} className="group">
|
||||
<td>
|
||||
<Icon icon={f.endsWith(".proto") ? "file_code" : "folder_code"} />
|
||||
@@ -1,9 +1,9 @@
|
||||
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 { useContainerSize } from "../hooks/useContainerQuery";
|
||||
import type { ReflectResponseService } from "../hooks/useGrpc";
|
||||
import { useHeadersTab } from "../hooks/useHeadersTab";
|
||||
import { useInheritedHeaders } from "../hooks/useInheritedHeaders";
|
||||
@@ -11,11 +11,9 @@ import { useRequestUpdateKey } from "../hooks/useRequestUpdateKey";
|
||||
import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { Button } from "./core/Button";
|
||||
import { CountBadge } from "./core/CountBadge";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
import { RadioDropdown } from "./core/RadioDropdown";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
import type { TabItem } from "./core/Tabs/Tabs";
|
||||
import { TabContent, Tabs } from "./core/Tabs/Tabs";
|
||||
import { GrpcEditor } from "./GrpcEditor";
|
||||
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -14,10 +15,7 @@ import { Editor } from "./core/Editor/LazyEditor";
|
||||
import { EventDetailHeader, EventViewer } from "./core/EventViewer";
|
||||
import { EventViewerRow } from "./core/EventViewerRow";
|
||||
import { HotkeyList } from "./core/HotkeyList";
|
||||
import { Icon, type IconProps } from "./core/Icon";
|
||||
import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow";
|
||||
import { LoadingIcon } from "./core/LoadingIcon";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
import { EmptyStateText } from "./EmptyStateText";
|
||||
import { ErrorBoundary } from "./ErrorBoundary";
|
||||
import { RecentGrpcConnectionsDropdown } from "./RecentGrpcConnectionsDropdown";
|
||||
@@ -93,7 +91,7 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) {
|
||||
getEventKey={(event) => event.id}
|
||||
error={activeConnection.error}
|
||||
header={header}
|
||||
splitLayoutName="grpc_events"
|
||||
splitLayoutStorageKey="grpc_events"
|
||||
defaultRatio={0.4}
|
||||
renderRow={({ event, isActive, onClick }) => (
|
||||
<GrpcEventRow event={event} isActive={isActive} onClick={onClick} />
|
||||
@@ -1,5 +1,6 @@
|
||||
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";
|
||||
@@ -13,7 +14,6 @@ import type { Pair, PairEditorProps } from "./core/PairEditor";
|
||||
import { PairEditorRow } from "./core/PairEditor";
|
||||
import { ensurePairId } from "./core/PairEditor.util";
|
||||
import { PairOrBulkEditor } from "./core/PairOrBulkEditor";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
type Props = {
|
||||
forceUpdateKey: string;
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
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";
|
||||
@@ -14,13 +15,10 @@ import { useInheritedAuthentication } from "../hooks/useInheritedAuthentication"
|
||||
import { useRenderTemplate } from "../hooks/useRenderTemplate";
|
||||
import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { Dropdown, type DropdownItem } from "./core/Dropdown";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { Input, type InputProps } from "./core/Input";
|
||||
import { Link } from "./core/Link";
|
||||
import { SegmentedControl } from "./core/SegmentedControl";
|
||||
import { HStack } from "./core/Stacks";
|
||||
import { DynamicForm } from "./DynamicForm";
|
||||
import { EmptyStateText } from "./EmptyStateText";
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { HttpRequest } from "@yaakapp-internal/models";
|
||||
import type { SlotProps } from "@yaakapp-internal/ui";
|
||||
import { SplitLayout } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
import type { CSSProperties } from "react";
|
||||
import { useCurrentGraphQLSchema } from "../hooks/useIntrospectGraphQL";
|
||||
import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace";
|
||||
import { workspaceLayoutAtom } from "../lib/atoms";
|
||||
import type { SlotProps } from "./core/SplitLayout";
|
||||
import { SplitLayout } from "./core/SplitLayout";
|
||||
import { GraphQLDocsExplorer } from "./graphql/GraphQLDocsExplorer";
|
||||
import { showGraphQLDocExplorerAtom } from "./graphql/graphqlAtoms";
|
||||
import { HttpRequestPane } from "./HttpRequestPane";
|
||||
@@ -20,10 +21,12 @@ export function HttpRequestLayout({ activeRequest, style }: Props) {
|
||||
const showGraphQLDocExplorer = useAtomValue(showGraphQLDocExplorerAtom);
|
||||
const graphQLSchema = useCurrentGraphQLSchema(activeRequest);
|
||||
const workspaceLayout = useAtomValue(workspaceLayoutAtom);
|
||||
const activeWorkspace = useAtomValue(activeWorkspaceAtom);
|
||||
const wsId = activeWorkspace?.id ?? "n/a";
|
||||
|
||||
const requestResponseSplit = ({ style }: Pick<SlotProps, "style">) => (
|
||||
<SplitLayout
|
||||
name="http_layout"
|
||||
storageKey={`http_layout::${wsId}`}
|
||||
className="p-3 gap-1.5"
|
||||
style={style}
|
||||
layout={workspaceLayout}
|
||||
@@ -47,7 +50,7 @@ export function HttpRequestLayout({ activeRequest, style }: Props) {
|
||||
) {
|
||||
return (
|
||||
<SplitLayout
|
||||
name="graphql_layout"
|
||||
storageKey={`graphql_layout::${wsId}`}
|
||||
defaultRatio={1 / 3}
|
||||
firstSlot={requestResponseSplit}
|
||||
secondSlot={({ style, orientation }) => (
|
||||
@@ -38,7 +38,7 @@ import { ConfirmLargeRequestBody } from "./ConfirmLargeRequestBody";
|
||||
import { CountBadge } from "./core/CountBadge";
|
||||
import type { GenericCompletionConfig } from "./core/Editor/genericCompletion";
|
||||
import { Editor } from "./core/Editor/LazyEditor";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { InlineCode } from "@yaakapp-internal/ui";
|
||||
import type { Pair } from "./core/PairEditor";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
import type { TabItem, TabsRef } from "./core/Tabs/Tabs";
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { HttpResponse, HttpResponseEvent } from "@yaakapp-internal/models";
|
||||
import { Banner, HStack, Icon, LoadingIcon, VStack } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import type { ComponentType, CSSProperties } from "react";
|
||||
import { lazy, Suspense, useMemo } from "react";
|
||||
@@ -12,17 +13,13 @@ import { getMimeTypeFromContentType } from "../lib/contentType";
|
||||
import { getContentTypeFromHeaders, getCookieCounts } from "../lib/model_util";
|
||||
import { ConfirmLargeResponse } from "./ConfirmLargeResponse";
|
||||
import { ConfirmLargeResponseRequest } from "./ConfirmLargeResponseRequest";
|
||||
import { Banner } from "./core/Banner";
|
||||
import { Button } from "./core/Button";
|
||||
import { CountBadge } from "./core/CountBadge";
|
||||
import { HotkeyList } from "./core/HotkeyList";
|
||||
import { HttpResponseDurationTag } from "./core/HttpResponseDurationTag";
|
||||
import { HttpStatusTag } from "./core/HttpStatusTag";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { LoadingIcon } from "./core/LoadingIcon";
|
||||
import { PillButton } from "./core/PillButton";
|
||||
import { SizeTag } from "./core/SizeTag";
|
||||
import { HStack, VStack } from "./core/Stacks";
|
||||
import type { TabItem } from "./core/Tabs/Tabs";
|
||||
import { TabContent, Tabs } from "./core/Tabs/Tabs";
|
||||
import { Tooltip } from "./core/Tooltip";
|
||||
@@ -9,7 +9,7 @@ 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 "./core/Icon";
|
||||
import { Icon, type IconProps } from "@yaakapp-internal/ui";
|
||||
import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow";
|
||||
import type { TimelineViewMode } from "./HttpResponsePane";
|
||||
|
||||
@@ -55,7 +55,7 @@ function Inner({ response, viewMode }: Props) {
|
||||
isLoading={isLoading}
|
||||
loadingMessage="Loading events..."
|
||||
emptyMessage="No events recorded"
|
||||
splitLayoutName="http_response_events"
|
||||
splitLayoutStorageKey="http_response_events"
|
||||
defaultRatio={0.25}
|
||||
renderRow={({ event, isActive, onClick }) => {
|
||||
const display = getEventDisplay(event.event);
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
|
||||
import { useImportCurl } from "../hooks/useImportCurl";
|
||||
import { useWindowFocus } from "../hooks/useWindowFocus";
|
||||
import { Button } from "./core/Button";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { Icon } from "@yaakapp-internal/ui";
|
||||
|
||||
export function ImportCurlButton() {
|
||||
const focused = useWindowFocus();
|
||||
@@ -13,11 +13,9 @@ export function ImportCurlButton() {
|
||||
const importCurl = useImportCurl();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
||||
// oxlint-disable-next-line react-hooks/exhaustive-deps -- none
|
||||
useEffect(() => {
|
||||
readText()
|
||||
.then(setClipboardText)
|
||||
.catch(() => {});
|
||||
void readText().then(setClipboardText);
|
||||
}, [focused]);
|
||||
|
||||
if (!clipboardText?.trim().startsWith("curl ")) {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { VStack } from "@yaakapp-internal/ui";
|
||||
import { useState } from "react";
|
||||
import { useLocalStorage } from "react-use";
|
||||
import { Button } from "./core/Button";
|
||||
import { VStack } from "./core/Stacks";
|
||||
import { SelectFile } from "./SelectFile";
|
||||
|
||||
interface Props {
|
||||
@@ -1,17 +1,16 @@
|
||||
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 { fireAndForget } from "../lib/fireAndForget";
|
||||
import { useKeyValue } from "../hooks/useKeyValue";
|
||||
import { fireAndForget } from "../lib/fireAndForget";
|
||||
import { textLikelyContainsJsonComments } from "../lib/jsonComments";
|
||||
import { Banner } from "./core/Banner";
|
||||
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 { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { IconTooltip } from "./core/IconTooltip";
|
||||
|
||||
@@ -12,7 +12,7 @@ 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 "./core/Icon";
|
||||
import { Icon } from "@yaakapp-internal/ui";
|
||||
import { PillButton } from "./core/PillButton";
|
||||
|
||||
const dismissedAtom = atomWithKVStorage<string | null>("dismissed_license_expired", null);
|
||||
@@ -1,5 +1,6 @@
|
||||
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";
|
||||
@@ -7,9 +8,7 @@ import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { router } from "../lib/router";
|
||||
import { showToast } from "../lib/toast";
|
||||
import { Button } from "./core/Button";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import { Select } from "./core/Select";
|
||||
import { VStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
activeWorkspaceId: string;
|
||||
@@ -1,12 +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 { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
connections: GrpcConnection[];
|
||||
@@ -1,14 +1,13 @@
|
||||
import type { HttpResponse } from "@yaakapp-internal/models";
|
||||
import { deleteModel } from "@yaakapp-internal/models";
|
||||
import { HStack, Icon } from "@yaakapp-internal/ui";
|
||||
import { useCopyHttpResponse } from "../hooks/useCopyHttpResponse";
|
||||
import { useDeleteHttpResponses } from "../hooks/useDeleteHttpResponses";
|
||||
import { useSaveResponse } from "../hooks/useSaveResponse";
|
||||
import { pluralize } from "../lib/pluralize";
|
||||
import { Dropdown } from "./core/Dropdown";
|
||||
import { HttpStatusTag } from "./core/HttpStatusTag";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
responses: HttpResponse[];
|
||||
@@ -1,12 +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 { Icon } from "./core/Icon";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
interface Props {
|
||||
connections: WebsocketConnection[];
|
||||
@@ -2,7 +2,7 @@ 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 "./core/LoadingIcon";
|
||||
import { LoadingIcon } from "@yaakapp-internal/ui";
|
||||
import { EmptyStateText } from "./EmptyStateText";
|
||||
import { AudioViewer } from "./responseViewers/AudioViewer";
|
||||
import { CsvViewer } from "./responseViewers/CsvViewer";
|
||||
@@ -6,7 +6,7 @@ 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 "./core/Icon";
|
||||
import { Icon } from "@yaakapp-internal/ui";
|
||||
import type { RadioDropdownItem } from "./core/RadioDropdown";
|
||||
import { RadioDropdown } from "./core/RadioDropdown";
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { Button } from "./core/Button";
|
||||
import { Button, FormattedError, Heading, VStack } from "@yaakapp-internal/ui";
|
||||
import { DetailsBanner } from "./core/DetailsBanner";
|
||||
import { FormattedError } from "./core/FormattedError";
|
||||
import { Heading } from "./core/Heading";
|
||||
import { VStack } from "./core/Stacks";
|
||||
|
||||
export default function RouteError({ error }: { error: unknown }) {
|
||||
console.log("Error", error);
|
||||
const stringified = JSON.stringify(error);
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
// oxlint-disable-next-line no-explicit-any -- none
|
||||
const message = (error as any).message ?? stringified;
|
||||
const stack =
|
||||
typeof error === "object" && error != null && "stack" in error ? String(error.stack) : null;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { HStack } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import mime from "mime";
|
||||
import type { ReactNode } from "react";
|
||||
@@ -9,7 +10,6 @@ import { Button } from "./core/Button";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { IconTooltip } from "./core/IconTooltip";
|
||||
import { Label } from "./core/Label";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
type Props = Omit<ButtonProps, "type"> & {
|
||||
onChange: (value: { filePath: string | null; contentType: string | null }) => void;
|
||||
@@ -3,16 +3,14 @@ import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { type } from "@tauri-apps/plugin-os";
|
||||
import { useLicense } from "@yaakapp-internal/license";
|
||||
import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { HeaderSize, HStack, Icon } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useKeyPressEvent } from "react-use";
|
||||
import { appInfo } from "../../lib/appInfo";
|
||||
import { capitalize } from "../../lib/capitalize";
|
||||
import { CountBadge } from "../core/CountBadge";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { HStack } from "../core/Stacks";
|
||||
import { TabContent, type TabItem, Tabs } from "../core/Tabs/Tabs";
|
||||
import { HeaderSize } from "../HeaderSize";
|
||||
import { SettingsCertificates } from "./SettingsCertificates";
|
||||
import { SettingsGeneral } from "./SettingsGeneral";
|
||||
import { SettingsHotkeys } from "./SettingsHotkeys";
|
||||
@@ -77,6 +75,10 @@ export default function Settings({ hide }: Props) {
|
||||
onlyXWindowControl
|
||||
size="md"
|
||||
className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold"
|
||||
osType={type()}
|
||||
hideWindowControls={settings.hideWindowControls}
|
||||
useNativeTitlebar={settings.useNativeTitlebar}
|
||||
interfaceScale={settings.interfaceScale}
|
||||
>
|
||||
<HStack
|
||||
space={2}
|
||||
@@ -1,17 +1,15 @@
|
||||
import type { ClientCertificate } from "@yaakapp-internal/models";
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { Heading, HStack, InlineCode, VStack } from "@yaakapp-internal/ui";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useRef } from "react";
|
||||
import { showConfirmDelete } from "../../lib/confirm";
|
||||
import { Button } from "../core/Button";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { DetailsBanner } from "../core/DetailsBanner";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
import { SelectFile } from "../SelectFile";
|
||||
|
||||
function createEmptyCertificate(): ClientCertificate {
|
||||
@@ -1,5 +1,6 @@
|
||||
import { revealItemInDir } from "@tauri-apps/plugin-opener";
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { Heading, VStack } from "@yaakapp-internal/ui";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { useCheckForUpdates } from "../../hooks/useCheckForUpdates";
|
||||
@@ -7,13 +8,11 @@ import { appInfo } from "../../lib/appInfo";
|
||||
import { revealInFinderText } from "../../lib/reveal";
|
||||
import { CargoFeature } from "../CargoFeature";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { KeyValueRow, KeyValueRows } from "../core/KeyValueRow";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Select } from "../core/Select";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { VStack } from "../core/Stacks";
|
||||
|
||||
export function SettingsGeneral() {
|
||||
const workspace = useAtomValue(activeWorkspaceAtom);
|
||||
@@ -1,4 +1,16 @@
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import {
|
||||
Heading,
|
||||
HStack,
|
||||
Icon,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
VStack,
|
||||
} from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import { fuzzyMatch } from "fuzzbunny";
|
||||
import { useAtomValue } from "jotai";
|
||||
@@ -16,13 +28,9 @@ import { capitalize } from "../../lib/capitalize";
|
||||
import { showDialog } from "../../lib/dialog";
|
||||
import { Button } from "../core/Button";
|
||||
import { Dropdown, type DropdownItem } from "../core/Dropdown";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { HotkeyRaw } from "../core/Hotkey";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "../core/Table";
|
||||
|
||||
const HOLD_KEYS = ["Shift", "Control", "Alt", "Meta"];
|
||||
const LAYOUT_INSENSITIVE_KEYS = [
|
||||
@@ -3,21 +3,17 @@ import { useFonts } from "@yaakapp-internal/fonts";
|
||||
import { useLicense } from "@yaakapp-internal/license";
|
||||
import type { EditorKeymap, Settings } from "@yaakapp-internal/models";
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { clamp, Heading, HStack, Icon, VStack } from "@yaakapp-internal/ui";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useState } from "react";
|
||||
|
||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { clamp } from "../../lib/clamp";
|
||||
import { showConfirm } from "../../lib/confirm";
|
||||
import { invokeCmd } from "../../lib/tauri";
|
||||
import { CargoFeature } from "../CargoFeature";
|
||||
import { Button } from "../core/Button";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { Link } from "../core/Link";
|
||||
import { Select } from "../core/Select";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
const NULL_FONT_VALUE = "__NULL_FONT__";
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { useLicense } from "@yaakapp-internal/license";
|
||||
import { Banner, HStack, Icon, VStack } from "@yaakapp-internal/ui";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import { formatDate } from "date-fns/format";
|
||||
import { useState } from "react";
|
||||
import { useToggle } from "../../hooks/useToggle";
|
||||
import { pluralizeCount } from "../../lib/pluralize";
|
||||
import { CargoFeature } from "../CargoFeature";
|
||||
import { Banner } from "../core/Banner";
|
||||
import { Button } from "../core/Button";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { Link } from "../core/Link";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
export function SettingsLicense() {
|
||||
return (
|
||||
@@ -9,10 +9,22 @@ import {
|
||||
searchPlugins,
|
||||
uninstallPlugin,
|
||||
} from "@yaakapp-internal/plugins";
|
||||
import {
|
||||
HStack,
|
||||
Icon,
|
||||
InlineCode,
|
||||
LoadingIcon,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
useDebouncedValue,
|
||||
} from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useState } from "react";
|
||||
import { useDebouncedValue } from "../../hooks/useDebouncedValue";
|
||||
import { useInstallPlugin } from "../../hooks/useInstallPlugin";
|
||||
import { usePluginInfo } from "../../hooks/usePluginInfo";
|
||||
import { usePluginsKey, useRefreshPlugins } from "../../hooks/usePlugins";
|
||||
@@ -21,14 +33,9 @@ import { minPromiseMillis } from "../../lib/minPromiseMillis";
|
||||
import { Button } from "../core/Button";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { CountBadge } from "../core/CountBadge";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { Link } from "../core/Link";
|
||||
import { LoadingIcon } from "../core/LoadingIcon";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { HStack } from "../core/Stacks";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "../core/Table";
|
||||
import { TabContent, Tabs } from "../core/Tabs/Tabs";
|
||||
import { EmptyStateText } from "../EmptyStateText";
|
||||
import { SelectFile } from "../SelectFile";
|
||||
@@ -1,13 +1,10 @@
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { Heading, HStack, InlineCode, VStack } from "@yaakapp-internal/ui";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { Heading } from "../core/Heading";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { PlainInput } from "../core/PlainInput";
|
||||
import { Select } from "../core/Select";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
export function SettingsProxy() {
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
@@ -1,18 +1,15 @@
|
||||
import { patchModel, settingsAtom } from "@yaakapp-internal/models";
|
||||
import { Heading, HStack, Icon, type IconProps, VStack } from "@yaakapp-internal/ui";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { useResolvedAppearance } from "../../hooks/useResolvedAppearance";
|
||||
import { useResolvedTheme } from "../../hooks/useResolvedTheme";
|
||||
import type { ButtonProps } from "../core/Button";
|
||||
import { Heading } from "../core/Heading";
|
||||
import type { IconProps } from "../core/Icon";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { Link } from "../core/Link";
|
||||
import type { SelectProps } from "../core/Select";
|
||||
import { Select } from "../core/Select";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
const Editor = lazy(() => import("../core/Editor/Editor").then((m) => ({ default: m.Editor })));
|
||||
|
||||
@@ -9,7 +9,7 @@ import { showDialog } from "../lib/dialog";
|
||||
import { importData } from "../lib/importData";
|
||||
import type { DropdownRef } from "./core/Dropdown";
|
||||
import { Dropdown } from "./core/Dropdown";
|
||||
import { Icon } from "./core/Icon";
|
||||
import { Icon } from "@yaakapp-internal/ui";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { KeyboardShortcutsDialog } from "./KeyboardShortcutsDialog";
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from "@yaakapp-internal/models";
|
||||
import classNames from "classnames";
|
||||
import { atom, useAtomValue } from "jotai";
|
||||
import { selectAtom } from "jotai/utils";
|
||||
import { atomFamily, selectAtom } from "jotai/utils";
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { moveToWorkspace } from "../commands/moveToWorkspace";
|
||||
import { openFolderSettings } from "../commands/openFolderSettings";
|
||||
@@ -43,31 +43,37 @@ import { useSidebarHidden } from "../hooks/useSidebarHidden";
|
||||
import { getWebsocketRequestActions } from "../hooks/useWebsocketRequestActions";
|
||||
import { deepEqualAtom } from "../lib/atoms";
|
||||
import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm";
|
||||
import { fireAndForget } from "../lib/fireAndForget";
|
||||
import { jotaiStore } from "../lib/jotai";
|
||||
import { resolvedModelName } from "../lib/resolvedModelName";
|
||||
import { isSidebarFocused } from "../lib/scopes";
|
||||
import { navigateToRequestOrFolderOrWorkspace } from "../lib/setWorkspaceSearchParams";
|
||||
import type { ContextMenuProps, DropdownItem } from "./core/Dropdown";
|
||||
import { Dropdown } from "./core/Dropdown";
|
||||
import { ContextMenu, Dropdown } from "./core/Dropdown";
|
||||
import type { FieldDef } from "./core/Editor/filter/extension";
|
||||
import { filter } from "./core/Editor/filter/extension";
|
||||
import { evaluate, parseQuery } from "./core/Editor/filter/query";
|
||||
import { HttpMethodTag } from "./core/HttpMethodTag";
|
||||
import { HttpStatusTag } from "./core/HttpStatusTag";
|
||||
import { Icon } from "./core/Icon";
|
||||
import {
|
||||
Icon,
|
||||
LoadingIcon,
|
||||
Tree,
|
||||
isSelectedFamily,
|
||||
selectedIdsFamily,
|
||||
InlineCode,
|
||||
} from "@yaakapp-internal/ui";
|
||||
import type { TreeNode, TreeHandle, TreeProps, TreeItemProps } from "@yaakapp-internal/ui";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { InlineCode } from "./core/InlineCode";
|
||||
import type { InputHandle } from "./core/Input";
|
||||
import { Input } from "./core/Input";
|
||||
import { LoadingIcon } from "./core/LoadingIcon";
|
||||
import { collapsedFamily, isSelectedFamily, selectedIdsFamily } from "./core/tree/atoms";
|
||||
import type { TreeNode } from "./core/tree/common";
|
||||
import type { TreeHandle, TreeProps } from "./core/tree/Tree";
|
||||
import { Tree } from "./core/tree/Tree";
|
||||
import type { TreeItemProps } from "./core/tree/TreeItem";
|
||||
import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage";
|
||||
import { GitDropdown } from "./git/GitDropdown";
|
||||
|
||||
const collapsedFamily = atomFamily((treeId: string) => {
|
||||
const key = ["sidebar_collapsed", treeId ?? "n/a"];
|
||||
return atomWithKVStorage<Record<string, boolean>>(key, {});
|
||||
});
|
||||
|
||||
type SidebarModel = Workspace | Folder | HttpRequest | GrpcRequest | WebsocketRequest;
|
||||
function isSidebarLeafModel(m: AnyModel): boolean {
|
||||
const modelMap: Record<Exclude<SidebarModel["model"], "workspace">, null> = {
|
||||
@@ -230,95 +236,127 @@ function Sidebar({ className }: { className?: string }) {
|
||||
[],
|
||||
);
|
||||
|
||||
const actions = useMemo(() => {
|
||||
const enable = () => treeRef.current?.hasFocus() ?? false;
|
||||
const treeHasFocus = useCallback(() => treeRef.current?.hasFocus() ?? false, []);
|
||||
|
||||
const actions = {
|
||||
"sidebar.context_menu": {
|
||||
enable,
|
||||
cb: () => treeRef.current?.showContextMenu(),
|
||||
},
|
||||
"sidebar.expand_all": {
|
||||
enable: isSidebarFocused,
|
||||
cb: () => {
|
||||
jotaiStore.set(collapsedFamily(treeId), {});
|
||||
},
|
||||
},
|
||||
"sidebar.collapse_all": {
|
||||
enable: isSidebarFocused,
|
||||
cb: () => {
|
||||
if (tree == null) return;
|
||||
const getSelectedTreeModels = useCallback(
|
||||
() => treeRef.current?.getSelectedItems() as SidebarModel[] | undefined,
|
||||
[],
|
||||
);
|
||||
|
||||
const next = (node: TreeNode<SidebarModel>, collapsed: Record<string, boolean>) => {
|
||||
let newCollapsed = { ...collapsed };
|
||||
for (const n of node.children ?? []) {
|
||||
if (n.item.model !== "folder") continue;
|
||||
newCollapsed[n.item.id] = true;
|
||||
newCollapsed = next(n, newCollapsed);
|
||||
}
|
||||
return newCollapsed;
|
||||
};
|
||||
const collapsed = next(tree, {});
|
||||
jotaiStore.set(collapsedFamily(treeId), collapsed);
|
||||
},
|
||||
},
|
||||
"sidebar.selected.delete": {
|
||||
enable,
|
||||
cb: async (items: SidebarModel[]) => {
|
||||
await deleteModelWithConfirm(items);
|
||||
},
|
||||
},
|
||||
"sidebar.selected.rename": {
|
||||
enable,
|
||||
allowDefault: true,
|
||||
cb: async (items: SidebarModel[]) => {
|
||||
const item = items[0];
|
||||
if (items.length === 1 && item != null) {
|
||||
treeRef.current?.renameItem(item.id);
|
||||
}
|
||||
},
|
||||
},
|
||||
"sidebar.selected.duplicate": {
|
||||
// Higher priority so this takes precedence over model.duplicate (same Meta+d binding)
|
||||
priority: 10,
|
||||
enable,
|
||||
cb: async (items: SidebarModel[]) => {
|
||||
if (items.length === 1 && items[0]) {
|
||||
const item = items[0];
|
||||
const newId = await duplicateModel(item);
|
||||
navigateToRequestOrFolderOrWorkspace(newId, item.model);
|
||||
} else {
|
||||
await Promise.all(items.map(duplicateModel));
|
||||
}
|
||||
},
|
||||
},
|
||||
"sidebar.selected.move": {
|
||||
enable,
|
||||
cb: async (items: SidebarModel[]) => {
|
||||
const requests = items.filter(
|
||||
(i): i is HttpRequest | GrpcRequest | WebsocketRequest =>
|
||||
i.model === "http_request" ||
|
||||
i.model === "grpc_request" ||
|
||||
i.model === "websocket_request",
|
||||
);
|
||||
if (requests.length > 0) {
|
||||
moveToWorkspace.mutate(requests);
|
||||
}
|
||||
},
|
||||
},
|
||||
"request.send": {
|
||||
enable,
|
||||
cb: async (items: SidebarModel[]) => {
|
||||
await Promise.all(
|
||||
items
|
||||
.filter((i) => i.model === "http_request")
|
||||
.map((i) => sendAnyHttpRequest.mutate(i.id)),
|
||||
);
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
return actions;
|
||||
}, [tree, treeId]);
|
||||
const handleRenameSelected = useCallback((items: SidebarModel[]) => {
|
||||
if (items.length === 1 && items[0] != null) {
|
||||
treeRef.current?.renameItem(items[0].id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleDeleteSelected = useCallback(async (items: SidebarModel[]) => {
|
||||
await deleteModelWithConfirm(items);
|
||||
}, []);
|
||||
|
||||
const handleDuplicateSelected = useCallback(async (items: SidebarModel[]) => {
|
||||
if (items.length === 1 && items[0]) {
|
||||
const newId = await duplicateModel(items[0]);
|
||||
navigateToRequestOrFolderOrWorkspace(newId, items[0].model);
|
||||
} else {
|
||||
await Promise.all(items.map(duplicateModel));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleMoveSelected = useCallback((items: SidebarModel[]) => {
|
||||
const requests = items.filter(
|
||||
(i): i is HttpRequest | GrpcRequest | WebsocketRequest =>
|
||||
i.model === "http_request" || i.model === "grpc_request" || i.model === "websocket_request",
|
||||
);
|
||||
if (requests.length > 0) {
|
||||
moveToWorkspace.mutate(requests);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSendSelected = useCallback(async (items: SidebarModel[]) => {
|
||||
await Promise.all(
|
||||
items.filter((i) => i.model === "http_request").map((i) => sendAnyHttpRequest.mutate(i.id)),
|
||||
);
|
||||
}, []);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.context_menu",
|
||||
useCallback(() => {
|
||||
treeRef.current?.showContextMenu();
|
||||
}, []),
|
||||
{ enable: treeHasFocus },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.expand_all",
|
||||
useCallback(() => {
|
||||
jotaiStore.set(collapsedFamily(treeId), {});
|
||||
}, [treeId]),
|
||||
{ enable: isSidebarFocused },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.collapse_all",
|
||||
useCallback(() => {
|
||||
if (tree == null) return;
|
||||
const next = (node: TreeNode<SidebarModel>, collapsed: Record<string, boolean>) => {
|
||||
let newCollapsed = { ...collapsed };
|
||||
for (const n of node.children ?? []) {
|
||||
if (n.item.model !== "folder") continue;
|
||||
newCollapsed[n.item.id] = true;
|
||||
newCollapsed = next(n, newCollapsed);
|
||||
}
|
||||
return newCollapsed;
|
||||
};
|
||||
const collapsed = next(tree, {});
|
||||
jotaiStore.set(collapsedFamily(treeId), collapsed);
|
||||
}, [tree, treeId]),
|
||||
{ enable: isSidebarFocused },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.selected.delete",
|
||||
useCallback(() => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) void handleDeleteSelected(items);
|
||||
}, [getSelectedTreeModels, handleDeleteSelected]),
|
||||
{ enable: treeHasFocus },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.selected.rename",
|
||||
useCallback(() => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) handleRenameSelected(items);
|
||||
}, [getSelectedTreeModels, handleRenameSelected]),
|
||||
{ enable: treeHasFocus, allowDefault: true },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.selected.duplicate",
|
||||
useCallback(async () => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) await handleDuplicateSelected(items);
|
||||
}, [getSelectedTreeModels, handleDuplicateSelected]),
|
||||
{ priority: 10, enable: treeHasFocus },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"sidebar.selected.move",
|
||||
useCallback(() => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) handleMoveSelected(items);
|
||||
}, [getSelectedTreeModels, handleMoveSelected]),
|
||||
{ enable: treeHasFocus },
|
||||
);
|
||||
|
||||
useHotKey(
|
||||
"request.send",
|
||||
useCallback(async () => {
|
||||
const items = getSelectedTreeModels();
|
||||
if (items) await handleSendSelected(items);
|
||||
}, [getSelectedTreeModels, handleSendSelected]),
|
||||
{ enable: treeHasFocus },
|
||||
);
|
||||
|
||||
const getContextMenu = useCallback<(items: SidebarModel[]) => Promise<DropdownItem[]>>(
|
||||
async (items) => {
|
||||
@@ -356,7 +394,7 @@ function Sidebar({ className }: { className?: string }) {
|
||||
hotKeyLabelOnly: true,
|
||||
hidden: !onlyHttpRequests,
|
||||
leftSlot: <Icon icon="send_horizontal" />,
|
||||
onSelect: () => actions["request.send"].cb(items),
|
||||
onSelect: () => handleSendSelected(items),
|
||||
},
|
||||
...(items.length === 1 && child.model === "http_request"
|
||||
? await getHttpRequestActions()
|
||||
@@ -426,16 +464,14 @@ function Sidebar({ className }: { className?: string }) {
|
||||
hidden: items.length > 1,
|
||||
hotKeyAction: "sidebar.selected.rename",
|
||||
hotKeyLabelOnly: true,
|
||||
onSelect: () => {
|
||||
treeRef.current?.renameItem(child.id);
|
||||
},
|
||||
onSelect: () => handleRenameSelected(items),
|
||||
},
|
||||
{
|
||||
label: "Duplicate",
|
||||
hotKeyAction: "model.duplicate",
|
||||
hotKeyLabelOnly: true, // Would trigger for every request (bad)
|
||||
leftSlot: <Icon icon="copy" />,
|
||||
onSelect: () => actions["sidebar.selected.duplicate"].cb(items),
|
||||
onSelect: () => handleDuplicateSelected(items),
|
||||
},
|
||||
{
|
||||
label: items.length <= 1 ? "Move" : `Move ${requestItems.length} Requests`,
|
||||
@@ -446,9 +482,7 @@ function Sidebar({ className }: { className?: string }) {
|
||||
workspaces.length <= 1 ||
|
||||
requestItems.length === 0 ||
|
||||
requestItems.length !== items.length,
|
||||
onSelect: () => {
|
||||
fireAndForget(actions["sidebar.selected.move"].cb(items));
|
||||
},
|
||||
onSelect: () => handleMoveSelected(items),
|
||||
},
|
||||
{
|
||||
color: "danger",
|
||||
@@ -456,16 +490,23 @@ function Sidebar({ className }: { className?: string }) {
|
||||
hotKeyAction: "sidebar.selected.delete",
|
||||
hotKeyLabelOnly: true,
|
||||
leftSlot: <Icon icon="trash" />,
|
||||
onSelect: () => actions["sidebar.selected.delete"].cb(items),
|
||||
onSelect: () => handleDeleteSelected(items),
|
||||
},
|
||||
...modelCreationItems,
|
||||
];
|
||||
return menuItems;
|
||||
},
|
||||
[actions],
|
||||
[],
|
||||
);
|
||||
|
||||
const hotkeys = useMemo<TreeProps<SidebarModel>["hotkeys"]>(() => ({ actions }), [actions]);
|
||||
const renderContextMenuFn = useCallback<
|
||||
NonNullable<TreeProps<SidebarModel>["renderContextMenu"]>
|
||||
>(
|
||||
({ items, position, onClose }) => (
|
||||
<ContextMenu items={items as DropdownItem[]} triggerPosition={position} onClose={onClose} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Use a language compartment for the filter so we can reconfigure it when the autocompletion changes
|
||||
const filterLanguageCompartmentRef = useRef(new Compartment());
|
||||
@@ -552,14 +593,29 @@ function Sidebar({ className }: { className?: string }) {
|
||||
{
|
||||
label: "Expand All Folders",
|
||||
leftSlot: <Icon icon="chevrons_up_down" />,
|
||||
onSelect: actions["sidebar.expand_all"].cb,
|
||||
onSelect: () => jotaiStore.set(collapsedFamily(treeId), {}),
|
||||
hotKeyAction: "sidebar.expand_all",
|
||||
hotKeyLabelOnly: true,
|
||||
},
|
||||
{
|
||||
label: "Collapse All Folders",
|
||||
leftSlot: <Icon icon="chevrons_down_up" />,
|
||||
onSelect: actions["sidebar.collapse_all"].cb,
|
||||
onSelect: () => {
|
||||
if (tree == null) return;
|
||||
const next = (
|
||||
node: TreeNode<SidebarModel>,
|
||||
collapsed: Record<string, boolean>,
|
||||
) => {
|
||||
let newCollapsed = { ...collapsed };
|
||||
for (const n of node.children ?? []) {
|
||||
if (n.item.model !== "folder") continue;
|
||||
newCollapsed[n.item.id] = true;
|
||||
newCollapsed = next(n, newCollapsed);
|
||||
}
|
||||
return newCollapsed;
|
||||
};
|
||||
jotaiStore.set(collapsedFamily(treeId), next(tree, {}));
|
||||
},
|
||||
hotKeyAction: "sidebar.collapse_all",
|
||||
hotKeyLabelOnly: true,
|
||||
},
|
||||
@@ -584,11 +640,12 @@ function Sidebar({ className }: { className?: string }) {
|
||||
ref={handleTreeRefInit}
|
||||
root={tree}
|
||||
treeId={treeId}
|
||||
hotkeys={hotkeys}
|
||||
collapsedAtom={collapsedFamily(treeId)}
|
||||
getItemKey={getItemKey}
|
||||
ItemInner={SidebarInnerItem}
|
||||
ItemLeftSlotInner={SidebarLeftSlot}
|
||||
getContextMenu={getContextMenu}
|
||||
renderContextMenu={renderContextMenuFn}
|
||||
onActivate={handleActivate}
|
||||
getEditOptions={getEditOptions}
|
||||
className="pl-2 pr-3 pt-2 pb-2"
|
||||
@@ -1,28 +1,28 @@
|
||||
import { HStack } from "@yaakapp-internal/ui";
|
||||
import { useMemo } from "react";
|
||||
import { useFloatingSidebarHidden } from "../hooks/useFloatingSidebarHidden";
|
||||
import { useShouldFloatSidebar } from "../hooks/useShouldFloatSidebar";
|
||||
import { useSidebarHidden } from "../hooks/useSidebarHidden";
|
||||
import { CreateDropdown } from "./CreateDropdown";
|
||||
import { IconButton } from "./core/IconButton";
|
||||
import { HStack } from "./core/Stacks";
|
||||
|
||||
export function SidebarActions() {
|
||||
const floating = useShouldFloatSidebar();
|
||||
const [normalHidden, setNormalHidden] = useSidebarHidden();
|
||||
interface Props {
|
||||
floating?: boolean;
|
||||
}
|
||||
|
||||
export function SidebarActions({ floating = false }: Props) {
|
||||
const [sidebarHidden, setSidebarHidden] = useSidebarHidden();
|
||||
const [floatingHidden, setFloatingHidden] = useFloatingSidebarHidden();
|
||||
|
||||
const hidden = floating ? floatingHidden : normalHidden;
|
||||
const hidden = floating ? floatingHidden : sidebarHidden;
|
||||
const setHidden = useMemo(
|
||||
() => (floating ? setFloatingHidden : setNormalHidden),
|
||||
[floating, setFloatingHidden, setNormalHidden],
|
||||
() => (floating ? setFloatingHidden : setSidebarHidden),
|
||||
[floating, setFloatingHidden, setSidebarHidden],
|
||||
);
|
||||
|
||||
return (
|
||||
<HStack className="h-full">
|
||||
<IconButton
|
||||
onClick={async () => {
|
||||
// NOTE: We're not using the (h) => !h pattern here because the data
|
||||
// might be different if another window changed it (out of sync)
|
||||
await setHidden(!hidden);
|
||||
}}
|
||||
className="pointer-events-auto"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user