2022-12-18 update

- increased text size a bit
- removed unused css from kbd tag
- removed unused white link css
- added space before all headers except h1
- moved type decclaration files to one directory
- replaced tag icon to hashtag
- redesigned post card
- removed unnecessary margin from post card titles
- removed post card content preview
- organized recursive parsing code
This commit is contained in:
Kim, Jimin 2021-12-18 16:54:51 +09:00
parent 823f599dc0
commit b43094fef9
21 changed files with 455 additions and 421 deletions

View file

@ -6,13 +6,14 @@
* - series must start with a number followed by an underscore * - series must start with a number followed by an underscore
*/ */
import fs from "fs" // read and write files import fs from "fs"
import { Map, SeriesMap } from "./types"
import { recursiveParse } from "./recursiveParse" import { recursiveParse } from "./recursiveParse"
import { contentDirectoryPath, mapFilePath, markdownPath } from "./config" import { contentDirectoryPath, mapFilePath, markdownPath } from "./config"
import { saveIndex } from "./searchIndex" import { saveIndex } from "./searchIndex"
import { Map, ParseMode, SeriesMap } from "../types/typing"
// searchable data that will be converted to JSON string // searchable data that will be converted to JSON string
export const map: Map = { export const map: Map = {
date: {}, date: {},
@ -53,9 +54,9 @@ if (!fs.lstatSync(markdownPath + "/unsearchable").isDirectory())
if (!fs.lstatSync(markdownPath + "/series").isDirectory()) if (!fs.lstatSync(markdownPath + "/series").isDirectory())
throw Error(`Cannot find directory: ${markdownPath + "/posts"}`) throw Error(`Cannot find directory: ${markdownPath + "/posts"}`)
recursiveParse("posts", markdownPath + "/posts") recursiveParse(ParseMode.POSTS, markdownPath + "/posts")
recursiveParse("unsearchable", markdownPath + "/unsearchable") recursiveParse(ParseMode.UNSEARCHABLE, markdownPath + "/unsearchable")
recursiveParse("series", markdownPath + "/series") recursiveParse(ParseMode.SERIES, markdownPath + "/series")
// sort dates // sort dates
let dateKeys: string[] = [] let dateKeys: string[] = []

View file

@ -28,7 +28,7 @@ const md = markdownIt({
}) })
.use(markdownItAnchor, {}) .use(markdownItAnchor, {})
export function parseMarkdown(markdownRaw: string): string { export default function parseMarkdown(markdownRaw: string): string {
return ( return (
md.render(markdownRaw.slice(nthIndex(markdownRaw, "---", 2) + 3)) || "" md.render(markdownRaw.slice(nthIndex(markdownRaw, "---", 2) + 3)) || ""
) )

View file

@ -5,287 +5,352 @@ import toc from "markdown-toc" // table of contents generation
import { JSDOM } from "jsdom" // HTML DOM parsing import { JSDOM } from "jsdom" // HTML DOM parsing
import { nthIndex, path2FileOrFolderName, path2URL, writeToJSON } from "./util" import { nthIndex, path2FileOrFolderName, path2URL, writeToJSON } from "./util"
import { parseMarkdown } from "./parseMarkdown" import parseMarkdown from "./parseMarkdown"
import { contentDirectoryPath } from "./config" import { contentDirectoryPath } from "./config"
import { addDocument } from "./searchIndex" import { addDocument } from "./searchIndex"
import { map, seriesMap } from "." import { map, seriesMap } from "."
// A recursive function that calls itself for every files and directories that it finds import { MarkdownData, ParseMode, PostData } from "../types/typing"
export function recursiveParse(
mode: "posts" | "series" | "unsearchable", // path that should not be checked when parsing in unsearchable mode
fileOrFolderPath: string const illegalPaths = [
) { "./markdown/unsearchable/posts",
if (mode == "unsearchable") { "./markdown/unsearchable/series",
// illegal names ]
if (
fileOrFolderPath == "./markdown/unsearchable/posts" || interface DataToPass {
fileOrFolderPath == "./markdown/unsearchable/series" path: string
) urlPath: string
throw Error( fileOrFolderName: string
`Illegal name (posts/series) in path: "${fileOrFolderPath}".` markdownRaw: string
) markdownData: MarkdownData
humanizedDuration: string
totalWords: number
}
/**
* A recursive function that calls itself for every files and directories that it finds
*
* @param {ParseMode} mode
* @param {string} path - path of file or folder
*
* @returns {void}
*/
export function recursiveParse(mode: ParseMode, path: string): void {
// don't parse specific directories when parsing unsearchable content
if (mode == ParseMode.UNSEARCHABLE) {
if (illegalPaths.includes(path)) {
throw Error(`Illegal name (posts/series) in path: "${path}".`)
}
} }
// get string after the last slash character const fileOrFolderName = path2FileOrFolderName(path)
const fileOrFolderName = path2FileOrFolderName(fileOrFolderPath)
// ignore if file or directory name starts with a underscore
if (fileOrFolderName.startsWith("_")) return if (fileOrFolderName.startsWith("_")) return
// get data about the given path const stats = fs.lstatSync(path)
const stats = fs.lstatSync(fileOrFolderPath)
// if it's a directory, call this function to every files/directories in it // if it's a directory, call this function to every files/directories in it
// if it's a file, parse it and then save it to file // if it's a file, parse it and then save it to file
if (stats.isDirectory()) { if (stats.isDirectory()) {
fs.readdirSync(fileOrFolderPath).map((childPath) => { fs.readdirSync(path).map((childPath) => {
recursiveParse(mode, `${fileOrFolderPath}/${childPath}`) recursiveParse(mode, `${path}/${childPath}`)
}) })
} else if (stats.isFile()) { } else if (stats.isFile()) {
// skip if it is not a markdown file parseFile(mode, path, fileOrFolderName)
if (!fileOrFolderName.endsWith(".md")) { }
console.log(`Ignoring non markdown file at: ${fileOrFolderPath}`) }
return
function parseFile(
mode: ParseMode,
path: string,
fileOrFolderName: string
): void {
// skip if it is not a markdown file
if (!fileOrFolderName.endsWith(".md")) {
console.log(`Ignoring non markdown file at: ${path}`)
return
}
const markdownRaw = fs.readFileSync(path, "utf8")
const markdownData: MarkdownData = parseFrontMatter(markdownRaw, path, mode)
// https://github.com/pritishvaidya/read-time-estimate
const { humanizedDuration, totalWords } = readTimeEstimate(
markdownData.content,
275,
12,
500,
["img", "Image"]
)
const dataToPass: DataToPass = {
path,
urlPath: path2URL(path),
fileOrFolderName,
markdownRaw,
markdownData,
humanizedDuration,
totalWords,
}
switch (mode) {
case ParseMode.POSTS: {
parsePost(dataToPass)
break
} }
// read markdown file case ParseMode.UNSEARCHABLE: {
const markdownRaw = fs.readFileSync(fileOrFolderPath, "utf8") dataToPass.urlPath = dataToPass.urlPath.slice(
dataToPass.urlPath
// parse markdown metadata
const markdownData = matter(
markdownRaw.slice(0, nthIndex(markdownRaw, "---", 2) + 3)
).data
if (!markdownData.title)
throw Error(`Title is not defined in file: ${fileOrFolderPath}`)
const dom = new JSDOM(parseMarkdown(markdownRaw))
// add .hljs to all block codes
dom.window.document.querySelectorAll("pre > code").forEach((item) => {
item.classList.add("hljs")
})
markdownData.content = dom.window.document.documentElement.innerHTML
// https://github.com/pritishvaidya/read-time-estimate
const { humanizedDuration, totalWords } = readTimeEstimate(
markdownData.content,
275,
12,
500,
["img", "Image"]
)
if (mode == "posts") {
if (!markdownData.date) {
throw Error(`Date is not defined in file: ${fileOrFolderPath}`)
}
// path that will be used as site url (starts with a slash)
const urlPath = path2URL(fileOrFolderPath)
writeToJSON(
`${contentDirectoryPath}${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
toc: toc(markdownRaw).json,
})
)
// Parse data that will be written to map.js
const postData = {
title: markdownData.title,
preview: "",
date: "",
readTime: humanizedDuration,
wordCount: totalWords,
tags: [],
}
// content preview
// parsedMarkdown.excerpt is intentionally not used
// todo: fix potential improper closing of html tag
const slicedContent = markdownData.content.split(" ")
if (slicedContent.length > 19) {
postData.preview = slicedContent.slice(0, 19).join(" ") + " ..."
} else {
postData.preview = markdownData.content
}
// date
const postDate = new Date(markdownData.date)
postData.date = postDate.toLocaleString("default", {
month: "short",
day: "numeric",
year: "numeric",
})
const YYYY_MM_DD = postDate.toISOString().split("T")[0]
if (map.date[YYYY_MM_DD]) {
map.date[YYYY_MM_DD].push(urlPath)
} else {
map.date[YYYY_MM_DD] = [urlPath]
}
//tags
postData.tags = markdownData.tags
if (postData.tags) {
postData.tags.forEach((tag) => {
if (map.tags[tag]) {
map.tags[tag].push(urlPath)
} else {
map.tags[tag] = [urlPath]
}
})
}
map.posts[urlPath] = postData
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
} else if (mode == "unsearchable") {
// path that will be used as site url (starts with a slash)
const _urlPath = path2URL(fileOrFolderPath)
const urlPath = _urlPath.slice(
_urlPath
.slice(1) // ignore the first slash .slice(1) // ignore the first slash
.indexOf("/") + 1 .indexOf("/") + 1
) )
writeToJSON( parseUnsearchable(dataToPass)
`${contentDirectoryPath}/unsearchable${urlPath}.json`, break
JSON.stringify({ }
content: markdownData.content,
})
)
// Parse data that will be written to map.js case ParseMode.SERIES: {
map.unsearchable[urlPath] = { let urlPath = dataToPass.urlPath
title: markdownData.title,
}
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
} else if (mode == "series") {
if (
!fileOrFolderName.includes("_") &&
!fileOrFolderName.startsWith("0")
)
throw Error(
`Invalid series post file name at: ${fileOrFolderPath}`
)
if (!markdownData.date) {
throw Error(`Date is not defined in file: ${fileOrFolderPath}`)
}
// path that will be used as site url (starts with a slash)
let urlPath = path2URL(fileOrFolderPath)
urlPath = urlPath.slice(0, urlPath.lastIndexOf("_")) urlPath = urlPath.slice(0, urlPath.lastIndexOf("_"))
urlPath = urlPath.replace(/\/$/, "") // remove trailing slash dataToPass.urlPath = urlPath.replace(/\/$/, "") // remove trailing slash
writeToJSON( parseSeries(dataToPass)
`${contentDirectoryPath}${urlPath}.json`, break
JSON.stringify({
content: markdownData.content,
toc: toc(markdownRaw).json,
})
)
// Parse data that will be written to map.js
const postData = {
title: markdownData.title,
preview: "",
date: "",
readTime: humanizedDuration,
wordCount: totalWords,
tags: [],
}
// content preview
// parsedMarkdown.excerpt is intentionally not used
// todo: fix potential improper closing of html tag
const slicedContent = markdownData.content.split(" ")
if (slicedContent.length > 19) {
postData.preview = slicedContent.slice(0, 19).join(" ") + " ..."
} else {
postData.preview = markdownData.content
}
// date
const postDate = new Date(markdownData.date)
postData.date = postDate.toLocaleString("default", {
month: "short",
day: "numeric",
year: "numeric",
})
const YYYY_MM_DD = postDate.toISOString().split("T")[0]
if (map.date[YYYY_MM_DD]) {
map.date[YYYY_MM_DD].push(urlPath)
} else {
map.date[YYYY_MM_DD] = [urlPath]
}
//tags
postData.tags = markdownData.tags
if (postData.tags) {
postData.tags.forEach((tag) => {
if (map.tags[tag]) {
map.tags[tag].push(urlPath)
} else {
map.tags[tag] = [urlPath]
}
})
}
if (fileOrFolderName.startsWith("0")) {
map.series[urlPath] = { ...postData, order: [], length: 0 }
} else {
map.posts[urlPath] = postData
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
for (const key of Object.keys(map.series)) {
if (
urlPath.slice(0, urlPath.lastIndexOf("/")).includes(key)
) {
const index = parseInt(
fileOrFolderName.slice(
0,
fileOrFolderName.lastIndexOf("_")
)
)
if (isNaN(index)) {
throw Error(
`Invalid series index at: ${fileOrFolderPath}`
)
}
const itemToPush = {
index: index,
url: urlPath,
}
if (seriesMap[key]) {
seriesMap[key].push(itemToPush)
} else {
seriesMap[key] = [itemToPush]
}
break
}
}
}
} }
} }
} }
function parsePost(data: DataToPass): void {
const {
urlPath,
markdownRaw,
markdownData,
humanizedDuration,
totalWords,
} = data
const postData: PostData = {
title: markdownData.title,
date: "",
readTime: humanizedDuration,
wordCount: totalWords,
tags: [],
}
/**
* Dates
*/
const postDate = new Date(markdownData.date)
postData.date = postDate.toLocaleString("default", {
month: "short",
day: "numeric",
year: "numeric",
})
const YYYY_MM_DD = postDate.toISOString().split("T")[0]
if (map.date[YYYY_MM_DD]) {
map.date[YYYY_MM_DD].push(urlPath)
} else {
map.date[YYYY_MM_DD] = [urlPath]
}
/**
* Tags
*/
postData.tags = markdownData.tags
if (postData.tags) {
postData.tags.forEach((tag) => {
if (map.tags[tag]) {
map.tags[tag].push(urlPath)
} else {
map.tags[tag] = [urlPath]
}
})
}
/**
*
*/
map.posts[urlPath] = postData
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
writeToJSON(
`${contentDirectoryPath}${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
toc: toc(markdownRaw).json,
})
)
}
function parseSeries(data: DataToPass): void {
const {
path,
urlPath,
fileOrFolderName,
markdownRaw,
markdownData,
humanizedDuration,
totalWords,
} = data
if (!fileOrFolderName.includes("_") && !fileOrFolderName.startsWith("0"))
throw Error(`Invalid series post file name at: ${path}`)
const postData: PostData = {
title: markdownData.title,
date: "",
readTime: humanizedDuration,
wordCount: totalWords,
tags: [],
}
/**
* Date
*/
const postDate = new Date(markdownData.date)
postData.date = postDate.toLocaleString("default", {
month: "short",
day: "numeric",
year: "numeric",
})
const YYYY_MM_DD = postDate.toISOString().split("T")[0]
if (map.date[YYYY_MM_DD]) {
map.date[YYYY_MM_DD].push(urlPath)
} else {
map.date[YYYY_MM_DD] = [urlPath]
}
/**
* Tags
*/
postData.tags = markdownData.tags
if (postData.tags) {
postData.tags.forEach((tag) => {
if (map.tags[tag]) {
map.tags[tag].push(urlPath)
} else {
map.tags[tag] = [urlPath]
}
})
}
// series markdown starting with 0 is a series descriptor
if (fileOrFolderName.startsWith("0")) {
map.series[urlPath] = {
...postData,
order: [],
length: 0,
}
} else {
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
map.posts[urlPath] = postData
for (const key of Object.keys(map.series)) {
if (urlPath.slice(0, urlPath.lastIndexOf("/")).includes(key)) {
const index = parseInt(
fileOrFolderName.slice(0, fileOrFolderName.lastIndexOf("_"))
)
if (isNaN(index))
throw Error(`Invalid series index at: ${path}`)
const itemToPush = {
index: index,
url: urlPath,
}
if (seriesMap[key]) {
seriesMap[key].push(itemToPush)
} else {
seriesMap[key] = [itemToPush]
}
break
}
}
}
/**
*
*/
writeToJSON(
`${contentDirectoryPath}${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
toc: toc(markdownRaw).json,
})
)
}
function parseUnsearchable(data: DataToPass): void {
const { urlPath, markdownData } = data
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
// Parse data that will be written to map.js
map.unsearchable[urlPath] = {
title: markdownData.title,
}
writeToJSON(
`${contentDirectoryPath}/unsearchable${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
})
)
}
/**
* todo: accurately calculate start and end of front matter
*
* @param {string} markdownRaw
* @param {string} path
*
* @returns {MarkdownData}
*/
function parseFrontMatter(
markdownRaw: string,
path: string,
mode: ParseMode
): MarkdownData {
const result = matter(
markdownRaw.slice(0, nthIndex(markdownRaw, "---", 2) + 3)
).data
if (!result.title) throw Error(`Title is not defined in file: ${path}`)
if (mode != ParseMode.UNSEARCHABLE && !result.date)
throw Error(`Date is not defined in file: ${path}`)
const dom = new JSDOM(parseMarkdown(markdownRaw))
// add .hljs class to all block codes
dom.window.document.querySelectorAll("pre > code").forEach((item) => {
item.classList.add("hljs")
})
result.content = dom.window.document.documentElement.innerHTML
return result as MarkdownData
}

View file

@ -1,56 +0,0 @@
export interface Map {
// key: YYYY-MM-DD
// value: url
date: {
[key: string]: string[]
}
// key: tag name
// value: url
tags: {
[key: string]: string[]
}
// list of all meta data
meta: {
tags: string[]
}
// searchable, non-series posts
// must have a post date
// tag is not required
posts: {
[key: string]: {
title: string
date: string
tags: string[]
preview: string
}
}
// series posts have "previous post" and "next post" button so they need to be ordered
series: {
[key: string]: {
title: string
length: number
order: string[] // url order
tags: string[]
}
}
// urls of unsearchable posts
// it is here to quickly check if a post exists or not
unsearchable: {
[key: string]: {
title: string
}
}
}
export interface SeriesMap {
// key: url
[key: string]: {
index: number
url: string
}[]
}

View file

@ -3,7 +3,12 @@ import { relative } from "path"
import { markdownPath } from "./config" import { markdownPath } from "./config"
// converts file path to url /**
* converts file path to url path that will be used in the url (starts with a slash)
*
* @param {string} pathToConvert
* @returns {string}
*/
export function path2URL(pathToConvert: string): string { export function path2URL(pathToConvert: string): string {
return `/${relative(markdownPath, pathToConvert)}` return `/${relative(markdownPath, pathToConvert)}`
.replace(/\.[^/.]+$/, "") // remove the file extension .replace(/\.[^/.]+$/, "") // remove the file extension

View file

@ -5,7 +5,7 @@ import { Helmet } from "react-helmet-async"
import storage from "local-storage-fallback" import storage from "local-storage-fallback"
import { isIE } from "react-device-detect" import { isIE } from "react-device-detect"
import { ThemeType } from "./types/styled-comonents" import { ThemeType } from "../types/styled-components"
import Loading from "./components/Loading" import Loading from "./components/Loading"
import Navbar from "./components/Navbar" import Navbar from "./components/Navbar"

View file

@ -1,7 +1,7 @@
import styled from "styled-components" import styled from "styled-components"
import { useNavigate } from "react-router-dom" import { useNavigate } from "react-router-dom"
import { Post } from "../types/typings" import { PostData } from "../../types/typing"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { import {
@ -40,6 +40,7 @@ const StyledPostCard = styled(MainContent)`
const StyledTitle = styled.h1` const StyledTitle = styled.h1`
font-size: 2rem; font-size: 2rem;
font-style: bold; font-style: bold;
margin: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
` `
@ -51,15 +52,7 @@ const StyledMetaContainer = styled.small`
})}; })};
` `
const StyledPostCardContent = styled.div` interface _PostDateBase extends PostData {
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "grey",
dark: "lightgrey",
})};
`
interface _PostDateBase extends Post {
url: string url: string
} }
@ -72,16 +65,17 @@ const PostCard = (props: Props) => {
return ( return (
<StyledPostCard <StyledPostCard
key={props.postData.url} onClick={() =>
onClick={() => {
navigate(process.env.PUBLIC_URL + props.postData.url) navigate(process.env.PUBLIC_URL + props.postData.url)
}} }
> >
<StyledTitle>{props.postData?.title || "No title"}</StyledTitle> <StyledTitle>{props.postData?.title || "No title"}</StyledTitle>
<br />
<StyledMetaContainer> <StyledMetaContainer>
<TagList direction="left"> <TagList direction="left">
{props.postData.tags ? ( {props.postData.tags &&
props.postData.tags.map((tag) => { props.postData.tags.map((tag) => {
return ( return (
<Tag <Tag
@ -89,11 +83,9 @@ const PostCard = (props: Props) => {
text={tag} text={tag}
/> />
) )
}) })}
) : (
<></>
)}
</TagList> </TagList>
<hr />
<FontAwesomeIcon icon={faCalendar} /> <FontAwesomeIcon icon={faCalendar} />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
{props.postData?.date || "Unknown date"} {props.postData?.date || "Unknown date"}
@ -110,15 +102,6 @@ const PostCard = (props: Props) => {
? props.postData.wordCount + " words" ? props.postData.wordCount + " words"
: "unknown words"} : "unknown words"}
</StyledMetaContainer> </StyledMetaContainer>
<hr />
<StyledPostCardContent
className="white-link"
dangerouslySetInnerHTML={{
__html: props.postData.preview,
}}
/>
</StyledPostCard> </StyledPostCard>
) )
} }

View file

@ -1,7 +1,7 @@
import { MouseEvent } from "react" import { MouseEvent } from "react"
import styled from "styled-components" import styled from "styled-components"
import { faTag } from "@fortawesome/free-solid-svg-icons" import { faHashtag } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import theming from "../styles/theming" import theming from "../styles/theming"
@ -24,7 +24,7 @@ interface Props {
const Tag = (props: Props) => { const Tag = (props: Props) => {
return ( return (
<StyledTag onClick={props.onClick || undefined}> <StyledTag onClick={props.onClick || undefined}>
<FontAwesomeIcon icon={faTag} /> &nbsp;{props.text} <FontAwesomeIcon icon={faHashtag} /> &nbsp;{props.text}
</StyledTag> </StyledTag>
) )
} }

View file

@ -6,7 +6,7 @@ import {
faHourglass, faHourglass,
} from "@fortawesome/free-solid-svg-icons" } from "@fortawesome/free-solid-svg-icons"
import { FetchedPage } from "../../types/typings" import { FetchedPage } from "../../../types/typing"
import theming from "../../styles/theming" import theming from "../../styles/theming"
const StyledMetaContainer = styled.div` const StyledMetaContainer = styled.div`

View file

@ -8,7 +8,7 @@ import styled from "styled-components"
import theming from "../../styles/theming" import theming from "../../styles/theming"
import { FetchedPage } from "../../types/typings" import { FetchedPage } from "../../../types/typing"
const StyledTocToggleButton = styled.button` const StyledTocToggleButton = styled.button`
border: none; border: none;

View file

@ -4,7 +4,7 @@ import { useLocation } from "react-router-dom"
import styled from "styled-components" import styled from "styled-components"
import { HashLink } from "react-router-hash-link" import { HashLink } from "react-router-hash-link"
import { TocElement, FetchedPage, Map } from "../../types/typings" import { TocElement, FetchedPage, Map } from "../../../types/typing"
import MainContent from "../../components/MainContent" import MainContent from "../../components/MainContent"
import Loading from "../../components/Loading" import Loading from "../../components/Loading"

View file

@ -12,7 +12,7 @@ import PostCard from "../components/PostCard"
import _map from "../data/map.json" import _map from "../data/map.json"
import theming from "../styles/theming" import theming from "../styles/theming"
import { Map } from "../types/typings" import { Map } from "../../types/typing"
const map: Map = _map const map: Map = _map

View file

@ -5,7 +5,7 @@ import Select from "react-select"
import theming from "../../styles/theming" import theming from "../../styles/theming"
import { Query } from "." import { Query } from "."
import { Map } from "../../types/typings" import { Map } from "../../../types/typing"
import _map from "../../data/map.json" import _map from "../../data/map.json"
const map: Map = _map const map: Map = _map

View file

@ -19,7 +19,7 @@ import MainContent from "../../components/MainContent"
import SearchBar from "./SearchBar" import SearchBar from "./SearchBar"
import TagSelect, { TagsData } from "./TagSelect" import TagSelect, { TagsData } from "./TagSelect"
import { Map } from "../../types/typings" import { Map } from "../../../types/typing"
import "react-date-range/dist/styles.css" import "react-date-range/dist/styles.css"
import "react-date-range/dist/theme/default.css" import "react-date-range/dist/theme/default.css"

View file

@ -96,7 +96,7 @@ const kbdCSS = css`
dark: "white", dark: "white",
})}; })};
line-height: 1.4; line-height: 1.4;
font-size: 10px; font-size: 13.5px;
display: inline-block; display: inline-block;
box-shadow: ${(props) => box-shadow: ${(props) =>
theming.theme(props.theme.currentTheme, { theming.theme(props.theme.currentTheme, {
@ -108,11 +108,6 @@ const kbdCSS = css`
light: "#F7F7F7", light: "#F7F7F7",
dark: "black", dark: "black",
})}; })};
text-shadow: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "0 1px 0 white",
dark: "0 1px 0 black",
})};
} }
` `
@ -165,14 +160,15 @@ const blockquoteCSS = css`
} }
` `
const whiteLinkCSS = css` const headerCSS = css`
.white-link a { /* intentionally left out h1 */
text-decoration: none;
color: ${theming.color.linkColor};
&:visited { h2,
color: ${theming.color.linkColor}; h3,
} h4,
h5,
h6 {
margin-top: 2.5rem;
} }
` `
@ -182,7 +178,7 @@ const globalStyle = css`
${kbdCSS} ${kbdCSS}
${tableCSS} ${tableCSS}
${blockquoteCSS} ${blockquoteCSS}
${whiteLinkCSS} ${headerCSS}
body { body {
overflow-x: hidden; overflow-x: hidden;

View file

@ -23,7 +23,7 @@ export default {
x2_small: "3px", x2_small: "3px",
x_small: "8px", x_small: "8px",
small: 0, small: 0,
medium: "14px", medium: "1rem",
large: 0, large: 0,
x_large: 0, x_large: 0,
screen_size1: "1000px", screen_size1: "1000px",

View file

@ -1,50 +0,0 @@
export interface TocElement {
slug: string
content: string
i: number
lvl: number
}
export interface Post {
title: string
preview: string
date: string
readTime: string
wordCount: number
tags?: string[]
}
export interface Series {
title: string
preview: string
date: string
readTime: string
wordCount: number
order: string[]
length: number
}
export interface FetchedPage {
title: string
preview: string
date: string
readTime: string
wordCount: number
tags: string[]
toc: JSX.Element | undefined
content: string
}
interface Map {
date: { [date: string]: string[] }
tags: { [tag: string]: string[] }
meta: { tags: string[] }
posts: { [url: string]: Post }
series: { [url: string]: Series }
unsearchable: { [url: string]: { title: string } }
}
declare module "*.json" {
const data: Map
export default data
}

90
source/types/typing.ts Normal file
View file

@ -0,0 +1,90 @@
export enum ParseMode {
POSTS,
SERIES,
UNSEARCHABLE,
}
export interface TocElement {
slug: string
content: string
i: number
lvl: number
}
export interface Base {}
export interface PostData {
title: string
date: string
readTime: string
wordCount: number
tags?: string[]
}
export interface Series {
title: string
date: string
readTime: string
wordCount: number
order: string[]
length: number
tags?: string[]
}
export interface FetchedPage {
title: string
date: string
readTime: string
wordCount: number
tags: string[]
toc?: JSX.Element
content: string
}
export interface Map {
// key: YYYY-MM-DD
// value: url
date: { [key: string]: string[] }
// key: tag name
// value: url
tags: {
[key: string]: string[]
}
// list of all meta data
meta: {
tags: string[]
}
// searchable, non-series posts
// must have a post date
// tag is not required
posts: {
[key: string]: PostData
}
// series posts have "previous post" and "next post" button so they need to be ordered
series: { [key: string]: Series }
// urls of unsearchable posts
// it is here to quickly check if a post exists or not
unsearchable: { [key: string]: { title: string } }
}
export interface SeriesEntry {
index: number
url: string
}
export interface SeriesMap {
// key: url
[key: string]: SeriesEntry[]
}
export interface MarkdownData {
content: string
date: string
title: string
tags: string[]
}