forked from abner/for-legacy-web
Feature: Font selector and ligature toggle.
This commit is contained in:
@@ -24,14 +24,16 @@ const PermissionTooltipBase = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
color: var(--secondary-foreground);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Fira Mono';
|
||||
font-family: var(--monoscape-font);
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-family: "Fira Mono", sans-serif;
|
||||
font-family: var(--monoscape-font), sans-serif;
|
||||
}
|
||||
|
||||
&[data-loading="true"] {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import "@fontsource/fira-mono/400.css";
|
||||
|
||||
.markdown {
|
||||
:global(.emoji) {
|
||||
height: 1.25em;
|
||||
@@ -89,7 +87,7 @@
|
||||
font-size: 90%;
|
||||
border-radius: 4px;
|
||||
background: var(--block);
|
||||
font-family: "Fira Mono", monospace;
|
||||
font-family: var(--monoscape-font), monospace;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
@@ -136,7 +134,7 @@
|
||||
}
|
||||
|
||||
:global(.code) {
|
||||
font-family: "Fira Mono", monospace;
|
||||
font-family: var(--monoscape-font), monospace;
|
||||
|
||||
:global(.lang) {
|
||||
// height: 8px;
|
||||
|
||||
@@ -10,7 +10,7 @@ export default styled.button<Props>`
|
||||
padding: 8px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-family: inherit;
|
||||
|
||||
transition: 0.2s ease opacity;
|
||||
transition: 0.2s ease background-color;
|
||||
|
||||
@@ -3,6 +3,7 @@ import styled from "styled-components";
|
||||
export default styled.select`
|
||||
padding: 8px;
|
||||
border-radius: 2px;
|
||||
font-family: inherit;
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export default styled.input<Props>`
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
|
||||
font-family: inherit;
|
||||
color: var(--foreground);
|
||||
background: var(--primary-background);
|
||||
transition: 0.2s ease background-color;
|
||||
|
||||
@@ -39,8 +39,10 @@ export default styled.textarea<TextAreaProps>`
|
||||
}
|
||||
|
||||
${ props => props.code ? css`
|
||||
font-family: 'Fira Mono', 'Courier New', Courier, monospace;
|
||||
font-family: var(--monoscape-font-font), monospace;
|
||||
` : css`
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-family: inherit;
|
||||
` }
|
||||
|
||||
font-variant-ligatures: var(--ligatures);
|
||||
`;
|
||||
|
||||
@@ -32,18 +32,158 @@ export type Variables =
|
||||
| "status-streaming"
|
||||
| "status-invisible";
|
||||
|
||||
export type Fonts = 'Open Sans' | 'Inter' | 'Atkinson Hyperlegible' | 'Roboto' | 'Noto Sans' | 'Lato' | 'Bree Serif' | 'Montserrat' | 'Poppins' | 'Raleway' | 'Ubuntu' | 'Comic Neue';
|
||||
export type MonoscapeFonts = 'Fira Code' | 'Roboto Mono' | 'Source Code Pro' | 'Space Mono' | 'Ubuntu Mono';
|
||||
|
||||
export type Theme = {
|
||||
[variable in Variables]: string;
|
||||
} & {
|
||||
light?: boolean;
|
||||
font?: Fonts;
|
||||
css?: string;
|
||||
monoscapeFont?: MonoscapeFonts;
|
||||
};
|
||||
|
||||
export interface ThemeOptions {
|
||||
preset?: string;
|
||||
ligatures?: boolean;
|
||||
custom?: Partial<Theme>;
|
||||
}
|
||||
|
||||
export const FONTS: Record<Fonts, { name: string, load: () => void }> = {
|
||||
"Open Sans": {
|
||||
name: "Open Sans",
|
||||
load: async () => {
|
||||
await import("@fontsource/open-sans/300.css");
|
||||
await import("@fontsource/open-sans/400.css");
|
||||
await import("@fontsource/open-sans/600.css");
|
||||
await import("@fontsource/open-sans/700.css");
|
||||
await import("@fontsource/open-sans/400-italic.css");
|
||||
}
|
||||
},
|
||||
Inter: {
|
||||
name: "Inter",
|
||||
load: async () => {
|
||||
await import("@fontsource/inter/300.css");
|
||||
await import("@fontsource/inter/400.css");
|
||||
await import("@fontsource/inter/600.css");
|
||||
await import("@fontsource/inter/700.css");
|
||||
}
|
||||
},
|
||||
"Atkinson Hyperlegible": {
|
||||
name: "Atkinson Hyperlegible",
|
||||
load: async () => {
|
||||
await import("@fontsource/atkinson-hyperlegible/400.css");
|
||||
await import("@fontsource/atkinson-hyperlegible/700.css");
|
||||
await import("@fontsource/atkinson-hyperlegible/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Roboto": {
|
||||
name: "Roboto",
|
||||
load: async () => {
|
||||
await import("@fontsource/roboto/400.css");
|
||||
await import("@fontsource/roboto/700.css");
|
||||
await import("@fontsource/roboto/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Noto Sans": {
|
||||
name: "Noto Sans",
|
||||
load: async () => {
|
||||
await import("@fontsource/noto-sans/400.css");
|
||||
await import("@fontsource/noto-sans/700.css");
|
||||
await import("@fontsource/noto-sans/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Bree Serif": {
|
||||
name: "Bree Serif",
|
||||
load: () => import("@fontsource/bree-serif/400.css")
|
||||
},
|
||||
"Lato": {
|
||||
name: "Lato",
|
||||
load: async () => {
|
||||
await import("@fontsource/lato/300.css");
|
||||
await import("@fontsource/lato/400.css");
|
||||
await import("@fontsource/lato/700.css");
|
||||
await import("@fontsource/lato/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Montserrat": {
|
||||
name: "Montserrat",
|
||||
load: async () => {
|
||||
await import("@fontsource/montserrat/300.css");
|
||||
await import("@fontsource/montserrat/400.css");
|
||||
await import("@fontsource/montserrat/600.css");
|
||||
await import("@fontsource/montserrat/700.css");
|
||||
await import("@fontsource/montserrat/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Poppins": {
|
||||
name: "Poppins",
|
||||
load: async () => {
|
||||
await import("@fontsource/poppins/300.css");
|
||||
await import("@fontsource/poppins/400.css");
|
||||
await import("@fontsource/poppins/600.css");
|
||||
await import("@fontsource/poppins/700.css");
|
||||
await import("@fontsource/poppins/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Raleway": {
|
||||
name: "Raleway",
|
||||
load: async () => {
|
||||
await import("@fontsource/raleway/300.css");
|
||||
await import("@fontsource/raleway/400.css");
|
||||
await import("@fontsource/raleway/600.css");
|
||||
await import("@fontsource/raleway/700.css");
|
||||
await import("@fontsource/raleway/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Ubuntu": {
|
||||
name: "Ubuntu",
|
||||
load: async () => {
|
||||
await import("@fontsource/ubuntu/300.css");
|
||||
await import("@fontsource/ubuntu/400.css");
|
||||
await import("@fontsource/ubuntu/500.css");
|
||||
await import("@fontsource/ubuntu/700.css");
|
||||
await import("@fontsource/ubuntu/400-italic.css");
|
||||
}
|
||||
},
|
||||
"Comic Neue": {
|
||||
name: "Comic Neue",
|
||||
load: async () => {
|
||||
await import("@fontsource/comic-neue/300.css");
|
||||
await import("@fontsource/comic-neue/400.css");
|
||||
await import("@fontsource/comic-neue/700.css");
|
||||
await import("@fontsource/comic-neue/400-italic.css");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const MONOSCAPE_FONTS: Record<MonoscapeFonts, { name: string, load: () => void }> = {
|
||||
"Fira Code": {
|
||||
name: "Fira Code",
|
||||
load: () => import("@fontsource/fira-code/400.css")
|
||||
},
|
||||
"Roboto Mono": {
|
||||
name: "Roboto Mono",
|
||||
load: () => import("@fontsource/roboto-mono/400.css")
|
||||
},
|
||||
"Source Code Pro": {
|
||||
name: "Source Code Pro",
|
||||
load: () => import("@fontsource/source-code-pro/400.css")
|
||||
},
|
||||
"Space Mono": {
|
||||
name: "Space Mono",
|
||||
load: () => import("@fontsource/space-mono/400.css")
|
||||
},
|
||||
"Ubuntu Mono": {
|
||||
name: "Ubuntu Mono",
|
||||
load: () => import("@fontsource/ubuntu-mono/400.css")
|
||||
}
|
||||
};
|
||||
|
||||
export const FONT_KEYS = Object.keys(FONTS).sort();
|
||||
export const MONOSCAPE_FONT_KEYS = Object.keys(MONOSCAPE_FONTS).sort();
|
||||
|
||||
// Generated from https://gitlab.insrt.uk/revolt/community/themes
|
||||
export const PRESETS: { [key: string]: Theme } = {
|
||||
light: {
|
||||
@@ -120,15 +260,32 @@ interface Props {
|
||||
options?: ThemeOptions;
|
||||
}
|
||||
|
||||
function Theme(props: Props) {
|
||||
function Theme({ children, options }: Props) {
|
||||
const theme: Theme = {
|
||||
...PRESETS["dark"],
|
||||
...(PRESETS as any)[props.options?.preset as any],
|
||||
...props.options?.custom
|
||||
...(PRESETS as any)[options?.preset as any],
|
||||
...options?.custom
|
||||
};
|
||||
|
||||
const root = document.documentElement.style;
|
||||
useEffect(() => {
|
||||
const resize = () => document.documentElement.style.setProperty('--app-height', `${window.innerHeight}px`);
|
||||
const font = theme.font ?? 'Inter';
|
||||
root.setProperty('--font', `"${font}"`);
|
||||
FONTS[font].load();
|
||||
}, [ theme.font ]);
|
||||
|
||||
useEffect(() => {
|
||||
const font = theme.monoscapeFont ?? 'Fira Code';
|
||||
root.setProperty('--monoscape-font', `"${font}"`);
|
||||
MONOSCAPE_FONTS[font].load();
|
||||
}, [ theme.monoscapeFont ]);
|
||||
|
||||
useEffect(() => {
|
||||
root.setProperty('--ligatures', options?.ligatures ? 'normal' : 'none');
|
||||
}, [ options?.ligatures ]);
|
||||
|
||||
useEffect(() => {
|
||||
const resize = () => root.setProperty('--app-height', `${window.innerHeight}px`);
|
||||
resize();
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
@@ -151,7 +308,7 @@ function Theme(props: Props) {
|
||||
{theme.css && (
|
||||
<style dangerouslySetInnerHTML={{ __html: theme.css }} />
|
||||
)}
|
||||
{props.children}
|
||||
{ children }
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
user-select: all;
|
||||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
font-family: "Fira Mono";
|
||||
font-family: var(--monoscape-font);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
margin: 1rem 12px 0;
|
||||
font-size: 10px;
|
||||
color: var(--secondary-foreground);
|
||||
font-family: "Fira Mono", monospace;
|
||||
font-family: var(--monoscape-font), monospace;
|
||||
user-select: text;
|
||||
|
||||
display: grid;
|
||||
|
||||
@@ -2,15 +2,18 @@ import { Text } from "preact-i18n";
|
||||
import styles from "./Panes.module.scss";
|
||||
import { debounce } from "../../../lib/debounce";
|
||||
import Button from "../../../components/ui/Button";
|
||||
import Checkbox from "../../../components/ui/Checkbox";
|
||||
import ComboBox from "../../../components/ui/ComboBox";
|
||||
import InputBox from "../../../components/ui/InputBox";
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { WithDispatcher } from "../../../redux/reducers";
|
||||
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
||||
import ColourSwatches from "../../../components/ui/ColourSwatches";
|
||||
import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
|
||||
import { Theme, ThemeContext, ThemeOptions } from "../../../context/Theme";
|
||||
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import CollapsibleSection from "../../../components/common/CollapsibleSection";
|
||||
import { FONTS, FONT_KEYS, MONOSCAPE_FONTS, MONOSCAPE_FONT_KEYS, Theme, ThemeContext, ThemeOptions } from "../../../context/Theme";
|
||||
|
||||
// @ts-ignore
|
||||
import pSBC from 'shade-blend-color';
|
||||
@@ -22,7 +25,6 @@ import mutantSVG from '../assets/mutant_emoji.svg';
|
||||
import notoSVG from '../assets/noto_emoji.svg';
|
||||
import openmojiSVG from '../assets/openmoji_emoji.svg';
|
||||
import twemojiSVG from '../assets/twemoji_emoji.svg';
|
||||
import CollapsibleSection from "../../../components/common/CollapsibleSection";
|
||||
|
||||
interface Props {
|
||||
settings: Settings;
|
||||
@@ -130,6 +132,25 @@ export function Component(props: Props & WithDispatcher) {
|
||||
</Radio>
|
||||
</div>*/}
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.font" />
|
||||
</h3>
|
||||
<ComboBox value={theme.font} onChange={e => setTheme({ custom: { font: e.currentTarget.value as any } })}>
|
||||
{
|
||||
FONT_KEYS
|
||||
.map(key =>
|
||||
<option value={key}>{ FONTS[key as keyof typeof FONTS].name }</option>
|
||||
)
|
||||
}
|
||||
</ComboBox>
|
||||
<p>
|
||||
<Checkbox checked={props.settings.theme?.ligatures === true}
|
||||
onChange={() => setTheme({ ligatures: !props.settings.theme?.ligatures })}
|
||||
description={<Text id="app.settings.pages.appearance.ligatures_desc" />}>
|
||||
<Text id="app.settings.pages.appearance.ligatures" />
|
||||
</Checkbox>
|
||||
</p>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.emoji_pack" />
|
||||
</h3>
|
||||
@@ -260,6 +281,19 @@ export function Component(props: Props & WithDispatcher) {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.mono_font" />
|
||||
</h3>
|
||||
<ComboBox value={theme.monoscapeFont} onChange={e => setTheme({ custom: { monoscapeFont: e.currentTarget.value as any } })}>
|
||||
{
|
||||
MONOSCAPE_FONT_KEYS
|
||||
.map(key =>
|
||||
<option value={key}>{ MONOSCAPE_FONTS[key as keyof typeof MONOSCAPE_FONTS].name }</option>
|
||||
)
|
||||
}
|
||||
</ComboBox>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.custom_css" />
|
||||
</h3>
|
||||
|
||||
@@ -65,7 +65,7 @@ export function settings(
|
||||
return {
|
||||
...state,
|
||||
theme: {
|
||||
...filter(state.theme, ["custom", "preset"]),
|
||||
...filter(state.theme, ["custom", "preset", "ligatures"]),
|
||||
...action.theme,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@import "@fontsource/open-sans/300.css";
|
||||
@import "@fontsource/open-sans/400.css";
|
||||
@import "@fontsource/open-sans/600.css";
|
||||
@import "@fontsource/open-sans/700.css";
|
||||
|
||||
@import "@fontsource/open-sans/400-italic.css";
|
||||
@@ -18,7 +18,9 @@ html,
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: "Open Sans", sans-serif;
|
||||
font-family: var(--font), sans-serif;
|
||||
font-variant-ligatures: var(--ligatures);
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
caret-color: var(--accent);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
:root {
|
||||
--ligatures: none;
|
||||
--font: "Open Sans";
|
||||
--app-height: 100vh;
|
||||
--codeblock-font: "Fira Code";
|
||||
--sidebar-active: var(--secondary-background);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@import "variables";
|
||||
@import "context-menu";
|
||||
@import "elements";
|
||||
@import "fonts";
|
||||
@import "page";
|
||||
|
||||
@import "react-overlapping-panels/dist";
|
||||
|
||||
Reference in New Issue
Block a user