Add dark theme

This commit is contained in:
Steven 2019-01-24 17:40:38 -05:00
parent 6a92ef3781
commit 3287736872
5 changed files with 62 additions and 12 deletions

View File

@ -1,4 +1,6 @@
import { toClipboard } from 'https://cdn.jsdelivr.net/npm/copee@1.0.6/dist/copee.mjs'; import { toClipboard } from 'https://cdn.jsdelivr.net/npm/copee@1.0.6/dist/copee.mjs';
const nowBlack = 'https://assets.zeit.co/image/upload/front/assets/design/now-black.svg';
const nowWhite = 'https://assets.zeit.co/image/upload/front/assets/design/now-white.svg';
function debounce(func, wait) { function debounce(func, wait) {
var timeout; var timeout;
@ -86,6 +88,11 @@ const Toast = ({ show, message }) => {
); );
} }
const themeOptions = [
{ text: 'Light', value: 'light' },
{ text: 'Dark', value: 'dark' },
];
const fileTypeOptions = [ const fileTypeOptions = [
{ text: 'PNG', value: 'png' }, { text: 'PNG', value: 'png' },
{ text: 'JPEG', value: 'jpeg' }, { text: 'JPEG', value: 'jpeg' },
@ -103,19 +110,24 @@ const markdownOptions = [
]; ];
const App = (props, state, setState) => { const App = (props, state, setState) => {
const setLoadingState = (newState) => setState({ ...newState, loading: true }); const setLoadingState = (newState) => {
setState({ ...newState, loading: true });
};
const { const {
fileType = 'png', fileType = 'png',
fontSize = '75px', fontSize = '75px',
theme = 'light',
md = '1', md = '1',
text = '**Hello** World', text = '**Hello** World',
images=['https://assets.zeit.co/image/upload/front/assets/design/now-black.svg'], images=[nowBlack],
showToast = false, showToast = false,
messageToast = '', messageToast = '',
loading = true loading = true
} = state; } = state;
const url = new URL(window.location.hostname === 'localhost' ? 'https://og-image.now.sh' : window.location.origin); const url = new URL(window.location.hostname === 'localhost' ? 'https://og-image.now.sh' : window.location.origin);
url.pathname = `${encodeURIComponent(text)}.${fileType}`; url.pathname = `${encodeURIComponent(text)}.${fileType}`;
url.searchParams.append('theme', theme);
url.searchParams.append('md', md); url.searchParams.append('md', md);
url.searchParams.append('fontSize', fontSize); url.searchParams.append('fontSize', fontSize);
for (let image of images) { for (let image of images) {
@ -127,6 +139,21 @@ const App = (props, state, setState) => {
H('div', H('div',
{ className: 'pull-left' }, { className: 'pull-left' },
H('div', H('div',
H(Field, {
label: 'Theme',
input: H(Dropdown, {
options: themeOptions,
value: theme,
onchange: val => {
if (images[0] === nowBlack && val === 'dark') {
images[0] = nowWhite;
} else if (images[0] === nowWhite && val === 'light') {
images[0] = nowBlack;
}
setLoadingState({ theme: val, images: [...images] });
}
})
}),
H(Field, { H(Field, {
label: 'File Type', label: 'File Type',
input: H(Dropdown, { options: fileTypeOptions, value: fileType, onchange: val => setLoadingState({fileType: val}) }) input: H(Dropdown, { options: fileTypeOptions, value: fileType, onchange: val => setLoadingState({fileType: val}) })

View File

@ -4,10 +4,15 @@ import { parse } from 'url';
export function parseRequest(req: IncomingMessage) { export function parseRequest(req: IncomingMessage) {
console.log('HTTP ' + req.url); console.log('HTTP ' + req.url);
const { pathname = '/', query = {} } = parse(req.url || '', true); const { pathname = '/', query = {} } = parse(req.url || '', true);
const { fontSize, images, md } = query; const { fontSize, images, theme, md } = query;
if (Array.isArray(fontSize)) { if (Array.isArray(fontSize)) {
throw new Error('Expected a single fontSize'); throw new Error('Expected a single fontSize');
} }
if (Array.isArray(theme)) {
throw new Error('Expected a single theme');
}
const arr = pathname.slice(1).split('.'); const arr = pathname.slice(1).split('.');
let extension = ''; let extension = '';
let text = ''; let text = '';
@ -23,11 +28,20 @@ export function parseRequest(req: IncomingMessage) {
const parsedRequest: ParsedRequest = { const parsedRequest: ParsedRequest = {
type: extension === 'jpeg' ? extension : 'png', type: extension === 'jpeg' ? extension : 'png',
text: decodeURIComponent(text), text: decodeURIComponent(text),
theme: theme === 'dark' ? 'dark' : 'light',
md: md === '1' || md === 'true', md: md === '1' || md === 'true',
fontSize: fontSize || '75px', fontSize: fontSize || '75px',
images: Array.isArray(images) && images.length > 0 images: Array.isArray(images) ? images : [images],
? images
: ['https://assets.zeit.co/image/upload/front/assets/design/now-black.svg'],
}; };
parsedRequest.images = getDefaultImages(parsedRequest.images, parsedRequest.theme);
return parsedRequest; return parsedRequest;
} }
function getDefaultImages(images: string[], theme: Theme) {
if (images.length > 0 && /^http/.test(images[0])) {
return images;
}
return theme === 'light'
? ['https://assets.zeit.co/image/upload/front/assets/design/now-black.svg']
: ['https://assets.zeit.co/image/upload/front/assets/design/now-white.svg'];
}

View File

@ -8,6 +8,6 @@ const entityMap: { [key: string]: string } = {
}; };
export function sanitizeHtml(html: string) { export function sanitizeHtml(html: string) {
return html.replace(/[&<>"'\/]/g, key => entityMap[key]); return String(html).replace(/[&<>"'\/]/g, key => entityMap[key]);
} }

View File

@ -3,9 +3,15 @@ import { readFileSync } from 'fs';
import * as marked from 'marked'; import * as marked from 'marked';
import { sanitizeHtml } from './sanitizer'; import { sanitizeHtml } from './sanitizer';
function getCss(fontSize: string) { function getCss(theme: string, fontSize: string) {
const regular = readFileSync(`${__dirname}/../fonts/Inter-UI-Regular.woff2`).toString('base64'); const regular = readFileSync(`${__dirname}/../fonts/Inter-UI-Regular.woff2`).toString('base64');
const bold = readFileSync(`${__dirname}/../fonts/Inter-UI-Bold.woff2`).toString('base64'); const bold = readFileSync(`${__dirname}/../fonts/Inter-UI-Bold.woff2`).toString('base64');
let background = 'white';
let foreground = 'black';
if (theme === 'dark') {
[foreground, background] = [background, foreground];
}
return ` return `
@font-face { @font-face {
font-family: 'Inter UI'; font-family: 'Inter UI';
@ -22,7 +28,7 @@ function getCss(fontSize: string) {
} }
body { body {
background: white; background: ${background};
background-image: radial-gradient(lightgray 5%, transparent 0); background-image: radial-gradient(lightgray 5%, transparent 0);
background-size: 60px 60px; background-size: 60px 60px;
height: 100vh; height: 100vh;
@ -59,18 +65,19 @@ function getCss(fontSize: string) {
font-family: 'Inter UI', sans-serif; font-family: 'Inter UI', sans-serif;
font-size: ${sanitizeHtml(fontSize)}; font-size: ${sanitizeHtml(fontSize)};
font-style: normal; font-style: normal;
color: ${foreground}
}`; }`;
} }
export function getHtml(parsedReq: ParsedRequest) { export function getHtml(parsedReq: ParsedRequest) {
const { text, md, fontSize, images } = parsedReq; const { text, theme, md, fontSize, images } = parsedReq;
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html> <html>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Generated Image</title> <title>Generated Image</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <style>
${getCss(fontSize)} ${getCss(theme, fontSize)}
</style> </style>
<body> <body>
<div> <div>

2
src/types.d.ts vendored
View File

@ -1,8 +1,10 @@
type ScreenshotType = 'png' | 'jpeg'; type ScreenshotType = 'png' | 'jpeg';
type Theme = 'light' | 'dark';
interface ParsedRequest { interface ParsedRequest {
type: ScreenshotType; type: ScreenshotType;
text: string; text: string;
theme: Theme;
md: boolean; md: boolean;
fontSize: string; fontSize: string;
images: string[]; images: string[];