insomnia/packages/insomnia-app/app/plugins/misc.ts

352 lines
9.0 KiB
TypeScript
Raw Normal View History

import Color from 'color';
2021-07-22 23:04:56 +00:00
import { unreachableCase } from 'ts-assert-unreachable';
2020-04-26 20:33:39 +00:00
import { getAppDefaultTheme } from '../common/constants';
2021-07-22 23:04:56 +00:00
import { render, THROW_ON_ERROR } from '../common/render';
import { ThemeSettings } from '../models/settings';
2021-07-22 23:04:56 +00:00
import type { Theme } from './index';
import { ColorScheme, getThemes } from './index';
interface ThemeBlock {
background?: {
default: string;
success?: string;
notice?: string;
warning?: string;
danger?: string;
surprise?: string;
info?: string;
};
foreground?: {
default: string;
success?: string;
notice?: string;
warning?: string;
danger?: string;
surprise?: string;
info?: string;
};
highlight?: {
default: string;
xxs?: string;
xs?: string;
sm?: string;
md?: string;
lg?: string;
xl?: string;
};
}
type ThemeInner = ThemeBlock & {
rawCss?: string;
styles?:
| {
dialog?: ThemeBlock;
dialogFooter?: ThemeBlock;
dialogHeader?: ThemeBlock;
dropdown?: ThemeBlock;
editor?: ThemeBlock;
link?: ThemeBlock;
overlay?: ThemeBlock;
pane?: ThemeBlock;
paneHeader?: ThemeBlock;
sidebar?: ThemeBlock;
sidebarHeader?: ThemeBlock;
sidebarList?: ThemeBlock;
tooltip?: ThemeBlock;
transparentOverlay?: ThemeBlock;
}
| null;
};
export interface PluginTheme {
name: string;
displayName: string;
theme: ThemeInner;
}
export async function generateThemeCSS(theme: PluginTheme) {
const renderedTheme: ThemeInner = await render(
theme.theme,
theme.theme,
null,
THROW_ON_ERROR,
theme.name,
);
const n = theme.name;
let css = '';
2020-02-05 15:42:51 +00:00
// For the top-level variables, merge with the base theme to ensure that
// we have everything we need.
css += wrapStyles(
n,
'',
getThemeBlockCSS({
...renderedTheme,
background: { ..._baseTheme.background, ...renderedTheme.background },
foreground: { ..._baseTheme.foreground, ...renderedTheme.foreground },
highlight: { ..._baseTheme.highlight, ...renderedTheme.highlight },
}),
);
if (renderedTheme.styles) {
const styles = renderedTheme.styles;
// Dropdown Menus
2020-04-26 20:33:39 +00:00
css += wrapStyles(
n,
'.theme--dropdown__menu',
getThemeBlockCSS(styles.dropdown || styles.dialog),
);
// Tooltips
2020-04-26 20:33:39 +00:00
css += wrapStyles(n, '.theme--tooltip', getThemeBlockCSS(styles.tooltip || styles.dialog));
// Overlay
css += wrapStyles(
n,
'.theme--transparent-overlay',
getThemeBlockCSS(styles.transparentOverlay),
);
// Dialogs
css += wrapStyles(n, '.theme--dialog', getThemeBlockCSS(styles.dialog));
2018-10-17 16:42:33 +00:00
css += wrapStyles(n, '.theme--dialog__header', getThemeBlockCSS(styles.dialogHeader));
css += wrapStyles(n, '.theme--dialog__footer', getThemeBlockCSS(styles.dialogFooter));
// Panes
css += wrapStyles(n, '.theme--pane', getThemeBlockCSS(styles.pane));
2018-10-17 16:42:33 +00:00
css += wrapStyles(n, '.theme--pane__header', getThemeBlockCSS(styles.paneHeader));
// @ts-expect-error -- TSCONVERSION
css += wrapStyles(n, '.theme--app-header', getThemeBlockCSS(styles.appHeader));
// Sidebar Styles
css += wrapStyles(n, '.theme--sidebar', getThemeBlockCSS(styles.sidebar));
2018-10-17 16:42:33 +00:00
css += wrapStyles(n, '.theme--sidebar__list', getThemeBlockCSS(styles.sidebarList));
css += wrapStyles(n, '.theme--sidebar__header', getThemeBlockCSS(styles.sidebarHeader));
// Link
css += wrapStyles(n, '.theme--link', getThemeBlockCSS(styles.link));
// Code Editors
css += wrapStyles(n, '.theme--editor', getThemeBlockCSS(styles.editor));
// HACK: Dialog styles for CodeMirror dialogs too
css += wrapStyles(n, '.CodeMirror-info', getThemeBlockCSS(styles.dialog));
}
2018-07-18 00:48:10 +00:00
return css;
}
function getThemeBlockCSS(block?: ThemeBlock) {
if (!block) {
return '';
}
const indent = '\t';
let css = '';
const addColorVar = (variable: string, value?: string) => {
if (!value) {
return;
}
try {
const parsedColor = Color(value);
2020-02-04 20:08:05 +00:00
const rgb = parsedColor.rgb();
addVar(variable, rgb.string());
addVar(`${variable}-rgb`, rgb.array().join(', '));
} catch (err) {
console.log('Failed to parse theme color', value);
}
};
const addVar = (variable: string, value?: string) => {
if (!value) {
return;
}
css += `${indent}--${variable}: ${value};\n`;
};
const addComment = comment => {
css += `${indent}/* ${comment} */\n`;
};
const addNewLine = () => {
css += '\n';
};
if (block.background) {
const { background } = block;
addComment('Background');
addColorVar('color-bg', background.default);
addColorVar('color-success', background.success);
addColorVar('color-notice', background.notice);
addColorVar('color-warning', background.warning);
addColorVar('color-danger', background.danger);
addColorVar('color-surprise', background.surprise);
addColorVar('color-info', background.info);
addNewLine();
}
if (block.foreground) {
const { foreground } = block;
addComment('Foreground');
addColorVar('color-font', foreground.default);
addColorVar('color-font-success', foreground.success);
addColorVar('color-font-notice', foreground.notice);
addColorVar('color-font-warning', foreground.warning);
addColorVar('color-font-danger', foreground.danger);
addColorVar('color-font-surprise', foreground.surprise);
addColorVar('color-font-info', foreground.info);
addNewLine();
}
if (block.highlight) {
const { highlight } = block;
addComment('Highlight');
addColorVar('hl', highlight.default);
addColorVar('hl-xxs', highlight.xxs);
addColorVar('hl-xs', highlight.xs);
addColorVar('hl-sm', highlight.sm);
addColorVar('hl-md', highlight.md);
addColorVar('hl-lg', highlight.lg);
addColorVar('hl-xl', highlight.xl);
addNewLine();
}
return css.replace(/\s+$/, '');
}
function wrapStyles(theme: string, selector: string, styles: string) {
if (!styles) {
return '';
}
return [
`[theme="${theme}"] ${selector}, `,
`[subtheme="${theme}"] ${selector ? selector + '--sub' : ''} {`,
styles,
'}',
'',
'',
].join('\n');
}
export function getColorScheme({ autoDetectColorScheme }: ThemeSettings): ColorScheme {
if (!autoDetectColorScheme) {
Add OS dark mode support (#2868) * Add settings for color scheme detection and themes Default light and dark themes can still be changed. For now its studio-light and default for core, and studio-dark and studio-light for designer. * Add color scheme type and supporting methods The detection of dark scheme is based on the background color at the moment. This seems to work pretty well, but is not an ideal solution. I think themes should at least get to override this. * Add support for choosing light and dark theme to settings This adds a checkbox to the theme settings that determines whether we use the OS color scheme. If we don't (default) everything stays the same as before. If we do, themes are rendered in two groups. One for the light themes and one for the dark themes. They can be chosen independently. None of this overrides the default theme choice. * Add padding to the theme settings tab Themes are still aligned by adding negative margin. A bit of a hack, open for suggestions. * Update theme on OS color scheme change * Replace usages of setTheme with applyColorScheme This makes sure that we don't override the user's choice. * Update packages/insomnia-app/app/plugins/misc.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove dark mode heuristic * Remove unused button value * Update theme settings design * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Replace object literal lookups Do not use object literal lookups to make code more readable * Remove unused parameter * Disable default theme select when auto detection is enabled * Fix imports after rebase * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove theme header * Disable hover animation and border on disabled theme buttons * Clean up double negation in css Replace :not(:disabled) with :enabled. Not sure what I was thinking there. * Update index.js Co-authored-by: Opender Singh <opender94@gmail.com> Co-authored-by: Opender Singh <opender.singh@konghq.com>
2021-03-13 03:38:54 +00:00
return 'default';
}
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
Add OS dark mode support (#2868) * Add settings for color scheme detection and themes Default light and dark themes can still be changed. For now its studio-light and default for core, and studio-dark and studio-light for designer. * Add color scheme type and supporting methods The detection of dark scheme is based on the background color at the moment. This seems to work pretty well, but is not an ideal solution. I think themes should at least get to override this. * Add support for choosing light and dark theme to settings This adds a checkbox to the theme settings that determines whether we use the OS color scheme. If we don't (default) everything stays the same as before. If we do, themes are rendered in two groups. One for the light themes and one for the dark themes. They can be chosen independently. None of this overrides the default theme choice. * Add padding to the theme settings tab Themes are still aligned by adding negative margin. A bit of a hack, open for suggestions. * Update theme on OS color scheme change * Replace usages of setTheme with applyColorScheme This makes sure that we don't override the user's choice. * Update packages/insomnia-app/app/plugins/misc.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove dark mode heuristic * Remove unused button value * Update theme settings design * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Replace object literal lookups Do not use object literal lookups to make code more readable * Remove unused parameter * Disable default theme select when auto detection is enabled * Fix imports after rebase * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove theme header * Disable hover animation and border on disabled theme buttons * Clean up double negation in css Replace :not(:disabled) with :enabled. Not sure what I was thinking there. * Update index.js Co-authored-by: Opender Singh <opender94@gmail.com> Co-authored-by: Opender Singh <opender.singh@konghq.com>
2021-03-13 03:38:54 +00:00
return 'light';
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
Add OS dark mode support (#2868) * Add settings for color scheme detection and themes Default light and dark themes can still be changed. For now its studio-light and default for core, and studio-dark and studio-light for designer. * Add color scheme type and supporting methods The detection of dark scheme is based on the background color at the moment. This seems to work pretty well, but is not an ideal solution. I think themes should at least get to override this. * Add support for choosing light and dark theme to settings This adds a checkbox to the theme settings that determines whether we use the OS color scheme. If we don't (default) everything stays the same as before. If we do, themes are rendered in two groups. One for the light themes and one for the dark themes. They can be chosen independently. None of this overrides the default theme choice. * Add padding to the theme settings tab Themes are still aligned by adding negative margin. A bit of a hack, open for suggestions. * Update theme on OS color scheme change * Replace usages of setTheme with applyColorScheme This makes sure that we don't override the user's choice. * Update packages/insomnia-app/app/plugins/misc.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove dark mode heuristic * Remove unused button value * Update theme settings design * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Replace object literal lookups Do not use object literal lookups to make code more readable * Remove unused parameter * Disable default theme select when auto detection is enabled * Fix imports after rebase * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove theme header * Disable hover animation and border on disabled theme buttons * Clean up double negation in css Replace :not(:disabled) with :enabled. Not sure what I was thinking there. * Update index.js Co-authored-by: Opender Singh <opender94@gmail.com> Co-authored-by: Opender Singh <opender.singh@konghq.com>
2021-03-13 03:38:54 +00:00
return 'dark';
}
return 'default';
}
export async function applyColorScheme(settings: ThemeSettings) {
Add OS dark mode support (#2868) * Add settings for color scheme detection and themes Default light and dark themes can still be changed. For now its studio-light and default for core, and studio-dark and studio-light for designer. * Add color scheme type and supporting methods The detection of dark scheme is based on the background color at the moment. This seems to work pretty well, but is not an ideal solution. I think themes should at least get to override this. * Add support for choosing light and dark theme to settings This adds a checkbox to the theme settings that determines whether we use the OS color scheme. If we don't (default) everything stays the same as before. If we do, themes are rendered in two groups. One for the light themes and one for the dark themes. They can be chosen independently. None of this overrides the default theme choice. * Add padding to the theme settings tab Themes are still aligned by adding negative margin. A bit of a hack, open for suggestions. * Update theme on OS color scheme change * Replace usages of setTheme with applyColorScheme This makes sure that we don't override the user's choice. * Update packages/insomnia-app/app/plugins/misc.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove dark mode heuristic * Remove unused button value * Update theme settings design * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Replace object literal lookups Do not use object literal lookups to make code more readable * Remove unused parameter * Disable default theme select when auto detection is enabled * Fix imports after rebase * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove theme header * Disable hover animation and border on disabled theme buttons * Clean up double negation in css Replace :not(:disabled) with :enabled. Not sure what I was thinking there. * Update index.js Co-authored-by: Opender Singh <opender94@gmail.com> Co-authored-by: Opender Singh <opender.singh@konghq.com>
2021-03-13 03:38:54 +00:00
const scheme = getColorScheme(settings);
switch (scheme) {
case 'light':
await setTheme(settings.lightTheme);
break;
case 'dark':
await setTheme(settings.darkTheme);
break;
case 'default':
await setTheme(settings.theme);
break;
default:
unreachableCase(scheme);
Add OS dark mode support (#2868) * Add settings for color scheme detection and themes Default light and dark themes can still be changed. For now its studio-light and default for core, and studio-dark and studio-light for designer. * Add color scheme type and supporting methods The detection of dark scheme is based on the background color at the moment. This seems to work pretty well, but is not an ideal solution. I think themes should at least get to override this. * Add support for choosing light and dark theme to settings This adds a checkbox to the theme settings that determines whether we use the OS color scheme. If we don't (default) everything stays the same as before. If we do, themes are rendered in two groups. One for the light themes and one for the dark themes. They can be chosen independently. None of this overrides the default theme choice. * Add padding to the theme settings tab Themes are still aligned by adding negative margin. A bit of a hack, open for suggestions. * Update theme on OS color scheme change * Replace usages of setTheme with applyColorScheme This makes sure that we don't override the user's choice. * Update packages/insomnia-app/app/plugins/misc.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove dark mode heuristic * Remove unused button value * Update theme settings design * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/settings/theme.js Co-authored-by: Opender Singh <opender94@gmail.com> * Replace object literal lookups Do not use object literal lookups to make code more readable * Remove unused parameter * Disable default theme select when auto detection is enabled * Fix imports after rebase * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Update packages/insomnia-app/app/ui/components/modals/settings-modal.js Co-authored-by: Opender Singh <opender94@gmail.com> * Remove theme header * Disable hover animation and border on disabled theme buttons * Clean up double negation in css Replace :not(:disabled) with :enabled. Not sure what I was thinking there. * Update index.js Co-authored-by: Opender Singh <opender94@gmail.com> Co-authored-by: Opender Singh <opender.singh@konghq.com>
2021-03-13 03:38:54 +00:00
}
}
2018-07-18 00:48:10 +00:00
export async function setTheme(themeName: string) {
if (!document) {
return;
}
const head = document.head;
2018-07-18 00:48:10 +00:00
const body = document.body;
2018-07-18 00:48:10 +00:00
if (!head || !body) {
return;
}
const themes: Theme[] = await getThemes();
2020-04-26 20:33:39 +00:00
// If theme isn't installed for some reason, set to the default
if (!themes.find(t => t.theme.name === themeName)) {
console.log(`[theme] Theme not found ${themeName}`);
themeName = getAppDefaultTheme();
}
body.setAttribute('theme', themeName);
for (const theme of themes) {
2018-07-18 00:48:10 +00:00
let themeCSS = (await generateThemeCSS(theme.theme)) + '\n';
const { name } = theme.theme;
const { rawCss } = theme.theme.theme;
let s = document.querySelector(`style[data-theme-name="${name}"]`);
if (!s) {
s = document.createElement('style');
2018-07-18 00:48:10 +00:00
s.setAttribute('data-theme-name', name);
head.appendChild(s);
}
2018-07-18 00:48:10 +00:00
if (typeof rawCss === 'string' && name === themeName) {
themeCSS += '\n\n' + rawCss;
}
s.innerHTML = themeCSS;
}
}
export async function setFont(settings: Record<string, any>) {
if (!document) {
return;
}
const html = document.querySelector('html');
if (!html) {
return;
}
2018-10-17 18:20:46 +00:00
html.style.setProperty('--font-default', settings.fontInterface);
html.style.setProperty('--font-monospace', settings.fontMonospace);
html.style.setProperty('--font-ligatures', settings.fontVariantLigatures ? 'normal' : 'none');
html.style.setProperty('font-size', `${settings.fontSize}px`);
}
const _baseTheme = {
background: {
default: '#fff',
success: '#75ba24',
notice: '#d8c84d',
warning: '#ec8702',
danger: '#e15251',
2020-04-26 20:33:39 +00:00
surprise: '#6030BF',
info: '#20aed9',
},
foreground: {
default: '#666',
success: '#fff',
notice: '#fff',
warning: '#fff',
danger: '#fff',
surprise: '#fff',
info: '#fff',
},
highlight: {
default: 'rgba(130, 130, 130, 1)',
xxs: 'rgba(130, 130, 130, 0.05)',
xs: 'rgba(130, 130, 130, 0.1)',
sm: 'rgba(130, 130, 130, 0.25)',
md: 'rgba(130, 130, 130, 0.35)',
lg: 'rgba(130, 130, 130, 0.5)',
xl: 'rgba(130, 130, 130, 0.8)',
},
};