Skip to content

Commit

Permalink
layouts generating
Browse files Browse the repository at this point in the history
  • Loading branch information
coffee-cup committed May 12, 2021
1 parent 0b46807 commit 50360c4
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 92 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start --port ${PORT-3000}",
"clean": "rm -rf .next"
"clean": "rm -rf .next yarn-error.log"
},
"dependencies": {
"chrome-aws-lambda": "9.1.0",
Expand All @@ -17,6 +17,7 @@
"react-dom": "^17.0.2",
"react-feather": "^2.0.9",
"styled-components": "^5.3.0",
"theme-custom-properties": "^1.0.0",
"twemoji": "13.0.2",
"use-local-storage-state": "^9.0.2",
"zustand": "^3.5.1"
Expand Down
14 changes: 14 additions & 0 deletions src/colours.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Colours, Theme } from "./types";

export const colourThemes: Record<Theme, Colours> = {
light: {
fg: "black",
bg: "white",
gray: "dimgray",
},
dark: {
fg: "white",
bg: "black",
gray: "dimgray",
},
};
9 changes: 7 additions & 2 deletions src/hooks/useConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { createLocalStorageStateHook } from "use-local-storage-state";
import { simpleLayout } from "../layouts";
import { IConfig } from "../types";

export const useConfig = createLocalStorageStateHook<IConfig>("config", {
export const defaultConfig: IConfig = {
theme: "light",
fileType: "png",
layoutName: simpleLayout.name,
});
};

export const useConfig = createLocalStorageStateHook<IConfig>(
"config",
defaultConfig,
);
45 changes: 36 additions & 9 deletions src/layouts.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { ILayout, ILayoutConfig } from "./types";
import twemoji from "twemoji";

const twOptions = { folder: "svg", ext: ".svg" };
const emojify = (text: string) => twemoji.parse(text, twOptions);

const gString = (layoutConfig: ILayoutConfig, name: string): string => {
const value = layoutConfig[name];
return Array.isArray(value) ? value.join(", ") : value;
};

export const simpleLayout: ILayout = {
name: "Simple",
properties: [
{ name: "Test", type: "text" },
{
name: "Boop",
description: "This is a test",
type: "select",
options: ["one", "two", "three"],
},
],
properties: [{ name: "Test", type: "text" }],
getCSS: () => `
h1 { font-size: 100px; }
`,
getBody: c => `
<h1>${emojify("✨")} ${gString(c, "Test")} ${emojify("✨")}</h1>
`,
};

export const starterLayout: ILayout = {
Expand Down Expand Up @@ -44,3 +51,23 @@ export const getDefaultLayout = (layout: ILayout): ILayoutConfig => {

return config;
};

export const getLayoutConfigFromQuery = (
layoutName: string,
query: Record<string, string | string[]>,
): ILayoutConfig => {
const layout = layouts.find(l => l.name === layoutName);

if (layout == null) {
return {};
}

const config: ILayoutConfig = getDefaultLayout(layout);
for (const p of layout.properties) {
if (query[p.name] != null) {
config[p.name] = query[p.name];
}
}

return config;
};
14 changes: 9 additions & 5 deletions src/pages/api/[...og].ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ import { getScreenshot } from "./_lib/chromium";
import { parseRequest } from "./_lib/parser";
import { getHtml } from "./_lib/template";

const isDev = !process.env.AWS_REGION;
const isHtmlDebug = process.env.OG_HTML_DEBUG === "1";
const isDev = !process.env.RAILWAY_STATIC_URL;
const isHtmlDebug = process.env.OG_HTML_DEBUG === "true";

const handler: NextApiHandler = async (req, res) => {
try {
const parsedReq = parseRequest(req);
const html = getHtml(parsedReq);
const { config, layoutConfig } = parseRequest(req);
console.log("\n\n---");
console.log("CONFIG", config);
console.log("LAYOUT CONFIG", layoutConfig);

const html = getHtml(config, layoutConfig);
if (isHtmlDebug) {
res.setHeader("Content-Type", "text/html");
res.end(html);
return;
}
const { fileType } = parsedReq;
const { fileType } = config;
const file = await getScreenshot(html, fileType, isDev);
res.statusCode = 200;
res.setHeader("Content-Type", `image/${fileType}`);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/api/_lib/chromium.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import core from "puppeteer-core";
import { FileType } from "../../../types";
import { getOptions } from "./options";
import { FileType } from "./types";
let _page: core.Page | null;

async function getPage(isDev: boolean) {
Expand Down
86 changes: 49 additions & 37 deletions src/pages/api/_lib/parser.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,59 @@
import { IncomingMessage } from "http";
import { parse } from "url";
import { ParsedRequest, Theme } from "./types";
import { NextApiRequest } from "next";
import { defaultConfig } from "../../../hooks/useConfig";
import { getLayoutConfigFromQuery } from "../../../layouts";
import { Theme, IConfig, ILayoutConfig, FileType } from "../../../types";

export function parseRequest(req: IncomingMessage) {
console.log("HTTP " + req.url);
const { pathname, query } = parse(req.url || "/", true);
const { fontSize, images, widths, heights, theme, md } = query || {};
export const parseRequest = (
req: NextApiRequest,
): {
config: IConfig;
layoutConfig: ILayoutConfig;
} => {
// const arr = (pathname || "/").slice(1).split(".");
// let extension = "";
// let text = "";
// if (arr.length === 0) {
// text = "";
// } else if (arr.length === 1) {
// text = arr[0];
// } else {
// extension = arr.pop() as string;
// text = arr.join(".");
// }

if (Array.isArray(fontSize)) {
throw new Error("Expected a single fontSize");
}
if (Array.isArray(theme)) {
const config: IConfig = {
...defaultConfig,
...req.query,
// fileType: extension as FileType,
};

const layoutConfig = getLayoutConfigFromQuery(config.layoutName, req.query);

if (Array.isArray(config.theme)) {
throw new Error("Expected a single theme");
}

const arr = (pathname || "/").slice(1).split(".");
let extension = "";
let text = "";
if (arr.length === 0) {
text = "";
} else if (arr.length === 1) {
text = arr[0];
} else {
extension = arr.pop() as string;
text = arr.join(".");
}
// const parsedRequest: ParsedRequest = {
// fileType: extension === "jpeg" ? extension : "png",
// text: decodeURIComponent(text),
// theme: theme === "dark" ? "dark" : "light",
// md: md === "1" || md === "true",
// fontSize: fontSize || "96px",
// images: getArray(images),
// widths: getArray(widths),
// heights: getArray(heights),
// };
// parsedRequest.images = getDefaultImages(
// parsedRequest.images,
// parsedRequest.theme,
// );
// return parsedRequest;

const parsedRequest: ParsedRequest = {
fileType: extension === "jpeg" ? extension : "png",
text: decodeURIComponent(text),
theme: theme === "dark" ? "dark" : "light",
md: md === "1" || md === "true",
fontSize: fontSize || "96px",
images: getArray(images),
widths: getArray(widths),
heights: getArray(heights),
return {
config,
layoutConfig,
};
parsedRequest.images = getDefaultImages(
parsedRequest.images,
parsedRequest.theme,
);
return parsedRequest;
}
};

function getArray(stringOrArray: string[] | string | undefined): string[] {
if (typeof stringOrArray === "undefined") {
Expand Down
66 changes: 44 additions & 22 deletions src/pages/api/_lib/template.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
import marked from "marked";
import { colourThemes } from "../../../colours";
import { layouts } from "../../../layouts";
import { IConfig, ILayoutConfig, Theme } from "../../../types";
import { sanitizeHtml } from "./sanitizer";
import { ParsedRequest } from "./types";
const twemoji = require("twemoji");
const twOptions = { folder: "svg", ext: ".svg" };
const emojify = (text: string) => twemoji.parse(text, twOptions);

function getCss(theme: string, fontSize: string) {
let background = "white";
let foreground = "black";
let radial = "lightgray";

if (theme === "dark") {
background = "black";
foreground = "white";
radial = "dimgray";
}

const getCommonCSS = (theme: Theme) => {
const colours = colourThemes[theme ?? "light"];

console.log(colours);

return `
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
body {
background: ${background};
background-image: radial-gradient(circle at 25px 25px, ${radial} 2%, transparent 0%), radial-gradient(circle at 75px 75px, ${radial} 2%, transparent 0%);
background: ${colours.bg};
color: ${colours.fg};
background-size: 100px 100px;
height: 100vh;
display: flex;
text-align: center;
align-items: center;
justify-content: center;
font-family: 'Inter', sans-serif;
font-weight: 400;
}
code {
Expand All @@ -40,6 +36,10 @@ function getCss(theme: string, fontSize: string) {
content: '\`';
}
h1 {
font-weight: 800;
}
.logo-wrapper {
display: flex;
align-items: center;
Expand Down Expand Up @@ -71,15 +71,36 @@ function getCss(theme: string, fontSize: string) {
.heading {
font-family: 'Inter', sans-serif;
font-size: ${sanitizeHtml(fontSize)};
font-size: 100px;
font-style: normal;
color: ${foreground};
color: ${colours.fg};
line-height: 1.8;
}`;
}
};

export function getHtml(config: IConfig, layoutConfig: ILayoutConfig) {
// const { text, theme, md, fontSize, images, widths, heights } = parsedReq;
const layout = layouts.find(l => l.name === config.layoutName);

return `<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>Generated Image</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
${getCommonCSS(config.theme)}
${layout?.getCSS != null ? layout.getCSS(layoutConfig) : ""}
</style>
<body>
${
layout?.getBody != null
? layout.getBody(layoutConfig)
: `<h1 style="font-size: 100px">Layout body not implemented</h1>`
}
</body>
</html>`;

export function getHtml(parsedReq: ParsedRequest) {
const { text, theme, md, fontSize, images, widths, heights } = parsedReq;
/*
return `<!DOCTYPE html>
<html>
<meta charset="utf-8">
Expand Down Expand Up @@ -107,6 +128,7 @@ export function getHtml(parsedReq: ParsedRequest) {
</div>
</body>
</html>`;
*/
}

function getImage(src: string, width = "auto", height = "225") {
Expand Down
13 changes: 0 additions & 13 deletions src/pages/api/_lib/types.ts

This file was deleted.

Loading

0 comments on commit 50360c4

Please sign in to comment.