client: improve markdown rendering
This commit is contained in:
parent
60f2ab99b3
commit
c55ca681b4
15 changed files with 285 additions and 259 deletions
|
@ -1,41 +1,48 @@
|
||||||
|
.card {
|
||||||
|
max-width: var(--main-content);
|
||||||
|
margin: var(--gap) auto;
|
||||||
|
padding: 2;
|
||||||
|
border: 1px solid var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
background: #efefef;
|
background: #efefef;
|
||||||
}
|
}
|
||||||
|
|
||||||
.descriptionContainer {
|
.descriptionContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileNameContainer {
|
.fileNameContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileNameContainer {
|
.fileNameContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileNameContainer > div {
|
.fileNameContainer > div {
|
||||||
/* Override geist-ui styling */
|
/* Override geist-ui styling */
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionWrapper {
|
.actionWrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionWrapper .actions {
|
.actionWrapper .actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
|
||||||
|
|
||||||
const newText = `${before}**${selectedText}**${after}`
|
const newText = `${before}**${selectedText}**${after}`
|
||||||
setText(newText)
|
setText(newText)
|
||||||
|
|
||||||
// TODO; fails because settext async
|
|
||||||
textareaRef.current.setSelectionRange(before.length + 2, before.length + 2 + selectedText.length)
|
|
||||||
}
|
}
|
||||||
}, [setText, textareaRef])
|
}, [setText, textareaRef])
|
||||||
|
|
||||||
|
@ -50,8 +47,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
|
||||||
const selectedText = text.substring(selectionStart, selectionEnd)
|
const selectedText = text.substring(selectionStart, selectionEnd)
|
||||||
const newText = `${before}*${selectedText}*${after}`
|
const newText = `${before}*${selectedText}*${after}`
|
||||||
setText(newText)
|
setText(newText)
|
||||||
textareaRef.current.focus()
|
|
||||||
textareaRef.current.setSelectionRange(before.length + 1, before.length + 1 + selectedText.length)
|
|
||||||
}
|
}
|
||||||
}, [setText, textareaRef])
|
}, [setText, textareaRef])
|
||||||
|
|
||||||
|
@ -71,8 +66,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
|
||||||
}
|
}
|
||||||
const newText = `${before}${formattedText}${after}`
|
const newText = `${before}${formattedText}${after}`
|
||||||
setText(newText)
|
setText(newText)
|
||||||
textareaRef.current.focus()
|
|
||||||
textareaRef.current.setSelectionRange(before.length + 1, before.length + 1 + selectedText.length)
|
|
||||||
}
|
}
|
||||||
}, [setText, textareaRef])
|
}, [setText, textareaRef])
|
||||||
|
|
||||||
|
@ -92,8 +85,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
|
||||||
}
|
}
|
||||||
const newText = `${before}${formattedText}${after}`
|
const newText = `${before}${formattedText}${after}`
|
||||||
setText(newText)
|
setText(newText)
|
||||||
textareaRef.current.focus()
|
|
||||||
textareaRef.current.setSelectionRange(before.length + 1, before.length + 1 + selectedText.length)
|
|
||||||
}
|
}
|
||||||
}, [setText, textareaRef])
|
}, [setText, textareaRef])
|
||||||
|
|
||||||
|
|
|
@ -23,34 +23,6 @@ type Props = {
|
||||||
remove?: () => void
|
remove?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
|
|
||||||
return (<div className={styles.actionWrapper}>
|
|
||||||
<ButtonGroup className={styles.actions}>
|
|
||||||
<Tooltip text="Download">
|
|
||||||
<a href={`${rawLink}?download=true`} target="_blank" rel="noopener noreferrer">
|
|
||||||
<Button
|
|
||||||
scale={2 / 3} px={0.6}
|
|
||||||
icon={<Download />}
|
|
||||||
auto
|
|
||||||
aria-label="Download"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip text="Open raw in new tab">
|
|
||||||
<a href={rawLink} target="_blank" rel="noopener noreferrer">
|
|
||||||
<Button
|
|
||||||
scale={2 / 3} px={0.6}
|
|
||||||
icon={<ExternalLink />}
|
|
||||||
auto
|
|
||||||
aria-label="Open raw file in new tab"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</Tooltip>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const Document = ({ remove, title, content, setTitle, setContent, initialTab = 'edit', skeleton, handleOnContentChange }: Props) => {
|
const Document = ({ remove, title, content, setTitle, setContent, initialTab = 'edit', skeleton, handleOnContentChange }: Props) => {
|
||||||
const codeEditorRef = useRef<HTMLTextAreaElement>(null)
|
const codeEditorRef = useRef<HTMLTextAreaElement>(null)
|
||||||
const [tab, setTab] = useState(initialTab)
|
const [tab, setTab] = useState(initialTab)
|
||||||
|
@ -98,7 +70,7 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Spacer height={1} />
|
<Spacer height={1} />
|
||||||
<Card marginBottom={'var(--gap)'} marginTop={'var(--gap)'} style={{ maxWidth: 'var(--main-content)', margin: "0 auto" }}>
|
<div className={styles.card}>
|
||||||
<div className={styles.fileNameContainer}>
|
<div className={styles.fileNameContainer}>
|
||||||
<Input
|
<Input
|
||||||
placeholder="MyFile.md"
|
placeholder="MyFile.md"
|
||||||
|
@ -138,8 +110,7 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
</div >
|
</div >
|
||||||
</Card >
|
</div >
|
||||||
<Spacer height={1} />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ const ListItem = ({ post }: { post: any }) => {
|
||||||
|
|
||||||
<Divider h="1px" my={0} />
|
<Divider h="1px" my={0} />
|
||||||
|
|
||||||
<Card.Content >
|
<Card.Content>
|
||||||
{post.files.map((file: any) => {
|
{post.files.map((file: any) => {
|
||||||
return <FilenameInput key={file.id} title={file.title} />
|
return <FilenameInput key={file.id} title={file.title} />
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -49,7 +49,7 @@ const MarkdownPreview = ({ height = 500, fileId, content, title }: Props) => {
|
||||||
fetchPost()
|
fetchPost()
|
||||||
}, [content, fileId, title])
|
}, [content, fileId, title])
|
||||||
return (<>
|
return (<>
|
||||||
{isLoading ? <div>Loading...</div> : <article data-theme={theme} className={styles.markdownPreview} dangerouslySetInnerHTML={{ __html: preview }} style={{
|
{isLoading ? <div>Loading...</div> : <article className={styles.markdownPreview} dangerouslySetInnerHTML={{ __html: preview }} style={{
|
||||||
height
|
height
|
||||||
}} />}
|
}} />}
|
||||||
</>)
|
</>)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
.markdownPreview pre {
|
.markdownPreview pre {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-family: "Courier New", Courier, monospace;
|
font-family: "Courier New", Courier, monospace;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.42857143;
|
line-height: 1.42857143;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h1,
|
.markdownPreview h1,
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
.markdownPreview h4,
|
.markdownPreview h4,
|
||||||
.markdownPreview h5,
|
.markdownPreview h5,
|
||||||
.markdownPreview h6 {
|
.markdownPreview h6 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auto-linked headers */
|
/* Auto-linked headers */
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
.markdownPreview h4 a,
|
.markdownPreview h4 a,
|
||||||
.markdownPreview h5 a,
|
.markdownPreview h5 a,
|
||||||
.markdownPreview h6 a {
|
.markdownPreview h6 a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auto-linked headers */
|
/* Auto-linked headers */
|
||||||
|
@ -36,53 +36,58 @@
|
||||||
.markdownPreview h4 a:hover::after,
|
.markdownPreview h4 a:hover::after,
|
||||||
.markdownPreview h5 a:hover::after,
|
.markdownPreview h5 a:hover::after,
|
||||||
.markdownPreview h6 a:hover::after {
|
.markdownPreview h6 a:hover::after {
|
||||||
content: "#";
|
content: "#";
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
margin-left: 0.25em;
|
margin-left: 0.25em;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
filter: opacity(0.5);
|
filter: opacity(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h1 {
|
.markdownPreview h1 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h2 {
|
.markdownPreview h2 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h3 {
|
.markdownPreview h3 {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h4 {
|
.markdownPreview h4 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h5 {
|
.markdownPreview h5 {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview h6 {
|
.markdownPreview h6 {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview ul {
|
.markdownPreview ul {
|
||||||
list-style: inside;
|
list-style: inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview ul li::before {
|
.markdownPreview ul li::before {
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview code {
|
.markdownPreview code {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
color: inherit !important;
|
color: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdownPreview code::before,
|
.markdownPreview code::before,
|
||||||
.markdownPreview code::after {
|
.markdownPreview code::after {
|
||||||
content: "";
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdownPreview img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 350px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,40 @@
|
||||||
.input {
|
.card {
|
||||||
background: #efefef;
|
margin: var(--gap) auto;
|
||||||
|
padding: var(--gap);
|
||||||
|
border: 1px solid var(--light-gray);
|
||||||
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.descriptionContainer {
|
.descriptionContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileNameContainer {
|
.fileNameContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileNameContainer {
|
.fileNameContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileNameContainer > div {
|
.fileNameContainer > div {
|
||||||
/* Override geist-ui styling */
|
/* Override geist-ui styling */
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
|
||||||
|
|
||||||
.textarea {
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionWrapper {
|
.actionWrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionWrapper .actions {
|
.actionWrapper .actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Spacer height={1} />
|
<Spacer height={1} />
|
||||||
<Card marginBottom={'var(--gap)'} marginTop={'var(--gap)'} style={{ maxWidth: 980, margin: "0 auto" }}>
|
<div className={styles.card}>
|
||||||
<div className={styles.fileNameContainer}>
|
<div className={styles.fileNameContainer}>
|
||||||
<Input
|
<Input
|
||||||
value={title}
|
value={title}
|
||||||
|
@ -121,8 +121,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
</div >
|
</div >
|
||||||
</Card >
|
</div >
|
||||||
<Spacer height={1} />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
18
client/lib/hooks/use-debounce.ts
Normal file
18
client/lib/hooks/use-debounce.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// useDebounce.js
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function useDebounce(value: any, delay: number) {
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedValue(value);
|
||||||
|
}, delay);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler);
|
||||||
|
};
|
||||||
|
}, [value, delay]);
|
||||||
|
|
||||||
|
return debouncedValue;
|
||||||
|
}
|
|
@ -1,7 +1,18 @@
|
||||||
import { marked } from 'marked'
|
import { marked, Lexer } from 'marked'
|
||||||
import Highlight, { defaultProps, Language, } from 'prism-react-renderer'
|
import Highlight, { defaultProps, Language, } from 'prism-react-renderer'
|
||||||
import { renderToStaticMarkup } from 'react-dom/server'
|
import { renderToStaticMarkup } from 'react-dom/server'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// image sizes. DDoS Safe?
|
||||||
|
const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
|
||||||
|
//@ts-ignore
|
||||||
|
Lexer.rules.inline.normal.link = imageSizeLink;
|
||||||
|
//@ts-ignore
|
||||||
|
Lexer.rules.inline.gfm.link = imageSizeLink;
|
||||||
|
//@ts-ignore
|
||||||
|
Lexer.rules.inline.breaks.link = imageSizeLink;
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
delete defaultProps.theme
|
delete defaultProps.theme
|
||||||
// import linkStyles from '../components/link/link.module.css'
|
// import linkStyles from '../components/link/link.module.css'
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"cookie": "^0.4.2",
|
"cookie": "^0.4.2",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
"marked": "^4.0.12",
|
"marked": "^4.0.12",
|
||||||
"next": "^12.1.1-canary.15",
|
"next": "^12.1.1-canary.15",
|
||||||
"postcss": "^8.4.12",
|
"postcss": "^8.4.12",
|
||||||
|
|
|
@ -18,7 +18,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||||
const skeletonHighlightColor = 'var(--lighter-gray)'
|
const skeletonHighlightColor = 'var(--lighter-gray)'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div data-theme={theme}>
|
||||||
<Head>
|
<Head>
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
|
@ -33,13 +33,13 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<title>Drift</title>
|
<title>Drift</title>
|
||||||
</Head>
|
</Head>
|
||||||
<GeistProvider themeType={theme} >
|
<GeistProvider themeType={theme}>
|
||||||
<SkeletonTheme baseColor={skeletonBaseColor} highlightColor={skeletonHighlightColor}>
|
<SkeletonTheme baseColor={skeletonBaseColor} highlightColor={skeletonHighlightColor}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</SkeletonTheme>
|
</SkeletonTheme>
|
||||||
</GeistProvider>
|
</GeistProvider>
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,122 +3,145 @@
|
||||||
@import "./inter.css";
|
@import "./inter.css";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Spacing */
|
/* Spacing */
|
||||||
--gap-quarter: 0.25rem;
|
--gap-quarter: 0.25rem;
|
||||||
--gap-half: 0.5rem;
|
--gap-half: 0.5rem;
|
||||||
--gap: 1rem;
|
--gap: 1rem;
|
||||||
--gap-double: 2rem;
|
--gap-double: 2rem;
|
||||||
--small-gap: 4rem;
|
--small-gap: 4rem;
|
||||||
--big-gap: 4rem;
|
--big-gap: 4rem;
|
||||||
--main-content: 55rem;
|
--main-content: 55rem;
|
||||||
--radius: 8px;
|
--radius: 8px;
|
||||||
--inline-radius: 5px;
|
--inline-radius: 5px;
|
||||||
|
|
||||||
/* Typography */
|
/* Typography */
|
||||||
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
--font-mono: ui-monospace, "SFMono-Regular", "Consolas", "Liberation Mono",
|
--font-mono: ui-monospace, "SFMono-Regular", "Consolas", "Liberation Mono",
|
||||||
"Menlo", monospace;
|
"Menlo", monospace;
|
||||||
|
|
||||||
/* Transitions */
|
/* Transitions */
|
||||||
--transition: 0.1s ease-in-out;
|
--transition: 0.1s ease-in-out;
|
||||||
--transition-slow: 0.3s ease-in-out;
|
--transition-slow: 0.3s ease-in-out;
|
||||||
|
|
||||||
--page-nav-height: 64px;
|
--page-nav-height: 64px;
|
||||||
--token: #999;
|
--token: #999;
|
||||||
--comment: #999;
|
--comment: #999;
|
||||||
--keyword: #fff;
|
--keyword: #fff;
|
||||||
--name: #fff;
|
--name: #fff;
|
||||||
--highlight: #2e2e2e;
|
--highlight: #2e2e2e;
|
||||||
|
|
||||||
|
/* Dark Mode Colors */
|
||||||
|
--bg: #131415;
|
||||||
|
--fg: #fafbfc;
|
||||||
|
--gray: #666;
|
||||||
|
--light-gray: #444;
|
||||||
|
--lighter-gray: #222;
|
||||||
|
--lightest-gray: #1a1a1a;
|
||||||
|
--article-color: #eaeaea;
|
||||||
|
--header-bg: rgba(19, 20, 21, 0.45);
|
||||||
|
--gray-alpha: rgba(255, 255, 255, 0.5);
|
||||||
|
--selection: rgba(255, 255, 255, 0.99);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] {
|
[data-theme="light"] {
|
||||||
--token: #666;
|
--token: #666;
|
||||||
--comment: #999;
|
--comment: #999;
|
||||||
--keyword: #000;
|
--keyword: #000;
|
||||||
--name: #333;
|
--name: #333;
|
||||||
--highlight: #eaeaea;
|
--highlight: #eaeaea;
|
||||||
|
|
||||||
|
--bg: #fff;
|
||||||
|
--fg: #000;
|
||||||
|
--gray: #888;
|
||||||
|
--light-gray: #dedede;
|
||||||
|
--lighter-gray: #f5f5f5;
|
||||||
|
--lightest-gray: #fafafa;
|
||||||
|
--article-color: #212121;
|
||||||
|
--header-bg: rgba(255, 255, 255, 0.8);
|
||||||
|
--gray-alpha: rgba(19, 20, 21, 0.5);
|
||||||
|
--selection: rgba(0, 0, 0, 0.99);
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
background: var(--selection);
|
background: var(--selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
p,
|
p,
|
||||||
li {
|
li {
|
||||||
letter-spacing: -0.33px;
|
letter-spacing: -0.33px;
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
border-left: 3px solid var(--light-gray);
|
border-left: 3px solid var(--light-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.reset {
|
a.reset {
|
||||||
outline: none;
|
outline: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre,
|
pre,
|
||||||
code {
|
code {
|
||||||
font-family: var(--font-mono) !important;
|
font-family: var(--font-mono) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
padding: 2px 7px;
|
padding: 2px 7px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
background: var(--lighter-gray);
|
background: var(--lighter-gray);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
:root {
|
:root {
|
||||||
--bg: #fff;
|
--bg: #fff;
|
||||||
--fg: #000;
|
--fg: #000;
|
||||||
--gray: #888;
|
--gray: #888;
|
||||||
--light-gray: #dedede;
|
--light-gray: #dedede;
|
||||||
--lighter-gray: #f5f5f5;
|
--lighter-gray: #f5f5f5;
|
||||||
--lightest-gray: #fafafa;
|
--lightest-gray: #fafafa;
|
||||||
--article-color: #212121;
|
--article-color: #212121;
|
||||||
--header-bg: rgba(255, 255, 255, 0.8);
|
--header-bg: rgba(255, 255, 255, 0.8);
|
||||||
--gray-alpha: rgba(19, 20, 21, 0.5);
|
--gray-alpha: rgba(19, 20, 21, 0.5);
|
||||||
--selection: rgba(0, 0, 0, 0.99);
|
--selection: rgba(0, 0, 0, 0.99);
|
||||||
|
|
||||||
--token: #666;
|
--token: #666;
|
||||||
--comment: #999;
|
--comment: #999;
|
||||||
--keyword: #000;
|
--keyword: #000;
|
||||||
--name: #333;
|
--name: #333;
|
||||||
--highlight: #eaeaea;
|
--highlight: #eaeaea;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
text-shadow: none !important;
|
text-shadow: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,129 +1,125 @@
|
||||||
article {
|
article {
|
||||||
max-width: var(--main-content);
|
max-width: var(--main-content);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
line-height: 1.9;
|
line-height: 1.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
article > * + * {
|
article > * + * {
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
article img {
|
article img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
/* width: var(--main-content); */
|
margin: auto;
|
||||||
width: auto;
|
|
||||||
margin: auto;
|
|
||||||
display: block;
|
|
||||||
border-radius: var(--radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article [id]::before {
|
article [id]::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
margin-top: -70px;
|
margin-top: -70px;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lists */
|
/* Lists */
|
||||||
|
|
||||||
article ul {
|
article ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
list-style-type: circle;
|
list-style-type: circle;
|
||||||
}
|
}
|
||||||
|
|
||||||
article ol {
|
article ol {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
article ul li.reset {
|
article ul li.reset {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin-left: -0.5rem;
|
margin-left: -0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
article ul li.reset .check {
|
article ul li.reset .check {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: 0.51rem;
|
margin-right: 0.51rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checkbox */
|
/* Checkbox */
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-origin: border-box;
|
background-origin: border-box;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
background-color: var(--bg);
|
background-color: var(--bg);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
border: 1px solid var(--fg);
|
border: 1px solid var(--fg);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"]:checked {
|
input[type="checkbox"]:checked {
|
||||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
background-color: currentColor;
|
background-color: currentColor;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="light"] input[type="checkbox"]:checked {
|
html[data-theme="light"] input[type="checkbox"]:checked {
|
||||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"]:focus {
|
input[type="checkbox"]:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--fg);
|
border-color: var(--fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code Snippets */
|
/* Code Snippets */
|
||||||
|
|
||||||
.token-line:not(:last-child) {
|
.token-line:not(:last-child) {
|
||||||
min-height: 1.4rem;
|
min-height: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
article *:not(pre) > code {
|
article *:not(pre) > code {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
}
|
}
|
||||||
|
|
||||||
article li > p {
|
article li > p {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
article pre {
|
article pre {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
border-radius: var(--inline-radius);
|
border-radius: var(--inline-radius);
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Linkable Headers */
|
/* Linkable Headers */
|
||||||
|
|
||||||
.header-link {
|
.header-link {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-link::after {
|
.header-link::after {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
content: "#";
|
content: "#";
|
||||||
margin-left: var(--gap-half);
|
margin-left: var(--gap-half);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-link:hover::after {
|
.header-link:hover::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2278,6 +2278,11 @@ lodash.camelcase@^4.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||||
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
|
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
|
||||||
|
|
||||||
|
lodash.debounce@^4.0.8:
|
||||||
|
version "4.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||||
|
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||||
|
|
||||||
lodash.merge@^4.6.2:
|
lodash.merge@^4.6.2:
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||||
|
|
Loading…
Reference in a new issue