mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 20:00:29 +01:00
More subtle layout tweaks
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use tauri::{Runtime, Window};
|
||||
|
||||
const TRAFFIC_LIGHT_OFFSET_X: f64 = 15.0;
|
||||
const TRAFFIC_LIGHT_OFFSET_Y: f64 = 20.0;
|
||||
const TRAFFIC_LIGHT_OFFSET_Y: f64 = 18.0;
|
||||
|
||||
pub trait WindowExt {
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import classnames from 'classnames';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Button } from './components/Button';
|
||||
import { Divider } from './components/Divider';
|
||||
import { Grid } from './components/Grid';
|
||||
import { IconButton } from './components/IconButton';
|
||||
import { RequestPane } from './components/RequestPane';
|
||||
import { ResponsePane } from './components/ResponsePane';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
import { HStack } from './components/Stacks';
|
||||
import { WindowDragRegion } from './components/WindowDragRegion';
|
||||
import {
|
||||
useDeleteRequest,
|
||||
useRequests,
|
||||
useRequestUpdate,
|
||||
useSendRequest,
|
||||
} from './hooks/useRequest';
|
||||
import { useRequests } from './hooks/useRequest';
|
||||
|
||||
type Params = {
|
||||
workspaceId: string;
|
||||
@@ -26,7 +17,6 @@ function App() {
|
||||
const p = useParams<Params>();
|
||||
const workspaceId = p.workspaceId ?? '';
|
||||
const { data: requests } = useRequests(workspaceId);
|
||||
const navigate = useNavigate();
|
||||
const request = requests?.find((r) => r.id === p.requestId);
|
||||
|
||||
const [screenWidth, setScreenWidth] = useState(window.innerWidth);
|
||||
@@ -36,40 +26,28 @@ function App() {
|
||||
const isH = screenWidth > 900;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-[auto_1fr] h-full text-gray-900">
|
||||
<div className="grid grid-cols-[auto_1fr] h-full text-gray-900 overflow-hidden rounded-[11px]">
|
||||
<Sidebar requests={requests ?? []} workspaceId={workspaceId} activeRequestId={request?.id} />
|
||||
{request && (
|
||||
<div className="p-2 h-full">
|
||||
<div className="grid grid-rows-[auto_1fr] rounded-md h-full overflow-hidden">
|
||||
<div className="h-full">
|
||||
<div className="grid grid-rows-[auto_1fr] h-full overflow-hidden">
|
||||
<HStack
|
||||
as={WindowDragRegion}
|
||||
className="pl-1 pr-3 bg-gray-50 text-sm"
|
||||
justify="center"
|
||||
className="px-3 bg-gray-50/50 text-sm text-gray-900 border-b border-b-gray-50 pt-[1px]"
|
||||
items="center"
|
||||
>
|
||||
<div className="mr-auto">
|
||||
<IconButton
|
||||
size="xs"
|
||||
icon="x"
|
||||
onClick={() => navigate(`/workspaces/${workspaceId}`)}
|
||||
/>
|
||||
</div>
|
||||
<div>{request.name}</div>
|
||||
<div className="ml-auto"></div>
|
||||
{request.name}
|
||||
</HStack>
|
||||
<div
|
||||
className={classnames(
|
||||
'py-2 px-1 bg-gray-25 grid overflow-auto',
|
||||
'bg-gray-25 grid overflow-auto',
|
||||
isH ? 'grid-cols-[1fr_1fr]' : 'grid-rows-[minmax(0,auto)_minmax(0,100%)]',
|
||||
)}
|
||||
>
|
||||
<RequestPane
|
||||
fullHeight={isH}
|
||||
request={request}
|
||||
className={classnames(
|
||||
'border-gray-100/50',
|
||||
isH ? 'pr-0 border-r' : 'pb-3 mb-1 border-b',
|
||||
)}
|
||||
className={classnames(isH ? 'pr-0' : 'pb-3 mb-1')}
|
||||
/>
|
||||
<ResponsePane requestId={request.id} />
|
||||
</div>
|
||||
|
||||
@@ -52,7 +52,7 @@ export const Button = forwardRef(function Button<T extends ElementType>(
|
||||
justify === 'center' && 'justify-center',
|
||||
size === 'md' && 'h-10 px-4',
|
||||
size === 'sm' && 'h-8 px-3 text-sm',
|
||||
size === 'xs' && 'h-6 px-3 text-xs',
|
||||
size === 'xs' && 'h-7 px-2.5 text-sm',
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
@apply w-full block text-[0.85rem] bg-gray-25;
|
||||
@apply bg-background w-full block text-[0.85rem];
|
||||
|
||||
&.cm-focused {
|
||||
outline: none !important;
|
||||
@@ -70,7 +70,7 @@
|
||||
}
|
||||
|
||||
.cm-editor .cm-gutters {
|
||||
@apply bg-gray-25 border-0 text-gray-200;
|
||||
@apply bg-background border-0 text-gray-200;
|
||||
}
|
||||
|
||||
.cm-editor .cm-gutterElement {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Compartment, EditorState } from '@codemirror/state';
|
||||
import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view';
|
||||
import classnames from 'classnames';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import type { CSSProperties, HTMLAttributes } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import './Editor.css';
|
||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||
@@ -13,6 +13,7 @@ import { singleLineExt } from './singleLine';
|
||||
export interface EditorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
||||
height?: 'auto' | 'full';
|
||||
contentType?: string;
|
||||
backgroundColor?: string;
|
||||
autoFocus?: boolean;
|
||||
valueKey?: string | number;
|
||||
defaultValue?: string;
|
||||
@@ -26,6 +27,7 @@ export interface EditorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onCha
|
||||
export default function Editor({
|
||||
height,
|
||||
contentType,
|
||||
backgroundColor,
|
||||
autoFocus,
|
||||
placeholder,
|
||||
valueKey,
|
||||
@@ -100,6 +102,7 @@ export default function Editor({
|
||||
height === 'auto' ? 'cm-auto-height' : 'cm-full-height',
|
||||
singleLine ? 'cm-singleline' : 'cm-multiline',
|
||||
)}
|
||||
data-color-background="var(--color-gray-50)"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import classnames from 'classnames';
|
||||
import { useRequestUpdate, useSendRequest } from '../hooks/useRequest';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { Button } from './Button';
|
||||
import { Divider } from './Divider';
|
||||
import Editor from './Editor/Editor';
|
||||
import { ScrollArea } from './ScrollArea';
|
||||
import { HStack } from './Stacks';
|
||||
@@ -18,10 +17,12 @@ export function RequestPane({ fullHeight, request, className }: Props) {
|
||||
const updateRequest = useRequestUpdate(request ?? null);
|
||||
const sendRequest = useSendRequest(request ?? null);
|
||||
return (
|
||||
<div className={classnames(className, 'grid grid-rows-[auto_auto_minmax(0,1fr)] grid-cols-1')}>
|
||||
<div>
|
||||
<div
|
||||
className={classnames(className, 'py-2 grid grid-rows-[auto_auto_minmax(0,1fr)] grid-cols-1')}
|
||||
>
|
||||
<div className="pl-2">
|
||||
<UrlBar
|
||||
className="bg-transparent border-0 mb-1"
|
||||
className="border-0 mb-1"
|
||||
key={request.id}
|
||||
method={request.method}
|
||||
url={request.url}
|
||||
@@ -30,9 +31,7 @@ export function RequestPane({ fullHeight, request, className }: Props) {
|
||||
onUrlChange={(url) => updateRequest.mutate({ url })}
|
||||
sendRequest={sendRequest.mutate}
|
||||
/>
|
||||
<div className="mx-2">
|
||||
<Divider />
|
||||
</div>
|
||||
{/*<Divider />*/}
|
||||
</div>
|
||||
{/*<Divider className="mb-2" />*/}
|
||||
<ScrollArea className="max-w-full pb-2 mx-2">
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import classnames from 'classnames';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useDeleteAllResponses, useDeleteResponse, useResponses } from '../hooks/useResponses';
|
||||
import { Button } from './Button';
|
||||
import { Divider } from './Divider';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import Editor from './Editor/Editor';
|
||||
import { Icon } from './Icon';
|
||||
import { IconButton } from './IconButton';
|
||||
import { ScrollArea } from './ScrollArea';
|
||||
import { HStack } from './Stacks';
|
||||
|
||||
interface Props {
|
||||
@@ -45,23 +42,23 @@ export function ResponsePane({ requestId, className }: Props) {
|
||||
}, [response?.body, contentType]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
className,
|
||||
'max-h-full h-full grid grid-rows-[auto_auto_minmax(0,1fr)] grid-cols-1',
|
||||
)}
|
||||
>
|
||||
{/*<HStack as={WindowDragRegion} items="center" className="pl-1.5 pr-1">*/}
|
||||
{/*</HStack>*/}
|
||||
{response?.error && (
|
||||
<div className="text-white bg-red-500 px-2 py-1 rounded">{response.error}</div>
|
||||
)}
|
||||
{response && (
|
||||
<>
|
||||
<div>
|
||||
<div className="p-2">
|
||||
<div
|
||||
className={classnames(
|
||||
className,
|
||||
'max-h-full h-full grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1 bg-gray-50/50 rounded-md',
|
||||
)}
|
||||
>
|
||||
{/*<HStack as={WindowDragRegion} items="center" className="pl-1.5 pr-1">*/}
|
||||
{/*</HStack>*/}
|
||||
{response?.error && (
|
||||
<div className="text-white bg-red-500 px-2 py-1 rounded">{response.error}</div>
|
||||
)}
|
||||
{response && (
|
||||
<>
|
||||
<HStack
|
||||
items="center"
|
||||
className="italic text-gray-500 text-sm w-full mb-1 flex-shrink-0 pl-2"
|
||||
className="italic text-gray-500 text-sm w-full mb-1 flex-shrink-0 pl-2 py-1"
|
||||
>
|
||||
<div className="whitespace-nowrap">
|
||||
{response.status}
|
||||
@@ -104,43 +101,27 @@ export function ResponsePane({ requestId, className }: Props) {
|
||||
</Dropdown>
|
||||
</HStack>
|
||||
</HStack>
|
||||
<div className="px-2">
|
||||
<Divider />
|
||||
</div>
|
||||
</div>
|
||||
<ScrollArea className="max-w-full pb-2 mx-2">
|
||||
<HStack className="mt-2 hide-scrollbar" space={1}>
|
||||
{['Preview', 'Headers', 'Cookies', 'Timing'].map((label, i) => (
|
||||
<Button
|
||||
key={label}
|
||||
size="xs"
|
||||
color={i === 0 && 'gray'}
|
||||
className={i !== 0 && 'opacity-50 hover:opacity-60'}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
))}
|
||||
</HStack>
|
||||
</ScrollArea>
|
||||
|
||||
{viewMode === 'pretty' && contentForIframe !== null ? (
|
||||
<div className="pl-2">
|
||||
<iframe
|
||||
title="Response preview"
|
||||
srcDoc={contentForIframe}
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
className="h-full w-full rounded-lg"
|
||||
{viewMode === 'pretty' && contentForIframe !== null ? (
|
||||
<div className="px-2 pb-2">
|
||||
<iframe
|
||||
title="Response preview"
|
||||
srcDoc={contentForIframe}
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
className="h-full w-full rounded-md border border-gray-100/20"
|
||||
/>
|
||||
</div>
|
||||
) : response?.body ? (
|
||||
<Editor
|
||||
backgroundColor="red"
|
||||
valueKey={`${contentType}:${response.body}`}
|
||||
defaultValue={response?.body}
|
||||
contentType={contentType}
|
||||
/>
|
||||
</div>
|
||||
) : response?.body ? (
|
||||
<Editor
|
||||
valueKey={`${contentType}:${response.body}`}
|
||||
defaultValue={response?.body}
|
||||
contentType={contentType}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,21 +23,24 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
|
||||
const { toggleTheme } = useTheme();
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
return (
|
||||
<div className={classnames(className, 'w-52 bg-gray-50 h-full px-2')} {...props}>
|
||||
<HStack as={WindowDragRegion} items="center" className="py-2" justify="end">
|
||||
<div
|
||||
className={classnames(className, 'w-52 bg-gray-50 h-full border-gray-100/50 relative z-10')}
|
||||
{...props}
|
||||
>
|
||||
<HStack as={WindowDragRegion} items="center" justify="end">
|
||||
<Dialog wide open={open} onOpenChange={setOpen} title="Edit Headers">
|
||||
<HeaderEditor />
|
||||
<Button className="ml-auto mt-5" color="primary" onClick={() => setOpen(false)}>
|
||||
Save
|
||||
</Button>
|
||||
</Dialog>
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon="camera"
|
||||
onClick={() => {
|
||||
setOpen((v) => !v);
|
||||
}}
|
||||
/>
|
||||
{/*<IconButton*/}
|
||||
{/* size="sm"*/}
|
||||
{/* icon="camera"*/}
|
||||
{/* onClick={() => {*/}
|
||||
{/* setOpen((v) => !v);*/}
|
||||
{/* }}*/}
|
||||
{/*/>*/}
|
||||
<IconButton size="sm" icon="sun" onClick={toggleTheme} />
|
||||
<IconButton
|
||||
size="sm"
|
||||
@@ -47,7 +50,7 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
<VStack as="ul" className="pb-3" space={1}>
|
||||
<VStack as="ul" className="py-3 px-2" space={1}>
|
||||
{requests.map((r) => (
|
||||
<SidebarItem key={r.id} request={r} active={r.id === activeRequestId} />
|
||||
))}
|
||||
@@ -63,7 +66,7 @@ function SidebarItem({ request, active }: { request: HttpRequest; active: boolea
|
||||
as={Link}
|
||||
to={`/workspaces/${request.workspaceId}/requests/${request.id}`}
|
||||
className={classnames('w-full', active && 'bg-gray-500/[0.1] text-gray-900')}
|
||||
size="sm"
|
||||
size="xs"
|
||||
justify="start"
|
||||
>
|
||||
{request.name}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function WindowDragRegion({ className, ...props }: Props) {
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={classnames(className, 'w-full h-8 flex-shrink-0 box-content')}
|
||||
className={classnames(className, 'w-full h-10 flex-shrink-0')}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -41,6 +41,10 @@ html, body, #root {
|
||||
/* }*/
|
||||
/*}*/
|
||||
|
||||
[data-color-background] {
|
||||
--color-background: attr(data-bg-color);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root, [data-theme="light"] {
|
||||
/* Colors */
|
||||
|
||||
Reference in New Issue
Block a user