Tweak settings for release

This commit is contained in:
Gregory Schier
2024-05-30 10:28:59 -07:00
parent 0b4e367dfc
commit a481cf403d
20 changed files with 191 additions and 227 deletions

View File

@@ -23,7 +23,7 @@ use sqlx::{Pool, Sqlite, SqlitePool};
use sqlx::migrate::Migrator;
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::types::Json;
use tauri::{AppHandle, RunEvent, State, WebviewUrl, WebviewWindow};
use tauri::{AppHandle, LogicalSize, RunEvent, State, WebviewUrl, WebviewWindow};
use tauri::{Manager, WindowEvent};
use tauri::path::BaseDirectory;
#[cfg(target_os = "macos")]
@@ -74,6 +74,9 @@ mod tauri_plugin_mac_window;
#[cfg(target_os = "windows")]
mod tauri_plugin_windows_window;
const DEFAULT_WINDOW_WIDTH: i32 = 1100;
const DEFAULT_WINDOW_HEIGHT: i32 = 600;
async fn migrate_db(app_handle: &AppHandle, db: &Mutex<Pool<Sqlite>>) -> Result<(), String> {
let pool = &*db.lock().await;
let p = app_handle
@@ -1784,41 +1787,6 @@ fn create_nested_window(window: &WebviewWindow, label: &str, url: &str, title: &
let win = win_builder.build().expect("failed to build window");
// Tauri doesn't support shadows when hiding decorations, so we add our own
// #[cfg(any(windows, target_os = "macos"))]
// set_shadow(&win, true).unwrap();
let win2 = win.clone();
win.on_menu_event(move |w, event| {
if !w.is_focused().unwrap() {
return;
}
match event.id().0.as_str() {
"quit" => exit(0),
"close" => _ = w.close(),
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
"zoom_in" => w.emit("zoom_in", true).unwrap(),
"zoom_out" => w.emit("zoom_out", true).unwrap(),
"settings" => w.emit("settings", true).unwrap(),
"refresh" => win2.eval("location.reload()").unwrap(),
"open_feedback" => {
_ = win2
.app_handle()
.shell()
.open("https://yaak.canny.io", None)
}
"toggle_devtools" => {
if win2.is_devtools_open() {
win2.close_devtools();
} else {
win2.open_devtools();
}
}
_ => {}
}
});
win
}
@@ -1840,7 +1808,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
.resizable(true)
.fullscreen(false)
.disable_drag_drop_handler() // Required for frontend Dnd on windows
.inner_size(1100.0, 600.0)
.inner_size(DEFAULT_WINDOW_WIDTH as f64, DEFAULT_WINDOW_HEIGHT as f64)
.position(
// Randomly offset so windows don't stack exactly
100.0 + random::<f64>() * 30.0,
@@ -1866,7 +1834,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
let win = win_builder.build().expect("failed to build window");
let win2 = win.clone();
let webview_window = win.clone();
win.on_menu_event(move |w, event| {
if !w.is_focused().unwrap() {
return;
@@ -1874,23 +1842,26 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
match event.id().0.as_str() {
"quit" => exit(0),
"close" => _ = w.close(),
"close" => w.close().unwrap(),
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
"zoom_in" => w.emit("zoom_in", true).unwrap(),
"zoom_out" => w.emit("zoom_out", true).unwrap(),
"settings" => w.emit("settings", true).unwrap(),
"refresh" => win2.eval("location.reload()").unwrap(),
"open_feedback" => {
_ = win2
_ = webview_window
.app_handle()
.shell()
.open("https://yaak.canny.io", None)
}
"toggle_devtools" => {
if win2.is_devtools_open() {
win2.close_devtools();
// Commands for development
"dev.reset_size" => webview_window.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)).unwrap(),
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
"dev.toggle_devtools" => {
if webview_window.is_devtools_open() {
webview_window.close_devtools();
} else {
win2.open_devtools();
webview_window.open_devtools();
}
}
_ => {}

View File

@@ -126,12 +126,14 @@ pub fn app_menu(app_handle: &AppHandle) -> tauri::Result<Menu<Wry>> {
"Develop",
true,
&[
&MenuItemBuilder::with_id("refresh".to_string(), "Refresh")
&MenuItemBuilder::with_id("dev.refresh".to_string(), "Refresh")
.accelerator("CmdOrCtrl+Shift+r")
.build(app_handle)?,
&MenuItemBuilder::with_id("toggle_devtools".to_string(), "Open Devtools")
&MenuItemBuilder::with_id("dev.toggle_devtools".to_string(), "Open Devtools")
.accelerator("CmdOrCtrl+Option+i")
.build(app_handle)?,
&MenuItemBuilder::with_id("dev.reset_size".to_string(), "Reset Size")
.build(app_handle)?,
],
)?,
],

View File

@@ -3,7 +3,7 @@ import { routePaths, useAppRoutes } from '../hooks/useAppRoutes';
import { DefaultLayout } from './DefaultLayout';
import { RedirectToLatestWorkspace } from './RedirectToLatestWorkspace';
import RouteError from './RouteError';
import { SettingsDialog } from './Settings/SettingsDialog';
import { Settings } from './Settings/Settings';
import Workspace from './Workspace';
const router = createBrowserRouter([
@@ -41,7 +41,7 @@ const router = createBrowserRouter([
path: routePaths.workspaceSettings({
workspaceId: ':workspaceId',
}),
element: <SettingsDialog fullscreen />,
element: <Settings />,
},
],
},

View File

@@ -20,7 +20,6 @@ import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
import { settingsQueryKey, useSettings } from '../hooks/useSettings';
import { useSyncThemeToDocument } from '../hooks/useSyncThemeToDocument';
import { useSyncWindowTitle } from '../hooks/useSyncWindowTitle';
import { workspacesQueryKey } from '../hooks/useWorkspaces';
import { useZoom } from '../hooks/useZoom';
import type { Model } from '../lib/models';
@@ -34,7 +33,6 @@ export function GlobalHooks() {
// Other useful things
useSyncThemeToDocument();
useSyncWindowTitle();
useGlobalCommands();
useCommandPalette();
useNotificationToast();

View File

@@ -0,0 +1,47 @@
import { getCurrent } from '@tauri-apps/api/webviewWindow';
import { createGlobalState, useKeyPressEvent } from 'react-use';
import { capitalize } from '../../lib/capitalize';
import { TabContent, Tabs } from '../core/Tabs/Tabs';
import { SettingsAppearance } from './SettingsAppearance';
import { SettingsGeneral } from './SettingsGeneral';
enum Tab {
General = 'general',
Appearance = 'appearance',
}
const tabs = [Tab.General, Tab.Appearance];
const useTabState = createGlobalState<string>(tabs[0]!);
export const Settings = () => {
const [tab, setTab] = useTabState();
// 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', () => getCurrent().close());
return (
<>
<div
data-tauri-drag-region
className="h-[27px] bg-background-highlight-secondary flex items-center justify-center border-b border-background-highlight"
>
Settings
</div>
<Tabs
value={tab}
addBorders
label="Settings"
onChangeValue={setTab}
tabs={tabs.map((value) => ({ value, label: capitalize(value) }))}
>
<TabContent value={Tab.General} className="pt-3 overflow-y-auto h-full px-4">
<SettingsGeneral />
</TabContent>
<TabContent value={Tab.Appearance} className="pt-3 overflow-y-auto h-full px-4">
<SettingsAppearance />
</TabContent>
</Tabs>
</>
);
};

View File

@@ -5,19 +5,23 @@ import { useResolvedTheme } from '../../hooks/useResolvedTheme';
import { useSettings } from '../../hooks/useSettings';
import { useThemes } from '../../hooks/useThemes';
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
import { trackEvent } from '../../lib/analytics';
import { clamp } from '../../lib/clamp';
import { isThemeDark } from '../../lib/theme/window';
import type { ButtonProps } from '../core/Button';
import { Checkbox } from '../core/Checkbox';
import { Editor } from '../core/Editor';
import type { IconProps } from '../core/Icon';
import { Icon } from '../core/Icon';
import { IconButton } from '../core/IconButton';
import { PlainInput } from '../core/PlainInput';
import type { SelectOption } from '../core/Select';
import { Select } from '../core/Select';
import { Separator } from '../core/Separator';
import { HStack, VStack } from '../core/Stacks';
const fontSizes = [
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
].map((n) => ({ label: `${n}`, value: `${n}` }));
const buttonColors: ButtonProps['color'][] = [
'primary',
'info',
@@ -75,39 +79,28 @@ export function SettingsAppearance() {
return (
<VStack space={2} className="mb-4">
<PlainInput
<Select
size="sm"
name="interfaceFontSize"
label="Font Size"
placeholder="16"
step={0.5}
type="number"
labelPosition="left"
defaultValue={`${settings.interfaceFontSize}`}
validate={(value) => parseInt(value) >= 8 && parseInt(value) <= 30}
onChange={(v) =>
updateSettings.mutate({
...settings,
interfaceFontSize: clamp(parseInt(v) || 16, 8, 30),
})
}
value={`${settings.interfaceFontSize}`}
options={fontSizes}
onChange={(v) => updateSettings.mutate({ interfaceFontSize: parseInt(v) })}
/>
<PlainInput
<Select
size="sm"
name="editorFontSize"
label="Editor Font Size"
placeholder="14"
step={0.5}
type="number"
labelPosition="left"
defaultValue={`${settings.editorFontSize}`}
validate={(value) => parseInt(value) >= 8 && parseInt(value) <= 30}
onChange={(v) =>
updateSettings.mutate({
...settings,
editorFontSize: clamp(parseInt(v) || 14, 8, 30),
})
}
value={`${settings.editorFontSize}`}
options={fontSizes}
onChange={(v) => updateSettings.mutate({ editorFontSize: clamp(parseInt(v) || 14, 8, 30) })}
/>
<Checkbox
checked={settings.editorSoftWrap}
title="Wrap Editor Lines"
onChange={(editorSoftWrap) => updateSettings.mutate({ editorSoftWrap })}
/>
<Separator className="my-4" />
@@ -118,9 +111,8 @@ export function SettingsAppearance() {
labelPosition="left"
size="sm"
value={settings.appearance}
onChange={async (appearance) => {
await updateSettings.mutateAsync({ ...settings, appearance });
trackEvent('setting', 'update', { appearance });
onChange={(appearance) => {
updateSettings.mutateAsync({ appearance });
}}
options={[
{ label: 'Automatic', value: 'system' },
@@ -128,42 +120,41 @@ export function SettingsAppearance() {
{ label: 'Dark', value: 'dark' },
]}
/>
{(settings.appearance === 'system' || settings.appearance === 'light') && (
<Select
name="lightTheme"
label={settings.appearance === 'system' ? 'Light Theme' : 'Theme'}
labelPosition="left"
size="sm"
value={activeTheme.light.id}
options={lightThemes}
onChange={async (themeLight) => {
await updateSettings.mutateAsync({ ...settings, themeLight });
trackEvent('setting', 'update', { themeLight });
}}
/>
)}
{(settings.appearance === 'system' || settings.appearance === 'dark') && (
<Select
name="darkTheme"
label={settings.appearance === 'system' ? 'Dark Theme' : 'Theme'}
labelPosition="left"
size="sm"
value={activeTheme.dark.id}
options={darkThemes}
onChange={async (themeDark) => {
await updateSettings.mutateAsync({ ...settings, themeDark });
trackEvent('setting', 'update', { themeDark });
}}
/>
)}
<HStack space={2}>
{(settings.appearance === 'system' || settings.appearance === 'light') && (
<Select
hideLabel
leftSlot={<Icon icon="sun" />}
name="lightTheme"
label="Light Theme"
size="sm"
value={activeTheme.light.id}
options={lightThemes}
onChange={(themeLight) => updateSettings.mutateAsync({ ...settings, themeLight })}
/>
)}
{(settings.appearance === 'system' || settings.appearance === 'dark') && (
<Select
hideLabel
name="darkTheme"
label="Dark Theme"
leftSlot={<Icon icon="moon" />}
size="sm"
value={activeTheme.dark.id}
options={darkThemes}
onChange={(themeDark) => updateSettings.mutateAsync({ ...settings, themeDark })}
/>
)}
</HStack>
<VStack
space={3}
className="mt-3 w-full bg-background p-3 border border-dashed border-background-highlight rounded overflow-x-auto"
>
<div className="text-fg font-bold">
Theme Preview <span className="text-fg-subtle">({appearance})</span>
</div>
<HStack className="text-fg font-bold" alignItems="center" space={2}>
Theme Preview{' '}
<Icon icon={appearance === 'dark' ? 'moon' : 'sun'} className="text-fg-subtle" />
</HStack>
<HStack space={1.5} alignItems="center" className="w-full">
{buttonColors.map((c, i) => (
<IconButton

View File

@@ -1,61 +0,0 @@
import classNames from 'classnames';
import { createGlobalState } from 'react-use';
import { useAppInfo } from '../../hooks/useAppInfo';
import { capitalize } from '../../lib/capitalize';
import { TabContent, Tabs } from '../core/Tabs/Tabs';
import { SettingsAppearance } from './SettingsAppearance';
import { SettingsDesign } from './SettingsDesign';
import { SettingsGeneral } from './SettingsGeneral';
enum Tab {
General = 'general',
Appearance = 'appearance',
// Dev-only
Design = 'design',
}
const tabs = [Tab.General, Tab.Appearance, Tab.Design];
const useTabState = createGlobalState<string>(tabs[0]!);
interface Props {
fullscreen?: true;
}
export const SettingsDialog = ({ fullscreen }: Props) => {
const [tab, setTab] = useTabState();
const appInfo = useAppInfo();
const isDev = appInfo?.isDev ?? false;
return (
<div className={classNames(!fullscreen && 'w-[70vw] max-w-[40rem] h-[80vh]')}>
{fullscreen && (
<div
data-tauri-drag-region
className="h-[27px] bg-background-highlight-secondary flex items-center justify-center border-b border-background-highlight"
>
Settings
</div>
)}
<Tabs
value={tab}
addBorders
label="Settings"
onChangeValue={setTab}
tabs={tabs
.filter((t) => t !== Tab.Design || isDev)
.map((value) => ({ value, label: capitalize(value) }))}
>
<TabContent value={Tab.General} className="pt-3 overflow-y-auto h-full px-4">
<SettingsGeneral />
</TabContent>
<TabContent value={Tab.Appearance} className="pt-3 overflow-y-auto h-full px-4">
<SettingsAppearance />
</TabContent>
<TabContent value={Tab.Design} className="pt-3 overflow-y-auto h-full px-4">
<SettingsDesign />
</TabContent>
</Tabs>
</div>
);
};

View File

@@ -5,7 +5,6 @@ import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
import { useSettings } from '../../hooks/useSettings';
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
import { useUpdateWorkspace } from '../../hooks/useUpdateWorkspace';
import { trackEvent } from '../../lib/analytics';
import { Checkbox } from '../core/Checkbox';
import { Heading } from '../core/Heading';
import { IconButton } from '../core/IconButton';
@@ -36,10 +35,7 @@ export function SettingsGeneral() {
labelPosition="left"
size="sm"
value={settings.updateChannel}
onChange={(updateChannel) => {
trackEvent('setting', 'update', { update_channel: updateChannel });
updateSettings.mutate({ ...settings, updateChannel });
}}
onChange={(updateChannel) => updateSettings.mutate({ updateChannel })}
options={[
{ label: 'Release', value: 'stable' },
{ label: 'Early Bird (Beta)', value: 'beta' },
@@ -77,23 +73,15 @@ export function SettingsGeneral() {
<Checkbox
checked={workspace.settingValidateCertificates}
title="Validate TLS Certificates"
onChange={(settingValidateCertificates) => {
trackEvent('workspace', 'update', {
validate_certificates: JSON.stringify(settingValidateCertificates),
});
updateWorkspace.mutate({ settingValidateCertificates });
}}
onChange={(settingValidateCertificates) =>
updateWorkspace.mutate({ settingValidateCertificates })
}
/>
<Checkbox
checked={workspace.settingFollowRedirects}
title="Follow Redirects"
onChange={(settingFollowRedirects) => {
trackEvent('workspace', 'update', {
follow_redirects: JSON.stringify(settingFollowRedirects),
});
updateWorkspace.mutate({ settingFollowRedirects });
}}
onChange={(settingFollowRedirects) => updateWorkspace.mutate({ settingFollowRedirects })}
/>
</VStack>

View File

@@ -133,27 +133,31 @@ export function Sidebar({ className }: Props) {
) {
selectedRequest = node.item;
}
const childItems = [...requests, ...folders].filter((f) =>
node.item.model === 'workspace' ? f.folderId == null : f.folderId === node.item.id,
);
childItems.sort((a, b) => a.sortPriority - b.sortPriority);
// Recurse to children
const isCollapsed = collapsed.value?.[node.item.id];
const depth = node.depth + 1;
childItems.sort((a, b) => a.sortPriority - b.sortPriority);
for (const item of childItems) {
treeParentMap[item.id] = node;
// Add to children
node.children.push(next({ item, children: [], depth }));
if (item.model !== 'folder') {
// Add to selectable requests
if (item.model !== 'folder' && !isCollapsed) {
selectableRequests.push({ id: item.id, index: selectableRequestIndex++, tree: node });
}
}
return node;
};
const tree = next({ item: activeWorkspace, children: [], depth: 0 });
return { tree, treeParentMap, selectableRequests, selectedRequest };
}, [activeWorkspace, selectedId, requests, folders]);
}, [activeWorkspace, selectedId, requests, folders, collapsed.value]);
const deleteSelectedRequest = useDeleteRequest(selectedRequest);
@@ -455,6 +459,7 @@ export function Sidebar({ className }: Props) {
/>
<SidebarItems
treeParentMap={treeParentMap}
activeId={activeRequest?.id ?? null}
selectedId={selectedId}
selectedTree={selectedTree}
isCollapsed={isCollapsed}
@@ -476,6 +481,7 @@ interface SidebarItemsProps {
tree: TreeNode;
focused: boolean;
draggingId: string | null;
activeId: string | null;
selectedId: string | null;
selectedTree: TreeNode | null;
treeParentMap: Record<string, TreeNode>;
@@ -491,6 +497,7 @@ interface SidebarItemsProps {
function SidebarItems({
tree,
focused,
activeId,
selectedId,
selectedTree,
draggingId,
@@ -517,6 +524,7 @@ function SidebarItems({
>
{tree.children.map((child, i) => {
const selected = selectedId === child.item.id;
const active = activeId === child.item.id;
return (
<Fragment key={child.item.id}>
{hoveredIndex === i && hoveredTree?.item.id === tree.item.id && <DropMarker />}
@@ -535,7 +543,7 @@ function SidebarItems({
(child.item.model === 'http_request' || child.item.model === 'grpc_request') && (
<HttpMethodTag
request={child.item}
className={classNames(!selected && 'text-fg-subtler')}
className={classNames(!(active || selected) && 'text-fg-subtler')}
/>
)
}
@@ -558,6 +566,7 @@ function SidebarItems({
hoveredTree={hoveredTree}
hoveredIndex={hoveredIndex}
focused={focused}
activeId={activeId}
selectedId={selectedId}
selectedTree={selectedTree}
onSelect={onSelect}

View File

@@ -18,6 +18,7 @@ import { useOsInfo } from '../hooks/useOsInfo';
import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar';
import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { useSidebarWidth } from '../hooks/useSidebarWidth';
import { useSyncWorkspaceRequestTitle } from '../hooks/useSyncWorkspaceRequestTitle';
import { useWorkspaces } from '../hooks/useWorkspaces';
import { Banner } from './core/Banner';
import { Button } from './core/Button';
@@ -40,6 +41,7 @@ const body = { gridArea: 'body' };
const drag = { gridArea: 'drag' };
export default function Workspace() {
useSyncWorkspaceRequestTitle();
const workspaces = useWorkspaces();
const activeWorkspace = useActiveWorkspace();
const activeWorkspaceId = useActiveWorkspaceId();

View File

@@ -373,7 +373,6 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
bottom: upsideDown ? docRect.height - top : undefined,
right: onRight ? docRect.width - triggerShape?.right : undefined,
left: !onRight ? triggerShape?.left : undefined,
width: containerWidth ?? 'auto',
};
const size = { top: '-0.2rem', width: '0.4rem', height: '0.4rem' };
const triangleStyles = onRight
@@ -448,7 +447,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
<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-2xs"
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" />
<div className="text-fg">{filter}</div>

View File

@@ -17,6 +17,7 @@ import {
} from 'react';
import { useActiveEnvironment } from '../../../hooks/useActiveEnvironment';
import { useActiveWorkspace } from '../../../hooks/useActiveWorkspace';
import { useSettings } from '../../../hooks/useSettings';
import { IconButton } from '../IconButton';
import { HStack } from '../Stacks';
import './Editor.css';
@@ -85,11 +86,16 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
}: EditorProps,
ref,
) {
const s = useSettings();
const e = useActiveEnvironment();
const w = useActiveWorkspace();
const environment = autocompleteVariables ? e : null;
const workspace = autocompleteVariables ? w : null;
if (s && wrapLines === undefined) {
wrapLines = s.editorSoftWrap;
}
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
useImperativeHandle(ref, () => cm.current?.view);

View File

@@ -42,6 +42,7 @@ const icons = {
magicWand: lucide.Wand2Icon,
minus: lucide.MinusIcon,
moreVertical: lucide.MoreVerticalIcon,
moon: lucide.MoonIcon,
paste: lucide.ClipboardPasteIcon,
pencil: lucide.PencilIcon,
pin: lucide.PinIcon,
@@ -55,6 +56,7 @@ const icons = {
settings2: lucide.Settings2Icon,
settings: lucide.SettingsIcon,
sparkles: lucide.SparklesIcon,
sun: lucide.SunIcon,
trash: lucide.TrashIcon,
update: lucide.RefreshCcwIcon,
upload: lucide.UploadIcon,

View File

@@ -1,5 +1,7 @@
import classNames from 'classnames';
import type { CSSProperties } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { useState } from 'react';
import { HStack } from './Stacks';
interface Props<T extends string> {
name: string;
@@ -8,6 +10,7 @@ interface Props<T extends string> {
labelClassName?: string;
hideLabel?: boolean;
value: T;
leftSlot?: ReactNode;
options: SelectOption<T>[];
onChange: (value: T) => void;
size?: 'xs' | 'sm' | 'md' | 'lg';
@@ -27,10 +30,12 @@ export function Select<T extends string>({
label,
value,
options,
leftSlot,
onChange,
className,
size = 'md',
}: Props<T>) {
const [focused, setFocused] = useState<boolean>(false);
const id = `input-${name}`;
return (
<div
@@ -49,25 +54,36 @@ export function Select<T extends string>({
>
{label}
</label>
<select
value={value}
style={selectBackgroundStyles}
onChange={(e) => onChange(e.target.value as T)}
<HStack
space={2}
alignItems="center"
className={classNames(
'font-mono text-sm border w-full outline-none bg-transparent pl-2 pr-7',
'bg-background-highlight-secondary border-background-highlight focus:border-border-focus',
'w-full rounded-md text-fg text-sm font-mono',
'pl-2',
'bg-background-highlight-secondary border',
focused ? 'border-border-focus' : 'border-background-highlight',
size === 'xs' && 'h-xs',
size === 'sm' && 'h-sm',
size === 'md' && 'h-md',
size === 'lg' && 'h-lg',
)}
>
{options.map(({ label, value }) => (
<option key={label} value={value}>
{label}
</option>
))}
</select>
{leftSlot && <div>{leftSlot}</div>}
<select
value={value}
style={selectBackgroundStyles}
onChange={(e) => onChange(e.target.value as T)}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
className={classNames('pr-7 w-full outline-none bg-transparent')}
>
{options.map(({ label, value }) => (
<option key={label} value={value}>
{label}
</option>
))}
</select>
</HStack>
</div>
);
}

View File

@@ -12,7 +12,6 @@ export function useSettings() {
queryKey: settingsQueryKey(),
queryFn: async () => {
const settings = (await invoke('cmd_get_settings')) as Settings;
console.log('SETTINGS', settings);
return [settings];
},
}).data?.[0] ?? undefined

View File

@@ -7,7 +7,7 @@ import { useActiveWorkspace } from './useActiveWorkspace';
import { useOsInfo } from './useOsInfo';
import { emit } from '@tauri-apps/api/event';
export function useSyncWindowTitle() {
export function useSyncWorkspaceRequestTitle() {
const activeRequest = useActiveRequest();
const activeWorkspace = useActiveWorkspace();
const activeEnvironment = useActiveEnvironment();

View File

@@ -1,17 +1,16 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api/core';
import type { Settings } from '../lib/models';
import { settingsQueryKey } from './useSettings';
import { useSettings } from './useSettings';
export function useUpdateSettings() {
const queryClient = useQueryClient();
const settings = useSettings();
return useMutation<void, unknown, Settings>({
mutationFn: async (settings) => {
await invoke('cmd_update_settings', { settings });
},
onMutate: async (settings) => {
queryClient.setQueryData<Settings[]>(settingsQueryKey(), [settings]);
return useMutation<void, unknown, Partial<Settings>>({
mutationFn: async (patch) => {
if (settings == null) return;
const newSettings: Settings = { ...settings, ...patch };
await invoke('cmd_update_settings', { settings: newSettings });
},
});
}

View File

@@ -9,7 +9,6 @@ export function useZoom() {
const zoomIn = useCallback(() => {
if (!settings) return;
updateSettings.mutate({
...settings,
interfaceScale: Math.min(1.8, settings.interfaceScale * 1.1),
});
}, [settings, updateSettings]);
@@ -17,13 +16,11 @@ export function useZoom() {
const zoomOut = useCallback(() => {
if (!settings) return;
updateSettings.mutate({
...settings,
interfaceScale: Math.max(0.4, settings.interfaceScale * 0.9),
});
}, [settings, updateSettings]);
const zoomReset = useCallback(() => {
if (!settings) return;
updateSettings.mutate({ ...settings, interfaceScale: 1 });
}, [settings, updateSettings]);

View File

@@ -40,7 +40,7 @@ export interface Settings extends BaseModel {
interfaceFontSize: number;
interfaceScale: number;
editorFontSize: number;
editorSoftWrap: number;
editorSoftWrap: boolean;
}
export interface Workspace extends BaseModel {

View File

@@ -1,10 +1,9 @@
const plugin = require('tailwindcss/plugin');
const height = {
'2xs': '1.6rem',
xs: '1.8rem',
sm: '2.2rem',
md: '2.7rem',
sm: '2.0rem',
md: '2.5rem',
};
/** @type {import("tailwindcss").Config} */