From abd488c97247d1c528bcd26789fa59e94cccdcbf Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 3 Oct 2022 18:39:19 +0100 Subject: [PATCH] feat: add headings --- package.json | 4 ++ pnpm-lock.yaml | 96 +++++++++++++++++++++++++++++++++++++++++++ src/css/prose.css | 16 ++++++++ src/markdown/index.ts | 18 ++++++++ 4 files changed, 134 insertions(+) diff --git a/package.json b/package.json index e1cbc0ed..5ee9f5f4 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@floating-ui/react-dom-interactions": "^0.10.1", "@headlessui/react": "^1.7.2", "@heroicons/react": "^2.0.11", + "@iconify-json/akar-icons": "^1.1.8", "@iconify-json/tabler": "^1.1.36", "@monaco-editor/react": "^4.4.6", "@proselog/jwt": "^0.1.1", @@ -74,10 +75,12 @@ "react-scroll": "^1.8.7", "react-sortablejs": "^6.1.1", "refractor": "^4.8.0", + "rehype-autolink-headings": "^6.1.1", "rehype-prism-plus": "^1.5.0", "rehype-raw": "^6.1.1", "rehype-react": "^7.1.1", "rehype-sanitize": "^5.0.1", + "rehype-slug": "^5.0.1", "rehype-stringify": "^9.0.3", "remark-directive": "^2.0.1", "remark-directive-rehype": "^0.4.2", @@ -86,6 +89,7 @@ "remark-parse": "^10.0.1", "remark-rehype": "^10.1.0", "remark-stringify": "^10.0.2", + "remark-toc": "^8.0.1", "sharp": "^0.31.0", "sortablejs": "^1.15.0", "ua-parser-js": "^1.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e95606e5..79a36a4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,7 @@ specifiers: '@floating-ui/react-dom-interactions': ^0.10.1 '@headlessui/react': ^1.7.2 '@heroicons/react': ^2.0.11 + '@iconify-json/akar-icons': ^1.1.8 '@iconify-json/bi': ^1.1.8 '@iconify-json/bxs': ^1.1.4 '@iconify-json/carbon': ^1.1.8 @@ -94,10 +95,12 @@ specifiers: react-scroll: ^1.8.7 react-sortablejs: ^6.1.1 refractor: ^4.8.0 + rehype-autolink-headings: ^6.1.1 rehype-prism-plus: ^1.5.0 rehype-raw: ^6.1.1 rehype-react: ^7.1.1 rehype-sanitize: ^5.0.1 + rehype-slug: ^5.0.1 rehype-stringify: ^9.0.3 remark-directive: ^2.0.1 remark-directive-rehype: ^0.4.2 @@ -106,6 +109,7 @@ specifiers: remark-parse: ^10.0.1 remark-rehype: ^10.1.0 remark-stringify: ^10.0.2 + remark-toc: ^8.0.1 sharp: ^0.31.0 sortablejs: ^1.15.0 tailwindcss: ^3.1.8 @@ -137,6 +141,7 @@ dependencies: '@floating-ui/react-dom-interactions': 0.10.1_rj7ozvcq3uehdlnj3cbwzbi5ce '@headlessui/react': 1.7.2_biqbaboplfbrettd7655fr4n2y '@heroicons/react': 2.0.11_react@18.2.0 + '@iconify-json/akar-icons': 1.1.8 '@iconify-json/tabler': 1.1.36 '@monaco-editor/react': 4.4.6_biqbaboplfbrettd7655fr4n2y '@proselog/jwt': 0.1.1 @@ -182,10 +187,12 @@ dependencies: react-scroll: 1.8.7_biqbaboplfbrettd7655fr4n2y react-sortablejs: 6.1.4_pkvhgat2fktt3g4eqjjiisgkie refractor: 4.8.0 + rehype-autolink-headings: 6.1.1 rehype-prism-plus: 1.5.0 rehype-raw: 6.1.1 rehype-react: 7.1.1_@types+react@18.0.21 rehype-sanitize: 5.0.1 + rehype-slug: 5.0.1 rehype-stringify: 9.0.3 remark-directive: 2.0.1 remark-directive-rehype: 0.4.2 @@ -194,6 +201,7 @@ dependencies: remark-parse: 10.0.1 remark-rehype: 10.1.0 remark-stringify: 10.0.2 + remark-toc: 8.0.1 sharp: 0.31.0 sortablejs: 1.15.0 ua-parser-js: 1.0.2 @@ -1371,6 +1379,12 @@ packages: /@humanwhocodes/object-schema/1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + /@iconify-json/akar-icons/1.1.8: + resolution: {integrity: sha512-ip0q7oG8bKAmvTUiSzVGlsKCiVFbyQJS1Zl5jfBnXMXu8iBLf5KwLEOYWbg2Xj85KyOteZ1iG3cvEQipZv4iiA==} + dependencies: + '@iconify/types': 2.0.0 + dev: false + /@iconify-json/bi/1.1.8: resolution: {integrity: sha512-aUmRT4iKjpz9fRF2/6d2JcMd7Lz6Fwn4rQnMZy8rhAw3CCBKPRpcbSW3tf+O1K4+oVe/+gV2FqO50ZZiWsjaEw==} dependencies: @@ -2255,6 +2269,14 @@ packages: resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==} dev: true + /@types/extend/3.0.1: + resolution: {integrity: sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw==} + dev: false + + /@types/github-slugger/1.3.0: + resolution: {integrity: sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==} + dev: false + /@types/hast/2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} dependencies: @@ -6079,6 +6101,10 @@ packages: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} dev: false + /github-slugger/1.4.0: + resolution: {integrity: sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==} + dev: false + /glob-parent/5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -6265,6 +6291,16 @@ packages: web-namespaces: 2.0.1 dev: false + /hast-util-has-property/2.0.0: + resolution: {integrity: sha512-4Qf++8o5v14us4Muv3HRj+Er6wTNGA/N9uCaZMty4JWvyFKLdhULrv4KE1b65AthsSO9TXSZnjuxS8ecIyhb0w==} + dev: false + + /hast-util-heading-rank/2.1.0: + resolution: {integrity: sha512-w+Rw20Q/iWp2Bcnr6uTrYU6/ftZLbHKhvc8nM26VIWpDqDMlku2iXUVTeOlsdoih/UKQhY7PHQ+vZ0Aqq8bxtQ==} + dependencies: + '@types/hast': 2.3.4 + dev: false + /hast-util-is-element/2.1.2: resolution: {integrity: sha512-thjnlGAnwP8ef/GSO1Q8BfVk2gundnc2peGQqEg2kUt/IqesiGg/5mSwN2fE7nLzy61pg88NG6xV+UrGOrx9EA==} dependencies: @@ -7546,6 +7582,19 @@ packages: resolution: {integrity: sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==} dev: false + /mdast-util-toc/6.1.0: + resolution: {integrity: sha512-0PuqZELXZl4ms1sF7Lqigrqik4Ll3UhbI+jdTrfw7pZ9QPawgl7LD4GQ8MkU7bT/EwiVqChNTbifa2jLLKo76A==} + dependencies: + '@types/extend': 3.0.1 + '@types/github-slugger': 1.3.0 + '@types/mdast': 3.0.10 + extend: 3.0.2 + github-slugger: 1.4.0 + mdast-util-to-string: 3.1.0 + unist-util-is: 5.1.1 + unist-util-visit: 3.1.0 + dev: false + /mdn-data/2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} dev: true @@ -9202,6 +9251,18 @@ packages: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} + /rehype-autolink-headings/6.1.1: + resolution: {integrity: sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==} + dependencies: + '@types/hast': 2.3.4 + extend: 3.0.2 + hast-util-has-property: 2.0.0 + hast-util-heading-rank: 2.1.0 + hast-util-is-element: 2.1.2 + unified: 10.1.2 + unist-util-visit: 4.1.1 + dev: false + /rehype-parse/8.0.4: resolution: {integrity: sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==} dependencies: @@ -9251,6 +9312,18 @@ packages: unified: 10.1.2 dev: false + /rehype-slug/5.0.1: + resolution: {integrity: sha512-X5v3wV/meuOX9NFcGhJvUpEjIvQl2gDvjg3z40RVprYFt7q3th4qMmYLULiu3gXvbNX1ppx+oaa6JyY1W67pTA==} + dependencies: + '@types/hast': 2.3.4 + github-slugger: 1.4.0 + hast-util-has-property: 2.0.0 + hast-util-heading-rank: 2.1.0 + hast-util-to-string: 2.0.0 + unified: 10.1.2 + unist-util-visit: 4.1.1 + dev: false + /rehype-stringify/9.0.3: resolution: {integrity: sha512-kWiZ1bgyWlgOxpqD5HnxShKAdXtb2IUljn3hQAhySeak6IOQPPt6DeGnsIh4ixm7yKJWzm8TXFuC/lPfcWHJqw==} dependencies: @@ -9323,6 +9396,14 @@ packages: unified: 10.1.2 dev: false + /remark-toc/8.0.1: + resolution: {integrity: sha512-7he2VOm/cy13zilnOTZcyAoyoolV26ULlon6XyCFU+vG54Z/LWJnwphj/xKIDLOt66QmJUgTyUvLVHi2aAElyg==} + dependencies: + '@types/mdast': 3.0.10 + mdast-util-toc: 6.1.0 + unified: 10.1.2 + dev: false + /remove-accents/0.4.2: resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==} dev: false @@ -10398,6 +10479,13 @@ packages: unist-util-is: 3.0.0 dev: false + /unist-util-visit-parents/4.1.1: + resolution: {integrity: sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 5.1.1 + dev: false + /unist-util-visit-parents/5.1.0: resolution: {integrity: sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==} dependencies: @@ -10418,6 +10506,14 @@ packages: unist-util-visit-parents: 2.1.2 dev: false + /unist-util-visit/3.1.0: + resolution: {integrity: sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 5.1.1 + unist-util-visit-parents: 4.1.1 + dev: false + /unist-util-visit/4.1.1: resolution: {integrity: sha512-n9KN3WV9k4h1DxYR1LoajgN93wpEi/7ZplVe02IoB4gH5ctI1AaF2670BLHQYbwj+pY83gFtyeySFiyMHJklrg==} dependencies: diff --git a/src/css/prose.css b/src/css/prose.css index 0bf1d975..99da4d1c 100644 --- a/src/css/prose.css +++ b/src/css/prose.css @@ -18,6 +18,22 @@ @apply mb-0; } +.prose .xlog-anchor { + @apply pr-1 -ml-5 text-base align-middle hidden; +} + +.prose .xlog-anchor > span { + @apply inline-block text-zinc-900; +} + +.prose h2:hover .xlog-anchor, +.prose h3:hover .xlog-anchor, +.prose h4:hover .xlog-anchor, +.prose h5:hover .xlog-anchor, +.prose h6:hover .xlog-anchor { + @apply inline; +} + .prose h2 { @apply text-3xl font-semibold; @apply my-7; diff --git a/src/markdown/index.ts b/src/markdown/index.ts index f1ab1de2..7ffead97 100644 --- a/src/markdown/index.ts +++ b/src/markdown/index.ts @@ -22,6 +22,8 @@ import remarkDirective from "remark-directive" import remarkDirectiveRehype from "remark-directive-rehype" import { remarkYoutube } from "./remark-youtube" import sanitizeScheme from "./sanitize-schema" +import rehypeSlug from "rehype-slug" +import rehypeAutolinkHeadings from "rehype-autolink-headings" export type MarkdownEnv = { excerpt: string @@ -85,6 +87,22 @@ export const renderPageContent = ( .use(rehypeTable) .use(rehypeExternalLink) .use(rehypeWrapCode) + .use(rehypeSlug) + .use(rehypeAutolinkHeadings, { + properties: { + className: ["xlog-anchor"], + ariaHidden: true, + tabIndex: -1, + }, + content: { + type: "element", + tagName: "span", + properties: { + className: ["i-akar-icons:link-chain"], + }, + children: [], + }, + }) .use(html ? () => (tree: any) => {} : rehypeReact, { createElement: createElement, components: {