mirror of
https://github.com/ruisaraiva19/favycon.git
synced 2026-09-12 19:51:07 +05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60d571fe11 | ||
|
|
135c260189 | ||
|
|
74275a31a7 | ||
|
|
78c9508195 | ||
|
|
3588ab0506 | ||
|
|
0705e4e404 | ||
|
|
a2d70d783f | ||
|
|
37a1af5f35 | ||
|
|
04a34e04c2 | ||
|
|
a82367ed0c |
4
.github/workflows/commitlint.yml
vendored
4
.github/workflows/commitlint.yml
vendored
@ -8,8 +8,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Lint commit messages
|
- name: Lint commit messages
|
||||||
uses: wagoid/commitlint-github-action@v2
|
uses: wagoid/commitlint-github-action@v6
|
||||||
|
|||||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@ -8,9 +8,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
|
|||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@ -11,22 +11,22 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
- name: Cache Next.js build cache
|
- name: Cache Next.js build cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/.next/cache
|
path: ${{ github.workspace }}/.next/cache
|
||||||
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
|
||||||
- name: Build static assets
|
- name: Build static assets
|
||||||
run: yarn build
|
run: yarn build
|
||||||
- name: Semantic Release
|
- name: Semantic Release
|
||||||
uses: cycjimmy/semantic-release-action@v2
|
uses: cycjimmy/semantic-release-action@v4
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
12
.github/workflows/tests.yml
vendored
12
.github/workflows/tests.yml
vendored
@ -7,10 +7,10 @@ jobs:
|
|||||||
name: Run Cypress
|
name: Run Cypress
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
@ -19,7 +19,7 @@ jobs:
|
|||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Cache Next.js build cache
|
- name: Cache Next.js build cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/.next/cache
|
path: ${{ github.workspace }}/.next/cache
|
||||||
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
|
||||||
@ -28,7 +28,7 @@ jobs:
|
|||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
- name: Run Cypress
|
- name: Run Cypress
|
||||||
uses: cypress-io/github-action@v2
|
uses: cypress-io/github-action@v6
|
||||||
with:
|
with:
|
||||||
start: npm start
|
start: npm start
|
||||||
wait-on: http://localhost:3000
|
wait-on: http://localhost:3000
|
||||||
@ -36,13 +36,13 @@ jobs:
|
|||||||
# store videos and any screenshots
|
# store videos and any screenshots
|
||||||
# NOTE: screenshots will be generated only if E2E test failed
|
# NOTE: screenshots will be generated only if E2E test failed
|
||||||
# thus we store screenshots only on failures
|
# thus we store screenshots only on failures
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v4
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: cypress-screenshots
|
name: cypress-screenshots
|
||||||
path: cypress/screenshots
|
path: cypress/screenshots
|
||||||
# Test run video was always captured, so this action uses "always()" condition
|
# Test run video was always captured, so this action uses "always()" condition
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: cypress-videos
|
name: cypress-videos
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"jsxBracketSameLine": true,
|
|
||||||
"semi": false
|
"semi": false
|
||||||
}
|
}
|
||||||
|
|||||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -3,11 +3,11 @@
|
|||||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||||
"eslint.run": "onType",
|
"eslint.run": "onType",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true,
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.fixAll.stylelint": true
|
"source.fixAll.stylelint": "explicit"
|
||||||
},
|
},
|
||||||
"css.validate": false,
|
"css.validate": false,
|
||||||
"less.validate": false,
|
"less.validate": false,
|
||||||
"scss.validate": false,
|
"scss.validate": false,
|
||||||
"cssModules.camelCase": true
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
.base {
|
.base {
|
||||||
padding: 5px 12px;
|
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;
|
font-size: $font-size-title;
|
||||||
line-height: $line-height-title;
|
line-height: $line-height-title;
|
||||||
color: $black;
|
color: $black;
|
||||||
|
|||||||
@ -208,6 +208,7 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
onDrop,
|
onDrop,
|
||||||
accept: Object.keys(ACCEPT_MIME_TYPES),
|
accept: Object.keys(ACCEPT_MIME_TYPES),
|
||||||
maxSize: ONE_MB,
|
maxSize: ONE_MB,
|
||||||
@ -230,13 +231,15 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
className={classnames(styles.container, {
|
className={classnames(styles.container, {
|
||||||
[styles.loading]: isLoading,
|
[styles.loading]: isLoading,
|
||||||
[styles.transparent]: !image,
|
[styles.transparent]: !image,
|
||||||
})}>
|
})}
|
||||||
|
>
|
||||||
{!image && !zipData && (
|
{!image && !zipData && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={styles.dropZoneWrapper}
|
className={styles.dropZoneWrapper}
|
||||||
onMouseEnter={() => setIsHover(true)}
|
onMouseEnter={() => setIsHover(true)}
|
||||||
onMouseLeave={() => setIsHover(false)}>
|
onMouseLeave={() => setIsHover(false)}
|
||||||
|
>
|
||||||
<SvgDropZone className={classnames(styles.dashed, { [styles.dragActive]: isDragActive })} />
|
<SvgDropZone className={classnames(styles.dashed, { [styles.dragActive]: isDragActive })} />
|
||||||
<div {...getRootProps()} className={styles.dropZone} data-cy="drag-and-drop">
|
<div {...getRootProps()} className={styles.dropZone} data-cy="drag-and-drop">
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
@ -249,7 +252,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
className={classnames(styles.imageUploadText, styles.mobileUploadText, {
|
className={classnames(styles.imageUploadText, styles.mobileUploadText, {
|
||||||
[styles.dragActive]: isDragActive,
|
[styles.dragActive]: isDragActive,
|
||||||
})}
|
})}
|
||||||
data-cy="drag-and-drop-text">
|
data-cy="drag-and-drop-text"
|
||||||
|
>
|
||||||
<span>
|
<span>
|
||||||
Generate all the sizes for your
|
Generate all the sizes for your
|
||||||
<br />
|
<br />
|
||||||
@ -262,7 +266,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
className={classnames(styles.imageUploadText, styles.desktopUploadText, {
|
className={classnames(styles.imageUploadText, styles.desktopUploadText, {
|
||||||
[styles.dragActive]: isDragActive,
|
[styles.dragActive]: isDragActive,
|
||||||
})}
|
})}
|
||||||
data-cy="drag-and-drop-text">
|
data-cy="drag-and-drop-text"
|
||||||
|
>
|
||||||
<CSSTransition in={!isDragActive} timeout={200} classNames="collapse" unmountOnExit>
|
<CSSTransition in={!isDragActive} timeout={200} classNames="collapse" unmountOnExit>
|
||||||
<span>Drag & </span>
|
<span>Drag & </span>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
@ -313,7 +318,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
weight="semiBold"
|
weight="semiBold"
|
||||||
color="gray"
|
color="gray"
|
||||||
tag="div"
|
tag="div"
|
||||||
className={styles.filenameWrapper}>
|
className={styles.filenameWrapper}
|
||||||
|
>
|
||||||
<span className={styles.filename} data-cy="preview-filename">
|
<span className={styles.filename} data-cy="preview-filename">
|
||||||
{imageData.name}
|
{imageData.name}
|
||||||
{imageData.extension}
|
{imageData.extension}
|
||||||
@ -357,7 +363,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
id="pwa"
|
id="pwa"
|
||||||
disabled={PWADisabled}
|
disabled={PWADisabled}
|
||||||
onChange={togglePwa}
|
onChange={togglePwa}
|
||||||
data-cy="preview-pwa-compatible">
|
data-cy="preview-pwa-compatible"
|
||||||
|
>
|
||||||
<Typography variant="regularBody" weight="semiBold" muted={PWADisabled}>
|
<Typography variant="regularBody" weight="semiBold" muted={PWADisabled}>
|
||||||
PWA compatible
|
PWA compatible
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -373,7 +380,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
id="dark"
|
id="dark"
|
||||||
disabled
|
disabled
|
||||||
onChange={toggleDarkMode}
|
onChange={toggleDarkMode}
|
||||||
data-cy="preview-dark-mode-version">
|
data-cy="preview-dark-mode-version"
|
||||||
|
>
|
||||||
<Typography variant="regularBody" weight="semiBold" muted>
|
<Typography variant="regularBody" weight="semiBold" muted>
|
||||||
Dark Mode version
|
Dark Mode version
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -392,14 +400,18 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
splitbee.track('Re-upload')
|
splitbee.track('Re-upload')
|
||||||
resetImage()
|
resetImage()
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Re-upload
|
Re-upload
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="white"
|
color="white"
|
||||||
background="bgLink"
|
background="bgLink"
|
||||||
className={styles.imageGenerate}
|
className={styles.imageGenerate}
|
||||||
onClick={() => generateFavicon(image)}>
|
onClick={() => {
|
||||||
|
generateFavicon(image)
|
||||||
|
}}
|
||||||
|
>
|
||||||
Generate Favicon
|
Generate Favicon
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -442,7 +454,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
className={styles.showCode}
|
className={styles.showCode}
|
||||||
variant="regularTransparent"
|
variant="regularTransparent"
|
||||||
color="gray"
|
color="gray"
|
||||||
onClick={() => setIsModalOpen(true)}>
|
onClick={() => setIsModalOpen(true)}
|
||||||
|
>
|
||||||
Show
|
Show
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -455,14 +468,16 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
splitbee.track('Make a new one')
|
splitbee.track('Make a new one')
|
||||||
resetImage()
|
resetImage()
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
Make a new one
|
Make a new one
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="white"
|
color="white"
|
||||||
background="bgGreen"
|
background="bgGreen"
|
||||||
className={styles.imageGenerate}
|
className={styles.imageGenerate}
|
||||||
onClick={() => onDownload(zipData)}>
|
onClick={() => onDownload(zipData)}
|
||||||
|
>
|
||||||
Download Favicon
|
Download Favicon
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -475,7 +490,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
|
|||||||
className={styles.content}
|
className={styles.content}
|
||||||
overlayClassName={styles.overlay}
|
overlayClassName={styles.overlay}
|
||||||
closeTimeoutMS={200}
|
closeTimeoutMS={200}
|
||||||
contentLabel="Code generated">
|
contentLabel="Code generated"
|
||||||
|
>
|
||||||
<div className={styles.modalContainer} {...bind()}>
|
<div className={styles.modalContainer} {...bind()}>
|
||||||
<div className={classnames(styles.modalHeader, styles.modalHeaderMobile)}>
|
<div className={classnames(styles.modalHeader, styles.modalHeaderMobile)}>
|
||||||
<Typography variant="extraLargeTitle" weight="extraBold" color="white" colorImmutable>
|
<Typography variant="extraLargeTitle" weight="extraBold" color="white" colorImmutable>
|
||||||
|
|||||||
@ -38,7 +38,8 @@ const FavyconError = ({ error, clearError }: FavyconErrorProps) => {
|
|||||||
variant="regularBody"
|
variant="regularBody"
|
||||||
color="white"
|
color="white"
|
||||||
colorImmutable={true}
|
colorImmutable={true}
|
||||||
weight="semiBold">
|
weight="semiBold"
|
||||||
|
>
|
||||||
{error}
|
{error}
|
||||||
</Typography>
|
</Typography>
|
||||||
<svg height={20} width={20}>
|
<svg height={20} width={20}>
|
||||||
|
|||||||
@ -290,7 +290,11 @@
|
|||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.personGithub {
|
// 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 {
|
||||||
color: $black;
|
color: $black;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -316,7 +320,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.dark) .personTwitter.personGithub p {
|
:global(.dark) .personTwitter.personGithub p,
|
||||||
|
:global(.dark) .personTwitter.personWebsite p {
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -328,6 +333,13 @@
|
|||||||
fill: $white;
|
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 {
|
.modalFooter {
|
||||||
padding-bottom: 48px;
|
padding-bottom: 48px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { Typography } from 'components/typography'
|
|||||||
import { ToolTitle } from 'components/tool-title'
|
import { ToolTitle } from 'components/tool-title'
|
||||||
import { SvgTwitter } from 'components/svgs/svg-twitter'
|
import { SvgTwitter } from 'components/svgs/svg-twitter'
|
||||||
import { SvgGithub } from 'components/svgs/svg-github'
|
import { SvgGithub } from 'components/svgs/svg-github'
|
||||||
|
import { SvgWebsite } from 'components/svgs/svg-website'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import { SvgFavycon } from 'components/svgs/svg-favycon'
|
import { SvgFavycon } from 'components/svgs/svg-favycon'
|
||||||
@ -19,26 +20,47 @@ import miguelTeixeira from '../../public/images/people/miguel-teixeira.png'
|
|||||||
|
|
||||||
const Modal = dynamic(() => import('react-modal'))
|
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 = [
|
const people = [
|
||||||
{
|
{
|
||||||
screenName: 'aboutaugusto',
|
handle: 'aboutaugusto',
|
||||||
name: 'Augusto Lopes',
|
name: 'Augusto Lopes',
|
||||||
role: 'Product Designer',
|
role: 'Product Designer',
|
||||||
social: 'twitter',
|
social: 'twitter' as const,
|
||||||
photo: augustoLopes,
|
photo: augustoLopes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
screenName: 'ruisaraiva19',
|
handle: 'ruisaraiva.dev',
|
||||||
name: 'Rui Saraiva',
|
name: 'Rui Saraiva',
|
||||||
role: 'Full-Stack Developer',
|
role: 'Principal Engineer',
|
||||||
social: 'twitter',
|
social: 'website' as const,
|
||||||
photo: ruiSaraiva,
|
photo: ruiSaraiva,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
screenName: 'miguellteixeira',
|
handle: 'miguellteixeira',
|
||||||
name: 'Miguel Teixeira',
|
name: 'Miguel Teixeira',
|
||||||
role: 'Full-Stack Developer',
|
role: 'Full-Stack Developer',
|
||||||
social: 'github',
|
social: 'github' as const,
|
||||||
photo: miguelTeixeira,
|
photo: miguelTeixeira,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -102,7 +124,8 @@ const FavyconInfo = ({
|
|||||||
className={styles.content}
|
className={styles.content}
|
||||||
overlayClassName={styles.overlay}
|
overlayClassName={styles.overlay}
|
||||||
closeTimeoutMS={200}
|
closeTimeoutMS={200}
|
||||||
contentLabel="About Us">
|
contentLabel="About Us"
|
||||||
|
>
|
||||||
<div className={styles.modalContainer} {...bind()}>
|
<div className={styles.modalContainer} {...bind()}>
|
||||||
<div className={styles.modalHeaderMobile}>
|
<div className={styles.modalHeaderMobile}>
|
||||||
<div className={styles.modalHeaderTop}>
|
<div className={styles.modalHeaderTop}>
|
||||||
@ -137,54 +160,47 @@ const FavyconInfo = ({
|
|||||||
Behind the curtains
|
Behind the curtains
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="largeBody" weight="medium" className={styles.subTitle}>
|
<Typography variant="largeBody" weight="medium" className={styles.subTitle}>
|
||||||
For new updates be sure to follow these guys on Twitter.
|
For new updates be sure to follow these guys.
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<hr className={styles.hr} />
|
<hr className={styles.hr} />
|
||||||
<div className={styles.people}>
|
<div className={styles.people}>
|
||||||
{people.map((person) => (
|
{people.map((person) => {
|
||||||
<div key={person.screenName} className={styles.person}>
|
const { Icon, url, modifier } = socials[person.social]
|
||||||
<div className={styles.personAvatar}>
|
|
||||||
<Image
|
return (
|
||||||
src={person.photo}
|
<div key={person.handle} className={styles.person}>
|
||||||
alt={person.name}
|
<div className={styles.personAvatar}>
|
||||||
width={56}
|
<Image
|
||||||
height={56}
|
src={person.photo}
|
||||||
placeholder="blur"
|
alt={person.name}
|
||||||
layout="intrinsic"
|
width={56}
|
||||||
/>
|
height={56}
|
||||||
</div>
|
placeholder="blur"
|
||||||
<div>
|
layout="intrinsic"
|
||||||
<Typography variant="largeBody" weight="semiBold">
|
/>
|
||||||
{person.name}
|
</div>
|
||||||
</Typography>
|
<div>
|
||||||
<Typography variant="regularBody" weight="medium" className={styles.personRole}>
|
<Typography variant="largeBody" weight="semiBold">
|
||||||
{person.role}
|
{person.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
{person.social === 'twitter' ? (
|
<Typography variant="regularBody" weight="medium" className={styles.personRole}>
|
||||||
|
{person.role}
|
||||||
|
</Typography>
|
||||||
<a
|
<a
|
||||||
href={`https://twitter.com/${person.screenName}`}
|
href={url(person.handle)}
|
||||||
className={styles.personTwitter}
|
className={classnames(styles.personTwitter, modifier)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
<Typography variant="footer" weight="semiBold">
|
<Typography variant="footer" weight="semiBold">
|
||||||
<SvgTwitter /> {person.screenName}
|
<Icon /> {person.handle}
|
||||||
</Typography>
|
</Typography>
|
||||||
</a>
|
</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>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<hr className={styles.hr} />
|
<hr className={styles.hr} />
|
||||||
<div className={classnames(styles.footer, styles.modalFooter)}>
|
<div className={classnames(styles.footer, styles.modalFooter)}>
|
||||||
@ -194,12 +210,13 @@ const FavyconInfo = ({
|
|||||||
className={styles.repo}
|
className={styles.repo}
|
||||||
href="https://github.com/ruisaraiva19/favycon"
|
href="https://github.com/ruisaraiva19/favycon"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
GitHub
|
GitHub
|
||||||
</a>
|
</a>
|
||||||
. Have feedback? Send it to{' '}
|
. Have feedback? Send it to{' '}
|
||||||
<a className={styles.contact} href="mailto:hello@favycon.app">
|
<a className={styles.contact} href="mailto:hello@ruisaraiva.dev">
|
||||||
hello@favycon.app
|
hello@ruisaraiva.dev
|
||||||
</a>
|
</a>
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,43 +1,80 @@
|
|||||||
import Head from 'next/head'
|
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 = {
|
type SEOProps = {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SEO = ({ title, description }: SEOProps) => (
|
const SEO = ({ title, description }: SEOProps) => {
|
||||||
<Head>
|
const structuredData = {
|
||||||
<title>{title}</title>
|
'@context': 'https://schema.org',
|
||||||
<meta name="description" content={description} />
|
'@type': 'SoftwareApplication',
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/favicon-57x57.png" />
|
name: 'Favycon',
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/favicon-60x60.png" />
|
description,
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/favicon-72x72.png" />
|
url: SITE_URL,
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="/favicon-76x76.png" />
|
applicationCategory: 'DeveloperApplication',
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="/favicon-114x114.png" />
|
operatingSystem: 'Any',
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="/favicon-120x120.png" />
|
browserRequirements: 'Requires JavaScript',
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/favicon-144x144.png" />
|
image: `${SITE_URL}/share.png`,
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/favicon-152x152.png" />
|
license: 'https://opensource.org/licenses/MIT',
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon-180x180.png" />
|
// It is genuinely free, and Google wants the price stated to believe it.
|
||||||
<meta name="apple-mobile-web-app-title" content="Favycon" />
|
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
author: { '@type': 'Person', ...AUTHOR },
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
contributor: CONTRIBUTORS.map((person) => ({ '@type': 'Person', ...person })),
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
codeRepository: 'https://github.com/ruisaraiva19/favycon',
|
||||||
<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" />
|
return (
|
||||||
<meta name="msapplication-TileImage" content="/favicon-144x144.png" />
|
<Head>
|
||||||
<meta name="msapplication-config" content="/browserconfig.xml" />
|
<title>{title}</title>
|
||||||
<meta property="og:title" content={title} />
|
<meta name="description" content={description} />
|
||||||
<meta property="og:description" content={description} />
|
<meta name="author" content={AUTHOR.name} />
|
||||||
<meta property="og:type" content="website" />
|
<link rel="author" href={AUTHOR.url} />
|
||||||
<meta property="og:site_name" content={title} />
|
{/* favycon.app has no DNS, so vercel.app is the only hostname this answers on. */}
|
||||||
<meta property="og:url" content="https://favycon.vercel.app" />
|
<link rel="canonical" href={SITE_URL} />
|
||||||
<meta property="og:image" content="https://favycon.vercel.app/share.png?v5" />
|
<link rel="apple-touch-icon" sizes="57x57" href="/favicon-57x57.png" />
|
||||||
<meta property="og:image:type" content="image/png" />
|
<link rel="apple-touch-icon" sizes="60x60" href="/favicon-60x60.png" />
|
||||||
<meta property="og:image:width" content="1200" />
|
<link rel="apple-touch-icon" sizes="72x72" href="/favicon-72x72.png" />
|
||||||
<meta property="og:image:height" content="630" />
|
<link rel="apple-touch-icon" sizes="76x76" href="/favicon-76x76.png" />
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<link rel="apple-touch-icon" sizes="114x114" href="/favicon-114x114.png" />
|
||||||
</Head>
|
<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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export { SEO }
|
export { SEO }
|
||||||
|
|||||||
22
components/svgs/svg-website.tsx
Normal file
22
components/svgs/svg-website.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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 }
|
||||||
@ -1,7 +1,7 @@
|
|||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
font-family: Manrope, Arial, Helvetica Neue, Helvetica, sans-serif;
|
font-family: 'Manrope', Arial, Helvetica Neue, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h1 {
|
.h1 {
|
||||||
|
|||||||
15
cypress.config.ts
Normal file
15
cypress.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"baseUrl": "http://localhost:3000",
|
|
||||||
"viewportWidth": 1280,
|
|
||||||
"viewportHeight": 720
|
|
||||||
}
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
poweredByHeader: false,
|
poweredByHeader: false,
|
||||||
images: {
|
images: {
|
||||||
@ -5,28 +6,33 @@ module.exports = {
|
|||||||
imageSizes: [56, 112, 185, 370, 370, 375, 540, 740, 750, 1080],
|
imageSizes: [56, 112, 185, 370, 370, 375, 540, 740, 750, 1080],
|
||||||
},
|
},
|
||||||
headers() {
|
headers() {
|
||||||
return [
|
return process.env.NODE_ENV === 'production'
|
||||||
{
|
? [
|
||||||
source: '/(.*)',
|
|
||||||
headers: [
|
|
||||||
{
|
{
|
||||||
key: 'X-XSS-Protection',
|
source: '/(.*)',
|
||||||
value: '1; mode=block',
|
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-Frame-Options',
|
: []
|
||||||
value: 'SAMEORIGIN',
|
},
|
||||||
},
|
eslint: {
|
||||||
{
|
ignoreDuringBuilds: true,
|
||||||
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'`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
116
package.json
116
package.json
@ -18,7 +18,7 @@
|
|||||||
],
|
],
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Rui Saraiva",
|
"name": "Rui Saraiva",
|
||||||
"email": "ruisaraiva19@gmail.com",
|
"email": "hello@ruisaraiva.dev",
|
||||||
"url": "https://ruisaraiva.dev/"
|
"url": "https://ruisaraiva.dev/"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -44,97 +44,101 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fiahfy/ico-convert": "0.0.12",
|
"@fiahfy/ico-convert": "0.0.12",
|
||||||
"@splitbee/web": "0.3.0",
|
"@splitbee/web": "0.3.0",
|
||||||
"@use-gesture/react": "10.2.0",
|
"@use-gesture/react": "10.2.16",
|
||||||
"adm-zip": "0.5.9",
|
"adm-zip": "0.5.9",
|
||||||
"blurhash": "1.1.4",
|
"blurhash": "1.1.5",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
"multer": "1.4.3",
|
"multer": "1.4.3",
|
||||||
"next": "12.0.6",
|
"next": "12.2.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"prismjs": "1.25.0",
|
"prismjs": "1.28.0",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.8.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-clipboard.js": "2.0.16",
|
"react-clipboard.js": "2.0.16",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-dropzone": "11.4.2",
|
"react-dropzone": "11.4.2",
|
||||||
"react-modal": "3.14.4",
|
"react-modal": "3.15.1",
|
||||||
"react-transition-group": "4.4.2",
|
"react-transition-group": "4.4.2",
|
||||||
"react-use": "17.3.1"
|
"react-use": "17.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.16.0",
|
"@babel/core": "7.18.6",
|
||||||
"@commitlint/cli": "15.0.0",
|
"@commitlint/cli": "17.0.3",
|
||||||
"@commitlint/config-conventional": "15.0.0",
|
"@commitlint/config-conventional": "17.0.3",
|
||||||
"@storybook/addon-a11y": "6.4.7",
|
"@storybook/addon-a11y": "6.5.9",
|
||||||
"@storybook/addon-actions": "6.4.7",
|
"@storybook/addon-actions": "6.5.9",
|
||||||
"@storybook/addon-console": "1.2.3",
|
"@storybook/addon-console": "1.2.3",
|
||||||
"@storybook/addon-docs": "6.4.7",
|
"@storybook/addon-docs": "6.5.9",
|
||||||
"@storybook/addon-essentials": "6.4.7",
|
"@storybook/addon-essentials": "6.5.9",
|
||||||
"@storybook/addon-links": "6.4.7",
|
"@storybook/addon-links": "6.5.9",
|
||||||
"@storybook/addon-postcss": "2.0.0",
|
"@storybook/addon-postcss": "2.0.0",
|
||||||
"@storybook/addons": "6.4.7",
|
"@storybook/addons": "6.5.9",
|
||||||
"@storybook/api": "6.4.7",
|
"@storybook/api": "6.5.9",
|
||||||
"@storybook/builder-webpack5": "6.4.7",
|
"@storybook/builder-webpack5": "6.5.9",
|
||||||
"@storybook/client-api": "6.4.7",
|
"@storybook/client-api": "6.5.9",
|
||||||
"@storybook/components": "6.4.7",
|
"@storybook/components": "6.5.9",
|
||||||
"@storybook/core-events": "6.4.7",
|
"@storybook/core-events": "6.5.9",
|
||||||
"@storybook/manager-webpack5": "6.4.7",
|
"@storybook/manager-webpack5": "6.5.9",
|
||||||
"@storybook/node-logger": "6.4.7",
|
"@storybook/node-logger": "6.5.9",
|
||||||
"@storybook/react": "6.4.7",
|
"@storybook/react": "6.5.9",
|
||||||
"@storybook/theming": "6.4.7",
|
"@storybook/theming": "6.5.9",
|
||||||
"@types/adm-zip": "0.4.34",
|
"@types/adm-zip": "0.4.34",
|
||||||
"@types/multer": "1.4.7",
|
"@types/multer": "1.4.7",
|
||||||
"@types/node": "16.11.11",
|
"@types/node": "16.11.11",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
"@types/prismjs": "1.16.6",
|
"@types/prismjs": "1.26.0",
|
||||||
"@types/react": "17.0.37",
|
"@types/react": "17.0.37",
|
||||||
"@types/react-modal": "3.13.1",
|
"@types/react-modal": "3.13.1",
|
||||||
"@types/react-transition-group": "4.4.4",
|
"@types/react-transition-group": "4.4.5",
|
||||||
"@types/sharp": "0.29.4",
|
"@types/sharp": "0.29.4",
|
||||||
"@typescript-eslint/eslint-plugin": "5.5.0",
|
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||||
"@typescript-eslint/parser": "5.5.0",
|
"@typescript-eslint/parser": "5.30.5",
|
||||||
"all-contributors-cli": "6.20.0",
|
"all-contributors-cli": "6.20.0",
|
||||||
"babel-loader": "8.2.3",
|
"babel-loader": "8.2.5",
|
||||||
"commitizen": "4.2.4",
|
"commitizen": "4.2.4",
|
||||||
"cypress": "9.1.1",
|
"cypress": "10.3.0",
|
||||||
"cypress-file-upload": "5.0.8",
|
"cypress-file-upload": "5.0.8",
|
||||||
"cz-conventional-changelog": "3.3.0",
|
"cz-conventional-changelog": "3.3.0",
|
||||||
"dotenv": "10.0.0",
|
"dotenv": "16.0.1",
|
||||||
"eslint": "8.4.0",
|
"eslint": "8.19.0",
|
||||||
"eslint-config-next": "12.0.6",
|
"eslint-config-next": "12.2.0",
|
||||||
"eslint-config-prettier": "8.3.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-plugin-cypress": "2.12.1",
|
"eslint-plugin-cypress": "2.12.1",
|
||||||
"eslint-plugin-prettier": "4.0.0",
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"eslint-plugin-storybook": "0.5.3",
|
"eslint-plugin-storybook": "0.5.13",
|
||||||
"husky": "7.0.4",
|
"husky": "8.0.1",
|
||||||
"lint-staged": "12.1.2",
|
"lint-staged": "13.0.3",
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.1.5",
|
||||||
"postcss": "8.4.4",
|
"postcss": "8.4.14",
|
||||||
"postcss-100vh-fix": "1.0.2",
|
"postcss-100vh-fix": "1.0.2",
|
||||||
"postcss-flexbugs-fixes": "5.0.2",
|
"postcss-flexbugs-fixes": "5.0.2",
|
||||||
"postcss-preset-env": "7.0.1",
|
"postcss-preset-env": "7.7.2",
|
||||||
"postcss-scss": "4.0.2",
|
"postcss-scss": "4.0.4",
|
||||||
"prettier": "2.3.2",
|
"prettier": "2.7.1",
|
||||||
"react-refresh": "0.11.0",
|
"react-refresh": "0.14.0",
|
||||||
"sass": "1.44.0",
|
"require-from-string": "2.0.2",
|
||||||
"sass-loader": "12.3.0",
|
"sass": "1.53.0",
|
||||||
"semantic-release": "18.0.1",
|
"sass-loader": "13.0.2",
|
||||||
|
"semantic-release": "19.0.3",
|
||||||
"start-server-and-test": "1.14.0",
|
"start-server-and-test": "1.14.0",
|
||||||
"storybook-addon-next-router": "3.1.0",
|
"storybook-addon-next-router": "4.0.0",
|
||||||
"storybook-dark-mode": "1.0.8",
|
"storybook-dark-mode": "1.1.0",
|
||||||
"stylelint": "14.1.0",
|
"stylelint": "14.9.1",
|
||||||
"stylelint-config-css-modules": "2.3.0",
|
"stylelint-config-css-modules": "4.1.0",
|
||||||
"stylelint-config-prettier": "9.0.3",
|
"stylelint-config-prettier": "9.0.3",
|
||||||
"stylelint-config-recess-order": "3.0.0",
|
"stylelint-config-recess-order": "3.0.0",
|
||||||
"stylelint-config-standard": "24.0.0",
|
"stylelint-config-standard": "26.0.0",
|
||||||
"stylelint-prettier": "2.0.0",
|
"stylelint-prettier": "2.0.0",
|
||||||
"stylelint-scss": "4.0.0",
|
"stylelint-scss": "4.2.0",
|
||||||
"typescript": "4.4.4",
|
"typescript": "4.7.4",
|
||||||
"webpack": "5.64.4"
|
"webpack": "5.73.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
"path": "cz-conventional-changelog"
|
"path": "cz-conventional-changelog"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"sharp": "^0.28.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,16 +4,13 @@ import Document, { Html, Head, Main, NextScript } from 'next/document'
|
|||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Html lang="en">
|
<Html lang="en" className="preload">
|
||||||
<Head>
|
<Head />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<body>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
<script src="/js/noflash.js?v1" />
|
||||||
</Head>
|
|
||||||
<body className="preload">
|
|
||||||
<script src="/js/noflash.js" />
|
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
<script src="/js/onload.js" />
|
<script src="/js/onload.js?v1" />
|
||||||
</body>
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
)
|
)
|
||||||
|
|||||||
BIN
public/fonts/manrope-v12.woff2
Normal file
BIN
public/fonts/manrope-v12.woff2
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 8.3 KiB |
@ -1,5 +1,5 @@
|
|||||||
;(function () {
|
;(function () {
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
document.body.classList.remove('preload')
|
document.documentElement.classList.remove('preload')
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
|
|||||||
48
public/llms.txt
Normal file
48
public/llms.txt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# 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.
|
||||||
@ -1,2 +1,8 @@
|
|||||||
User-Agent: *
|
User-Agent: *
|
||||||
Allow: /
|
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
|
||||||
|
|||||||
6
public/sitemap.xml
Normal file
6
public/sitemap.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?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>
|
||||||
@ -1,48 +1,48 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: 'Manrope';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
|
src: url('/fonts/manrope-v12.woff2') format('woff2');
|
||||||
font-display: swap;
|
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,
|
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;
|
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: 'Manrope';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
|
src: url('/fonts/manrope-v12.woff2') format('woff2');
|
||||||
font-display: swap;
|
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,
|
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;
|
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: 'Manrope';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
|
src: url('/fonts/manrope-v12.woff2') format('woff2');
|
||||||
font-display: swap;
|
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,
|
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;
|
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: 'Manrope';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
|
src: url('/fonts/manrope-v12.woff2') format('woff2');
|
||||||
font-display: swap;
|
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,
|
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;
|
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: 'Manrope';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
|
src: url('/fonts/manrope-v12.woff2') format('woff2');
|
||||||
font-display: swap;
|
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,
|
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;
|
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
|||||||
@ -11,14 +11,14 @@ body {
|
|||||||
|
|
||||||
/* Disables pull-to-refresh but allows overscroll glow effects. */
|
/* Disables pull-to-refresh but allows overscroll glow effects. */
|
||||||
overscroll-behavior-y: contain;
|
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 */
|
/* https://gist.github.com/hsleonis/55712b0eafc9b25f1944 */
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-variant-ligatures: none;
|
font-variant-ligatures: none;
|
||||||
text-shadow: rgba($black, 0.01) 0 0 1px;
|
text-shadow: rgba($black, 0.01) 0 0 1px;
|
||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizelegibility;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user