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[] +}