diff --git a/source/generate/index.ts b/source/generate/index.ts
index 1d708be..a3bfece 100644
--- a/source/generate/index.ts
+++ b/source/generate/index.ts
@@ -6,13 +6,14 @@
* - 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 { contentDirectoryPath, mapFilePath, markdownPath } from "./config"
import { saveIndex } from "./searchIndex"
+import { Map, ParseMode, SeriesMap } from "../types/typing"
+
// searchable data that will be converted to JSON string
export const map: Map = {
date: {},
@@ -53,9 +54,9 @@ if (!fs.lstatSync(markdownPath + "/unsearchable").isDirectory())
if (!fs.lstatSync(markdownPath + "/series").isDirectory())
throw Error(`Cannot find directory: ${markdownPath + "/posts"}`)
-recursiveParse("posts", markdownPath + "/posts")
-recursiveParse("unsearchable", markdownPath + "/unsearchable")
-recursiveParse("series", markdownPath + "/series")
+recursiveParse(ParseMode.POSTS, markdownPath + "/posts")
+recursiveParse(ParseMode.UNSEARCHABLE, markdownPath + "/unsearchable")
+recursiveParse(ParseMode.SERIES, markdownPath + "/series")
// sort dates
let dateKeys: string[] = []
diff --git a/source/generate/parseMarkdown.ts b/source/generate/parseMarkdown.ts
index 13205d6..8446051 100644
--- a/source/generate/parseMarkdown.ts
+++ b/source/generate/parseMarkdown.ts
@@ -28,7 +28,7 @@ const md = markdownIt({
})
.use(markdownItAnchor, {})
-export function parseMarkdown(markdownRaw: string): string {
+export default function parseMarkdown(markdownRaw: string): string {
return (
md.render(markdownRaw.slice(nthIndex(markdownRaw, "---", 2) + 3)) || ""
)
diff --git a/source/generate/recursiveParse.ts b/source/generate/recursiveParse.ts
index df7eae4..1e0a966 100644
--- a/source/generate/recursiveParse.ts
+++ b/source/generate/recursiveParse.ts
@@ -5,287 +5,352 @@ import toc from "markdown-toc" // table of contents generation
import { JSDOM } from "jsdom" // HTML DOM parsing
import { nthIndex, path2FileOrFolderName, path2URL, writeToJSON } from "./util"
-import { parseMarkdown } from "./parseMarkdown"
+import parseMarkdown from "./parseMarkdown"
import { contentDirectoryPath } from "./config"
import { addDocument } from "./searchIndex"
import { map, seriesMap } from "."
-// A recursive function that calls itself for every files and directories that it finds
-export function recursiveParse(
- mode: "posts" | "series" | "unsearchable",
- fileOrFolderPath: string
-) {
- if (mode == "unsearchable") {
- // illegal names
- if (
- fileOrFolderPath == "./markdown/unsearchable/posts" ||
- fileOrFolderPath == "./markdown/unsearchable/series"
- )
- throw Error(
- `Illegal name (posts/series) in path: "${fileOrFolderPath}".`
- )
+import { MarkdownData, ParseMode, PostData } from "../types/typing"
+
+// path that should not be checked when parsing in unsearchable mode
+const illegalPaths = [
+ "./markdown/unsearchable/posts",
+ "./markdown/unsearchable/series",
+]
+
+interface DataToPass {
+ path: string
+ urlPath: string
+ fileOrFolderName: string
+ 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(fileOrFolderPath)
-
- // ignore if file or directory name starts with a underscore
+ const fileOrFolderName = path2FileOrFolderName(path)
if (fileOrFolderName.startsWith("_")) return
- // get data about the given path
- const stats = fs.lstatSync(fileOrFolderPath)
+ const stats = fs.lstatSync(path)
// 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 (stats.isDirectory()) {
- fs.readdirSync(fileOrFolderPath).map((childPath) => {
- recursiveParse(mode, `${fileOrFolderPath}/${childPath}`)
+ fs.readdirSync(path).map((childPath) => {
+ recursiveParse(mode, `${path}/${childPath}`)
})
} else if (stats.isFile()) {
- // skip if it is not a markdown file
- if (!fileOrFolderName.endsWith(".md")) {
- console.log(`Ignoring non markdown file at: ${fileOrFolderPath}`)
- return
+ parseFile(mode, path, fileOrFolderName)
+ }
+}
+
+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
- const markdownRaw = fs.readFileSync(fileOrFolderPath, "utf8")
-
- // 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
+ case ParseMode.UNSEARCHABLE: {
+ dataToPass.urlPath = dataToPass.urlPath.slice(
+ dataToPass.urlPath
.slice(1) // ignore the first slash
.indexOf("/") + 1
)
- writeToJSON(
- `${contentDirectoryPath}/unsearchable${urlPath}.json`,
- JSON.stringify({
- content: markdownData.content,
- })
- )
+ parseUnsearchable(dataToPass)
+ break
+ }
- // Parse data that will be written to map.js
- map.unsearchable[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)
+ case ParseMode.SERIES: {
+ let urlPath = dataToPass.urlPath
urlPath = urlPath.slice(0, urlPath.lastIndexOf("_"))
- urlPath = urlPath.replace(/\/$/, "") // remove trailing slash
+ dataToPass.urlPath = urlPath.replace(/\/$/, "") // remove trailing slash
- 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]
- }
- })
- }
-
- 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
- }
- }
- }
+ parseSeries(dataToPass)
+ 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
+}
diff --git a/source/generate/types.ts b/source/generate/types.ts
deleted file mode 100644
index 06bbe3f..0000000
--- a/source/generate/types.ts
+++ /dev/null
@@ -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
- }[]
-}
diff --git a/source/generate/util.ts b/source/generate/util.ts
index d83b50d..279aaa8 100644
--- a/source/generate/util.ts
+++ b/source/generate/util.ts
@@ -3,7 +3,12 @@ import { relative } from "path"
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 {
return `/${relative(markdownPath, pathToConvert)}`
.replace(/\.[^/.]+$/, "") // remove the file extension
diff --git a/source/src/App.tsx b/source/src/App.tsx
index 9e3ade9..a557773 100644
--- a/source/src/App.tsx
+++ b/source/src/App.tsx
@@ -5,7 +5,7 @@ import { Helmet } from "react-helmet-async"
import storage from "local-storage-fallback"
import { isIE } from "react-device-detect"
-import { ThemeType } from "./types/styled-comonents"
+import { ThemeType } from "../types/styled-components"
import Loading from "./components/Loading"
import Navbar from "./components/Navbar"
diff --git a/source/src/components/PostCard.tsx b/source/src/components/PostCard.tsx
index 31d6da0..5312ffa 100644
--- a/source/src/components/PostCard.tsx
+++ b/source/src/components/PostCard.tsx
@@ -1,7 +1,7 @@
import styled from "styled-components"
import { useNavigate } from "react-router-dom"
-import { Post } from "../types/typings"
+import { PostData } from "../../types/typing"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import {
@@ -40,6 +40,7 @@ const StyledPostCard = styled(MainContent)`
const StyledTitle = styled.h1`
font-size: 2rem;
font-style: bold;
+ margin: 0;
margin-bottom: 1rem;
`
@@ -51,15 +52,7 @@ const StyledMetaContainer = styled.small`
})};
`
-const StyledPostCardContent = styled.div`
- color: ${(props) =>
- theming.theme(props.theme.currentTheme, {
- light: "grey",
- dark: "lightgrey",
- })};
-`
-
-interface _PostDateBase extends Post {
+interface _PostDateBase extends PostData {
url: string
}
@@ -72,16 +65,17 @@ const PostCard = (props: Props) => {
return (
{
+ onClick={() =>
navigate(process.env.PUBLIC_URL + props.postData.url)
- }}
+ }
>
{props.postData?.title || "No title"}
+
+
- {props.postData.tags ? (
+ {props.postData.tags &&
props.postData.tags.map((tag) => {
return (
{
text={tag}
/>
)
- })
- ) : (
- <>>
- )}
+ })}
+
{props.postData?.date || "Unknown date"}
@@ -110,15 +102,6 @@ const PostCard = (props: Props) => {
? props.postData.wordCount + " words"
: "unknown words"}
-
-
-
-
)
}
diff --git a/source/src/components/Tag.tsx b/source/src/components/Tag.tsx
index 4687584..2f1fd9b 100644
--- a/source/src/components/Tag.tsx
+++ b/source/src/components/Tag.tsx
@@ -1,7 +1,7 @@
import { MouseEvent } from "react"
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 theming from "../styles/theming"
@@ -24,7 +24,7 @@ interface Props {
const Tag = (props: Props) => {
return (
- {props.text}
+ {props.text}
)
}
diff --git a/source/src/pages/Page/Meta.tsx b/source/src/pages/Page/Meta.tsx
index c751ceb..f5d10bf 100644
--- a/source/src/pages/Page/Meta.tsx
+++ b/source/src/pages/Page/Meta.tsx
@@ -6,7 +6,7 @@ import {
faHourglass,
} from "@fortawesome/free-solid-svg-icons"
-import { FetchedPage } from "../../types/typings"
+import { FetchedPage } from "../../../types/typing"
import theming from "../../styles/theming"
const StyledMetaContainer = styled.div`
diff --git a/source/src/pages/Page/Toc.tsx b/source/src/pages/Page/Toc.tsx
index 06fb96c..2580652 100644
--- a/source/src/pages/Page/Toc.tsx
+++ b/source/src/pages/Page/Toc.tsx
@@ -8,7 +8,7 @@ import styled from "styled-components"
import theming from "../../styles/theming"
-import { FetchedPage } from "../../types/typings"
+import { FetchedPage } from "../../../types/typing"
const StyledTocToggleButton = styled.button`
border: none;
diff --git a/source/src/pages/Page/index.tsx b/source/src/pages/Page/index.tsx
index 2460bd1..d4983f8 100644
--- a/source/src/pages/Page/index.tsx
+++ b/source/src/pages/Page/index.tsx
@@ -4,7 +4,7 @@ import { useLocation } from "react-router-dom"
import styled from "styled-components"
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 Loading from "../../components/Loading"
diff --git a/source/src/pages/PostList.tsx b/source/src/pages/PostList.tsx
index def7c3e..0ad06de 100644
--- a/source/src/pages/PostList.tsx
+++ b/source/src/pages/PostList.tsx
@@ -12,7 +12,7 @@ import PostCard from "../components/PostCard"
import _map from "../data/map.json"
import theming from "../styles/theming"
-import { Map } from "../types/typings"
+import { Map } from "../../types/typing"
const map: Map = _map
diff --git a/source/src/pages/Search/TagSelect.tsx b/source/src/pages/Search/TagSelect.tsx
index 8d34c66..7d063f0 100644
--- a/source/src/pages/Search/TagSelect.tsx
+++ b/source/src/pages/Search/TagSelect.tsx
@@ -5,7 +5,7 @@ import Select from "react-select"
import theming from "../../styles/theming"
import { Query } from "."
-import { Map } from "../../types/typings"
+import { Map } from "../../../types/typing"
import _map from "../../data/map.json"
const map: Map = _map
diff --git a/source/src/pages/Search/index.tsx b/source/src/pages/Search/index.tsx
index 679689e..806ea3c 100644
--- a/source/src/pages/Search/index.tsx
+++ b/source/src/pages/Search/index.tsx
@@ -19,7 +19,7 @@ import MainContent from "../../components/MainContent"
import SearchBar from "./SearchBar"
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/theme/default.css"
diff --git a/source/src/styles/globalStyle.tsx b/source/src/styles/globalStyle.tsx
index 177b3fa..a2c0812 100644
--- a/source/src/styles/globalStyle.tsx
+++ b/source/src/styles/globalStyle.tsx
@@ -96,7 +96,7 @@ const kbdCSS = css`
dark: "white",
})};
line-height: 1.4;
- font-size: 10px;
+ font-size: 13.5px;
display: inline-block;
box-shadow: ${(props) =>
theming.theme(props.theme.currentTheme, {
@@ -108,11 +108,6 @@ const kbdCSS = css`
light: "#F7F7F7",
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`
- .white-link a {
- text-decoration: none;
- color: ${theming.color.linkColor};
+const headerCSS = css`
+ /* intentionally left out h1 */
- &:visited {
- color: ${theming.color.linkColor};
- }
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ margin-top: 2.5rem;
}
`
@@ -182,7 +178,7 @@ const globalStyle = css`
${kbdCSS}
${tableCSS}
${blockquoteCSS}
- ${whiteLinkCSS}
+ ${headerCSS}
body {
overflow-x: hidden;
diff --git a/source/src/styles/theming.ts b/source/src/styles/theming.ts
index b89ebda..df819d8 100644
--- a/source/src/styles/theming.ts
+++ b/source/src/styles/theming.ts
@@ -23,7 +23,7 @@ export default {
x2_small: "3px",
x_small: "8px",
small: 0,
- medium: "14px",
+ medium: "1rem",
large: 0,
x_large: 0,
screen_size1: "1000px",
diff --git a/source/src/types/typings.d.ts b/source/src/types/typings.d.ts
deleted file mode 100644
index 2b4debc..0000000
--- a/source/src/types/typings.d.ts
+++ /dev/null
@@ -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
-}
diff --git a/source/generate/types/markdown-it-task-checkbox.d.ts b/source/types/markdown-it-task-checkbox.d.ts
similarity index 100%
rename from source/generate/types/markdown-it-task-checkbox.d.ts
rename to source/types/markdown-it-task-checkbox.d.ts
diff --git a/source/src/types/react-date-range.d.ts b/source/types/react-date-range.d.ts
similarity index 100%
rename from source/src/types/react-date-range.d.ts
rename to source/types/react-date-range.d.ts
diff --git a/source/src/types/styled-comonents.d.ts b/source/types/styled-components.ts
similarity index 100%
rename from source/src/types/styled-comonents.d.ts
rename to source/types/styled-components.ts
diff --git a/source/types/typing.ts b/source/types/typing.ts
new file mode 100644
index 0000000..0046da3
--- /dev/null
+++ b/source/types/typing.ts
@@ -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[]
+}