diff --git a/.claude-context.md b/.claude-context.md index 94a20862..97e20f6b 100644 --- a/.claude-context.md +++ b/.claude-context.md @@ -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 ` 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 diff --git a/.gitattributes b/.gitattributes index 4565c740..67ab542c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index 3a6f695f..5e128331 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -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,8 @@ 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" + projectPath: ./crates-tauri/yaak-app-client + args: "${{ matrix.args }} --config ./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 +172,9 @@ 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"}}}}' + Push-Location crates-tauri/yaak-app-client + npx tauri bundle ${{ matrix.args }} --bundles nsis --config ./tauri.release.conf.json --config '{"bundle":{"createUpdaterArtifacts":true,"windows":{"nsis":{"installMode":"perMachine"}}}}' + Pop-Location $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' diff --git a/.github/workflows/release-cli-npm.yml b/.github/workflows/release-cli-npm.yml index e0413584..c08e687a 100644 --- a/.github/workflows/release-cli-npm.yml +++ b/.github/workflows/release-cli-npm.yml @@ -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 diff --git a/.gitignore b/.gitignore index c91877d6..5beed3a8 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.oxfmtignore b/.oxfmtignore new file mode 100644 index 00000000..0dca30df --- /dev/null +++ b/.oxfmtignore @@ -0,0 +1,3 @@ +**/bindings/** +**/routeTree.gen.ts +crates/yaak-templates/pkg/** diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 389ff9b4..682f3b97 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -1,4 +1,8 @@ { "printWidth": 100, - "ignorePatterns": ["**/bindings/**", "crates/yaak-templates/pkg/**", "src-web/routeTree.gen.ts"] + "ignorePatterns": [ + "**/bindings/**", + "crates/yaak-templates/pkg/**", + "apps/yaak-client/routeTree.gen.ts" + ] } diff --git a/Cargo.lock b/Cargo.lock index 9e0c5e2e..477efc5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", @@ -6807,6 +6860,7 @@ version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" 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" diff --git a/Cargo.toml b/Cargo.toml index acd11e16..48090c3d 100644 --- a/Cargo.toml +++ b/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 diff --git a/README.md b/README.md index b60f8769..980d6048 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- +

diff --git a/src-web/.gitignore b/apps/yaak-client/.gitignore similarity index 100% rename from src-web/.gitignore rename to apps/yaak-client/.gitignore diff --git a/src-web/commands/commands.tsx b/apps/yaak-client/commands/commands.tsx similarity index 97% rename from src-web/commands/commands.tsx rename to apps/yaak-client/commands/commands.tsx index 5a44d67d..40618a77 100644 --- a/src-web/commands/commands.tsx +++ b/apps/yaak-client/commands/commands.tsx @@ -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"; diff --git a/src-web/commands/createEnvironment.tsx b/apps/yaak-client/commands/createEnvironment.tsx similarity index 100% rename from src-web/commands/createEnvironment.tsx rename to apps/yaak-client/commands/createEnvironment.tsx diff --git a/src-web/commands/deleteWebsocketConnections.ts b/apps/yaak-client/commands/deleteWebsocketConnections.ts similarity index 100% rename from src-web/commands/deleteWebsocketConnections.ts rename to apps/yaak-client/commands/deleteWebsocketConnections.ts diff --git a/src-web/commands/moveToWorkspace.tsx b/apps/yaak-client/commands/moveToWorkspace.tsx similarity index 100% rename from src-web/commands/moveToWorkspace.tsx rename to apps/yaak-client/commands/moveToWorkspace.tsx diff --git a/src-web/commands/openFolderSettings.tsx b/apps/yaak-client/commands/openFolderSettings.tsx similarity index 100% rename from src-web/commands/openFolderSettings.tsx rename to apps/yaak-client/commands/openFolderSettings.tsx diff --git a/src-web/commands/openSettings.tsx b/apps/yaak-client/commands/openSettings.tsx similarity index 100% rename from src-web/commands/openSettings.tsx rename to apps/yaak-client/commands/openSettings.tsx diff --git a/src-web/commands/openWorkspaceFromSyncDir.tsx b/apps/yaak-client/commands/openWorkspaceFromSyncDir.tsx similarity index 100% rename from src-web/commands/openWorkspaceFromSyncDir.tsx rename to apps/yaak-client/commands/openWorkspaceFromSyncDir.tsx diff --git a/src-web/commands/openWorkspaceSettings.tsx b/apps/yaak-client/commands/openWorkspaceSettings.tsx similarity index 100% rename from src-web/commands/openWorkspaceSettings.tsx rename to apps/yaak-client/commands/openWorkspaceSettings.tsx diff --git a/src-web/commands/switchWorkspace.tsx b/apps/yaak-client/commands/switchWorkspace.tsx similarity index 100% rename from src-web/commands/switchWorkspace.tsx rename to apps/yaak-client/commands/switchWorkspace.tsx diff --git a/src-web/components/BinaryFileEditor.tsx b/apps/yaak-client/components/BinaryFileEditor.tsx similarity index 93% rename from src-web/components/BinaryFileEditor.tsx rename to apps/yaak-client/components/BinaryFileEditor.tsx index a4a80d3d..7fc3d458 100644 --- a/src-web/components/BinaryFileEditor.tsx +++ b/apps/yaak-client/components/BinaryFileEditor.tsx @@ -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 = { diff --git a/src-web/components/CargoFeature.tsx b/apps/yaak-client/components/CargoFeature.tsx similarity index 100% rename from src-web/components/CargoFeature.tsx rename to apps/yaak-client/components/CargoFeature.tsx diff --git a/src-web/components/CloneGitRepositoryDialog.tsx b/apps/yaak-client/components/CloneGitRepositoryDialog.tsx similarity index 98% rename from src-web/components/CloneGitRepositoryDialog.tsx rename to apps/yaak-client/components/CloneGitRepositoryDialog.tsx index b0e27373..3b3e2af2 100644 --- a/src-web/components/CloneGitRepositoryDialog.tsx +++ b/apps/yaak-client/components/CloneGitRepositoryDialog.tsx @@ -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 { diff --git a/src-web/components/ColorIndicator.tsx b/apps/yaak-client/components/ColorIndicator.tsx similarity index 100% rename from src-web/components/ColorIndicator.tsx rename to apps/yaak-client/components/ColorIndicator.tsx diff --git a/src-web/components/CommandPaletteDialog.tsx b/apps/yaak-client/components/CommandPaletteDialog.tsx similarity index 99% rename from src-web/components/CommandPaletteDialog.tsx rename to apps/yaak-client/components/CommandPaletteDialog.tsx index e0505f0c..eec1d3b9 100644 --- a/src-web/components/CommandPaletteDialog.tsx +++ b/apps/yaak-client/components/CommandPaletteDialog.tsx @@ -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 { diff --git a/src-web/components/ConfirmLargeRequestBody.tsx b/apps/yaak-client/components/ConfirmLargeRequestBody.tsx similarity index 94% rename from src-web/components/ConfirmLargeRequestBody.tsx rename to apps/yaak-client/components/ConfirmLargeRequestBody.tsx index 574c5336..f8acf535 100644 --- a/src-web/components/ConfirmLargeRequestBody.tsx +++ b/apps/yaak-client/components/ConfirmLargeRequestBody.tsx @@ -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; diff --git a/src-web/components/ConfirmLargeResponse.tsx b/apps/yaak-client/components/ConfirmLargeResponse.tsx similarity index 94% rename from src-web/components/ConfirmLargeResponse.tsx rename to apps/yaak-client/components/ConfirmLargeResponse.tsx index a2b58cf9..90bb2c85 100644 --- a/src-web/components/ConfirmLargeResponse.tsx +++ b/apps/yaak-client/components/ConfirmLargeResponse.tsx @@ -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; diff --git a/src-web/components/ConfirmLargeResponseRequest.tsx b/apps/yaak-client/components/ConfirmLargeResponseRequest.tsx similarity index 93% rename from src-web/components/ConfirmLargeResponseRequest.tsx rename to apps/yaak-client/components/ConfirmLargeResponseRequest.tsx index ea7f48a0..92525f70 100644 --- a/src-web/components/ConfirmLargeResponseRequest.tsx +++ b/apps/yaak-client/components/ConfirmLargeResponseRequest.tsx @@ -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; diff --git a/src-web/components/CookieDialog.tsx b/apps/yaak-client/components/CookieDialog.tsx similarity index 96% rename from src-web/components/CookieDialog.tsx rename to apps/yaak-client/components/CookieDialog.tsx index 34329188..6c8062c5 100644 --- a/src-web/components/CookieDialog.tsx +++ b/apps/yaak-client/components/CookieDialog.tsx @@ -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; diff --git a/src-web/components/CookieDropdown.tsx b/apps/yaak-client/components/CookieDropdown.tsx similarity index 97% rename from src-web/components/CookieDropdown.tsx rename to apps/yaak-client/components/CookieDropdown.tsx index 4e590497..7c64d6c8 100644 --- a/src-web/components/CookieDropdown.tsx +++ b/apps/yaak-client/components/CookieDropdown.tsx @@ -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(); diff --git a/src-web/components/CopyButton.tsx b/apps/yaak-client/components/CopyButton.tsx similarity index 93% rename from src-web/components/CopyButton.tsx rename to apps/yaak-client/components/CopyButton.tsx index 93f0c1f7..2a0da485 100644 --- a/src-web/components/CopyButton.tsx +++ b/apps/yaak-client/components/CopyButton.tsx @@ -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"; diff --git a/src-web/components/CopyIconButton.tsx b/apps/yaak-client/components/CopyIconButton.tsx similarity index 83% rename from src-web/components/CopyIconButton.tsx rename to apps/yaak-client/components/CopyIconButton.tsx index a828103a..a97a032c 100644 --- a/src-web/components/CopyIconButton.tsx +++ b/apps/yaak-client/components/CopyIconButton.tsx @@ -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 { text: string | (() => Promise); diff --git a/src-web/components/CreateDropdown.tsx b/apps/yaak-client/components/CreateDropdown.tsx similarity index 100% rename from src-web/components/CreateDropdown.tsx rename to apps/yaak-client/components/CreateDropdown.tsx diff --git a/src-web/components/CreateEnvironmentDialog.tsx b/apps/yaak-client/components/CreateEnvironmentDialog.tsx similarity index 100% rename from src-web/components/CreateEnvironmentDialog.tsx rename to apps/yaak-client/components/CreateEnvironmentDialog.tsx diff --git a/src-web/components/CreateWorkspaceDialog.tsx b/apps/yaak-client/components/CreateWorkspaceDialog.tsx similarity index 98% rename from src-web/components/CreateWorkspaceDialog.tsx rename to apps/yaak-client/components/CreateWorkspaceDialog.tsx index 86eb8503..339206d5 100644 --- a/src-web/components/CreateWorkspaceDialog.tsx +++ b/apps/yaak-client/components/CreateWorkspaceDialog.tsx @@ -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"; diff --git a/src-web/components/Dialogs.tsx b/apps/yaak-client/components/Dialogs.tsx similarity index 100% rename from src-web/components/Dialogs.tsx rename to apps/yaak-client/components/Dialogs.tsx diff --git a/src-web/components/DnsOverridesEditor.tsx b/apps/yaak-client/components/DnsOverridesEditor.tsx similarity index 97% rename from src-web/components/DnsOverridesEditor.tsx rename to apps/yaak-client/components/DnsOverridesEditor.tsx index 60948de3..bcdb5483 100644 --- a/src-web/components/DnsOverridesEditor.tsx +++ b/apps/yaak-client/components/DnsOverridesEditor.tsx @@ -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; diff --git a/src-web/components/DropMarker.tsx b/apps/yaak-client/components/DropMarker.tsx similarity index 100% rename from src-web/components/DropMarker.tsx rename to apps/yaak-client/components/DropMarker.tsx diff --git a/src-web/components/DynamicForm.tsx b/apps/yaak-client/components/DynamicForm.tsx similarity index 99% rename from src-web/components/DynamicForm.tsx rename to apps/yaak-client/components/DynamicForm.tsx index 91d49890..167f4fdb 100644 --- a/src-web/components/DynamicForm.tsx +++ b/apps/yaak-client/components/DynamicForm.tsx @@ -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"; diff --git a/src-web/components/EmptyStateText.tsx b/apps/yaak-client/components/EmptyStateText.tsx similarity index 100% rename from src-web/components/EmptyStateText.tsx rename to apps/yaak-client/components/EmptyStateText.tsx diff --git a/src-web/components/EncryptionHelp.tsx b/apps/yaak-client/components/EncryptionHelp.tsx similarity index 87% rename from src-web/components/EncryptionHelp.tsx rename to apps/yaak-client/components/EncryptionHelp.tsx index 920cdf75..96985ab0 100644 --- a/src-web/components/EncryptionHelp.tsx +++ b/apps/yaak-client/components/EncryptionHelp.tsx @@ -1,4 +1,4 @@ -import { VStack } from "./core/Stacks"; +import { VStack } from "@yaakapp-internal/ui"; export function EncryptionHelp() { return ( diff --git a/src-web/components/EnvironmentActionsDropdown.tsx b/apps/yaak-client/components/EnvironmentActionsDropdown.tsx similarity index 98% rename from src-web/components/EnvironmentActionsDropdown.tsx rename to apps/yaak-client/components/EnvironmentActionsDropdown.tsx index f93c6872..429f8e9b 100644 --- a/src-web/components/EnvironmentActionsDropdown.tsx +++ b/apps/yaak-client/components/EnvironmentActionsDropdown.tsx @@ -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 = { diff --git a/src-web/components/EnvironmentColorIndicator.tsx b/apps/yaak-client/components/EnvironmentColorIndicator.tsx similarity index 100% rename from src-web/components/EnvironmentColorIndicator.tsx rename to apps/yaak-client/components/EnvironmentColorIndicator.tsx diff --git a/src-web/components/EnvironmentColorPicker.tsx b/apps/yaak-client/components/EnvironmentColorPicker.tsx similarity index 95% rename from src-web/components/EnvironmentColorPicker.tsx rename to apps/yaak-client/components/EnvironmentColorPicker.tsx index 7c561ec7..2b5310c6 100644 --- a/src-web/components/EnvironmentColorPicker.tsx +++ b/apps/yaak-client/components/EnvironmentColorPicker.tsx @@ -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"; diff --git a/src-web/components/EnvironmentEditDialog.tsx b/apps/yaak-client/components/EnvironmentEditDialog.tsx similarity index 79% rename from src-web/components/EnvironmentEditDialog.tsx rename to apps/yaak-client/components/EnvironmentEditDialog.tsx index c7a6a6a9..effc819d 100644 --- a/src-web/components/EnvironmentEditDialog.tsx +++ b/apps/yaak-client/components/EnvironmentEditDialog.tsx @@ -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>(key, {}); +}); + interface Props { initialEnvironmentId: string | null; setRef?: (ref: PairEditorHandle | null) => void; @@ -49,7 +53,7 @@ export function EnvironmentEditDialog({ initialEnvironmentId, setRef }: Props) { return ( (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["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["renderContextMenu"]>>( + ({ items, position, onClose }) => ( + + ), + [], + ); + const tree = useAtomValue(treeAtom); return (