├── .github └── CODEOWNERS ├── .gitignore ├── .vercelignore ├── .yarnrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── api ├── _fonts │ ├── Inter-Bold.woff2 │ ├── Inter-License.txt │ ├── Inter-Regular.woff2 │ ├── Vera-License.txt │ └── Vera-Mono.woff2 ├── _lib │ ├── chromium.ts │ ├── options.ts │ ├── parser.ts │ ├── sanitizer.ts │ ├── template.ts │ └── types.ts ├── heropatterns.d.ts ├── index.ts └── tsconfig.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── robots.txt ├── style.css └── tweet.png ├── vercel.json ├── web ├── index.ts └── tsconfig.json └── yarn.lock /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @styfle 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .now 2 | .vercel 3 | node_modules 4 | api/dist 5 | public/dist 6 | 7 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | .github 2 | node_modules 3 | api/dist 4 | public/dist 5 | CONTRIBUTING.md 6 | README.md 7 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | save-prefix "" 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | There are two pieces to `og-image` that are worth noting before you begin development. 4 | 5 | 1. The backend image generator located in [/api/index.ts](https://github.com/vercel/og-image/blob/main/api/index.ts) 6 | 2. The frontend inputs located in [/web/index.ts](https://github.com/vercel/og-image/blob/main/web/index.ts) 7 | 8 | Vercel handles [routing](https://github.com/vercel/og-image/blob/main/vercel.json#L6) in an elegant way for us so deployment is easy. 9 | 10 | To start hacking, do the following: 11 | 12 | 1. Clone this repo with `git clone https://github.com/vercel/og-image` 13 | 2. Change directory with `cd og-image` 14 | 3. Run `yarn` or `npm install` to install all dependencies 15 | 4. Run locally with `vercel dev` and visit [localhost:3000](http://localhost:3000) (if nothing happens, run `npm install -g vercel`) 16 | 5. If necessary, edit the `exePath` in [options.ts](https://github.com/vercel/og-image/blob/main/api/_lib/options.ts) to point to your local Chrome executable 17 | 18 | Now you're ready to start local development! 19 | 20 | You can set an environment variable to assist with debugging `export OG_HTML_DEBUG=1`. This will render the image as HTML so you can play around with your browser's dev tools before committing changes to the template. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [Social Images for PHP Packages](https://banners.beyondco.de) 4 | 5 | ![](https://banners.beyondco.de/My%20Package.png?theme=light&packageName=vendor%2Fmy-awesome-package&pattern=architect&style=style_1&description=This+is+why+it%27s+awesome&md=1&fontSize=100px&images=https%3A%2F%2Flaravel.com%2Fimg%2Flogomark.min.svg) 6 | 7 | Serverless service that generates dynamic Open Graph images that you can embed in your `` tags. 8 | 9 | For each keystroke, headless chromium is used to render an HTML page and take a screenshot of the result which gets cached. 10 | 11 | See the image embedded in the tweet for a real use case. 12 | 13 | 14 | ## What is an Open Graph Image? 15 | 16 | Have you ever posted a hyperlink to Twitter, Facebook, or Slack and seen an image popup? 17 | How did your social network know how to "unfurl" the URL and get an image? 18 | The answer is in your ``. 19 | 20 | The [Open Graph protocol](http://ogp.me) says you can put a `` tag in the `` of a webpage to define this image. 21 | 22 | It looks like the following: 23 | 24 | ```html 25 | 26 | Title 27 | 28 | 29 | ``` 30 | 31 | ## Why use this service? 32 | 33 | This service lets you generate beautiful looking social images for your PHP, Javascript, Python and Laravel packages. You have already put a lot of time and effort into your package, so why not make it look beautiful using this service. 34 | 35 | ## Authors 36 | 37 | - Steven ([@styfle](https://twitter.com/styfle)) - [Vercel](https://vercel.com) 38 | - Evil Rabbit ([@evilrabbit_](https://twitter.com/evilrabbit_)) - [Vercel](https://vercel.com) 39 | - Marcel Pociot ([@marcelpociot](https://twitter.com/marcelpociot)) - [Beyond Code](https://beyondco.de) 40 | -------------------------------------------------------------------------------- /api/_fonts/Inter-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/api/_fonts/Inter-Bold.woff2 -------------------------------------------------------------------------------- /api/_fonts/Inter-License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2018 The Inter Project Authors (me@rsms.me) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font creation 14 | efforts of academic and linguistic communities, and to provide a free and 15 | open framework in which fonts may be shared and improved in partnership 16 | with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply 25 | to any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software components as 36 | distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, deleting, 39 | or substituting -- in part or in whole -- any of the components of the 40 | Original Version, by changing formats or by porting the Font Software to a 41 | new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION AND CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 49 | redistribute, and sell modified and unmodified copies of the Font 50 | Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, 53 | in Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the corresponding 64 | Copyright Holder. This restriction only applies to the primary font name as 65 | presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created 77 | using the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /api/_fonts/Inter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/api/_fonts/Inter-Regular.woff2 -------------------------------------------------------------------------------- /api/_fonts/Vera-License.txt: -------------------------------------------------------------------------------- 1 | Bitstream Vera Fonts Copyright 2 | 3 | The fonts have a generous copyright, allowing derivative works (as 4 | long as "Bitstream" or "Vera" are not in the names), and full 5 | redistribution (so long as they are not *sold* by themselves). They 6 | can be be bundled, redistributed and sold with any software. 7 | 8 | The fonts are distributed under the following copyright: 9 | 10 | Copyright 11 | ========= 12 | 13 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream 14 | Vera is a trademark of Bitstream, Inc. 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of the fonts accompanying this license ("Fonts") and associated 18 | documentation files (the "Font Software"), to reproduce and distribute 19 | the Font Software, including without limitation the rights to use, 20 | copy, merge, publish, distribute, and/or sell copies of the Font 21 | Software, and to permit persons to whom the Font Software is furnished 22 | to do so, subject to the following conditions: 23 | 24 | The above copyright and trademark notices and this permission notice 25 | shall be included in all copies of one or more of the Font Software 26 | typefaces. 27 | 28 | The Font Software may be modified, altered, or added to, and in 29 | particular the designs of glyphs or characters in the Fonts may be 30 | modified and additional glyphs or characters may be added to the 31 | Fonts, only if the fonts are renamed to names not containing either 32 | the words "Bitstream" or the word "Vera". 33 | 34 | This License becomes null and void to the extent applicable to Fonts 35 | or Font Software that has been modified and is distributed under the 36 | "Bitstream Vera" names. 37 | 38 | The Font Software may be sold as part of a larger software package but 39 | no copy of one or more of the Font Software typefaces may be sold by 40 | itself. 41 | 42 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 43 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 44 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 45 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL 46 | BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR 47 | OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, 48 | OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR 49 | OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT 50 | SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. 51 | 52 | Except as contained in this notice, the names of Gnome, the Gnome 53 | Foundation, and Bitstream Inc., shall not be used in advertising or 54 | otherwise to promote the sale, use or other dealings in this Font 55 | Software without prior written authorization from the Gnome Foundation 56 | or Bitstream Inc., respectively. For further information, contact: 57 | fonts at gnome dot org. 58 | 59 | Copyright FAQ 60 | ============= 61 | 62 | 1. I don't understand the resale restriction... What gives? 63 | 64 | Bitstream is giving away these fonts, but wishes to ensure its 65 | competitors can't just drop the fonts as is into a font sale system 66 | and sell them as is. It seems fair that if Bitstream can't make money 67 | from the Bitstream Vera fonts, their competitors should not be able to 68 | do so either. You can sell the fonts as part of any software package, 69 | however. 70 | 71 | 2. I want to package these fonts separately for distribution and 72 | sale as part of a larger software package or system. Can I do so? 73 | 74 | Yes. A RPM or Debian package is a "larger software package" to begin 75 | with, and you aren't selling them independently by themselves. 76 | See 1. above. 77 | 78 | 3. Are derivative works allowed? 79 | Yes! 80 | 81 | 4. Can I change or add to the font(s)? 82 | Yes, but you must change the name(s) of the font(s). 83 | 84 | 5. Under what terms are derivative works allowed? 85 | 86 | You must change the name(s) of the fonts. This is to ensure the 87 | quality of the fonts, both to protect Bitstream and Gnome. We want to 88 | ensure that if an application has opened a font specifically of these 89 | names, it gets what it expects (though of course, using fontconfig, 90 | substitutions could still could have occurred during font 91 | opening). You must include the Bitstream copyright. Additional 92 | copyrights can be added, as per copyright law. Happy Font Hacking! 93 | 94 | 6. If I have improvements for Bitstream Vera, is it possible they might get 95 | adopted in future versions? 96 | 97 | Yes. The contract between the Gnome Foundation and Bitstream has 98 | provisions for working with Bitstream to ensure quality additions to 99 | the Bitstream Vera font family. Please contact us if you have such 100 | additions. Note, that in general, we will want such additions for the 101 | entire family, not just a single font, and that you'll have to keep 102 | both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add 103 | glyphs to the font, they must be stylistically in keeping with Vera's 104 | design. Vera cannot become a "ransom note" font. Jim Lyles will be 105 | providing a document describing the design elements used in Vera, as a 106 | guide and aid for people interested in contributing to Vera. 107 | 108 | 7. I want to sell a software package that uses these fonts: Can I do so? 109 | 110 | Sure. Bundle the fonts with your software and sell your software 111 | with the fonts. That is the intent of the copyright. 112 | 113 | 8. If applications have built the names "Bitstream Vera" into them, 114 | can I override this somehow to use fonts of my choosing? 115 | 116 | This depends on exact details of the software. Most open source 117 | systems and software (e.g., Gnome, KDE, etc.) are now converting to 118 | use fontconfig (see www.fontconfig.org) to handle font configuration, 119 | selection and substitution; it has provisions for overriding font 120 | names and subsituting alternatives. An example is provided by the 121 | supplied local.conf file, which chooses the family Bitstream Vera for 122 | "sans", "serif" and "monospace". Other software (e.g., the XFree86 123 | core server) has other mechanisms for font substitution. 124 | 125 | -------------------------------------------------------------------------------- /api/_fonts/Vera-Mono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/api/_fonts/Vera-Mono.woff2 -------------------------------------------------------------------------------- /api/_lib/chromium.ts: -------------------------------------------------------------------------------- 1 | import { launch, Page } from 'puppeteer-core'; 2 | import { getOptions } from './options'; 3 | import { FileType } from './types'; 4 | let _page: Page | null; 5 | 6 | async function getPage(isDev: boolean) { 7 | if (_page) { 8 | return _page; 9 | } 10 | const options = await getOptions(isDev); 11 | const browser = await launch(options); 12 | _page = await browser.newPage(); 13 | return _page; 14 | } 15 | 16 | export async function getScreenshot(html: string, type: FileType, isDev: boolean) { 17 | const page = await getPage(isDev); 18 | await page.setViewport({ width: 2048, height: 1170 }); 19 | await page.setContent(html); 20 | const file = await page.screenshot({ type }); 21 | return file; 22 | } 23 | -------------------------------------------------------------------------------- /api/_lib/options.ts: -------------------------------------------------------------------------------- 1 | import chrome from 'chrome-aws-lambda'; 2 | const exePath = process.platform === 'win32' 3 | ? 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe' 4 | : process.platform === 'linux' 5 | ? '/usr/bin/google-chrome' 6 | : '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; 7 | 8 | interface Options { 9 | args: string[]; 10 | executablePath: string; 11 | headless: boolean; 12 | } 13 | 14 | export async function getOptions(isDev: boolean) { 15 | let options: Options; 16 | if (isDev) { 17 | options = { 18 | args: [], 19 | executablePath: exePath, 20 | headless: true 21 | }; 22 | } else { 23 | options = { 24 | args: chrome.args, 25 | executablePath: await chrome.executablePath, 26 | headless: chrome.headless, 27 | }; 28 | } 29 | return options; 30 | } 31 | -------------------------------------------------------------------------------- /api/_lib/parser.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from 'http'; 2 | import { parse } from 'url'; 3 | import { ParsedRequest } from './types'; 4 | 5 | export function parseRequest(req: IncomingMessage) { 6 | console.log('HTTP ' + req.url); 7 | const { pathname, query } = parse(req.url || '/', true); 8 | const { fontSize, images, widths, heights, theme, md, showWatermark, pattern, packageManager, packageName, description, style } = (query || {}); 9 | 10 | if (Array.isArray(fontSize)) { 11 | throw new Error('Expected a single fontSize'); 12 | } 13 | if (Array.isArray(theme)) { 14 | throw new Error('Expected a single theme'); 15 | } 16 | if (Array.isArray(style)) { 17 | throw new Error('Expected a single style'); 18 | } 19 | if (Array.isArray(pattern)) { 20 | throw new Error('Expected a single pattern'); 21 | } 22 | if (Array.isArray(packageManager)) { 23 | throw new Error('Expected a single package manager'); 24 | } 25 | if (Array.isArray(packageName)) { 26 | throw new Error('Expected a single package name'); 27 | } 28 | if (Array.isArray(description)) { 29 | throw new Error('Expected a single package name'); 30 | } 31 | 32 | const arr = (pathname || '/').slice(1).split('.'); 33 | let extension = ''; 34 | let text = ''; 35 | if (arr.length === 0) { 36 | text = ''; 37 | } else if (arr.length === 1) { 38 | text = arr[0]; 39 | } else { 40 | extension = arr.pop() as string; 41 | text = arr.join('.'); 42 | } 43 | 44 | const parsedRequest: ParsedRequest = { 45 | fileType: extension === 'jpeg' ? extension : 'png', 46 | text: decodeURIComponent(text), 47 | theme: theme === 'dark' ? 'dark' : 'light', 48 | style: style === 'style_2' ? 'style_2' : 'style_1', 49 | md: md === '1' || md === 'true', 50 | showWatermark: showWatermark === '1' || showWatermark === 'true', 51 | fontSize: fontSize || '96px', 52 | pattern: pattern || 'circuitBoard', 53 | packageManager: packageManager || '', 54 | packageName: packageName || '', 55 | description: description || '', 56 | images: getArray(images), 57 | widths: getArray(widths), 58 | heights: getArray(heights), 59 | }; 60 | parsedRequest.images = getDefaultImages(parsedRequest.images); 61 | return parsedRequest; 62 | } 63 | 64 | function getArray(stringOrArray: string[] | string | undefined): string[] { 65 | if (typeof stringOrArray === 'undefined') { 66 | return []; 67 | } else if (Array.isArray(stringOrArray)) { 68 | return stringOrArray; 69 | } else { 70 | return [stringOrArray]; 71 | } 72 | } 73 | 74 | function getDefaultImages(images: string[]): string[] { 75 | const defaultImage = 'https://laravel.com/img/logomark.min.svg'; 76 | 77 | if (!images || !images[0]) { 78 | return [defaultImage]; 79 | } 80 | return images; 81 | } 82 | -------------------------------------------------------------------------------- /api/_lib/sanitizer.ts: -------------------------------------------------------------------------------- 1 | const entityMap: { [key: string]: string } = { 2 | "&": "&", 3 | "<": "<", 4 | ">": ">", 5 | '"': '"', 6 | "'": ''', 7 | "/": '/' 8 | }; 9 | 10 | export function sanitizeHtml(html: string) { 11 | return String(html).replace(/[&<>"'\/]/g, key => entityMap[key]); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /api/_lib/template.ts: -------------------------------------------------------------------------------- 1 | 2 | import marked from 'marked'; 3 | import { sanitizeHtml } from './sanitizer'; 4 | import { ParsedRequest, Style, Theme } from './types'; 5 | import * as hero from 'hero-patterns'; 6 | import fs from 'fs'; 7 | 8 | const twemoji = require('twemoji'); 9 | const twOptions = { folder: 'svg', ext: '.svg' }; 10 | const emojify = (text: string) => twemoji.parse(text, twOptions); 11 | 12 | function getCss(theme: string, pattern: string, fontSize: string) { 13 | let foreground = '#000000'; 14 | let background = '#ffffff'; 15 | let opacity = 0.07; 16 | 17 | if (theme === 'dark') { 18 | foreground = '#ffffff'; 19 | background = '#000000'; 20 | opacity = 0.15; 21 | } 22 | return ` 23 | body { 24 | font-family: Inter; 25 | background-color: ${background}; 26 | background-image: ${hero[pattern](foreground, opacity)} 27 | } 28 | 29 | code { 30 | color: #ff2d20; 31 | font-size: 2vw; 32 | font-family: 'Space Mono'; 33 | font-weight: bold; 34 | white-space: pre-wrap; 35 | } 36 | 37 | code:before, code:after { 38 | content: '\`'; 39 | } 40 | 41 | .logo-wrapper { 42 | display: flex; 43 | align-items: center; 44 | align-content: center; 45 | justify-content: center; 46 | justify-items: center; 47 | } 48 | 49 | .logo { 50 | margin: 0 75px; 51 | } 52 | 53 | .spacer { 54 | margin: 150px; 55 | } 56 | 57 | .emoji { 58 | height: 1em; 59 | width: 1em; 60 | margin: 0 .05em 0 .1em; 61 | vertical-align: -0.1em; 62 | } 63 | 64 | .heading { 65 | font-size: ${sanitizeHtml(fontSize)}; 66 | font-style: normal; 67 | color: ${foreground}; 68 | font-family: 'Inter', sans-serif; 69 | font-weight: 800; 70 | line-height: 1.2; 71 | } 72 | 73 | .description { 74 | color: ${foreground}; 75 | } 76 | 77 | .text-laravel { 78 | color: #ff2d20; 79 | } 80 | `; 81 | } 82 | 83 | function getDescription(description: string) { 84 | if (description === '' || description === undefined) { 85 | return ''; 86 | } 87 | 88 | return ` 89 |

${sanitizeHtml(description)}

90 | ` 91 | } 92 | 93 | function getPackageInformation(packageManager: string, packageName: string) { 94 | if ( 95 | (packageManager === '' || packageManager === undefined) && 96 | (packageName === '' || packageName === undefined) 97 | ) { 98 | return ''; 99 | } 100 | 101 | const packageInformation: string = `${sanitizeHtml(packageManager)} ${sanitizeHtml(packageName)}`.trim(); 102 | 103 | return ` 104 | ${packageInformation} 105 | ` 106 | } 107 | 108 | function getAlternativeHtml(parsedReq: ParsedRequest) { 109 | const { text, theme, md, fontSize, images, widths, heights, pattern, packageManager, packageName, description, style, showWatermark } = parsedReq; 110 | 111 | return ` 112 | 113 | 114 | Generated Image 115 | 116 | 117 | 118 | 119 | 120 | 121 | 124 | 125 | ${images.map((img, i) => 126 | getImage(img, widths[i], heights[i], style) 127 | ).join('')} 128 |
129 |
${emojify( 130 | md ? marked(text) : sanitizeHtml(text) 131 | )} 132 |
133 | ${getDescription(description)} 134 | ${getPackageInformation(packageManager, packageName)} 135 |
136 | ${showWatermark ? getWatermark(theme) : ''} 137 | 138 | `; 139 | } 140 | 141 | function getWatermark(theme: Theme) { 142 | if (theme === 'dark') { 143 | return `
Generated using banners.beyondco.de
` 144 | } else { 145 | return `
Generated using banners.beyondco.de
` 146 | } 147 | } 148 | 149 | export function getHtml(parsedReq: ParsedRequest) { 150 | const { text, theme, md, fontSize, images, widths, heights, pattern, packageManager, packageName, description, style, showWatermark } = parsedReq; 151 | 152 | if (style === 'style_2') { 153 | return getAlternativeHtml(parsedReq); 154 | } 155 | 156 | return ` 157 | 158 | 159 | Generated Image 160 | 161 | 162 | 163 | 164 | 165 | 166 | 169 | 170 |
171 |
172 | ${images.map((img, i) => 173 | getImage(img, widths[i], heights[i], style) 174 | ).join('')} 175 |
176 |
${emojify( 177 | md ? marked(text) : sanitizeHtml(text) 178 | )} 179 |
180 | ${getDescription(description)} 181 | ${getPackageInformation(packageManager, packageName)} 182 | ${showWatermark ? getWatermark(theme) : ''} 183 |
184 | 185 | `; 186 | } 187 | 188 | function getImage(src: string, width ='225', height = '225', style: Style) { 189 | const filename = `${__dirname}/../../node_modules/heroicons/outline/${sanitizeHtml(src)}.svg`; 190 | 191 | if (fs.existsSync(filename)) { 192 | const iconContent = fs.readFileSync(filename).toString(); 193 | 194 | if (style === 'style_2') { 195 | return iconContent.replace('` 213 | } 214 | return `` 221 | } 222 | -------------------------------------------------------------------------------- /api/_lib/types.ts: -------------------------------------------------------------------------------- 1 | export type FileType = 'png' | 'jpeg'; 2 | export type Theme = 'light' | 'dark'; 3 | export type Style = 'style_1' | 'style_2'; 4 | 5 | export interface ParsedRequest { 6 | fileType: FileType; 7 | text: string; 8 | theme: Theme; 9 | style: Style; 10 | showWatermark: boolean; 11 | packageManager: string; 12 | packageName: string; 13 | description: string; 14 | pattern: string; 15 | md: boolean; 16 | fontSize: string; 17 | images: string[]; 18 | widths: string[]; 19 | heights: string[]; 20 | } 21 | -------------------------------------------------------------------------------- /api/heropatterns.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'hero-patterns'; 2 | declare module 'heroicons'; -------------------------------------------------------------------------------- /api/index.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage, ServerResponse } from 'http'; 2 | import { parseRequest } from './_lib/parser'; 3 | import { getScreenshot } from './_lib/chromium'; 4 | import { getHtml } from './_lib/template'; 5 | 6 | const isDev = !process.env.AWS_REGION; 7 | const isHtmlDebug = process.env.OG_HTML_DEBUG === '1'; 8 | 9 | export default async function handler(req: IncomingMessage, res: ServerResponse) { 10 | try { 11 | const parsedReq = parseRequest(req); 12 | const html = getHtml(parsedReq); 13 | if (isHtmlDebug) { 14 | res.setHeader('Content-Type', 'text/html'); 15 | res.end(html); 16 | return; 17 | } 18 | const { fileType } = parsedReq; 19 | const file = await getScreenshot(html, fileType, isDev); 20 | res.statusCode = 200; 21 | res.setHeader('Content-Type', `image/${fileType}`); 22 | res.setHeader('Cache-Control', `public, immutable, no-transform, s-maxage=31536000, max-age=31536000`); 23 | res.end(file); 24 | } catch (e) { 25 | res.statusCode = 500; 26 | res.setHeader('Content-Type', 'text/html'); 27 | res.end('

Internal Error

Sorry, there was a problem

'); 28 | console.error(e); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "commonjs", 5 | "target": "esnext", 6 | "moduleResolution": "node", 7 | "jsx": "react", 8 | "sourceMap": true, 9 | "strict": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitReturns": true, 12 | "noEmitOnError": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "removeComments": true, 16 | "preserveConstEnums": true, 17 | "esModuleInterop": true 18 | }, 19 | "include": [ 20 | "./" 21 | ] 22 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": "12.x" 5 | }, 6 | "scripts": { 7 | "build": "tsc -p api/tsconfig.json && tsc -p web/tsconfig.json" 8 | }, 9 | "dependencies": { 10 | "chrome-aws-lambda": "2.1.1", 11 | "hero-patterns": "2.1.0", 12 | "heroicons": "0.4.2", 13 | "marked": "0.8.2", 14 | "puppeteer-core": "2.1.1", 15 | "twemoji": "12.1.5" 16 | }, 17 | "devDependencies": { 18 | "@types/marked": "0.7.3", 19 | "@types/puppeteer": "2.0.1", 20 | "@types/puppeteer-core": "2.0.0", 21 | "typescript": "3.8.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Social Image Generator for Open Source packages - Beyond Code 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 43 |
44 |
45 | 46 | 52 | 53 |
54 | 55 | 56 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |

A BeyondCode service

71 |
72 |
73 |

74 | Social images for
75 |

76 | Open Source Packages 77 | Open Source Packages 78 |

79 |

80 |

81 | You put all your work into creating an awesome Open Source package - now it's time to make it shine! Use this generator to create beautiful looking social images for your package. 82 |

83 |
84 | Loading... 85 |
86 |
87 |
88 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif; 3 | margin: 20px; 4 | } 5 | 6 | .center, h1 { 7 | text-align: center; 8 | } 9 | 10 | .container { 11 | display: flex; 12 | justify-content: center; 13 | width: 100%; 14 | } 15 | 16 | .split { 17 | display: flex; 18 | justify-content: center; 19 | width: 100%; 20 | } 21 | 22 | .pull-left { 23 | min-width: 50%; 24 | display: flex; 25 | justify-content: flex-end; 26 | } 27 | 28 | .pull-right { 29 | min-width: 50%; 30 | } 31 | 32 | button { 33 | appearance: none; 34 | align-items: center; 35 | color: #fff; 36 | background: #000; 37 | display: inline-flex; 38 | width: 100px; 39 | height: 40px; 40 | padding: 0 25px; 41 | outline: none; 42 | border: 1px solid #000; 43 | font-size: 12px; 44 | justify-content: center; 45 | text-transform: uppercase; 46 | cursor: pointer; 47 | text-align: center; 48 | user-select: none; 49 | font-weight: 100; 50 | position: relative; 51 | overflow: hidden; 52 | transition: border 0.2s,background 0.2s,color 0.2s ease-out; 53 | border-radius: 5px; 54 | white-space: nowrap; 55 | text-decoration: none; 56 | line-height: 0; 57 | 58 | height: 24px; 59 | width: calc(100% + 2px); 60 | padding: 0 10px; 61 | font-size: 12px; 62 | background: #fff; 63 | border: 1px solid #eaeaea; 64 | color: #666; 65 | } 66 | 67 | button:hover { 68 | color: #000; 69 | border-color: #000; 70 | background: #fff; 71 | } 72 | 73 | .input-outer-wrapper { 74 | align-items: center; 75 | border-radius: 5px; 76 | border: 1px solid #e1e1e1; 77 | display: inline-flex; 78 | height: 37px; 79 | position: relative; 80 | transition: border 0.2s ease,color 0.2s ease; 81 | vertical-align: middle; 82 | width: 100%; 83 | } 84 | 85 | .input-outer-wrapper:hover { 86 | box-shadow: 0 2px 6px rgba(0,0,0,0.1); 87 | border-color: #ddd; 88 | } 89 | 90 | .input-inner-wrapper { 91 | display: block; 92 | margin: 4px 10px; 93 | position: relative; 94 | width: 100%; 95 | } 96 | 97 | .input-inner-wrapper input { 98 | background-color: transparent; 99 | border-radius: 0; 100 | border: none; 101 | box-shadow: none; 102 | box-sizing: border-box; 103 | display: block; 104 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 105 | font-size: 14px; 106 | line-height: 27px; 107 | outline: 0; 108 | width: 100%; 109 | } 110 | 111 | .select-wrapper { 112 | appearance: none; 113 | color: #000; 114 | background: #fff; 115 | display: inline-flex; 116 | height: 40px; 117 | outline: none; 118 | border: 1px solid #eaeaea; 119 | font-size: 12px; 120 | text-transform: uppercase; 121 | user-select: none; 122 | font-weight: 100; 123 | position: relative; 124 | overflow: hidden; 125 | transition: border 0.2s,background 0.2s,color 0.2s ease-out, box-shadow 0.2s ease; 126 | border-radius: 5px; 127 | white-space: nowrap; 128 | line-height: 0; 129 | width: auto; 130 | min-width: 100%; 131 | } 132 | 133 | .select-wrapper:hover { 134 | box-shadow: 0 2px 6px rgba(0,0,0,0.1); 135 | border-color: #ddd; 136 | } 137 | 138 | .select-wrapper.small { 139 | height: 30px; 140 | min-width: 100px; 141 | width: 100px; 142 | } 143 | 144 | .select-arrow { 145 | border-left: 1px solid #eaeaea; 146 | background: #fff; 147 | width: 40px; 148 | height: 100%; 149 | position: absolute; 150 | right: 0; 151 | pointer-events: none; 152 | display: flex; 153 | align-items: center; 154 | justify-content: center; 155 | } 156 | 157 | .select-arrow.small { 158 | width: 22px; 159 | } 160 | 161 | select { 162 | height: 100%; 163 | border: none; 164 | box-shadow: none; 165 | background: transparent; 166 | background-image: none; 167 | color: #000; 168 | line-height: 40px; 169 | font-size: 14px; 170 | margin-right: -20px; 171 | width: calc(100% + 20px); 172 | padding: 0 0 0 16px; 173 | text-transform: none; 174 | } 175 | 176 | .field-flex { 177 | display: flex; 178 | margin-top: 10px; 179 | } 180 | 181 | .field-value { 182 | margin: 10px 80px; 183 | } 184 | 185 | .field-label { 186 | display: inline-block; 187 | width: 100px; 188 | margin-right: 20px; 189 | text-align: right; 190 | } 191 | 192 | .field-value { 193 | width: 200px; 194 | display: inline-block; 195 | } 196 | 197 | label { 198 | display: flex; 199 | align-items: center; 200 | } 201 | 202 | .toast-area { 203 | position: fixed; 204 | bottom: 10px; 205 | right: 20px; 206 | max-width: 420px; 207 | z-index: 2000; 208 | transition: transform 0.4s ease; 209 | } 210 | 211 | .toast-outer { 212 | width: 420px; 213 | height: 72px; 214 | position: absolute; 215 | bottom: 0; 216 | right: 0; 217 | transition: all 0.4s ease; 218 | transform: translate3d(0,130%,0px) scale(1); 219 | animation: show-jsx-1861505484 0.4s ease forwards; 220 | opacity: 1; 221 | } 222 | 223 | .toast-inner { 224 | width: 420px; 225 | background: black; 226 | color: white; 227 | border: 0; 228 | border-radius: 5px; 229 | height: 60px; 230 | align-items: center; 231 | justify-content: space-between; 232 | 233 | box-shadow: 0 4px 9px rgba(0,0,0,0.12); 234 | font-size: 14px; 235 | display: flex; 236 | } 237 | 238 | .toast-message { 239 | text-overflow: ellipsis; 240 | white-space: nowrap; 241 | width: 100%; 242 | overflow: hidden; 243 | margin-top: -1px; 244 | margin-left: 20px; 245 | } 246 | 247 | img { 248 | max-width: 100%; 249 | transition: all 0.3s ease-in 0s; 250 | } 251 | 252 | .image-wrapper { 253 | display: block; 254 | cursor: pointer; 255 | text-decoration: none; 256 | background: #fff; 257 | box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.12); 258 | border-radius: 5px; 259 | margin-bottom: 50px; 260 | transition: all 0.2s ease; 261 | max-width: 100%; 262 | } 263 | 264 | .image-wrapper:hover { 265 | box-shadow: 0 1px 16px rgba(0,0,0,0.1); 266 | border-color: #ddd; 267 | } 268 | 269 | 270 | @media (max-width: 1000px) { 271 | .field { 272 | margin: 20px 10px; 273 | } 274 | } 275 | 276 | @media (max-width: 900px) { 277 | label { 278 | display: flex; 279 | flex-direction: column; 280 | align-items: stretch; 281 | } 282 | .split { 283 | flex-wrap: wrap; 284 | } 285 | 286 | .field-label { 287 | width: 200px; 288 | margin: 10px 80px; 289 | text-align: left; 290 | font-size: 13px; 291 | } 292 | } 293 | 294 | .github-corner:hover .octo-arm { 295 | animation: octocat-wave 560ms ease-in-out 296 | } 297 | 298 | @keyframes octocat-wave { 299 | 0%,100% { transform:rotate(0) } 300 | 20%,60% { transform:rotate(-25deg) } 301 | 40%,80% { transform:rotate(10deg) } 302 | } 303 | 304 | @media (max-width:500px) { 305 | .github-corner:hover .octo-arm { animation:none } 306 | .github-corner .octo-arm { animation:octocat-wave 560ms ease-in-out } 307 | } 308 | -------------------------------------------------------------------------------- /public/tweet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/banners/a365a00331282991f01d811c490cf6270ee7e2ef/public/tweet.png -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "api/**": { 4 | "memory": 3008 5 | } 6 | }, 7 | "rewrites": [ 8 | { "source": "/(.+)", "destination": "/api" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /web/index.ts: -------------------------------------------------------------------------------- 1 | import { ParsedRequest, Theme, FileType, Style } from '../api/_lib/types'; 2 | 3 | const { H, R, copee } = (window as any); 4 | let timeout = -1; 5 | 6 | interface ImagePreviewProps { 7 | src: string; 8 | onclick: () => void; 9 | onload: () => void; 10 | onerror: () => void; 11 | loading: boolean; 12 | } 13 | 14 | const ImagePreview = ({ src, onclick, onload, onerror, loading }: ImagePreviewProps) => { 15 | const style = { 16 | filter: loading ? 'blur(5px)' : '', 17 | opacity: loading ? 0.1 : 1, 18 | }; 19 | const title = 'Click to copy image URL to clipboard'; 20 | return H('a', 21 | { className: 'image-wrapper', href: src, onclick }, 22 | H('img', 23 | { src, onload, onerror, style, title } 24 | ) 25 | ); 26 | } 27 | 28 | interface DropdownOption { 29 | text: string; 30 | value: string; 31 | } 32 | 33 | interface DropdownProps { 34 | options: DropdownOption[]; 35 | value: string; 36 | onchange: (val: string) => void; 37 | small: boolean; 38 | } 39 | 40 | const Dropdown = ({ options, value, onchange, small }: DropdownProps) => { 41 | const wrapper = small ? 'select-wrapper small' : 'select-wrapper'; 42 | const arrow = small ? 'select-arrow small' : 'select-arrow'; 43 | return H('div', 44 | { className: wrapper }, 45 | H('select', 46 | { onchange: (e: any) => onchange(e.target.value) }, 47 | options.map(o => 48 | H('option', 49 | { value: o.value, selected: value === o.value }, 50 | o.text 51 | ) 52 | ) 53 | ), 54 | H('div', 55 | { className: arrow }, 56 | '▼' 57 | ) 58 | ); 59 | } 60 | 61 | interface TextInputProps { 62 | value: string; 63 | oninput: (val: string) => void; 64 | } 65 | 66 | const TextInput = ({ value, oninput }: TextInputProps) => { 67 | return H('div', 68 | { className: 'input-outer-wrapper' }, 69 | H('div', 70 | { className: 'input-inner-wrapper' }, 71 | H('input', 72 | { type: 'text', value, oninput: (e: any) => oninput(e.target.value) } 73 | ) 74 | ) 75 | ); 76 | } 77 | 78 | interface FieldProps { 79 | label: string; 80 | input: any; 81 | } 82 | 83 | const Field = ({ label, input }: FieldProps) => { 84 | return H('div', 85 | { className: 'field' }, 86 | H('label', 87 | H('div', {className: 'field-label'}, label), 88 | H('div', { className: 'field-value' }, input), 89 | ), 90 | ); 91 | } 92 | 93 | interface ToastProps { 94 | show: boolean; 95 | message: string; 96 | } 97 | 98 | const Toast = ({ show, message }: ToastProps) => { 99 | const style = { transform: show ? 'translate3d(0,-0px,-0px) scale(1)' : '' }; 100 | return H('div', 101 | { className: 'toast-area' }, 102 | H('div', 103 | { className: 'toast-outer', style }, 104 | H('div', 105 | { className: 'toast-inner' }, 106 | H('div', 107 | { className: 'toast-message'}, 108 | message 109 | ) 110 | ) 111 | ), 112 | ); 113 | } 114 | 115 | const packageManagerOptions: DropdownOption[] = [ 116 | {text: 'PHP/Composer', value: 'composer require'}, 117 | {text: 'Node/NPM', value: 'npm install'}, 118 | {text: 'Node/Yarn', value: 'yarn add'}, 119 | {text: 'Python/pip', value: 'pip install'}, 120 | {text: 'No Package Manager', value: ''}, 121 | ]; 122 | 123 | const themeOptions: DropdownOption[] = [ 124 | { text: 'Light', value: 'light' }, 125 | { text: 'Dark', value: 'dark' }, 126 | ]; 127 | 128 | const styleOptions: DropdownOption[] = [ 129 | { text: 'Default', value: 'style_1' }, 130 | { text: 'Alternative', value: 'style_2' }, 131 | ]; 132 | 133 | const fileTypeOptions: DropdownOption[] = [ 134 | { text: 'PNG', value: 'png' }, 135 | { text: 'JPEG', value: 'jpeg' }, 136 | ]; 137 | 138 | let heroPatternOptions: DropdownOption[] = [ 139 | { text: 'anchorsAway', value: 'anchorsAway' }, 140 | { text: 'architect', value: 'architect' }, 141 | { text: 'autumn', value: 'autumn' }, 142 | { text: 'aztec', value: 'aztec' }, 143 | { text: 'bamboo', value: 'bamboo' }, 144 | { text: 'bankNote', value: 'bankNote' }, 145 | { text: 'bathroomFloor', value: 'bathroomFloor' }, 146 | { text: 'bevelCircle', value: 'bevelCircle' }, 147 | { text: 'boxes', value: 'boxes' }, 148 | { text: 'brickWall', value: 'brickWall' }, 149 | { text: 'bubbles', value: 'bubbles' }, 150 | { text: 'cage', value: 'cage' }, 151 | { text: 'charlieBrown', value: 'charlieBrown' }, 152 | { text: 'churchOnSunday', value: 'churchOnSunday' }, 153 | { text: 'circlesAndSquares', value: 'circlesAndSquares' }, 154 | { text: 'circuitBoard', value: 'circuitBoard' }, 155 | { text: 'connections', value: 'connections' }, 156 | { text: 'corkScrew', value: 'corkScrew' }, 157 | { text: 'current', value: 'current' }, 158 | { text: 'curtain', value: 'curtain' }, 159 | { text: 'cutout', value: 'cutout' }, 160 | { text: 'deathStar', value: 'deathStar' }, 161 | { text: 'diagonalLines', value: 'diagonalLines' }, 162 | { text: 'diagonalStripes', value: 'diagonalStripes' }, 163 | { text: 'dominos', value: 'dominos' }, 164 | { text: 'endlessClouds', value: 'endlessClouds' }, 165 | { text: 'eyes', value: 'eyes' }, 166 | { text: 'fallingTriangles', value: 'fallingTriangles' }, 167 | { text: 'fancyRectangles', value: 'fancyRectangles' }, 168 | { text: 'flippedDiamonds', value: 'flippedDiamonds' }, 169 | { text: 'floatingCogs', value: 'floatingCogs' }, 170 | { text: 'floorTile', value: 'floorTile' }, 171 | { text: 'formalInvitation', value: 'formalInvitation' }, 172 | { text: 'fourPointStars', value: 'fourPointStars' }, 173 | { text: 'glamorous', value: 'glamorous' }, 174 | { text: 'graphPaper', value: 'graphPaper' }, 175 | { text: 'groovy', value: 'groovy' }, 176 | { text: 'happyIntersection', value: 'happyIntersection' }, 177 | { text: 'heavyRain', value: 'heavyRain' }, 178 | { text: 'hexagons', value: 'hexagons' }, 179 | { text: 'hideout', value: 'hideout' }, 180 | { text: 'houndstooth', value: 'houndstooth' }, 181 | { text: 'iLikeFood', value: 'iLikeFood' }, 182 | { text: 'intersectingCircles', value: 'intersectingCircles' }, 183 | { text: 'jigsaw', value: 'jigsaw' }, 184 | { text: 'jupiter', value: 'jupiter' }, 185 | { text: 'kiwi', value: 'kiwi' }, 186 | { text: 'leaf', value: 'leaf' }, 187 | { text: 'linesInMotion', value: 'linesInMotion' }, 188 | { text: 'lips', value: 'lips' }, 189 | { text: 'lisbon', value: 'lisbon' }, 190 | { text: 'melt', value: 'melt' }, 191 | { text: 'moroccan', value: 'moroccan' }, 192 | { text: 'morphingDiamonds', value: 'morphingDiamonds' }, 193 | { text: 'overcast', value: 'overcast' }, 194 | { text: 'overlappingCircles', value: 'overlappingCircles' }, 195 | { text: 'overlappingDiamonds', value: 'overlappingDiamonds' }, 196 | { text: 'overlappingHexagons', value: 'overlappingHexagons' }, 197 | { text: 'parkayFloor', value: 'parkayFloor' }, 198 | { text: 'pianoMan', value: 'pianoMan' }, 199 | { text: 'pieFactory', value: 'pieFactory' }, 200 | { text: 'pixelDots', value: 'pixelDots' }, 201 | { text: 'plus', value: 'plus' }, 202 | { text: 'polkaDots', value: 'polkaDots' }, 203 | { text: 'rails', value: 'rails' }, 204 | { text: 'rain', value: 'rain' }, 205 | { text: 'randomShapes', value: 'randomShapes' }, 206 | { text: 'roundedPlusConnected', value: 'roundedPlusConnected' }, 207 | { text: 'signal', value: 'signal' }, 208 | { text: 'skulls', value: 'skulls' }, 209 | { text: 'slantedStars', value: 'slantedStars' }, 210 | { text: 'squares', value: 'squares' }, 211 | { text: 'squaresInSquares', value: 'squaresInSquares' }, 212 | { text: 'stampCollection', value: 'stampCollection' }, 213 | { text: 'steelBeams', value: 'steelBeams' }, 214 | { text: 'stripes', value: 'stripes' }, 215 | { text: 'temple', value: 'temple' }, 216 | { text: 'texture', value: 'texture' }, 217 | { text: 'ticTacToe', value: 'ticTacToe' }, 218 | { text: 'tinyCheckers', value: 'tinyCheckers' }, 219 | { text: 'topography', value: 'topography' }, 220 | { text: 'volcanoLamp', value: 'volcanoLamp' }, 221 | { text: 'wallpaper', value: 'wallpaper' }, 222 | { text: 'wiggle', value: 'wiggle' }, 223 | { text: 'xEquals', value: 'xEquals' }, 224 | { text: 'yyy', value: 'yyy' }, 225 | { text: 'zigZag', value: 'zigZag' }, 226 | ]; 227 | 228 | const fontSizeOptions: DropdownOption[] = Array 229 | .from({ length: 10 }) 230 | .map((_, i) => i * 25) 231 | .filter(n => n > 0) 232 | .map(n => ({ text: n + 'px', value: n + 'px' })); 233 | 234 | const markdownOptions: DropdownOption[] = [ 235 | { text: 'Plain Text', value: '0' }, 236 | { text: 'Markdown', value: '1' }, 237 | ]; 238 | 239 | const watermarkOptions: DropdownOption[] = [ 240 | { text: 'No', value: '0' }, 241 | { text: 'Yes', value: '1' }, 242 | ]; 243 | 244 | const imageOptions: DropdownOption[] = [ 245 | { text: 'Laravel Logo', value: 'https://laravel.com/img/logomark.min.svg' }, 246 | { text: 'Beyond Code Logo', value: 'https://beyondco.de/img/logo.svg' }, 247 | { text: 'PHP Logo', value: 'https://www.php.net/images/logos/new-php-logo.svg' }, 248 | { text: 'Alternative PHP Logo', value: 'https://www.php.net/images/logos/php-logo.svg' }, 249 | { text: 'academic-cap', value: 'academic-cap' }, 250 | { text: 'adjustments', value: 'adjustments' }, 251 | { text: 'annotation', value: 'annotation' }, 252 | { text: 'archive', value: 'archive' }, 253 | { text: 'arrow-circle-down', value: 'arrow-circle-down' }, 254 | { text: 'arrow-circle-left', value: 'arrow-circle-left' }, 255 | { text: 'arrow-circle-right', value: 'arrow-circle-right' }, 256 | { text: 'arrow-circle-up', value: 'arrow-circle-up' }, 257 | { text: 'arrow-down', value: 'arrow-down' }, 258 | { text: 'arrow-left', value: 'arrow-left' }, 259 | { text: 'arrow-narrow-down', value: 'arrow-narrow-down' }, 260 | { text: 'arrow-narrow-left', value: 'arrow-narrow-left' }, 261 | { text: 'arrow-narrow-right', value: 'arrow-narrow-right' }, 262 | { text: 'arrow-narrow-up', value: 'arrow-narrow-up' }, 263 | { text: 'arrow-right', value: 'arrow-right' }, 264 | { text: 'arrow-up', value: 'arrow-up' }, 265 | { text: 'arrows-expand', value: 'arrows-expand' }, 266 | { text: 'at-symbol', value: 'at-symbol' }, 267 | { text: 'backspace', value: 'backspace' }, 268 | { text: 'badge-check', value: 'badge-check' }, 269 | { text: 'ban', value: 'ban' }, 270 | { text: 'beaker', value: 'beaker' }, 271 | { text: 'bell', value: 'bell' }, 272 | { text: 'book-open', value: 'book-open' }, 273 | { text: 'bookmark-alt', value: 'bookmark-alt' }, 274 | { text: 'bookmark', value: 'bookmark' }, 275 | { text: 'briefcase', value: 'briefcase' }, 276 | { text: 'cake', value: 'cake' }, 277 | { text: 'calculator', value: 'calculator' }, 278 | { text: 'calendar', value: 'calendar' }, 279 | { text: 'camera', value: 'camera' }, 280 | { text: 'cash', value: 'cash' }, 281 | { text: 'chart-bar', value: 'chart-bar' }, 282 | { text: 'chart-pie', value: 'chart-pie' }, 283 | { text: 'chart-square-bar', value: 'chart-square-bar' }, 284 | { text: 'chat-alt-2', value: 'chat-alt-2' }, 285 | { text: 'chat-alt', value: 'chat-alt' }, 286 | { text: 'chat', value: 'chat' }, 287 | { text: 'check-circle', value: 'check-circle' }, 288 | { text: 'check', value: 'check' }, 289 | { text: 'chevron-double-down', value: 'chevron-double-down' }, 290 | { text: 'chevron-double-left', value: 'chevron-double-left' }, 291 | { text: 'chevron-double-right', value: 'chevron-double-right' }, 292 | { text: 'chevron-double-up', value: 'chevron-double-up' }, 293 | { text: 'chevron-down', value: 'chevron-down' }, 294 | { text: 'chevron-left', value: 'chevron-left' }, 295 | { text: 'chevron-right', value: 'chevron-right' }, 296 | { text: 'chevron-up', value: 'chevron-up' }, 297 | { text: 'chip', value: 'chip' }, 298 | { text: 'clipboard-check', value: 'clipboard-check' }, 299 | { text: 'clipboard-copy', value: 'clipboard-copy' }, 300 | { text: 'clipboard-list', value: 'clipboard-list' }, 301 | { text: 'clipboard', value: 'clipboard' }, 302 | { text: 'clock', value: 'clock' }, 303 | { text: 'cloud-download', value: 'cloud-download' }, 304 | { text: 'cloud-upload', value: 'cloud-upload' }, 305 | { text: 'cloud', value: 'cloud' }, 306 | { text: 'code', value: 'code' }, 307 | { text: 'cog', value: 'cog' }, 308 | { text: 'collection', value: 'collection' }, 309 | { text: 'color-swatch', value: 'color-swatch' }, 310 | { text: 'credit-card', value: 'credit-card' }, 311 | { text: 'cube-transparent', value: 'cube-transparent' }, 312 | { text: 'cube', value: 'cube' }, 313 | { text: 'currency-bangladeshi', value: 'currency-bangladeshi' }, 314 | { text: 'currency-dollar', value: 'currency-dollar' }, 315 | { text: 'currency-euro', value: 'currency-euro' }, 316 | { text: 'currency-pound', value: 'currency-pound' }, 317 | { text: 'currency-rupee', value: 'currency-rupee' }, 318 | { text: 'currency-yen', value: 'currency-yen' }, 319 | { text: 'cursor-click', value: 'cursor-click' }, 320 | { text: 'database', value: 'database' }, 321 | { text: 'desktop-computer', value: 'desktop-computer' }, 322 | { text: 'device-mobile', value: 'device-mobile' }, 323 | { text: 'device-tablet', value: 'device-tablet' }, 324 | { text: 'document-add', value: 'document-add' }, 325 | { text: 'document-download', value: 'document-download' }, 326 | { text: 'document-duplicate', value: 'document-duplicate' }, 327 | { text: 'document-remove', value: 'document-remove' }, 328 | { text: 'document-report', value: 'document-report' }, 329 | { text: 'document-search', value: 'document-search' }, 330 | { text: 'document-text', value: 'document-text' }, 331 | { text: 'document', value: 'document' }, 332 | { text: 'dots-circle-horizontal', value: 'dots-circle-horizontal' }, 333 | { text: 'dots-horizontal', value: 'dots-horizontal' }, 334 | { text: 'dots-vertical', value: 'dots-vertical' }, 335 | { text: 'download', value: 'download' }, 336 | { text: 'duplicate', value: 'duplicate' }, 337 | { text: 'emoji-happy', value: 'emoji-happy' }, 338 | { text: 'emoji-sad', value: 'emoji-sad' }, 339 | { text: 'exclamation-circle', value: 'exclamation-circle' }, 340 | { text: 'exclamation', value: 'exclamation' }, 341 | { text: 'external-link', value: 'external-link' }, 342 | { text: 'eye-off', value: 'eye-off' }, 343 | { text: 'eye', value: 'eye' }, 344 | { text: 'fast-forward', value: 'fast-forward' }, 345 | { text: 'film', value: 'film' }, 346 | { text: 'filter', value: 'filter' }, 347 | { text: 'finger-print', value: 'finger-print' }, 348 | { text: 'fire', value: 'fire' }, 349 | { text: 'flag', value: 'flag' }, 350 | { text: 'folder-add', value: 'folder-add' }, 351 | { text: 'folder-download', value: 'folder-download' }, 352 | { text: 'folder-open', value: 'folder-open' }, 353 | { text: 'folder-remove', value: 'folder-remove' }, 354 | { text: 'folder', value: 'folder' }, 355 | { text: 'gift', value: 'gift' }, 356 | { text: 'globe-alt', value: 'globe-alt' }, 357 | { text: 'globe', value: 'globe' }, 358 | { text: 'hand', value: 'hand' }, 359 | { text: 'hashtag', value: 'hashtag' }, 360 | { text: 'heart', value: 'heart' }, 361 | { text: 'home', value: 'home' }, 362 | { text: 'identification', value: 'identification' }, 363 | { text: 'inbox-in', value: 'inbox-in' }, 364 | { text: 'inbox', value: 'inbox' }, 365 | { text: 'information-circle', value: 'information-circle' }, 366 | { text: 'key', value: 'key' }, 367 | { text: 'library', value: 'library' }, 368 | { text: 'light-bulb', value: 'light-bulb' }, 369 | { text: 'lightning-bolt', value: 'lightning-bolt' }, 370 | { text: 'link', value: 'link' }, 371 | { text: 'location-marker', value: 'location-marker' }, 372 | { text: 'lock-closed', value: 'lock-closed' }, 373 | { text: 'lock-open', value: 'lock-open' }, 374 | { text: 'login', value: 'login' }, 375 | { text: 'logout', value: 'logout' }, 376 | { text: 'mail-open', value: 'mail-open' }, 377 | { text: 'mail', value: 'mail' }, 378 | { text: 'map', value: 'map' }, 379 | { text: 'menu-alt-1', value: 'menu-alt-1' }, 380 | { text: 'menu-alt-2', value: 'menu-alt-2' }, 381 | { text: 'menu-alt-3', value: 'menu-alt-3' }, 382 | { text: 'menu-alt-4', value: 'menu-alt-4' }, 383 | { text: 'menu', value: 'menu' }, 384 | { text: 'microphone', value: 'microphone' }, 385 | { text: 'minus-circle', value: 'minus-circle' }, 386 | { text: 'minus-sm', value: 'minus-sm' }, 387 | { text: 'minus', value: 'minus' }, 388 | { text: 'moon', value: 'moon' }, 389 | { text: 'music-note', value: 'music-note' }, 390 | { text: 'newspaper', value: 'newspaper' }, 391 | { text: 'office-building', value: 'office-building' }, 392 | { text: 'paper-airplane', value: 'paper-airplane' }, 393 | { text: 'paper-clip', value: 'paper-clip' }, 394 | { text: 'pause', value: 'pause' }, 395 | { text: 'pencil-alt', value: 'pencil-alt' }, 396 | { text: 'pencil', value: 'pencil' }, 397 | { text: 'phone-incoming', value: 'phone-incoming' }, 398 | { text: 'phone-missed-call', value: 'phone-missed-call' }, 399 | { text: 'phone-outgoing', value: 'phone-outgoing' }, 400 | { text: 'phone', value: 'phone' }, 401 | { text: 'photograph', value: 'photograph' }, 402 | { text: 'play', value: 'play' }, 403 | { text: 'plus-circle', value: 'plus-circle' }, 404 | { text: 'plus-sm', value: 'plus-sm' }, 405 | { text: 'plus', value: 'plus' }, 406 | { text: 'presentation-chart-bar', value: 'presentation-chart-bar' }, 407 | { text: 'presentation-chart-line', value: 'presentation-chart-line' }, 408 | { text: 'printer', value: 'printer' }, 409 | { text: 'puzzle', value: 'puzzle' }, 410 | { text: 'qrcode', value: 'qrcode' }, 411 | { text: 'question-mark-circle', value: 'question-mark-circle' }, 412 | { text: 'receipt-refund', value: 'receipt-refund' }, 413 | { text: 'receipt-tax', value: 'receipt-tax' }, 414 | { text: 'refresh', value: 'refresh' }, 415 | { text: 'reply', value: 'reply' }, 416 | { text: 'rewind', value: 'rewind' }, 417 | { text: 'rss', value: 'rss' }, 418 | { text: 'save-as', value: 'save-as' }, 419 | { text: 'save', value: 'save' }, 420 | { text: 'scale', value: 'scale' }, 421 | { text: 'scissors', value: 'scissors' }, 422 | { text: 'search-circle', value: 'search-circle' }, 423 | { text: 'search', value: 'search' }, 424 | { text: 'selector', value: 'selector' }, 425 | { text: 'server', value: 'server' }, 426 | { text: 'share', value: 'share' }, 427 | { text: 'shield-check', value: 'shield-check' }, 428 | { text: 'shield-exclamation', value: 'shield-exclamation' }, 429 | { text: 'shopping-bag', value: 'shopping-bag' }, 430 | { text: 'shopping-cart', value: 'shopping-cart' }, 431 | { text: 'sort-ascending', value: 'sort-ascending' }, 432 | { text: 'sort-descending', value: 'sort-descending' }, 433 | { text: 'sparkles', value: 'sparkles' }, 434 | { text: 'speakerphone', value: 'speakerphone' }, 435 | { text: 'star', value: 'star' }, 436 | { text: 'status-offline', value: 'status-offline' }, 437 | { text: 'status-online', value: 'status-online' }, 438 | { text: 'stop', value: 'stop' }, 439 | { text: 'sun', value: 'sun' }, 440 | { text: 'support', value: 'support' }, 441 | { text: 'switch-horizontal', value: 'switch-horizontal' }, 442 | { text: 'switch-vertical', value: 'switch-vertical' }, 443 | { text: 'table', value: 'table' }, 444 | { text: 'tag', value: 'tag' }, 445 | { text: 'template', value: 'template' }, 446 | { text: 'terminal', value: 'terminal' }, 447 | { text: 'thumb-down', value: 'thumb-down' }, 448 | { text: 'thumb-up', value: 'thumb-up' }, 449 | { text: 'ticket', value: 'ticket' }, 450 | { text: 'translate', value: 'translate' }, 451 | { text: 'trash', value: 'trash' }, 452 | { text: 'trending-down', value: 'trending-down' }, 453 | { text: 'trending-up', value: 'trending-up' }, 454 | { text: 'truck', value: 'truck' }, 455 | { text: 'upload', value: 'upload' }, 456 | { text: 'user-add', value: 'user-add' }, 457 | { text: 'user-circle', value: 'user-circle' }, 458 | { text: 'user-group', value: 'user-group' }, 459 | { text: 'user-remove', value: 'user-remove' }, 460 | { text: 'user', value: 'user' }, 461 | { text: 'users', value: 'users' }, 462 | { text: 'variable', value: 'variable' }, 463 | { text: 'video-camera', value: 'video-camera' }, 464 | { text: 'view-boards', value: 'view-boards' }, 465 | { text: 'view-grid-add', value: 'view-grid-add' }, 466 | { text: 'view-grid', value: 'view-grid' }, 467 | { text: 'view-list', value: 'view-list' }, 468 | { text: 'volume-off', value: 'volume-off' }, 469 | { text: 'volume-up', value: 'volume-up' }, 470 | { text: 'wifi', value: 'wifi' }, 471 | { text: 'x-circle', value: 'x-circle' }, 472 | { text: 'x', value: 'x' }, 473 | { text: 'zoom-in', value: 'zoom-in' }, 474 | { text: 'zoom-out', value: 'zoom-out' }, 475 | ]; 476 | 477 | const widthOptions = [ 478 | { text: 'width', value: 'auto' }, 479 | { text: '50', value: '50' }, 480 | { text: '100', value: '100' }, 481 | { text: '150', value: '150' }, 482 | { text: '200', value: '200' }, 483 | { text: '250', value: '250' }, 484 | { text: '300', value: '300' }, 485 | { text: '350', value: '350' }, 486 | { text: '350', value: '350' }, 487 | { text: '400', value: '400' }, 488 | { text: '450', value: '450' }, 489 | { text: '500', value: '500' }, 490 | { text: '550', value: '550' }, 491 | { text: '600', value: '600' }, 492 | { text: '650', value: '650' }, 493 | { text: '700', value: '700' }, 494 | { text: '750', value: '750' }, 495 | { text: '800', value: '800' }, 496 | ]; 497 | 498 | const heightOptions = [ 499 | { text: 'height', value: 'auto' }, 500 | { text: '50', value: '50' }, 501 | { text: '100', value: '100' }, 502 | { text: '150', value: '150' }, 503 | { text: '200', value: '200' }, 504 | { text: '250', value: '250' }, 505 | { text: '300', value: '300' }, 506 | { text: '350', value: '350' }, 507 | { text: '350', value: '350' }, 508 | { text: '400', value: '400' }, 509 | { text: '450', value: '450' }, 510 | { text: '500', value: '500' }, 511 | { text: '550', value: '550' }, 512 | { text: '600', value: '600' }, 513 | { text: '650', value: '650' }, 514 | { text: '700', value: '700' }, 515 | { text: '750', value: '750' }, 516 | { text: '800', value: '800' }, 517 | ]; 518 | 519 | interface AppState extends ParsedRequest { 520 | loading: boolean; 521 | showToast: boolean; 522 | messageToast: string; 523 | selectedImageIndex: number; 524 | widths: string[]; 525 | heights: string[]; 526 | overrideUrl: URL | null; 527 | } 528 | 529 | type SetState = (state: Partial) => void; 530 | 531 | const App = (_: any, state: AppState, setState: SetState) => { 532 | const setLoadingState = (newState: Partial) => { 533 | window.clearTimeout(timeout); 534 | if (state.overrideUrl && state.overrideUrl !== newState.overrideUrl) { 535 | newState.overrideUrl = state.overrideUrl; 536 | } 537 | if (newState.overrideUrl) { 538 | timeout = window.setTimeout(() => setState({ overrideUrl: null }), 200); 539 | } 540 | 541 | setState({ ...newState, loading: true }); 542 | }; 543 | const { 544 | fileType = 'png', 545 | fontSize = '100px', 546 | theme = 'light', 547 | md = true, 548 | text = 'My Package', 549 | packageManager = packageManagerOptions[0].value, 550 | packageName = 'vendor/my-awesome-package', 551 | images = [imageOptions[0].value], 552 | widths=[], 553 | heights=[], 554 | showWatermark = true, 555 | style = 'style_1', 556 | pattern = 'architect', 557 | description = 'This is why it\'s awesome', 558 | showToast = false, 559 | messageToast = '', 560 | loading = true, 561 | selectedImageIndex = 0, 562 | overrideUrl = null, 563 | } = state; 564 | const mdValue = md ? '1' : '0'; 565 | const watermarkValue = showWatermark ? '1' : '0'; 566 | const url = new URL(window.location.origin); 567 | url.pathname = `${encodeURIComponent(text)}.${fileType}`; 568 | url.searchParams.append('theme', theme); 569 | url.searchParams.append('packageManager', packageManager); 570 | url.searchParams.append('packageName', packageName); 571 | url.searchParams.append('pattern', pattern); 572 | url.searchParams.append('style', style); 573 | url.searchParams.append('description', description); 574 | url.searchParams.append('md', mdValue); 575 | url.searchParams.append('showWatermark', watermarkValue); 576 | url.searchParams.append('fontSize', fontSize); 577 | for (let image of images) { 578 | url.searchParams.append('images', image); 579 | } 580 | for (let width of widths) { 581 | url.searchParams.append('widths', width); 582 | } 583 | for (let height of heights) { 584 | url.searchParams.append('heights', height); 585 | } 586 | 587 | return H('div', 588 | { className: 'split' }, 589 | H('div', 590 | { className: 'pull-left' }, 591 | H('div', 592 | H(Field, { 593 | label: 'Theme', 594 | input: H(Dropdown, { 595 | options: themeOptions, 596 | value: theme, 597 | onchange: (val: Theme) => { 598 | let clone = [...images]; 599 | clone[0] = imageOptions[selectedImageIndex].value; 600 | setLoadingState({ theme: val, images: clone }); 601 | } 602 | }) 603 | }), 604 | H(Field, { 605 | label: 'Style', 606 | input: H(Dropdown, { 607 | options: styleOptions, 608 | value: style, 609 | onchange: (val: Style) => { 610 | setLoadingState({ style: val }); 611 | } 612 | }) 613 | }), 614 | H(Field, { 615 | label: 'Background Pattern', 616 | input: H(Dropdown, { 617 | options: heroPatternOptions, 618 | value: pattern, 619 | onchange: (val: string) => { 620 | setLoadingState({ pattern: val }); 621 | } 622 | }) 623 | }), 624 | H(Field, { 625 | label: 'File Type', 626 | input: H(Dropdown, { 627 | options: fileTypeOptions, 628 | value: fileType, 629 | onchange: (val: FileType) => setLoadingState({ fileType: val }) 630 | }) 631 | }), 632 | H(Field, { 633 | label: 'Font Size', 634 | input: H(Dropdown, { 635 | options: fontSizeOptions, 636 | value: fontSize, 637 | onchange: (val: string) => setLoadingState({ fontSize: val }) 638 | }) 639 | }), 640 | H(Field, { 641 | label: 'Text Type', 642 | input: H(Dropdown, { 643 | options: markdownOptions, 644 | value: mdValue, 645 | onchange: (val: string) => setLoadingState({ md: val === '1' }) 646 | }) 647 | }), 648 | H(Field, { 649 | label: 'Text Input', 650 | input: H(TextInput, { 651 | value: text, 652 | oninput: (val: string) => { 653 | setLoadingState({ text: val, overrideUrl: url }); 654 | } 655 | }) 656 | }), 657 | H(Field, { 658 | label: 'Short Description', 659 | input: H(TextInput, { 660 | value: description, 661 | oninput: (val: string) => { 662 | setLoadingState({ description: val, overrideUrl: url }); 663 | } 664 | }) 665 | }), 666 | H(Field, { 667 | label: 'Package Manager', 668 | input: H(Dropdown, { 669 | options: packageManagerOptions, 670 | value: packageManager, 671 | onchange: (val: string) => setLoadingState({ packageManager: val, overrideUrl: url }) 672 | }) 673 | }), 674 | H(Field, { 675 | label: 'Package Name', 676 | input: H(TextInput, { 677 | value: packageName, 678 | oninput: (val: string) => { 679 | setLoadingState({ packageName: val, overrideUrl: url }); 680 | } 681 | }) 682 | }), 683 | H(Field, { 684 | label: 'Image', 685 | input: H('div', 686 | H(Dropdown, { 687 | options: imageOptions, 688 | value: imageOptions[selectedImageIndex].value, 689 | onchange: (val: string) => { 690 | let clone = [...images]; 691 | clone[0] = val; 692 | const selected = imageOptions.map(o => o.value).indexOf(val); 693 | setLoadingState({ images: clone, selectedImageIndex: selected }); 694 | } 695 | }), 696 | H('div', 697 | { className: 'field-flex' }, 698 | H(Dropdown, { 699 | options: widthOptions, 700 | value: widths[0], 701 | small: true, 702 | onchange: (val: string) => { 703 | let clone = [...widths]; 704 | clone[0] = val; 705 | setLoadingState({ widths: clone }); 706 | } 707 | }), 708 | H(Dropdown, { 709 | options: heightOptions, 710 | value: heights[0], 711 | small: true, 712 | onchange: (val: string) => { 713 | let clone = [...heights]; 714 | clone[0] = val; 715 | setLoadingState({ heights: clone }); 716 | } 717 | }) 718 | ) 719 | ), 720 | }), 721 | H(Field, { 722 | label: 'Watermark', 723 | input: H(Dropdown, { 724 | options: watermarkOptions, 725 | value: watermarkValue, 726 | onchange: (val: string) => setLoadingState({ showWatermark: val === '1', overrideUrl: url }) 727 | }) 728 | }), 729 | ) 730 | ), 731 | H('div', 732 | { className: 'pull-right' }, 733 | H(ImagePreview, { 734 | src: overrideUrl ? overrideUrl.href : url.href, 735 | loading: loading, 736 | onload: () => setState({ loading: false }), 737 | onerror: () => { 738 | setState({ showToast: true, messageToast: 'Oops, an error occurred' }); 739 | setTimeout(() => setState({ showToast: false }), 2000); 740 | }, 741 | onclick: (e: Event) => { 742 | e.preventDefault(); 743 | const success = copee.toClipboard(url.href); 744 | if (success) { 745 | setState({ showToast: true, messageToast: 'Copied image URL to clipboard' }); 746 | setTimeout(() => setState({ showToast: false }), 3000); 747 | } else { 748 | window.open(url.href, '_blank'); 749 | } 750 | return false; 751 | } 752 | }) 753 | ), 754 | H(Toast, { 755 | message: messageToast, 756 | show: showToast, 757 | }) 758 | ); 759 | }; 760 | 761 | R(H(App), document.getElementById('app')); 762 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../api/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "outDir": "../public/dist" 6 | }, 7 | "include": [ 8 | "./" 9 | ] 10 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/marked@0.7.3": 6 | version "0.7.3" 7 | resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.7.3.tgz#3859f6fea52a2b73f42283018bd34b03f3c4fb3f" 8 | integrity sha512-WXdEKuT3azHxLTThd5dwnpLt2Q9QiC8iKj09KZRtVqro3pX8hhY+GbD8FZOae6SBBEJ22yKJn3c7ejL0aucAcA== 9 | 10 | "@types/mime-types@^2.1.0": 11 | version "2.1.0" 12 | resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73" 13 | integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM= 14 | 15 | "@types/node@*": 16 | version "11.10.4" 17 | resolved "https://registry.yarnpkg.com/@types/node/-/node-11.10.4.tgz#3f5fc4f0f322805f009e00ab35a2ff3d6b778e42" 18 | integrity sha512-wa09itaLE8L705aXd8F80jnFpxz3Y1/KRHfKsYL2bPc0XF+wEWu8sR9n5bmeu8Ba1N9z2GRNzm/YdHcghLkLKg== 19 | 20 | "@types/puppeteer-core@2.0.0": 21 | version "2.0.0" 22 | resolved "https://registry.yarnpkg.com/@types/puppeteer-core/-/puppeteer-core-2.0.0.tgz#3b7fbbac53d56b566f5ef096116e1d60d504aa45" 23 | integrity sha512-JvoEb7KgEkUet009ZDrtpUER3hheXoHgQByuYpJZ5WWT7LWwMH+0NTqGQXGgoOKzs+G5NA1T4DZwXK79Bhnejw== 24 | dependencies: 25 | "@types/puppeteer" "*" 26 | 27 | "@types/puppeteer@*": 28 | version "1.12.1" 29 | resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.12.1.tgz#f61f9e0da45e36cdbbeab3b088995a8a2cad47a4" 30 | integrity sha512-6qpe7XXM93iWh8quEP8Ay516Vmfc2r+ZAxFH3Mt6fx3vzmZz+4Q+hYxc9PxeEIXJhWLAAPYAgAiM/vLHEUwGpw== 31 | dependencies: 32 | "@types/node" "*" 33 | 34 | "@types/puppeteer@2.0.1": 35 | version "2.0.1" 36 | resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-2.0.1.tgz#83a1d7f0a1c2e0edbbb488b4d8fb54b14ec9d455" 37 | integrity sha512-G8vEyU83Bios+dzs+DZGpAirDmMqRhfFBJCkFrg+A5+6n5EPPHxwBLImJto3qjh0mrBXbLBCyuahhhtTrAfR5g== 38 | dependencies: 39 | "@types/node" "*" 40 | 41 | agent-base@5: 42 | version "5.1.1" 43 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" 44 | integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== 45 | 46 | async-limiter@~1.0.0: 47 | version "1.0.0" 48 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" 49 | integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== 50 | 51 | balanced-match@^1.0.0: 52 | version "1.0.0" 53 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 54 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 55 | 56 | bl@^3.0.0: 57 | version "3.0.1" 58 | resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f" 59 | integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ== 60 | dependencies: 61 | readable-stream "^3.0.1" 62 | 63 | brace-expansion@^1.1.7: 64 | version "1.1.11" 65 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 66 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 67 | dependencies: 68 | balanced-match "^1.0.0" 69 | concat-map "0.0.1" 70 | 71 | buffer-from@^1.0.0: 72 | version "1.1.1" 73 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 74 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 75 | 76 | chownr@^1.1.1: 77 | version "1.1.3" 78 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" 79 | integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== 80 | 81 | chrome-aws-lambda@2.1.1: 82 | version "2.1.1" 83 | resolved "https://registry.yarnpkg.com/chrome-aws-lambda/-/chrome-aws-lambda-2.1.1.tgz#2aeb0c97fb67e908d06dc8d92d92c7d4fb58467c" 84 | integrity sha512-Wer2QuygxsCov5bM2+8CLa6qYpNsc5AxYTlgTne00aFoxFP491LGJRxOQtGnYtsJP6UG4pB0SfrwTyPnLys1Lw== 85 | dependencies: 86 | lambdafs "^1.3.0" 87 | 88 | concat-map@0.0.1: 89 | version "0.0.1" 90 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 91 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 92 | 93 | concat-stream@1.6.2: 94 | version "1.6.2" 95 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 96 | integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 97 | dependencies: 98 | buffer-from "^1.0.0" 99 | inherits "^2.0.3" 100 | readable-stream "^2.2.2" 101 | typedarray "^0.0.6" 102 | 103 | core-util-is@~1.0.0: 104 | version "1.0.2" 105 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 106 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 107 | 108 | debug@2.6.9: 109 | version "2.6.9" 110 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 111 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 112 | dependencies: 113 | ms "2.0.0" 114 | 115 | debug@4, debug@^4.1.0: 116 | version "4.1.1" 117 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 118 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 119 | dependencies: 120 | ms "^2.1.1" 121 | 122 | end-of-stream@^1.1.0, end-of-stream@^1.4.1: 123 | version "1.4.4" 124 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" 125 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 126 | dependencies: 127 | once "^1.4.0" 128 | 129 | extract-zip@^1.6.6: 130 | version "1.6.7" 131 | resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" 132 | integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= 133 | dependencies: 134 | concat-stream "1.6.2" 135 | debug "2.6.9" 136 | mkdirp "0.5.1" 137 | yauzl "2.4.1" 138 | 139 | fd-slicer@~1.0.1: 140 | version "1.0.1" 141 | resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" 142 | integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= 143 | dependencies: 144 | pend "~1.2.0" 145 | 146 | fs-constants@^1.0.0: 147 | version "1.0.0" 148 | resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" 149 | integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== 150 | 151 | fs-extra@^8.0.1: 152 | version "8.1.0" 153 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" 154 | integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== 155 | dependencies: 156 | graceful-fs "^4.2.0" 157 | jsonfile "^4.0.0" 158 | universalify "^0.1.0" 159 | 160 | fs.realpath@^1.0.0: 161 | version "1.0.0" 162 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 163 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 164 | 165 | glob@^7.1.3: 166 | version "7.1.3" 167 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 168 | integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== 169 | dependencies: 170 | fs.realpath "^1.0.0" 171 | inflight "^1.0.4" 172 | inherits "2" 173 | minimatch "^3.0.4" 174 | once "^1.3.0" 175 | path-is-absolute "^1.0.0" 176 | 177 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 178 | version "4.2.3" 179 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" 180 | integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== 181 | 182 | hero-patterns@2.1.0: 183 | version "2.1.0" 184 | resolved "https://registry.yarnpkg.com/hero-patterns/-/hero-patterns-2.1.0.tgz#f99109197bdfb8a8a352bdf86e255eaf5fab89de" 185 | integrity sha512-cHob8+etbF3kl+ylmvFQORBdFx3njmFUv+JIbu4i+GVNvfCgvNkWolkBhYU9fE2cecx+DGxLSiSNq+F5NWJrTQ== 186 | 187 | heroicons@0.4.2: 188 | version "0.4.2" 189 | resolved "https://registry.yarnpkg.com/heroicons/-/heroicons-0.4.2.tgz#edaffde42d14bffdd456f0566fd9b8e295ad69e2" 190 | integrity sha512-24Bc6LKmamKHzGiNC80/r9v/7pQrma2V5KlaLUHGoarqm+hggmxnngU+RjHQT8sANWPa5FWKLftn4fpmvry/7w== 191 | 192 | https-proxy-agent@^4.0.0: 193 | version "4.0.0" 194 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" 195 | integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== 196 | dependencies: 197 | agent-base "5" 198 | debug "4" 199 | 200 | inflight@^1.0.4: 201 | version "1.0.6" 202 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 203 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 204 | dependencies: 205 | once "^1.3.0" 206 | wrappy "1" 207 | 208 | inherits@2, inherits@~2.0.3: 209 | version "2.0.3" 210 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 211 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 212 | 213 | inherits@^2.0.3: 214 | version "2.0.4" 215 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 216 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 217 | 218 | isarray@~1.0.0: 219 | version "1.0.0" 220 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 221 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 222 | 223 | jsonfile@^4.0.0: 224 | version "4.0.0" 225 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 226 | integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= 227 | optionalDependencies: 228 | graceful-fs "^4.1.6" 229 | 230 | jsonfile@^5.0.0: 231 | version "5.0.0" 232 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922" 233 | integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w== 234 | dependencies: 235 | universalify "^0.1.2" 236 | optionalDependencies: 237 | graceful-fs "^4.1.6" 238 | 239 | lambdafs@^1.3.0: 240 | version "1.3.0" 241 | resolved "https://registry.yarnpkg.com/lambdafs/-/lambdafs-1.3.0.tgz#7e369cedc9a09623bb365fa99a1113c2ab2fc7ae" 242 | integrity sha512-HqRPmEgtkTW4sCYDUjTEuTGkjCHuLvtZU8iM8GkhD7SpjW4AJJbBk86YU4K43sWGuW5Vmzp1lVCx4ab/kJsuBw== 243 | dependencies: 244 | tar-fs "^2.0.0" 245 | 246 | marked@0.8.2: 247 | version "0.8.2" 248 | resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355" 249 | integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw== 250 | 251 | mime-db@1.43.0: 252 | version "1.43.0" 253 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" 254 | integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== 255 | 256 | mime-types@^2.1.25: 257 | version "2.1.26" 258 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" 259 | integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== 260 | dependencies: 261 | mime-db "1.43.0" 262 | 263 | mime@^2.0.3: 264 | version "2.4.0" 265 | resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6" 266 | integrity sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w== 267 | 268 | minimatch@^3.0.4: 269 | version "3.0.4" 270 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 271 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 272 | dependencies: 273 | brace-expansion "^1.1.7" 274 | 275 | minimist@0.0.8: 276 | version "0.0.8" 277 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 278 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 279 | 280 | mkdirp@0.5.1, mkdirp@^0.5.1: 281 | version "0.5.1" 282 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 283 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 284 | dependencies: 285 | minimist "0.0.8" 286 | 287 | ms@2.0.0: 288 | version "2.0.0" 289 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 290 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 291 | 292 | ms@^2.1.1: 293 | version "2.1.1" 294 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 295 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 296 | 297 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 298 | version "1.4.0" 299 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 300 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 301 | dependencies: 302 | wrappy "1" 303 | 304 | path-is-absolute@^1.0.0: 305 | version "1.0.1" 306 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 307 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 308 | 309 | pend@~1.2.0: 310 | version "1.2.0" 311 | resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 312 | integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= 313 | 314 | process-nextick-args@~2.0.0: 315 | version "2.0.0" 316 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 317 | integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== 318 | 319 | progress@^2.0.1: 320 | version "2.0.3" 321 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 322 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 323 | 324 | proxy-from-env@^1.0.0: 325 | version "1.0.0" 326 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" 327 | integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= 328 | 329 | pump@^3.0.0: 330 | version "3.0.0" 331 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 332 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 333 | dependencies: 334 | end-of-stream "^1.1.0" 335 | once "^1.3.1" 336 | 337 | puppeteer-core@2.1.1: 338 | version "2.1.1" 339 | resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-2.1.1.tgz#e9b3fbc1237b4f66e25999832229e9db3e0b90ed" 340 | integrity sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w== 341 | dependencies: 342 | "@types/mime-types" "^2.1.0" 343 | debug "^4.1.0" 344 | extract-zip "^1.6.6" 345 | https-proxy-agent "^4.0.0" 346 | mime "^2.0.3" 347 | mime-types "^2.1.25" 348 | progress "^2.0.1" 349 | proxy-from-env "^1.0.0" 350 | rimraf "^2.6.1" 351 | ws "^6.1.0" 352 | 353 | readable-stream@^2.2.2: 354 | version "2.3.6" 355 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 356 | integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== 357 | dependencies: 358 | core-util-is "~1.0.0" 359 | inherits "~2.0.3" 360 | isarray "~1.0.0" 361 | process-nextick-args "~2.0.0" 362 | safe-buffer "~5.1.1" 363 | string_decoder "~1.1.1" 364 | util-deprecate "~1.0.1" 365 | 366 | readable-stream@^3.0.1, readable-stream@^3.1.1: 367 | version "3.6.0" 368 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 369 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 370 | dependencies: 371 | inherits "^2.0.3" 372 | string_decoder "^1.1.1" 373 | util-deprecate "^1.0.1" 374 | 375 | rimraf@^2.6.1: 376 | version "2.6.3" 377 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 378 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 379 | dependencies: 380 | glob "^7.1.3" 381 | 382 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 383 | version "5.1.2" 384 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 385 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 386 | 387 | safe-buffer@~5.2.0: 388 | version "5.2.1" 389 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 390 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 391 | 392 | string_decoder@^1.1.1: 393 | version "1.3.0" 394 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 395 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 396 | dependencies: 397 | safe-buffer "~5.2.0" 398 | 399 | string_decoder@~1.1.1: 400 | version "1.1.1" 401 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 402 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 403 | dependencies: 404 | safe-buffer "~5.1.0" 405 | 406 | tar-fs@^2.0.0: 407 | version "2.0.0" 408 | resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" 409 | integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== 410 | dependencies: 411 | chownr "^1.1.1" 412 | mkdirp "^0.5.1" 413 | pump "^3.0.0" 414 | tar-stream "^2.0.0" 415 | 416 | tar-stream@^2.0.0: 417 | version "2.1.0" 418 | resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" 419 | integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== 420 | dependencies: 421 | bl "^3.0.0" 422 | end-of-stream "^1.4.1" 423 | fs-constants "^1.0.0" 424 | inherits "^2.0.3" 425 | readable-stream "^3.1.1" 426 | 427 | twemoji-parser@12.1.3: 428 | version "12.1.3" 429 | resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-12.1.3.tgz#916c0153e77bd5f1011e7a99cbeacf52e43c9371" 430 | integrity sha512-ND4LZXF4X92/PFrzSgGkq6KPPg8swy/U0yRw1k/+izWRVmq1HYi3khPwV3XIB6FRudgVICAaBhJfW8e8G3HC7Q== 431 | 432 | twemoji@12.1.5: 433 | version "12.1.5" 434 | resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-12.1.5.tgz#a961fb65a1afcb1f729ad7e59391f9fe969820b9" 435 | integrity sha512-B0PBVy5xomwb1M/WZxf/IqPZfnoIYy1skXnlHjMwLwTNfZ9ljh8VgWQktAPcJXu8080WoEh6YwQGPVhDVqvrVQ== 436 | dependencies: 437 | fs-extra "^8.0.1" 438 | jsonfile "^5.0.0" 439 | twemoji-parser "12.1.3" 440 | universalify "^0.1.2" 441 | 442 | typedarray@^0.0.6: 443 | version "0.0.6" 444 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 445 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 446 | 447 | typescript@3.8.3: 448 | version "3.8.3" 449 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" 450 | integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== 451 | 452 | universalify@^0.1.0, universalify@^0.1.2: 453 | version "0.1.2" 454 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" 455 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== 456 | 457 | util-deprecate@^1.0.1, util-deprecate@~1.0.1: 458 | version "1.0.2" 459 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 460 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 461 | 462 | wrappy@1: 463 | version "1.0.2" 464 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 465 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 466 | 467 | ws@^6.1.0: 468 | version "6.1.4" 469 | resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" 470 | integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== 471 | dependencies: 472 | async-limiter "~1.0.0" 473 | 474 | yauzl@2.4.1: 475 | version "2.4.1" 476 | resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" 477 | integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= 478 | dependencies: 479 | fd-slicer "~1.0.1" 480 | --------------------------------------------------------------------------------