Compare commits

..

No commits in common. "develop" and "v1.14.2" have entirely different histories.

31 changed files with 3290 additions and 3796 deletions

View File

@ -8,8 +8,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Lint commit messages
uses: wagoid/commitlint-github-action@v6
uses: wagoid/commitlint-github-action@v2

View File

@ -8,9 +8,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version-file: '.nvmrc'
cache: 'yarn'

View File

@ -11,22 +11,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Cache Next.js build cache
uses: actions/cache@v4
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
- name: Build static assets
run: yarn build
- name: Semantic Release
uses: cycjimmy/semantic-release-action@v4
uses: cycjimmy/semantic-release-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,10 +7,10 @@ jobs:
name: Run Cypress
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version-file: '.nvmrc'
cache: 'yarn'
@ -19,7 +19,7 @@ jobs:
run: yarn install --frozen-lockfile
- name: Cache Next.js build cache
uses: actions/cache@v4
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
@ -28,7 +28,7 @@ jobs:
run: yarn build
- name: Run Cypress
uses: cypress-io/github-action@v6
uses: cypress-io/github-action@v2
with:
start: npm start
wait-on: http://localhost:3000
@ -36,13 +36,13 @@ jobs:
# store videos and any screenshots
# NOTE: screenshots will be generated only if E2E test failed
# thus we store screenshots only on failures
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v2
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
# Test run video was always captured, so this action uses "always()" condition
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v2
if: always()
with:
name: cypress-videos

2
.nvmrc
View File

@ -1 +1 @@
22.11
14.18

View File

@ -1,7 +1,8 @@
{
"useTabs": true,
"useTabs": true,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "es5",
"jsxBracketSameLine": true,
"semi": false
}

View File

@ -3,11 +3,11 @@
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"eslint.run": "onType",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"typescript.tsdk": "node_modules/typescript/lib"
"cssModules.camelCase": true
}

View File

@ -2,7 +2,7 @@
.base {
padding: 5px 12px;
font-family: 'Manrope', Arial, '"Helvetica Neue"', Helvetica, sans-serif;
font-family: Manrope, Arial, '"Helvetica Neue"', Helvetica, sans-serif;
font-size: $font-size-title;
line-height: $line-height-title;
color: $black;

View File

@ -208,7 +208,6 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
}
const { getRootProps, getInputProps, isDragActive } = useDropzone({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onDrop,
accept: Object.keys(ACCEPT_MIME_TYPES),
maxSize: ONE_MB,
@ -231,15 +230,13 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
className={classnames(styles.container, {
[styles.loading]: isLoading,
[styles.transparent]: !image,
})}
>
})}>
{!image && !zipData && (
<>
<div
className={styles.dropZoneWrapper}
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
>
onMouseLeave={() => setIsHover(false)}>
<SvgDropZone className={classnames(styles.dashed, { [styles.dragActive]: isDragActive })} />
<div {...getRootProps()} className={styles.dropZone} data-cy="drag-and-drop">
<input {...getInputProps()} />
@ -252,8 +249,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
className={classnames(styles.imageUploadText, styles.mobileUploadText, {
[styles.dragActive]: isDragActive,
})}
data-cy="drag-and-drop-text"
>
data-cy="drag-and-drop-text">
<span>
Generate all the sizes for your
<br />
@ -266,8 +262,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
className={classnames(styles.imageUploadText, styles.desktopUploadText, {
[styles.dragActive]: isDragActive,
})}
data-cy="drag-and-drop-text"
>
data-cy="drag-and-drop-text">
<CSSTransition in={!isDragActive} timeout={200} classNames="collapse" unmountOnExit>
<span>Drag &amp;&nbsp;</span>
</CSSTransition>
@ -318,8 +313,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
weight="semiBold"
color="gray"
tag="div"
className={styles.filenameWrapper}
>
className={styles.filenameWrapper}>
<span className={styles.filename} data-cy="preview-filename">
{imageData.name}
{imageData.extension}
@ -363,8 +357,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
id="pwa"
disabled={PWADisabled}
onChange={togglePwa}
data-cy="preview-pwa-compatible"
>
data-cy="preview-pwa-compatible">
<Typography variant="regularBody" weight="semiBold" muted={PWADisabled}>
PWA compatible
</Typography>
@ -380,8 +373,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
id="dark"
disabled
onChange={toggleDarkMode}
data-cy="preview-dark-mode-version"
>
data-cy="preview-dark-mode-version">
<Typography variant="regularBody" weight="semiBold" muted>
Dark Mode version
</Typography>
@ -400,18 +392,14 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
onClick={() => {
splitbee.track('Re-upload')
resetImage()
}}
>
}}>
Re-upload
</Button>
<Button
color="white"
background="bgLink"
className={styles.imageGenerate}
onClick={() => {
generateFavicon(image)
}}
>
onClick={() => generateFavicon(image)}>
Generate Favicon
</Button>
</div>
@ -454,8 +442,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
className={styles.showCode}
variant="regularTransparent"
color="gray"
onClick={() => setIsModalOpen(true)}
>
onClick={() => setIsModalOpen(true)}>
Show
</Button>
</div>
@ -468,16 +455,14 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
onClick={() => {
splitbee.track('Make a new one')
resetImage()
}}
>
}}>
Make a new one
</Button>
<Button
color="white"
background="bgGreen"
className={styles.imageGenerate}
onClick={() => onDownload(zipData)}
>
onClick={() => onDownload(zipData)}>
Download Favicon
</Button>
</div>
@ -490,8 +475,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
className={styles.content}
overlayClassName={styles.overlay}
closeTimeoutMS={200}
contentLabel="Code generated"
>
contentLabel="Code generated">
<div className={styles.modalContainer} {...bind()}>
<div className={classnames(styles.modalHeader, styles.modalHeaderMobile)}>
<Typography variant="extraLargeTitle" weight="extraBold" color="white" colorImmutable>

View File

@ -38,8 +38,7 @@ const FavyconError = ({ error, clearError }: FavyconErrorProps) => {
variant="regularBody"
color="white"
colorImmutable={true}
weight="semiBold"
>
weight="semiBold">
{error}
</Typography>
<svg height={20} width={20}>

View File

@ -290,11 +290,7 @@
margin-right: 2px;
}
// Both modifiers do the same job: drop the base class's Twitter blue for the
// neutral ink. Kept as two names rather than one shared class so the markup
// still says which kind of link it is.
&.personGithub,
&.personWebsite {
&.personGithub {
color: $black;
p {
@ -320,8 +316,7 @@
}
}
:global(.dark) .personTwitter.personGithub p,
:global(.dark) .personTwitter.personWebsite p {
:global(.dark) .personTwitter.personGithub p {
color: $white;
&:hover {
@ -333,13 +328,6 @@
fill: $white;
}
// The GitHub mark is a filled path; the globe is stroked, so it needs the other
// property. Both are set explicitly rather than relying on currentColor, to
// match how the rule above already works.
:global(.dark) .personTwitter.personWebsite p svg {
stroke: $white;
}
.modalFooter {
padding-bottom: 48px;
margin-top: 12px;

View File

@ -7,7 +7,6 @@ import { Typography } from 'components/typography'
import { ToolTitle } from 'components/tool-title'
import { SvgTwitter } from 'components/svgs/svg-twitter'
import { SvgGithub } from 'components/svgs/svg-github'
import { SvgWebsite } from 'components/svgs/svg-website'
import styles from './index.module.scss'
import { SvgFavycon } from 'components/svgs/svg-favycon'
@ -20,47 +19,26 @@ import miguelTeixeira from '../../public/images/people/miguel-teixeira.png'
const Modal = dynamic(() => import('react-modal'))
// A lookup rather than a chain of ternaries in the JSX, because there are three
// kinds of link now. `personTwitter` is the shared base class — it predates the
// other two and carries the layout, so the modifiers only restate the colour.
const socials = {
twitter: {
Icon: SvgTwitter,
url: (handle: string) => `https://twitter.com/${handle}`,
modifier: undefined,
},
github: {
Icon: SvgGithub,
url: (handle: string) => `https://github.com/${handle}`,
modifier: styles.personGithub,
},
website: {
Icon: SvgWebsite,
url: (handle: string) => `https://${handle}`,
modifier: styles.personWebsite,
},
}
const people = [
{
handle: 'aboutaugusto',
screenName: 'aboutaugusto',
name: 'Augusto Lopes',
role: 'Product Designer',
social: 'twitter' as const,
social: 'twitter',
photo: augustoLopes,
},
{
handle: 'ruisaraiva.dev',
screenName: 'ruisaraiva19',
name: 'Rui Saraiva',
role: 'Principal Engineer',
social: 'website' as const,
role: 'Full-Stack Developer',
social: 'twitter',
photo: ruiSaraiva,
},
{
handle: 'miguellteixeira',
screenName: 'miguellteixeira',
name: 'Miguel Teixeira',
role: 'Full-Stack Developer',
social: 'github' as const,
social: 'github',
photo: miguelTeixeira,
},
]
@ -124,8 +102,7 @@ const FavyconInfo = ({
className={styles.content}
overlayClassName={styles.overlay}
closeTimeoutMS={200}
contentLabel="About Us"
>
contentLabel="About Us">
<div className={styles.modalContainer} {...bind()}>
<div className={styles.modalHeaderMobile}>
<div className={styles.modalHeaderTop}>
@ -160,47 +137,54 @@ const FavyconInfo = ({
Behind the curtains
</Typography>
<Typography variant="largeBody" weight="medium" className={styles.subTitle}>
For new updates be sure to follow these guys.
For new updates be sure to follow these guys on Twitter.
</Typography>
</div>
<hr className={styles.hr} />
<div className={styles.people}>
{people.map((person) => {
const { Icon, url, modifier } = socials[person.social]
return (
<div key={person.handle} className={styles.person}>
<div className={styles.personAvatar}>
<Image
src={person.photo}
alt={person.name}
width={56}
height={56}
placeholder="blur"
layout="intrinsic"
/>
</div>
<div>
<Typography variant="largeBody" weight="semiBold">
{person.name}
</Typography>
<Typography variant="regularBody" weight="medium" className={styles.personRole}>
{person.role}
</Typography>
{people.map((person) => (
<div key={person.screenName} className={styles.person}>
<div className={styles.personAvatar}>
<Image
src={person.photo}
alt={person.name}
width={56}
height={56}
placeholder="blur"
layout="intrinsic"
/>
</div>
<div>
<Typography variant="largeBody" weight="semiBold">
{person.name}
</Typography>
<Typography variant="regularBody" weight="medium" className={styles.personRole}>
{person.role}
</Typography>
{person.social === 'twitter' ? (
<a
href={url(person.handle)}
className={classnames(styles.personTwitter, modifier)}
href={`https://twitter.com/${person.screenName}`}
className={styles.personTwitter}
target="_blank"
rel="noopener noreferrer"
>
rel="noopener noreferrer">
<Typography variant="footer" weight="semiBold">
<Icon /> {person.handle}
<SvgTwitter /> {person.screenName}
</Typography>
</a>
</div>
) : (
<a
href={`https://github.com/${person.screenName}`}
className={classnames(styles.personTwitter, styles.personGithub)}
target="_blank"
rel="noopener noreferrer">
<Typography variant="footer" weight="semiBold">
<SvgGithub /> {person.screenName}
</Typography>
</a>
)}
</div>
)
})}
</div>
))}
</div>
<hr className={styles.hr} />
<div className={classnames(styles.footer, styles.modalFooter)}>
@ -210,13 +194,12 @@ const FavyconInfo = ({
className={styles.repo}
href="https://github.com/ruisaraiva19/favycon"
target="_blank"
rel="noopener noreferrer"
>
rel="noopener noreferrer">
GitHub
</a>
. Have feedback? Send it to{' '}
<a className={styles.contact} href="mailto:hello@ruisaraiva.dev">
hello@ruisaraiva.dev
<a className={styles.contact} href="mailto:hello@favycon.app">
hello@favycon.app
</a>
</Typography>
</div>

View File

@ -1,80 +1,43 @@
import Head from 'next/head'
const SITE_URL = 'https://favycon.vercel.app'
const AUTHOR = { name: 'Rui Saraiva', url: 'https://ruisaraiva.dev' }
// The tool credits three people in its About modal, so the structured data says
// the same thing rather than a tidier untruth: Rui as author, the other two as
// contributors, linked where the modal links them.
const CONTRIBUTORS = [
{ name: 'Augusto Lopes', url: 'https://twitter.com/aboutaugusto' },
{ name: 'Miguel Teixeira', url: 'https://github.com/miguellteixeira' },
]
type SEOProps = {
title: string
description: string
}
const SEO = ({ title, description }: SEOProps) => {
const structuredData = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'Favycon',
description,
url: SITE_URL,
applicationCategory: 'DeveloperApplication',
operatingSystem: 'Any',
browserRequirements: 'Requires JavaScript',
image: `${SITE_URL}/share.png`,
license: 'https://opensource.org/licenses/MIT',
// It is genuinely free, and Google wants the price stated to believe it.
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
author: { '@type': 'Person', ...AUTHOR },
contributor: CONTRIBUTORS.map((person) => ({ '@type': 'Person', ...person })),
codeRepository: 'https://github.com/ruisaraiva19/favycon',
}
return (
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<meta name="author" content={AUTHOR.name} />
<link rel="author" href={AUTHOR.url} />
{/* favycon.app has no DNS, so vercel.app is the only hostname this answers on. */}
<link rel="canonical" href={SITE_URL} />
<link rel="apple-touch-icon" sizes="57x57" href="/favicon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/favicon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/favicon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/favicon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/favicon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/favicon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/favicon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/favicon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon-180x180.png" />
<meta name="apple-mobile-web-app-title" content="Favycon" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192x192.png" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-TileImage" content="/favicon-144x144.png" />
<meta name="msapplication-config" content="/browserconfig.xml" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:site_name" content={title} />
<meta property="og:url" content={SITE_URL} />
<meta property="og:image" content={`${SITE_URL}/share.png?v5`} />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }} />
</Head>
)
}
const SEO = ({ title, description }: SEOProps) => (
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<link rel="apple-touch-icon" sizes="57x57" href="/favicon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/favicon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/favicon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/favicon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/favicon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/favicon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/favicon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/favicon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon-180x180.png" />
<meta name="apple-mobile-web-app-title" content="Favycon" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192x192.png" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-TileImage" content="/favicon-144x144.png" />
<meta name="msapplication-config" content="/browserconfig.xml" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:site_name" content={title} />
<meta property="og:url" content="https://favycon.vercel.app" />
<meta property="og:image" content="https://favycon.vercel.app/share.png?v5" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
</Head>
)
export { SEO }

View File

@ -1,22 +0,0 @@
// Drawn from primitives rather than path data so the geometry is exact: a
// circle, the equator and one meridian. Stroked rather than filled, which is
// why the dark-mode rule for .personWebsite sets `stroke` where .personGithub
// sets `fill`.
const SvgWebsite: React.FC<JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>> = (props) => (
<svg
width={18}
height={18}
viewBox="0 0 18 18"
fill="none"
stroke="currentColor"
strokeWidth={1.4}
strokeLinecap="round"
{...props}
>
<circle cx={9} cy={9} r={7} />
<line x1={2} y1={9} x2={16} y2={9} />
<ellipse cx={9} cy={9} rx={3.2} ry={7} />
</svg>
)
export { SvgWebsite }

View File

@ -1,7 +1,7 @@
@import '../../styles/variables.scss';
.root {
font-family: 'Manrope', Arial, Helvetica Neue, Helvetica, sans-serif;
font-family: Manrope, Arial, Helvetica Neue, Helvetica, sans-serif;
}
.h1 {

View File

@ -1,15 +0,0 @@
import { defineConfig } from 'cypress'
export default defineConfig({
viewportWidth: 1280,
viewportHeight: 720,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:3000',
},
})

5
cypress.json Normal file
View File

@ -0,0 +1,5 @@
{
"baseUrl": "http://localhost:3000",
"viewportWidth": 1280,
"viewportHeight": 720
}

View File

@ -1,4 +1,3 @@
/** @type {import('next').NextConfig} */
module.exports = {
poweredByHeader: false,
images: {
@ -6,33 +5,28 @@ module.exports = {
imageSizes: [56, 112, 185, 370, 370, 375, 540, 740, 750, 1080],
},
headers() {
return process.env.NODE_ENV === 'production'
? [
return [
{
source: '/(.*)',
headers: [
{
source: '/(.*)',
headers: [
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Content-Security-Policy',
value: `default-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://hive.splitbee.io https://vitals.vercel-insights.com; script-src 'self' https://cdn.splitbee.io; font-src 'self'; frame-ancestors 'self'`,
},
],
key: 'X-XSS-Protection',
value: '1; mode=block',
},
]
: []
},
eslint: {
ignoreDuringBuilds: true,
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Content-Security-Policy',
value: `default-src 'self' 'unsafe-inline'; connect-src 'self' https://hive.splitbee.io https://vitals.vercel-insights.com; script-src 'self' https://cdn.splitbee.io; font-src 'self' https://fonts.gstatic.com; frame-ancestors 'self'`,
},
],
},
]
},
}

View File

@ -18,7 +18,7 @@
],
"author": {
"name": "Rui Saraiva",
"email": "hello@ruisaraiva.dev",
"email": "ruisaraiva19@gmail.com",
"url": "https://ruisaraiva.dev/"
},
"license": "MIT",
@ -44,101 +44,97 @@
"dependencies": {
"@fiahfy/ico-convert": "0.0.12",
"@splitbee/web": "0.3.0",
"@use-gesture/react": "10.2.16",
"@use-gesture/react": "10.2.0",
"adm-zip": "0.5.9",
"blurhash": "1.1.5",
"blurhash": "1.1.4",
"classnames": "2.3.1",
"multer": "1.4.3",
"next": "12.2.0",
"next": "12.0.6",
"nprogress": "0.2.0",
"prismjs": "1.28.0",
"prop-types": "15.8.1",
"prismjs": "1.25.0",
"prop-types": "15.7.2",
"react": "17.0.2",
"react-clipboard.js": "2.0.16",
"react-dom": "17.0.2",
"react-dropzone": "11.4.2",
"react-modal": "3.15.1",
"react-modal": "3.14.4",
"react-transition-group": "4.4.2",
"react-use": "17.4.0"
"react-use": "17.3.1"
},
"devDependencies": {
"@babel/core": "7.18.6",
"@commitlint/cli": "17.0.3",
"@commitlint/config-conventional": "17.0.3",
"@storybook/addon-a11y": "6.5.9",
"@storybook/addon-actions": "6.5.9",
"@babel/core": "7.16.0",
"@commitlint/cli": "15.0.0",
"@commitlint/config-conventional": "15.0.0",
"@storybook/addon-a11y": "6.4.7",
"@storybook/addon-actions": "6.4.7",
"@storybook/addon-console": "1.2.3",
"@storybook/addon-docs": "6.5.9",
"@storybook/addon-essentials": "6.5.9",
"@storybook/addon-links": "6.5.9",
"@storybook/addon-docs": "6.4.7",
"@storybook/addon-essentials": "6.4.7",
"@storybook/addon-links": "6.4.7",
"@storybook/addon-postcss": "2.0.0",
"@storybook/addons": "6.5.9",
"@storybook/api": "6.5.9",
"@storybook/builder-webpack5": "6.5.9",
"@storybook/client-api": "6.5.9",
"@storybook/components": "6.5.9",
"@storybook/core-events": "6.5.9",
"@storybook/manager-webpack5": "6.5.9",
"@storybook/node-logger": "6.5.9",
"@storybook/react": "6.5.9",
"@storybook/theming": "6.5.9",
"@storybook/addons": "6.4.7",
"@storybook/api": "6.4.7",
"@storybook/builder-webpack5": "6.4.7",
"@storybook/client-api": "6.4.7",
"@storybook/components": "6.4.7",
"@storybook/core-events": "6.4.7",
"@storybook/manager-webpack5": "6.4.7",
"@storybook/node-logger": "6.4.7",
"@storybook/react": "6.4.7",
"@storybook/theming": "6.4.7",
"@types/adm-zip": "0.4.34",
"@types/multer": "1.4.7",
"@types/node": "16.11.11",
"@types/nprogress": "0.2.0",
"@types/prismjs": "1.26.0",
"@types/prismjs": "1.16.6",
"@types/react": "17.0.37",
"@types/react-modal": "3.13.1",
"@types/react-transition-group": "4.4.5",
"@types/react-transition-group": "4.4.4",
"@types/sharp": "0.29.4",
"@typescript-eslint/eslint-plugin": "5.30.5",
"@typescript-eslint/parser": "5.30.5",
"@typescript-eslint/eslint-plugin": "5.5.0",
"@typescript-eslint/parser": "5.5.0",
"all-contributors-cli": "6.20.0",
"babel-loader": "8.2.5",
"babel-loader": "8.2.3",
"commitizen": "4.2.4",
"cypress": "10.3.0",
"cypress": "9.1.1",
"cypress-file-upload": "5.0.8",
"cz-conventional-changelog": "3.3.0",
"dotenv": "16.0.1",
"eslint": "8.19.0",
"eslint-config-next": "12.2.0",
"eslint-config-prettier": "8.5.0",
"dotenv": "10.0.0",
"eslint": "8.4.0",
"eslint-config-next": "12.0.6",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-storybook": "0.5.13",
"husky": "8.0.1",
"lint-staged": "13.0.3",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-storybook": "0.5.3",
"husky": "7.0.4",
"lint-staged": "12.1.2",
"npm-run-all": "4.1.5",
"postcss": "8.4.14",
"postcss": "8.4.4",
"postcss-100vh-fix": "1.0.2",
"postcss-flexbugs-fixes": "5.0.2",
"postcss-preset-env": "7.7.2",
"postcss-scss": "4.0.4",
"prettier": "2.7.1",
"react-refresh": "0.14.0",
"require-from-string": "2.0.2",
"sass": "1.53.0",
"sass-loader": "13.0.2",
"semantic-release": "19.0.3",
"postcss-preset-env": "7.0.1",
"postcss-scss": "4.0.2",
"prettier": "2.3.2",
"react-refresh": "0.11.0",
"sass": "1.44.0",
"sass-loader": "12.3.0",
"semantic-release": "18.0.1",
"start-server-and-test": "1.14.0",
"storybook-addon-next-router": "4.0.0",
"storybook-dark-mode": "1.1.0",
"stylelint": "14.9.1",
"stylelint-config-css-modules": "4.1.0",
"storybook-addon-next-router": "3.1.0",
"storybook-dark-mode": "1.0.8",
"stylelint": "14.1.0",
"stylelint-config-css-modules": "2.3.0",
"stylelint-config-prettier": "9.0.3",
"stylelint-config-recess-order": "3.0.0",
"stylelint-config-standard": "26.0.0",
"stylelint-config-standard": "24.0.0",
"stylelint-prettier": "2.0.0",
"stylelint-scss": "4.2.0",
"typescript": "4.7.4",
"webpack": "5.73.0"
"stylelint-scss": "4.0.0",
"typescript": "4.4.4",
"webpack": "5.64.4"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"resolutions": {
"sharp": "^0.28.0"
}
}

View File

@ -4,13 +4,16 @@ import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
render() {
return (
<Html lang="en" className="preload">
<Head />
<body>
<script src="/js/noflash.js?v1" />
<Html lang="en">
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
</Head>
<body className="preload">
<script src="/js/noflash.js" />
<Main />
<NextScript />
<script src="/js/onload.js?v1" />
<script src="/js/onload.js" />
</body>
</Html>
)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,5 +1,5 @@
;(function () {
window.addEventListener('load', function () {
document.documentElement.classList.remove('preload')
document.body.classList.remove('preload')
})
})()

View File

@ -1,48 +0,0 @@
# Favycon
> A free online tool that turns one image into a complete set of favicons, in
> every size and format a site needs, as a downloadable zip.
Drag and drop an image and Favycon returns `favicons.zip` plus the markup to
paste into your `<head>`. Nothing is stored: the image is converted in the
request and the zip is streamed straight back.
Site: https://favycon.vercel.app
## Input
- Accepted formats: JPEG, PNG, SVG.
- Minimum dimensions: 310x310. SVG input is exempt, since it is rescaled from
vector.
- With the PWA option enabled the minimum is 512x512, again except for SVG.
## What the zip contains
- `icons/favicon-<size>.png` at 16x16, 32x32, 57x57, 60x60, 70x70, 72x72,
76x76, 96x96, 114x114, 120x120, 144x144, 150x150, 152x152, 180x180, 192x192
and 310x310.
- `icons/favicon.ico`, rendered from a 256x256 master.
- `icons/favicon.svg`, only when the uploaded image was itself an SVG.
- `icons/browserconfig.xml` for IE10+ tiles.
- `icons/manifest.json`, only when the PWA option is on. That option also adds
128x128, 384x384 and 512x512 PNGs.
- `readme.txt` with the HTML to add, written to match the options chosen.
## Facts
- Author: Rui Saraiva (https://ruisaraiva.dev)
- Contributors: Augusto Lopes (product design), Miguel Teixeira
- Built in 2020
- Licence: MIT
- Source: https://github.com/ruisaraiva19/favycon
- Price: free, no account, no upload limit published
- Contact: hello@ruisaraiva.dev
## Notes
- This is a single-page tool. There is no blog, docs site or API documentation
to crawl beyond this file and the repository.
- There is a public HTTP endpoint at `POST /api/favycon`. It takes a multipart
body with an `icon` file field and an optional `pwa` field set to `1`, and
responds with `application/zip`. It is what the page itself calls; it is not
a documented or versioned public API.

View File

@ -1,8 +1,2 @@
User-Agent: *
Allow: /
# Nothing is disallowed. /api/favycon is POST-only and answers a GET with 405,
# which is a clearer signal to a crawler than a rule it has to fetch robots.txt
# to learn. A plain-text description of the tool lives at /llms.txt
Sitemap: https://favycon.vercel.app/sitemap.xml
Allow: /

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://favycon.vercel.app</loc>
</url>
</urlset>

View File

@ -1,48 +1,48 @@
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 400;
src: url('/fonts/manrope-v12.woff2') format('woff2');
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 500;
src: url('/fonts/manrope-v12.woff2') format('woff2');
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 600;
src: url('/fonts/manrope-v12.woff2') format('woff2');
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 700;
src: url('/fonts/manrope-v12.woff2') format('woff2');
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 800;
src: url('/fonts/manrope-v12.woff2') format('woff2');
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;

View File

@ -11,14 +11,14 @@ body {
/* Disables pull-to-refresh but allows overscroll glow effects. */
overscroll-behavior-y: contain;
font-family: 'Manrope', Arial, 'Helvetica Neue', Helvetica, sans-serif;
font-family: Manrope, Arial, 'Helvetica Neue', Helvetica, sans-serif;
/* https://gist.github.com/hsleonis/55712b0eafc9b25f1944 */
font-size: 100%;
font-variant-ligatures: none;
text-shadow: rgba($black, 0.01) 0 0 1px;
transition: background 0.2s ease;
text-rendering: optimizelegibility;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;

6455
yarn.lock

File diff suppressed because it is too large Load Diff