mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-21 00:49:17 +01:00
Custom font selection (#226)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
import { useFonts } from '@yaakapp-internal/fonts';
|
||||
import type { EditorKeymap } from '@yaakapp-internal/models';
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
@@ -14,11 +16,11 @@ import { Editor } from '../core/Editor/Editor';
|
||||
import type { IconProps } from '../core/Icon';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import type { SelectProps } from '../core/Select';
|
||||
import { Select } from '../core/Select';
|
||||
import { Select, SelectProps } from '../core/Select';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
|
||||
const NULL_FONT_VALUE = '__NULL_FONT__';
|
||||
|
||||
const fontSizeOptions = [
|
||||
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
@@ -67,6 +69,7 @@ export function SettingsAppearance() {
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
const appearance = useResolvedAppearance();
|
||||
const activeTheme = useResolvedTheme();
|
||||
const fonts = useFonts();
|
||||
|
||||
if (settings == null || workspace == null) {
|
||||
return null;
|
||||
@@ -87,46 +90,97 @@ export function SettingsAppearance() {
|
||||
}));
|
||||
|
||||
return (
|
||||
<VStack space={2} className="mb-4">
|
||||
<Select
|
||||
size="sm"
|
||||
name="interfaceFontSize"
|
||||
label="Font Size"
|
||||
labelPosition="left"
|
||||
defaultValue="15"
|
||||
value={`${settings.interfaceFontSize}`}
|
||||
options={fontSizeOptions}
|
||||
onChange={(v) => patchModel(settings, { interfaceFontSize: parseInt(v) })}
|
||||
/>
|
||||
<Select
|
||||
size="sm"
|
||||
name="editorFontSize"
|
||||
label="Editor Font Size"
|
||||
labelPosition="left"
|
||||
defaultValue="13"
|
||||
value={`${settings.editorFontSize}`}
|
||||
options={fontSizeOptions}
|
||||
onChange={(v) => patchModel(settings, { editorFontSize: clamp(parseInt(v) || 14, 8, 30) })}
|
||||
/>
|
||||
<VStack space={3} className="mb-4">
|
||||
<HStack space={2} alignItems="end">
|
||||
{fonts.data && (
|
||||
<Select
|
||||
size="sm"
|
||||
name="uiFont"
|
||||
label="Interface Font"
|
||||
value={settings.interfaceFont ?? NULL_FONT_VALUE}
|
||||
options={[
|
||||
{ label: 'System Default', value: NULL_FONT_VALUE },
|
||||
...(fonts.data.uiFonts.map((f) => ({
|
||||
label: f,
|
||||
value: f,
|
||||
})) ?? []),
|
||||
// Some people like monospace fonts for the UI
|
||||
...(fonts.data.editorFonts.map((f) => ({
|
||||
label: f,
|
||||
value: f,
|
||||
})) ?? []),
|
||||
]}
|
||||
onChange={async (v) => {
|
||||
const interfaceFont = v === NULL_FONT_VALUE ? null : v;
|
||||
await patchModel(settings, { interfaceFont });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Select
|
||||
hideLabel
|
||||
size="sm"
|
||||
name="interfaceFontSize"
|
||||
label="Interface Font Size"
|
||||
defaultValue="15"
|
||||
value={`${settings.interfaceFontSize}`}
|
||||
options={fontSizeOptions}
|
||||
onChange={(v) => patchModel(settings, { interfaceFontSize: parseInt(v) })}
|
||||
/>
|
||||
</HStack>
|
||||
<HStack space={2} alignItems="end">
|
||||
{fonts.data && (
|
||||
<Select
|
||||
size="sm"
|
||||
name="editorFont"
|
||||
label="Editor Font"
|
||||
value={settings.editorFont ?? NULL_FONT_VALUE}
|
||||
options={[
|
||||
{ label: 'System Default', value: NULL_FONT_VALUE },
|
||||
...(fonts.data.editorFonts.map((f) => ({
|
||||
label: f,
|
||||
value: f,
|
||||
})) ?? []),
|
||||
]}
|
||||
onChange={async (v) => {
|
||||
const editorFont = v === NULL_FONT_VALUE ? null : v;
|
||||
await patchModel(settings, { editorFont });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Select
|
||||
hideLabel
|
||||
size="sm"
|
||||
name="editorFontSize"
|
||||
label="Editor Font Size"
|
||||
defaultValue="13"
|
||||
value={`${settings.editorFontSize}`}
|
||||
options={fontSizeOptions}
|
||||
onChange={(v) =>
|
||||
patchModel(settings, { editorFontSize: clamp(parseInt(v) || 14, 8, 30) })
|
||||
}
|
||||
/>
|
||||
</HStack>
|
||||
<Select
|
||||
leftSlot={<Icon icon="keyboard" color="secondary" />}
|
||||
size="sm"
|
||||
name="editorKeymap"
|
||||
label="Editor Keymap"
|
||||
labelPosition="left"
|
||||
value={`${settings.editorKeymap}`}
|
||||
options={keymaps}
|
||||
onChange={(v) => patchModel(settings, { editorKeymap: v })}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={settings.editorSoftWrap}
|
||||
title="Wrap Editor Lines"
|
||||
onChange={(editorSoftWrap) => patchModel(settings, { editorSoftWrap })}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={settings.coloredMethods}
|
||||
title="Colorize Request Methods"
|
||||
onChange={(coloredMethods) => patchModel(settings, { coloredMethods })}
|
||||
/>
|
||||
<div className="grid grid-cols-2">
|
||||
<Checkbox
|
||||
checked={settings.editorSoftWrap}
|
||||
title="Wrap Editor Lines"
|
||||
onChange={(editorSoftWrap) => patchModel(settings, { editorSoftWrap })}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={settings.coloredMethods}
|
||||
title="Colorize Request Methods"
|
||||
onChange={(coloredMethods) => patchModel(settings, { coloredMethods })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{type() !== 'macos' && (
|
||||
<Checkbox
|
||||
@@ -156,7 +210,7 @@ export function SettingsAppearance() {
|
||||
{(settings.appearance === 'system' || settings.appearance === 'light') && (
|
||||
<Select
|
||||
hideLabel
|
||||
leftSlot={<Icon icon="sun" />}
|
||||
leftSlot={<Icon icon="sun" color="secondary" />}
|
||||
name="lightTheme"
|
||||
label="Light Theme"
|
||||
size="sm"
|
||||
@@ -172,7 +226,7 @@ export function SettingsAppearance() {
|
||||
name="darkTheme"
|
||||
className="flex-1"
|
||||
label="Dark Theme"
|
||||
leftSlot={<Icon icon="moon" />}
|
||||
leftSlot={<Icon icon="moon" color="secondary" />}
|
||||
size="sm"
|
||||
value={activeTheme.dark.id}
|
||||
options={darkThemes}
|
||||
|
||||
19
src-web/font.ts
Normal file
19
src-web/font.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// Listen for settings changes, the re-compute theme
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import type { ModelPayload, Settings } from '@yaakapp-internal/models';
|
||||
import { getSettings } from './lib/settings';
|
||||
|
||||
function setFonts(settings: Settings) {
|
||||
document.documentElement.style.setProperty('--font-family-editor', settings.editorFont ?? '');
|
||||
document.documentElement.style.setProperty(
|
||||
'--font-family-interface',
|
||||
settings.interfaceFont ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
listen<ModelPayload>('upserted_model', async (event) => {
|
||||
if (event.payload.model.model !== 'settings') return;
|
||||
setFonts(event.payload.model);
|
||||
}).catch(console.error);
|
||||
|
||||
getSettings().then((settings) => setFonts(settings));
|
||||
@@ -28,6 +28,7 @@
|
||||
<div id="radix-portal" class="cm-portal"></div>
|
||||
<script type="module" src="/theme.ts"></script>
|
||||
<script type="module" src="/font-size.ts"></script>
|
||||
<script type="module" src="/font.ts"></script>
|
||||
<script type="module" src="/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
@apply w-full h-full overflow-hidden text-text bg-surface;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Must default these variables or the default will break */
|
||||
--font-family-interface: '';
|
||||
--font-family-editor: '';
|
||||
}
|
||||
|
||||
/* Never show ligatures */
|
||||
:root {
|
||||
font-variant-ligatures: none;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { createRootRoute, Outlet } from '@tanstack/react-router';
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
import classNames from 'classnames';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { domAnimation, LazyMotion, MotionConfig } from 'motion/react';
|
||||
@@ -13,7 +14,6 @@ import RouteError from '../components/RouteError';
|
||||
import { Toasts } from '../components/Toasts';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { queryClient } from '../lib/queryClient';
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RouteComponent,
|
||||
@@ -32,14 +32,7 @@ function RouteComponent() {
|
||||
<GlobalHooks />
|
||||
<Toasts />
|
||||
<Dialogs />
|
||||
<div
|
||||
className={classNames(
|
||||
'w-full h-full',
|
||||
type() === 'linux' && 'border border-border-subtle',
|
||||
)}
|
||||
>
|
||||
<Outlet />
|
||||
</div>
|
||||
<Layout />
|
||||
</Suspense>
|
||||
</DndProvider>
|
||||
</HelmetProvider>
|
||||
@@ -49,3 +42,13 @@ function RouteComponent() {
|
||||
</JotaiProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function Layout() {
|
||||
return (
|
||||
<div
|
||||
className={classNames('w-full h-full', type() === 'linux' && 'border border-border-subtle')}
|
||||
>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ const sizes = {
|
||||
md: '2.3rem',
|
||||
};
|
||||
|
||||
/** @type {import("tailwindcss").Config} */
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: ['class', '[data-resolved-appearance="dark"]'],
|
||||
content: [
|
||||
@@ -43,6 +43,7 @@ module.exports = {
|
||||
},
|
||||
fontFamily: {
|
||||
mono: [
|
||||
'var(--font-family-editor)',
|
||||
'JetBrains Mono',
|
||||
'ui-monospace',
|
||||
'SFMono-Regular',
|
||||
@@ -58,6 +59,7 @@ module.exports = {
|
||||
'monospace',
|
||||
],
|
||||
sans: [
|
||||
'var(--font-family-interface)',
|
||||
'Inter UI',
|
||||
'-apple-system',
|
||||
'BlinkMacSystemFont',
|
||||
|
||||
Reference in New Issue
Block a user