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(1).max(256).default(15),
});
};

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 @@ -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 @@ -15,23 +15,36 @@ limitations under the License.
*/

import React, { useEffect, useRef } from 'react';
import styled, { useTheme } from 'styled-components';
import styled from 'styled-components';
import { Box, Flex } from 'design';
import { debounce } from 'shared/utils/highbar';

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
11 changes: 9 additions & 2 deletions web/packages/teleterm/src/ui/DocumentTerminal/Terminal/ctrl.ts
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,14 @@ export default class TtyTerminal {
open(): void {
this.term = new Terminal({
cursorBlink: false,
fontFamily: this.options.fontFamily,
/**
* `fontFamily` can be provided by the user and is unsanitized. This means that it cannot be directly used in CSS,
* as it may inject malicious CSS code.
* To sanitize the value, we set it as a style on the HTML element and then read it from it.
* Read more https://frontarm.com/james-k-nelson/how-can-i-use-css-in-js-securely/.
*/
fontFamily: this.el.style.fontFamily,
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';
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