diff --git a/plugins/importer-curl/src/index.ts b/plugins/importer-curl/src/index.ts index 021d25a3..9b25c0fe 100644 --- a/plugins/importer-curl/src/index.ts +++ b/plugins/importer-curl/src/index.ts @@ -137,8 +137,6 @@ export function importCommand(parseEntries: ParseEntry[], workspaceId: string) { // Start at 1 so we can skip the ^curl part for (let i = 1; i < parseEntries.length; 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') { parseEntry = parseEntry.trim(); } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 508d5ebb..c47a62dd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1051,16 +1051,6 @@ dependencies = [ "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]] name = "cookie_store" version = "0.16.2" @@ -1830,26 +1820,11 @@ dependencies = [ "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]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1857,15 +1832,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1885,9 +1860,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -1904,9 +1879,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -1915,23 +1890,22 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -2226,11 +2200,9 @@ dependencies = [ "hyper 0.14.27", "hyper-rustls 0.24.2", "log", - "once_cell", "prost", "prost-reflect", "prost-types", - "protoc-bin-vendored", "serde", "serde_json", "tauri", @@ -4321,56 +4293,6 @@ dependencies = [ "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]] name = "psl-types" version = "2.0.11" @@ -7251,18 +7173,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "window-vibrancy" version = "0.5.0" @@ -7731,9 +7641,7 @@ dependencies = [ "boa_runtime", "chrono", "cocoa", - "cookie 0.18.0", "datetime", - "futures", "grpc", "http 0.2.10", "log", @@ -7759,7 +7667,6 @@ dependencies = [ "tokio", "tokio-stream", "uuid", - "window-shadows", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 33c72a27..c1eff932 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -3,10 +3,6 @@ workspace = { members = ["grpc"] } [package] name = "yaak-app" 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" # Produce a library for mobile support @@ -32,13 +28,11 @@ base64 = "0.22.0" boa_engine = { version = "0.18.0", features = ["annex-b"] } boa_runtime = { version = "0.18.0" } chrono = { version = "0.4.31", features = ["serde"] } -futures = "0.3.26" -http = "0.2.8" +http = "0.2.10" rand = "0.8.5" reqwest = { version = "0.11.23", features = ["multipart", "cookies", "gzip", "brotli", "deflate"] } -cookie = { version = "0.18.0" } -serde = { version = "1.0.195", features = ["derive"] } -serde_json = { version = "1.0.111", features = ["raw_value"] } +serde = { version = "1.0.198", features = ["derive"] } +serde_json = { version = "1.0.116", features = ["raw_value"] } sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio-rustls", "json", "chrono", "time"] } tauri = { version = "2.0.0-beta.17", features = [ "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-fs = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tokio = { version = "1.36.0", features = ["sync"] } -uuid = "1.3.0" -log = "0.4.20" +uuid = "1.7.0" +log = "0.4.21" datetime = "0.5.2" -window-shadows = "0.2.2" reqwest_cookie_store = "0.6.0" grpc = { path = "./grpc" } tokio-stream = "0.1.15" diff --git a/src-tauri/grpc/Cargo.toml b/src-tauri/grpc/Cargo.toml index 8ba233fa..84bed58b 100644 --- a/src-tauri/grpc/Cargo.toml +++ b/src-tauri/grpc/Cargo.toml @@ -14,11 +14,9 @@ serde = { version = "1.0.196", features = ["derive"] } serde_json = "1.0.113" prost-reflect = { version = "0.12.0", features = ["serde", "derive"] } log = "0.4.20" -once_cell = { version = "1.19.0", features = [] } anyhow = "1.0.79" hyper = { version = "0.14" } hyper-rustls = { version = "0.24.0", features = ["http2"] } -protoc-bin-vendored = "3.0.0" uuid = { version = "1.7.0", features = ["v4"] } tauri = { version = "2.0.0-beta.16" } tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } diff --git a/src-web/components/BinaryFileEditor.tsx b/src-web/components/BinaryFileEditor.tsx index e1f94df0..f54a3dea 100644 --- a/src-web/components/BinaryFileEditor.tsx +++ b/src-web/components/BinaryFileEditor.tsx @@ -30,15 +30,14 @@ export function BinaryFileEditor({ const handleClick = async () => { await ignoreContentType.set(false); - const path = await open({ + const selected = await open({ title: 'Select File', multiple: false, }); - if (path) { - onChange({ filePath: path }); - } else { - onChange({ filePath: undefined }); + if (selected == null) { + return; } + onChange({ filePath: selected.path }); }; const filePath = typeof body.filePath === 'string' ? body.filePath : undefined; diff --git a/src-web/components/GrpcProtoSelection.tsx b/src-web/components/GrpcProtoSelection.tsx index f7ba9dc1..846a8782 100644 --- a/src-web/components/GrpcProtoSelection.tsx +++ b/src-web/components/GrpcProtoSelection.tsx @@ -42,15 +42,15 @@ export function GrpcProtoSelection({ requestId }: Props) { color="primary" size="sm" onClick={async () => { - const files = await open({ + const selected = await open({ title: 'Select Proto Files', multiple: true, filters: [{ name: 'Proto Files', extensions: ['proto'] }], }); - if (files == null) { + if (selected == null) { 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 grpc.reflect.refetch(); }} diff --git a/src-web/components/ImportCurlButton.tsx b/src-web/components/ImportCurlButton.tsx new file mode 100644 index 00000000..19c77b5f --- /dev/null +++ b/src-web/components/ImportCurlButton.tsx @@ -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(''); + const importCurl = useImportCurl(); + + if (!clipboardText?.trim().startsWith('curl ') || lastImportedCmd === clipboardText) { + return null; + } + + return ( + + + + ); +} diff --git a/src-web/components/RequestPane.tsx b/src-web/components/RequestPane.tsx index 8654b0eb..6efd6eb3 100644 --- a/src-web/components/RequestPane.tsx +++ b/src-web/components/RequestPane.tsx @@ -38,9 +38,7 @@ import { GraphQLEditor } from './GraphQLEditor'; import { HeadersEditor } from './HeadersEditor'; import { UrlBar } from './UrlBar'; import { UrlParametersEditor } from './UrlParameterEditor'; -import { useCurlToRequest } from '../hooks/useCurlToRequest'; -import { useToast } from './ToastContext'; -import { Icon } from './core/Icon'; +import { useImportCurl } from '../hooks/useImportCurl'; interface Props { style: CSSProperties; @@ -230,11 +228,9 @@ export const RequestPane = memo(function RequestPane({ [updateRequest], ); - const importCurl = useCurlToRequest(); - const toast = useToast(); - const isLoading = useIsResponseLoading(activeRequestId ?? null); const { updateKey } = useRequestUpdateKey(activeRequestId ?? null); + const importCurl = useImportCurl(); return (
[ - <> - - Curl command imported - , - ], - }); }} onSend={handleSend} onCancel={handleCancel} @@ -326,17 +315,6 @@ export const RequestPane = memo(function RequestPane({ contentType="text/xml" onChange={handleBodyTextChange} /> - ) : activeRequest.bodyType === BODY_TYPE_OTHER ? ( - ) : activeRequest.bodyType === BODY_TYPE_GRAPHQL ? ( + ) : typeof activeRequest.bodyType === 'string' ? ( + ) : ( Empty Body )} diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index 6e4a8256..d586aed9 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -608,7 +608,7 @@ const SidebarItem = forwardRef(function SidebarItem( const deleteRequest = useDeleteRequest(activeRequest ?? null); const duplicateHttpRequest = useDuplicateHttpRequest({ id: itemId, navigateAfter: true }); const duplicateGrpcRequest = useDuplicateGrpcRequest({ id: itemId, navigateAfter: true }); - const [copyAsCurl] = useCopyAsCurl(itemId); + const [, copyAsCurl] = useCopyAsCurl(itemId); const sendRequest = useSendRequest(itemId); const sendManyRequests = useSendManyRequests(); const latestHttpResponse = useLatestHttpResponse(itemId); diff --git a/src-web/components/ToastContext.tsx b/src-web/components/ToastContext.tsx index 9a70e79c..37b034ec 100644 --- a/src-web/components/ToastContext.tsx +++ b/src-web/components/ToastContext.tsx @@ -1,3 +1,4 @@ +import type { ReactNode } from 'react'; import React, { createContext, useContext, useMemo, useRef, useState } from 'react'; import type { ToastProps } from './core/Toast'; import { Toast } from './core/Toast'; @@ -6,7 +7,7 @@ import { Portal } from './Portal'; import { AnimatePresence } from 'framer-motion'; type ToastEntry = { - render: ({ hide }: { hide: () => void }) => React.ReactNode; + message: ReactNode; timeout?: number; } & Omit; @@ -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 children = render({ hide: () => actions.hide(id) }); return ( actions.hide(id)} {...props}> - {children} + {message} ); } diff --git a/src-web/components/UrlBar.tsx b/src-web/components/UrlBar.tsx index a5323bde..e765d462 100644 --- a/src-web/components/UrlBar.tsx +++ b/src-web/components/UrlBar.tsx @@ -1,5 +1,5 @@ import type { EditorView } from 'codemirror'; -import type { FormEvent } from 'react'; +import type { FormEvent, ReactNode } from 'react'; import { memo, useRef, useState } from 'react'; import { useHotKey } from '../hooks/useHotKey'; import type { HttpRequest } from '../lib/models'; @@ -20,6 +20,7 @@ type Props = Pick & { onMethodChange?: (method: string) => void; isLoading: boolean; forceUpdateKey: string; + rightSlot?: ReactNode; }; export const UrlBar = memo(function UrlBar({ @@ -34,6 +35,7 @@ export const UrlBar = memo(function UrlBar({ onMethodChange, onPaste, submitIcon = 'sendHorizontal', + rightSlot, isLoading, }: Props) { const inputRef = useRef(null); @@ -84,17 +86,20 @@ export const UrlBar = memo(function UrlBar({ ) } rightSlot={ - submitIcon !== null && ( - - ) + <> + {rightSlot} + {submitIcon !== null && ( + + )} + } /> diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index 41d67611..7780bf84 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -6,7 +6,7 @@ import type { MouseEvent as ReactMouseEvent, ReactNode, } from 'react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useMemo, useRef, useState } from 'react'; import { useWindowSize } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; @@ -33,8 +33,6 @@ import { ResizeHandle } from './ResizeHandle'; import { Sidebar } from './Sidebar'; import { SidebarActions } from './SidebarActions'; import { WorkspaceHeader } from './WorkspaceHeader'; -import { useClipboardText } from '../hooks/useClipboardText'; -import { useToast } from './ToastContext'; const side = { gridArea: 'side' }; const head = { gridArea: 'head' }; @@ -56,24 +54,6 @@ export default function Workspace() { const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void } | null>( null, ); - const clipboardText = useClipboardText(); - const toast = useToast(); - - useEffect(() => { - const isCurlInClipboard = clipboardText?.startsWith('curl '); - if (!isCurlInClipboard) { - return; - } - - toast.show({ - render: () => ( -
-

Curl command detected?

- -
- ), - }); - }, [clipboardText, toast]); const unsub = () => { if (moveState.current !== null) { diff --git a/src-web/components/WorkspaceHeader.tsx b/src-web/components/WorkspaceHeader.tsx index f1e085eb..c19b9513 100644 --- a/src-web/components/WorkspaceHeader.tsx +++ b/src-web/components/WorkspaceHeader.tsx @@ -11,6 +11,7 @@ import { RecentRequestsDropdown } from './RecentRequestsDropdown'; import { SettingsDropdown } from './SettingsDropdown'; import { SidebarActions } from './SidebarActions'; import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown'; +import { ImportCurlButton } from './ImportCurlButton'; interface Props { className?: string; @@ -19,6 +20,7 @@ interface Props { export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) { const osInfo = useOsInfo(); const [maximized, setMaximized] = useState(false); + return (
+ {(osInfo?.osType === 'linux' || osInfo?.osType === 'windows') && ( diff --git a/src-web/components/core/Button.tsx b/src-web/components/core/Button.tsx index 7b2b6111..376df5ac 100644 --- a/src-web/components/core/Button.tsx +++ b/src-web/components/core/Button.tsx @@ -10,7 +10,7 @@ export type ButtonProps = Omit, 'color'> & { color?: 'custom' | 'default' | 'gray' | 'primary' | 'secondary' | 'warning' | 'danger'; variant?: 'border' | 'solid'; isLoading?: boolean; - size?: 'sm' | 'md' | 'xs'; + size?: 'xs' | 'sm' | 'md'; justify?: 'start' | 'center'; type?: 'button' | 'submit'; forDropdown?: boolean; diff --git a/src-web/components/core/Dropdown.tsx b/src-web/components/core/Dropdown.tsx index 9e7d200d..0d0f41a6 100644 --- a/src-web/components/core/Dropdown.tsx +++ b/src-web/components/core/Dropdown.tsx @@ -312,8 +312,8 @@ const Menu = forwardRef, MenuPro handleClose(); } setSelectedIndex(null); - if (i.type !== 'separator') { - i.onSelect?.(); + if (i.type !== 'separator' && typeof i.onSelect === 'function') { + i.onSelect(); } }, [handleClose], @@ -506,7 +506,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men return (