Tweak appearance settings

This commit is contained in:
Gregory Schier
2024-05-30 12:32:12 -07:00
parent 3efb5bb4eb
commit b13207072a
22 changed files with 170 additions and 144 deletions

View File

@@ -45,7 +45,7 @@ export function BinaryFileEditor({
return (
<VStack space={2}>
<HStack space={2} alignItems="center">
<HStack space={2}>
<Button variant="border" color="secondary" size="sm" onClick={handleClick}>
Choose File
</Button>

View File

@@ -36,7 +36,7 @@ export const CookieDialog = function ({ cookieJarId }: Props) {
<th className="py-2 pl-4"></th>
</tr>
</thead>
<tbody className="divide-y">
<tbody className="divide-y divide-background-highlight-secondary">
{cookieJar?.cookies.map((c) => (
<tr key={c.domain + c.raw_cookie}>
<td className="py-2 select-text cursor-text font-mono font-semibold max-w-0">

View File

@@ -61,8 +61,8 @@ export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }:
firstSlot={() =>
activeConnection && (
<div className="w-full grid grid-rows-[auto_minmax(0,1fr)] items-center">
<HStack className="pl-3 mb-1 font-mono" alignItems="center">
<HStack alignItems="center" space={2}>
<HStack className="pl-3 mb-1 font-mono">
<HStack space={2}>
<span>{events.length} messages</span>
{activeConnection.elapsed === 0 && (
<Icon icon="refresh" size="sm" spin className="text-fg-subtler" />

View File

@@ -0,0 +1,31 @@
import classNames from 'classnames';
import type { HTMLAttributes, ReactNode } from 'react';
import { useIsFullscreen } from '../hooks/useIsFullscreen';
import { useOsInfo } from '../hooks/useOsInfo';
interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
size: 'md' | 'lg';
}
export function HeaderSize({ className, style, size, ...props }: HeaderSizeProps) {
const platform = useOsInfo();
const fullscreen = useIsFullscreen();
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen;
return (
<div
data-tauri-drag-region
style={style}
className={classNames(
className,
'pt-[1px] w-full border-b border-background-highlight min-w-0',
stoplightsVisible ? 'pl-20 pr-1' : 'pl-1',
size === 'md' && 'h-[27px]',
size === 'lg' && 'h-[38px]',
)}
>
{/* NOTE: This needs display:grid or else the element shrinks (even though scrollable) */}
<div className="h-full w-full overflow-x-auto hide-scrollbars grid" {...props} />
</div>
);
}

View File

@@ -43,7 +43,7 @@ export function RecentConnectionsDropdown({
...connections.slice(0, 20).map((c) => ({
key: c.id,
label: (
<HStack space={2} alignItems="center">
<HStack space={2}>
{formatDistanceToNowStrict(c.createdAt + 'Z')} ago &bull;{' '}
<span className="font-mono text-sm">{c.elapsed}ms</span>
</HStack>

View File

@@ -46,7 +46,7 @@ export const RecentResponsesDropdown = function ResponsePane({
...responses.slice(0, 20).map((r: HttpResponse) => ({
key: r.id,
label: (
<HStack space={2} alignItems="center">
<HStack space={2}>
<StatusTag className="text-sm" response={r} />
<span>&rarr;</span>{' '}
<span className="font-mono text-sm">{r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'}</span>

View File

@@ -92,7 +92,6 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
) : (
<div className="h-full w-full grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1">
<HStack
alignItems="center"
className={classNames(
'text-fg-subtle w-full flex-shrink-0',
// Remove a bit of space because the tabs have lots too
@@ -102,7 +101,6 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
{activeResponse && (
<HStack
space={2}
alignItems="center"
className="whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm"
>
<StatusTag showReason response={activeResponse} />

View File

@@ -1,7 +1,11 @@
import { getCurrent } from '@tauri-apps/api/webviewWindow';
import React from 'react';
import { createGlobalState, useKeyPressEvent } from 'react-use';
import { capitalize } from '../../lib/capitalize';
import { HStack } from '../core/Stacks';
import { TabContent, Tabs } from '../core/Tabs/Tabs';
import { HeaderSize } from '../HeaderSize';
import { WindowControls } from '../WindowControls';
import { SettingsAppearance } from './SettingsAppearance';
import { SettingsGeneral } from './SettingsGeneral';
@@ -22,12 +26,20 @@ export const Settings = () => {
return (
<>
<div
<HeaderSize
data-tauri-drag-region
className="x-theme-appHeader h-[27px] bg-background text-fg-subtle flex items-center justify-center border-b border-background-highlight text-sm font-semibold"
size="md"
className="x-theme-appHeader bg-background text-fg-subtle flex items-center justify-center border-b border-background-highlight text-sm font-semibold"
>
Settings
</div>
<HStack
space={2}
justifyContent="center"
className="w-full h-full grid grid-cols-[1fr_auto]"
>
<div className="text-center">Settings</div>
<WindowControls className="ml-auto" />
</HStack>
</HeaderSize>
<Tabs
value={tab}
addBorders

View File

@@ -123,6 +123,7 @@ export function SettingsAppearance() {
]}
/>
<HStack space={2}>
<div>Theme</div>
{(settings.appearance === 'system' || settings.appearance === 'light') && (
<Select
hideLabel
@@ -131,7 +132,7 @@ export function SettingsAppearance() {
label="Light Theme"
size="sm"
value={activeTheme.light.id}
options={lightThemes}
options={[{ label: 'Light Mode Themes', options: lightThemes }]}
onChange={(themeLight) => {
trackEvent('theme', 'update', { theme: themeLight, appearance: 'light' });
updateSettings.mutateAsync({ ...settings, themeLight });
@@ -146,7 +147,7 @@ export function SettingsAppearance() {
leftSlot={<Icon icon="moon" />}
size="sm"
value={activeTheme.dark.id}
options={darkThemes}
options={[{ label: 'Dark Mode Themes', options: darkThemes }]}
onChange={(themeDark) => {
trackEvent('theme', 'update', { theme: themeDark, appearance: 'dark' });
updateSettings.mutateAsync({ ...settings, themeDark });
@@ -159,11 +160,11 @@ export function SettingsAppearance() {
space={3}
className="mt-3 w-full bg-background p-3 border border-dashed border-background-highlight rounded overflow-x-auto"
>
<HStack className="text-fg font-bold" alignItems="center" space={2}>
<HStack className="text-fg font-bold" space={2}>
Theme Preview{' '}
<Icon icon={appearance === 'dark' ? 'moon' : 'sun'} className="text-fg-subtle" />
</HStack>
<HStack space={1.5} alignItems="center" className="w-full">
<HStack space={1.5} className="w-full">
{buttonColors.map((c, i) => (
<IconButton
key={c}

View File

@@ -19,7 +19,7 @@ export function SidebarActions() {
);
return (
<HStack className="h-full" alignItems="center">
<HStack className="h-full">
<IconButton
onClick={async () => {
trackEvent('sidebar', 'toggle');

View File

@@ -0,0 +1,69 @@
import { getCurrent } from '@tauri-apps/api/webviewWindow';
import classNames from 'classnames';
import React, { useState } from 'react';
import { useOsInfo } from '../hooks/useOsInfo';
import { Button } from './core/Button';
import { HStack } from './core/Stacks';
interface Props {
className?: string;
}
export function WindowControls({ className }: Props) {
const [maximized, setMaximized] = useState<boolean>(false);
const osInfo = useOsInfo();
const shouldShow = osInfo?.osType === 'linux' || osInfo?.osType === 'windows';
if (!shouldShow) {
return null;
}
return (
<HStack className={classNames(className, 'ml-4 h-full')}>
<Button
className="!h-full px-4 text-fg-subtle hocus:text-fg hocus:bg-background-highlight-secondary rounded-none"
color="custom"
onClick={() => getCurrent().minimize()}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="currentColor" d="M14 8v1H3V8z" />
</svg>
</Button>
<Button
className="!h-full px-4 text-fg-subtle hocus:text-fg hocus:bg-background-highlight rounded-none"
color="custom"
onClick={async () => {
const w = getCurrent();
await w.toggleMaximize();
setMaximized(await w.isMaximized());
}}
>
{maximized ? (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="currentColor">
<path d="M3 5v9h9V5zm8 8H4V6h7z" />
<path fillRule="evenodd" d="M5 5h1V4h7v7h-1v1h2V3H5z" clipRule="evenodd" />
</g>
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="currentColor" d="M3 3v10h10V3zm9 9H4V4h8z" />
</svg>
)}
</Button>
<Button
color="custom"
className="!h-full px-4 text-fg-subtle rounded-none hocus:bg-fg-danger hocus:text-fg"
onClick={() => getCurrent().close()}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path
fill="currentColor"
fillRule="evenodd"
d="m7.116 8l-4.558 4.558l.884.884L8 8.884l4.558 4.558l.884-.884L8.884 8l4.558-4.558l-.884-.884L8 7.116L3.442 2.558l-.884.884z"
clipRule="evenodd"
/>
</svg>
</Button>
</HStack>
);
}

View File

@@ -1,11 +1,6 @@
import classNames from 'classnames';
import { motion } from 'framer-motion';
import type {
CSSProperties,
HTMLAttributes,
MouseEvent as ReactMouseEvent,
ReactNode,
} from 'react';
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import { useWindowSize } from 'react-use';
import { useActiveRequest } from '../hooks/useActiveRequest';
@@ -13,8 +8,6 @@ import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
import { useFloatingSidebarHidden } from '../hooks/useFloatingSidebarHidden';
import { useImportData } from '../hooks/useImportData';
import { useIsFullscreen } from '../hooks/useIsFullscreen';
import { useOsInfo } from '../hooks/useOsInfo';
import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar';
import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { useSidebarWidth } from '../hooks/useSidebarWidth';
@@ -28,6 +21,7 @@ import { FeedbackLink } from './core/Link';
import { HStack } from './core/Stacks';
import { CreateDropdown } from './CreateDropdown';
import { GrpcConnectionLayout } from './GrpcConnectionLayout';
import { HeaderSize } from './HeaderSize';
import { HttpRequestLayout } from './HttpRequestLayout';
import { Overlay } from './Overlay';
import { ResizeHandle } from './ResizeHandle';
@@ -150,7 +144,7 @@ export default function Workspace() {
'grid grid-rows-[auto_1fr]',
)}
>
<HeaderSize className="border-transparent">
<HeaderSize size="lg" className="border-transparent">
<SidebarActions />
</HeaderSize>
<Sidebar />
@@ -174,7 +168,12 @@ export default function Workspace() {
/>
</>
)}
<HeaderSize data-tauri-drag-region className="x-theme-appHeader bg-background" style={head}>
<HeaderSize
data-tauri-drag-region
size="lg"
className="x-theme-appHeader bg-background"
style={head}
>
<WorkspaceHeader className="pointer-events-none" />
</HeaderSize>
{activeWorkspace == null ? (
@@ -209,27 +208,3 @@ export default function Workspace() {
</div>
);
}
interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
}
export function HeaderSize({ className, style, ...props }: HeaderSizeProps) {
const platform = useOsInfo();
const fullscreen = useIsFullscreen();
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen;
return (
<div
data-tauri-drag-region
style={style}
className={classNames(
className,
'h-md pt-[1px] w-full border-b border-background-highlight min-w-0',
stoplightsVisible ? 'pl-20 pr-1' : 'pl-1',
)}
>
{/* NOTE: This needs display:grid or else the element shrinks (even though scrollable) */}
<div className="h-full w-full overflow-x-auto hide-scrollbars grid" {...props} />
</div>
);
}

View File

@@ -52,12 +52,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
),
render: ({ hide }) => {
return (
<HStack
space={2}
justifyContent="start"
alignItems="center"
className="mt-4 mb-6 flex-row-reverse"
>
<HStack space={2} justifyContent="start" className="mt-4 mb-6 flex-row-reverse">
<Button
className="focus"
color="primary"

View File

@@ -1,9 +1,6 @@
import { getCurrent } from '@tauri-apps/api/webviewWindow';
import classNames from 'classnames';
import React, { memo, useState } from 'react';
import { useOsInfo } from '../hooks/useOsInfo';
import React, { memo } from 'react';
import { CookieDropdown } from './CookieDropdown';
import { Button } from './core/Button';
import { Icon } from './core/Icon';
import { HStack } from './core/Stacks';
import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown';
@@ -11,6 +8,7 @@ import { ImportCurlButton } from './ImportCurlButton';
import { RecentRequestsDropdown } from './RecentRequestsDropdown';
import { SettingsDropdown } from './SettingsDropdown';
import { SidebarActions } from './SidebarActions';
import { WindowControls } from './WindowControls';
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
interface Props {
@@ -18,20 +16,12 @@ interface Props {
}
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
const osInfo = useOsInfo();
const [maximized, setMaximized] = useState<boolean>(false);
return (
<HStack
space={2}
justifyContent="center"
alignItems="center"
className={classNames(className, 'w-full h-full')}
>
<HStack space={0.5} className="flex-1 pointer-events-none" alignItems="center">
<HStack space={2} justifyContent="center" className={classNames(className, 'w-full h-full')}>
<HStack space={0.5} className="flex-1 pointer-events-none">
<SidebarActions />
<CookieDropdown />
<HStack alignItems="center">
<HStack>
<WorkspaceActionsDropdown />
<Icon icon="chevronRight" className="text-fg-subtle" />
<EnvironmentActionsDropdown className="w-auto pointer-events-auto" />
@@ -43,55 +33,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-0.5">
<ImportCurlButton />
<SettingsDropdown />
{(osInfo?.osType === 'linux' || osInfo?.osType === 'windows') && (
<HStack className="ml-4" alignItems="center">
<Button
className="px-4 text-fg-subtle hocus:text-fg hocus:bg-background-highlight-secondary rounded-none"
color="custom"
onClick={() => getCurrent().minimize()}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="currentColor" d="M14 8v1H3V8z" />
</svg>
</Button>
<Button
className="px-4 text-fg-subtle hocus:text-fg hocus:bg-background-highlight rounded-none"
color="custom"
onClick={async () => {
const w = getCurrent();
await w.toggleMaximize();
setMaximized(await w.isMaximized());
}}
>
{maximized ? (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="currentColor">
<path d="M3 5v9h9V5zm8 8H4V6h7z" />
<path fillRule="evenodd" d="M5 5h1V4h7v7h-1v1h2V3H5z" clipRule="evenodd" />
</g>
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="currentColor" d="M3 3v10h10V3zm9 9H4V4h8z" />
</svg>
)}
</Button>
<Button
color="custom"
className="px-4 text-fg-subtle rounded-none hocus:bg-fg-danger hocus:text-fg"
onClick={() => getCurrent().close()}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path
fill="currentColor"
fillRule="evenodd"
d="m7.116 8l-4.558 4.558l.884.884L8 8.884l4.558 4.558l.884-.884L8.884 8l4.558-4.558l-.884-.884L8 7.116L3.442 2.558l-.884.884z"
clipRule="evenodd"
/>
</svg>
</Button>
</HStack>
)}
<WindowControls />
</div>
</HStack>
);

View File

@@ -27,7 +27,6 @@ export function Checkbox({
<HStack
as="label"
space={2}
alignItems="center"
className={classNames(className, 'text-fg', disabled && 'opacity-disabled')}
>
<div className={classNames(inputWrapperClassName, 'x-theme-input', 'relative flex')}>

View File

@@ -224,7 +224,6 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
);
const [menuStyles, setMenuStyles] = useState<CSSProperties>({});
const [filter, setFilter] = useState<string>('');
const [containerWidth, setContainerWidth] = useState<number | null>(null);
// Calculate the max height so we can scroll
const initMenu = useCallback((el: HTMLDivElement | null) => {
@@ -349,11 +348,6 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
[handleClose, handleNext, handlePrev, handleSelect, items, selectedIndex],
);
const initContainerRef = (n: HTMLDivElement | null) => {
if (n == null) return null;
setContainerWidth(n.offsetWidth);
};
const { containerStyles, triangleStyles } = useMemo<{
containerStyles: CSSProperties;
triangleStyles: CSSProperties | null;
@@ -379,7 +373,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
? { right: width / 2, marginRight: '-0.2rem', ...size }
: { left: width / 2, marginLeft: '-0.2rem', ...size };
return { containerStyles, triangleStyles };
}, [triggerShape, containerWidth]);
}, [triggerShape]);
const filteredItems = useMemo(
() => items.filter((i) => getNodeText(i.label).toLowerCase().includes(filter.toLowerCase())),
@@ -422,7 +416,6 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
role="menu"
aria-orientation="vertical"
dir="ltr"
ref={initContainerRef}
style={containerStyles}
className={classNames(className, 'outline-none my-1 pointer-events-auto fixed z-50')}
>
@@ -446,7 +439,6 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
{filter && (
<HStack
space={2}
alignItems="center"
className="pb-0.5 px-1.5 mb-2 text-sm border border-background-highlight-secondary mx-2 rounded font-mono h-xs"
>
<Icon icon="search" size="xs" className="text-fg-subtle" />

View File

@@ -294,7 +294,6 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
{decoratedActions && (
<HStack
space={1}
alignItems="center"
justifyContent="end"
className={classNames(
'absolute bottom-2 left-0 right-0',

View File

@@ -153,7 +153,6 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
>
{leftSlot}
<HStack
alignItems="center"
className={classNames(
'w-full min-w-0',
leftSlot && 'pl-0.5 -ml-2',

View File

@@ -105,7 +105,6 @@ export const PlainInput = forwardRef<HTMLInputElement, PlainInputProps>(function
>
{leftSlot}
<HStack
alignItems="center"
className={classNames(
'w-full min-w-0',
leftSlot && 'pl-0.5 -ml-2',

View File

@@ -11,7 +11,7 @@ interface Props<T extends string> {
hideLabel?: boolean;
value: T;
leftSlot?: ReactNode;
options: SelectOption<T>[];
options: SelectOption<T>[] | SelectOptionGroup<T>[];
onChange: (value: T) => void;
size?: 'xs' | 'sm' | 'md' | 'lg';
className?: string;
@@ -22,6 +22,11 @@ export interface SelectOption<T extends string> {
value: T;
}
export interface SelectOptionGroup<T extends string> {
label: string;
options: SelectOption<T>[];
}
export function Select<T extends string>({
labelPosition = 'top',
name,
@@ -56,7 +61,6 @@ export function Select<T extends string>({
</label>
<HStack
space={2}
alignItems="center"
className={classNames(
'w-full rounded-md text-fg text-sm font-mono',
'pl-2',
@@ -77,11 +81,21 @@ export function Select<T extends string>({
onBlur={() => setFocused(false)}
className={classNames('pr-7 w-full outline-none bg-transparent')}
>
{options.map(({ label, value }) => (
<option key={label} value={value}>
{label}
</option>
))}
{options.map((o) =>
'options' in o ? (
<optgroup key={o.label} label={o.label}>
{o.options.map(({ label, value }) => (
<option key={label} value={value}>
{label}
</option>
))}
</optgroup>
) : (
<option key={o.label} value={o.value}>
{o.label}
</option>
),
)}
</select>
</HStack>
</div>

View File

@@ -19,7 +19,7 @@ interface HStackProps extends BaseStackProps {
}
export const HStack = forwardRef(function HStack(
{ className, space, children, ...props }: HStackProps,
{ className, space, children, alignItems = 'center', ...props }: HStackProps,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ref: ForwardedRef<any>,
) {
@@ -27,6 +27,7 @@ export const HStack = forwardRef(function HStack(
<BaseStack
ref={ref}
className={classNames(className, 'flex-row', space != null && gapClasses[space])}
alignItems={alignItems}
{...props}
>
{children}