client: beging markdown rendering on server
This commit is contained in:
parent
da46422764
commit
d1ee9d857f
9 changed files with 737 additions and 49 deletions
19
client/lib/hooks/use-trace-route.ts
Normal file
19
client/lib/hooks/use-trace-route.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useRef, useEffect } from "react";
|
||||||
|
|
||||||
|
function useTraceUpdate(props: { [key: string]: any }) {
|
||||||
|
const prev = useRef(props)
|
||||||
|
useEffect(() => {
|
||||||
|
const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
|
||||||
|
if (prev.current[k] !== v) {
|
||||||
|
ps[k] = [prev.current[k], v]
|
||||||
|
}
|
||||||
|
return ps
|
||||||
|
}, {} as { [key: string]: any })
|
||||||
|
if (Object.keys(changedProps).length > 0) {
|
||||||
|
console.log('Changed props:', changedProps)
|
||||||
|
}
|
||||||
|
prev.current = props
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useTraceUpdate
|
140
client/lib/render-markdown.tsx
Normal file
140
client/lib/render-markdown.tsx
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import Link from '@components/Link'
|
||||||
|
import { marked } from 'marked'
|
||||||
|
import Highlight, { defaultProps, Language } from 'prism-react-renderer'
|
||||||
|
import { renderToStaticMarkup } from 'react-dom/server'
|
||||||
|
//@ts-ignore
|
||||||
|
delete defaultProps.theme
|
||||||
|
// import linkStyles from '../components/link/link.module.css'
|
||||||
|
|
||||||
|
const renderer = new marked.Renderer()
|
||||||
|
|
||||||
|
renderer.heading = (text, level, _, slugger) => {
|
||||||
|
const id = slugger.slug(text)
|
||||||
|
const Component = `h${level}`
|
||||||
|
|
||||||
|
return renderToStaticMarkup(
|
||||||
|
//@ts-ignore
|
||||||
|
<Component>
|
||||||
|
<a href={`#${id}`} id={id} style={{ color: "inherit" }} >
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
</Component>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.link = (href, _, text) => {
|
||||||
|
const isHrefLocal = href?.startsWith('/') || href?.startsWith('#')
|
||||||
|
if (isHrefLocal) {
|
||||||
|
return renderToStaticMarkup(
|
||||||
|
<a href={href || ''}>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return `<a href="${href}" target="_blank" rel="noopener noreferrer">${text}</a>`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.image = function (href, _, text) {
|
||||||
|
return `<Image loading="lazy" src="${href}" alt="${text}" layout="fill" />`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.checkbox = () => ''
|
||||||
|
renderer.listitem = (text, task, checked) => {
|
||||||
|
if (task) {
|
||||||
|
return `<li class="reset"><span class="check">​<input type="checkbox" disabled ${checked ? 'checked' : ''
|
||||||
|
} /></span><span>${text}</span></li>`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<li>${text}</li>`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.code = (code: string, language: string) => {
|
||||||
|
return renderToStaticMarkup(
|
||||||
|
<pre>
|
||||||
|
{/* {title && <code>{title} </code>} */}
|
||||||
|
{/* {language && title && <code style={{}}> {language} </code>} */}
|
||||||
|
<Code
|
||||||
|
language={language}
|
||||||
|
// title={title}
|
||||||
|
code={code}
|
||||||
|
// highlight={highlight}
|
||||||
|
/>
|
||||||
|
</pre>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
marked.setOptions({
|
||||||
|
gfm: true,
|
||||||
|
breaks: true,
|
||||||
|
headerIds: true,
|
||||||
|
renderer,
|
||||||
|
})
|
||||||
|
|
||||||
|
const markdown = (markdown: string) => marked(markdown)
|
||||||
|
|
||||||
|
export default markdown
|
||||||
|
|
||||||
|
const Code = ({ code, language, highlight, title, ...props }: {
|
||||||
|
code: string,
|
||||||
|
language: string,
|
||||||
|
highlight?: string,
|
||||||
|
title?: string,
|
||||||
|
}) => {
|
||||||
|
if (!language)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<code {...props} dangerouslySetInnerHTML={{ __html: code }
|
||||||
|
} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
const highlightedLines = highlight
|
||||||
|
//@ts-ignore
|
||||||
|
? highlight.split(',').reduce((lines, h) => {
|
||||||
|
if (h.includes('-')) {
|
||||||
|
// Expand ranges like 3-5 into [3,4,5]
|
||||||
|
const [start, end] = h.split('-').map(Number)
|
||||||
|
const x = Array(end - start + 1)
|
||||||
|
.fill(undefined)
|
||||||
|
.map((_, i) => i + start)
|
||||||
|
return [...lines, ...x]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...lines, Number(h)]
|
||||||
|
}, [])
|
||||||
|
: ''
|
||||||
|
|
||||||
|
// https://mdxjs.com/guides/syntax-harkedighlighting#all-together
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Highlight {...defaultProps} code={code.trim()} language={language as Language} >
|
||||||
|
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||||
|
<code className={className} style={{ ...style }}>
|
||||||
|
{
|
||||||
|
tokens.map((line, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
{...getLineProps({ line, key: i })}
|
||||||
|
style={
|
||||||
|
highlightedLines.includes((i + 1).toString())
|
||||||
|
? {
|
||||||
|
background: 'var(--highlight)',
|
||||||
|
margin: '0 -1rem',
|
||||||
|
padding: '0 1rem',
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
line.map((token, key) => (
|
||||||
|
<span key={key} {...getTokenProps({ token, key })} />
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</code>
|
||||||
|
)}
|
||||||
|
</Highlight>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
44
client/pages/api/markdown/[id].tsx
Normal file
44
client/pages/api/markdown/[id].tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import type { NextApiHandler } from "next";
|
||||||
|
|
||||||
|
import markdown from "@lib/render-markdown";
|
||||||
|
|
||||||
|
const renderMarkdown: NextApiHandler = async (req, res) => {
|
||||||
|
const { id } = req.query
|
||||||
|
const file = await fetch(`${process.env.API_URL}/files/raw/${id}`, {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'text/plain',
|
||||||
|
'x-secret-key': process.env.SECRET_KEY || '',
|
||||||
|
'Authorization': `Bearer ${req.cookies['drift-token']}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const json = await file.json()
|
||||||
|
const { content, title } = json
|
||||||
|
const renderAsMarkdown = ['m', 'markdown', 'md', 'mdown', 'mkdn', 'mkd', 'mdwn', 'mdtxt', 'mdtext', 'text', '']
|
||||||
|
const fileType = () => {
|
||||||
|
const pathParts = title.split(".")
|
||||||
|
const language = pathParts.length > 1 ? pathParts[pathParts.length - 1] : ""
|
||||||
|
return language
|
||||||
|
}
|
||||||
|
const type = fileType()
|
||||||
|
let contentToRender: string = content;
|
||||||
|
|
||||||
|
if (!renderAsMarkdown.includes(type)) {
|
||||||
|
contentToRender = `~~~${type}
|
||||||
|
${content}
|
||||||
|
~~~`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof contentToRender !== 'string') {
|
||||||
|
res.status(400).send('content must be a string')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'text/plain')
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=4800')
|
||||||
|
res.status(200).write(markdown(contentToRender))
|
||||||
|
res.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default renderMarkdown
|
|
@ -1,28 +1,11 @@
|
||||||
.main {
|
.wrapper {
|
||||||
min-height: 100vh;
|
height: 100% !important;
|
||||||
flex: 1;
|
padding-bottom: var(--small-gap) !important;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0 auto;
|
|
||||||
width: var(--main-content-width);
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
.main {
|
||||||
.container {
|
max-width: var(--main-content) !important;
|
||||||
width: 100%;
|
margin: 0 auto !important;
|
||||||
margin: 0 auto !important;
|
padding: 0 var(--gap) !important;
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,251 @@
|
||||||
|
@import "./syntax.css";
|
||||||
|
@import "./markdown.css";
|
||||||
|
@import "./nprogress.css";
|
||||||
|
@import "./inter.css";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--main-content-width: 800px;
|
/* Spacing */
|
||||||
--page-nav-height: 60px;
|
--gap-quarter: 0.25rem;
|
||||||
--gap: 8px;
|
--gap-half: 0.5rem;
|
||||||
--gap-half: calc(var(--gap) / 2);
|
--gap: 1rem;
|
||||||
--gap-double: calc(var(--gap) * 2);
|
--gap-double: 2rem;
|
||||||
--border-radius: 4px;
|
--small-gap: 4rem;
|
||||||
--font-size: 16px;
|
--big-gap: 4rem;
|
||||||
|
--main-content: 55rem;
|
||||||
|
--radius: 8px;
|
||||||
|
--inline-radius: 5px;
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||||
|
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
|
--font-mono: ui-monospace, "SFMono-Regular", "Consolas", "Liberation Mono",
|
||||||
|
"Menlo", monospace;
|
||||||
|
|
||||||
|
/* Transitions */
|
||||||
|
--transition: 0.1s ease-in-out;
|
||||||
|
--transition-slow: 0.3s ease-in-out;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
|
--input-height: 2.5rem;
|
||||||
|
--input-border: 1px solid var(--light-gray);
|
||||||
|
--input-border-focus: 1px solid var(--gray);
|
||||||
|
--input-border-error: 1px solid var(--red);
|
||||||
|
--input-bg: var(--bg);
|
||||||
|
--input-fg: var(--fg);
|
||||||
|
--input-placeholder-fg: var(--light-gray);
|
||||||
|
--input-bg-hover: var(--lightest-gray);
|
||||||
|
|
||||||
|
/* Syntax Highlighting */
|
||||||
|
--token: #999;
|
||||||
|
--comment: #999;
|
||||||
|
--keyword: #fff;
|
||||||
|
--name: #fff;
|
||||||
|
--highlight: #2e2e2e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
[data-theme="light"] {
|
||||||
:root {
|
--bg: #fff;
|
||||||
--main-content-width: 100%;
|
--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);
|
||||||
|
|
||||||
html,
|
--token: #666;
|
||||||
body {
|
--comment: #999;
|
||||||
padding: 0;
|
--keyword: #000;
|
||||||
margin: 0;
|
--name: #333;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
--highlight: #eaeaea;
|
||||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
li:before {
|
::selection {
|
||||||
content: "" !important;
|
text-shadow: none;
|
||||||
|
background: var(--selection);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 15px;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--fg);
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
li {
|
||||||
|
letter-spacing: -0.33px;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
letter-spacing: -0.89px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
letter-spacing: -0.69px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
letter-spacing: -0.47px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
letter-spacing: -0.33px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1rem;
|
||||||
|
border-left: 3px solid var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
p a,
|
||||||
|
a.reset {
|
||||||
|
outline: none;
|
||||||
|
color: var(--fg);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p a:hover,
|
||||||
|
p a:focus,
|
||||||
|
p a:active,
|
||||||
|
a.reset:hover,
|
||||||
|
a.reset:focus {
|
||||||
|
color: var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clamp {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clamp-2 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 2px 7px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: var(--lighter-gray);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--lightest-gray);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
:root {
|
||||||
|
--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);
|
||||||
|
|
||||||
|
--token: #666;
|
||||||
|
--comment: #999;
|
||||||
|
--keyword: #000;
|
||||||
|
--name: #333;
|
||||||
|
--highlight: #eaeaea;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
100
client/styles/inter.css
Normal file
100
client/styles/inter.css
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
font-display: block;
|
||||||
|
src: url(https://assets.vercel.com/raw/upload/v1587415301/fonts/2/inter-var-latin.woff2)
|
||||||
|
format("woff2");
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||||
|
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||||
|
U+FEFF, U+FFFD;
|
||||||
|
}
|
140
client/styles/markdown.css
Normal file
140
client/styles/markdown.css
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
article {
|
||||||
|
max-width: var(--main-content);
|
||||||
|
margin: 0 auto;
|
||||||
|
line-height: 1.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
article > * + * {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
article p {
|
||||||
|
color: var(--article-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
article img {
|
||||||
|
max-width: 100%;
|
||||||
|
/* width: var(--main-content); */
|
||||||
|
width: auto;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
article [id]::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 70px;
|
||||||
|
margin-top: -70px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lists */
|
||||||
|
|
||||||
|
article ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-position: inside;
|
||||||
|
list-style-type: circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
article ol {
|
||||||
|
padding: 0;
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul li.reset {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
list-style-type: none;
|
||||||
|
margin-left: -0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul li.reset .check {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 0.51rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox */
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
vertical-align: middle;
|
||||||
|
appearance: none;
|
||||||
|
display: inline-block;
|
||||||
|
background-origin: border-box;
|
||||||
|
user-select: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--fg);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px var(--gray);
|
||||||
|
border-color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code Snippets */
|
||||||
|
|
||||||
|
.token-line:not(:last-child) {
|
||||||
|
min-height: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
article *:not(pre) > code {
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article *:not(pre) > code::before,
|
||||||
|
article *:not(pre) > code::after {
|
||||||
|
content: "\`";
|
||||||
|
color: var(--gray);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
article pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
background: var(--lightest-gray);
|
||||||
|
border-radius: var(--inline-radius);
|
||||||
|
line-height: 1.8;
|
||||||
|
padding: 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Linkable Headers */
|
||||||
|
|
||||||
|
.header-link {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-link::after {
|
||||||
|
opacity: 0;
|
||||||
|
content: "#";
|
||||||
|
margin-left: var(--gap-half);
|
||||||
|
color: var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-link:hover::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
23
client/styles/nprogress.css
Normal file
23
client/styles/nprogress.css
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#nprogress {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .bar {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2000;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
background: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress::after {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
24
client/styles/syntax.css
Normal file
24
client/styles/syntax.css
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
.keyword {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.operator,
|
||||||
|
.token.punctuation,
|
||||||
|
.token.string,
|
||||||
|
.token.number,
|
||||||
|
.token.builtin,
|
||||||
|
.token.variable {
|
||||||
|
color: var(--token);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment {
|
||||||
|
color: var(--comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.class-name,
|
||||||
|
.token.function,
|
||||||
|
.token.tag,
|
||||||
|
.token.attr-name {
|
||||||
|
color: var(--name);
|
||||||
|
}
|
Loading…
Reference in a new issue