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 {
|
||||
min-height: 100vh;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
width: var(--main-content-width);
|
||||
}
|
||||
|
||||
.container {
|
||||
.wrapper {
|
||||
height: 100% !important;
|
||||
padding-bottom: var(--small-gap) !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.container {
|
||||
width: 100%;
|
||||
margin: 0 auto !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
}
|
||||
.main {
|
||||
max-width: var(--main-content) !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 var(--gap) !important;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,251 @@
|
|||
@import "./syntax.css";
|
||||
@import "./markdown.css";
|
||||
@import "./nprogress.css";
|
||||
@import "./inter.css";
|
||||
|
||||
:root {
|
||||
--main-content-width: 800px;
|
||||
--page-nav-height: 60px;
|
||||
--gap: 8px;
|
||||
--gap-half: calc(var(--gap) / 2);
|
||||
--gap-double: calc(var(--gap) * 2);
|
||||
--border-radius: 4px;
|
||||
--font-size: 16px;
|
||||
/* Spacing */
|
||||
--gap-quarter: 0.25rem;
|
||||
--gap-half: 0.5rem;
|
||||
--gap: 1rem;
|
||||
--gap-double: 2rem;
|
||||
--small-gap: 4rem;
|
||||
--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) {
|
||||
:root {
|
||||
--main-content-width: 100%;
|
||||
}
|
||||
}
|
||||
[data-theme="light"] {
|
||||
--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);
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
--token: #666;
|
||||
--comment: #999;
|
||||
--keyword: #000;
|
||||
--name: #333;
|
||||
--highlight: #eaeaea;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
li:before {
|
||||
content: "" !important;
|
||||
::selection {
|
||||
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