next-mdx-remote v4 (#737)
@@ -27,7 +27,7 @@ module.exports = {
|
|||||||
files: ["*.md", "*.mdx"],
|
files: ["*.md", "*.mdx"],
|
||||||
extends: ["plugin:mdx/recommended"],
|
extends: ["plugin:mdx/recommended"],
|
||||||
rules: {
|
rules: {
|
||||||
"import/no-unresolved": "off",
|
"react/jsx-no-undef": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
97
components/CodeBlock/CodeBlock.module.css
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
.code {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the following sub-classes MUST be global -- the highlight rehype plugin isn't aware of this file */
|
||||||
|
|
||||||
|
.code :global(.code-highlight) {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 1em;
|
||||||
|
tab-size: 2;
|
||||||
|
color: var(--code-text);
|
||||||
|
background-color: var(--code-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* leave room for clipboard button to the right of the first line */
|
||||||
|
.code :global(.code-highlight) > :global(.code-line:first-of-type) {
|
||||||
|
margin-right: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) > :global(.code-line.line-number::before) {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1.5em;
|
||||||
|
margin-right: 1.5em;
|
||||||
|
text-align: right;
|
||||||
|
color: var(--code-comment);
|
||||||
|
content: attr(line); /* added to spans by prism */
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.comment),
|
||||||
|
.code :global(.code-highlight) :global(.token.prolog),
|
||||||
|
.code :global(.code-highlight) :global(.token.cdata) {
|
||||||
|
color: var(--code-comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.delimiter),
|
||||||
|
.code :global(.code-highlight) :global(.token.boolean),
|
||||||
|
.code :global(.code-highlight) :global(.token.keyword),
|
||||||
|
.code :global(.code-highlight) :global(.token.selector),
|
||||||
|
.code :global(.code-highlight) :global(.token.important),
|
||||||
|
.code :global(.code-highlight) :global(.token.doctype),
|
||||||
|
.code :global(.code-highlight) :global(.token.atrule),
|
||||||
|
.code :global(.code-highlight) :global(.token.url) {
|
||||||
|
color: var(--code-keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.tag),
|
||||||
|
.code :global(.code-highlight) :global(.token.builtin),
|
||||||
|
.code :global(.code-highlight) :global(.token.regex) {
|
||||||
|
color: var(--code-namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.property),
|
||||||
|
.code :global(.code-highlight) :global(.token.constant),
|
||||||
|
.code :global(.code-highlight) :global(.token.variable),
|
||||||
|
.code :global(.code-highlight) :global(.token.attr-value),
|
||||||
|
.code :global(.code-highlight) :global(.token.class-name),
|
||||||
|
.code :global(.code-highlight) :global(.token.string),
|
||||||
|
.code :global(.code-highlight) :global(.token.char) {
|
||||||
|
color: var(--code-variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.literal-property),
|
||||||
|
.code :global(.code-highlight) :global(.token.attr-name) {
|
||||||
|
color: var(--code-attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.function) {
|
||||||
|
color: var(--code-literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.tag .punctuation),
|
||||||
|
.code :global(.code-highlight) :global(.token.attr-value .punctuation) {
|
||||||
|
color: var(--code-punctuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.inserted) {
|
||||||
|
background-color: var(--code-addition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.deleted) {
|
||||||
|
background-color: var(--code-deletion);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.url) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.bold) {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code :global(.code-highlight) :global(.token.italic) {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
@@ -1,19 +1,19 @@
|
|||||||
import CopyButton from "./CopyButton";
|
import CopyButton from "../CopyButton/CopyButton";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import styles from "./Code.module.css";
|
import styles from "./CodeBlock.module.css";
|
||||||
|
|
||||||
export type CustomCodeProps = {
|
export type Props = {
|
||||||
className?: string;
|
className?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomCode = (props: CustomCodeProps) => {
|
const CodeBlock = (props: Props) => {
|
||||||
if (props.className?.split(" ").includes("code-highlight")) {
|
if (props.className?.split(" ").includes("code-highlight")) {
|
||||||
// full multi-line code blocks with prism highlighting and copy-to-clipboard button
|
// full multi-line code blocks with prism highlighting and copy-to-clipboard button
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.code_block}>
|
<div className={styles.code}>
|
||||||
<CopyButton source={props.children} />
|
<CopyButton source={props.children} />
|
||||||
<code {...props}>{props.children}</code>
|
<code {...props}>{props.children}</code>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,4 +25,4 @@ const CustomCode = (props: CustomCodeProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomCode;
|
export default CodeBlock;
|
@@ -3,7 +3,7 @@ import Link from "next/link";
|
|||||||
import css from "styled-jsx/css";
|
import css from "styled-jsx/css";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
type ColorLinkProps = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
href: string;
|
href: string;
|
||||||
lightColor: string;
|
lightColor: string;
|
||||||
@@ -12,7 +12,7 @@ type ColorLinkProps = {
|
|||||||
external?: boolean;
|
external?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFancyLinkStyles = ({ lightColor, darkColor }: Partial<ColorLinkProps>) => {
|
const getFancyLinkStyles = ({ lightColor, darkColor }: Partial<Props>) => {
|
||||||
// spits out a linear-gradient (that's not realy a gradient) with translucent color in rgba() format
|
// spits out a linear-gradient (that's not realy a gradient) with translucent color in rgba() format
|
||||||
const linearGradient = (hex: string, alpha = 0.4) => {
|
const linearGradient = (hex: string, alpha = 0.4) => {
|
||||||
// hex -> rgb, adapted from https://github.com/sindresorhus/hex-rgb/blob/main/index.js
|
// hex -> rgb, adapted from https://github.com/sindresorhus/hex-rgb/blob/main/index.js
|
||||||
@@ -39,7 +39,7 @@ const getFancyLinkStyles = ({ lightColor, darkColor }: Partial<ColorLinkProps>)
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ColorLink = ({ href, title, lightColor, darkColor, external = false, children }: ColorLinkProps) => {
|
const ColorfulLink = ({ href, title, lightColor, darkColor, external = false, children }: Props) => {
|
||||||
const { className, styles } = getFancyLinkStyles({ lightColor, darkColor });
|
const { className, styles } = getFancyLinkStyles({ lightColor, darkColor });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -60,4 +60,4 @@ const ColorLink = ({ href, title, lightColor, darkColor, external = false, child
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ColorLink);
|
export default memo(ColorfulLink);
|
@@ -4,7 +4,7 @@ import classNames from "classnames/bind";
|
|||||||
import { Formik, Form, Field } from "formik";
|
import { Formik, Form, Field } from "formik";
|
||||||
import HCaptcha from "@hcaptcha/react-hcaptcha";
|
import HCaptcha from "@hcaptcha/react-hcaptcha";
|
||||||
import isEmailLike from "is-email-like";
|
import isEmailLike from "is-email-like";
|
||||||
import { SendIcon, CheckOcticon, XOcticon } from "../icons";
|
import { SendIcon, CheckOcticon, XOcticon } from "../Icons";
|
||||||
|
|
||||||
import type { FormikHelpers } from "formik";
|
import type { FormikHelpers } from "formik";
|
||||||
|
|
@@ -1,6 +1,7 @@
|
|||||||
import styles from "./Content.module.css";
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
import styles from "./Content.module.css";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
|
|||||||
import classNames from "classnames/bind";
|
import classNames from "classnames/bind";
|
||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import innerText from "react-innertext";
|
import innerText from "react-innertext";
|
||||||
import { ClipboardOcticon, CheckOcticon } from "../icons";
|
import { ClipboardOcticon, CheckOcticon } from "../Icons";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import styles from "./CopyButton.module.css";
|
import styles from "./CopyButton.module.css";
|
@@ -1,14 +1,14 @@
|
|||||||
import Image from "./Image";
|
import Image from "../Image/Image";
|
||||||
import innerText from "react-innertext";
|
import innerText from "react-innertext";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import type { ImageProps } from "next/image";
|
import type { ImageProps as NextImageProps } from "next/image";
|
||||||
|
|
||||||
type CustomFigureProps = Omit<ImageProps, "alt"> & {
|
type Props = Omit<NextImageProps, "alt"> & {
|
||||||
children: ReactNode; // caption (can be in markdown, yay!!!)
|
children: ReactNode; // caption (can be in markdown, yay!!!)
|
||||||
alt?: string; // becomes optional -- pulled from plaintext-ified caption if missing
|
alt?: string; // becomes optional -- pulled from plaintext-ified caption if missing
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomFigure = ({ children, alt, ...imageProps }: CustomFigureProps) => {
|
const Figure = ({ children, alt, ...imageProps }: Props) => {
|
||||||
return (
|
return (
|
||||||
<figure>
|
<figure>
|
||||||
<Image alt={alt || innerText(children)} {...imageProps} />
|
<Image alt={alt || innerText(children)} {...imageProps} />
|
||||||
@@ -17,4 +17,4 @@ const CustomFigure = ({ children, alt, ...imageProps }: CustomFigureProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomFigure;
|
export default Figure;
|
@@ -1,6 +1,6 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { HeartIcon, NextjsLogo } from "../icons";
|
import { HeartIcon, NextjsLogo } from "../Icons";
|
||||||
import * as config from "../../lib/config";
|
import * as config from "../../lib/config";
|
||||||
|
|
||||||
import styles from "./Footer.module.css";
|
import styles from "./Footer.module.css";
|
3
components/GistEmbed/GistEmbed.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import GistEmbed from "react-gist";
|
||||||
|
|
||||||
|
export default GistEmbed;
|
52
components/Header/Header.module.css
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
.header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4.5em;
|
||||||
|
padding: 0.7em 1.5em;
|
||||||
|
border-bottom: 1px solid var(--kinda-light);
|
||||||
|
background-color: var(--background-header);
|
||||||
|
backdrop-filter: saturate(180%) blur(5px);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 865px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav > div {
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.header {
|
||||||
|
padding: 0.75em 1.25em;
|
||||||
|
height: 5.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 325px;
|
||||||
|
margin-left: 2.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 380px) {
|
||||||
|
.right {
|
||||||
|
max-width: 225px;
|
||||||
|
margin-left: 1.6em;
|
||||||
|
}
|
||||||
|
}
|
21
components/Header/Header.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { memo } from "react";
|
||||||
|
import Name from "../Name/Name";
|
||||||
|
import Menu from "../Menu/Menu";
|
||||||
|
|
||||||
|
import styles from "./Header.module.css";
|
||||||
|
|
||||||
|
const Header = () => (
|
||||||
|
<header className={styles.header}>
|
||||||
|
<nav className={styles.nav}>
|
||||||
|
<div className={styles.left}>
|
||||||
|
<Name />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.right}>
|
||||||
|
<Menu />
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default memo(Header);
|
@@ -1,5 +1,5 @@
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import Loading from "../loading/Loading";
|
import Loading from "../Loading/Loading";
|
||||||
import { fetcher } from "../../lib/fetcher";
|
import { fetcher } from "../../lib/fetcher";
|
||||||
|
|
||||||
const HitCounter = ({ slug }) => {
|
const HitCounter = ({ slug }) => {
|
21
components/Image/Image.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import NextImage from "next/image";
|
||||||
|
import type { ImageProps as NextImageProps } from "next/image";
|
||||||
|
|
||||||
|
const Image = ({ src, width, height, alt, quality, priority }: NextImageProps) => {
|
||||||
|
return (
|
||||||
|
<div className="image_wrapper">
|
||||||
|
<NextImage
|
||||||
|
src={(src as string).replace(/^\/public/g, "")}
|
||||||
|
layout="intrinsic"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
alt={alt || ""}
|
||||||
|
quality={quality || 65}
|
||||||
|
loading={priority ? "eager" : "lazy"}
|
||||||
|
priority={!!priority}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Image;
|
@@ -1,8 +1,8 @@
|
|||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import Header from "./header/Header";
|
import Header from "../Header/Header";
|
||||||
import Footer from "./footer/Footer";
|
import Footer from "../Footer/Footer";
|
||||||
import { themeColors } from "../lib/config";
|
import { themeColors } from "../../lib/config";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import styles from "./Layout.module.css";
|
import styles from "./Layout.module.css";
|
71
components/Menu/Menu.module.css
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
.menu {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li {
|
||||||
|
list-style: none;
|
||||||
|
display: inline-flex;
|
||||||
|
margin-left: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li .link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--medium-dark);
|
||||||
|
background: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li .link:hover {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li .icon {
|
||||||
|
width: 1.6em;
|
||||||
|
height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li span {
|
||||||
|
font-size: 0.95em;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-left: 0.8em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li.theme_toggle {
|
||||||
|
margin-left: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.menu {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li .icon {
|
||||||
|
width: 1.8em;
|
||||||
|
height: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide text next to emojis on mobile */
|
||||||
|
.menu li span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li.theme_toggle {
|
||||||
|
margin-left: -0.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the home icon is redundant when space is SUPER tight */
|
||||||
|
@media screen and (max-width: 380px) {
|
||||||
|
.menu li:first-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
49
components/Menu/Menu.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { memo } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import ThemeToggle from "../ThemeToggle/ThemeToggle";
|
||||||
|
import { HomeIcon, NotesIcon, ProjectsIcon, ContactIcon } from "../Icons";
|
||||||
|
|
||||||
|
import styles from "./Menu.module.css";
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{
|
||||||
|
icon: <HomeIcon className={`icon ${styles.icon}`} />,
|
||||||
|
text: "Home",
|
||||||
|
href: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <NotesIcon className={`icon ${styles.icon}`} />,
|
||||||
|
text: "Notes",
|
||||||
|
href: "/notes/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <ProjectsIcon className={`icon ${styles.icon}`} />,
|
||||||
|
text: "Projects",
|
||||||
|
href: "/projects/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <ContactIcon className={`icon ${styles.icon}`} />,
|
||||||
|
text: "Contact",
|
||||||
|
href: "/contact/",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Menu = () => (
|
||||||
|
<ul className={styles.menu}>
|
||||||
|
{links.map((link, index) => (
|
||||||
|
<li key={index}>
|
||||||
|
<Link href={link.href} prefetch={false}>
|
||||||
|
<a className={styles.link}>
|
||||||
|
{link.icon} <span>{link.text}</span>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<li className={styles.theme_toggle}>
|
||||||
|
<ThemeToggle className={styles.icon} />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default memo(Menu);
|
45
components/Name/Name.module.css
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
.name {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--medium-dark);
|
||||||
|
background: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name .selfie {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name .selfie img {
|
||||||
|
border: 1px solid var(--light) !important;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name:hover {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.name:hover .selfie {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name span:last-of-type {
|
||||||
|
margin: 0 0.6em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.name .selfie {
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name span:last-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
20
components/Name/Name.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { memo } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
import styles from "./Name.module.css";
|
||||||
|
|
||||||
|
import meJpg from "../../public/static/images/me.jpg";
|
||||||
|
|
||||||
|
const Name = () => (
|
||||||
|
<Link href="/">
|
||||||
|
<a className={styles.name}>
|
||||||
|
<div className={styles.selfie}>
|
||||||
|
<Image src={meJpg} alt="Photo of Jake Jarvis" width={70} height={70} quality={60} layout="intrinsic" priority />
|
||||||
|
</div>
|
||||||
|
<span>Jake Jarvis</span>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default memo(Name);
|
@@ -1,13 +1,13 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import HitCounter from "./HitCounter";
|
import HitCounter from "../HitCounter/HitCounter";
|
||||||
import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../icons";
|
import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../Icons";
|
||||||
import * as config from "../../lib/config";
|
import * as config from "../../lib/config";
|
||||||
import type { NoteMetaType } from "../../types";
|
import type { NoteMetaType } from "../../types";
|
||||||
|
|
||||||
import styles from "./Meta.module.css";
|
import styles from "./NoteMeta.module.css";
|
||||||
|
|
||||||
const Meta = ({ slug, date, title, htmlTitle, tags = [] }: NoteMetaType) => (
|
const NoteMeta = ({ slug, date, title, htmlTitle, tags = [] }: NoteMetaType) => (
|
||||||
<>
|
<>
|
||||||
<div className={styles.meta}>
|
<div className={styles.meta}>
|
||||||
<div className={styles.date}>
|
<div className={styles.date}>
|
||||||
@@ -66,4 +66,4 @@ const Meta = ({ slug, date, title, htmlTitle, tags = [] }: NoteMetaType) => (
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Meta;
|
export default NoteMeta;
|
@@ -2,9 +2,9 @@ import Link from "next/link";
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import type { NoteMetaType } from "../../types";
|
import type { NoteMetaType } from "../../types";
|
||||||
|
|
||||||
import styles from "./List.module.css";
|
import styles from "./NotesList.module.css";
|
||||||
|
|
||||||
const List = ({ notesByYear }) => {
|
const NotesList = ({ notesByYear }) => {
|
||||||
const sections = [];
|
const sections = [];
|
||||||
|
|
||||||
Object.entries(notesByYear).forEach(([year, notes]: [string, NoteMetaType[]]) => {
|
Object.entries(notesByYear).forEach(([year, notes]: [string, NoteMetaType[]]) => {
|
||||||
@@ -39,4 +39,4 @@ const List = ({ notesByYear }) => {
|
|||||||
return <>{reversed}</>;
|
return <>{reversed}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default List;
|
export default NotesList;
|
19
components/OctocatLink/OctocatLink.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { OctocatOcticon } from "../Icons";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
repo: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OctocatLink = (props: Props) => (
|
||||||
|
<a
|
||||||
|
className="no-underline"
|
||||||
|
href={`https://github.com/${props.repo}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style={{ margin: "0 0.4em", color: "var(--text)" }}
|
||||||
|
>
|
||||||
|
<OctocatOcticon fill="currentColor" />
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default OctocatLink;
|
@@ -1,10 +1,10 @@
|
|||||||
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
|
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
|
||||||
import { StarOcticon, ForkOcticon } from "../icons";
|
import { StarOcticon, ForkOcticon } from "../Icons";
|
||||||
import { RepoType } from "../../types";
|
import { RepoType } from "../../types";
|
||||||
|
|
||||||
import styles from "./RepoCard.module.css";
|
import styles from "./RepositoryCard.module.css";
|
||||||
|
|
||||||
const RepoCard = (props: RepoType) => (
|
const RepositoryCard = (props: RepoType) => (
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<a className={styles.name} href={props.url} target="_blank" rel="noopener noreferrer">
|
<a className={styles.name} href={props.url} target="_blank" rel="noopener noreferrer">
|
||||||
{props.name}
|
{props.name}
|
||||||
@@ -71,4 +71,4 @@ const RepoCard = (props: RepoType) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default RepoCard;
|
export default RepositoryCard;
|
17
components/TweetEmbed/TweetEmbed.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Tweet from "react-tweet-embed";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TweetEmbed = (props: Props) => (
|
||||||
|
<Tweet
|
||||||
|
id={props.id}
|
||||||
|
options={{
|
||||||
|
dnt: true,
|
||||||
|
align: "center",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default TweetEmbed;
|
10
components/Video/Video.module.css
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 56.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper > div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
63
components/Video/Video.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import ReactPlayer from "react-player/file";
|
||||||
|
|
||||||
|
import styles from "./Video.module.css";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
webm?: string;
|
||||||
|
mp4?: string;
|
||||||
|
thumbnail?: string;
|
||||||
|
subs?: string;
|
||||||
|
autoplay?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Video = ({ webm, mp4, thumbnail, subs, autoplay }: Props) => {
|
||||||
|
const url = [
|
||||||
|
webm && {
|
||||||
|
src: webm,
|
||||||
|
type: "video/webm",
|
||||||
|
},
|
||||||
|
mp4 && {
|
||||||
|
src: mp4,
|
||||||
|
type: "video/mp4",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
file: {
|
||||||
|
attributes: {
|
||||||
|
controlsList: "nodownload",
|
||||||
|
preload: "metadata",
|
||||||
|
autoPlay: autoplay,
|
||||||
|
muted: autoplay,
|
||||||
|
loop: autoplay,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (thumbnail) {
|
||||||
|
// @ts-ignore
|
||||||
|
config.file.attributes.poster = thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subs) {
|
||||||
|
// @ts-ignore
|
||||||
|
config.file.tracks = [
|
||||||
|
{
|
||||||
|
kind: "subtitles",
|
||||||
|
src: subs,
|
||||||
|
srcLang: "en",
|
||||||
|
label: "English",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<ReactPlayer width="100%" height="100%" url={url} config={config} controls={!autoplay} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Video;
|
10
components/YouTubeEmbed/YouTubeEmbed.module.css
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 56.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper > div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
21
components/YouTubeEmbed/YouTubeEmbed.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import ReactPlayer from "react-player/youtube";
|
||||||
|
|
||||||
|
import styles from "./YouTubeEmbed.module.css";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const YouTubeEmbed = ({ id }: Props) => (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<ReactPlayer
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
url={`https://www.youtube-nocookie.com/watch?v=${id}`}
|
||||||
|
light={`https://i.ytimg.com/vi/${id}/hqdefault.jpg`}
|
||||||
|
controls
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default YouTubeEmbed;
|
@@ -1,97 +0,0 @@
|
|||||||
.code_block {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
margin: 1em auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the following sub-classes MUST be global -- the highlight rehype plugin isn't aware of this file */
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 1em;
|
|
||||||
tab-size: 2;
|
|
||||||
color: var(--code-text);
|
|
||||||
background-color: var(--code-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* leave room for clipboard button to the right of the first line */
|
|
||||||
.code_block :global(.code-highlight) > :global(.code-line:first-of-type) {
|
|
||||||
margin-right: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) > :global(.code-line.line-number::before) {
|
|
||||||
display: inline-block;
|
|
||||||
width: 1.5em;
|
|
||||||
margin-right: 1.5em;
|
|
||||||
text-align: right;
|
|
||||||
color: var(--code-comment);
|
|
||||||
content: attr(line); /* added to spans by prism */
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.comment),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.prolog),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.cdata) {
|
|
||||||
color: var(--code-comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.delimiter),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.boolean),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.keyword),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.selector),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.important),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.doctype),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.atrule),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.url) {
|
|
||||||
color: var(--code-keyword);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.tag),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.builtin),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.regex) {
|
|
||||||
color: var(--code-namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.property),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.constant),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.variable),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.attr-value),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.class-name),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.string),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.char) {
|
|
||||||
color: var(--code-variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.literal-property),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.attr-name) {
|
|
||||||
color: var(--code-attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.function) {
|
|
||||||
color: var(--code-literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.tag .punctuation),
|
|
||||||
.code_block :global(.code-highlight) :global(.token.attr-value .punctuation) {
|
|
||||||
color: var(--code-punctuation);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.inserted) {
|
|
||||||
background-color: var(--code-addition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.deleted) {
|
|
||||||
background-color: var(--code-deletion);
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.url) {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.bold) {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code_block :global(.code-highlight) :global(.token.italic) {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
@@ -1,168 +0,0 @@
|
|||||||
.header {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 4.5em;
|
|
||||||
padding: 0.7em 1.5em;
|
|
||||||
border-bottom: 1px solid var(--kinda-light);
|
|
||||||
background-color: var(--background-header);
|
|
||||||
backdrop-filter: saturate(180%) blur(5px);
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 865px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav > div {
|
|
||||||
line-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** left side: photo/name ***/
|
|
||||||
|
|
||||||
.left {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--medium-dark);
|
|
||||||
background: none;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name .selfie {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
line-height: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name .selfie img {
|
|
||||||
border: 1px solid var(--light) !important;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name:hover {
|
|
||||||
color: var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
.name:hover .selfie {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name span:last-of-type {
|
|
||||||
margin: 0 0.6em;
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** right side: menu ***/
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
display: inline-flex;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li {
|
|
||||||
list-style: none;
|
|
||||||
display: inline-flex;
|
|
||||||
margin-left: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li .link {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--medium-dark);
|
|
||||||
background: none;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li .link:hover {
|
|
||||||
color: var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li .icon {
|
|
||||||
width: 1.6em;
|
|
||||||
height: 1.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li span {
|
|
||||||
font-size: 0.95em;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-left: 0.8em;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li.theme_toggle {
|
|
||||||
margin-left: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
|
||||||
.header {
|
|
||||||
padding: 0.75em 1.25em;
|
|
||||||
height: 5.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name .selfie {
|
|
||||||
width: 70px;
|
|
||||||
height: 70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name span:last-of-type {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
flex: 1;
|
|
||||||
max-width: 325px;
|
|
||||||
margin-left: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li .icon {
|
|
||||||
width: 1.8em;
|
|
||||||
height: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hide text next to emojis on mobile */
|
|
||||||
.menu li span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li.theme_toggle {
|
|
||||||
margin-left: -0.3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the home icon is redundant when space is SUPER tight */
|
|
||||||
@media screen and (max-width: 380px) {
|
|
||||||
.right {
|
|
||||||
max-width: 225px;
|
|
||||||
margin-left: 1.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu li:first-of-type {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,77 +0,0 @@
|
|||||||
import { memo } from "react";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import ThemeToggle from "./ThemeToggle";
|
|
||||||
import { HomeIcon, NotesIcon, ProjectsIcon, ContactIcon } from "../icons";
|
|
||||||
|
|
||||||
import meJpg from "../../public/static/images/me.jpg";
|
|
||||||
|
|
||||||
import styles from "./Header.module.css";
|
|
||||||
|
|
||||||
const links = [
|
|
||||||
{
|
|
||||||
icon: <HomeIcon className={`icon ${styles.icon}`} />,
|
|
||||||
text: "Home",
|
|
||||||
href: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <NotesIcon className={`icon ${styles.icon}`} />,
|
|
||||||
text: "Notes",
|
|
||||||
href: "/notes/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <ProjectsIcon className={`icon ${styles.icon}`} />,
|
|
||||||
text: "Projects",
|
|
||||||
href: "/projects/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <ContactIcon className={`icon ${styles.icon}`} />,
|
|
||||||
text: "Contact",
|
|
||||||
href: "/contact/",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Header = () => (
|
|
||||||
<header className={styles.header}>
|
|
||||||
<nav className={styles.nav}>
|
|
||||||
<div className={styles.left}>
|
|
||||||
<Link href="/">
|
|
||||||
<a className={styles.name}>
|
|
||||||
<div className={styles.selfie}>
|
|
||||||
<Image
|
|
||||||
src={meJpg}
|
|
||||||
alt="Photo of Jake Jarvis"
|
|
||||||
width={70}
|
|
||||||
height={70}
|
|
||||||
quality={60}
|
|
||||||
layout="intrinsic"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span>Jake Jarvis</span>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.right}>
|
|
||||||
<ul className={styles.menu}>
|
|
||||||
{links.map((link, index) => (
|
|
||||||
<li key={index}>
|
|
||||||
<Link href={link.href} prefetch={false}>
|
|
||||||
<a className={styles.link}>
|
|
||||||
{link.icon} <span>{link.text}</span>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<li className={styles.theme_toggle}>
|
|
||||||
<ThemeToggle className={styles.icon} />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default memo(Header);
|
|
@@ -1,3 +0,0 @@
|
|||||||
import Gist from "react-gist";
|
|
||||||
|
|
||||||
export default Gist;
|
|
@@ -1,21 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import type { ImageProps } from "next/image";
|
|
||||||
|
|
||||||
const CustomImage = ({ src, width, height, alt, priority }: ImageProps) => {
|
|
||||||
return (
|
|
||||||
<div className="image_wrapper">
|
|
||||||
<Image
|
|
||||||
src={src}
|
|
||||||
layout="intrinsic"
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
alt={alt || ""}
|
|
||||||
quality={65}
|
|
||||||
loading={priority ? "eager" : "lazy"}
|
|
||||||
priority={!!priority}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomImage;
|
|
@@ -1,17 +0,0 @@
|
|||||||
import TweetEmbed from "react-tweet-embed";
|
|
||||||
|
|
||||||
type CustomTweetProps = {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Tweet = (props: CustomTweetProps) => (
|
|
||||||
<TweetEmbed
|
|
||||||
id={props.id}
|
|
||||||
options={{
|
|
||||||
dnt: true,
|
|
||||||
align: "center",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Tweet;
|
|
@@ -1,24 +0,0 @@
|
|||||||
import ReactPlayer from "react-player/lazy";
|
|
||||||
import type { ReactPlayerProps } from "react-player";
|
|
||||||
|
|
||||||
const Video = (props: ReactPlayerProps) => (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: "relative",
|
|
||||||
paddingTop: "56.25%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ReactPlayer
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
}}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Video;
|
|
17
lib/mdx-components.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
// Bundle these components by default:
|
||||||
|
export { default as Image } from "../components/Image/Image";
|
||||||
|
export { default as Figure } from "../components/Figure/Figure";
|
||||||
|
// `code` is intentionally lowercase here -- replaces `<code>` tag from remark
|
||||||
|
export { default as code } from "../components/CodeBlock/CodeBlock";
|
||||||
|
|
||||||
|
// All of these components are technically passed into all posts, but next/dynamic ensures they're loaded only
|
||||||
|
// when they're referenced in the individual mdx files.
|
||||||
|
export const Video = dynamic(() => import("../components/Video/Video"));
|
||||||
|
export const YouTube = dynamic(() => import("../components/YouTubeEmbed/YouTubeEmbed"));
|
||||||
|
export const Tweet = dynamic(() => import("../components/TweetEmbed/TweetEmbed"));
|
||||||
|
export const Gist = dynamic(() => import("../components/GistEmbed/GistEmbed"));
|
||||||
|
|
||||||
|
// one-offs for specific posts
|
||||||
|
export const OctocatLink = dynamic(() => import("../components/OctocatLink/OctocatLink"));
|
@@ -2,7 +2,7 @@ import fs from "fs";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { renderToStaticMarkup } from "react-dom/server";
|
import { renderToStaticMarkup } from "react-dom/server";
|
||||||
import matter from "gray-matter";
|
import matter from "gray-matter";
|
||||||
import { bundleMDX } from "mdx-bundler";
|
import { serialize } from "next-mdx-remote/serialize";
|
||||||
import { compiler } from "markdown-to-jsx";
|
import { compiler } from "markdown-to-jsx";
|
||||||
import removeMarkdown from "remove-markdown";
|
import removeMarkdown from "remove-markdown";
|
||||||
import sanitizeHtml from "sanitize-html";
|
import sanitizeHtml from "sanitize-html";
|
||||||
@@ -64,20 +64,12 @@ export const getNoteData = (slug: string): { frontMatter: NoteMetaType; content:
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getNote = async (slug: string): Promise<NoteType> => {
|
export const getNote = async (slug: string): Promise<NoteType> => {
|
||||||
// https://github.com/kentcdodds/mdx-bundler#nextjs-esbuild-enoent
|
|
||||||
process.env.ESBUILD_BINARY_PATH =
|
|
||||||
process.platform === "win32"
|
|
||||||
? path.join(process.cwd(), "node_modules", "esbuild", "esbuild.exe")
|
|
||||||
: path.join(process.cwd(), "node_modules", "esbuild", "bin", "esbuild");
|
|
||||||
|
|
||||||
const { frontMatter, content } = getNoteData(slug);
|
const { frontMatter, content } = getNoteData(slug);
|
||||||
const { code: mdxSource } = await bundleMDX({
|
const source = await serialize(content, {
|
||||||
source: content,
|
parseFrontmatter: false,
|
||||||
cwd: path.join(process.cwd(), NOTES_DIR),
|
mdxOptions: {
|
||||||
xdmOptions: (options) => {
|
remarkPlugins: [[remarkGfm, { singleTilde: false }]],
|
||||||
options.remarkPlugins = [...(options.remarkPlugins ?? []), [remarkGfm, { singleTilde: false }]];
|
rehypePlugins: [
|
||||||
options.rehypePlugins = [
|
|
||||||
...(options.rehypePlugins ?? []),
|
|
||||||
[rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }],
|
[rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }],
|
||||||
[rehypeSlug, {}],
|
[rehypeSlug, {}],
|
||||||
[
|
[
|
||||||
@@ -90,26 +82,13 @@ export const getNote = async (slug: string): Promise<NoteType> => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[rehypePrism, { ignoreMissing: true }],
|
[rehypePrism, { ignoreMissing: true }],
|
||||||
];
|
],
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
esbuildOptions: (options) => {
|
|
||||||
options.minify = true;
|
|
||||||
options.target = ["es2018"];
|
|
||||||
options.loader = {
|
|
||||||
...options.loader,
|
|
||||||
".js": "jsx",
|
|
||||||
".ts": "tsx",
|
|
||||||
};
|
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
frontMatter,
|
frontMatter,
|
||||||
mdxSource,
|
source,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -11,14 +11,10 @@ tags:
|
|||||||
image: "/static/images/notes/bernie-sanders-bern-app-data/sad-bernie.jpg"
|
image: "/static/images/notes/bernie-sanders-bern-app-data/sad-bernie.jpg"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
import Video from "../components/media/Video";
|
|
||||||
|
|
||||||
The team behind Bernie Sanders' 2020 campaign [released a new web app](https://www.nbcnews.com/politics/2020-election/bernie-sanders-2020-campaign-unveils-app-increase-its-voter-database-n999206) last month named [BERN](https://app.berniesanders.com/). The goal of BERN is simple: to gather as much information as they can on as many voters in the United States as they can, and make their grassroots army of enthusiastic supporters do the work. It's undoubtedly a smart strategy, but also a concerning one for myself and other privacy advocates.
|
The team behind Bernie Sanders' 2020 campaign [released a new web app](https://www.nbcnews.com/politics/2020-election/bernie-sanders-2020-campaign-unveils-app-increase-its-voter-database-n999206) last month named [BERN](https://app.berniesanders.com/). The goal of BERN is simple: to gather as much information as they can on as many voters in the United States as they can, and make their grassroots army of enthusiastic supporters do the work. It's undoubtedly a smart strategy, but also a concerning one for myself and other privacy advocates.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/bernie-sanders-bern-app-data/sad-bernie.jpg"
|
src="/public/static/images/notes/bernie-sanders-bern-app-data/sad-bernie.jpg"
|
||||||
width="865"
|
width="865"
|
||||||
height="433"
|
height="433"
|
||||||
alt="Sad Bernie"
|
alt="Sad Bernie"
|
||||||
@@ -51,34 +47,32 @@ Using either feature, a volunteer starts with a search of the database for the v
|
|||||||
Here's one of the instructional videos provided internally to volunteers:
|
Here's one of the instructional videos provided internally to volunteers:
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/notes/bernie-sanders-bern-app-data/friend-to-friend.webm"
|
||||||
{ src: "/static/images/notes/bernie-sanders-bern-app-data/friend-to-friend.webm", type: "video/webm" },
|
mp4="/static/images/notes/bernie-sanders-bern-app-data/friend-to-friend.mp4"
|
||||||
{ src: "/static/images/notes/bernie-sanders-bern-app-data/friend-to-friend.mp4", type: "video/mp4" },
|
thumbnail="/static/images/notes/bernie-sanders-bern-app-data/poster-friend-to-friend.png"
|
||||||
]}
|
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: "/static/images/notes/bernie-sanders-bern-app-data/poster-friend-to-friend.png",
|
|
||||||
controlsList: "nodownload",
|
|
||||||
preload: "metadata",
|
|
||||||
autoPlay: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={true}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
...and a few privacy-related questions about the friend-to-friend feature were answered by campaign staff in a separate closed webinar for volunteers this week:
|
...and a few privacy-related questions about the friend-to-friend feature were answered by campaign staff in a separate closed webinar for volunteers this week:
|
||||||
|
|
||||||
<Image src="/static/images/notes/bernie-sanders-bern-app-data/webinar-qa-1.png" width="400" height="155" alt="Q&A 1" />
|
<Image
|
||||||
|
src="/public/static/images/notes/bernie-sanders-bern-app-data/webinar-qa-1.png"
|
||||||
|
width="400"
|
||||||
|
height="155"
|
||||||
|
alt="Q&A 1"
|
||||||
|
/>
|
||||||
|
|
||||||
<Image src="/static/images/notes/bernie-sanders-bern-app-data/webinar-qa-2.png" width="400" height="184" alt="Q&A 2" />
|
<Image
|
||||||
|
src="/public/static/images/notes/bernie-sanders-bern-app-data/webinar-qa-2.png"
|
||||||
|
width="400"
|
||||||
|
height="184"
|
||||||
|
alt="Q&A 2"
|
||||||
|
/>
|
||||||
|
|
||||||
Defenders of the BERN app have pointed out that the information used is already available from public voter rolls maintained independently by each state. This is true. But these public records have never been tied to a campaign's internal voter files through a tool that's wide open to the entire internet, with incentives to add valuable data that benefits one candidate.
|
Defenders of the BERN app have pointed out that the information used is already available from public voter rolls maintained independently by each state. This is true. But these public records have never been tied to a campaign's internal voter files through a tool that's wide open to the entire internet, with incentives to add valuable data that benefits one candidate.
|
||||||
|
|
||||||
There were even unverified claims that [BERN was leaking voter ID numbers](https://info.idagent.com/blog/bern-app-exposes-150m-voter-records), which are the same as one's driver's license ID numbers in some states, through JSON responses in the first few days after its release. There don't be appear to be strict rate limits on calls to the API either, potentially inviting malicious actors from around the world — wink wink — to scrape personal data on tens of millions of Americans en masse.
|
There were even unverified claims that [BERN was leaking voter ID numbers](https://info.idagent.com/blog/bern-app-exposes-150m-voter-records), which are the same as one's driver's license ID numbers in some states, through JSON responses in the first few days after its release. There don't be appear to be strict rate limits on calls to the API either, potentially inviting malicious actors from around the world — wink wink — to scrape personal data on tens of millions of Americans en masse.
|
||||||
|
|
||||||
<Figure src="/static/images/notes/bernie-sanders-bern-app-data/json-response.jpg" width="865" height="369">
|
<Figure src="/public/static/images/notes/bernie-sanders-bern-app-data/json-response.jpg" width="865" height="369">
|
||||||
BERN's API response in Chrome DevTools
|
BERN's API response in Chrome DevTools
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -86,14 +80,14 @@ Others have noted that web-based organizing tools like BERN have been used by ca
|
|||||||
|
|
||||||
But the latter category of databases — like [NationBuilder](https://nationbuilder.com/) and, more notably, [NGP VAN's VoteBuilder](https://act.ngpvan.com/votebuilder) software based on the Obama campaign's inventions and now used by almost all Democratic campaigns across the United States — are secured and strictly guarded. Volunteer accounts need to be created and approved by paid campaign organizers and are locked down to provide the bare minimum amount of information necessary for one to canvass or phone bank a shortlist of voters. Every single click is also recorded in a [detailed log](sanders-campaign-audit.pdf) down to the millisecond. (This is how [Bernie's organizers got busted](https://time.com/4155185/bernie-sanders-hillary-clinton-data/) snooping around Hillary's VoteBuilder data last cycle, by the way.)
|
But the latter category of databases — like [NationBuilder](https://nationbuilder.com/) and, more notably, [NGP VAN's VoteBuilder](https://act.ngpvan.com/votebuilder) software based on the Obama campaign's inventions and now used by almost all Democratic campaigns across the United States — are secured and strictly guarded. Volunteer accounts need to be created and approved by paid campaign organizers and are locked down to provide the bare minimum amount of information necessary for one to canvass or phone bank a shortlist of voters. Every single click is also recorded in a [detailed log](sanders-campaign-audit.pdf) down to the millisecond. (This is how [Bernie's organizers got busted](https://time.com/4155185/bernie-sanders-hillary-clinton-data/) snooping around Hillary's VoteBuilder data last cycle, by the way.)
|
||||||
|
|
||||||
<Figure src="/static/images/notes/bernie-sanders-bern-app-data/votebuilder-audit.png" width="750" height="447">
|
<Figure src="/public/static/images/notes/bernie-sanders-bern-app-data/votebuilder-audit.png" width="750" height="447">
|
||||||
[NGP VAN's audit of the Sanders campaign's VoteBuilder
|
[NGP VAN's audit of the Sanders campaign's VoteBuilder
|
||||||
activity](/static/images/notes/bernie-sanders-bern-app-data/sanders-campaign-audit.pdf)
|
activity](/static/images/notes/bernie-sanders-bern-app-data/sanders-campaign-audit.pdf)
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
BERN is taking this to an unprecedented level. Allowing anybody on the internet to sign up and add others' personal information to the campaign's database without their knowledge is troubling, especially when you consider the gamified "points" system they've added as an incentive to report as much information on as many people as possible.
|
BERN is taking this to an unprecedented level. Allowing anybody on the internet to sign up and add others' personal information to the campaign's database without their knowledge is troubling, especially when you consider the gamified "points" system they've added as an incentive to report as much information on as many people as possible.
|
||||||
|
|
||||||
<Figure src="/static/images/notes/bernie-sanders-bern-app-data/reddit-bros.png" width="600" height="301">
|
<Figure src="/public/static/images/notes/bernie-sanders-bern-app-data/reddit-bros.png" width="600" height="301">
|
||||||
[BERN discussion on /r/SandersForPresident
|
[BERN discussion on /r/SandersForPresident
|
||||||
thread](https://www.reddit.com/r/SandersForPresident/comments/bi15la/new_get_the_official_bernie_sanders_2020_app_bern/elxi85m/)
|
thread](https://www.reddit.com/r/SandersForPresident/comments/bi15la/new_get_the_official_bernie_sanders_2020_app_bern/elxi85m/)
|
||||||
</Figure>
|
</Figure>
|
||||||
@@ -101,7 +95,7 @@ BERN is taking this to an unprecedented level. Allowing anybody on the internet
|
|||||||
In addition to the points system, it was revealed in the webinar mentioned above that the campaign is planning on giving out shiny rewards based on how many friends one adds, setting expectations at 50+ contacts to reach the "Bernie Super Bundler" tier — whatever that means.
|
In addition to the points system, it was revealed in the webinar mentioned above that the campaign is planning on giving out shiny rewards based on how many friends one adds, setting expectations at 50+ contacts to reach the "Bernie Super Bundler" tier — whatever that means.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/bernie-sanders-bern-app-data/webinar-slide-1.png"
|
src="/public/static/images/notes/bernie-sanders-bern-app-data/webinar-slide-1.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="451"
|
height="451"
|
||||||
alt="Webinar Slide 1"
|
alt="Webinar Slide 1"
|
||||||
@@ -110,7 +104,7 @@ In addition to the points system, it was revealed in the webinar mentioned above
|
|||||||
In the middle of the webinar, the organizer also paused the presentation for _fifteen minutes_ — complete with a countdown clock — and told volunteers to race to add as many of their friends as possible in that time. She announced afterwards that participants added 20 to 40 friends into the app on average, with some allegedly adding close to 100 in fifteen minutes.
|
In the middle of the webinar, the organizer also paused the presentation for _fifteen minutes_ — complete with a countdown clock — and told volunteers to race to add as many of their friends as possible in that time. She announced afterwards that participants added 20 to 40 friends into the app on average, with some allegedly adding close to 100 in fifteen minutes.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/bernie-sanders-bern-app-data/webinar-slide-2.png"
|
src="/public/static/images/notes/bernie-sanders-bern-app-data/webinar-slide-2.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="451"
|
height="451"
|
||||||
alt="Webinar Slide 2"
|
alt="Webinar Slide 2"
|
||||||
|
@@ -11,13 +11,10 @@ tags:
|
|||||||
image: "/static/images/notes/cloudflare-dns-archive-is-blocked/archive-is.png"
|
image: "/static/images/notes/cloudflare-dns-archive-is-blocked/archive-is.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Tweet from "../components/media/Tweet";
|
|
||||||
|
|
||||||
**tl;dr:** No. Quite the opposite, actually — [Archive.is](https://archive.is/)'s owner is intentionally blocking 1.1.1.1 users.
|
**tl;dr:** No. Quite the opposite, actually — [Archive.is](https://archive.is/)'s owner is intentionally blocking 1.1.1.1 users.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/cloudflare-dns-archive-is-blocked/archive-is.png"
|
src="/public/static/images/notes/cloudflare-dns-archive-is-blocked/archive-is.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="180"
|
height="180"
|
||||||
alt="Archive.today screenshot"
|
alt="Archive.today screenshot"
|
||||||
|
@@ -12,10 +12,8 @@ tags:
|
|||||||
image: "/static/images/notes/cool-bash-tricks-for-your-terminal-dotfiles/terminal.png"
|
image: "/static/images/notes/cool-bash-tricks-for-your-terminal-dotfiles/terminal.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/cool-bash-tricks-for-your-terminal-dotfiles/terminal.png"
|
src="/public/static/images/notes/cool-bash-tricks-for-your-terminal-dotfiles/terminal.png"
|
||||||
width="320"
|
width="320"
|
||||||
height="284"
|
height="284"
|
||||||
alt="Terminal.app on macOS"
|
alt="Terminal.app on macOS"
|
||||||
|
@@ -11,159 +11,129 @@ tags:
|
|||||||
image: "/static/images/notes/coronavirus-open-source/covid19dashboards.png"
|
image: "/static/images/notes/coronavirus-open-source/covid19dashboards.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Video from "../components/media/Video";
|
|
||||||
|
|
||||||
export const Octocat = (props) => {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
className="no-underline"
|
|
||||||
href={`https://github.com/${props.repo}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
style={{ margin: "0 0.3em", color: "var(--text)" }}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
aria-hidden="true"
|
|
||||||
className="icon"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
We're all quickly learning that worldwide pandemics can bring out both [the best](https://www.vox.com/culture/2020/3/13/21179293/coronavirus-italy-covid19-music-balconies-sing) and [the worst](https://twitter.com/9NewsAUS/status/1236088663093608448) of humanity. But one thing has become readily apparent to me — outside of the large teams of medical professionals risking their lives right this minute, the open source community stands alone in its ability to rapidly organize in the midst of chaos to give back to the world and, in this case, make it safer for all of us.
|
We're all quickly learning that worldwide pandemics can bring out both [the best](https://www.vox.com/culture/2020/3/13/21179293/coronavirus-italy-covid19-music-balconies-sing) and [the worst](https://twitter.com/9NewsAUS/status/1236088663093608448) of humanity. But one thing has become readily apparent to me — outside of the large teams of medical professionals risking their lives right this minute, the open source community stands alone in its ability to rapidly organize in the midst of chaos to give back to the world and, in this case, make it safer for all of us.
|
||||||
|
|
||||||
These are just a few incredible open source projects that didn't exist a few months ago, but rapidly formed teams of dozens of contributors to fill both big needs and small niches in the fight to defeat the novel coronavirus, aka [**COVID-19**](https://www.cdc.gov/coronavirus/2019-nCoV/index.html).
|
These are just a few incredible open source projects that didn't exist a few months ago, but rapidly formed teams of dozens of contributors to fill both big needs and small niches in the fight to defeat the novel coronavirus, aka [**COVID-19**](https://www.cdc.gov/coronavirus/2019-nCoV/index.html).
|
||||||
|
|
||||||
## [The COVID Tracking Project](https://covidtracking.com/) <Octocat repo="COVID19Tracking/website" />
|
## [The COVID Tracking Project](https://covidtracking.com/) <OctocatLink repo="COVID19Tracking/website" />
|
||||||
|
|
||||||
Now that Americans are _finally_ starting to get tested for the coronavirus, information and statistics about the results are being released state-by-state, which has led to a scattering of primary sources across the web, each releasing [different figures in different forms](https://docs.google.com/document/d/1OyN6_1UeDePwPwKi6UKZB8GwNC7-kSf1-BO2af8kqVA/edit). The [COVID Tracking Project](https://covidtracking.com/) collects as much information as possible from each local health authority's website and puts everything together in [easy-to-digest tables](https://covidtracking.com/data/), as well as [spreadsheets](https://docs.google.com/spreadsheets/u/2/d/e/2PACX-1vRwAqp96T9sYYq2-i7Tj0pvTf6XVHjDSMIKBdZHXiCGGdNC0ypEU9NbngS8mxea55JuCFuua1MUeOj5/pubhtml) and a [public API](https://covidtracking.com/api/).
|
Now that Americans are _finally_ starting to get tested for the coronavirus, information and statistics about the results are being released state-by-state, which has led to a scattering of primary sources across the web, each releasing [different figures in different forms](https://docs.google.com/document/d/1OyN6_1UeDePwPwKi6UKZB8GwNC7-kSf1-BO2af8kqVA/edit). The [COVID Tracking Project](https://covidtracking.com/) collects as much information as possible from each local health authority's website and puts everything together in [easy-to-digest tables](https://covidtracking.com/data/), as well as [spreadsheets](https://docs.google.com/spreadsheets/u/2/d/e/2PACX-1vRwAqp96T9sYYq2-i7Tj0pvTf6XVHjDSMIKBdZHXiCGGdNC0ypEU9NbngS8mxea55JuCFuua1MUeOj5/pubhtml) and a [public API](https://covidtracking.com/api/).
|
||||||
|
|
||||||
The maintainers are also [fully transparent](https://covidtracking.com/about-tracker/) about their process and take great care to annotate individual figures with the methodology used to arrive at each, which has earned them the [trust](https://covidtracking.com/#press) of even the largest national news organizations reporting on COVID-19.
|
The maintainers are also [fully transparent](https://covidtracking.com/about-tracker/) about their process and take great care to annotate individual figures with the methodology used to arrive at each, which has earned them the [trust](https://covidtracking.com/#press) of even the largest national news organizations reporting on COVID-19.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/covidtracking.png"
|
src="/public/static/images/notes/coronavirus-open-source/covidtracking.png"
|
||||||
width="680"
|
width="680"
|
||||||
height="328"
|
height="328"
|
||||||
alt="The COVID Tracking Project"
|
alt="The COVID Tracking Project"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [#findthemasks](https://findthemasks.com/) <Octocat repo="r-pop/findthemasks" />
|
## [#findthemasks](https://findthemasks.com/) <OctocatLink repo="r-pop/findthemasks" />
|
||||||
|
|
||||||
This one might be my favorite, simply because of its laser-like focus on solving a very specific (yet catastrophic) problem. The United States is [already running out](https://www.nytimes.com/2020/03/19/health/coronavirus-masks-shortage.html) of [personal protective equipment (PPE)](https://www.fda.gov/medical-devices/general-hospital-devices-and-supplies/personal-protective-equipment-infection-control) for the healthcare professionals on the front lines of this crisis. [#findthemasks.com](https://findthemasks.com/) has gathered specific donation requests and points of contact from hospitals around the country in desperate need of basic supplies.
|
This one might be my favorite, simply because of its laser-like focus on solving a very specific (yet catastrophic) problem. The United States is [already running out](https://www.nytimes.com/2020/03/19/health/coronavirus-masks-shortage.html) of [personal protective equipment (PPE)](https://www.fda.gov/medical-devices/general-hospital-devices-and-supplies/personal-protective-equipment-infection-control) for the healthcare professionals on the front lines of this crisis. [#findthemasks.com](https://findthemasks.com/) has gathered specific donation requests and points of contact from hospitals around the country in desperate need of basic supplies.
|
||||||
|
|
||||||
_Please_ look up your local hospitals on [#findthemasks](https://findthemasks.com/#sites) and follow their instructions to donate anything you have hoarded — it's likely the single most impactful thing you can do at this point. If you don't see your local hospital, or don't feel comfortable shipping equipment to any hospital listed, you can also visit [PPE Link](https://ppelink.org/ppe-donations/) and they will connect you with hospitals in your area.
|
_Please_ look up your local hospitals on [#findthemasks](https://findthemasks.com/#sites) and follow their instructions to donate anything you have hoarded — it's likely the single most impactful thing you can do at this point. If you don't see your local hospital, or don't feel comfortable shipping equipment to any hospital listed, you can also visit [PPE Link](https://ppelink.org/ppe-donations/) and they will connect you with hospitals in your area.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/findthemasks.png"
|
src="/public/static/images/notes/coronavirus-open-source/findthemasks.png"
|
||||||
width="600"
|
width="600"
|
||||||
height="295"
|
height="295"
|
||||||
alt="#findthemasks"
|
alt="#findthemasks"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [#StayTheFuckHome](https://staythefuckhome.com/) <Octocat repo="flore2003/staythefuckhome" />
|
## [#StayTheFuckHome](https://staythefuckhome.com/) <OctocatLink repo="flore2003/staythefuckhome" />
|
||||||
|
|
||||||
I figured I'd throw in this cheeky website broadcasting a simple but serious message: **STAY THE FUCK HOME!!!** If you're _still_ not convinced of the importance of this "suggestion," give their ["Self-Quarantine Manifesto"](https://staythefuckhome.com/) a quick read. Now.
|
I figured I'd throw in this cheeky website broadcasting a simple but serious message: **STAY THE FUCK HOME!!!** If you're _still_ not convinced of the importance of this "suggestion," give their ["Self-Quarantine Manifesto"](https://staythefuckhome.com/) a quick read. Now.
|
||||||
|
|
||||||
The [GitHub community](https://github.com/flore2003/staythefuckhome/pulls?q=is%3Apr) has translated the instructional essay into over a dozen different languages — including a [safe-for-work version](https://staythefuckhome.com/sfw/), if that helps — and they're [looking for more translators](https://github.com/flore2003/staythefuckhome#contributing) if you're multilingual and need something besides Netflix to fill your time with while you **_stay the fuck home!_** 😉
|
The [GitHub community](https://github.com/flore2003/staythefuckhome/pulls?q=is%3Apr) has translated the instructional essay into over a dozen different languages — including a [safe-for-work version](https://staythefuckhome.com/sfw/), if that helps — and they're [looking for more translators](https://github.com/flore2003/staythefuckhome#contributing) if you're multilingual and need something besides Netflix to fill your time with while you **_stay the fuck home!_** 😉
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/staythefuckhome.png"
|
src="/public/static/images/notes/coronavirus-open-source/staythefuckhome.png"
|
||||||
width="600"
|
width="600"
|
||||||
height="215"
|
height="215"
|
||||||
alt="#StayTheFuckHome"
|
alt="#StayTheFuckHome"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [COVID-19 Dashboards](https://covid19dashboards.com/) <Octocat repo="github/covid19-dashboard" />
|
## [COVID-19 Dashboards](https://covid19dashboards.com/) <OctocatLink repo="github/covid19-dashboard" />
|
||||||
|
|
||||||
This collection of various visualizations is fascinating (and sobering) to look at. If you're smarter than I am and have experience in data analysis, their team (led by a [GitHub engineer](https://github.com/hamelsmu)) would be more than happy to [add your contribution](https://github.com/github/covid19-dashboard/blob/master/CONTRIBUTING.md) to the site — they're using [Jupyter Notebooks](https://jupyter.org/) and [fastpages](https://github.com/fastai/fastpages).
|
This collection of various visualizations is fascinating (and sobering) to look at. If you're smarter than I am and have experience in data analysis, their team (led by a [GitHub engineer](https://github.com/hamelsmu)) would be more than happy to [add your contribution](https://github.com/github/covid19-dashboard/blob/master/CONTRIBUTING.md) to the site — they're using [Jupyter Notebooks](https://jupyter.org/) and [fastpages](https://github.com/fastai/fastpages).
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/covid19dashboards.png"
|
src="/public/static/images/notes/coronavirus-open-source/covid19dashboards.png"
|
||||||
width="580"
|
width="580"
|
||||||
height="442"
|
height="442"
|
||||||
alt="COVID-19 Dashboards"
|
alt="COVID-19 Dashboards"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [CoronaTracker](https://coronatracker.samabox.com/) <Octocat repo="MhdHejazi/CoronaTracker" />
|
## [CoronaTracker](https://coronatracker.samabox.com/) <OctocatLink repo="MhdHejazi/CoronaTracker" />
|
||||||
|
|
||||||
CoronaTracker is a _beautiful_ cross-platform app for iOS and macOS with intuitive maps and charts fed by reputable live data. Apple is [being justifiably picky](https://developer.apple.com/news/?id=03142020a) about "non-official" Coronavirus apps in their App Store ([so is Google](https://blog.google/inside-google/company-announcements/coronavirus-covid19-response/), by the way) but you can still [download the macOS app directly](https://coronatracker.samabox.com/) or [compile the iOS source code](https://github.com/MhdHejazi/CoronaTracker#1-ios-app) yourself using Xcode if you wish.
|
CoronaTracker is a _beautiful_ cross-platform app for iOS and macOS with intuitive maps and charts fed by reputable live data. Apple is [being justifiably picky](https://developer.apple.com/news/?id=03142020a) about "non-official" Coronavirus apps in their App Store ([so is Google](https://blog.google/inside-google/company-announcements/coronavirus-covid19-response/), by the way) but you can still [download the macOS app directly](https://coronatracker.samabox.com/) or [compile the iOS source code](https://github.com/MhdHejazi/CoronaTracker#1-ios-app) yourself using Xcode if you wish.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/coronatracker.png"
|
src="/public/static/images/notes/coronavirus-open-source/coronatracker.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="417"
|
height="417"
|
||||||
alt="CoronaTracker"
|
alt="CoronaTracker"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [Staying Home Club](https://stayinghome.club/) <Octocat repo="phildini/stayinghomeclub" />
|
## [Staying Home Club](https://stayinghome.club/) <OctocatLink repo="phildini/stayinghomeclub" />
|
||||||
|
|
||||||
A bit more family-friendly than [#StayTheFuckHome](https://staythefuckhome.com/), the [Staying Home Club](https://stayinghome.club/) is maintaining a running list of over a thousand companies and universities mandating that employees and students work from home, as well as events that have been canceled or moved online. Quarantining yourself might feel lonely, but here's solid proof that you're far from alone right now.
|
A bit more family-friendly than [#StayTheFuckHome](https://staythefuckhome.com/), the [Staying Home Club](https://stayinghome.club/) is maintaining a running list of over a thousand companies and universities mandating that employees and students work from home, as well as events that have been canceled or moved online. Quarantining yourself might feel lonely, but here's solid proof that you're far from alone right now.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/stayinghome.png"
|
src="/public/static/images/notes/coronavirus-open-source/stayinghome.png"
|
||||||
width="600"
|
width="600"
|
||||||
height="137"
|
height="137"
|
||||||
alt="Staying Home Club"
|
alt="Staying Home Club"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [Nextstrain for nCoV](https://nextstrain.org/ncov) <Octocat repo="nextstrain/ncov" />
|
## [Nextstrain for nCoV](https://nextstrain.org/ncov) <OctocatLink repo="nextstrain/ncov" />
|
||||||
|
|
||||||
This one is a bit over my head, but apparently [Nextstrain](https://nextstrain.org/) is a pretty impressive open-source service targeted at genome data analysis and visualization of different pathogens. Their [COVID-19 page](https://nextstrain.org/ncov) is still awe-inspiring to look at for a layman like me, but probably a thousand times more so if you're an actual scientist — in which case, the [genome data they've open-sourced](https://github.com/nextstrain/ncov) might be of interest to you.
|
This one is a bit over my head, but apparently [Nextstrain](https://nextstrain.org/) is a pretty impressive open-source service targeted at genome data analysis and visualization of different pathogens. Their [COVID-19 page](https://nextstrain.org/ncov) is still awe-inspiring to look at for a layman like me, but probably a thousand times more so if you're an actual scientist — in which case, the [genome data they've open-sourced](https://github.com/nextstrain/ncov) might be of interest to you.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/nextstrain.png"
|
src="/public/static/images/notes/coronavirus-open-source/nextstrain.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="345"
|
height="345"
|
||||||
alt="Nextstrain for nCOV"
|
alt="Nextstrain for nCOV"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [Johns Hopkins 2019-nCoV Data](https://systems.jhu.edu/research/public-health/ncov/) <Octocat repo="CSSEGISandData/COVID-19" />
|
## [Johns Hopkins 2019-nCoV Data](https://systems.jhu.edu/research/public-health/ncov/) <OctocatLink repo="CSSEGISandData/COVID-19" />
|
||||||
|
|
||||||
Johns Hopkins University's [visual COVID-19 global dashboard](https://www.arcgis.com/apps/opsdashboard/index.html#/bda7594740fd40299423467b48e9ecf6) has been bookmarked as my go-to source of information since the beginning of this crisis earlier this year. Now, JHU's [Center for Systems Science and Engineering](https://systems.jhu.edu/) has open-sourced [their data and analysis](https://github.com/CSSEGISandData/COVID-19) for anybody to use.
|
Johns Hopkins University's [visual COVID-19 global dashboard](https://www.arcgis.com/apps/opsdashboard/index.html#/bda7594740fd40299423467b48e9ecf6) has been bookmarked as my go-to source of information since the beginning of this crisis earlier this year. Now, JHU's [Center for Systems Science and Engineering](https://systems.jhu.edu/) has open-sourced [their data and analysis](https://github.com/CSSEGISandData/COVID-19) for anybody to use.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/hopkins.png"
|
src="/public/static/images/notes/coronavirus-open-source/hopkins.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="426"
|
height="426"
|
||||||
alt="Johns Hopkins 2019-nCoV Data"
|
alt="Johns Hopkins 2019-nCoV Data"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [COVID-19 Scenarios](https://neherlab.org/covid19/) <Octocat repo="neherlab/covid19_scenarios" />
|
## [COVID-19 Scenarios](https://neherlab.org/covid19/) <OctocatLink repo="neherlab/covid19_scenarios" />
|
||||||
|
|
||||||
COVID-19 Scenarios will probably hit everyone in a different way, depending on your levels of optimism and/or pessimism right now. It uses [advanced scientific models](https://neherlab.org/covid19/about) to predict the future of the virus based on past data and future variables and assumptions you can tinker with yourself.
|
COVID-19 Scenarios will probably hit everyone in a different way, depending on your levels of optimism and/or pessimism right now. It uses [advanced scientific models](https://neherlab.org/covid19/about) to predict the future of the virus based on past data and future variables and assumptions you can tinker with yourself.
|
||||||
|
|
||||||
The maintainers at the [Neher Lab in Basel, Switzerland](https://neherlab.org/) even have a [discussion thread](https://github.com/neherlab/covid19_scenarios/issues/18) and an [open chatroom](https://spectrum.chat/covid19-scenarios/general/questions-discussions~8d49f461-a890-4beb-84f7-2d6ed0ae503a) set up for both scientists and non-scientists to ask questions and post ideas, which I find really nice of them!
|
The maintainers at the [Neher Lab in Basel, Switzerland](https://neherlab.org/) even have a [discussion thread](https://github.com/neherlab/covid19_scenarios/issues/18) and an [open chatroom](https://spectrum.chat/covid19-scenarios/general/questions-discussions~8d49f461-a890-4beb-84f7-2d6ed0ae503a) set up for both scientists and non-scientists to ask questions and post ideas, which I find really nice of them!
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/scenarios.png"
|
src="/public/static/images/notes/coronavirus-open-source/scenarios.png"
|
||||||
width="740"
|
width="740"
|
||||||
height="433"
|
height="433"
|
||||||
alt="COVID-19 Scenarios"
|
alt="COVID-19 Scenarios"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [Corona Data Scraper](https://coronadatascraper.com/#home) <Octocat repo="lazd/coronadatascraper" />
|
## [Corona Data Scraper](https://coronadatascraper.com/#home) <OctocatLink repo="lazd/coronadatascraper" />
|
||||||
|
|
||||||
Similar to the [COVID Tracking Project](https://covidtracking.com/) above, the [Corona Data Scraper](https://coronadatascraper.com/#home) has set up an automated process to scrape verified data from across the web to form massive CSV spreadsheets and JSON objects. They even [rate the quality](https://github.com/lazd/coronadatascraper#source-rating) of each source to prioritize data accordingly.
|
Similar to the [COVID Tracking Project](https://covidtracking.com/) above, the [Corona Data Scraper](https://coronadatascraper.com/#home) has set up an automated process to scrape verified data from across the web to form massive CSV spreadsheets and JSON objects. They even [rate the quality](https://github.com/lazd/coronadatascraper#source-rating) of each source to prioritize data accordingly.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/coronadatascraper.png"
|
src="/public/static/images/notes/coronavirus-open-source/coronadatascraper.png"
|
||||||
width="750"
|
width="750"
|
||||||
height="358"
|
height="358"
|
||||||
alt="Corona Data Scraper"
|
alt="Corona Data Scraper"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [Folding@home](https://foldingathome.org/covid19/) <Octocat repo="FoldingAtHome/coronavirus" />
|
## [Folding@home](https://foldingathome.org/covid19/) <OctocatLink repo="FoldingAtHome/coronavirus" />
|
||||||
|
|
||||||
[Folding@home](https://foldingathome.org/) has been around [_forever_](https://en.wikipedia.org/wiki/Folding@home). I remember installing it on my family's home computer as a curious kid and making my father infuriated over how slow it got. But they [switched gears this month](https://foldingathome.org/2020/03/15/coronavirus-what-were-doing-and-how-you-can-help-in-simple-terms/) from using our computers to crunch various proteins and molecules in the background, and all of their power is now going towards discovering unknown "folds" in the coronavirus, which might be able to lead scientists to find better cures and potential vaccines.
|
[Folding@home](https://foldingathome.org/) has been around [_forever_](https://en.wikipedia.org/wiki/Folding@home). I remember installing it on my family's home computer as a curious kid and making my father infuriated over how slow it got. But they [switched gears this month](https://foldingathome.org/2020/03/15/coronavirus-what-were-doing-and-how-you-can-help-in-simple-terms/) from using our computers to crunch various proteins and molecules in the background, and all of their power is now going towards discovering unknown "folds" in the coronavirus, which might be able to lead scientists to find better cures and potential vaccines.
|
||||||
|
|
||||||
@@ -172,30 +142,18 @@ You can [download their software here](https://foldingathome.org/start-folding/)
|
|||||||
**Fun fact:** The team behind Folding@home has seen a [**huge** spike in computational power](https://www.reddit.com/r/pcmasterrace/comments/flgm7q/ama_with_the_team_behind_foldinghome_coronavirus/) this month after cryptominers started mining coronavirus proteins instead of boring, old Ethereum with their insanely overpowered GPUs! 👏
|
**Fun fact:** The team behind Folding@home has seen a [**huge** spike in computational power](https://www.reddit.com/r/pcmasterrace/comments/flgm7q/ama_with_the_team_behind_foldinghome_coronavirus/) this month after cryptominers started mining coronavirus proteins instead of boring, old Ethereum with their insanely overpowered GPUs! 👏
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/notes/coronavirus-open-source/folding.webm"
|
||||||
{ src: "/static/images/notes/coronavirus-open-source/folding.webm", type: "video/webm" },
|
mp4="/static/images/notes/coronavirus-open-source/folding.mp4"
|
||||||
{ src: "/static/images/notes/coronavirus-open-source/folding.mp4", type: "video/mp4" },
|
thumbnail="/static/images/notes/coronavirus-open-source/folding-thumb.png"
|
||||||
]}
|
autoplay
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: "/static/images/notes/coronavirus-open-source/folding-thumb.png",
|
|
||||||
autoPlay: true,
|
|
||||||
loop: true,
|
|
||||||
volume: 0,
|
|
||||||
muted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={false}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## [Coronavirus Tracker API](https://coronavirus-tracker-api.herokuapp.com/v2/locations) <Octocat repo="ExpDev07/coronavirus-tracker-api" />
|
## [Coronavirus Tracker API](https://coronavirus-tracker-api.herokuapp.com/v2/locations) <OctocatLink repo="ExpDev07/coronavirus-tracker-api" />
|
||||||
|
|
||||||
To wrap this list up, I thought I'd include [yet another API](https://github.com/ExpDev07/coronavirus-tracker-api) fed by multiple data sources that you can use to create your own open-source project if any of these inspired you. This one is incredibly flexible in terms of [query parameters and endpoints](https://github.com/ExpDev07/coronavirus-tracker-api#api-endpoints) but they all return simple JSON responses like we all know and love.
|
To wrap this list up, I thought I'd include [yet another API](https://github.com/ExpDev07/coronavirus-tracker-api) fed by multiple data sources that you can use to create your own open-source project if any of these inspired you. This one is incredibly flexible in terms of [query parameters and endpoints](https://github.com/ExpDev07/coronavirus-tracker-api#api-endpoints) but they all return simple JSON responses like we all know and love.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/coronavirus-open-source/tracker-api.png"
|
src="/public/static/images/notes/coronavirus-open-source/tracker-api.png"
|
||||||
width="712"
|
width="712"
|
||||||
height="371"
|
height="371"
|
||||||
alt="Coronavirus Tracker API"
|
alt="Coronavirus Tracker API"
|
||||||
|
@@ -11,14 +11,9 @@ tags:
|
|||||||
image: "/static/images/notes/dropping-dropbox/email.png"
|
image: "/static/images/notes/dropping-dropbox/email.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
import Tweet from "../components/media/Tweet";
|
|
||||||
import Video from "../components/media/Video";
|
|
||||||
|
|
||||||
I've been a loyal Dropbox user since its inception as a [Y Combinator startup](https://www.ycombinator.com/apply/dropbox/) ten years ago. Having a folder on all of my devices that instantly synchronized with each other was a game-changer for me, and I grew dependent on it more and more as they gave out free storage like candy — 48 GB for having a Samsung Chromebook, 1 GB for "Posting \<3 to Twitter," and so on — until I needed to upgrade to Dropbox Pro. But this month I canceled my Pro subscription after a few too many strikes.
|
I've been a loyal Dropbox user since its inception as a [Y Combinator startup](https://www.ycombinator.com/apply/dropbox/) ten years ago. Having a folder on all of my devices that instantly synchronized with each other was a game-changer for me, and I grew dependent on it more and more as they gave out free storage like candy — 48 GB for having a Samsung Chromebook, 1 GB for "Posting \<3 to Twitter," and so on — until I needed to upgrade to Dropbox Pro. But this month I canceled my Pro subscription after a few too many strikes.
|
||||||
|
|
||||||
<Figure src="/static/images/notes/dropping-dropbox/email.png" width="504" height="223" priority>
|
<Figure src="/public/static/images/notes/dropping-dropbox/email.png" width="504" height="223" priority>
|
||||||
Deleting 401,907 files from Dropbox... 😬
|
Deleting 401,907 files from Dropbox... 😬
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -36,21 +31,9 @@ Decisions made by the top folks at Dropbox gave me an increasingly sour taste in
|
|||||||
- ...and as a bonus, making the process of canceling Dropbox Pro incredibly convoluted, annoying, and sketchy. Here's a video demonstration via [Justin Dunham](https://twitter.com/jwyattd):
|
- ...and as a bonus, making the process of canceling Dropbox Pro incredibly convoluted, annoying, and sketchy. Here's a video demonstration via [Justin Dunham](https://twitter.com/jwyattd):
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/notes/dropping-dropbox/cancel.webm"
|
||||||
{ src: "/static/images/notes/dropping-dropbox/cancel.webm", type: "video/webm" },
|
mp4="/static/images/notes/dropping-dropbox/cancel.mp4"
|
||||||
{ src: "/static/images/notes/dropping-dropbox/cancel.mp4", type: "video/mp4" },
|
thumbnail="/static/images/notes/dropping-dropbox/cancel.png"
|
||||||
]}
|
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: "/static/images/notes/dropping-dropbox/cancel.png",
|
|
||||||
controlsList: "nodownload",
|
|
||||||
preload: "metadata",
|
|
||||||
autoPlay: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={true}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## Seeking an alternative...
|
## Seeking an alternative...
|
||||||
@@ -58,7 +41,7 @@ Decisions made by the top folks at Dropbox gave me an increasingly sour taste in
|
|||||||
The infamous [Apple Ecosystem™](https://medium.com/swlh/the-irresistible-lure-of-the-apple-ecosystem-81bf8d66294a) has held me firmly in its grasp for over a decade now, and the main requirement of a replacement cloud storage service for me was smooth interoperability between my MacBook, iPhone, and iPad.
|
The infamous [Apple Ecosystem™](https://medium.com/swlh/the-irresistible-lure-of-the-apple-ecosystem-81bf8d66294a) has held me firmly in its grasp for over a decade now, and the main requirement of a replacement cloud storage service for me was smooth interoperability between my MacBook, iPhone, and iPad.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/dropping-dropbox/icloud-storage.png"
|
src="/public/static/images/notes/dropping-dropbox/icloud-storage.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="137"
|
height="137"
|
||||||
alt="iCloud Drive storage"
|
alt="iCloud Drive storage"
|
||||||
@@ -66,7 +49,12 @@ The infamous [Apple Ecosystem™](https://medium.com/swlh/the-irresistible-lure-
|
|||||||
|
|
||||||
I've never been a proponent of leaving all your eggs in one basket. But it's hard to ignore the convenience of Apple's streamlined (and [finally](https://www.imore.com/developers-encounter-major-icloud-issues-ios-13-beta) reliable) [**iCloud Drive**](https://www.apple.com/icloud/), which is already installed on all of my devices (and actually cheaper than Dropbox gigabyte-for-gigabyte, at \$9.99/month for 2 TB). In fact, it's nearly invisible on macOS: I can simply save files in my Documents or Desktop folders as I always have and they're uploaded in the background. Git repositories now sync just fine and my files reappeared without a hitch after I recently formatted my Mac.
|
I've never been a proponent of leaving all your eggs in one basket. But it's hard to ignore the convenience of Apple's streamlined (and [finally](https://www.imore.com/developers-encounter-major-icloud-issues-ios-13-beta) reliable) [**iCloud Drive**](https://www.apple.com/icloud/), which is already installed on all of my devices (and actually cheaper than Dropbox gigabyte-for-gigabyte, at \$9.99/month for 2 TB). In fact, it's nearly invisible on macOS: I can simply save files in my Documents or Desktop folders as I always have and they're uploaded in the background. Git repositories now sync just fine and my files reappeared without a hitch after I recently formatted my Mac.
|
||||||
|
|
||||||
<Image src="/static/images/notes/dropping-dropbox/icloud-drive.png" width="680" height="423" alt="iCloud Drive" />
|
<Image
|
||||||
|
src="/public/static/images/notes/dropping-dropbox/icloud-drive.png"
|
||||||
|
width="680"
|
||||||
|
height="423"
|
||||||
|
alt="iCloud Drive"
|
||||||
|
/>
|
||||||
|
|
||||||
I still use (and highly recommend) [**Backblaze**](https://www.backblaze.com/) ([referral link](https://secure.backblaze.com/r/00x84e)) to backup my home folder and add a second layer of redundancy to storing all of my most important files on ["someone else's computer."](https://www.zdnet.com/article/stop-saying-the-cloud-is-just-someone-elses-computer-because-its-not/) And as long as I remember to plug in my external SSD every so often, they're also backed up locally via [Time Machine](https://support.apple.com/en-us/HT201250).
|
I still use (and highly recommend) [**Backblaze**](https://www.backblaze.com/) ([referral link](https://secure.backblaze.com/r/00x84e)) to backup my home folder and add a second layer of redundancy to storing all of my most important files on ["someone else's computer."](https://www.zdnet.com/article/stop-saying-the-cloud-is-just-someone-elses-computer-because-its-not/) And as long as I remember to plug in my external SSD every so often, they're also backed up locally via [Time Machine](https://support.apple.com/en-us/HT201250).
|
||||||
|
|
||||||
|
@@ -11,14 +11,12 @@ tags:
|
|||||||
image: "/static/images/notes/finding-candidates-subdomain-takeovers/hackerone-2.png"
|
image: "/static/images/notes/finding-candidates-subdomain-takeovers/hackerone-2.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
|
|
||||||
A **subdomain takeover** occurs when a subdomain (like _example_.jarv.is) points to a shared hosting account that is abandoned by its owner, leaving the endpoint available to claim for yourself.
|
A **subdomain takeover** occurs when a subdomain (like _example_.jarv.is) points to a shared hosting account that is abandoned by its owner, leaving the endpoint available to claim for yourself.
|
||||||
|
|
||||||
Not only are takeovers a fun way to dip your toes into [penetration testing](https://www.cloudflare.com/learning/security/glossary/what-is-penetration-testing/), but they can also be incredibly lucrative thanks to [bug bounty programs](https://en.wikipedia.org/wiki/Bug_bounty_program) on services like [HackerOne](https://hackerone.com/hacktivity?order_direction=DESC&order_field=popular&filter=type%3Aall&querystring=subdomain%20takeover) and [Bugcrowd](https://bugcrowd.com/programs), where corporations pay pentesters for their discoveries.
|
Not only are takeovers a fun way to dip your toes into [penetration testing](https://www.cloudflare.com/learning/security/glossary/what-is-penetration-testing/), but they can also be incredibly lucrative thanks to [bug bounty programs](https://en.wikipedia.org/wiki/Bug_bounty_program) on services like [HackerOne](https://hackerone.com/hacktivity?order_direction=DESC&order_field=popular&filter=type%3Aall&querystring=subdomain%20takeover) and [Bugcrowd](https://bugcrowd.com/programs), where corporations pay pentesters for their discoveries.
|
||||||
|
|
||||||
<Figure
|
<Figure
|
||||||
src="/static/images/notes/finding-candidates-subdomain-takeovers/hackerone-2.png"
|
src="/public/static/images/notes/finding-candidates-subdomain-takeovers/hackerone-2.png"
|
||||||
width="620"
|
width="620"
|
||||||
height="347"
|
height="347"
|
||||||
priority
|
priority
|
||||||
|
@@ -11,12 +11,8 @@ tags:
|
|||||||
image: "/static/images/notes/github-actions/actions-flow.png"
|
image: "/static/images/notes/github-actions/actions-flow.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
import Gist from "../components/media/Gist";
|
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/github-actions/actions-flow.png"
|
src="/public/static/images/notes/github-actions/actions-flow.png"
|
||||||
width="780"
|
width="780"
|
||||||
height="322"
|
height="322"
|
||||||
alt="Example workflow for a GitHub Action"
|
alt="Example workflow for a GitHub Action"
|
||||||
@@ -27,7 +23,7 @@ Since being accepted into the beta for [GitHub Actions](https://github.com/featu
|
|||||||
|
|
||||||
My favorite so far is my [Lighthouse Audit action](https://github.com/jakejarvis/lighthouse-action), which spins up a headless Google Chrome instance in an Ubuntu container and runs [Google's Lighthouse tool](https://developers.google.com/web/tools/lighthouse), which scores webpages on performance, accessibility, SEO, etc. and provides actual suggestions to improve them. It's a perfect example of the power of combining containers with Git workflows.
|
My favorite so far is my [Lighthouse Audit action](https://github.com/jakejarvis/lighthouse-action), which spins up a headless Google Chrome instance in an Ubuntu container and runs [Google's Lighthouse tool](https://developers.google.com/web/tools/lighthouse), which scores webpages on performance, accessibility, SEO, etc. and provides actual suggestions to improve them. It's a perfect example of the power of combining containers with Git workflows.
|
||||||
|
|
||||||
<Figure src="/static/images/notes/github-actions/lighthouse-output.png" width="750" height="297">
|
<Figure src="/public/static/images/notes/github-actions/lighthouse-output.png" width="750" height="297">
|
||||||
The results of a Lighthouse audit on this website, after running tests in a headless Google Chrome.
|
The results of a Lighthouse audit on this website, after running tests in a headless Google Chrome.
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -62,7 +58,7 @@ Using an action is also surprisingly simple, and more intuitive than [Travis CI]
|
|||||||
For a more complex example, when I forked [Hugo](https://github.com/gohugoio/hugo) (the static site generator used to build this website) to make some small personalized changes, I also translated [their `.travis.yml` file](https://github.com/gohugoio/hugo/blob/master/.travis.yml) into a [`workflow.yml` file](https://github.com/jakejarvis/hugo-custom/blob/master/.github/workflows/workflow.yml) for practice, which simultaneously runs comprehensive unit tests on **three operating systems** (Ubuntu 18.04, Windows 10, and macOS 10.14) with the latest two Go versions _each!_ If the tests are all successful, it builds a Docker image and pushes it to both [Docker Hub](https://hub.docker.com/r/jakejarvis/hugo-custom) and the [GitHub Package Registry](https://github.com/jakejarvis/hugo-custom/packages) (also [in beta](https://github.com/features/package-registry)).
|
For a more complex example, when I forked [Hugo](https://github.com/gohugoio/hugo) (the static site generator used to build this website) to make some small personalized changes, I also translated [their `.travis.yml` file](https://github.com/gohugoio/hugo/blob/master/.travis.yml) into a [`workflow.yml` file](https://github.com/jakejarvis/hugo-custom/blob/master/.github/workflows/workflow.yml) for practice, which simultaneously runs comprehensive unit tests on **three operating systems** (Ubuntu 18.04, Windows 10, and macOS 10.14) with the latest two Go versions _each!_ If the tests are all successful, it builds a Docker image and pushes it to both [Docker Hub](https://hub.docker.com/r/jakejarvis/hugo-custom) and the [GitHub Package Registry](https://github.com/jakejarvis/hugo-custom/packages) (also [in beta](https://github.com/features/package-registry)).
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/github-actions/hugo-logs.png"
|
src="/public/static/images/notes/github-actions/hugo-logs.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="418"
|
height="418"
|
||||||
alt="Build logs for my Hugo fork"
|
alt="Build logs for my Hugo fork"
|
||||||
|
@@ -12,11 +12,8 @@ tags:
|
|||||||
image: "/static/images/notes/github-rename-master/github-default.png"
|
image: "/static/images/notes/github-rename-master/github-default.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Tweet from "../components/media/Tweet";
|
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/github-rename-master/blm-topic.png"
|
src="/public/static/images/notes/github-rename-master/blm-topic.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="162"
|
height="162"
|
||||||
alt="Black lives matter."
|
alt="Black lives matter."
|
||||||
@@ -63,7 +60,7 @@ You can verify this worked by running `git branch -r`. You should see something
|
|||||||
Setting the default branch remotely is the only step that can't be done on the command line (although you can technically [use the GitHub API](https://github.com/erbridge/github-branch-renamer)). Head to **Settings → Branches** on GitHub to [change the default branch](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-base-branch-of-a-pull-request).
|
Setting the default branch remotely is the only step that can't be done on the command line (although you can technically [use the GitHub API](https://github.com/erbridge/github-branch-renamer)). Head to **Settings → Branches** on GitHub to [change the default branch](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-base-branch-of-a-pull-request).
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/github-rename-master/github-default.png"
|
src="/public/static/images/notes/github-rename-master/github-default.png"
|
||||||
width="810"
|
width="810"
|
||||||
height="405"
|
height="405"
|
||||||
alt="Changing the default branch of a GitHub repository"
|
alt="Changing the default branch of a GitHub repository"
|
||||||
@@ -86,7 +83,7 @@ Do a quick search of your codebase for `master` to manually replace any dead ref
|
|||||||
Pay attention to CI files — `.travis.yml`, `.github/workflows/`, `.circleci/config.yml`, etc. — and make sure there aren't any external services relying on `master` being there. For example, I almost forgot to change the branch [Netlify triggers auto-deploys](https://docs.netlify.com/site-deploys/overview/#branches-and-deploys) from to build this site:
|
Pay attention to CI files — `.travis.yml`, `.github/workflows/`, `.circleci/config.yml`, etc. — and make sure there aren't any external services relying on `master` being there. For example, I almost forgot to change the branch [Netlify triggers auto-deploys](https://docs.netlify.com/site-deploys/overview/#branches-and-deploys) from to build this site:
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/github-rename-master/netlify-deploy.png"
|
src="/public/static/images/notes/github-rename-master/netlify-deploy.png"
|
||||||
width="720"
|
width="720"
|
||||||
height="460"
|
height="460"
|
||||||
alt="Netlify auto-deployment branch setting"
|
alt="Netlify auto-deployment branch setting"
|
||||||
|
@@ -12,10 +12,7 @@ tags:
|
|||||||
image: "/static/images/notes/how-to-backup-linux-server/apocalypse.png"
|
image: "/static/images/notes/how-to-backup-linux-server/apocalypse.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Figure from "../components/media/Figure";
|
<Figure src="/public/static/images/notes/how-to-backup-linux-server/apocalypse.png" width="865" height="303" priority>
|
||||||
import Tweet from "../components/media/Tweet";
|
|
||||||
|
|
||||||
<Figure src="/static/images/notes/how-to-backup-linux-server/apocalypse.png" width="865" height="303" priority>
|
|
||||||
**The Cloud-pocalypse:** Coming soon(er than you think) to a server near you.
|
**The Cloud-pocalypse:** Coming soon(er than you think) to a server near you.
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
|
@@ -12,8 +12,6 @@ tags:
|
|||||||
image: "/static/images/notes/how-to-pull-request-fork-github/step7-2.png"
|
image: "/static/images/notes/how-to-pull-request-fork-github/step7-2.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
|
|
||||||
<svg width="150" height="150" viewBox="0 0 40 40" style={{ float: "right", marginBottom: "6px", marginLeft: "12px" }}>
|
<svg width="150" height="150" viewBox="0 0 40 40" style={{ float: "right", marginBottom: "6px", marginLeft: "12px" }}>
|
||||||
<path d="M6.5 35v-4.8c0-5.4 4.3-9.7 9.7-9.7h7.6c5.4 0 9.7-4.3 9.7-9.7V6M6.5 32.5v-26" fill="none" stroke="#a3b7cc" />
|
<path d="M6.5 35v-4.8c0-5.4 4.3-9.7 9.7-9.7h7.6c5.4 0 9.7-4.3 9.7-9.7V6M6.5 32.5v-26" fill="none" stroke="#a3b7cc" />
|
||||||
<path d="M6.5 10.5a4 4 0 110-8 4 4 0 010 8z" fill="#8bb7f0" />
|
<path d="M6.5 10.5a4 4 0 110-8 4 4 0 010 8z" fill="#8bb7f0" />
|
||||||
@@ -36,7 +34,12 @@ Starting from the very beginning, we'll fork an existing repository to our accou
|
|||||||
|
|
||||||
Assuming you're using GitHub, this step is easy. Just find the repository you're contributing to and press the Fork button in the upper left. This will create an exact copy of the repository (and all of its branches) under your own username.
|
Assuming you're using GitHub, this step is easy. Just find the repository you're contributing to and press the Fork button in the upper left. This will create an exact copy of the repository (and all of its branches) under your own username.
|
||||||
|
|
||||||
<Image src="/static/images/notes/how-to-pull-request-fork-github/step1.png" width="865" height="80" alt="Step 1" />
|
<Image
|
||||||
|
src="/public/static/images/notes/how-to-pull-request-fork-github/step1.png"
|
||||||
|
width="865"
|
||||||
|
height="80"
|
||||||
|
alt="Step 1"
|
||||||
|
/>
|
||||||
|
|
||||||
## 2. Clone your new fork locally
|
## 2. Clone your new fork locally
|
||||||
|
|
||||||
@@ -46,7 +49,12 @@ GitHub will automatically redirect you to the forked repository under your usern
|
|||||||
git clone git@github.com:jakejarvis/react-native.git
|
git clone git@github.com:jakejarvis/react-native.git
|
||||||
```
|
```
|
||||||
|
|
||||||
<Image src="/static/images/notes/how-to-pull-request-fork-github/step2.png" width="420" height="208" alt="Step 2" />
|
<Image
|
||||||
|
src="/public/static/images/notes/how-to-pull-request-fork-github/step2.png"
|
||||||
|
width="420"
|
||||||
|
height="208"
|
||||||
|
alt="Step 2"
|
||||||
|
/>
|
||||||
|
|
||||||
## 3. Track the original repository as a remote of the fork
|
## 3. Track the original repository as a remote of the fork
|
||||||
|
|
||||||
@@ -94,9 +102,19 @@ git push -u origin fix-readme-typo
|
|||||||
|
|
||||||
You're now all ready to submit the improvement you've made to the project's maintainers for approval. Head over to the original repositories Pull Requests tab, and you should see an automatic suggestion from GitHub to create a pull request from your new branch.
|
You're now all ready to submit the improvement you've made to the project's maintainers for approval. Head over to the original repositories Pull Requests tab, and you should see an automatic suggestion from GitHub to create a pull request from your new branch.
|
||||||
|
|
||||||
<Image src="/static/images/notes/how-to-pull-request-fork-github/step7-1.png" width="865" height="75" alt="Step 7.1" />
|
<Image
|
||||||
|
src="/public/static/images/notes/how-to-pull-request-fork-github/step7-1.png"
|
||||||
|
width="865"
|
||||||
|
height="75"
|
||||||
|
alt="Step 7.1"
|
||||||
|
/>
|
||||||
|
|
||||||
<Image src="/static/images/notes/how-to-pull-request-fork-github/step7-2.png" width="700" height="354" alt="Step 7.2" />
|
<Image
|
||||||
|
src="/public/static/images/notes/how-to-pull-request-fork-github/step7-2.png"
|
||||||
|
width="700"
|
||||||
|
height="354"
|
||||||
|
alt="Step 7.2"
|
||||||
|
/>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -11,11 +11,8 @@ tags:
|
|||||||
image: "/static/images/notes/how-to-shrink-linux-virtual-disk-vmware/screen-shot-2018-12-07-at-2-04-04-pm.png"
|
image: "/static/images/notes/how-to-shrink-linux-virtual-disk-vmware/screen-shot-2018-12-07-at-2-04-04-pm.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
|
|
||||||
<Figure
|
<Figure
|
||||||
src="/static/images/notes/how-to-shrink-linux-virtual-disk-vmware/screen-shot-2018-12-07-at-2-04-04-pm.png"
|
src="/public/static/images/notes/how-to-shrink-linux-virtual-disk-vmware/screen-shot-2018-12-07-at-2-04-04-pm.png"
|
||||||
width="620"
|
width="620"
|
||||||
height="456"
|
height="456"
|
||||||
priority
|
priority
|
||||||
@@ -82,7 +79,7 @@ VMware on macOS makes this a little tricky, since it packages VMs in what looks
|
|||||||
We need to right click on the .vmwarevm "file," and select **Show Package Contents** to see what's really in there. You should see the actual .VMDK file sitting there — normally we're looking for the plain VMDK file (named _Virtual Disk.vmdk_ by default) without a bunch of numbers after it, but if you have snapshots associated with your VM, this might not be the file we actually want. But run the command below with it anyways, and the output will tell you if you need to use a different file.
|
We need to right click on the .vmwarevm "file," and select **Show Package Contents** to see what's really in there. You should see the actual .VMDK file sitting there — normally we're looking for the plain VMDK file (named _Virtual Disk.vmdk_ by default) without a bunch of numbers after it, but if you have snapshots associated with your VM, this might not be the file we actually want. But run the command below with it anyways, and the output will tell you if you need to use a different file.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/how-to-shrink-linux-virtual-disk-vmware/screen-shot-2018-12-07-at-1-58-42-pm.png"
|
src="/public/static/images/notes/how-to-shrink-linux-virtual-disk-vmware/screen-shot-2018-12-07-at-1-58-42-pm.png"
|
||||||
width="680"
|
width="680"
|
||||||
height="572"
|
height="572"
|
||||||
alt="Finding .vmwarevm in Finder"
|
alt="Finding .vmwarevm in Finder"
|
||||||
|
@@ -11,11 +11,8 @@ image: "/static/images/notes/millenial-with-hillary-clinton/24707394571_0818d4ab
|
|||||||
noComments: true
|
noComments: true
|
||||||
---
|
---
|
||||||
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
import Video from "../components/media/Video";
|
|
||||||
|
|
||||||
<Figure
|
<Figure
|
||||||
src="/static/images/notes/millenial-with-hillary-clinton/24707394571_0818d4ab83_o-1-copy.jpg"
|
src="/public/static/images/notes/millenial-with-hillary-clinton/24707394571_0818d4ab83_o-1-copy.jpg"
|
||||||
width="865"
|
width="865"
|
||||||
height="411"
|
height="411"
|
||||||
priority
|
priority
|
||||||
@@ -36,7 +33,7 @@ My goal here isn't to convince every Bernie believer to jump ship and support he
|
|||||||
After working for months as a fellow on Hillary's campaign in New Hampshire leading up to the first primary in the country, I could feed you all the standard campaign talking points in my sleep: After graduating from Yale Law she went to work at the [Children's Defense Fund](https://www.childrensdefense.org/), not a high-paying New York law firm. She [went undercover](https://www.nytimes.com/2015/12/28/us/politics/how-hillary-clinton-went-undercover-to-examine-race-in-education.html?_r=0) in Alabama to investigate discrimination in public schools. She [got juveniles out of adult prisons](https://www.huffingtonpost.com/entry/huffpost-criminal-justice-survey-democratics_us_56bb85eae4b0b40245c5038b). She [gave 8 million children healthcare](https://www.hillaryclinton.com/briefing/factsheets/2015/12/23/hillary-clintons-lifelong-fight-for-quality-affordable-health-care-for-all-americans/). But there's just one thing that, for some reason, is hard for people to believe: at her core she is a good, caring, and loving person who has had only selfless intentions her entire life. I promise you.
|
After working for months as a fellow on Hillary's campaign in New Hampshire leading up to the first primary in the country, I could feed you all the standard campaign talking points in my sleep: After graduating from Yale Law she went to work at the [Children's Defense Fund](https://www.childrensdefense.org/), not a high-paying New York law firm. She [went undercover](https://www.nytimes.com/2015/12/28/us/politics/how-hillary-clinton-went-undercover-to-examine-race-in-education.html?_r=0) in Alabama to investigate discrimination in public schools. She [got juveniles out of adult prisons](https://www.huffingtonpost.com/entry/huffpost-criminal-justice-survey-democratics_us_56bb85eae4b0b40245c5038b). She [gave 8 million children healthcare](https://www.hillaryclinton.com/briefing/factsheets/2015/12/23/hillary-clintons-lifelong-fight-for-quality-affordable-health-care-for-all-americans/). But there's just one thing that, for some reason, is hard for people to believe: at her core she is a good, caring, and loving person who has had only selfless intentions her entire life. I promise you.
|
||||||
|
|
||||||
<Figure
|
<Figure
|
||||||
src="/static/images/notes/millenial-with-hillary-clinton/9e58a-1bvweqv_ve2_c1tw5-ihrhw.jpg"
|
src="/public/static/images/notes/millenial-with-hillary-clinton/9e58a-1bvweqv_ve2_c1tw5-ihrhw.jpg"
|
||||||
width="400"
|
width="400"
|
||||||
height="500"
|
height="500"
|
||||||
>
|
>
|
||||||
@@ -55,39 +52,15 @@ I'm aware of the street cred young Democrats collect by claiming they hated Hill
|
|||||||
|
|
||||||
As [Bill Maher](https://medium.com/u/cdc04a9799f6) (an avid Bernie supporter) [said this weekend](https://www.youtube.com/watch?v=rd1gpjkjcfc), some in our party need to "learn the difference between an imperfect friend and a deadly enemy." I don't agree with everything Hillary has said or done. I don't unconditionally defend every single chapter in her public record over the past 30 years (and [neither does she](https://www.washingtonpost.com/blogs/post-partisan/wp/2016/02/25/hillary-clinton-responds-to-activist-who-demanded-apology-for-superpredator-remarks/), by the way). I don't think that's possible for any voter to find in a politician. But if you identify as a Democrat, she is the farthest thing from your enemy. Plain and simple. Like you and Bernie, she wants to prevent a Republican from winning in November and reversing so much of the progress we've made over the past seven years on their first day in office. That is our number one goal right now. And whether it gets accomplished by a President Clinton or a President Sanders, I am 100% on board either way. Let's stop fighting each other and start fighting together.
|
As [Bill Maher](https://medium.com/u/cdc04a9799f6) (an avid Bernie supporter) [said this weekend](https://www.youtube.com/watch?v=rd1gpjkjcfc), some in our party need to "learn the difference between an imperfect friend and a deadly enemy." I don't agree with everything Hillary has said or done. I don't unconditionally defend every single chapter in her public record over the past 30 years (and [neither does she](https://www.washingtonpost.com/blogs/post-partisan/wp/2016/02/25/hillary-clinton-responds-to-activist-who-demanded-apology-for-superpredator-remarks/), by the way). I don't think that's possible for any voter to find in a politician. But if you identify as a Democrat, she is the farthest thing from your enemy. Plain and simple. Like you and Bernie, she wants to prevent a Republican from winning in November and reversing so much of the progress we've made over the past seven years on their first day in office. That is our number one goal right now. And whether it gets accomplished by a President Clinton or a President Sanders, I am 100% on board either way. Let's stop fighting each other and start fighting together.
|
||||||
|
|
||||||
<Video
|
<YouTube id="TqrwDMTByNM" />
|
||||||
url="https://www.youtube-nocookie.com/watch?v=TqrwDMTByNM"
|
|
||||||
light="https://i.ytimg.com/vi/TqrwDMTByNM/hqdefault.jpg"
|
|
||||||
controls
|
|
||||||
/>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Update:** The campaign has included my snowy New Hampshire interaction with her in one of the DNC convention videos! See a few short seconds of my joy at 1:24.
|
**Update:** The campaign has included my snowy New Hampshire interaction with her in one of the DNC convention videos! See a few short seconds of my joy at 1:24.
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/hillary/convention-720p.webm"
|
||||||
{ src: "/static/images/hillary/convention-720p.webm", type: "video/webm" },
|
mp4="/static/images/hillary/convention-720p.mp4"
|
||||||
{ src: "/static/images/hillary/convention-720p.mp4", type: "video/mp4" },
|
thumbnail="/static/images/hillary/thumb.png"
|
||||||
]}
|
subs="/static/images/hillary/subs.en.vtt"
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: "/static/images/hillary/thumb.png",
|
|
||||||
controlsList: "nodownload",
|
|
||||||
preload: "metadata",
|
|
||||||
autoPlay: false,
|
|
||||||
},
|
|
||||||
tracks: [
|
|
||||||
{
|
|
||||||
kind: "subtitles",
|
|
||||||
src: "/static/images/hillary/subs.en.vtt",
|
|
||||||
srcLang: "en",
|
|
||||||
label: "English",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={true}
|
|
||||||
/>
|
/>
|
||||||
|
@@ -11,18 +11,20 @@ tags:
|
|||||||
image: "/static/images/notes/my-first-code/jbb-screen1.png"
|
image: "/static/images/notes/my-first-code/jbb-screen1.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/my-first-code/netscape.png"
|
src="/public/static/images/notes/my-first-code/netscape.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="155"
|
height="155"
|
||||||
alt="Awesome First Code on GitHub"
|
alt="Awesome First Code on GitHub"
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Image src="/static/images/notes/my-first-code/badges.png" width="537" height="36" alt="Code Quality: A for effort" />
|
<Image
|
||||||
|
src="/public/static/images/notes/my-first-code/badges.png"
|
||||||
|
width="537"
|
||||||
|
height="36"
|
||||||
|
alt="Code Quality: A for effort"
|
||||||
|
/>
|
||||||
|
|
||||||
I recently published my terrible, horrible, no good, very bad [first HTML site](https://jakejarvis.github.io/my-first-website/) and [first PHP project](https://github.com/jakejarvis/jbb#readme) ever and developed a new addiction to Web 1.0 nostalgia, fed by others who were brave enough to do the same.
|
I recently published my terrible, horrible, no good, very bad [first HTML site](https://jakejarvis.github.io/my-first-website/) and [first PHP project](https://github.com/jakejarvis/jbb#readme) ever and developed a new addiction to Web 1.0 nostalgia, fed by others who were brave enough to do the same.
|
||||||
|
|
||||||
@@ -32,7 +34,7 @@ Hopefully we can all look back at our first projects and be proud of how far we'
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Figure src="/static/images/notes/my-first-code/jbb-logo.png" width="640" height="80">
|
<Figure src="/public/static/images/notes/my-first-code/jbb-logo.png" width="640" height="80">
|
||||||
[Jake's Bulletin Board](https://github.com/jakejarvis/jbb)
|
[Jake's Bulletin Board](https://github.com/jakejarvis/jbb)
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -163,16 +165,16 @@ while ($topic = mysql_fetch_object($result30)) {
|
|||||||
|
|
||||||
The installation "wizard" (that's the joke, I presume...) ([sql_submit.php](https://github.com/jakejarvis/jbb/blob/87b606797414b2fe563af85e269566fc5e076cc5/setup/sql_submit.php))
|
The installation "wizard" (that's the joke, I presume...) ([sql_submit.php](https://github.com/jakejarvis/jbb/blob/87b606797414b2fe563af85e269566fc5e076cc5/setup/sql_submit.php))
|
||||||
|
|
||||||
<Figure src="/static/images/notes/my-first-code/jbb-screen1.png" width="865" height="458">
|
<Figure src="/public/static/images/notes/my-first-code/jbb-screen1.png" width="865" height="458">
|
||||||
JBB Installation Wizard
|
JBB Installation Wizard
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
And finally, JBB's actual interface... or literally as much of it as I could get to function in 2019. ([index.php](https://github.com/jakejarvis/jbb/blob/87b606797414b2fe563af85e269566fc5e076cc5/index.php))
|
And finally, JBB's actual interface... or literally as much of it as I could get to function in 2019. ([index.php](https://github.com/jakejarvis/jbb/blob/87b606797414b2fe563af85e269566fc5e076cc5/index.php))
|
||||||
|
|
||||||
<Figure src="/static/images/notes/my-first-code/jbb-screen3.png" width="865" height="561">
|
<Figure src="/public/static/images/notes/my-first-code/jbb-screen3.png" width="865" height="561">
|
||||||
JBB Homepage
|
JBB Homepage
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
<Figure src="/static/images/notes/my-first-code/jbb-screen4.png" width="865" height="493">
|
<Figure src="/public/static/images/notes/my-first-code/jbb-screen4.png" width="865" height="493">
|
||||||
JBB Post
|
JBB Post
|
||||||
</Figure>
|
</Figure>
|
||||||
|
@@ -12,12 +12,10 @@ tags:
|
|||||||
image: "/static/images/notes/netlify-analytics-review/overview.png"
|
image: "/static/images/notes/netlify-analytics-review/overview.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
|
|
||||||
I've been trying out [Netlify Analytics](https://www.netlify.com/products/analytics/) on this site for over a month now and have some quick thoughts about this unique offering in a world full of bloated and invasive tracking scripts.
|
I've been trying out [Netlify Analytics](https://www.netlify.com/products/analytics/) on this site for over a month now and have some quick thoughts about this unique offering in a world full of bloated and invasive tracking scripts.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/netlify-analytics-review/pageviews-2.png"
|
src="/public/static/images/notes/netlify-analytics-review/pageviews-2.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="361"
|
height="361"
|
||||||
alt="Pageview charts on Netlify Analytics"
|
alt="Pageview charts on Netlify Analytics"
|
||||||
@@ -55,7 +53,7 @@ Ad blocking is becoming commonplace on the World Wide Web with [over 25% of user
|
|||||||
That's a _huge_ chunk of visitors missing that Netlify Analytics gains back for you — and probably far more if your audience is tech-savvy like those reading this post likely are. (Some might even [block JavaScript completely](https://www.gnu.org/philosophy/javascript-trap.en.html) using extensions like [NoScript](https://addons.mozilla.org/en-US/firefox/addon/noscript/).)
|
That's a _huge_ chunk of visitors missing that Netlify Analytics gains back for you — and probably far more if your audience is tech-savvy like those reading this post likely are. (Some might even [block JavaScript completely](https://www.gnu.org/philosophy/javascript-trap.en.html) using extensions like [NoScript](https://addons.mozilla.org/en-US/firefox/addon/noscript/).)
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/netlify-analytics-review/pages.png"
|
src="/public/static/images/notes/netlify-analytics-review/pages.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="390"
|
height="390"
|
||||||
alt="Pageview and 404 tracking on Netlify Analytics"
|
alt="Pageview and 404 tracking on Netlify Analytics"
|
||||||
@@ -76,7 +74,7 @@ It makes sense that Netlify needs to subsidize the cost of providing free enterp
|
|||||||
### 📈 Accuracy
|
### 📈 Accuracy
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/netlify-analytics-review/sources-bandwidth.png"
|
src="/public/static/images/notes/netlify-analytics-review/sources-bandwidth.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="422"
|
height="422"
|
||||||
alt="Referrer and bandwidth tracking on Netlify Analytics"
|
alt="Referrer and bandwidth tracking on Netlify Analytics"
|
||||||
@@ -93,7 +91,7 @@ One more note: since Netlify doesn't process IP addresses or user agents, bots c
|
|||||||
### ⏱️ Historical Data
|
### ⏱️ Historical Data
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/netlify-analytics-review/overview.png"
|
src="/public/static/images/notes/netlify-analytics-review/overview.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="355"
|
height="355"
|
||||||
alt="Overview of Netlify Analytics stats"
|
alt="Overview of Netlify Analytics stats"
|
||||||
|
@@ -10,12 +10,8 @@ tags:
|
|||||||
image: "/static/images/notes/presidential-candidates-404-pages/obama-laughing.jpg"
|
image: "/static/images/notes/presidential-candidates-404-pages/obama-laughing.jpg"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
import Video from "../components/media/Video";
|
|
||||||
|
|
||||||
<Figure
|
<Figure
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/obama-laughing.jpg"
|
src="/public/static/images/notes/presidential-candidates-404-pages/obama-laughing.jpg"
|
||||||
width="865"
|
width="865"
|
||||||
height="460"
|
height="460"
|
||||||
priority
|
priority
|
||||||
@@ -32,7 +28,7 @@ More recently, though, little-known hidden Easter eggs on ["404 Not Found"](http
|
|||||||
I'm a _huge_ sucker for Kate McKinnon's spot-on impression of Warren on Saturday Night Live. And [unfortunately](https://twitter.com/realdonaldtrump/status/1097116612279316480), seeing a campaign embrace SNL is like a breath of fresh air these days. [Watch all of the Kate McWarren videos so far here; you won't regret it.](https://www.nbc.com/saturday-night-live/cast/kate-mckinnon-15056/impersonation/elizabeth-warren-287903)
|
I'm a _huge_ sucker for Kate McKinnon's spot-on impression of Warren on Saturday Night Live. And [unfortunately](https://twitter.com/realdonaldtrump/status/1097116612279316480), seeing a campaign embrace SNL is like a breath of fresh air these days. [Watch all of the Kate McWarren videos so far here; you won't regret it.](https://www.nbc.com/saturday-night-live/cast/kate-mckinnon-15056/impersonation/elizabeth-warren-287903)
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/warren.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/warren.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="579"
|
height="579"
|
||||||
alt="Elizabeth Warren"
|
alt="Elizabeth Warren"
|
||||||
@@ -43,21 +39,9 @@ I'm a _huge_ sucker for Kate McKinnon's spot-on impression of Warren on Saturday
|
|||||||
Although the designer who selected this GIF likely had _thousands_ of choices when searching "[Bernie finger wagging GIF](https://www.google.com/search?q=Bernie+finger+wagging+GIF&tbm=isch&tbs=itp:animated)," the text beside it is well-written and funny — even though we both know putting a page at [berniesanders.com/zxcliaosid](https://berniesanders.com/zxcliaosid/) probably won't be a top priority of a President Sanders.
|
Although the designer who selected this GIF likely had _thousands_ of choices when searching "[Bernie finger wagging GIF](https://www.google.com/search?q=Bernie+finger+wagging+GIF&tbm=isch&tbs=itp:animated)," the text beside it is well-written and funny — even though we both know putting a page at [berniesanders.com/zxcliaosid](https://berniesanders.com/zxcliaosid/) probably won't be a top priority of a President Sanders.
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/notes/presidential-candidates-404-pages/sanders.webm"
|
||||||
{ src: "/static/images/notes/presidential-candidates-404-pages/sanders.webm", type: "video/webm" },
|
mp4="/static/images/notes/presidential-candidates-404-pages/sanders.mp4"
|
||||||
{ src: "/static/images/notes/presidential-candidates-404-pages/sanders.mp4", type: "video/mp4" },
|
autoplay
|
||||||
]}
|
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
autoPlay: true,
|
|
||||||
loop: true,
|
|
||||||
volume: 0,
|
|
||||||
muted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={false}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## 3. Joe Biden — [joebiden.com](https://joebiden.com/asdfasdf404)
|
## 3. Joe Biden — [joebiden.com](https://joebiden.com/asdfasdf404)
|
||||||
@@ -65,7 +49,7 @@ Although the designer who selected this GIF likely had _thousands_ of choices wh
|
|||||||
Uncle Joe has a nice and simple 404 page. I like it, along with the Ray-Bans and his choice of vanilla ice cream.
|
Uncle Joe has a nice and simple 404 page. I like it, along with the Ray-Bans and his choice of vanilla ice cream.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/biden.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/biden.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="539"
|
height="539"
|
||||||
alt="Joe Biden"
|
alt="Joe Biden"
|
||||||
@@ -76,21 +60,9 @@ Uncle Joe has a nice and simple 404 page. I like it, along with the Ray-Bans and
|
|||||||
A ballsy move, considering Beto's infamous [DUI arrest](https://www.politifact.com/texas/statements/2019/mar/14/club-growth/beto-orourke-arrested-dwi-flee-scene/) in the '90s — but still a clever ask for a donation and a great use of a GIF, even if it's left over from his Senate campaign.
|
A ballsy move, considering Beto's infamous [DUI arrest](https://www.politifact.com/texas/statements/2019/mar/14/club-growth/beto-orourke-arrested-dwi-flee-scene/) in the '90s — but still a clever ask for a donation and a great use of a GIF, even if it's left over from his Senate campaign.
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/notes/presidential-candidates-404-pages/orourke.webm"
|
||||||
{ src: "/static/images/notes/presidential-candidates-404-pages/orourke.webm", type: "video/webm" },
|
mp4="/static/images/notes/presidential-candidates-404-pages/orourke.mp4"
|
||||||
{ src: "/static/images/notes/presidential-candidates-404-pages/orourke.mp4", type: "video/mp4" },
|
autoplay
|
||||||
]}
|
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
autoPlay: true,
|
|
||||||
loop: true,
|
|
||||||
volume: 0,
|
|
||||||
muted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={false}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## 5. Kamala Harris — [kamalaharris.org](https://kamalaharris.org/asdfasdf404)
|
## 5. Kamala Harris — [kamalaharris.org](https://kamalaharris.org/asdfasdf404)
|
||||||
@@ -98,21 +70,9 @@ A ballsy move, considering Beto's infamous [DUI arrest](https://www.politifact.c
|
|||||||
Another clean and simple page with a top-notch GIF. It injected some emotion into visiting [kamalaharris.com/alskdjf](https://kamalaharris.com/alskdjf).
|
Another clean and simple page with a top-notch GIF. It injected some emotion into visiting [kamalaharris.com/alskdjf](https://kamalaharris.com/alskdjf).
|
||||||
|
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/notes/presidential-candidates-404-pages/harris.webm"
|
||||||
{ src: "/static/images/notes/presidential-candidates-404-pages/harris.webm", type: "video/webm" },
|
mp4="/static/images/notes/presidential-candidates-404-pages/harris.mp4"
|
||||||
{ src: "/static/images/notes/presidential-candidates-404-pages/harris.mp4", type: "video/mp4" },
|
autoplay
|
||||||
]}
|
|
||||||
config={{
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
autoPlay: true,
|
|
||||||
loop: true,
|
|
||||||
volume: 0,
|
|
||||||
muted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={false}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## 6. Pete Buttigeg — [peteforamerica.com](https://peteforamerica.com/asdfasdf404/)
|
## 6. Pete Buttigeg — [peteforamerica.com](https://peteforamerica.com/asdfasdf404/)
|
||||||
@@ -120,7 +80,7 @@ Another clean and simple page with a top-notch GIF. It injected some emotion int
|
|||||||
I love, love, _love_ Pete's design for his whole campaign, and his beautiful 404 page is no exception. In case you didn't know, Pete for America has an entire ["Design Toolkit"](https://design.peteforamerica.com/) publicly available for all to view and use, with really cool and in-depth explanations for all of their choices — even their [color palette](https://design.peteforamerica.com/colors). Very progressive indeed.
|
I love, love, _love_ Pete's design for his whole campaign, and his beautiful 404 page is no exception. In case you didn't know, Pete for America has an entire ["Design Toolkit"](https://design.peteforamerica.com/) publicly available for all to view and use, with really cool and in-depth explanations for all of their choices — even their [color palette](https://design.peteforamerica.com/colors). Very progressive indeed.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/buttigeg.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/buttigeg.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="344"
|
height="344"
|
||||||
alt="Pete Buttigeg"
|
alt="Pete Buttigeg"
|
||||||
@@ -131,7 +91,7 @@ I love, love, _love_ Pete's design for his whole campaign, and his beautiful 404
|
|||||||
Love the photo choice. But although pains me to go against my Senator from my home state, I still _cannot stand_ his choice of font. Oh well, I guess that's now a criterion for running for president in 2020.
|
Love the photo choice. But although pains me to go against my Senator from my home state, I still _cannot stand_ his choice of font. Oh well, I guess that's now a criterion for running for president in 2020.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/booker.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/booker.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="503"
|
height="503"
|
||||||
alt="Cory Booker"
|
alt="Cory Booker"
|
||||||
@@ -142,7 +102,7 @@ Love the photo choice. But although pains me to go against my Senator from my ho
|
|||||||
Not sure if donating to Yang 2020 will help put a page at [yang2020.com/alsdjfzoif](https://www.yang2020.com/alsdjfzoif) — the actual URL I visited to grab this screenshot — but the Bitmoji Andrew looks pretty chill.
|
Not sure if donating to Yang 2020 will help put a page at [yang2020.com/alsdjfzoif](https://www.yang2020.com/alsdjfzoif) — the actual URL I visited to grab this screenshot — but the Bitmoji Andrew looks pretty chill.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/yang.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/yang.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="470"
|
height="470"
|
||||||
alt="Andrew Yang"
|
alt="Andrew Yang"
|
||||||
@@ -153,7 +113,7 @@ Not sure if donating to Yang 2020 will help put a page at [yang2020.com/alsdjfzo
|
|||||||
This is the 404 page of someone who won't forget the [Midwestern roots](https://en.wikipedia.org/wiki/Uff_da) she comes from once she moves into the White House...or writes a memoir about her campaign from her Minnesota home.
|
This is the 404 page of someone who won't forget the [Midwestern roots](https://en.wikipedia.org/wiki/Uff_da) she comes from once she moves into the White House...or writes a memoir about her campaign from her Minnesota home.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/klobuchar.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/klobuchar.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="456"
|
height="456"
|
||||||
alt="Amy Klobuchar"
|
alt="Amy Klobuchar"
|
||||||
@@ -164,7 +124,7 @@ This is the 404 page of someone who won't forget the [Midwestern roots](https://
|
|||||||
I'll never publicly say anything against a good Dad joke. This is no exception.
|
I'll never publicly say anything against a good Dad joke. This is no exception.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/bullock.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/bullock.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="467"
|
height="467"
|
||||||
alt="Steve Bullock"
|
alt="Steve Bullock"
|
||||||
@@ -175,7 +135,7 @@ I'll never publicly say anything against a good Dad joke. This is no exception.
|
|||||||
Another quality Dad joke here.
|
Another quality Dad joke here.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/bennet.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/bennet.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="543"
|
height="543"
|
||||||
alt="Michael Bennet"
|
alt="Michael Bennet"
|
||||||
@@ -186,7 +146,7 @@ Another quality Dad joke here.
|
|||||||
Yet another Dad joke? I honestly had the hardest time ranking these three.
|
Yet another Dad joke? I honestly had the hardest time ranking these three.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/delaney.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/delaney.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="405"
|
height="405"
|
||||||
alt="John Delaney"
|
alt="John Delaney"
|
||||||
@@ -197,7 +157,7 @@ Yet another Dad joke? I honestly had the hardest time ranking these three.
|
|||||||
A 404 page only a motivational author and speaker running for president could envision.
|
A 404 page only a motivational author and speaker running for president could envision.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/williamson.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/williamson.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="357"
|
height="357"
|
||||||
alt="Marianne Williamson"
|
alt="Marianne Williamson"
|
||||||
@@ -208,7 +168,7 @@ A 404 page only a motivational author and speaker running for president could en
|
|||||||
I guess this would be slightly humorous...four years ago. Time to move on from your middle-school crush, Donny.
|
I guess this would be slightly humorous...four years ago. Time to move on from your middle-school crush, Donny.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/trump.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/trump.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="524"
|
height="524"
|
||||||
alt="Trump/Pence"
|
alt="Trump/Pence"
|
||||||
@@ -223,7 +183,7 @@ These candidates haven't configured a custom 404 page, settling for the default
|
|||||||
### 15. Julián Castro — [julianforthefuture.com](https://www.julianforthefuture.com/asdfasdf404)
|
### 15. Julián Castro — [julianforthefuture.com](https://www.julianforthefuture.com/asdfasdf404)
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/castro.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/castro.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="316"
|
height="316"
|
||||||
alt="Julián Castro"
|
alt="Julián Castro"
|
||||||
@@ -232,7 +192,7 @@ These candidates haven't configured a custom 404 page, settling for the default
|
|||||||
### 16. Wayne Messam — [wayneforusa.com](https://wayneforusa.com/asdfasdf404)
|
### 16. Wayne Messam — [wayneforusa.com](https://wayneforusa.com/asdfasdf404)
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/messam.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/messam.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="529"
|
height="529"
|
||||||
alt="Wayne Messam"
|
alt="Wayne Messam"
|
||||||
@@ -241,7 +201,7 @@ These candidates haven't configured a custom 404 page, settling for the default
|
|||||||
### 17. Tulsi Gabbard — [tulsi2020.com](https://www.tulsi2020.com/asdfasdf404)
|
### 17. Tulsi Gabbard — [tulsi2020.com](https://www.tulsi2020.com/asdfasdf404)
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/gabbard.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/gabbard.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="333"
|
height="333"
|
||||||
alt="Tulsi Gabbard"
|
alt="Tulsi Gabbard"
|
||||||
@@ -250,7 +210,7 @@ These candidates haven't configured a custom 404 page, settling for the default
|
|||||||
### 18. Joe Sestak — [joesestak.com](https://www.joesestak.com/asdfasdf404)
|
### 18. Joe Sestak — [joesestak.com](https://www.joesestak.com/asdfasdf404)
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/presidential-candidates-404-pages/sestak.png"
|
src="/public/static/images/notes/presidential-candidates-404-pages/sestak.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="366"
|
height="366"
|
||||||
alt="Joe Sestak"
|
alt="Joe Sestak"
|
||||||
|
@@ -11,11 +11,8 @@ tags:
|
|||||||
image: "/static/images/notes/security-headers-cloudflare-workers/security-headers.png"
|
image: "/static/images/notes/security-headers-cloudflare-workers/security-headers.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
|
|
||||||
<Figure
|
<Figure
|
||||||
src="/static/images/notes/security-headers-cloudflare-workers/security-headers.png"
|
src="/public/static/images/notes/security-headers-cloudflare-workers/security-headers.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="275"
|
height="275"
|
||||||
priority
|
priority
|
||||||
@@ -28,7 +25,7 @@ In 2019, it's becoming more and more important to harden websites via HTTP respo
|
|||||||
[Cloudflare Workers](https://www.cloudflare.com/products/cloudflare-workers/) are a great feature of [Cloudflare](https://www.cloudflare.com/) that allows you to modify responses on-the-fly between your origin server and the user, similar to [AWS Lambda](https://aws.amazon.com/lambda/) (but much simpler). We'll use a Worker to add the headers.
|
[Cloudflare Workers](https://www.cloudflare.com/products/cloudflare-workers/) are a great feature of [Cloudflare](https://www.cloudflare.com/) that allows you to modify responses on-the-fly between your origin server and the user, similar to [AWS Lambda](https://aws.amazon.com/lambda/) (but much simpler). We'll use a Worker to add the headers.
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/security-headers-cloudflare-workers/cf-workers.png"
|
src="/public/static/images/notes/security-headers-cloudflare-workers/cf-workers.png"
|
||||||
width="650"
|
width="650"
|
||||||
height="325"
|
height="325"
|
||||||
alt="Cloudflare Workers"
|
alt="Cloudflare Workers"
|
||||||
|
@@ -11,12 +11,9 @@ tags:
|
|||||||
image: "/static/images/notes/shodan-search-queries/shodan.png"
|
image: "/static/images/notes/shodan-search-queries/shodan.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Image from "../components/media/Image";
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
|
|
||||||
Over time, I've collected an assortment of interesting, funny, and depressing search queries to plug into [Shodan](https://www.shodan.io/), the ([literal](https://www.vice.com/en_uk/article/9bvxmd/shodan-exposes-the-dark-side-of-the-net)) internet search engine. Some return facepalm-inducing results, while others return serious and/or ancient vulnerabilities in the wild.
|
Over time, I've collected an assortment of interesting, funny, and depressing search queries to plug into [Shodan](https://www.shodan.io/), the ([literal](https://www.vice.com/en_uk/article/9bvxmd/shodan-exposes-the-dark-side-of-the-net)) internet search engine. Some return facepalm-inducing results, while others return serious and/or ancient vulnerabilities in the wild.
|
||||||
|
|
||||||
<Figure src="/static/images/notes/shodan-search-queries/shodan.png" width="865" height="248" priority>
|
<Figure src="/public/static/images/notes/shodan-search-queries/shodan.png" width="865" height="248" priority>
|
||||||
[**Most search filters require a Shodan account.**](https://account.shodan.io/register)
|
[**Most search filters require a Shodan account.**](https://account.shodan.io/register)
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -50,7 +47,7 @@ The world and its devices are quickly becoming more connected through the shiny
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/billboard3.png"
|
src="/public/static/images/notes/shodan-search-queries/billboard3.png"
|
||||||
width="450"
|
width="450"
|
||||||
height="329"
|
height="329"
|
||||||
alt="Example: Electronic Billboards"
|
alt="Example: Electronic Billboards"
|
||||||
@@ -63,7 +60,7 @@ The world and its devices are quickly becoming more connected through the shiny
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/7-11.png"
|
src="/public/static/images/notes/shodan-search-queries/7-11.png"
|
||||||
width="600"
|
width="600"
|
||||||
height="226"
|
height="226"
|
||||||
alt="Example: Gas Station Pump Inventories"
|
alt="Example: Gas Station Pump Inventories"
|
||||||
@@ -76,7 +73,7 @@ P372 "ANPR enabled"
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/plate-reader.png"
|
src="/public/static/images/notes/shodan-search-queries/plate-reader.png"
|
||||||
width="680"
|
width="680"
|
||||||
height="284"
|
height="284"
|
||||||
alt="Example: Automatic License Plate Reader"
|
alt="Example: Automatic License Plate Reader"
|
||||||
@@ -117,7 +114,7 @@ http.title:"Tesla PowerPack System" http.component:"d3" -ga3ca4f2
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/tesla.png"
|
src="/public/static/images/notes/shodan-search-queries/tesla.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="543"
|
height="543"
|
||||||
alt="Example: Tesla PowerPack Charging Status"
|
alt="Example: Tesla PowerPack Charging Status"
|
||||||
@@ -138,7 +135,7 @@ Shodan made a pretty sweet [Ship Tracker](https://shiptracker.shodan.io/) that m
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/sailor-vsat.png"
|
src="/public/static/images/notes/shodan-search-queries/sailor-vsat.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="361"
|
height="361"
|
||||||
alt="Example: Maritime Satellites"
|
alt="Example: Maritime Satellites"
|
||||||
@@ -157,7 +154,7 @@ title:"Slocum Fleet Mission Control"
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/refrigeration.png"
|
src="/public/static/images/notes/shodan-search-queries/refrigeration.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="456"
|
height="456"
|
||||||
alt="Example: CAREL PlantVisor Refrigeration Units"
|
alt="Example: CAREL PlantVisor Refrigeration Units"
|
||||||
@@ -176,7 +173,7 @@ http.title:"Nordex Control" "Windows 2000 5.0 x86" "Jetty/3.1 (JSP 1.1; Servlet
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/c4max.png"
|
src="/public/static/images/notes/shodan-search-queries/c4max.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="171"
|
height="171"
|
||||||
alt="Example: C4 Max Vehicle GPS"
|
alt="Example: C4 Max Vehicle GPS"
|
||||||
@@ -197,7 +194,7 @@ Secured by default, thankfully, but these 1,700+ machines still [have no busines
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/power-gaugetech.png"
|
src="/public/static/images/notes/shodan-search-queries/power-gaugetech.png"
|
||||||
width="500"
|
width="500"
|
||||||
height="246"
|
height="246"
|
||||||
alt="Example: GaugeTech Electricity Meters"
|
alt="Example: GaugeTech Electricity Meters"
|
||||||
@@ -239,7 +236,7 @@ Secured by default, thankfully, but these 1,700+ machines still [have no busines
|
|||||||
|
|
||||||
[Shodan Images](https://images.shodan.io/) is a great supplementary tool to browse screenshots, by the way! [🔎 →](https://images.shodan.io/?query=%22authentication+disabled%22+%21screenshot.label%3Ablank)
|
[Shodan Images](https://images.shodan.io/) is a great supplementary tool to browse screenshots, by the way! [🔎 →](https://images.shodan.io/?query=%22authentication+disabled%22+%21screenshot.label%3Ablank)
|
||||||
|
|
||||||
<Figure src="/static/images/notes/shodan-search-queries/vnc.png" width="500" height="375">
|
<Figure src="/public/static/images/notes/shodan-search-queries/vnc.png" width="500" height="375">
|
||||||
The first result right now. 😞
|
The first result right now. 😞
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -264,7 +261,7 @@ title:"Weave Scope" http.favicon.hash:567176827
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/weavescope.png"
|
src="/public/static/images/notes/shodan-search-queries/weavescope.png"
|
||||||
width="865"
|
width="865"
|
||||||
height="465"
|
height="465"
|
||||||
alt="Example: Weave Scope Dashboards"
|
alt="Example: Weave Scope Dashboards"
|
||||||
@@ -278,7 +275,12 @@ Older versions were insecure by default. [Very scary.](https://krebsonsecurity.c
|
|||||||
"MongoDB Server Information" port:27017 -authentication
|
"MongoDB Server Information" port:27017 -authentication
|
||||||
```
|
```
|
||||||
|
|
||||||
<Image src="/static/images/notes/shodan-search-queries/mongo.png" width="500" height="238" alt="Example: MongoDB" />
|
<Image
|
||||||
|
src="/public/static/images/notes/shodan-search-queries/mongo.png"
|
||||||
|
width="500"
|
||||||
|
height="238"
|
||||||
|
alt="Example: MongoDB"
|
||||||
|
/>
|
||||||
|
|
||||||
### [Mongo Express](https://github.com/mongo-express/mongo-express) Web GUI [🔎 →](https://www.shodan.io/search?query=%22Set-Cookie%3A+mongo-express%3D%22+%22200+OK%22)
|
### [Mongo Express](https://github.com/mongo-express/mongo-express) Web GUI [🔎 →](https://www.shodan.io/search?query=%22Set-Cookie%3A+mongo-express%3D%22+%22200+OK%22)
|
||||||
|
|
||||||
@@ -289,7 +291,7 @@ Like the [infamous phpMyAdmin](https://www.cvedetails.com/vulnerability-list/ven
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/mongo-express.png"
|
src="/public/static/images/notes/shodan-search-queries/mongo-express.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="395"
|
height="395"
|
||||||
alt="Example: Mongo Express GUI"
|
alt="Example: Mongo Express GUI"
|
||||||
@@ -302,7 +304,7 @@ Like the [infamous phpMyAdmin](https://www.cvedetails.com/vulnerability-list/ven
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/jenkins.png"
|
src="/public/static/images/notes/shodan-search-queries/jenkins.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="225"
|
height="225"
|
||||||
alt="Example: Jenkins CI"
|
alt="Example: Jenkins CI"
|
||||||
@@ -353,7 +355,7 @@ Lantronix password port:30718 -secured
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/citrix.png"
|
src="/public/static/images/notes/shodan-search-queries/citrix.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="273"
|
height="273"
|
||||||
alt="Example: Citrix Virtual Apps"
|
alt="Example: Citrix Virtual Apps"
|
||||||
@@ -386,7 +388,7 @@ Telnet Configuration: [🔎 →](https://www.shodan.io/search?query=%22Polycom+C
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/polycom.png"
|
src="/public/static/images/notes/shodan-search-queries/polycom.png"
|
||||||
width="550"
|
width="550"
|
||||||
height="251"
|
height="251"
|
||||||
alt="Example: Polycom Video Conferencing"
|
alt="Example: Polycom Video Conferencing"
|
||||||
@@ -419,7 +421,7 @@ HP-ILO-4 !"HP-ILO-4/2.53" !"HP-ILO-4/2.54" !"HP-ILO-4/2.55" !"HP-ILO-4/2.60" !"H
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/owa2007.png"
|
src="/public/static/images/notes/shodan-search-queries/owa2007.png"
|
||||||
width="450"
|
width="450"
|
||||||
height="494"
|
height="494"
|
||||||
alt="Example: OWA for Exchange 2007"
|
alt="Example: OWA for Exchange 2007"
|
||||||
@@ -432,7 +434,7 @@ HP-ILO-4 !"HP-ILO-4/2.53" !"HP-ILO-4/2.54" !"HP-ILO-4/2.55" !"HP-ILO-4/2.60" !"H
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/owa2010.png"
|
src="/public/static/images/notes/shodan-search-queries/owa2010.png"
|
||||||
width="450"
|
width="450"
|
||||||
height="429"
|
height="429"
|
||||||
alt="Example: OWA for Exchange 2010"
|
alt="Example: OWA for Exchange 2010"
|
||||||
@@ -445,7 +447,7 @@ HP-ILO-4 !"HP-ILO-4/2.53" !"HP-ILO-4/2.54" !"HP-ILO-4/2.55" !"HP-ILO-4/2.60" !"H
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/owa2013.png"
|
src="/public/static/images/notes/shodan-search-queries/owa2013.png"
|
||||||
width="580"
|
width="580"
|
||||||
height="230"
|
height="230"
|
||||||
alt="Example: OWA for Exchange 2013/2016"
|
alt="Example: OWA for Exchange 2013/2016"
|
||||||
@@ -494,7 +496,7 @@ Concerning [default network shares of QuickBooks](https://quickbooks.intuit.com/
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/iomega.png"
|
src="/public/static/images/notes/shodan-search-queries/iomega.png"
|
||||||
width="600"
|
width="600"
|
||||||
height="215"
|
height="215"
|
||||||
alt="Example: Iomega / LenovoEMC NAS Drives"
|
alt="Example: Iomega / LenovoEMC NAS Drives"
|
||||||
@@ -507,7 +509,7 @@ Redirecting sencha port:9000
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/buffalo.png"
|
src="/public/static/images/notes/shodan-search-queries/buffalo.png"
|
||||||
width="580"
|
width="580"
|
||||||
height="140"
|
height="140"
|
||||||
alt="Example: Buffalo TeraStation NAS Drives"
|
alt="Example: Buffalo TeraStation NAS Drives"
|
||||||
@@ -520,7 +522,7 @@ Redirecting sencha port:9000
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/logitech.png"
|
src="/public/static/images/notes/shodan-search-queries/logitech.png"
|
||||||
width="500"
|
width="500"
|
||||||
height="224"
|
height="224"
|
||||||
alt="Example: Logitech Media Servers"
|
alt="Example: Logitech Media Servers"
|
||||||
@@ -539,7 +541,7 @@ Redirecting sencha port:9000
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/plexpy.png"
|
src="/public/static/images/notes/shodan-search-queries/plexpy.png"
|
||||||
width="560"
|
width="560"
|
||||||
height="266"
|
height="266"
|
||||||
alt="Example: PlexPy / Tautulli Dashboards"
|
alt="Example: PlexPy / Tautulli Dashboards"
|
||||||
@@ -585,7 +587,12 @@ html:"DVR_H264 ActiveX"
|
|||||||
"Serial Number:" "Built:" "Server: HP HTTP"
|
"Serial Number:" "Built:" "Server: HP HTTP"
|
||||||
```
|
```
|
||||||
|
|
||||||
<Image src="/static/images/notes/shodan-search-queries/hp.png" width="700" height="272" alt="Example: HP Printers" />
|
<Image
|
||||||
|
src="/public/static/images/notes/shodan-search-queries/hp.png"
|
||||||
|
width="700"
|
||||||
|
height="272"
|
||||||
|
alt="Example: HP Printers"
|
||||||
|
/>
|
||||||
|
|
||||||
### Xerox Copiers/Printers [🔎 →](https://www.shodan.io/search?query=ssl%3A%22Xerox+Generic+Root%22)
|
### Xerox Copiers/Printers [🔎 →](https://www.shodan.io/search?query=ssl%3A%22Xerox+Generic+Root%22)
|
||||||
|
|
||||||
@@ -594,7 +601,7 @@ ssl:"Xerox Generic Root"
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/xerox.png"
|
src="/public/static/images/notes/shodan-search-queries/xerox.png"
|
||||||
width="620"
|
width="620"
|
||||||
height="263"
|
height="263"
|
||||||
alt="Example: Xerox Copiers/Printers"
|
alt="Example: Xerox Copiers/Printers"
|
||||||
@@ -611,7 +618,7 @@ ssl:"Xerox Generic Root"
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/epson.png"
|
src="/public/static/images/notes/shodan-search-queries/epson.png"
|
||||||
width="550"
|
width="550"
|
||||||
height="308"
|
height="308"
|
||||||
alt="Example: Epson Printers"
|
alt="Example: Epson Printers"
|
||||||
@@ -628,7 +635,7 @@ ssl:"Xerox Generic Root"
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/canon.png"
|
src="/public/static/images/notes/shodan-search-queries/canon.png"
|
||||||
width="550"
|
width="550"
|
||||||
height="195"
|
height="195"
|
||||||
alt="Example: Canon Printers"
|
alt="Example: Canon Printers"
|
||||||
@@ -645,7 +652,7 @@ ssl:"Xerox Generic Root"
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/yamaha.png"
|
src="/public/static/images/notes/shodan-search-queries/yamaha.png"
|
||||||
width="550"
|
width="550"
|
||||||
height="349"
|
height="349"
|
||||||
alt="Example: Yamaha Stereos"
|
alt="Example: Yamaha Stereos"
|
||||||
@@ -682,7 +689,7 @@ title:"OctoPrint" -title:"Login" http.favicon.hash:1307375944
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/octoprint.png"
|
src="/public/static/images/notes/shodan-search-queries/octoprint.png"
|
||||||
width="700"
|
width="700"
|
||||||
height="335"
|
height="335"
|
||||||
alt="Example: OctoPrint 3D Printers"
|
alt="Example: OctoPrint 3D Printers"
|
||||||
@@ -695,7 +702,7 @@ title:"OctoPrint" -title:"Login" http.favicon.hash:1307375944
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src="/static/images/notes/shodan-search-queries/eth.png"
|
src="/public/static/images/notes/shodan-search-queries/eth.png"
|
||||||
width="800"
|
width="800"
|
||||||
height="141"
|
height="141"
|
||||||
alt="Example: Etherium Miners"
|
alt="Example: Etherium Miners"
|
||||||
|
@@ -10,13 +10,11 @@ tags:
|
|||||||
image: "/static/images/notes/y2k-sandbox/screenshot.png"
|
image: "/static/images/notes/y2k-sandbox/screenshot.png"
|
||||||
---
|
---
|
||||||
|
|
||||||
import Figure from "../components/media/Figure";
|
|
||||||
|
|
||||||
A few months ago, I stumbled upon [my first website ever](https://jakejarvis.github.io/my-first-website/) on an old floppy disk. Despite the instant cringing, I [uploaded it](https://github.com/jakejarvis/my-first-website) to GitHub, [collected other iterations](/previously/), and made an [#awesome-list](https://github.com/jakejarvis/awesome-first-code) of others who were brave and/or shameless enough to do the same. But why not take that ~~one~~ 1,000 steps further?
|
A few months ago, I stumbled upon [my first website ever](https://jakejarvis.github.io/my-first-website/) on an old floppy disk. Despite the instant cringing, I [uploaded it](https://github.com/jakejarvis/my-first-website) to GitHub, [collected other iterations](/previously/), and made an [#awesome-list](https://github.com/jakejarvis/awesome-first-code) of others who were brave and/or shameless enough to do the same. But why not take that ~~one~~ 1,000 steps further?
|
||||||
|
|
||||||
Introducing the [**Y2K Sandbox**](https://y2k.app/) — with fully-featured, fully-isolated, on-demand [**Windows Millennium Edition®**](https://www.youtube.com/watch?v=CaNDeyYP98A) virtual machines, simply to experience my first website in its natural Internet Explorer 5 habitat. And maybe play some [3D Pinball: Space Cadet](https://en.wikipedia.org/wiki/Full_Tilt!_Pinball#3D_Pinball_for_Windows_%E2%80%93_Space_Cadet). Oh, and [Microsoft Bob](https://en.wikipedia.org/wiki/Microsoft_Bob) is there too if you want to say hello and catch up. 🤓
|
Introducing the [**Y2K Sandbox**](https://y2k.app/) — with fully-featured, fully-isolated, on-demand [**Windows Millennium Edition®**](https://www.youtube.com/watch?v=CaNDeyYP98A) virtual machines, simply to experience my first website in its natural Internet Explorer 5 habitat. And maybe play some [3D Pinball: Space Cadet](https://en.wikipedia.org/wiki/Full_Tilt!_Pinball#3D_Pinball_for_Windows_%E2%80%93_Space_Cadet). Oh, and [Microsoft Bob](https://en.wikipedia.org/wiki/Microsoft_Bob) is there too if you want to say hello and catch up. 🤓
|
||||||
|
|
||||||
<Figure src="/static/images/notes/y2k-sandbox/screenshot.png" width="865" height="649" priority>
|
<Figure src="/public/static/images/notes/y2k-sandbox/screenshot.png" width="865" height="649" priority>
|
||||||
[**Play in the Y2K Sandbox, at your own risk.**](https://y2k.app/)
|
[**Play in the Y2K Sandbox, at your own risk.**](https://y2k.app/)
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
@@ -28,7 +26,7 @@ The frontend is _much_ simpler with [a few lines of JavaScript](https://github.c
|
|||||||
|
|
||||||
I must give credit to both [charlie.bz](https://charlie.bz/) and [benjojo.co.uk](https://benjojo.co.uk/), similar websites I was enamored with when they were posted on Hacker News a few years ago. Think we'll see some websites like these with Windows 29 in a decade?
|
I must give credit to both [charlie.bz](https://charlie.bz/) and [benjojo.co.uk](https://benjojo.co.uk/), similar websites I was enamored with when they were posted on Hacker News a few years ago. Think we'll see some websites like these with Windows 29 in a decade?
|
||||||
|
|
||||||
<Figure src="/static/images/notes/y2k-sandbox/windows-me.png" width="320" height="92">
|
<Figure src="/public/static/images/notes/y2k-sandbox/windows-me.png" width="320" height="92">
|
||||||
**@microsoft** Please don't sue me.
|
**@microsoft** Please don't sue me.
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
|
@@ -45,10 +45,10 @@
|
|||||||
"is-absolute-url": "^4.0.1",
|
"is-absolute-url": "^4.0.1",
|
||||||
"is-email-like": "^2.0.0",
|
"is-email-like": "^2.0.0",
|
||||||
"markdown-to-jsx": "^7.1.6",
|
"markdown-to-jsx": "^7.1.6",
|
||||||
"mdx-bundler": "^8.0.1",
|
|
||||||
"modern-normalize": "github:sindresorhus/modern-normalize#1fc6b5a86676b7ac8abc62d04d6080f92debc70f",
|
"modern-normalize": "github:sindresorhus/modern-normalize#1fc6b5a86676b7ac8abc62d04d6080f92debc70f",
|
||||||
"next": "v12.0.8",
|
"next": "^12.0.8",
|
||||||
"next-compose-plugins": "^2.2.1",
|
"next-compose-plugins": "^2.2.1",
|
||||||
|
"next-mdx-remote": "4.0.0-rc.1",
|
||||||
"next-seo": "^4.28.1",
|
"next-seo": "^4.28.1",
|
||||||
"next-themes": "^0.0.15",
|
"next-themes": "^0.0.15",
|
||||||
"node-fetch": "^3.1.1",
|
"node-fetch": "^3.1.1",
|
||||||
@@ -85,11 +85,10 @@
|
|||||||
"@types/react-is": "^17.0.3",
|
"@types/react-is": "^17.0.3",
|
||||||
"@types/remove-markdown": "^0.3.1",
|
"@types/remove-markdown": "^0.3.1",
|
||||||
"@types/sanitize-html": "^2.6.2",
|
"@types/sanitize-html": "^2.6.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||||
"@typescript-eslint/parser": "^5.9.1",
|
"@typescript-eslint/parser": "^5.10.0",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.14.11",
|
|
||||||
"eslint": "~8.7.0",
|
"eslint": "~8.7.0",
|
||||||
"eslint-config-next": "~12.0.8",
|
"eslint-config-next": "~12.0.8",
|
||||||
"eslint-config-prettier": "~8.3.0",
|
"eslint-config-prettier": "~8.3.0",
|
||||||
|
@@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
import { DefaultSeo, SocialProfileJsonLd } from "next-seo";
|
import { DefaultSeo, SocialProfileJsonLd } from "next-seo";
|
||||||
import * as Fathom from "fathom-client";
|
import * as Fathom from "fathom-client";
|
||||||
import Layout from "../components/Layout";
|
import Layout from "../components/Layout/Layout";
|
||||||
import * as config from "../lib/config";
|
import * as config from "../lib/config";
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import Video from "../components/media/Video";
|
import Video from "../components/Video/Video";
|
||||||
import { TapeIcon } from "../components/icons";
|
import { TapeIcon } from "../components/Icons";
|
||||||
|
|
||||||
import thumbnail from "../public/static/images/birthday/thumb.png";
|
import thumbnail from "../public/static/images/birthday/thumb.png";
|
||||||
|
|
||||||
@@ -22,22 +22,9 @@ const Birthday = () => (
|
|||||||
|
|
||||||
<Content>
|
<Content>
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/birthday/birthday.webm"
|
||||||
{ src: "/static/images/birthday/birthday.webm", type: "video/webm" },
|
mp4="/static/images/birthday/birthday.mp4"
|
||||||
{ src: "/static/images/birthday/birthday.mp4", type: "video/mp4" },
|
thumbnail={thumbnail.src}
|
||||||
]}
|
|
||||||
config={{
|
|
||||||
// @ts-ignore
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: thumbnail.src,
|
|
||||||
controlsList: "nodownload",
|
|
||||||
preload: "metadata",
|
|
||||||
autoPlay: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={true}
|
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
</>
|
</>
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import { BotIcon } from "../components/icons";
|
import { BotIcon } from "../components/Icons";
|
||||||
|
|
||||||
import cliImg from "../public/static/images/cli/screenshot.png";
|
import cliImg from "../public/static/images/cli/screenshot.png";
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import ContactForm from "../components/contact/ContactForm";
|
import ContactForm from "../components/ContactForm/ContactForm";
|
||||||
import { MailIcon, LockIcon } from "../components/icons";
|
import { MailIcon, LockIcon } from "../components/Icons";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
|
|
||||||
const Contact = () => (
|
const Contact = () => (
|
||||||
<>
|
<>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import Video from "../components/media/Video";
|
import Video from "../components/Video/Video";
|
||||||
|
|
||||||
import thumbnail from "../public/static/images/hillary/thumb.png";
|
import thumbnail from "../public/static/images/hillary/thumb.png";
|
||||||
|
|
||||||
@@ -18,31 +18,10 @@ const Hillary = () => (
|
|||||||
<Title>My Brief Apperance in Hillary Clinton's DNC Video</Title>
|
<Title>My Brief Apperance in Hillary Clinton's DNC Video</Title>
|
||||||
<Content>
|
<Content>
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/hillary/convention-720p.webm"
|
||||||
{ src: "/static/images/hillary/convention-720p.webm", type: "video/webm" },
|
mp4="/static/images/hillary/convention-720p.mp4"
|
||||||
{ src: "/static/images/hillary/convention-720p.mp4", type: "video/mp4" },
|
thumbnail={thumbnail.src}
|
||||||
]}
|
subs="/static/images/hillary/subs.en.vtt"
|
||||||
config={{
|
|
||||||
// @ts-ignore
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: thumbnail.src,
|
|
||||||
controlsList: "nodownload",
|
|
||||||
preload: "metadata",
|
|
||||||
autoPlay: false,
|
|
||||||
},
|
|
||||||
tracks: [
|
|
||||||
{
|
|
||||||
kind: "subtitles",
|
|
||||||
src: "/static/images/hillary/subs.en.vtt",
|
|
||||||
srcLang: "en",
|
|
||||||
label: "English",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={true}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="copyright">
|
<p className="copyright">
|
||||||
|
112
pages/index.tsx
@@ -1,5 +1,5 @@
|
|||||||
import ColorLink from "../components/home/ColorLink";
|
import ColorfulLink from "../components/ColorfulLink/ColorfulLink";
|
||||||
import { WaveIcon, LockIcon } from "../components/icons";
|
import { WaveIcon, LockIcon } from "../components/Icons";
|
||||||
|
|
||||||
const Index = () => (
|
const Index = () => (
|
||||||
<>
|
<>
|
||||||
@@ -12,7 +12,7 @@ const Index = () => (
|
|||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
I'm a frontend web developer based in{" "}
|
I'm a frontend web developer based in{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://www.youtube-nocookie.com/embed/rLwbzGyC6t4?hl=en&fs=1&showinfo=1&rel=0&iv_load_policy=3"
|
href="https://www.youtube-nocookie.com/embed/rLwbzGyC6t4?hl=en&fs=1&showinfo=1&rel=0&iv_load_policy=3"
|
||||||
title='"Boston Accent Trailer - Late Night with Seth Meyers" on YouTube'
|
title='"Boston Accent Trailer - Late Night with Seth Meyers" on YouTube'
|
||||||
lightColor="#fb4d42"
|
lightColor="#fb4d42"
|
||||||
@@ -20,13 +20,13 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
Boston
|
Boston
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
.
|
.
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
I specialize in{" "}
|
I specialize in{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://stackoverflow.blog/2018/01/11/brutal-lifecycle-javascript-frameworks/"
|
href="https://stackoverflow.blog/2018/01/11/brutal-lifecycle-javascript-frameworks/"
|
||||||
title='"The Brutal Lifecycle of JavaScript Frameworks" by Ian Allen'
|
title='"The Brutal Lifecycle of JavaScript Frameworks" by Ian Allen'
|
||||||
lightColor="#1091b3"
|
lightColor="#1091b3"
|
||||||
@@ -34,9 +34,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
modern JS frameworks
|
modern JS frameworks
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
and{" "}
|
and{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="http://vanilla-js.com/"
|
href="http://vanilla-js.com/"
|
||||||
title="The best JS framework in the world by Eric Wastl"
|
title="The best JS framework in the world by Eric Wastl"
|
||||||
lightColor="#f48024"
|
lightColor="#f48024"
|
||||||
@@ -44,9 +44,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
vanilla JavaScript
|
vanilla JavaScript
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
to make nifty{" "}
|
to make nifty{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://jamstack.wtf/"
|
href="https://jamstack.wtf/"
|
||||||
title="WTF is JAMstack?"
|
title="WTF is JAMstack?"
|
||||||
lightColor="#04a699"
|
lightColor="#04a699"
|
||||||
@@ -54,9 +54,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
JAMstack sites
|
JAMstack sites
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
with dynamic{" "}
|
with dynamic{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://nodejs.org/en/"
|
href="https://nodejs.org/en/"
|
||||||
title="Node.js Official Website"
|
title="Node.js Official Website"
|
||||||
lightColor="#6fbc4e"
|
lightColor="#6fbc4e"
|
||||||
@@ -64,9 +64,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
Node.js
|
Node.js
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
services. But I'm fluent in non-buzzwords like{" "}
|
services. But I'm fluent in non-buzzwords like{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://stitcher.io/blog/php-in-2020"
|
href="https://stitcher.io/blog/php-in-2020"
|
||||||
title='"PHP in 2020" by Brent Roose'
|
title='"PHP in 2020" by Brent Roose'
|
||||||
lightColor="#8892bf"
|
lightColor="#8892bf"
|
||||||
@@ -74,9 +74,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
PHP
|
PHP
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
,{" "}
|
,{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://www.ruby-lang.org/en/"
|
href="https://www.ruby-lang.org/en/"
|
||||||
title="Ruby Official Website"
|
title="Ruby Official Website"
|
||||||
lightColor="#d34135"
|
lightColor="#d34135"
|
||||||
@@ -84,9 +84,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
Ruby
|
Ruby
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
, and{" "}
|
, and{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://golang.org/"
|
href="https://golang.org/"
|
||||||
title="Golang Official Website"
|
title="Golang Official Website"
|
||||||
lightColor="#00acd7"
|
lightColor="#00acd7"
|
||||||
@@ -94,13 +94,13 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
Go
|
Go
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
too.
|
too.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Whenever possible, I also apply my experience in{" "}
|
Whenever possible, I also apply my experience in{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://github.com/jakejarvis/awesome-shodan-queries"
|
href="https://github.com/jakejarvis/awesome-shodan-queries"
|
||||||
title="jakejarvis/awesome-shodan-queries on GitHub"
|
title="jakejarvis/awesome-shodan-queries on GitHub"
|
||||||
lightColor="#00b81a"
|
lightColor="#00b81a"
|
||||||
@@ -108,9 +108,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
application security
|
application security
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
,{" "}
|
,{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://www.cloudflare.com/learning/serverless/what-is-serverless/"
|
href="https://www.cloudflare.com/learning/serverless/what-is-serverless/"
|
||||||
title='"What is serverless computing?" on Cloudflare'
|
title='"What is serverless computing?" on Cloudflare'
|
||||||
lightColor="#0098ec"
|
lightColor="#0098ec"
|
||||||
@@ -118,9 +118,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
serverless stacks
|
serverless stacks
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
, and{" "}
|
, and{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://xkcd.com/1319/"
|
href="https://xkcd.com/1319/"
|
||||||
title='"Automation" on xkcd'
|
title='"Automation" on xkcd'
|
||||||
lightColor="#ff6200"
|
lightColor="#ff6200"
|
||||||
@@ -128,46 +128,46 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
DevOps automation
|
DevOps automation
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
I fell in love with{" "}
|
I fell in love with{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="/previously/"
|
href="/previously/"
|
||||||
title="My Terrible, Horrible, No Good, Very Bad First Websites"
|
title="My Terrible, Horrible, No Good, Very Bad First Websites"
|
||||||
lightColor="#4169e1"
|
lightColor="#4169e1"
|
||||||
darkColor="#8ca9ff"
|
darkColor="#8ca9ff"
|
||||||
>
|
>
|
||||||
frontend web design
|
frontend web design
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
and{" "}
|
and{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="/notes/my-first-code/"
|
href="/notes/my-first-code/"
|
||||||
title="Jake's Bulletin Board, circa 2003"
|
title="Jake's Bulletin Board, circa 2003"
|
||||||
lightColor="#9932cc"
|
lightColor="#9932cc"
|
||||||
darkColor="#d588fb"
|
darkColor="#d588fb"
|
||||||
>
|
>
|
||||||
backend programming
|
backend programming
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
back when my only source of income was{" "}
|
back when my only source of income was{" "}
|
||||||
<span className="birthday">
|
<span className="birthday">
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="/birthday/"
|
href="/birthday/"
|
||||||
title="🎉 Cranky Birthday Boy on VHS Tape 📼"
|
title="🎉 Cranky Birthday Boy on VHS Tape 📼"
|
||||||
lightColor="#e40088"
|
lightColor="#e40088"
|
||||||
darkColor="#fd40b1"
|
darkColor="#fd40b1"
|
||||||
>
|
>
|
||||||
the Tooth Fairy
|
the Tooth Fairy
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
</span>
|
</span>
|
||||||
. <span className="quiet">I've improved a bit since then, I think...</span>
|
. <span className="quiet">I've improved a bit since then, I think...</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Over the years, some of my side projects{" "}
|
Over the years, some of my side projects{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://tuftsdaily.com/news/2012/04/06/student-designs-iphone-joeytracker-app/"
|
href="https://tuftsdaily.com/news/2012/04/06/student-designs-iphone-joeytracker-app/"
|
||||||
title='"Student designs iPhone JoeyTracker app" on The Tufts Daily'
|
title='"Student designs iPhone JoeyTracker app" on The Tufts Daily'
|
||||||
lightColor="#ff1b1b"
|
lightColor="#ff1b1b"
|
||||||
@@ -175,16 +175,16 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
have
|
have
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="/leo/"
|
href="/leo/"
|
||||||
title="Powncer segment on The Lab with Leo Laporte (G4techTV)"
|
title="Powncer segment on The Lab with Leo Laporte (G4techTV)"
|
||||||
lightColor="#f78200"
|
lightColor="#f78200"
|
||||||
darkColor="#fd992a"
|
darkColor="#fd992a"
|
||||||
>
|
>
|
||||||
been
|
been
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://www.google.com/books/edition/The_Facebook_Effect/RRUkLhyGZVgC?hl=en&gbpv=1&dq=%22jake%20jarvis%22&pg=PA226&printsec=frontcover&bsq=%22jake%20jarvis%22"
|
href="https://www.google.com/books/edition/The_Facebook_Effect/RRUkLhyGZVgC?hl=en&gbpv=1&dq=%22jake%20jarvis%22&pg=PA226&printsec=frontcover&bsq=%22jake%20jarvis%22"
|
||||||
title='"The Facebook Effect" by David Kirkpatrick (Google Books)'
|
title='"The Facebook Effect" by David Kirkpatrick (Google Books)'
|
||||||
lightColor="#f2b702"
|
lightColor="#f2b702"
|
||||||
@@ -192,8 +192,8 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
featured
|
featured
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://money.cnn.com/2007/06/01/technology/facebookplatform.fortune/index.htm"
|
href="https://money.cnn.com/2007/06/01/technology/facebookplatform.fortune/index.htm"
|
||||||
title='"The new Facebook is on a roll" on CNN Money'
|
title='"The new Facebook is on a roll" on CNN Money'
|
||||||
lightColor="#5ebd3e"
|
lightColor="#5ebd3e"
|
||||||
@@ -201,8 +201,8 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
by
|
by
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://www.wired.com/2007/04/our-web-servers/"
|
href="https://www.wired.com/2007/04/our-web-servers/"
|
||||||
title='"Middio: A YouTube Scraper for Major Label Music Videos" on Wired'
|
title='"Middio: A YouTube Scraper for Major Label Music Videos" on Wired'
|
||||||
lightColor="#009cdf"
|
lightColor="#009cdf"
|
||||||
@@ -210,8 +210,8 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
various
|
various
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://gigaom.com/2009/10/06/fresh-faces-in-tech-10-kid-entrepreneurs-to-watch/6/"
|
href="https://gigaom.com/2009/10/06/fresh-faces-in-tech-10-kid-entrepreneurs-to-watch/6/"
|
||||||
title='"Fresh Faces in Tech: 10 Kid Entrepreneurs to Watch" on Gigaom'
|
title='"Fresh Faces in Tech: 10 Kid Entrepreneurs to Watch" on Gigaom'
|
||||||
lightColor="#3e49bb"
|
lightColor="#3e49bb"
|
||||||
@@ -219,8 +219,8 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
media
|
media
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://adage.com/article/small-agency-diary/client-ceo-s-son/116723/"
|
href="https://adage.com/article/small-agency-diary/client-ceo-s-son/116723/"
|
||||||
title='"Your Next Client? The CEO's Son" on Advertising Age'
|
title='"Your Next Client? The CEO's Son" on Advertising Age'
|
||||||
lightColor="#973999"
|
lightColor="#973999"
|
||||||
@@ -228,13 +228,13 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
outlets
|
outlets
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You can find more of my work on{" "}
|
You can find more of my work on{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://github.com/jakejarvis"
|
href="https://github.com/jakejarvis"
|
||||||
title="Jake Jarvis on GitHub"
|
title="Jake Jarvis on GitHub"
|
||||||
lightColor="#8d4eff"
|
lightColor="#8d4eff"
|
||||||
@@ -242,9 +242,9 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
GitHub
|
GitHub
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
and{" "}
|
and{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://www.linkedin.com/in/jakejarvis/"
|
href="https://www.linkedin.com/in/jakejarvis/"
|
||||||
title="Jake Jarvis on LinkedIn"
|
title="Jake Jarvis on LinkedIn"
|
||||||
lightColor="#0073b1"
|
lightColor="#0073b1"
|
||||||
@@ -252,18 +252,18 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
LinkedIn
|
LinkedIn
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
. I'm always available to connect over{" "}
|
. I'm always available to connect over{" "}
|
||||||
<ColorLink href="/contact/" title="Send an email" lightColor="#de0c0c" darkColor="#ff5050">
|
<ColorfulLink href="/contact/" title="Send an email" lightColor="#de0c0c" darkColor="#ff5050">
|
||||||
email
|
email
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
<sup className="monospace pgp_key">
|
<sup className="monospace pgp_key">
|
||||||
<ColorLink href="/pubkey.asc" title="My Public Key" lightColor="#757575" darkColor="#959595" external>
|
<ColorfulLink href="/pubkey.asc" title="My Public Key" lightColor="#757575" darkColor="#959595" external>
|
||||||
<LockIcon className="icon" /> 2B0C 9CF2 51E6 9A39
|
<LockIcon className="icon" /> 2B0C 9CF2 51E6 9A39
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
</sup>
|
</sup>
|
||||||
,{" "}
|
,{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="https://twitter.com/jakejarvis"
|
href="https://twitter.com/jakejarvis"
|
||||||
title="Jake Jarvis on Twitter"
|
title="Jake Jarvis on Twitter"
|
||||||
lightColor="#00acee"
|
lightColor="#00acee"
|
||||||
@@ -271,16 +271,16 @@ const Index = () => (
|
|||||||
external
|
external
|
||||||
>
|
>
|
||||||
Twitter
|
Twitter
|
||||||
</ColorLink>
|
</ColorfulLink>
|
||||||
, or{" "}
|
, or{" "}
|
||||||
<ColorLink
|
<ColorfulLink
|
||||||
href="sms:+1-617-917-3737"
|
href="sms:+1-617-917-3737"
|
||||||
title="Send SMS to +1 (617) 917-3737"
|
title="Send SMS to +1 (617) 917-3737"
|
||||||
lightColor="#6fcc01"
|
lightColor="#6fcc01"
|
||||||
darkColor="#8edb34"
|
darkColor="#8edb34"
|
||||||
>
|
>
|
||||||
SMS
|
SMS
|
||||||
</ColorLink>{" "}
|
</ColorfulLink>{" "}
|
||||||
as well!
|
as well!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import Video from "../components/media/Video";
|
import Video from "../components/Video/Video";
|
||||||
|
|
||||||
import thumbnail from "../public/static/images/leo/thumb.png";
|
import thumbnail from "../public/static/images/leo/thumb.png";
|
||||||
|
|
||||||
@@ -19,31 +19,10 @@ const Leo = () => (
|
|||||||
|
|
||||||
<Content>
|
<Content>
|
||||||
<Video
|
<Video
|
||||||
url={[
|
webm="/static/images/leo/leo.webm"
|
||||||
{ src: "/static/images/leo/leo.webm", type: "video/webm" },
|
mp4="/static/images/leo/leo.mp4"
|
||||||
{ src: "/static/images/leo/leo.mp4", type: "video/mp4" },
|
thumbnail={thumbnail.src}
|
||||||
]}
|
subs="/static/images/leo/subs.en.vtt"
|
||||||
config={{
|
|
||||||
// @ts-ignore
|
|
||||||
file: {
|
|
||||||
attributes: {
|
|
||||||
poster: thumbnail.src,
|
|
||||||
controlsList: "nodownload",
|
|
||||||
preload: "metadata",
|
|
||||||
autoPlay: false,
|
|
||||||
},
|
|
||||||
tracks: [
|
|
||||||
{
|
|
||||||
kind: "subtitles",
|
|
||||||
src: "/static/images/leo/subs.en.vtt",
|
|
||||||
srcLang: "en",
|
|
||||||
label: "English",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
controls={true}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="copyright">
|
<p className="copyright">
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import { LicenseIcon } from "../components/icons";
|
import { LicenseIcon } from "../components/Icons";
|
||||||
|
|
||||||
const License = () => (
|
const License = () => (
|
||||||
<>
|
<>
|
||||||
|
@@ -1,20 +1,17 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import { InView } from "react-intersection-observer";
|
import { InView } from "react-intersection-observer";
|
||||||
import { NextSeo, ArticleJsonLd } from "next-seo";
|
import { NextSeo, ArticleJsonLd } from "next-seo";
|
||||||
|
import { MDXRemote } from "next-mdx-remote";
|
||||||
import { escape } from "html-escaper";
|
import { escape } from "html-escaper";
|
||||||
import { getMDXComponent } from "mdx-bundler/client";
|
import Content from "../../components/Content/Content";
|
||||||
import Content from "../../components/Content";
|
import Meta from "../../components/NoteMeta/NoteMeta";
|
||||||
import Meta from "../../components/notes/Meta";
|
import Comments from "../../components/Comments/Comments";
|
||||||
import Comments from "../../components/notes/Comments";
|
import * as mdxComponents from "../../lib/mdx-components";
|
||||||
import CustomCode from "../../components/code-block/Code";
|
|
||||||
import { getNote, getNoteSlugs } from "../../lib/parse-notes";
|
import { getNote, getNoteSlugs } from "../../lib/parse-notes";
|
||||||
import * as config from "../../lib/config";
|
import * as config from "../../lib/config";
|
||||||
import type { GetStaticProps, GetStaticPaths } from "next";
|
import type { GetStaticProps, GetStaticPaths } from "next";
|
||||||
import type { NoteType } from "../../types";
|
import type { NoteType } from "../../types";
|
||||||
|
|
||||||
const Note = ({ frontMatter, mdxSource }: NoteType) => {
|
const Note = ({ frontMatter, source }: NoteType) => {
|
||||||
const MDXComponent = useMemo(() => getMDXComponent(mdxSource, { process }), [mdxSource]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
@@ -56,7 +53,7 @@ const Note = ({ frontMatter, mdxSource }: NoteType) => {
|
|||||||
|
|
||||||
<Meta {...frontMatter} />
|
<Meta {...frontMatter} />
|
||||||
<Content>
|
<Content>
|
||||||
<MDXComponent components={{ code: CustomCode }} />
|
<MDXRemote {...source} components={{ ...mdxComponents }} lazy />
|
||||||
</Content>
|
</Content>
|
||||||
{frontMatter.noComments !== true && (
|
{frontMatter.noComments !== true && (
|
||||||
<InView rootMargin="140px" triggerOnce={true} fallbackInView={true}>
|
<InView rootMargin="140px" triggerOnce={true} fallbackInView={true}>
|
||||||
@@ -72,12 +69,12 @@ const Note = ({ frontMatter, mdxSource }: NoteType) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||||
const { frontMatter, mdxSource } = await getNote(params.slug as string);
|
const { frontMatter, source } = await getNote(params.slug as string);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
frontMatter,
|
frontMatter,
|
||||||
mdxSource,
|
source,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import List from "../../components/notes/List";
|
import List from "../../components/NotesList/NotesList";
|
||||||
import { getAllNotes } from "../../lib/parse-notes";
|
import { getAllNotes } from "../../lib/parse-notes";
|
||||||
import type { GetStaticProps } from "next";
|
import type { GetStaticProps } from "next";
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import { FloppyIcon, SirenIcon } from "../components/icons";
|
import { FloppyIcon, SirenIcon } from "../components/Icons";
|
||||||
|
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
import img_wayback from "../public/static/images/previously/wayback.png";
|
import img_wayback from "../public/static/images/previously/wayback.png";
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import { PrivacyIcon } from "../components/icons";
|
import { PrivacyIcon } from "../components/Icons";
|
||||||
|
|
||||||
import faunaImg from "../public/static/images/privacy/fauna_hits.png";
|
import faunaImg from "../public/static/images/privacy/fauna_hits.png";
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ const Privacy = () => (
|
|||||||
</a>{" "}
|
</a>{" "}
|
||||||
and{" "}
|
and{" "}
|
||||||
<a
|
<a
|
||||||
href="https://github.com/jakejarvis/jarv.is/blob/main/components/hits/Hits.tsx"
|
href="https://github.com/jakejarvis/jarv.is/blob/main/components/notes/HitCounter.tsx"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { graphql } from "@octokit/graphql";
|
import { graphql } from "@octokit/graphql";
|
||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import RepoCard from "../components/projects/RepoCard";
|
import RepoCard from "../components/RepositoryCard/RepositoryCard";
|
||||||
import { ProjectsIcon } from "../components/icons";
|
import { ProjectsIcon } from "../components/Icons";
|
||||||
import type { GetStaticProps } from "next";
|
import type { GetStaticProps } from "next";
|
||||||
import { RepoType } from "../types";
|
import { RepoType } from "../types";
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { NextSeo } from "next-seo";
|
import { NextSeo } from "next-seo";
|
||||||
import Content from "../components/Content";
|
import Content from "../components/Content/Content";
|
||||||
import Title from "../components/title/Title";
|
import Title from "../components/Title/Title";
|
||||||
import { LaptopIcon } from "../components/icons";
|
import { LaptopIcon } from "../components/Icons";
|
||||||
|
|
||||||
import desktopImg from "../public/static/images/uses/bigsur.png";
|
import desktopImg from "../public/static/images/uses/bigsur.png";
|
||||||
|
|
||||||
|
0
public/static/images/notes/bernie-sanders-bern-app-data/webinar-qa-1.png
Executable file → Normal file
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
0
public/static/images/notes/bernie-sanders-bern-app-data/webinar-qa-2.png
Executable file → Normal file
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
0
public/static/images/notes/bernie-sanders-bern-app-data/webinar-slide-1.png
Executable file → Normal file
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
0
public/static/images/notes/bernie-sanders-bern-app-data/webinar-slide-2.png
Executable file → Normal file
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
0
public/static/images/notes/how-to-pull-request-fork-github/step1.png
Executable file → Normal file
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
public/static/images/notes/how-to-pull-request-fork-github/step7-1.png
Executable file → Normal file
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/bennet.png
Executable file → Normal file
Before Width: | Height: | Size: 903 KiB After Width: | Height: | Size: 903 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/biden.png
Executable file → Normal file
Before Width: | Height: | Size: 501 KiB After Width: | Height: | Size: 501 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/booker.png
Executable file → Normal file
Before Width: | Height: | Size: 442 KiB After Width: | Height: | Size: 442 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/bullock.png
Executable file → Normal file
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 408 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/buttigeg.png
Executable file → Normal file
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/castro.png
Executable file → Normal file
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
0
public/static/images/notes/presidential-candidates-404-pages/delaney.png
Executable file → Normal file
Before Width: | Height: | Size: 393 KiB After Width: | Height: | Size: 393 KiB |