mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-20 16:01:18 +02:00
Improve copy-as-curl
This commit is contained in:
@@ -137,8 +137,6 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) {
|
|||||||
// Start at 1 so we can skip the ^curl part
|
// Start at 1 so we can skip the ^curl part
|
||||||
for (let i = 1; i < parseEntries.length; i++) {
|
for (let i = 1; i < parseEntries.length; i++) {
|
||||||
let parseEntry = parseEntries[i];
|
let parseEntry = parseEntries[i];
|
||||||
// trim leading spaces between parsed entries
|
|
||||||
// regex won't match otherwise (e.g. -H 'Content-Type: application/json')
|
|
||||||
if (typeof parseEntry === 'string') {
|
if (typeof parseEntry === 'string') {
|
||||||
parseEntry = parseEntry.trim();
|
parseEntry = parseEntry.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
125
src-tauri/Cargo.lock
generated
125
src-tauri/Cargo.lock
generated
@@ -1051,16 +1051,6 @@ dependencies = [
|
|||||||
"version_check 0.9.4",
|
"version_check 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cookie"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
|
|
||||||
dependencies = [
|
|
||||||
"time",
|
|
||||||
"version_check 0.9.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie_store"
|
name = "cookie_store"
|
||||||
version = "0.16.2"
|
version = "0.16.2"
|
||||||
@@ -1830,26 +1820,11 @@ dependencies = [
|
|||||||
"new_debug_unreachable",
|
"new_debug_unreachable",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
|
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -1857,15 +1832,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-executor"
|
name = "futures-executor"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
|
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@@ -1885,9 +1860,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
|
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-lite"
|
name = "futures-lite"
|
||||||
@@ -1904,9 +1879,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
|
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1915,23 +1890,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
|
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
@@ -2226,11 +2200,9 @@ dependencies = [
|
|||||||
"hyper 0.14.27",
|
"hyper 0.14.27",
|
||||||
"hyper-rustls 0.24.2",
|
"hyper-rustls 0.24.2",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"prost",
|
"prost",
|
||||||
"prost-reflect",
|
"prost-reflect",
|
||||||
"prost-types",
|
"prost-types",
|
||||||
"protoc-bin-vendored",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
@@ -4321,56 +4293,6 @@ dependencies = [
|
|||||||
"prost",
|
"prost",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d"
|
|
||||||
dependencies = [
|
|
||||||
"protoc-bin-vendored-linux-aarch_64",
|
|
||||||
"protoc-bin-vendored-linux-ppcle_64",
|
|
||||||
"protoc-bin-vendored-linux-x86_32",
|
|
||||||
"protoc-bin-vendored-linux-x86_64",
|
|
||||||
"protoc-bin-vendored-macos-x86_64",
|
|
||||||
"protoc-bin-vendored-win32",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored-linux-aarch_64"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored-linux-ppcle_64"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored-linux-x86_32"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored-linux-x86_64"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored-macos-x86_64"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protoc-bin-vendored-win32"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psl-types"
|
name = "psl-types"
|
||||||
version = "2.0.11"
|
version = "2.0.11"
|
||||||
@@ -7251,18 +7173,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "window-shadows"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67ff424735b1ac21293b0492b069394b0a189c8a463fb015a16dea7c2e221c08"
|
|
||||||
dependencies = [
|
|
||||||
"cocoa",
|
|
||||||
"objc",
|
|
||||||
"raw-window-handle 0.5.2",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "window-vibrancy"
|
name = "window-vibrancy"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -7731,9 +7641,7 @@ dependencies = [
|
|||||||
"boa_runtime",
|
"boa_runtime",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"cookie 0.18.0",
|
|
||||||
"datetime",
|
"datetime",
|
||||||
"futures",
|
|
||||||
"grpc",
|
"grpc",
|
||||||
"http 0.2.10",
|
"http 0.2.10",
|
||||||
"log",
|
"log",
|
||||||
@@ -7759,7 +7667,6 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"uuid",
|
"uuid",
|
||||||
"window-shadows",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ workspace = { members = ["grpc"] }
|
|||||||
[package]
|
[package]
|
||||||
name = "yaak-app"
|
name = "yaak-app"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
description = "A network protocol testing utility app"
|
|
||||||
authors = ["Gregory Schier"]
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/gschier/yaak-app"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# Produce a library for mobile support
|
# Produce a library for mobile support
|
||||||
@@ -32,13 +28,11 @@ base64 = "0.22.0"
|
|||||||
boa_engine = { version = "0.18.0", features = ["annex-b"] }
|
boa_engine = { version = "0.18.0", features = ["annex-b"] }
|
||||||
boa_runtime = { version = "0.18.0" }
|
boa_runtime = { version = "0.18.0" }
|
||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
futures = "0.3.26"
|
http = "0.2.10"
|
||||||
http = "0.2.8"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
reqwest = { version = "0.11.23", features = ["multipart", "cookies", "gzip", "brotli", "deflate"] }
|
reqwest = { version = "0.11.23", features = ["multipart", "cookies", "gzip", "brotli", "deflate"] }
|
||||||
cookie = { version = "0.18.0" }
|
serde = { version = "1.0.198", features = ["derive"] }
|
||||||
serde = { version = "1.0.195", features = ["derive"] }
|
serde_json = { version = "1.0.116", features = ["raw_value"] }
|
||||||
serde_json = { version = "1.0.111", features = ["raw_value"] }
|
|
||||||
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio-rustls", "json", "chrono", "time"] }
|
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio-rustls", "json", "chrono", "time"] }
|
||||||
tauri = { version = "2.0.0-beta.17", features = [
|
tauri = { version = "2.0.0-beta.17", features = [
|
||||||
"config-toml",
|
"config-toml",
|
||||||
@@ -53,10 +47,9 @@ tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace"
|
|||||||
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
tauri-plugin-fs = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
tauri-plugin-fs = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
tokio = { version = "1.36.0", features = ["sync"] }
|
tokio = { version = "1.36.0", features = ["sync"] }
|
||||||
uuid = "1.3.0"
|
uuid = "1.7.0"
|
||||||
log = "0.4.20"
|
log = "0.4.21"
|
||||||
datetime = "0.5.2"
|
datetime = "0.5.2"
|
||||||
window-shadows = "0.2.2"
|
|
||||||
reqwest_cookie_store = "0.6.0"
|
reqwest_cookie_store = "0.6.0"
|
||||||
grpc = { path = "./grpc" }
|
grpc = { path = "./grpc" }
|
||||||
tokio-stream = "0.1.15"
|
tokio-stream = "0.1.15"
|
||||||
|
|||||||
@@ -14,11 +14,9 @@ serde = { version = "1.0.196", features = ["derive"] }
|
|||||||
serde_json = "1.0.113"
|
serde_json = "1.0.113"
|
||||||
prost-reflect = { version = "0.12.0", features = ["serde", "derive"] }
|
prost-reflect = { version = "0.12.0", features = ["serde", "derive"] }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
once_cell = { version = "1.19.0", features = [] }
|
|
||||||
anyhow = "1.0.79"
|
anyhow = "1.0.79"
|
||||||
hyper = { version = "0.14" }
|
hyper = { version = "0.14" }
|
||||||
hyper-rustls = { version = "0.24.0", features = ["http2"] }
|
hyper-rustls = { version = "0.24.0", features = ["http2"] }
|
||||||
protoc-bin-vendored = "3.0.0"
|
|
||||||
uuid = { version = "1.7.0", features = ["v4"] }
|
uuid = { version = "1.7.0", features = ["v4"] }
|
||||||
tauri = { version = "2.0.0-beta.16" }
|
tauri = { version = "2.0.0-beta.16" }
|
||||||
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
|
|||||||
@@ -30,15 +30,14 @@ export function BinaryFileEditor({
|
|||||||
|
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
await ignoreContentType.set(false);
|
await ignoreContentType.set(false);
|
||||||
const path = await open({
|
const selected = await open({
|
||||||
title: 'Select File',
|
title: 'Select File',
|
||||||
multiple: false,
|
multiple: false,
|
||||||
});
|
});
|
||||||
if (path) {
|
if (selected == null) {
|
||||||
onChange({ filePath: path });
|
return;
|
||||||
} else {
|
|
||||||
onChange({ filePath: undefined });
|
|
||||||
}
|
}
|
||||||
|
onChange({ filePath: selected.path });
|
||||||
};
|
};
|
||||||
|
|
||||||
const filePath = typeof body.filePath === 'string' ? body.filePath : undefined;
|
const filePath = typeof body.filePath === 'string' ? body.filePath : undefined;
|
||||||
|
|||||||
@@ -42,15 +42,15 @@ export function GrpcProtoSelection({ requestId }: Props) {
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const files = await open({
|
const selected = await open({
|
||||||
title: 'Select Proto Files',
|
title: 'Select Proto Files',
|
||||||
multiple: true,
|
multiple: true,
|
||||||
filters: [{ name: 'Proto Files', extensions: ['proto'] }],
|
filters: [{ name: 'Proto Files', extensions: ['proto'] }],
|
||||||
});
|
});
|
||||||
if (files == null) {
|
if (selected == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newFiles = files.map((f) => f.path).filter((p) => !protoFiles.includes(p));
|
const newFiles = selected.map((f) => f.path).filter((p) => !protoFiles.includes(p));
|
||||||
await protoFilesKv.set([...protoFiles, ...newFiles]);
|
await protoFilesKv.set([...protoFiles, ...newFiles]);
|
||||||
await grpc.reflect.refetch();
|
await grpc.reflect.refetch();
|
||||||
}}
|
}}
|
||||||
|
|||||||
41
src-web/components/ImportCurlButton.tsx
Normal file
41
src-web/components/ImportCurlButton.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button } from './core/Button';
|
||||||
|
import { useClipboardText } from '../hooks/useClipboardText';
|
||||||
|
import { useImportCurl } from '../hooks/useImportCurl';
|
||||||
|
import { Icon } from './core/Icon';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
export function ImportCurlButton() {
|
||||||
|
const [clipboardText] = useClipboardText();
|
||||||
|
const [lastImportedCmd, setLastImportedCmd] = useState<string>('');
|
||||||
|
const importCurl = useImportCurl();
|
||||||
|
|
||||||
|
if (!clipboardText?.trim().startsWith('curl ') || lastImportedCmd === clipboardText) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ delay: 0.5 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
variant="border"
|
||||||
|
color="secondary"
|
||||||
|
leftSlot={<Icon icon="paste" size="sm" />}
|
||||||
|
onClick={() => {
|
||||||
|
importCurl.mutate({
|
||||||
|
requestId: null, // Create request
|
||||||
|
command: clipboardText,
|
||||||
|
});
|
||||||
|
// setClipboardText('');
|
||||||
|
setLastImportedCmd(clipboardText);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Import Curl
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -38,9 +38,7 @@ import { GraphQLEditor } from './GraphQLEditor';
|
|||||||
import { HeadersEditor } from './HeadersEditor';
|
import { HeadersEditor } from './HeadersEditor';
|
||||||
import { UrlBar } from './UrlBar';
|
import { UrlBar } from './UrlBar';
|
||||||
import { UrlParametersEditor } from './UrlParameterEditor';
|
import { UrlParametersEditor } from './UrlParameterEditor';
|
||||||
import { useCurlToRequest } from '../hooks/useCurlToRequest';
|
import { useImportCurl } from '../hooks/useImportCurl';
|
||||||
import { useToast } from './ToastContext';
|
|
||||||
import { Icon } from './core/Icon';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
style: CSSProperties;
|
style: CSSProperties;
|
||||||
@@ -230,11 +228,9 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
[updateRequest],
|
[updateRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
const importCurl = useCurlToRequest();
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const isLoading = useIsResponseLoading(activeRequestId ?? null);
|
const isLoading = useIsResponseLoading(activeRequestId ?? null);
|
||||||
const { updateKey } = useRequestUpdateKey(activeRequestId ?? null);
|
const { updateKey } = useRequestUpdateKey(activeRequestId ?? null);
|
||||||
|
const importCurl = useImportCurl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -244,6 +240,7 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
{activeRequest && (
|
{activeRequest && (
|
||||||
<>
|
<>
|
||||||
<UrlBar
|
<UrlBar
|
||||||
|
key={forceUpdateKey}
|
||||||
url={activeRequest.url}
|
url={activeRequest.url}
|
||||||
method={activeRequest.method}
|
method={activeRequest.method}
|
||||||
placeholder="https://example.com"
|
placeholder="https://example.com"
|
||||||
@@ -252,14 +249,6 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
importCurl.mutate({ requestId: activeRequestId, command });
|
importCurl.mutate({ requestId: activeRequestId, command });
|
||||||
toast.show({
|
|
||||||
render: () => [
|
|
||||||
<>
|
|
||||||
<Icon icon="info" />
|
|
||||||
<span>Curl command imported</span>
|
|
||||||
</>,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onSend={handleSend}
|
onSend={handleSend}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
@@ -326,17 +315,6 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
contentType="text/xml"
|
contentType="text/xml"
|
||||||
onChange={handleBodyTextChange}
|
onChange={handleBodyTextChange}
|
||||||
/>
|
/>
|
||||||
) : activeRequest.bodyType === BODY_TYPE_OTHER ? (
|
|
||||||
<Editor
|
|
||||||
forceUpdateKey={forceUpdateKey}
|
|
||||||
useTemplating
|
|
||||||
autocompleteVariables
|
|
||||||
placeholder="..."
|
|
||||||
className="!bg-gray-50"
|
|
||||||
heightMode={fullHeight ? 'full' : 'auto'}
|
|
||||||
defaultValue={`${activeRequest.body?.text ?? ''}`}
|
|
||||||
onChange={handleBodyTextChange}
|
|
||||||
/>
|
|
||||||
) : activeRequest.bodyType === BODY_TYPE_GRAPHQL ? (
|
) : activeRequest.bodyType === BODY_TYPE_GRAPHQL ? (
|
||||||
<GraphQLEditor
|
<GraphQLEditor
|
||||||
forceUpdateKey={forceUpdateKey}
|
forceUpdateKey={forceUpdateKey}
|
||||||
@@ -365,6 +343,17 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
onChange={handleBinaryFileChange}
|
onChange={handleBinaryFileChange}
|
||||||
onChangeContentType={handleContentTypeChange}
|
onChangeContentType={handleContentTypeChange}
|
||||||
/>
|
/>
|
||||||
|
) : typeof activeRequest.bodyType === 'string' ? (
|
||||||
|
<Editor
|
||||||
|
forceUpdateKey={forceUpdateKey}
|
||||||
|
useTemplating
|
||||||
|
autocompleteVariables
|
||||||
|
placeholder="..."
|
||||||
|
className="!bg-gray-50"
|
||||||
|
heightMode={fullHeight ? 'full' : 'auto'}
|
||||||
|
defaultValue={`${activeRequest.body?.text ?? ''}`}
|
||||||
|
onChange={handleBodyTextChange}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<EmptyStateText>Empty Body</EmptyStateText>
|
<EmptyStateText>Empty Body</EmptyStateText>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ const SidebarItem = forwardRef(function SidebarItem(
|
|||||||
const deleteRequest = useDeleteRequest(activeRequest ?? null);
|
const deleteRequest = useDeleteRequest(activeRequest ?? null);
|
||||||
const duplicateHttpRequest = useDuplicateHttpRequest({ id: itemId, navigateAfter: true });
|
const duplicateHttpRequest = useDuplicateHttpRequest({ id: itemId, navigateAfter: true });
|
||||||
const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: itemId, navigateAfter: true });
|
const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: itemId, navigateAfter: true });
|
||||||
const [copyAsCurl] = useCopyAsCurl(itemId);
|
const [, copyAsCurl] = useCopyAsCurl(itemId);
|
||||||
const sendRequest = useSendRequest(itemId);
|
const sendRequest = useSendRequest(itemId);
|
||||||
const sendManyRequests = useSendManyRequests();
|
const sendManyRequests = useSendManyRequests();
|
||||||
const latestHttpResponse = useLatestHttpResponse(itemId);
|
const latestHttpResponse = useLatestHttpResponse(itemId);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
import React, { createContext, useContext, useMemo, useRef, useState } from 'react';
|
import React, { createContext, useContext, useMemo, useRef, useState } from 'react';
|
||||||
import type { ToastProps } from './core/Toast';
|
import type { ToastProps } from './core/Toast';
|
||||||
import { Toast } from './core/Toast';
|
import { Toast } from './core/Toast';
|
||||||
@@ -6,7 +7,7 @@ import { Portal } from './Portal';
|
|||||||
import { AnimatePresence } from 'framer-motion';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
type ToastEntry = {
|
type ToastEntry = {
|
||||||
render: ({ hide }: { hide: () => void }) => React.ReactNode;
|
message: ReactNode;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
} & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>;
|
} & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>;
|
||||||
|
|
||||||
@@ -66,12 +67,11 @@ export const ToastProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function ToastInstance({ id, render, timeout, ...props }: PrivateToastEntry) {
|
function ToastInstance({ id, message, timeout, ...props }: PrivateToastEntry) {
|
||||||
const { actions } = useContext(ToastContext);
|
const { actions } = useContext(ToastContext);
|
||||||
const children = render({ hide: () => actions.hide(id) });
|
|
||||||
return (
|
return (
|
||||||
<Toast open timeout={timeout} onClose={() => actions.hide(id)} {...props}>
|
<Toast open timeout={timeout} onClose={() => actions.hide(id)} {...props}>
|
||||||
{children}
|
{message}
|
||||||
</Toast>
|
</Toast>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { EditorView } from 'codemirror';
|
import type { EditorView } from 'codemirror';
|
||||||
import type { FormEvent } from 'react';
|
import type { FormEvent, ReactNode } from 'react';
|
||||||
import { memo, useRef, useState } from 'react';
|
import { memo, useRef, useState } from 'react';
|
||||||
import { useHotKey } from '../hooks/useHotKey';
|
import { useHotKey } from '../hooks/useHotKey';
|
||||||
import type { HttpRequest } from '../lib/models';
|
import type { HttpRequest } from '../lib/models';
|
||||||
@@ -20,6 +20,7 @@ type Props = Pick<HttpRequest, 'url'> & {
|
|||||||
onMethodChange?: (method: string) => void;
|
onMethodChange?: (method: string) => void;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
forceUpdateKey: string;
|
forceUpdateKey: string;
|
||||||
|
rightSlot?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UrlBar = memo(function UrlBar({
|
export const UrlBar = memo(function UrlBar({
|
||||||
@@ -34,6 +35,7 @@ export const UrlBar = memo(function UrlBar({
|
|||||||
onMethodChange,
|
onMethodChange,
|
||||||
onPaste,
|
onPaste,
|
||||||
submitIcon = 'sendHorizontal',
|
submitIcon = 'sendHorizontal',
|
||||||
|
rightSlot,
|
||||||
isLoading,
|
isLoading,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const inputRef = useRef<EditorView>(null);
|
const inputRef = useRef<EditorView>(null);
|
||||||
@@ -84,17 +86,20 @@ export const UrlBar = memo(function UrlBar({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
rightSlot={
|
rightSlot={
|
||||||
submitIcon !== null && (
|
<>
|
||||||
<IconButton
|
{rightSlot}
|
||||||
size="xs"
|
{submitIcon !== null && (
|
||||||
iconSize="md"
|
<IconButton
|
||||||
title="Send Request"
|
size="xs"
|
||||||
type="submit"
|
iconSize="md"
|
||||||
className="w-8 my-0.5 mr-0.5"
|
title="Send Request"
|
||||||
icon={isLoading ? 'x' : submitIcon}
|
type="submit"
|
||||||
hotkeyAction="http_request.send"
|
className="w-8 my-0.5 mr-0.5"
|
||||||
/>
|
icon={isLoading ? 'x' : submitIcon}
|
||||||
)
|
hotkeyAction="http_request.send"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
MouseEvent as ReactMouseEvent,
|
MouseEvent as ReactMouseEvent,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import { useWindowSize } from 'react-use';
|
import { useWindowSize } from 'react-use';
|
||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
@@ -33,8 +33,6 @@ import { ResizeHandle } from './ResizeHandle';
|
|||||||
import { Sidebar } from './Sidebar';
|
import { Sidebar } from './Sidebar';
|
||||||
import { SidebarActions } from './SidebarActions';
|
import { SidebarActions } from './SidebarActions';
|
||||||
import { WorkspaceHeader } from './WorkspaceHeader';
|
import { WorkspaceHeader } from './WorkspaceHeader';
|
||||||
import { useClipboardText } from '../hooks/useClipboardText';
|
|
||||||
import { useToast } from './ToastContext';
|
|
||||||
|
|
||||||
const side = { gridArea: 'side' };
|
const side = { gridArea: 'side' };
|
||||||
const head = { gridArea: 'head' };
|
const head = { gridArea: 'head' };
|
||||||
@@ -56,24 +54,6 @@ export default function Workspace() {
|
|||||||
const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void } | null>(
|
const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void } | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
const clipboardText = useClipboardText();
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const isCurlInClipboard = clipboardText?.startsWith('curl ');
|
|
||||||
if (!isCurlInClipboard) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.show({
|
|
||||||
render: () => (
|
|
||||||
<div>
|
|
||||||
<p>Curl command detected?</p>
|
|
||||||
<Button color="primary">Import</Button>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}, [clipboardText, toast]);
|
|
||||||
|
|
||||||
const unsub = () => {
|
const unsub = () => {
|
||||||
if (moveState.current !== null) {
|
if (moveState.current !== null) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { RecentRequestsDropdown } from './RecentRequestsDropdown';
|
|||||||
import { SettingsDropdown } from './SettingsDropdown';
|
import { SettingsDropdown } from './SettingsDropdown';
|
||||||
import { SidebarActions } from './SidebarActions';
|
import { SidebarActions } from './SidebarActions';
|
||||||
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
|
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
|
||||||
|
import { ImportCurlButton } from './ImportCurlButton';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -19,6 +20,7 @@ interface Props {
|
|||||||
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
||||||
const osInfo = useOsInfo();
|
const osInfo = useOsInfo();
|
||||||
const [maximized, setMaximized] = useState<boolean>(false);
|
const [maximized, setMaximized] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
space={2}
|
space={2}
|
||||||
@@ -39,6 +41,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
<RecentRequestsDropdown />
|
<RecentRequestsDropdown />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex items-center h-full justify-end pointer-events-none">
|
<div className="flex-1 flex items-center h-full justify-end pointer-events-none">
|
||||||
|
<ImportCurlButton />
|
||||||
<SettingsDropdown />
|
<SettingsDropdown />
|
||||||
{(osInfo?.osType === 'linux' || osInfo?.osType === 'windows') && (
|
{(osInfo?.osType === 'linux' || osInfo?.osType === 'windows') && (
|
||||||
<HStack className="ml-4" alignItems="center">
|
<HStack className="ml-4" alignItems="center">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type ButtonProps = Omit<HTMLAttributes<HTMLButtonElement>, 'color'> & {
|
|||||||
color?: 'custom' | 'default' | 'gray' | 'primary' | 'secondary' | 'warning' | 'danger';
|
color?: 'custom' | 'default' | 'gray' | 'primary' | 'secondary' | 'warning' | 'danger';
|
||||||
variant?: 'border' | 'solid';
|
variant?: 'border' | 'solid';
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
size?: 'sm' | 'md' | 'xs';
|
size?: 'xs' | 'sm' | 'md';
|
||||||
justify?: 'start' | 'center';
|
justify?: 'start' | 'center';
|
||||||
type?: 'button' | 'submit';
|
type?: 'button' | 'submit';
|
||||||
forDropdown?: boolean;
|
forDropdown?: boolean;
|
||||||
|
|||||||
@@ -312,8 +312,8 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
|||||||
handleClose();
|
handleClose();
|
||||||
}
|
}
|
||||||
setSelectedIndex(null);
|
setSelectedIndex(null);
|
||||||
if (i.type !== 'separator') {
|
if (i.type !== 'separator' && typeof i.onSelect === 'function') {
|
||||||
i.onSelect?.();
|
i.onSelect();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleClose],
|
[handleClose],
|
||||||
@@ -506,7 +506,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
ref={initRef}
|
ref={initRef}
|
||||||
size="xs"
|
size="sm"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onMouseEnter={(e) => e.currentTarget.focus()}
|
onMouseEnter={(e) => e.currentTarget.focus()}
|
||||||
onMouseLeave={(e) => e.currentTarget.blur()}
|
onMouseLeave={(e) => e.currentTarget.blur()}
|
||||||
@@ -518,6 +518,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
|
|||||||
rightSlot={rightSlot && <div className="ml-auto pl-3">{rightSlot}</div>}
|
rightSlot={rightSlot && <div className="ml-auto pl-3">{rightSlot}</div>}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
|
'h-xs', // More compact
|
||||||
'min-w-[8rem] outline-none px-2 mx-1.5 flex text-sm text-gray-700 whitespace-nowrap',
|
'min-w-[8rem] outline-none px-2 mx-1.5 flex text-sm text-gray-700 whitespace-nowrap',
|
||||||
'focus:bg-highlight focus:text-gray-800 rounded',
|
'focus:bg-highlight focus:text-gray-800 rounded',
|
||||||
item.variant === 'danger' && 'text-red-600',
|
item.variant === 'danger' && 'text-red-600',
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const icons = {
|
|||||||
cake: lucide.CakeIcon,
|
cake: lucide.CakeIcon,
|
||||||
chat: lucide.MessageSquare,
|
chat: lucide.MessageSquare,
|
||||||
check: lucide.CheckIcon,
|
check: lucide.CheckIcon,
|
||||||
|
checkCircle: lucide.CheckCircleIcon,
|
||||||
chevronDown: lucide.ChevronDownIcon,
|
chevronDown: lucide.ChevronDownIcon,
|
||||||
chevronRight: lucide.ChevronRightIcon,
|
chevronRight: lucide.ChevronRightIcon,
|
||||||
code: lucide.CodeIcon,
|
code: lucide.CodeIcon,
|
||||||
@@ -41,6 +42,7 @@ const icons = {
|
|||||||
magicWand: lucide.Wand2Icon,
|
magicWand: lucide.Wand2Icon,
|
||||||
minus: lucide.MinusIcon,
|
minus: lucide.MinusIcon,
|
||||||
moreVertical: lucide.MoreVerticalIcon,
|
moreVertical: lucide.MoreVerticalIcon,
|
||||||
|
paste: lucide.ClipboardPasteIcon,
|
||||||
pencil: lucide.PencilIcon,
|
pencil: lucide.PencilIcon,
|
||||||
plug: lucide.Plug,
|
plug: lucide.Plug,
|
||||||
plus: lucide.PlusIcon,
|
plus: lucide.PlusIcon,
|
||||||
|
|||||||
@@ -392,11 +392,15 @@ function PairEditorRow({
|
|||||||
className="font-mono text-xs"
|
className="font-mono text-xs"
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const file = await open({
|
const selected = await open({
|
||||||
title: 'Select file',
|
title: 'Select file',
|
||||||
multiple: false,
|
multiple: false,
|
||||||
});
|
});
|
||||||
handleChangeValueFile((Array.isArray(file) ? file[0] : file) ?? '');
|
if (selected == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChangeValueFile(selected.path);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{getFileName(pairContainer.pair.value) || 'Select File'}
|
{getFileName(pairContainer.pair.value) || 'Select File'}
|
||||||
@@ -491,7 +495,8 @@ const newPairContainer = (initialPair?: Pair): PairContainer => {
|
|||||||
return { id, pair };
|
return { id, pair };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFileName = (path: string): string => {
|
const getFileName = (path: string | null | undefined): string => {
|
||||||
const parts = path.split(/[\\/]/);
|
console.log('PATH', path);
|
||||||
|
const parts = String(path).split(/[\\/]/);
|
||||||
return parts[parts.length - 1] ?? '';
|
return parts[parts.length - 1] ?? '';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,23 +1,37 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useMemo } from 'react';
|
import React from 'react';
|
||||||
import { useKey } from 'react-use';
|
import { useKey } from 'react-use';
|
||||||
import { Heading } from './Heading';
|
|
||||||
import { IconButton } from './IconButton';
|
import { IconButton } from './IconButton';
|
||||||
|
import type { IconProps } from './Icon';
|
||||||
|
import { Icon } from './Icon';
|
||||||
|
|
||||||
export interface ToastProps {
|
export interface ToastProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
title?: ReactNode;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
|
variant?: 'copied' | 'success' | 'info' | 'warning' | 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Toast({ children, className, open, onClose, title, timeout }: ToastProps) {
|
const ICONS: Record<NonNullable<ToastProps['variant']>, IconProps['icon']> = {
|
||||||
const titleId = useMemo(() => Math.random().toString(36).slice(2), []);
|
copied: 'copyCheck',
|
||||||
|
warning: 'alert',
|
||||||
|
error: 'alert',
|
||||||
|
info: 'info',
|
||||||
|
success: 'checkCircle',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Toast({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
timeout,
|
||||||
|
variant = 'info',
|
||||||
|
}: ToastProps) {
|
||||||
useKey(
|
useKey(
|
||||||
'Escape',
|
'Escape',
|
||||||
() => {
|
() => {
|
||||||
@@ -46,13 +60,16 @@ export function Toast({ children, className, open, onClose, title, timeout }: To
|
|||||||
'text-gray-700',
|
'text-gray-700',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="px-3 py-2">
|
<div className="px-3 py-2 flex items-center gap-2">
|
||||||
{title && (
|
<Icon
|
||||||
<Heading size={3} id={titleId}>
|
icon={ICONS[variant]}
|
||||||
{title}
|
className={classNames(
|
||||||
</Heading>
|
variant === 'success' && 'text-green-500',
|
||||||
)}
|
variant === 'warning' && 'text-orange-500',
|
||||||
|
variant === 'error' && 'text-red-500',
|
||||||
|
variant === 'copied' && 'text-violet-500',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className="flex items-center gap-2">{children}</div>
|
<div className="flex items-center gap-2">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { readText, writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||||
import { readText } from '@tauri-apps/plugin-clipboard-manager';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useWindowFocus } from './useWindowFocus';
|
||||||
|
|
||||||
export function useClipboardText() {
|
export function useClipboardText() {
|
||||||
return useQuery({
|
const [value, setValue] = useState<string>('');
|
||||||
queryKey: [],
|
const focused = useWindowFocus();
|
||||||
queryFn: () => readText(),
|
|
||||||
}).data;
|
useEffect(() => {
|
||||||
|
readText().then(setValue);
|
||||||
|
}, [focused]);
|
||||||
|
|
||||||
|
const setText = useCallback((text: string) => {
|
||||||
|
writeText(text).catch(console.error);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [value, setText] as const;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
|
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||||
import React, { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useToast } from '../components/ToastContext';
|
import { useToast } from '../components/ToastContext';
|
||||||
import { Icon } from '../components/core/Icon';
|
|
||||||
|
|
||||||
export function useCopyAsCurl(requestId: string) {
|
export function useCopyAsCurl(requestId: string) {
|
||||||
const [checked, setChecked] = useState<boolean>(false);
|
const [checked, setChecked] = useState<boolean>(false);
|
||||||
@@ -15,12 +14,8 @@ export function useCopyAsCurl(requestId: string) {
|
|||||||
setChecked(true);
|
setChecked(true);
|
||||||
setTimeout(() => setChecked(false), 800);
|
setTimeout(() => setChecked(false), 800);
|
||||||
toast.show({
|
toast.show({
|
||||||
render: () => [
|
variant: 'copied',
|
||||||
<>
|
message: 'Curl copied to clipboard',
|
||||||
<Icon icon="copyCheck" />
|
|
||||||
<span>Command copied to clipboard</span>
|
|
||||||
</>,
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,22 +3,34 @@ import { useMutation } from '@tanstack/react-query';
|
|||||||
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
import { useActiveWorkspaceId } from './useActiveWorkspaceId';
|
||||||
import { useRequestUpdateKey } from './useRequestUpdateKey';
|
import { useRequestUpdateKey } from './useRequestUpdateKey';
|
||||||
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
|
import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest';
|
||||||
|
import { useToast } from '../components/ToastContext';
|
||||||
|
import { useCreateHttpRequest } from './useCreateHttpRequest';
|
||||||
|
|
||||||
export function useCurlToRequest() {
|
export function useImportCurl() {
|
||||||
const workspaceId = useActiveWorkspaceId();
|
const workspaceId = useActiveWorkspaceId();
|
||||||
const updateRequest = useUpdateAnyHttpRequest();
|
const updateRequest = useUpdateAnyHttpRequest();
|
||||||
|
const createRequest = useCreateHttpRequest();
|
||||||
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
const { wasUpdatedExternally } = useRequestUpdateKey(null);
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ requestId, command }: { requestId: string; command: string }) => {
|
mutationFn: async ({ requestId, command }: { requestId: string | null; command: string }) => {
|
||||||
const request: Record<string, unknown> = await invoke('cmd_curl_to_request', {
|
const request: Record<string, unknown> = await invoke('cmd_curl_to_request', {
|
||||||
command,
|
command,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
delete request.id;
|
delete request.id;
|
||||||
await updateRequest.mutateAsync({ id: requestId, update: request });
|
|
||||||
wasUpdatedExternally(requestId);
|
const id = requestId ?? (await createRequest.mutateAsync({})).id;
|
||||||
console.log('FOO', request);
|
await updateRequest.mutateAsync({ id, update: request });
|
||||||
|
|
||||||
|
const verb = requestId ? 'updated' : 'created';
|
||||||
|
toast.show({
|
||||||
|
variant: 'success',
|
||||||
|
message: `Request ${verb} from Curl`,
|
||||||
|
});
|
||||||
|
|
||||||
|
wasUpdatedExternally(id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user