Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect: Enable font configuration #21965

Merged
merged 11 commits into from
Feb 22, 2023
33 changes: 13 additions & 20 deletions web/packages/teleterm/src/services/config/configService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { createConfigStore } from './configStore';

const createAppConfigSchema = (platform: Platform) => {
const defaultKeymap = getDefaultKeymap(platform);
const defaultFonts = getDefaultFonts(platform);
const defaultTerminalFont = getDefaultTerminalFont(platform);

// Important: all keys except 'usageReporting.enabled' are currently not
// configurable by the user. Before we let the user configure them,
Expand Down Expand Up @@ -85,12 +85,14 @@ const createAppConfigSchema = (platform: Platform) => {
'keymap.openQuickInput': omitStoredConfigValue(
z.string().default(defaultKeymap['open-quick-input'])
),
'fonts.sansSerifFamily': omitStoredConfigValue(
z.string().default(defaultFonts['sansSerif'])
),
'fonts.monoFamily': omitStoredConfigValue(
z.string().default(defaultFonts['mono'])
),
/**
* This value can be provided by the user and is unsanitized. This means that it cannot be directly interpolated
* in a styled component or used in CSS, as it may inject malicious CSS code.
* Before using it, sanitize it with `CSS.escape` or pass it as a `style` prop.
* Read more https://frontarm.com/james-k-nelson/how-can-i-use-css-in-js-securely/.
*/
'terminal.fontFamily': z.string().default(defaultTerminalFont),
'terminal.fontSize': z.number().int().min(5).max(100).default(15),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah damn, forgot to say that I don't think we need to be as strict here, we could do like min 1 and max, idk, 256. I'd rather allow super tiny values since it doesn't change anything for us but might enable some niche use cases for the users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do it.

});
};

Expand Down Expand Up @@ -189,23 +191,14 @@ const getDefaultKeymap = (platform: Platform) => {
}
};

function getDefaultFonts(platform: Platform) {
function getDefaultTerminalFont(platform: Platform) {
switch (platform) {
case 'win32':
return {
sansSerif: "system-ui, 'Segoe WPC', 'Segoe UI', sans-serif",
mono: "'Consolas', 'Courier New', monospace",
};
return "'Consolas', 'Courier New', monospace";
case 'linux':
return {
sansSerif: "system-ui, 'Ubuntu', 'Droid Sans', sans-serif",
mono: "'Droid Sans Mono', 'Courier New', monospace, 'Droid Sans Fallback'",
};
return "'Droid Sans Mono', 'Courier New', monospace, 'Droid Sans Fallback'";
case 'darwin':
return {
sansSerif: '-apple-system, BlinkMacSystemFont, sans-serif',
mono: "Menlo, Monaco, 'Courier New', monospace",
};
return "Menlo, Monaco, 'Courier New', monospace";
}
}

Expand Down
13 changes: 2 additions & 11 deletions web/packages/teleterm/src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,15 @@ import { AppInitializer } from 'teleterm/ui/AppInitializer';
import CatchError from './components/CatchError';
import AppContextProvider from './appContextProvider';
import AppContext from './appContext';
import ThemeProvider from './ThemeProvider';
import { ThemeProvider } from './ThemeProvider';

export const App: React.FC<{ ctx: AppContext }> = ({ ctx }) => {
return (
<StyledApp>
<CatchError>
<DndProvider backend={HTML5Backend}>
<AppContextProvider value={ctx}>
<ThemeProvider
fonts={{
mono: ctx.mainProcessClient.configService.get(
'fonts.monoFamily'
).value,
sansSerif: ctx.mainProcessClient.configService.get(
'fonts.sansSerifFamily'
).value,
}}
>
<ThemeProvider>
<AppInitializer />
</ThemeProvider>
</AppContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export function CliCommand({ cliCommand, onRun, isLoading }: CliCommandProps) {
color={shouldDisplayIsLoading ? 'text.secondary' : 'text.primary'}
width="100%"
css={`
font-family: ${props => props.theme.fonts.mono};
gzdunek marked this conversation as resolved.
Show resolved Hide resolved
overflow: auto;
white-space: pre;
word-break: break-all;
font-size: 12px;
font-family: ${props => props.theme.fonts.mono};
`}
>
<Box mr="1">{`$`}</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
import Document from 'teleterm/ui/Document';
import { useAppContext } from 'teleterm/ui/appContextProvider';

import Terminal from './Terminal';
import { Terminal } from './Terminal';
import DocumentReconnect from './DocumentReconnect';
import useDocTerminal, { Props } from './useDocumentTerminal';
import { useTshFileTransferHandlers } from './useTshFileTransferHandlers';
Expand All @@ -40,10 +40,15 @@ export default function DocumentTerminalContainer({ doc, visible }: Props) {

export function DocumentTerminal(props: Props & { visible: boolean }) {
const ctx = useAppContext();
const { configService } = ctx.mainProcessClient;
const { visible, doc } = props;
const state = useDocTerminal(doc);
const ptyProcess = state.data?.ptyProcess;
const { upload, download } = useTshFileTransferHandlers();
const unsanitizedTerminalFontFamily = configService.get(
'terminal.fontFamily'
).value;
const terminalFontSize = configService.get('terminal.fontSize').value;

return (
<Document
Expand Down Expand Up @@ -101,6 +106,8 @@ export function DocumentTerminal(props: Props & { visible: boolean }) {
<Terminal
ptyProcess={ptyProcess}
visible={props.visible}
unsanitizedFontFamily={unsanitizedTerminalFontFamily}
fontSize={terminalFontSize}
onEnterKey={state.data.refreshTitle}
/>
</>
Expand Down
32 changes: 21 additions & 11 deletions web/packages/teleterm/src/ui/DocumentTerminal/Terminal/Terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,35 @@ limitations under the License.

import { debounce } from 'lodash';
import React, { useEffect, useRef } from 'react';
import styled, { useTheme } from 'styled-components';
import styled from 'styled-components';
import { Box, Flex } from 'design';

import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost';

import XTermCtrl from './ctrl';

export default function Terminal(props: Props) {
type TerminalProps = {
ptyProcess: IPtyProcess;
visible: boolean;
/**
* This value can be provided by the user and is unsanitized. This means that it cannot be directly interpolated
* in a styled component or used in CSS, as it may inject malicious CSS code.
* Before using it, sanitize it with `CSS.escape` or pass it as a `style` prop.
* Read more https://frontarm.com/james-k-nelson/how-can-i-use-css-in-js-securely/.
*/
unsanitizedFontFamily: string;
fontSize: number;
onEnterKey?(): void;
};

export function Terminal(props: TerminalProps) {
const refElement = useRef<HTMLElement>();
const refCtrl = useRef<XTermCtrl>();
const fontFamily = useTheme().fonts.mono;

useEffect(() => {
const ctrl = new XTermCtrl(props.ptyProcess, {
el: refElement.current,
fontFamily,
fontSize: props.fontSize,
});

ctrl.open();
Expand Down Expand Up @@ -70,17 +83,14 @@ export default function Terminal(props: Props) {
width="100%"
style={{ overflow: 'hidden' }}
>
<StyledXterm ref={refElement} />
<StyledXterm
ref={refElement}
style={{ fontFamily: props.unsanitizedFontFamily }}
/>
</Flex>
);
}

type Props = {
ptyProcess: IPtyProcess;
visible: boolean;
onEnterKey?(): void;
};

const StyledXterm = styled(Box)`
height: 100%;
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const WINDOW_RESIZE_DEBOUNCE_DELAY = 200;

type Options = {
el: HTMLElement;
fontFamily?: string;
fontSize: number;
};

export default class TtyTerminal {
Expand All @@ -51,7 +51,8 @@ export default class TtyTerminal {
open(): void {
this.term = new Terminal({
cursorBlink: false,
fontFamily: this.options.fontFamily,
fontFamily: this.el.style.fontFamily, // grab font family from style to prevent injecting malicious CSS
ravicious marked this conversation as resolved.
Show resolved Hide resolved
gzdunek marked this conversation as resolved.
Show resolved Hide resolved
fontSize: this.options.fontSize,
scrollback: 5000,
theme: {
background: theme.colors.primary.darker,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import Terminal from './Terminal';

export default Terminal;
export { Terminal } from './Terminal';
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import React from 'react';
import { Text } from 'design';

import styled from 'styled-components';
import styled, { useTheme } from 'styled-components';

import Document from 'teleterm/ui/Document';
import { useKeyboardShortcutFormatters } from 'teleterm/ui/services/keyboardShortcuts';
Expand Down Expand Up @@ -67,20 +67,26 @@ export function KeyboardShortcutsPanel() {
}

function Entry(props: { title: string; shortcut: string }) {
const theme = useTheme();
return (
<>
<Text textAlign="right" color="light" typography="subtitle1" py="4px">
{props.title}
</Text>
<MonoText bg="primary.main" textAlign="left" px="12px" py="4px">
<MonoText
style={{ fontFamily: theme.fonts.unsanitizedMono }}
gzdunek marked this conversation as resolved.
Show resolved Hide resolved
bg="primary.main"
textAlign="left"
px="12px"
py="4px"
>
{props.shortcut}
</MonoText>
</>
);
}

const MonoText = styled(Text)`
font-family: ${props => props.theme.fonts.mono};
width: fit-content;
opacity: 0.7;
border-radius: 4px;
Expand Down
41 changes: 14 additions & 27 deletions web/packages/teleterm/src/ui/ThemeProvider/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,21 @@ limitations under the License.
*/

import React from 'react';
import { ThemeProvider, StyleSheetManager } from 'styled-components';
import {
ThemeProvider as StyledThemeProvider,
StyleSheetManager,
} from 'styled-components';

import { GlobalStyle } from './globals';
import theme from './theme';

type TeletermThemeProvider = {
fonts: {
mono: string;
sansSerif: string;
};
};

const TeletermThemeProvider: React.FC<TeletermThemeProvider> = props => {
if (props?.fonts) {
theme.font = props.fonts.sansSerif;
theme.fonts = props.fonts;
}

return (
<ThemeProvider theme={theme}>
<StyleSheetManager disableVendorPrefixes>
<React.Fragment>
<GlobalStyle />
{props.children}
</React.Fragment>
</StyleSheetManager>
</ThemeProvider>
);
};

export default TeletermThemeProvider;
export const ThemeProvider: React.FC = props => (
<StyledThemeProvider theme={theme}>
<StyleSheetManager disableVendorPrefixes>
<React.Fragment>
<GlobalStyle />
{props.children}
</React.Fragment>
</StyleSheetManager>
</StyledThemeProvider>
);
3 changes: 1 addition & 2 deletions web/packages/teleterm/src/ui/ThemeProvider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import ThemeProvider from './ThemeProvider';
export default ThemeProvider;
export { ThemeProvider } from './ThemeProvider';
9 changes: 7 additions & 2 deletions web/packages/teleterm/src/ui/ThemeProvider/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,16 @@ const borders = [
'32px solid',
];

const sansSerif = 'system-ui';

const theme = {
colors,
typography,
font: fonts.sansSerif,
fonts: fonts,
font: sansSerif,
fonts: {
sansSerif,
mono: fonts.mono,
},
fontWeights,
fontSizes,
space,
Expand Down