- {f.split('/').pop()}
+ {f.split('/').pop()}
{
await protoFilesKv.set(protoFiles.filter((p) => p !== f));
}}
diff --git a/src-web/components/KeyboardShortcutsDialog.tsx b/src-web/components/KeyboardShortcutsDialog.tsx
index 1e40dabd..e9d3da3d 100644
--- a/src-web/components/KeyboardShortcutsDialog.tsx
+++ b/src-web/components/KeyboardShortcutsDialog.tsx
@@ -1,10 +1,10 @@
import { hotkeyActions } from '../hooks/useHotKey';
import { HotKeyList } from './core/HotKeyList';
-export const KeyboardShortcutsDialog = () => {
+export function KeyboardShortcutsDialog() {
return (
);
-};
+}
diff --git a/src-web/components/RecentConnectionsDropdown.tsx b/src-web/components/RecentConnectionsDropdown.tsx
index f2d55e35..06e53a61 100644
--- a/src-web/components/RecentConnectionsDropdown.tsx
+++ b/src-web/components/RecentConnectionsDropdown.tsx
@@ -45,7 +45,7 @@ export function RecentConnectionsDropdown({
label: (
{formatDistanceToNowStrict(c.createdAt + 'Z')} ago •{' '}
- {c.elapsed}ms
+ {c.elapsed}ms
),
leftSlot: activeConnection?.id === c.id ? : ,
diff --git a/src-web/components/RecentRequestsDropdown.tsx b/src-web/components/RecentRequestsDropdown.tsx
index 4bc1693c..e875ec61 100644
--- a/src-web/components/RecentRequestsDropdown.tsx
+++ b/src-web/components/RecentRequestsDropdown.tsx
@@ -86,7 +86,7 @@ export function RecentRequestsDropdown({ className }: Pick
diff --git a/src-web/components/RecentResponsesDropdown.tsx b/src-web/components/RecentResponsesDropdown.tsx
index d9f8cf69..d33ebc81 100644
--- a/src-web/components/RecentResponsesDropdown.tsx
+++ b/src-web/components/RecentResponsesDropdown.tsx
@@ -43,13 +43,13 @@ export const RecentResponsesDropdown = function ResponsePane({
disabled: responses.length === 0,
},
{ type: 'separator', label: 'History' },
- ...responses.slice(0, 20).map((r) => ({
+ ...responses.slice(0, 20).map((r: HttpResponse) => ({
key: r.id,
label: (
-
+
→ {' '}
- {r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'}
+ {r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'}
),
leftSlot: activeResponse?.id === r.id ? : ,
diff --git a/src-web/components/ResponsePane.tsx b/src-web/components/ResponsePane.tsx
index 9d463c09..26774a51 100644
--- a/src-web/components/ResponsePane.tsx
+++ b/src-web/components/ResponsePane.tsx
@@ -94,7 +94,7 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
+ parseInt(value) >= 8 && parseInt(value) <= 30}
+ onChange={(v) =>
+ updateSettings.mutate({
+ ...settings,
+ interfaceFontSize: clamp(parseInt(v) || 16, 8, 30),
+ })
+ }
+ />
+ parseInt(value) >= 8 && parseInt(value) <= 30}
+ onChange={(v) =>
+ updateSettings.mutate({
+ ...settings,
+ editorFontSize: clamp(parseInt(v) || 14, 8, 30),
+ })
+ }
+ />
+
+
+
-
- {
- await updateSettings.mutateAsync({ ...settings, themeLight });
- trackEvent('setting', 'update', { themeLight });
- }}
- />
- {
- await updateSettings.mutateAsync({ ...settings, themeDark });
- trackEvent('setting', 'update', { themeDark });
- }}
- />
-
+ {
+ await updateSettings.mutateAsync({ ...settings, themeLight });
+ trackEvent('setting', 'update', { themeLight });
+ }}
+ />
+ {
+ await updateSettings.mutateAsync({ ...settings, themeDark });
+ trackEvent('setting', 'update', { themeDark });
+ }}
+ />
-
+
Theme Preview ({appearance})
diff --git a/src-web/components/Settings/SettingsDialog.tsx b/src-web/components/Settings/SettingsDialog.tsx
index 70dc4ed8..811b9a29 100644
--- a/src-web/components/Settings/SettingsDialog.tsx
+++ b/src-web/components/Settings/SettingsDialog.tsx
@@ -16,8 +16,7 @@ enum Tab {
}
const tabs = [Tab.General, Tab.Appearance, Tab.Design];
-
-const useTabState = createGlobalState(Tab.Appearance);
+const useTabState = createGlobalState(tabs[0]!);
export const SettingsDialog = () => {
const [tab, setTab] = useTabState();
diff --git a/src-web/components/Settings/SettingsGeneral.tsx b/src-web/components/Settings/SettingsGeneral.tsx
index 81fa91c3..d1bd230f 100644
--- a/src-web/components/Settings/SettingsGeneral.tsx
+++ b/src-web/components/Settings/SettingsGeneral.tsx
@@ -1,3 +1,4 @@
+import React from 'react';
import { useActiveWorkspace } from '../../hooks/useActiveWorkspace';
import { useAppInfo } from '../../hooks/useAppInfo';
import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
@@ -35,19 +36,13 @@ export function SettingsGeneral() {
labelPosition="left"
size="sm"
value={settings.updateChannel}
- onChange={async (updateChannel) => {
+ onChange={(updateChannel) => {
trackEvent('setting', 'update', { update_channel: updateChannel });
- await updateSettings.mutateAsync({ ...settings, updateChannel });
+ updateSettings.mutate({ ...settings, updateChannel });
}}
options={[
- {
- label: 'Release',
- value: 'stable',
- },
- {
- label: 'Early Bird (Beta)',
- value: 'beta',
- },
+ { label: 'Release', value: 'stable' },
+ { label: 'Early Bird (Beta)', value: 'beta' },
]}
/>
checkForUpdates.mutateAsync()}
/>
-
Workspace{' '}
-
+
{workspace.name}
@@ -77,28 +71,28 @@ export function SettingsGeneral() {
labelPosition="left"
defaultValue={`${workspace.settingRequestTimeout}`}
validate={(value) => parseInt(value) >= 0}
- onChange={(v) => updateWorkspace.mutateAsync({ settingRequestTimeout: parseInt(v) || 0 })}
+ onChange={(v) => updateWorkspace.mutate({ settingRequestTimeout: parseInt(v) || 0 })}
/>
{
+ onChange={(settingValidateCertificates) => {
trackEvent('workspace', 'update', {
validate_certificates: JSON.stringify(settingValidateCertificates),
});
- await updateWorkspace.mutateAsync({ settingValidateCertificates });
+ updateWorkspace.mutate({ settingValidateCertificates });
}}
/>
{
+ onChange={(settingFollowRedirects) => {
trackEvent('workspace', 'update', {
follow_redirects: JSON.stringify(settingFollowRedirects),
});
- await updateWorkspace.mutateAsync({ settingFollowRedirects });
+ updateWorkspace.mutate({ settingFollowRedirects });
}}
/>
diff --git a/src-web/components/SettingsDropdown.tsx b/src-web/components/SettingsDropdown.tsx
index 310994e7..d7929f71 100644
--- a/src-web/components/SettingsDropdown.tsx
+++ b/src-web/components/SettingsDropdown.tsx
@@ -53,7 +53,7 @@ export function SettingsDropdown() {
dialog.show({
id: 'hotkey',
title: 'Keyboard Shortcuts',
- size: 'sm',
+ size: 'dynamic',
render: () => ,
});
},
diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx
index c7c2395a..6b0857c6 100644
--- a/src-web/components/Sidebar.tsx
+++ b/src-web/components/Sidebar.tsx
@@ -512,7 +512,7 @@ function SidebarItems({
className={classNames(
tree.depth > 0 && 'border-l border-background-highlight-secondary',
tree.depth === 0 && 'ml-0',
- tree.depth >= 1 && 'ml-[1.2em]',
+ tree.depth >= 1 && 'ml-[1.2rem]',
)}
>
{tree.children.map((child, i) => {
@@ -811,7 +811,7 @@ const SidebarItem = forwardRef(function SidebarItem(
data-active={isActive}
data-selected={selected}
className={classNames(
- 'w-full flex gap-1.5 items-center text-sm h-xs px-1.5 rounded-md',
+ 'w-full flex gap-1.5 items-center h-xs px-1.5 rounded-md',
editing && 'ring-1 focus-within:ring-focus',
isActive && 'bg-background-highlight-secondary text-fg',
!isActive &&
@@ -855,7 +855,7 @@ const SidebarItem = forwardRef(function SidebarItem(
{isResponseLoading(latestHttpResponse) ? (
) : (
-
+
)}
) : null}
diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx
index cfbed29f..9635a695 100644
--- a/src-web/components/Workspace.tsx
+++ b/src-web/components/Workspace.tsx
@@ -130,7 +130,7 @@ export default function Workspace() {
'grid w-full h-full',
// Animate sidebar width changes but only when not resizing
// because it's too slow to animate on mouse move
- !isResizing && 'transition-all',
+ !isResizing && 'transition-grid',
)}
>
{floating ? (
diff --git a/src-web/components/core/Button.tsx b/src-web/components/core/Button.tsx
index 2e583d58..e47b6817 100644
--- a/src-web/components/core/Button.tsx
+++ b/src-web/components/core/Button.tsx
@@ -71,7 +71,7 @@ export const Button = forwardRef(function Button
justify === 'start' && 'justify-start',
justify === 'center' && 'justify-center',
size === 'md' && 'h-md px-3 rounded-md',
- size === 'sm' && 'h-sm px-2.5 text-sm rounded-md',
+ size === 'sm' && 'h-sm px-2.5 rounded-md',
size === 'xs' && 'h-xs px-2 text-sm rounded-md',
size === '2xs' && 'h-5 px-1 text-xs rounded',
diff --git a/src-web/components/core/Checkbox.tsx b/src-web/components/core/Checkbox.tsx
index 684037bc..b4136338 100644
--- a/src-web/components/core/Checkbox.tsx
+++ b/src-web/components/core/Checkbox.tsx
@@ -28,7 +28,7 @@ export function Checkbox({
as="label"
space={2}
alignItems="center"
- className={classNames(className, 'text-fg text-sm', disabled && 'opacity-disabled')}
+ className={classNames(className, 'text-fg', disabled && 'opacity-disabled')}
>
, MenuPro
{filter}
)}
{filteredItems.length === 0 && (
-
- No matches
-
+
No matches
)}
{filteredItems.map((item, i) => {
if (item.type === 'separator') {
@@ -531,14 +529,18 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
onFocus={handleFocus}
onClick={handleClick}
justify="start"
- leftSlot={item.leftSlot &&
{item.leftSlot}
}
+ leftSlot={
+ item.leftSlot && (
+
{item.leftSlot}
+ )
+ }
rightSlot={rightSlot &&
{rightSlot}
}
innerClassName="!text-left"
color="custom"
className={classNames(
className,
'h-xs', // More compact
- 'min-w-[8rem] outline-none px-2 mx-1.5 flex text-sm whitespace-nowrap',
+ 'min-w-[8rem] outline-none px-2 mx-1.5 flex whitespace-nowrap',
'focus:bg-background-highlight focus:text-fg rounded',
item.variant === 'default' && 'text-fg-subtle',
item.variant === 'danger' && 'text-fg-danger',
diff --git a/src-web/components/core/Editor/Editor.css b/src-web/components/core/Editor/Editor.css
index 341f2580..7d78ab04 100644
--- a/src-web/components/core/Editor/Editor.css
+++ b/src-web/components/core/Editor/Editor.css
@@ -104,8 +104,7 @@
}
.cm-scroller {
- @apply font-mono text-[0.75rem];
-
+ @apply font-mono text-editor;
/*
* Round corners or they'll stick out of the editor bounds of editor is rounded.
* Could potentially be pushed up from the editor like we do with bg color but this
@@ -185,7 +184,7 @@
}
.cm-tooltip.cm-tooltip-hover {
- @apply shadow-lg bg-background rounded text-fg-subtle border border-fg-subtler z-50 pointer-events-auto text-xs;
+ @apply shadow-lg bg-background rounded text-fg-subtle border border-fg-subtler z-50 pointer-events-auto text-sm;
@apply px-2 py-1;
a {
@@ -208,7 +207,7 @@
/* NOTE: Extra selector required to override default styles */
.cm-tooltip.cm-tooltip-autocomplete,
.cm-tooltip.cm-completionInfo {
- @apply shadow-lg bg-background rounded text-fg-subtle border border-background-highlight z-50 pointer-events-auto text-xs;
+ @apply shadow-lg bg-background rounded text-fg-subtle border border-background-highlight z-50 pointer-events-auto text-sm;
.cm-completionIcon {
@apply italic font-mono;
@@ -267,7 +266,7 @@
}
&.cm-completionInfo-right {
- @apply ml-1 -mt-0.5 text-sm;
+ @apply ml-1 -mt-0.5;
}
&.cm-completionInfo-right-narrow {
@@ -279,12 +278,14 @@
}
&.cm-tooltip-autocomplete {
+ @apply font-mono text-editor;
+
& > ul {
@apply p-1 max-h-[40vh];
}
& > ul > li {
- @apply cursor-default px-2 rounded-sm text-fg-subtle h-7 flex items-center;
+ @apply cursor-default px-2 py-1.5 rounded-sm text-fg-subtle flex items-center;
}
& > ul > li[aria-selected] {
@@ -292,7 +293,7 @@
}
.cm-completionIcon {
- @apply text-xs flex items-center pb-0.5 flex-shrink-0;
+ @apply text-sm flex items-center pb-0.5 flex-shrink-0;
}
.cm-completionLabel {
diff --git a/src-web/components/core/FormattedError.tsx b/src-web/components/core/FormattedError.tsx
index 3b70f8ca..87a159db 100644
--- a/src-web/components/core/FormattedError.tsx
+++ b/src-web/components/core/FormattedError.tsx
@@ -9,7 +9,7 @@ export function FormattedError({ children }: Props) {
return (
diff --git a/src-web/components/core/HotKeyLabel.tsx b/src-web/components/core/HotKeyLabel.tsx
index dbcceb1f..8ea0ba86 100644
--- a/src-web/components/core/HotKeyLabel.tsx
+++ b/src-web/components/core/HotKeyLabel.tsx
@@ -7,5 +7,5 @@ interface Props {
export function HotKeyLabel({ action }: Props) {
const label = useHotKeyLabel(action);
- return {label} ;
+ return {label} ;
}
diff --git a/src-web/components/core/HotKeyList.tsx b/src-web/components/core/HotKeyList.tsx
index 2388bb0e..8f4c142b 100644
--- a/src-web/components/core/HotKeyList.tsx
+++ b/src-web/components/core/HotKeyList.tsx
@@ -11,7 +11,7 @@ interface Props {
export const HotKeyList = ({ hotkeys, bottomSlot }: Props) => {
return (
-
+
{hotkeys.map((hotkey) => (
diff --git a/src-web/components/core/HttpMethodTag.tsx b/src-web/components/core/HttpMethodTag.tsx
index 69051b18..98aeb030 100644
--- a/src-web/components/core/HttpMethodTag.tsx
+++ b/src-web/components/core/HttpMethodTag.tsx
@@ -27,7 +27,7 @@ export function HttpMethodTag({ request, className }: Props) {
const m = method.toLowerCase();
return (
-
+
{methodMap[m] ?? m.slice(0, 3).toUpperCase()}
);
diff --git a/src-web/components/core/InlineCode.tsx b/src-web/components/core/InlineCode.tsx
index f43e7502..1890f7c9 100644
--- a/src-web/components/core/InlineCode.tsx
+++ b/src-web/components/core/InlineCode.tsx
@@ -6,8 +6,8 @@ export function InlineCode({ className, ...props }: HTMLAttributes
diff --git a/src-web/components/core/Input.tsx b/src-web/components/core/Input.tsx
index fba19719..8492c5c1 100644
--- a/src-web/components/core/Input.tsx
+++ b/src-web/components/core/Input.tsx
@@ -133,11 +133,7 @@ export const Input = forwardRef(function Inp
>
{label}
diff --git a/src-web/components/core/JsonAttributeTree.tsx b/src-web/components/core/JsonAttributeTree.tsx
index e11139ba..1d9800ee 100644
--- a/src-web/components/core/JsonAttributeTree.tsx
+++ b/src-web/components/core/JsonAttributeTree.tsx
@@ -80,7 +80,7 @@ export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPa
{label}
);
return (
-
+
{isExpandable ? (
diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx
index e8e28fb4..bd8dea68 100644
--- a/src-web/components/core/PairEditor.tsx
+++ b/src-web/components/core/PairEditor.tsx
@@ -389,7 +389,7 @@ function PairEditorRow({
{
e.preventDefault();
const selected = await open({
diff --git a/src-web/components/core/PlainInput.tsx b/src-web/components/core/PlainInput.tsx
new file mode 100644
index 00000000..5767b38f
--- /dev/null
+++ b/src-web/components/core/PlainInput.tsx
@@ -0,0 +1,148 @@
+import classNames from 'classnames';
+import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
+import { useStateWithDeps } from '../../hooks/useStateWithDeps';
+import { IconButton } from './IconButton';
+import type { InputProps } from './Input';
+import { HStack } from './Stacks';
+
+export type PlainInputProps = Omit & {
+ type: 'text' | 'password' | 'number';
+};
+
+export const PlainInput = forwardRef(function Input(
+ {
+ className,
+ containerClassName,
+ defaultValue,
+ forceUpdateKey,
+ hideLabel,
+ label,
+ labelClassName,
+ labelPosition = 'top',
+ leftSlot,
+ name,
+ onBlur,
+ onChange,
+ onFocus,
+ onPaste,
+ placeholder,
+ require,
+ rightSlot,
+ size = 'md',
+ type = 'text',
+ validate,
+ ...props
+ }: PlainInputProps,
+ ref,
+) {
+ const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
+ const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
+ const [focused, setFocused] = useState(false);
+
+ const handleFocus = useCallback(() => {
+ setFocused(true);
+ onFocus?.();
+ }, [onFocus]);
+
+ const handleBlur = useCallback(() => {
+ setFocused(false);
+ onBlur?.();
+ }, [onBlur]);
+
+ const id = `input-${name}`;
+ const inputClassName = classNames(
+ className,
+ '!bg-transparent min-w-0 h-auto w-full focus:outline-none placeholder:text-placeholder',
+ 'px-1.5 text-xs font-mono',
+ );
+
+ const isValid = useMemo(() => {
+ if (require && !validateRequire(currentValue)) return false;
+ if (validate && !validate(currentValue)) return false;
+ return true;
+ }, [currentValue, validate, require]);
+
+ const handleChange = useCallback(
+ (value: string) => {
+ setCurrentValue(value);
+ onChange?.(value);
+ },
+ [onChange],
+ );
+
+ const wrapperRef = useRef(null);
+
+ return (
+
+
+ {label}
+
+
+ {leftSlot}
+
+ handleChange(e.target.value)}
+ onPaste={(e) => onPaste?.(e.clipboardData.getData('Text'))}
+ className={inputClassName}
+ onFocus={handleFocus}
+ onBlur={handleBlur}
+ {...props}
+ />
+
+ {type === 'password' && (
+ setObscured((o) => !o)}
+ />
+ )}
+ {rightSlot}
+
+
+ );
+});
+
+function validateRequire(v: string) {
+ return v.length > 0;
+}
diff --git a/src-web/components/core/Select.tsx b/src-web/components/core/Select.tsx
index 3a5e7e4b..766ca7c7 100644
--- a/src-web/components/core/Select.tsx
+++ b/src-web/components/core/Select.tsx
@@ -45,11 +45,7 @@ export function Select({
>
{label}
@@ -58,7 +54,7 @@ export function Select({
style={selectBackgroundStyles}
onChange={(e) => onChange(e.target.value as T)}
className={classNames(
- 'font-mono text-xs border w-full outline-none bg-transparent pl-2 pr-7',
+ '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',
size === 'xs' && 'h-xs',
size === 'sm' && 'h-sm',
diff --git a/src-web/components/core/Separator.tsx b/src-web/components/core/Separator.tsx
index c1e3d940..43760a45 100644
--- a/src-web/components/core/Separator.tsx
+++ b/src-web/components/core/Separator.tsx
@@ -11,7 +11,7 @@ interface Props {
export function Separator({ className, orientation = 'horizontal', children }: Props) {
return (
- {children &&
{children}
}
+ {children &&
{children}
}
{
const isActive = t.value === value;
const btnClassName = classNames(
- 'h-full flex items-center text-sm rounded',
+ 'h-full flex items-center rounded',
'!px-2 ml-[1px]',
addBorders && 'border',
isActive ? 'text-fg' : 'text-fg-subtle hover:text-fg',
diff --git a/src-web/components/responseViewers/ImageViewer.tsx b/src-web/components/responseViewers/ImageViewer.tsx
index 6c1a941c..cadcce83 100644
--- a/src-web/components/responseViewers/ImageViewer.tsx
+++ b/src-web/components/responseViewers/ImageViewer.tsx
@@ -20,7 +20,7 @@ export function ImageViewer({ response, className }: Props) {
if (!show) {
return (
<>
-
+
Response body is too large to preview.{' '}
setShow(true)}>
Show anyway
diff --git a/src-web/hooks/useHotKey.ts b/src-web/hooks/useHotKey.ts
index 791f9eac..d3a36fde 100644
--- a/src-web/hooks/useHotKey.ts
+++ b/src-web/hooks/useHotKey.ts
@@ -20,7 +20,10 @@ export type HotkeyAction =
| 'sidebar.focus'
| 'sidebar.toggle'
| 'urlBar.focus'
- | 'command_palette.toggle';
+ | 'command_palette.toggle'
+ | 'app.zoom_in'
+ | 'app.zoom_out'
+ | 'app.zoom_reset';
const hotkeys: Record = {
'environmentEditor.toggle': ['CmdCtrl+Shift+e'],
@@ -37,6 +40,9 @@ const hotkeys: Record = {
'sidebar.toggle': ['CmdCtrl+b'],
'urlBar.focus': ['CmdCtrl+l'],
'command_palette.toggle': ['CmdCtrl+k'],
+ 'app.zoom_in': ['CmdCtrl+='],
+ 'app.zoom_out': ['CmdCtrl+-'],
+ 'app.zoom_reset': ['CmdCtrl+0'],
};
const hotkeyLabels: Record = {
@@ -54,6 +60,9 @@ const hotkeyLabels: Record = {
'sidebar.toggle': 'Toggle Sidebar',
'urlBar.focus': 'Focus URL',
'command_palette.toggle': 'Toggle Command Palette',
+ 'app.zoom_in': 'Zoom In',
+ 'app.zoom_out': 'Zoom Out',
+ 'app.zoom_reset': 'Zoom to Actual Size',
};
export const hotkeyActions: HotkeyAction[] = Object.keys(hotkeys) as (keyof typeof hotkeys)[];
diff --git a/src-web/hooks/usePreferredAppearance.ts b/src-web/hooks/usePreferredAppearance.ts
index a43902d7..4023245d 100644
--- a/src-web/hooks/usePreferredAppearance.ts
+++ b/src-web/hooks/usePreferredAppearance.ts
@@ -1,17 +1,17 @@
import { useEffect, useState } from 'react';
import {
- type Appearance,
- getPreferredAppearance,
- subscribeToPreferredAppearanceChange,
-} from '../lib/theme/window';
+ getCSSAppearance,
+ getWindowAppearance,
+ subscribeToWindowAppearanceChange,
+} from '../lib/theme/appearance';
+import { type Appearance } from '../lib/theme/window';
export function usePreferredAppearance() {
- const [preferredAppearance, setPreferredAppearance] = useState();
+ const [preferredAppearance, setPreferredAppearance] = useState(getCSSAppearance());
- // Set appearance when preferred theme changes
useEffect(() => {
- getPreferredAppearance().then(setPreferredAppearance);
- return subscribeToPreferredAppearanceChange(setPreferredAppearance);
+ getWindowAppearance().then(setPreferredAppearance);
+ return subscribeToWindowAppearanceChange(setPreferredAppearance);
}, []);
return preferredAppearance;
diff --git a/src-web/hooks/useResolvedAppearance.ts b/src-web/hooks/useResolvedAppearance.ts
index 1a31b253..7702fb02 100644
--- a/src-web/hooks/useResolvedAppearance.ts
+++ b/src-web/hooks/useResolvedAppearance.ts
@@ -10,7 +10,5 @@ export function useResolvedAppearance() {
? preferredAppearance
: settings.appearance;
- console.log('HELLO', settings?.appearance, preferredAppearance);
-
return appearance;
}
diff --git a/src-web/hooks/useZoom.ts b/src-web/hooks/useZoom.ts
new file mode 100644
index 00000000..fe303904
--- /dev/null
+++ b/src-web/hooks/useZoom.ts
@@ -0,0 +1,31 @@
+import { useCallback } from 'react';
+import { useSettings } from './useSettings';
+import { useUpdateSettings } from './useUpdateSettings';
+
+export function useZoom() {
+ const settings = useSettings();
+ const updateSettings = useUpdateSettings();
+
+ const zoomIn = useCallback(() => {
+ if (!settings) return;
+ updateSettings.mutate({
+ ...settings,
+ interfaceScale: Math.min(1.8, settings.interfaceScale * 1.1),
+ });
+ }, [settings, updateSettings]);
+
+ 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]);
+
+ return { zoomIn, zoomOut, zoomReset };
+}
diff --git a/src-web/lib/indent.ts b/src-web/lib/indent.ts
deleted file mode 100644
index 51f5b4fd..00000000
--- a/src-web/lib/indent.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export function indent(text: string, space = ' '): string {
- return text
- .split('\n')
- .map((line) => space + line)
- .join('\n');
-}
diff --git a/src-web/lib/models.ts b/src-web/lib/models.ts
index 6c137633..0b2f6c87 100644
--- a/src-web/lib/models.ts
+++ b/src-web/lib/models.ts
@@ -37,6 +37,10 @@ export interface Settings extends BaseModel {
themeLight: string;
themeDark: string;
updateChannel: string;
+ interfaceFontSize: number;
+ interfaceScale: number;
+ editorFontSize: number;
+ editorSoftWrap: number;
}
export interface Workspace extends BaseModel {
diff --git a/src-web/lib/theme/appearance.ts b/src-web/lib/theme/appearance.ts
new file mode 100644
index 00000000..0d5a6f07
--- /dev/null
+++ b/src-web/lib/theme/appearance.ts
@@ -0,0 +1,31 @@
+import { getCurrent } from '@tauri-apps/api/webviewWindow';
+import type { Appearance } from './window';
+
+export function getCSSAppearance(): Appearance {
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+}
+
+export async function getWindowAppearance(): Promise {
+ const a = await getCurrent().theme();
+ return a ?? getCSSAppearance();
+}
+
+/**
+ * Subscribe to appearance (dark/light) changes. Note, we use Tauri Window appearance instead of
+ * CSS appearance because CSS won't fire the way we handle window theme management.
+ */
+export function subscribeToWindowAppearanceChange(
+ cb: (appearance: Appearance) => void,
+): () => void {
+ const container = { unsubscribe: () => {} };
+
+ getCurrent()
+ .onThemeChanged((t) => {
+ cb(t.payload);
+ })
+ .then((l) => {
+ container.unsubscribe = l;
+ });
+
+ return () => container.unsubscribe();
+}
diff --git a/src-web/lib/theme/color.ts b/src-web/lib/theme/color.ts
index ad7c8b36..d9faeda7 100644
--- a/src-web/lib/theme/color.ts
+++ b/src-web/lib/theme/color.ts
@@ -41,22 +41,10 @@ export class Color {
return this.theme === 'dark' ? this._darken(mod) : this._lighten(mod);
}
- lowerTo(value: number): Color {
- return this.theme === 'dark'
- ? this._darken(1)._lighten(value)
- : this._lighten(1)._darken(1 - value);
- }
-
lift(mod: number): Color {
return this.theme === 'dark' ? this._lighten(mod) : this._darken(mod);
}
- liftTo(value: number): Color {
- return this.theme === 'dark'
- ? this._lighten(1)._darken(1 - value)
- : this._darken(1)._lighten(value);
- }
-
translucify(mod: number): Color {
const c = this.clone();
c.alpha = c.alpha - c.alpha * mod;
diff --git a/src-web/lib/theme/themes/yaak.ts b/src-web/lib/theme/themes/yaak.ts
index 74ae3941..a5e229fb 100644
--- a/src-web/lib/theme/themes/yaak.ts
+++ b/src-web/lib/theme/themes/yaak.ts
@@ -31,10 +31,10 @@ export const yaakLight: YaakTheme = {
export const yaakDark: YaakTheme = {
id: 'yaak-dark',
name: 'Yaak',
- background: new Color('hsl(244,23%,13%)', 'dark'),
+ background: new Color('hsl(244,23%,14%)', 'dark'),
backgroundHighlight: new Color('hsl(244,23%,23%)', 'dark'),
backgroundHighlightSecondary: new Color('hsl(244,23%,20%)', 'dark'),
- foreground: new Color('hsl(245,23%,86%)', 'dark'),
+ foreground: new Color('hsl(245,23%,80%)', 'dark'),
foregroundSubtle: new Color('hsl(245,20%,65%)', 'dark'),
foregroundSubtler: new Color('hsl(245,18%,50%)', 'dark'),
diff --git a/src-web/lib/theme/window.ts b/src-web/lib/theme/window.ts
index 7d791b72..eae18eef 100644
--- a/src-web/lib/theme/window.ts
+++ b/src-web/lib/theme/window.ts
@@ -1,5 +1,3 @@
-import { getCurrent } from '@tauri-apps/api/webviewWindow';
-import { indent } from '../indent';
import { Color } from './color';
export type Appearance = 'dark' | 'light' | 'system';
@@ -238,24 +236,9 @@ export function setThemeOnDocument(theme: YaakTheme) {
document.documentElement.setAttribute('data-theme', theme.id);
}
-export async function getPreferredAppearance(): Promise {
- const a = await getCurrent().theme();
- return a ?? 'light';
-}
-
-export function subscribeToPreferredAppearanceChange(
- cb: (appearance: Appearance) => void,
-): () => void {
- const container = { unsubscribe: () => {} };
-
- getCurrent()
- .onThemeChanged((t) => {
- console.log('THEME CHANGED', t);
- cb(t.payload);
- })
- .then((l) => {
- container.unsubscribe = l;
- });
-
- return () => container.unsubscribe();
+export function indent(text: string, space = ' '): string {
+ return text
+ .split('\n')
+ .map((line) => space + line)
+ .join('\n');
}
diff --git a/src-web/main.css b/src-web/main.css
index ee60b728..96891b03 100644
--- a/src-web/main.css
+++ b/src-web/main.css
@@ -38,7 +38,7 @@
.hide-scrollbars {
&::-webkit-scrollbar-corner,
&::-webkit-scrollbar {
- display: none !important;
+ display: NONE !important;
}
}
diff --git a/tailwind.config.cjs b/tailwind.config.cjs
index 60f7799f..372c37b2 100644
--- a/tailwind.config.cjs
+++ b/tailwind.config.cjs
@@ -1,10 +1,10 @@
const plugin = require('tailwindcss/plugin');
const height = {
- '2xs': '1.5rem',
- xs: '1.75rem',
- sm: '2.0rem',
- md: '2.5rem',
+ '2xs': '1.6rem',
+ xs: '1.8rem',
+ sm: '2.2rem',
+ md: '2.7rem',
};
/** @type {import("tailwindcss").Config} */
@@ -27,11 +27,14 @@ module.exports = {
sm: 'calc(2.0rem - 2px)',
md: 'calc(2.5rem - 2px)',
},
+ transitionProperty: {
+ grid: 'grid',
+ },
},
fontFamily: {
mono: ['JetBrains Mono', 'Menlo', 'monospace'],
sans: [
- 'Inter',
+ 'Inter UI',
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
@@ -58,6 +61,8 @@ module.exports = {
'3xl': '2rem',
'4xl': '2.5rem',
'5xl': '3rem',
+ 'editor': 'var(--editor-font-size)',
+ 'shrink': '0.8em',
},
boxShadow: {
DEFAULT: '0 1px 3px 0 var(--shadow);',