mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-19 07:19:45 +02:00
Tweak settings for release
This commit is contained in:
@@ -23,7 +23,7 @@ use sqlx::{Pool, Sqlite, SqlitePool};
|
|||||||
use sqlx::migrate::Migrator;
|
use sqlx::migrate::Migrator;
|
||||||
use sqlx::sqlite::SqliteConnectOptions;
|
use sqlx::sqlite::SqliteConnectOptions;
|
||||||
use sqlx::types::Json;
|
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::{Manager, WindowEvent};
|
||||||
use tauri::path::BaseDirectory;
|
use tauri::path::BaseDirectory;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -74,6 +74,9 @@ mod tauri_plugin_mac_window;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod tauri_plugin_windows_window;
|
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> {
|
async fn migrate_db(app_handle: &AppHandle, db: &Mutex<Pool<Sqlite>>) -> Result<(), String> {
|
||||||
let pool = &*db.lock().await;
|
let pool = &*db.lock().await;
|
||||||
let p = app_handle
|
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");
|
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
|
win
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1840,7 +1808,7 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
|||||||
.resizable(true)
|
.resizable(true)
|
||||||
.fullscreen(false)
|
.fullscreen(false)
|
||||||
.disable_drag_drop_handler() // Required for frontend Dnd on windows
|
.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(
|
.position(
|
||||||
// Randomly offset so windows don't stack exactly
|
// Randomly offset so windows don't stack exactly
|
||||||
100.0 + random::<f64>() * 30.0,
|
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 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| {
|
win.on_menu_event(move |w, event| {
|
||||||
if !w.is_focused().unwrap() {
|
if !w.is_focused().unwrap() {
|
||||||
return;
|
return;
|
||||||
@@ -1874,23 +1842,26 @@ fn create_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
|||||||
|
|
||||||
match event.id().0.as_str() {
|
match event.id().0.as_str() {
|
||||||
"quit" => exit(0),
|
"quit" => exit(0),
|
||||||
"close" => _ = w.close(),
|
"close" => w.close().unwrap(),
|
||||||
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
||||||
"zoom_in" => w.emit("zoom_in", true).unwrap(),
|
"zoom_in" => w.emit("zoom_in", true).unwrap(),
|
||||||
"zoom_out" => w.emit("zoom_out", true).unwrap(),
|
"zoom_out" => w.emit("zoom_out", true).unwrap(),
|
||||||
"settings" => w.emit("settings", true).unwrap(),
|
"settings" => w.emit("settings", true).unwrap(),
|
||||||
"refresh" => win2.eval("location.reload()").unwrap(),
|
|
||||||
"open_feedback" => {
|
"open_feedback" => {
|
||||||
_ = win2
|
_ = webview_window
|
||||||
.app_handle()
|
.app_handle()
|
||||||
.shell()
|
.shell()
|
||||||
.open("https://yaak.canny.io", None)
|
.open("https://yaak.canny.io", None)
|
||||||
}
|
}
|
||||||
"toggle_devtools" => {
|
|
||||||
if win2.is_devtools_open() {
|
// Commands for development
|
||||||
win2.close_devtools();
|
"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 {
|
} else {
|
||||||
win2.open_devtools();
|
webview_window.open_devtools();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
@@ -126,12 +126,14 @@ pub fn app_menu(app_handle: &AppHandle) -> tauri::Result<Menu<Wry>> {
|
|||||||
"Develop",
|
"Develop",
|
||||||
true,
|
true,
|
||||||
&[
|
&[
|
||||||
&MenuItemBuilder::with_id("refresh".to_string(), "Refresh")
|
&MenuItemBuilder::with_id("dev.refresh".to_string(), "Refresh")
|
||||||
.accelerator("CmdOrCtrl+Shift+r")
|
.accelerator("CmdOrCtrl+Shift+r")
|
||||||
.build(app_handle)?,
|
.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")
|
.accelerator("CmdOrCtrl+Option+i")
|
||||||
.build(app_handle)?,
|
.build(app_handle)?,
|
||||||
|
&MenuItemBuilder::with_id("dev.reset_size".to_string(), "Reset Size")
|
||||||
|
.build(app_handle)?,
|
||||||
],
|
],
|
||||||
)?,
|
)?,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { routePaths, useAppRoutes } from '../hooks/useAppRoutes';
|
|||||||
import { DefaultLayout } from './DefaultLayout';
|
import { DefaultLayout } from './DefaultLayout';
|
||||||
import { RedirectToLatestWorkspace } from './RedirectToLatestWorkspace';
|
import { RedirectToLatestWorkspace } from './RedirectToLatestWorkspace';
|
||||||
import RouteError from './RouteError';
|
import RouteError from './RouteError';
|
||||||
import { SettingsDialog } from './Settings/SettingsDialog';
|
import { Settings } from './Settings/Settings';
|
||||||
import Workspace from './Workspace';
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
@@ -41,7 +41,7 @@ const router = createBrowserRouter([
|
|||||||
path: routePaths.workspaceSettings({
|
path: routePaths.workspaceSettings({
|
||||||
workspaceId: ':workspaceId',
|
workspaceId: ':workspaceId',
|
||||||
}),
|
}),
|
||||||
element: <SettingsDialog fullscreen />,
|
element: <Settings />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
|
|||||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||||
import { settingsQueryKey, useSettings } from '../hooks/useSettings';
|
import { settingsQueryKey, useSettings } from '../hooks/useSettings';
|
||||||
import { useSyncThemeToDocument } from '../hooks/useSyncThemeToDocument';
|
import { useSyncThemeToDocument } from '../hooks/useSyncThemeToDocument';
|
||||||
import { useSyncWindowTitle } from '../hooks/useSyncWindowTitle';
|
|
||||||
import { workspacesQueryKey } from '../hooks/useWorkspaces';
|
import { workspacesQueryKey } from '../hooks/useWorkspaces';
|
||||||
import { useZoom } from '../hooks/useZoom';
|
import { useZoom } from '../hooks/useZoom';
|
||||||
import type { Model } from '../lib/models';
|
import type { Model } from '../lib/models';
|
||||||
@@ -34,7 +33,6 @@ export function GlobalHooks() {
|
|||||||
|
|
||||||
// Other useful things
|
// Other useful things
|
||||||
useSyncThemeToDocument();
|
useSyncThemeToDocument();
|
||||||
useSyncWindowTitle();
|
|
||||||
useGlobalCommands();
|
useGlobalCommands();
|
||||||
useCommandPalette();
|
useCommandPalette();
|
||||||
useNotificationToast();
|
useNotificationToast();
|
||||||
|
|||||||
47
src-web/components/Settings/Settings.tsx
Normal file
47
src-web/components/Settings/Settings.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -5,19 +5,23 @@ import { useResolvedTheme } from '../../hooks/useResolvedTheme';
|
|||||||
import { useSettings } from '../../hooks/useSettings';
|
import { useSettings } from '../../hooks/useSettings';
|
||||||
import { useThemes } from '../../hooks/useThemes';
|
import { useThemes } from '../../hooks/useThemes';
|
||||||
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
|
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import { clamp } from '../../lib/clamp';
|
import { clamp } from '../../lib/clamp';
|
||||||
import { isThemeDark } from '../../lib/theme/window';
|
import { isThemeDark } from '../../lib/theme/window';
|
||||||
import type { ButtonProps } from '../core/Button';
|
import type { ButtonProps } from '../core/Button';
|
||||||
|
import { Checkbox } from '../core/Checkbox';
|
||||||
import { Editor } from '../core/Editor';
|
import { Editor } from '../core/Editor';
|
||||||
import type { IconProps } from '../core/Icon';
|
import type { IconProps } from '../core/Icon';
|
||||||
|
import { Icon } from '../core/Icon';
|
||||||
import { IconButton } from '../core/IconButton';
|
import { IconButton } from '../core/IconButton';
|
||||||
import { PlainInput } from '../core/PlainInput';
|
|
||||||
import type { SelectOption } from '../core/Select';
|
import type { SelectOption } from '../core/Select';
|
||||||
import { Select } from '../core/Select';
|
import { Select } from '../core/Select';
|
||||||
import { Separator } from '../core/Separator';
|
import { Separator } from '../core/Separator';
|
||||||
import { HStack, VStack } from '../core/Stacks';
|
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'][] = [
|
const buttonColors: ButtonProps['color'][] = [
|
||||||
'primary',
|
'primary',
|
||||||
'info',
|
'info',
|
||||||
@@ -75,39 +79,28 @@ export function SettingsAppearance() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack space={2} className="mb-4">
|
<VStack space={2} className="mb-4">
|
||||||
<PlainInput
|
<Select
|
||||||
size="sm"
|
size="sm"
|
||||||
name="interfaceFontSize"
|
name="interfaceFontSize"
|
||||||
label="Font Size"
|
label="Font Size"
|
||||||
placeholder="16"
|
|
||||||
step={0.5}
|
|
||||||
type="number"
|
|
||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
defaultValue={`${settings.interfaceFontSize}`}
|
value={`${settings.interfaceFontSize}`}
|
||||||
validate={(value) => parseInt(value) >= 8 && parseInt(value) <= 30}
|
options={fontSizes}
|
||||||
onChange={(v) =>
|
onChange={(v) => updateSettings.mutate({ interfaceFontSize: parseInt(v) })}
|
||||||
updateSettings.mutate({
|
|
||||||
...settings,
|
|
||||||
interfaceFontSize: clamp(parseInt(v) || 16, 8, 30),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<PlainInput
|
<Select
|
||||||
size="sm"
|
size="sm"
|
||||||
name="editorFontSize"
|
name="editorFontSize"
|
||||||
label="Editor Font Size"
|
label="Editor Font Size"
|
||||||
placeholder="14"
|
|
||||||
step={0.5}
|
|
||||||
type="number"
|
|
||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
defaultValue={`${settings.editorFontSize}`}
|
value={`${settings.editorFontSize}`}
|
||||||
validate={(value) => parseInt(value) >= 8 && parseInt(value) <= 30}
|
options={fontSizes}
|
||||||
onChange={(v) =>
|
onChange={(v) => updateSettings.mutate({ editorFontSize: clamp(parseInt(v) || 14, 8, 30) })}
|
||||||
updateSettings.mutate({
|
/>
|
||||||
...settings,
|
<Checkbox
|
||||||
editorFontSize: clamp(parseInt(v) || 14, 8, 30),
|
checked={settings.editorSoftWrap}
|
||||||
})
|
title="Wrap Editor Lines"
|
||||||
}
|
onChange={(editorSoftWrap) => updateSettings.mutate({ editorSoftWrap })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
@@ -118,9 +111,8 @@ export function SettingsAppearance() {
|
|||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
size="sm"
|
size="sm"
|
||||||
value={settings.appearance}
|
value={settings.appearance}
|
||||||
onChange={async (appearance) => {
|
onChange={(appearance) => {
|
||||||
await updateSettings.mutateAsync({ ...settings, appearance });
|
updateSettings.mutateAsync({ appearance });
|
||||||
trackEvent('setting', 'update', { appearance });
|
|
||||||
}}
|
}}
|
||||||
options={[
|
options={[
|
||||||
{ label: 'Automatic', value: 'system' },
|
{ label: 'Automatic', value: 'system' },
|
||||||
@@ -128,42 +120,41 @@ export function SettingsAppearance() {
|
|||||||
{ label: 'Dark', value: 'dark' },
|
{ label: 'Dark', value: 'dark' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
{(settings.appearance === 'system' || settings.appearance === 'light') && (
|
<HStack space={2}>
|
||||||
<Select
|
{(settings.appearance === 'system' || settings.appearance === 'light') && (
|
||||||
name="lightTheme"
|
<Select
|
||||||
label={settings.appearance === 'system' ? 'Light Theme' : 'Theme'}
|
hideLabel
|
||||||
labelPosition="left"
|
leftSlot={<Icon icon="sun" />}
|
||||||
size="sm"
|
name="lightTheme"
|
||||||
value={activeTheme.light.id}
|
label="Light Theme"
|
||||||
options={lightThemes}
|
size="sm"
|
||||||
onChange={async (themeLight) => {
|
value={activeTheme.light.id}
|
||||||
await updateSettings.mutateAsync({ ...settings, themeLight });
|
options={lightThemes}
|
||||||
trackEvent('setting', 'update', { themeLight });
|
onChange={(themeLight) => updateSettings.mutateAsync({ ...settings, themeLight })}
|
||||||
}}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
{(settings.appearance === 'system' || settings.appearance === 'dark') && (
|
||||||
{(settings.appearance === 'system' || settings.appearance === 'dark') && (
|
<Select
|
||||||
<Select
|
hideLabel
|
||||||
name="darkTheme"
|
name="darkTheme"
|
||||||
label={settings.appearance === 'system' ? 'Dark Theme' : 'Theme'}
|
label="Dark Theme"
|
||||||
labelPosition="left"
|
leftSlot={<Icon icon="moon" />}
|
||||||
size="sm"
|
size="sm"
|
||||||
value={activeTheme.dark.id}
|
value={activeTheme.dark.id}
|
||||||
options={darkThemes}
|
options={darkThemes}
|
||||||
onChange={async (themeDark) => {
|
onChange={(themeDark) => updateSettings.mutateAsync({ ...settings, themeDark })}
|
||||||
await updateSettings.mutateAsync({ ...settings, themeDark });
|
/>
|
||||||
trackEvent('setting', 'update', { themeDark });
|
)}
|
||||||
}}
|
</HStack>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<VStack
|
<VStack
|
||||||
space={3}
|
space={3}
|
||||||
className="mt-3 w-full bg-background p-3 border border-dashed border-background-highlight rounded overflow-x-auto"
|
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">
|
<HStack className="text-fg font-bold" alignItems="center" space={2}>
|
||||||
Theme Preview <span className="text-fg-subtle">({appearance})</span>
|
Theme Preview{' '}
|
||||||
</div>
|
<Icon icon={appearance === 'dark' ? 'moon' : 'sun'} className="text-fg-subtle" />
|
||||||
|
</HStack>
|
||||||
<HStack space={1.5} alignItems="center" className="w-full">
|
<HStack space={1.5} alignItems="center" className="w-full">
|
||||||
{buttonColors.map((c, i) => (
|
{buttonColors.map((c, i) => (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -5,7 +5,6 @@ import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
|
|||||||
import { useSettings } from '../../hooks/useSettings';
|
import { useSettings } from '../../hooks/useSettings';
|
||||||
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
|
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
|
||||||
import { useUpdateWorkspace } from '../../hooks/useUpdateWorkspace';
|
import { useUpdateWorkspace } from '../../hooks/useUpdateWorkspace';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
|
||||||
import { Checkbox } from '../core/Checkbox';
|
import { Checkbox } from '../core/Checkbox';
|
||||||
import { Heading } from '../core/Heading';
|
import { Heading } from '../core/Heading';
|
||||||
import { IconButton } from '../core/IconButton';
|
import { IconButton } from '../core/IconButton';
|
||||||
@@ -36,10 +35,7 @@ export function SettingsGeneral() {
|
|||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
size="sm"
|
size="sm"
|
||||||
value={settings.updateChannel}
|
value={settings.updateChannel}
|
||||||
onChange={(updateChannel) => {
|
onChange={(updateChannel) => updateSettings.mutate({ updateChannel })}
|
||||||
trackEvent('setting', 'update', { update_channel: updateChannel });
|
|
||||||
updateSettings.mutate({ ...settings, updateChannel });
|
|
||||||
}}
|
|
||||||
options={[
|
options={[
|
||||||
{ label: 'Release', value: 'stable' },
|
{ label: 'Release', value: 'stable' },
|
||||||
{ label: 'Early Bird (Beta)', value: 'beta' },
|
{ label: 'Early Bird (Beta)', value: 'beta' },
|
||||||
@@ -77,23 +73,15 @@ export function SettingsGeneral() {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={workspace.settingValidateCertificates}
|
checked={workspace.settingValidateCertificates}
|
||||||
title="Validate TLS Certificates"
|
title="Validate TLS Certificates"
|
||||||
onChange={(settingValidateCertificates) => {
|
onChange={(settingValidateCertificates) =>
|
||||||
trackEvent('workspace', 'update', {
|
updateWorkspace.mutate({ settingValidateCertificates })
|
||||||
validate_certificates: JSON.stringify(settingValidateCertificates),
|
}
|
||||||
});
|
|
||||||
updateWorkspace.mutate({ settingValidateCertificates });
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={workspace.settingFollowRedirects}
|
checked={workspace.settingFollowRedirects}
|
||||||
title="Follow Redirects"
|
title="Follow Redirects"
|
||||||
onChange={(settingFollowRedirects) => {
|
onChange={(settingFollowRedirects) => updateWorkspace.mutate({ settingFollowRedirects })}
|
||||||
trackEvent('workspace', 'update', {
|
|
||||||
follow_redirects: JSON.stringify(settingFollowRedirects),
|
|
||||||
});
|
|
||||||
updateWorkspace.mutate({ settingFollowRedirects });
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
|
|||||||
@@ -133,27 +133,31 @@ export function Sidebar({ className }: Props) {
|
|||||||
) {
|
) {
|
||||||
selectedRequest = node.item;
|
selectedRequest = node.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
const childItems = [...requests, ...folders].filter((f) =>
|
const childItems = [...requests, ...folders].filter((f) =>
|
||||||
node.item.model === 'workspace' ? f.folderId == null : f.folderId === node.item.id,
|
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;
|
const depth = node.depth + 1;
|
||||||
|
childItems.sort((a, b) => a.sortPriority - b.sortPriority);
|
||||||
for (const item of childItems) {
|
for (const item of childItems) {
|
||||||
treeParentMap[item.id] = node;
|
treeParentMap[item.id] = node;
|
||||||
|
// Add to children
|
||||||
node.children.push(next({ item, children: [], depth }));
|
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 });
|
selectableRequests.push({ id: item.id, index: selectableRequestIndex++, tree: node });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tree = next({ item: activeWorkspace, children: [], depth: 0 });
|
const tree = next({ item: activeWorkspace, children: [], depth: 0 });
|
||||||
|
|
||||||
return { tree, treeParentMap, selectableRequests, selectedRequest };
|
return { tree, treeParentMap, selectableRequests, selectedRequest };
|
||||||
}, [activeWorkspace, selectedId, requests, folders]);
|
}, [activeWorkspace, selectedId, requests, folders, collapsed.value]);
|
||||||
|
|
||||||
const deleteSelectedRequest = useDeleteRequest(selectedRequest);
|
const deleteSelectedRequest = useDeleteRequest(selectedRequest);
|
||||||
|
|
||||||
@@ -455,6 +459,7 @@ export function Sidebar({ className }: Props) {
|
|||||||
/>
|
/>
|
||||||
<SidebarItems
|
<SidebarItems
|
||||||
treeParentMap={treeParentMap}
|
treeParentMap={treeParentMap}
|
||||||
|
activeId={activeRequest?.id ?? null}
|
||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
selectedTree={selectedTree}
|
selectedTree={selectedTree}
|
||||||
isCollapsed={isCollapsed}
|
isCollapsed={isCollapsed}
|
||||||
@@ -476,6 +481,7 @@ interface SidebarItemsProps {
|
|||||||
tree: TreeNode;
|
tree: TreeNode;
|
||||||
focused: boolean;
|
focused: boolean;
|
||||||
draggingId: string | null;
|
draggingId: string | null;
|
||||||
|
activeId: string | null;
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
selectedTree: TreeNode | null;
|
selectedTree: TreeNode | null;
|
||||||
treeParentMap: Record<string, TreeNode>;
|
treeParentMap: Record<string, TreeNode>;
|
||||||
@@ -491,6 +497,7 @@ interface SidebarItemsProps {
|
|||||||
function SidebarItems({
|
function SidebarItems({
|
||||||
tree,
|
tree,
|
||||||
focused,
|
focused,
|
||||||
|
activeId,
|
||||||
selectedId,
|
selectedId,
|
||||||
selectedTree,
|
selectedTree,
|
||||||
draggingId,
|
draggingId,
|
||||||
@@ -517,6 +524,7 @@ function SidebarItems({
|
|||||||
>
|
>
|
||||||
{tree.children.map((child, i) => {
|
{tree.children.map((child, i) => {
|
||||||
const selected = selectedId === child.item.id;
|
const selected = selectedId === child.item.id;
|
||||||
|
const active = activeId === child.item.id;
|
||||||
return (
|
return (
|
||||||
<Fragment key={child.item.id}>
|
<Fragment key={child.item.id}>
|
||||||
{hoveredIndex === i && hoveredTree?.item.id === tree.item.id && <DropMarker />}
|
{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') && (
|
(child.item.model === 'http_request' || child.item.model === 'grpc_request') && (
|
||||||
<HttpMethodTag
|
<HttpMethodTag
|
||||||
request={child.item}
|
request={child.item}
|
||||||
className={classNames(!selected && 'text-fg-subtler')}
|
className={classNames(!(active || selected) && 'text-fg-subtler')}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -558,6 +566,7 @@ function SidebarItems({
|
|||||||
hoveredTree={hoveredTree}
|
hoveredTree={hoveredTree}
|
||||||
hoveredIndex={hoveredIndex}
|
hoveredIndex={hoveredIndex}
|
||||||
focused={focused}
|
focused={focused}
|
||||||
|
activeId={activeId}
|
||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
selectedTree={selectedTree}
|
selectedTree={selectedTree}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { useOsInfo } from '../hooks/useOsInfo';
|
|||||||
import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar';
|
import { useShouldFloatSidebar } from '../hooks/useShouldFloatSidebar';
|
||||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||||
import { useSidebarWidth } from '../hooks/useSidebarWidth';
|
import { useSidebarWidth } from '../hooks/useSidebarWidth';
|
||||||
|
import { useSyncWorkspaceRequestTitle } from '../hooks/useSyncWorkspaceRequestTitle';
|
||||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||||
import { Banner } from './core/Banner';
|
import { Banner } from './core/Banner';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
@@ -40,6 +41,7 @@ const body = { gridArea: 'body' };
|
|||||||
const drag = { gridArea: 'drag' };
|
const drag = { gridArea: 'drag' };
|
||||||
|
|
||||||
export default function Workspace() {
|
export default function Workspace() {
|
||||||
|
useSyncWorkspaceRequestTitle();
|
||||||
const workspaces = useWorkspaces();
|
const workspaces = useWorkspaces();
|
||||||
const activeWorkspace = useActiveWorkspace();
|
const activeWorkspace = useActiveWorkspace();
|
||||||
const activeWorkspaceId = useActiveWorkspaceId();
|
const activeWorkspaceId = useActiveWorkspaceId();
|
||||||
|
|||||||
@@ -373,7 +373,6 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
|||||||
bottom: upsideDown ? docRect.height - top : undefined,
|
bottom: upsideDown ? docRect.height - top : undefined,
|
||||||
right: onRight ? docRect.width - triggerShape?.right : undefined,
|
right: onRight ? docRect.width - triggerShape?.right : undefined,
|
||||||
left: !onRight ? triggerShape?.left : undefined,
|
left: !onRight ? triggerShape?.left : undefined,
|
||||||
width: containerWidth ?? 'auto',
|
|
||||||
};
|
};
|
||||||
const size = { top: '-0.2rem', width: '0.4rem', height: '0.4rem' };
|
const size = { top: '-0.2rem', width: '0.4rem', height: '0.4rem' };
|
||||||
const triangleStyles = onRight
|
const triangleStyles = onRight
|
||||||
@@ -448,7 +447,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
|||||||
<HStack
|
<HStack
|
||||||
space={2}
|
space={2}
|
||||||
alignItems="center"
|
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" />
|
<Icon icon="search" size="xs" className="text-fg-subtle" />
|
||||||
<div className="text-fg">{filter}</div>
|
<div className="text-fg">{filter}</div>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { useActiveEnvironment } from '../../../hooks/useActiveEnvironment';
|
import { useActiveEnvironment } from '../../../hooks/useActiveEnvironment';
|
||||||
import { useActiveWorkspace } from '../../../hooks/useActiveWorkspace';
|
import { useActiveWorkspace } from '../../../hooks/useActiveWorkspace';
|
||||||
|
import { useSettings } from '../../../hooks/useSettings';
|
||||||
import { IconButton } from '../IconButton';
|
import { IconButton } from '../IconButton';
|
||||||
import { HStack } from '../Stacks';
|
import { HStack } from '../Stacks';
|
||||||
import './Editor.css';
|
import './Editor.css';
|
||||||
@@ -85,11 +86,16 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
}: EditorProps,
|
}: EditorProps,
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
|
const s = useSettings();
|
||||||
const e = useActiveEnvironment();
|
const e = useActiveEnvironment();
|
||||||
const w = useActiveWorkspace();
|
const w = useActiveWorkspace();
|
||||||
const environment = autocompleteVariables ? e : null;
|
const environment = autocompleteVariables ? e : null;
|
||||||
const workspace = autocompleteVariables ? w : null;
|
const workspace = autocompleteVariables ? w : null;
|
||||||
|
|
||||||
|
if (s && wrapLines === undefined) {
|
||||||
|
wrapLines = s.editorSoftWrap;
|
||||||
|
}
|
||||||
|
|
||||||
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
||||||
useImperativeHandle(ref, () => cm.current?.view);
|
useImperativeHandle(ref, () => cm.current?.view);
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const icons = {
|
|||||||
magicWand: lucide.Wand2Icon,
|
magicWand: lucide.Wand2Icon,
|
||||||
minus: lucide.MinusIcon,
|
minus: lucide.MinusIcon,
|
||||||
moreVertical: lucide.MoreVerticalIcon,
|
moreVertical: lucide.MoreVerticalIcon,
|
||||||
|
moon: lucide.MoonIcon,
|
||||||
paste: lucide.ClipboardPasteIcon,
|
paste: lucide.ClipboardPasteIcon,
|
||||||
pencil: lucide.PencilIcon,
|
pencil: lucide.PencilIcon,
|
||||||
pin: lucide.PinIcon,
|
pin: lucide.PinIcon,
|
||||||
@@ -55,6 +56,7 @@ const icons = {
|
|||||||
settings2: lucide.Settings2Icon,
|
settings2: lucide.Settings2Icon,
|
||||||
settings: lucide.SettingsIcon,
|
settings: lucide.SettingsIcon,
|
||||||
sparkles: lucide.SparklesIcon,
|
sparkles: lucide.SparklesIcon,
|
||||||
|
sun: lucide.SunIcon,
|
||||||
trash: lucide.TrashIcon,
|
trash: lucide.TrashIcon,
|
||||||
update: lucide.RefreshCcwIcon,
|
update: lucide.RefreshCcwIcon,
|
||||||
upload: lucide.UploadIcon,
|
upload: lucide.UploadIcon,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import classNames from 'classnames';
|
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> {
|
interface Props<T extends string> {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -8,6 +10,7 @@ interface Props<T extends string> {
|
|||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
hideLabel?: boolean;
|
hideLabel?: boolean;
|
||||||
value: T;
|
value: T;
|
||||||
|
leftSlot?: ReactNode;
|
||||||
options: SelectOption<T>[];
|
options: SelectOption<T>[];
|
||||||
onChange: (value: T) => void;
|
onChange: (value: T) => void;
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg';
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
||||||
@@ -27,10 +30,12 @@ export function Select<T extends string>({
|
|||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
|
leftSlot,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
size = 'md',
|
size = 'md',
|
||||||
}: Props<T>) {
|
}: Props<T>) {
|
||||||
|
const [focused, setFocused] = useState<boolean>(false);
|
||||||
const id = `input-${name}`;
|
const id = `input-${name}`;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -49,25 +54,36 @@ export function Select<T extends string>({
|
|||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<HStack
|
||||||
value={value}
|
space={2}
|
||||||
style={selectBackgroundStyles}
|
alignItems="center"
|
||||||
onChange={(e) => onChange(e.target.value as T)}
|
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'font-mono text-sm border w-full outline-none bg-transparent pl-2 pr-7',
|
'w-full rounded-md text-fg text-sm font-mono',
|
||||||
'bg-background-highlight-secondary border-background-highlight focus:border-border-focus',
|
'pl-2',
|
||||||
|
'bg-background-highlight-secondary border',
|
||||||
|
focused ? 'border-border-focus' : 'border-background-highlight',
|
||||||
size === 'xs' && 'h-xs',
|
size === 'xs' && 'h-xs',
|
||||||
size === 'sm' && 'h-sm',
|
size === 'sm' && 'h-sm',
|
||||||
size === 'md' && 'h-md',
|
size === 'md' && 'h-md',
|
||||||
size === 'lg' && 'h-lg',
|
size === 'lg' && 'h-lg',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{options.map(({ label, value }) => (
|
{leftSlot && <div>{leftSlot}</div>}
|
||||||
<option key={label} value={value}>
|
<select
|
||||||
{label}
|
value={value}
|
||||||
</option>
|
style={selectBackgroundStyles}
|
||||||
))}
|
onChange={(e) => onChange(e.target.value as T)}
|
||||||
</select>
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export function useSettings() {
|
|||||||
queryKey: settingsQueryKey(),
|
queryKey: settingsQueryKey(),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const settings = (await invoke('cmd_get_settings')) as Settings;
|
const settings = (await invoke('cmd_get_settings')) as Settings;
|
||||||
console.log('SETTINGS', settings);
|
|
||||||
return [settings];
|
return [settings];
|
||||||
},
|
},
|
||||||
}).data?.[0] ?? undefined
|
}).data?.[0] ?? undefined
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useActiveWorkspace } from './useActiveWorkspace';
|
|||||||
import { useOsInfo } from './useOsInfo';
|
import { useOsInfo } from './useOsInfo';
|
||||||
import { emit } from '@tauri-apps/api/event';
|
import { emit } from '@tauri-apps/api/event';
|
||||||
|
|
||||||
export function useSyncWindowTitle() {
|
export function useSyncWorkspaceRequestTitle() {
|
||||||
const activeRequest = useActiveRequest();
|
const activeRequest = useActiveRequest();
|
||||||
const activeWorkspace = useActiveWorkspace();
|
const activeWorkspace = useActiveWorkspace();
|
||||||
const activeEnvironment = useActiveEnvironment();
|
const activeEnvironment = useActiveEnvironment();
|
||||||
@@ -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 { invoke } from '@tauri-apps/api/core';
|
||||||
import type { Settings } from '../lib/models';
|
import type { Settings } from '../lib/models';
|
||||||
import { settingsQueryKey } from './useSettings';
|
import { useSettings } from './useSettings';
|
||||||
|
|
||||||
export function useUpdateSettings() {
|
export function useUpdateSettings() {
|
||||||
const queryClient = useQueryClient();
|
const settings = useSettings();
|
||||||
|
|
||||||
return useMutation<void, unknown, Settings>({
|
return useMutation<void, unknown, Partial<Settings>>({
|
||||||
mutationFn: async (settings) => {
|
mutationFn: async (patch) => {
|
||||||
await invoke('cmd_update_settings', { settings });
|
if (settings == null) return;
|
||||||
},
|
const newSettings: Settings = { ...settings, ...patch };
|
||||||
onMutate: async (settings) => {
|
await invoke('cmd_update_settings', { settings: newSettings });
|
||||||
queryClient.setQueryData<Settings[]>(settingsQueryKey(), [settings]);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export function useZoom() {
|
|||||||
const zoomIn = useCallback(() => {
|
const zoomIn = useCallback(() => {
|
||||||
if (!settings) return;
|
if (!settings) return;
|
||||||
updateSettings.mutate({
|
updateSettings.mutate({
|
||||||
...settings,
|
|
||||||
interfaceScale: Math.min(1.8, settings.interfaceScale * 1.1),
|
interfaceScale: Math.min(1.8, settings.interfaceScale * 1.1),
|
||||||
});
|
});
|
||||||
}, [settings, updateSettings]);
|
}, [settings, updateSettings]);
|
||||||
@@ -17,13 +16,11 @@ export function useZoom() {
|
|||||||
const zoomOut = useCallback(() => {
|
const zoomOut = useCallback(() => {
|
||||||
if (!settings) return;
|
if (!settings) return;
|
||||||
updateSettings.mutate({
|
updateSettings.mutate({
|
||||||
...settings,
|
|
||||||
interfaceScale: Math.max(0.4, settings.interfaceScale * 0.9),
|
interfaceScale: Math.max(0.4, settings.interfaceScale * 0.9),
|
||||||
});
|
});
|
||||||
}, [settings, updateSettings]);
|
}, [settings, updateSettings]);
|
||||||
|
|
||||||
const zoomReset = useCallback(() => {
|
const zoomReset = useCallback(() => {
|
||||||
if (!settings) return;
|
|
||||||
updateSettings.mutate({ ...settings, interfaceScale: 1 });
|
updateSettings.mutate({ ...settings, interfaceScale: 1 });
|
||||||
}, [settings, updateSettings]);
|
}, [settings, updateSettings]);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export interface Settings extends BaseModel {
|
|||||||
interfaceFontSize: number;
|
interfaceFontSize: number;
|
||||||
interfaceScale: number;
|
interfaceScale: number;
|
||||||
editorFontSize: number;
|
editorFontSize: number;
|
||||||
editorSoftWrap: number;
|
editorSoftWrap: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Workspace extends BaseModel {
|
export interface Workspace extends BaseModel {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
const plugin = require('tailwindcss/plugin');
|
const plugin = require('tailwindcss/plugin');
|
||||||
|
|
||||||
const height = {
|
const height = {
|
||||||
'2xs': '1.6rem',
|
|
||||||
xs: '1.8rem',
|
xs: '1.8rem',
|
||||||
sm: '2.2rem',
|
sm: '2.0rem',
|
||||||
md: '2.7rem',
|
md: '2.5rem',
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {import("tailwindcss").Config} */
|
/** @type {import("tailwindcss").Config} */
|
||||||
|
|||||||
Reference in New Issue
Block a user