mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-23 00:58:32 +02:00
Hot keys and cleanup
This commit is contained in:
21
.github/workflows/artifacts.yml
vendored
21
.github/workflows/artifacts.yml
vendored
@@ -5,19 +5,22 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-artifacts:
|
build-artifacts:
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
# platform: [ ubuntu-latest, macos-latest, windows-latest ]
|
include:
|
||||||
platform: [ macos-latest ]
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: 'aarch64-apple-darwin,x86_64-apple-darwin'
|
targets: ${{ matrix.target }}
|
||||||
- name: Cache Rust
|
- name: Cache Rust
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
@@ -31,7 +34,7 @@ jobs:
|
|||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
- name: install dependencies (ubuntu only)
|
- name: install dependencies (ubuntu only)
|
||||||
if: matrix.platform == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
@@ -55,7 +58,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tagName: 'v__VERSION__'
|
tagName: 'v__VERSION__'
|
||||||
releaseName: 'Release __VERSION__'
|
releaseName: 'Release __VERSION__'
|
||||||
releaseBody: 'See the assets to download this version and install.'
|
releaseBody: '<!-- Release Notes -->'
|
||||||
releaseDraft: false
|
releaseDraft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
args: '--target universal-apple-darwin'
|
args: '--target ${{ matrix.target }}'
|
||||||
|
|||||||
@@ -79,11 +79,8 @@ async fn actually_send_ephemeral_request(
|
|||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
let mut url_string = request.url.to_string();
|
let mut url_string = request.url.to_string();
|
||||||
|
|
||||||
let mut variables = HashMap::new();
|
let variables: HashMap<&str, &str> = HashMap::new();
|
||||||
variables.insert("PROJECT_ID", "project_123");
|
// variables.insert("", "");
|
||||||
variables.insert("TOKEN", "s3cret");
|
|
||||||
variables.insert("DOMAIN", "schier.co");
|
|
||||||
variables.insert("BASE_URL", "https://schier.co");
|
|
||||||
|
|
||||||
let re = Regex::new(r"\$\{\[\s*([^]\s]+)\s*]}").expect("Failed to create regex");
|
let re = Regex::new(r"\$\{\[\s*([^]\s]+)\s*]}").expect("Failed to create regex");
|
||||||
url_string = re
|
url_string = re
|
||||||
@@ -608,6 +605,14 @@ fn create_window(handle: &AppHandle<Wry>) -> Window<Wry> {
|
|||||||
.add_item(
|
.add_item(
|
||||||
CustomMenuItem::new("focus_url".to_string(), "Focus URL").accelerator("CmdOrCtrl+l"),
|
CustomMenuItem::new("focus_url".to_string(), "Focus URL").accelerator("CmdOrCtrl+l"),
|
||||||
)
|
)
|
||||||
|
.add_item(
|
||||||
|
CustomMenuItem::new("new_request".to_string(), "New Request")
|
||||||
|
.accelerator("CmdOrCtrl+n"),
|
||||||
|
)
|
||||||
|
.add_item(
|
||||||
|
CustomMenuItem::new("duplicate_request".to_string(), "Duplicate Request")
|
||||||
|
.accelerator("CmdOrCtrl+d"),
|
||||||
|
)
|
||||||
.add_item(CustomMenuItem::new("new_window".to_string(), "New Window"));
|
.add_item(CustomMenuItem::new("new_window".to_string(), "New Window"));
|
||||||
if is_dev() {
|
if is_dev() {
|
||||||
test_menu = test_menu
|
test_menu = test_menu
|
||||||
@@ -652,6 +657,8 @@ fn create_window(handle: &AppHandle<Wry>) -> Window<Wry> {
|
|||||||
"toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(),
|
"toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(),
|
||||||
"focus_url" => win2.emit("focus_url", true).unwrap(),
|
"focus_url" => win2.emit("focus_url", true).unwrap(),
|
||||||
"send_request" => win2.emit("send_request", true).unwrap(),
|
"send_request" => win2.emit("send_request", true).unwrap(),
|
||||||
|
"new_request" => _ = win2.emit("new_request", true).unwrap(),
|
||||||
|
"duplicate_request" => _ = win2.emit("duplicate_request", true).unwrap(),
|
||||||
"refresh" => win2.eval("location.reload()").unwrap(),
|
"refresh" => win2.eval("location.reload()").unwrap(),
|
||||||
"new_window" => _ = create_window(&handle2),
|
"new_window" => _ = create_window(&handle2),
|
||||||
"toggle_devtools" => {
|
"toggle_devtools" => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "Yaak",
|
"productName": "Yaak",
|
||||||
"version": "2023.0.10"
|
"version": "2023.0.11"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"windows": [],
|
"windows": [],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
|||||||
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
||||||
import { useRequest } from '../hooks/useRequest';
|
import { useRequest } from '../hooks/useRequest';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
|
import { HotKey } from './core/HotKey';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
import { InlineCode } from './core/InlineCode';
|
import { InlineCode } from './core/InlineCode';
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ export function RequestActionsDropdown({ requestId, children }: Props) {
|
|||||||
label: 'Duplicate',
|
label: 'Duplicate',
|
||||||
onSelect: duplicateRequest.mutate,
|
onSelect: duplicateRequest.mutate,
|
||||||
leftSlot: <Icon icon="copy" />,
|
leftSlot: <Icon icon="copy" />,
|
||||||
|
rightSlot: <HotKey>⌘D</HotKey>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
|
|||||||
@@ -80,105 +80,109 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
|
|||||||
'shadow shadow-gray-100 dark:shadow-gray-0 relative',
|
'shadow shadow-gray-100 dark:shadow-gray-0 relative',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<HStack
|
{activeResponse && (
|
||||||
alignItems="center"
|
<>
|
||||||
className={classnames(
|
<HStack
|
||||||
'italic text-gray-700 text-sm w-full flex-shrink-0',
|
alignItems="center"
|
||||||
// Remove a bit of space because the tabs have lots too
|
className={classnames(
|
||||||
'-mb-1.5',
|
'italic text-gray-700 text-sm w-full flex-shrink-0',
|
||||||
)}
|
// Remove a bit of space because the tabs have lots too
|
||||||
>
|
'-mb-1.5',
|
||||||
{activeResponse && (
|
)}
|
||||||
<>
|
>
|
||||||
<div className="whitespace-nowrap p-3 py-2">
|
{activeResponse && (
|
||||||
<StatusColor statusCode={activeResponse.status}>
|
<>
|
||||||
{activeResponse.status}
|
<div className="whitespace-nowrap p-3 py-2">
|
||||||
{activeResponse.statusReason && ` ${activeResponse.statusReason}`}
|
<StatusColor statusCode={activeResponse.status}>
|
||||||
</StatusColor>
|
{activeResponse.status}
|
||||||
•
|
{activeResponse.statusReason && ` ${activeResponse.statusReason}`}
|
||||||
{activeResponse.elapsed}ms •
|
</StatusColor>
|
||||||
{Math.round(activeResponse.body.length / 1000)} KB
|
•
|
||||||
</div>
|
{activeResponse.elapsed}ms •
|
||||||
|
{Math.round(activeResponse.body.length / 1000)} KB
|
||||||
|
</div>
|
||||||
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
label: viewMode === 'pretty' ? 'View Raw' : 'View Prettified',
|
label: viewMode === 'pretty' ? 'View Raw' : 'View Prettified',
|
||||||
onSelect: toggleViewMode,
|
onSelect: toggleViewMode,
|
||||||
},
|
},
|
||||||
{ type: 'separator', label: 'Actions' },
|
{ type: 'separator', label: 'Actions' },
|
||||||
{
|
{
|
||||||
label: 'Clear Response',
|
label: 'Clear Response',
|
||||||
onSelect: deleteResponse.mutate,
|
onSelect: deleteResponse.mutate,
|
||||||
disabled: responses.length === 0,
|
disabled: responses.length === 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `Clear ${responses.length} ${pluralize('Response', responses.length)}`,
|
label: `Clear ${responses.length} ${pluralize('Response', responses.length)}`,
|
||||||
onSelect: deleteAllResponses.mutate,
|
onSelect: deleteAllResponses.mutate,
|
||||||
hidden: responses.length <= 1,
|
hidden: responses.length <= 1,
|
||||||
disabled: responses.length === 0,
|
disabled: responses.length === 0,
|
||||||
},
|
},
|
||||||
{ type: 'separator', label: 'History' },
|
{ type: 'separator', label: 'History' },
|
||||||
...responses.slice(0, 10).map((r) => ({
|
...responses.slice(0, 10).map((r) => ({
|
||||||
label: r.status + ' - ' + r.elapsed + ' ms',
|
label: r.status + ' - ' + r.elapsed + ' ms',
|
||||||
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <></>,
|
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <></>,
|
||||||
onSelect: () => setPinnedResponseId(r.id),
|
onSelect: () => setPinnedResponseId(r.id),
|
||||||
})),
|
})),
|
||||||
]}
|
]}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
title="Show response history"
|
||||||
|
icon="triangleDown"
|
||||||
|
className="ml-auto"
|
||||||
|
size="sm"
|
||||||
|
iconSize="md"
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{activeResponse?.error ? (
|
||||||
|
<Banner className="m-2">{activeResponse.error}</Banner>
|
||||||
|
) : (
|
||||||
|
<Tabs
|
||||||
|
value={activeTab}
|
||||||
|
onChangeValue={setActiveTab}
|
||||||
|
label="Response"
|
||||||
|
className="px-3"
|
||||||
|
tabs={tabs}
|
||||||
>
|
>
|
||||||
<IconButton
|
<TabContent value="body">
|
||||||
title="Show response history"
|
{!activeResponse.body ? (
|
||||||
icon="triangleDown"
|
<EmptyStateText>No Response</EmptyStateText>
|
||||||
className="ml-auto"
|
) : viewMode === 'pretty' && contentType.includes('html') ? (
|
||||||
size="sm"
|
<Webview
|
||||||
iconSize="md"
|
body={activeResponse.body}
|
||||||
/>
|
contentType={contentType}
|
||||||
</Dropdown>
|
url={activeResponse.url}
|
||||||
</>
|
/>
|
||||||
)}
|
) : viewMode === 'pretty' && contentType.includes('json') ? (
|
||||||
</HStack>
|
<Editor
|
||||||
|
readOnly
|
||||||
{activeResponse?.error ? (
|
forceUpdateKey={`pretty::${activeResponse.updatedAt}`}
|
||||||
<Banner className="m-2">{activeResponse.error}</Banner>
|
className="bg-gray-50 dark:!bg-gray-100"
|
||||||
) : (
|
defaultValue={tryFormatJson(activeResponse?.body)}
|
||||||
<Tabs
|
contentType={contentType}
|
||||||
value={activeTab}
|
/>
|
||||||
onChangeValue={setActiveTab}
|
) : activeResponse?.body ? (
|
||||||
label="Response"
|
<Editor
|
||||||
className="px-3"
|
readOnly
|
||||||
tabs={tabs}
|
forceUpdateKey={activeResponse.updatedAt}
|
||||||
>
|
className="bg-gray-50 dark:!bg-gray-100"
|
||||||
<TabContent value="body">
|
defaultValue={activeResponse?.body}
|
||||||
{activeResponse === null ? (
|
contentType={contentType}
|
||||||
<EmptyStateText>No Response</EmptyStateText>
|
/>
|
||||||
) : viewMode === 'pretty' && contentType.includes('html') ? (
|
) : null}
|
||||||
<Webview
|
</TabContent>
|
||||||
body={activeResponse.body}
|
<TabContent value="headers">
|
||||||
contentType={contentType}
|
<ResponseHeaders headers={activeResponse?.headers ?? []} />
|
||||||
url={activeResponse.url}
|
</TabContent>
|
||||||
/>
|
</Tabs>
|
||||||
) : viewMode === 'pretty' && contentType.includes('json') ? (
|
)}
|
||||||
<Editor
|
</>
|
||||||
readOnly
|
|
||||||
forceUpdateKey={`pretty::${activeResponse.updatedAt}`}
|
|
||||||
className="bg-gray-50 dark:!bg-gray-100"
|
|
||||||
defaultValue={tryFormatJson(activeResponse?.body)}
|
|
||||||
contentType={contentType}
|
|
||||||
/>
|
|
||||||
) : activeResponse?.body ? (
|
|
||||||
<Editor
|
|
||||||
readOnly
|
|
||||||
forceUpdateKey={activeResponse.updatedAt}
|
|
||||||
className="bg-gray-50 dark:!bg-gray-100"
|
|
||||||
defaultValue={activeResponse?.body}
|
|
||||||
contentType={contentType}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</TabContent>
|
|
||||||
<TabContent value="headers">
|
|
||||||
<ResponseHeaders headers={activeResponse?.headers ?? []} />
|
|
||||||
</TabContent>
|
|
||||||
</Tabs>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useActiveRequestId } from '../hooks/useActiveRequestId';
|
||||||
import { useCreateRequest } from '../hooks/useCreateRequest';
|
import { useCreateRequest } from '../hooks/useCreateRequest';
|
||||||
|
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
||||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||||
|
import { useTauriEvent } from '../hooks/useTauriEvent';
|
||||||
import { IconButton } from './core/IconButton';
|
import { IconButton } from './core/IconButton';
|
||||||
|
|
||||||
export const SidebarActions = memo(function SidebarDisplayToggle() {
|
export const SidebarActions = memo(function SidebarDisplayToggle() {
|
||||||
const { hidden, toggle } = useSidebarHidden();
|
const { hidden, toggle } = useSidebarHidden();
|
||||||
|
const activeRequestId = useActiveRequestId();
|
||||||
const createRequest = useCreateRequest({ navigateAfter: true });
|
const createRequest = useCreateRequest({ navigateAfter: true });
|
||||||
|
const duplicateRequest = useDuplicateRequest({ id: activeRequestId, navigateAfter: true });
|
||||||
const handleCreateRequest = useCallback(() => {
|
const handleCreateRequest = useCallback(() => {
|
||||||
createRequest.mutate({ name: 'New Request' });
|
createRequest.mutate({});
|
||||||
}, [createRequest]);
|
}, [createRequest]);
|
||||||
|
useTauriEvent('new_request', () => {
|
||||||
|
createRequest.mutate({});
|
||||||
|
});
|
||||||
|
// TODO: Put this somewhere better
|
||||||
|
useTauriEvent('duplicate_request', () => {
|
||||||
|
duplicateRequest.mutate();
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface Props {
|
|||||||
|
|
||||||
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
||||||
const activeRequest = useActiveRequest();
|
const activeRequest = useActiveRequest();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={classnames(
|
className={classnames(
|
||||||
className,
|
className,
|
||||||
'min-w-[8rem] outline-none px-2 mx-1.5 h-7 flex items-center text-sm text-gray-700 whitespace-nowrap pr-4',
|
'min-w-[8rem] outline-none px-2 mx-1.5 h-7 flex items-center text-sm text-gray-700 whitespace-nowrap',
|
||||||
'focus:bg-highlight focus:text-gray-900 rounded',
|
'focus:bg-highlight focus:text-gray-900 rounded',
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -3,18 +3,8 @@ import type { CompletionContext } from '@codemirror/autocomplete';
|
|||||||
const openTag = '${[ ';
|
const openTag = '${[ ';
|
||||||
const closeTag = ' ]}';
|
const closeTag = ' ]}';
|
||||||
|
|
||||||
const variables = [
|
const variables: { name: string }[] = [
|
||||||
{ name: 'DOMAIN' },
|
// TODO: Put variables here
|
||||||
{ name: 'BASE_URL' },
|
|
||||||
{ name: 'CONTENT_THINGY' },
|
|
||||||
{ name: 'TOKEN' },
|
|
||||||
{ name: 'PROJECT_ID' },
|
|
||||||
{ name: 'DUMMY' },
|
|
||||||
{ name: 'DUMMY_2' },
|
|
||||||
{ name: 'STRIPE_PUB_KEY' },
|
|
||||||
{ name: 'RAILWAY_TOKEN' },
|
|
||||||
{ name: 'SECRET' },
|
|
||||||
{ name: 'PORT' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const MIN_MATCH_VAR = 2;
|
const MIN_MATCH_VAR = 2;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function HotKey({ children }: HTMLAttributes<HTMLSpanElement>) {
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={classnames(
|
className={classnames(
|
||||||
'bg-gray-400 bg-opacity-20 px-1.5 py-0.5 rounded text-sm',
|
'bg-highlightSecondary bg-opacity-20 px-1.5 py-0.5 rounded text-sm',
|
||||||
'font-mono text-gray-500 tracking-widest',
|
'font-mono text-gray-500 tracking-widest',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ export function useCreateRequest({ navigateAfter }: { navigateAfter: boolean })
|
|||||||
if (workspaceId === null) {
|
if (workspaceId === null) {
|
||||||
throw new Error("Cannot create request when there's no active workspace");
|
throw new Error("Cannot create request when there's no active workspace");
|
||||||
}
|
}
|
||||||
const sortPriority = maxSortPriority(requests) + 1000;
|
patch.name = patch.name || 'New Request';
|
||||||
return invoke('create_request', { sortPriority, workspaceId, ...patch });
|
patch.sortPriority = patch.sortPriority || maxSortPriority(requests) + 1000;
|
||||||
|
return invoke('create_request', { workspaceId, ...patch });
|
||||||
},
|
},
|
||||||
onSuccess: async (request) => {
|
onSuccess: async (request) => {
|
||||||
queryClient.setQueryData<HttpRequest[]>(
|
queryClient.setQueryData<HttpRequest[]>(
|
||||||
|
|||||||
Reference in New Issue
Block a user