mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-21 08:59:07 +01:00
Always show window controls, and open Linux settings in dialog
This commit is contained in:
@@ -1,27 +1,31 @@
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import { useIsFullscreen } from '../hooks/useIsFullscreen';
|
||||
import { useOsInfo } from '../hooks/useOsInfo';
|
||||
import React from 'react';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
import { useStoplightsVisible } from '../hooks/useStoplightsVisible';
|
||||
import { WINDOW_CONTROLS_WIDTH, WindowControls } from './WindowControls';
|
||||
|
||||
interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: ReactNode;
|
||||
size: 'md' | 'lg';
|
||||
ignoreStoplights?: boolean;
|
||||
ignoreControlsSpacing?: boolean;
|
||||
onlyXWindowControl?: boolean;
|
||||
}
|
||||
|
||||
export const HEADER_SIZE_MD = '27px';
|
||||
export const HEADER_SIZE_LG = '38px';
|
||||
|
||||
export function HeaderSize({
|
||||
className,
|
||||
style,
|
||||
size,
|
||||
ignoreStoplights,
|
||||
...props
|
||||
ignoreControlsSpacing,
|
||||
onlyXWindowControl,
|
||||
children,
|
||||
}: HeaderSizeProps) {
|
||||
const settings = useSettings();
|
||||
const platform = useOsInfo();
|
||||
const fullscreen = useIsFullscreen();
|
||||
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen && !ignoreStoplights;
|
||||
const stoplightsVisible = useStoplightsVisible();
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
@@ -33,18 +37,23 @@ export function HeaderSize({
|
||||
...style,
|
||||
// Add padding for macOS stoplights, but keep it the same width (account for the interface scale)
|
||||
paddingLeft: stoplightsVisible ? 72 / settings.interfaceScale : undefined,
|
||||
...(size === 'md' ? { height: HEADER_SIZE_MD } : {}),
|
||||
...(size === 'lg' ? { height: HEADER_SIZE_LG } : {}),
|
||||
...(stoplightsVisible || ignoreControlsSpacing
|
||||
? { paddingRight: '2px' }
|
||||
: { paddingLeft: '2px', paddingRight: WINDOW_CONTROLS_WIDTH }),
|
||||
}}
|
||||
className={classNames(
|
||||
className,
|
||||
'select-none',
|
||||
'select-none relative',
|
||||
'pt-[1px] w-full border-b border-border-subtle min-w-0',
|
||||
stoplightsVisible ? '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 className="pointer-events-none h-full w-full overflow-x-auto hide-scrollbars grid">
|
||||
{children}
|
||||
</div>
|
||||
<WindowControls onlyX={onlyXWindowControl} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,8 +86,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
|
||||
hotkeyAction="request_switcher.toggle"
|
||||
className={classNames(
|
||||
className,
|
||||
'max-w-[40vw]',
|
||||
'text truncate pointer-events-auto',
|
||||
'truncate pointer-events-auto',
|
||||
activeRequest === null && 'text-text-subtlest italic',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -7,11 +7,14 @@ 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';
|
||||
import { SettingsPlugins } from './SettingsPlugins';
|
||||
|
||||
interface Props {
|
||||
hide?: () => void;
|
||||
}
|
||||
|
||||
enum Tab {
|
||||
General = 'general',
|
||||
Appearance = 'appearance',
|
||||
@@ -20,33 +23,45 @@ enum Tab {
|
||||
|
||||
const tabs = [Tab.General, Tab.Appearance, Tab.Plugins];
|
||||
|
||||
export default function Settings() {
|
||||
export default function Settings({ hide }: Props) {
|
||||
const osInfo = useOsInfo();
|
||||
const [tab, setTab] = useState<string>(Tab.General);
|
||||
|
||||
// Close settings window on escape
|
||||
// TODO: Could this be put in a better place? Eg. in Rust key listener when creating the window
|
||||
useKeyPressEvent('Escape', () => getCurrentWebviewWindow().close());
|
||||
useKeyPressEvent('Escape', async () => {
|
||||
if (hide != null) {
|
||||
// It's being shown in a dialog, so close the dialog
|
||||
hide();
|
||||
} else {
|
||||
// It's being shown in a window, so close the window
|
||||
await getCurrentWebviewWindow().close();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classNames('grid grid-rows-[auto_minmax(0,1fr)] h-full')}>
|
||||
<HeaderSize
|
||||
data-tauri-drag-region
|
||||
ignoreStoplights
|
||||
size="md"
|
||||
className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold"
|
||||
>
|
||||
<HStack
|
||||
space={2}
|
||||
justifyContent="center"
|
||||
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
|
||||
{hide ? (
|
||||
<span />
|
||||
) : (
|
||||
<HeaderSize
|
||||
data-tauri-drag-region
|
||||
ignoreControlsSpacing
|
||||
onlyXWindowControl
|
||||
size="md"
|
||||
className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold"
|
||||
>
|
||||
<div className={classNames(osInfo?.osType === 'macos' ? 'text-center' : 'pl-2')}>
|
||||
Settings
|
||||
</div>
|
||||
<WindowControls className="ml-auto" onlyX />
|
||||
</HStack>
|
||||
</HeaderSize>
|
||||
<HStack
|
||||
space={2}
|
||||
justifyContent="center"
|
||||
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
|
||||
>
|
||||
<div className={classNames(osInfo?.osType === 'macos' ? 'text-center' : 'pl-2')}>
|
||||
Settings
|
||||
</div>
|
||||
</HStack>
|
||||
</HeaderSize>
|
||||
)}
|
||||
<Tabs
|
||||
value={tab}
|
||||
addBorders
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import { useOsInfo } from '../hooks/useOsInfo';
|
||||
import { useStoplightsVisible } from '../hooks/useStoplightsVisible';
|
||||
import { Button } from './core/Button';
|
||||
import { HStack } from './core/Stacks';
|
||||
|
||||
@@ -10,16 +10,21 @@ interface Props {
|
||||
onlyX?: boolean;
|
||||
}
|
||||
|
||||
export const WINDOW_CONTROLS_WIDTH = '10.5rem';
|
||||
|
||||
export function WindowControls({ className, onlyX }: Props) {
|
||||
const [maximized, setMaximized] = useState<boolean>(false);
|
||||
const osInfo = useOsInfo();
|
||||
const shouldShow = osInfo?.osType === 'linux' || osInfo?.osType === 'windows';
|
||||
if (!shouldShow) {
|
||||
const stoplightsVisible = useStoplightsVisible();
|
||||
if (stoplightsVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<HStack className={classNames(className, 'ml-4 h-full')}>
|
||||
<HStack
|
||||
className={classNames(className, 'ml-4 absolute right-0 top-0 bottom-0')}
|
||||
justifyContent="end"
|
||||
style={{ width: WINDOW_CONTROLS_WIDTH }}
|
||||
>
|
||||
{!onlyX && (
|
||||
<>
|
||||
<Button
|
||||
@@ -57,7 +62,7 @@ export function WindowControls({ className, onlyX }: Props) {
|
||||
)}
|
||||
<Button
|
||||
color="custom"
|
||||
className="!h-full px-4 text-text-subtle rounded-none hocus:bg-danger hocus:text"
|
||||
className="!h-full px-4 text-text-subtle rounded-none hocus:bg-danger hocus:text-text"
|
||||
onClick={() => getCurrentWebviewWindow().close()}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
|
||||
@@ -10,7 +10,6 @@ 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 {
|
||||
@@ -20,17 +19,22 @@ interface Props {
|
||||
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
||||
const togglePalette = useToggleCommandPalette();
|
||||
return (
|
||||
<HStack space={2} justifyContent="center" className={classNames(className, 'w-full h-full')}>
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
'grid grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center w-full h-full',
|
||||
)}
|
||||
>
|
||||
<HStack space={0.5} className="flex-1 pointer-events-none">
|
||||
<SidebarActions />
|
||||
<CookieDropdown />
|
||||
<HStack>
|
||||
<HStack className="min-w-0">
|
||||
<WorkspaceActionsDropdown />
|
||||
<Icon icon="chevron_right" className="text-text-subtle" />
|
||||
<EnvironmentActionsDropdown className="w-auto pointer-events-auto" />
|
||||
</HStack>
|
||||
</HStack>
|
||||
<div className="pointer-events-none">
|
||||
<div className="pointer-events-none w-full max-w-[30vw] mx-auto">
|
||||
<RecentRequestsDropdown />
|
||||
</div>
|
||||
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-0.5">
|
||||
@@ -42,8 +46,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
||||
onClick={togglePalette}
|
||||
/>
|
||||
<SettingsDropdown />
|
||||
<WindowControls />
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useDialog } from '../components/DialogContext';
|
||||
import Settings from '../components/Settings/Settings';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { useActiveWorkspace } from './useActiveWorkspace';
|
||||
import { useAppRoutes } from './useAppRoutes';
|
||||
import { useOsInfo } from './useOsInfo';
|
||||
|
||||
export function useOpenSettings() {
|
||||
const routes = useAppRoutes();
|
||||
const workspace = useActiveWorkspace();
|
||||
const dialog = useDialog();
|
||||
const { osType } = useOsInfo();
|
||||
return useMutation({
|
||||
mutationKey: ['open_settings'],
|
||||
mutationFn: async () => {
|
||||
if (workspace == null) return;
|
||||
|
||||
// HACK: Show settings in dialog on Linux until this is fixed: https://github.com/tauri-apps/tauri/issues/11171
|
||||
if (osType === 'linux') {
|
||||
dialog.show({
|
||||
id: 'settings',
|
||||
size: 'lg',
|
||||
render: ({ hide }) => <Settings hide={hide} />,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await invokeCmd('cmd_new_child_window', {
|
||||
url: routes.paths.workspaceSettings({ workspaceId: workspace.id }),
|
||||
label: 'settings',
|
||||
9
src-web/hooks/useStoplightsVisible.ts
Normal file
9
src-web/hooks/useStoplightsVisible.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { useIsFullscreen } from './useIsFullscreen';
|
||||
import { useOsInfo } from './useOsInfo';
|
||||
|
||||
export function useStoplightsVisible() {
|
||||
const platform = useOsInfo();
|
||||
const fullscreen = useIsFullscreen();
|
||||
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen;
|
||||
return stoplightsVisible;
|
||||
}
|
||||
Reference in New Issue
Block a user