From df50e5897bc6176ff257a59d2a0b7e4cc515e68e Mon Sep 17 00:00:00 2001 From: Yufan Sheng Date: Fri, 15 Nov 2024 12:54:58 +0800 Subject: [PATCH] feat: add toc generation for astro articles. (#63) --- README.md | 7 +- options.ts | 14 +- package-lock.json | 12 +- src/assets/scripts/yufan.me.js | 9 + src/assets/styles/globals.css | 287 +- src/assets/styles/iconfont/iconfont.html | 3566 +++++++++++++++++ src/assets/styles/toc.css | 271 ++ src/components/page/friend/Friends.astro | 10 +- src/components/page/toc/TableOfContents.astro | 34 + src/components/page/toc/TocItems.astro | 22 + src/content/config.ts | 26 + src/content/friends/index.yml | 9 +- .../2024/2024-04-07-switch-blog-to-nextjs.mdx | 3 + .../2024/2024-06-30-pre-sale-housing.mdx | 1 + .../posts/2024/2024-10-16-english-in-use.mdx | 1 + src/helpers/schema.ts | 17 +- src/helpers/toc.ts | 34 + src/layouts/PageLayout.astro | 4 +- src/layouts/PostLayout.astro | 4 +- 19 files changed, 4281 insertions(+), 50 deletions(-) create mode 100644 src/assets/styles/iconfont/iconfont.html create mode 100644 src/assets/styles/toc.css create mode 100644 src/components/page/toc/TableOfContents.astro create mode 100644 src/components/page/toc/TocItems.astro create mode 100644 src/helpers/toc.ts diff --git a/README.md b/README.md index 29b3405..35ebf4c 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ date: 2013/7/13 20:46:25 | Setting | Description | Required | Default | |-------------|--------------------------------------|----------|----------------------| -| `id` | ID (unique), used as the permalink | true | Filename | +| `slug` | ID (unique), used as the permalink | true | Filename | | `title` | Title | true | Filename | | `date` | Published date | true | | | `updated` | Updated date | false | Published date | @@ -145,18 +145,21 @@ date: 2013/7/13 20:46:25 | `summary` | Post summary in plain text | false | First 140 characters | | `cover` | The cover image | false | `null` | | `published` | Whether the post should be published | false | `true` | +| `toc` | Display the Table of Contents | false | `false` | +| `alias` | The alternatives slugs for post | false | `[]` | ### Pages Front Matter Settings | Setting | Description | Required | Default | |-------------|--------------------------------------|----------|----------------| -| `id` | ID (unique), used as the permalink | true | Filename | +| `slug` | ID (unique), used as the permalink | true | Filename | | `title` | Title | true | Filename | | `date` | Published date | true | | | `updated` | Updated date | false | Published date | | `comments` | Enables comment feature for the post | false | `true` | | `cover` | The cover image | false | `null` | | `published` | Whether the post should be published | false | `true` | +| `toc` | Display the Table of Contents | false | `false` | ## Weblog Design diff --git a/options.ts b/options.ts index 5372bee..4b125f5 100644 --- a/options.ts +++ b/options.ts @@ -74,6 +74,10 @@ const Options = z size: z.number(), }), }), + toc: z.object({ + minHeadingLevel: z.number().optional().default(2), + maxHeadingLevel: z.number().optional().default(3), + }), }), thumbnail: z .function() @@ -92,7 +96,11 @@ const Options = z assetsPrefix, defaultOpenGraph: (): string => `${assetsPrefix()}/images/open-graph.png`, }; - }); + }) + .refine( + (options) => options.settings.toc.minHeadingLevel <= options.settings.toc.maxHeadingLevel, + 'Invalid toc setting, the minHeadingLevel should bellow the maxHeadingLevel', + ); const options: z.input = { local: { @@ -191,6 +199,10 @@ const options: z.input = { size: 120, }, }, + toc: { + minHeadingLevel: 2, + maxHeadingLevel: 3, + }, }, thumbnail: ({ src, width, height }) => { if (src.endsWith('.svg')) { diff --git a/package-lock.json b/package-lock.json index 76735ed..6755bcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3896,9 +3896,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.58", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.58.tgz", - "integrity": "sha512-al2l4r+24ZFL7WzyPTlyD0fC33LLzvxqLCwurtBibVPghRGO9hSTl+tis8t1kD7biPiH/en4U0I7o/nQbYeoVA==", + "version": "1.5.59", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.59.tgz", + "integrity": "sha512-faAXB6+gEbC8FsiRdpOXgOe4snP49YwjiXynEB8Mp7sUx80W5eN+BnnBHJ/F7eIeLzs+QBfDD40bJMm97oEFcw==", "license": "ISC" }, "node_modules/emmet": { @@ -4621,9 +4621,9 @@ } }, "node_modules/hast-util-raw": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", - "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", diff --git a/src/assets/scripts/yufan.me.js b/src/assets/scripts/yufan.me.js index 0d7a917..135830f 100644 --- a/src/assets/scripts/yufan.me.js +++ b/src/assets/scripts/yufan.me.js @@ -292,6 +292,15 @@ for (const anchor of document.querySelectorAll('a[href^="#"]')) { }); } +const tocToggle = document.querySelector('.toggle-menu-tree'); +if (typeof tocToggle !== 'undefined' && tocToggle !== null) { + tocToggle.addEventListener('click', () => { + const body = document.querySelector('body'); + const displayToc = !body.classList.contains('display-menu-tree'); + body.classList.toggle('display-menu-tree', displayToc); + }); +} + // Add like button for updating likes. const likeButton = document.querySelector('button.post-like'); diff --git a/src/assets/styles/globals.css b/src/assets/styles/globals.css index 67b4952..2432f34 100644 --- a/src/assets/styles/globals.css +++ b/src/assets/styles/globals.css @@ -1424,14 +1424,6 @@ textarea.form-control { padding: 0.75rem 1rem 1rem; } -.list-bookmarks .list-favicon { - background-size: cover; - display: inline-block; - margin-left: 8px; - width: 12px; - height: 12px; -} - @media (max-width: 767.98px) { .list-bookmarks { margin: 2.5rem 0 0; @@ -3266,3 +3258,282 @@ blockquote p:last-child { margin: 0 2rem 1.25rem; } } + +/** Table of Contents ------------------------------------ Default */ +.toggle-menu-tree { + top: 0; + bottom: 0; + font-size: 1.375rem; + position: fixed; + right: 0; + margin-right: -5rem; + margin-bottom: auto; + margin-top: auto; + padding-left: 0.35rem; + color: #555; + width: 6.25rem; + height: 6.25rem; + background-color: rgba(255, 255, 255, 0.9); + cursor: pointer; + line-height: 6.25rem; + border-radius: 6.25rem; + border: 0.0625rem solid #f0f0f0; + opacity: 1; + font-family: -apple-system; + z-index: 890; + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; + -webkit-box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.117); + -moz-box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.117); + box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.117); +} + +.toggle-menu-tree i { + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; +} + +.toggle-menu-tree:hover { + background: #fafafa; + -webkit-transform: translateX(-1.25rem); + -moz-transform: translateX(-1.25rem); + -ms-transform: translateX(-1.25rem); + -o-transform: translateX(-1.25rem); + transform: translateX(-1.25rem); +} + +.post-menu { + position: fixed; + display: table; + top: 0; + right: -18.125rem; + bottom: 0; + width: 17.5rem; + height: 100%; + background-color: #fafafa; + border-left: 0.0625rem solid #f0f0f0; + opacity: 1; + z-index: 880; + font-weight: 400; + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; +} + +.post-menu::-webkit-scrollbar { + height: 8px; + width: 4px; +} + +.post-menu::-webkit-scrollbar-button { + height: 0; + width: 0; +} + +.post-menu::-webkit-scrollbar-button:start:decrement, +.post-menu::-webkit-scrollbar-button:end:increment { + display: block; +} + +.post-menu::-webkit-scrollbar-button:vertical:start:increment, +.post-menu::-webkit-scrollbar-button:vertical:end:decrement { + display: none; +} + +.post-menu::-webkit-scrollbar-track:vertical, +.post-menu::-webkit-scrollbar-track:horizontal { + background-clip: padding-box; + background-color: #191919; + border: 0 solid transparent; +} + +.post-menu::-webkit-scrollbar-track:hover { + background-color: #191919; + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.1); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.1); +} + +.post-menu::-webkit-scrollbar-track:active { + background-color: #191919; + -webkit-box-shadow: + inset 1px 0 0 rgba(0, 0, 0, 0.14), + inset -1px -1px 0 rgba(0, 0, 0, 0.07); + -moz-box-shadow: + inset 1px 0 0 rgba(0, 0, 0, 0.14), + inset -1px -1px 0 rgba(0, 0, 0, 0.07); + box-shadow: + inset 1px 0 0 rgba(0, 0, 0, 0.14), + inset -1px -1px 0 rgba(0, 0, 0, 0.07); +} + +.post-menu::-webkit-scrollbar-thumb { + background-clip: padding-box; + background-color: rgba(255, 255, 255, 0.3); + min-height: 40px; + padding-top: 100px; + border-radius: 2px; + -webkit-box-shadow: + inset 1px 1px 0 rgba(0, 0, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.07); + -moz-box-shadow: + inset 1px 1px 0 rgba(0, 0, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.07); + box-shadow: + inset 1px 1px 0 rgba(0, 0, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.07); +} + +.post-menu::-webkit-scrollbar-thumb:hover { + background-color: rgba(255, 255, 255, 0.4); + -webkit-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25); + -moz-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25); + box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25); +} + +.post-menu::-webkit-scrollbar-thumb:active { + background-color: rgba(255, 255, 255, 0.5); + -webkit-box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35); + -moz-box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35); + box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35); +} + +.post-menu::-webkit-scrollbar-thumb:vertical, +.post-menu::-webkit-scrollbar-thumb:horizontal { + border: 0 solid transparent; +} + +.toc-wrap { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: -3rem; + overflow-x: hidden; + overflow-y: auto; + --webkit-overflow-scrolling: touch; +} + +.toc-content { + margin-right: 3rem; + padding-top: 2.875rem; + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; +} + +.post-menu-title { + font-size: 1.5rem; + text-align: left; + line-height: 3.6rem; + width: 100%; + font-weight: 700; + padding: 0 2.5rem; + color: #202020; +} + +.index-menu { + padding-top: 2rem; +} + +.index-menu-list { + line-height: 1.8em; + list-style: none; + padding: 0; +} + +.index-menu-item { + overflow: hidden; + text-overflow: ellipsis; +} + +.index-menu-item > .index-menu-list span.menu-content { + padding-left: 2rem; +} + +.index-menu-item > .index-menu-list > .index-menu-item > .index-menu-list span.menu-content { + padding-left: 4rem; +} + +.index-menu-item.current > a.index-menu-link { + background: #f5f5f5; + color: #1abc9c; + font-weight: 700; +} + +a.index-menu-link { + color: #555; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.85rem; + padding: 0.375rem 2.5rem; + position: relative; + display: block; +} + +a.index-menu-link:hover { + background: #efefef; + color: #333; +} + +.post-menu-overlay { + visibility: hidden; + pointer-events: none; +} + +/** Table of Contents ------------------------------------ Display */ +.display-menu-tree .post-menu { + -webkit-transform: translateX(-18.125rem); + -moz-transform: translateX(-18.125rem); + -ms-transform: translateX(-18.125rem); + -o-transform: translateX(-18.125rem); + transform: translateX(-18.125rem); + z-index: 1000; +} + +.display-menu-tree .toggle-menu-tree { + background: #fafafa; + padding-left: 0; + width: 3.125rem; + height: 3.125rem; + line-height: 3.125rem; + text-align: center; + margin-right: -1.5625rem; + -webkit-transform: translateX(-17.5rem); + -moz-transform: translateX(-17.5rem); + -ms-transform: translateX(-17.5rem); + -o-transform: translateX(-17.5rem); + transform: translateX(-17.5rem); + z-index: 1500; +} + +.display-menu-tree .toggle-menu-tree i { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); +} + +.display-menu-tree .post-menu-overlay { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + background-color: rgba(8, 15, 25, 0.3); + visibility: visible; + pointer-events: initial; + z-index: 500; +} diff --git a/src/assets/styles/iconfont/iconfont.html b/src/assets/styles/iconfont/iconfont.html new file mode 100644 index 0000000..e04fa53 --- /dev/null +++ b/src/assets/styles/iconfont/iconfont.html @@ -0,0 +1,3566 @@ + + + + + Iconfont + + + + + + + + + + + +
+ +
+
+
    +
  • + +
    客服
    +
    .icon-kefu
    +
  • +
  • + +
    客服
    +
    .icon-kefu1
    +
  • +
  • + +
    caret-up-1
    +
    .icon-caret-up1
    +
  • +
  • + +
    caret-down-1
    +
    .icon-caret-down1
    +
  • +
  • + +
    dot
    +
    .icon-dot
    +
  • +
  • + +
    play
    +
    .icon-play
    +
  • +
  • + +
    资源 2
    +
    .icon-zan
    +
  • +
  • + +
    square
    +
    .icon-ellipsis1
    +
  • +
  • + +
    箭头1
    +
    .icon-jiantou1
    +
  • +
  • + +
    night-circle-fill
    +
    .icon-night-circle-fill
    +
  • +
  • + +
    night
    +
    .icon-night
    +
  • +
  • + +
    share-square
    +
    .icon-share-square
    +
  • +
  • + +
    bxl-linkedin
    +
    .icon-linkedin2
    +
  • +
  • + +
    facebook
    +
    .icon-facebook1
    +
  • +
  • + +
    instagram-fill
    +
    .icon-instagram-fill1
    +
  • +
  • + +
    arrowdown
    +
    .icon-arrowdown
    +
  • +
  • + +
    dribbble-circle-fill
    +
    .icon-dribbble-circle-fill
    +
  • +
  • + +
    upload
    +
    .icon-upload
    +
  • +
  • + +
    google plus-circle-f
    +
    .icon-googleplus-circle-f
    +
  • +
  • + +
    colum-height
    +
    .icon-colum-height
    +
  • +
  • + +
    medium-circle-fill
    +
    .icon-medium-circle-fill
    +
  • +
  • + +
    vertical-align-botto
    +
    .icon-vertical-align-botto
    +
  • +
  • + +
    QQ-circle-fill
    +
    .icon-QQ-circle-fill
    +
  • +
  • + +
    vertical-align-middl
    +
    .icon-vertical-align-middl
    +
  • +
  • + +
    IE-circle-fill
    +
    .icon-IE-circle-fill
    +
  • +
  • + +
    totop
    +
    .icon-totop
    +
  • +
  • + +
    google-circle-fill
    +
    .icon-google-circle-fill
    +
  • +
  • + +
    vertical-align-top
    +
    .icon-vertical-align-top
    +
  • +
  • + +
    dingtalk-circle-fill
    +
    .icon-dingtalk-circle-fill
    +
  • +
  • + +
    download
    +
    .icon-download
    +
  • +
  • + +
    sketch-circle-fill
    +
    .icon-sketch-circle-fill
    +
  • +
  • + +
    sort-descending
    +
    .icon-sort-descending
    +
  • +
  • + +
    slack-circle-fill
    +
    .icon-slack-circle-fill
    +
  • +
  • + +
    sort-ascending
    +
    .icon-sort-ascending
    +
  • +
  • + +
    twitter-circle-fill
    +
    .icon-twitter-circle-fill
    +
  • +
  • + +
    fall
    +
    .icon-fall
    +
  • +
  • + +
    taobao-circle-fill
    +
    .icon-taobao-circle-fill
    +
  • +
  • + +
    swap
    +
    .icon-swap
    +
  • +
  • + +
    weibo-circle-fill
    +
    .icon-weibo-circle-fill
    +
  • +
  • + +
    stock
    +
    .icon-stock
    +
  • +
  • + +
    zhihu-circle-fill
    +
    .icon-zhihu-circle-fill
    +
  • +
  • + +
    rise
    +
    .icon-rise
    +
  • +
  • + +
    reddit-circle-fill
    +
    .icon-reddit-circle-fill
    +
  • +
  • + +
    indent
    +
    .icon-indent
    +
  • +
  • + +
    alipay-square-fill
    +
    .icon-alipay-square-fill
    +
  • +
  • + +
    outdent
    +
    .icon-outdent
    +
  • +
  • + +
    dingtalk-square-fill
    +
    .icon-dingtalk-square-fill
    +
  • +
  • + +
    menu
    +
    .icon-menu
    +
  • +
  • + +
    CodeSandbox-square-f
    +
    .icon-CodeSandbox-square-f
    +
  • +
  • + +
    unordered list
    +
    .icon-unorderedlist
    +
  • +
  • + +
    behance-square-fill
    +
    .icon-behance-square-fill
    +
  • +
  • + +
    ordered list
    +
    .icon-orderedlist
    +
  • +
  • + +
    amazon-square-fill
    +
    .icon-amazon-square-fill
    +
  • +
  • + +
    align-right
    +
    .icon-align-right
    +
  • +
  • + +
    codepen-square-fill
    +
    .icon-codepen-square-fill
    +
  • +
  • + +
    align-center
    +
    .icon-align-center
    +
  • +
  • + +
    dribbble-square-fill
    +
    .icon-dribbble-square-fill
    +
  • +
  • + +
    align-left
    +
    .icon-align-left
    +
  • +
  • + +
    dropbox-square-fill
    +
    .icon-dropbox-square-fill
    +
  • +
  • + +
    pic-right
    +
    .icon-pic-right
    +
  • +
  • + +
    facebook-fill
    +
    .icon-facebook-fill
    +
  • +
  • + +
    pic-left
    +
    .icon-pic-left
    +
  • +
  • + +
    google plus-square-f
    +
    .icon-googleplus-square-f
    +
  • +
  • + +
    bold
    +
    .icon-bold
    +
  • +
  • + +
    google-square-fill
    +
    .icon-google-square-fill
    +
  • +
  • + +
    font-colors
    +
    .icon-font-colors
    +
  • +
  • + +
    instagram-fill
    +
    .icon-instagram-fill
    +
  • +
  • + +
    exclaimination
    +
    .icon-exclaimination
    +
  • +
  • + +
    IE-square-fill
    +
    .icon-IE-square-fill
    +
  • +
  • + +
    font-size
    +
    .icon-font-size
    +
  • +
  • + +
    medium-square-fill
    +
    .icon-medium-square-fill
    +
  • +
  • + +
    check-circle
    +
    .icon-check-circle
    +
  • +
  • + +
    infomation
    +
    .icon-infomation
    +
  • +
  • + +
    linkedin-fill
    +
    .icon-linkedin-fill
    +
  • +
  • + +
    CI
    +
    .icon-CI
    +
  • +
  • + +
    line-height
    +
    .icon-line-height
    +
  • +
  • + +
    QQ-square-fill
    +
    .icon-QQ-square-fill
    +
  • +
  • + +
    Dollar
    +
    .icon-Dollar
    +
  • +
  • + +
    strikethrough
    +
    .icon-strikethrough
    +
  • +
  • + +
    reddit-square-fill
    +
    .icon-reddit-square-fill
    +
  • +
  • + +
    compass
    +
    .icon-compass
    +
  • +
  • + +
    underline
    +
    .icon-underline
    +
  • +
  • + +
    twitter-square-fill
    +
    .icon-twitter-square-fill
    +
  • +
  • + +
    close-circle
    +
    .icon-close-circle
    +
  • +
  • + +
    number
    +
    .icon-number
    +
  • +
  • + +
    sketch-square-fill
    +
    .icon-sketch-square-fill
    +
  • +
  • + +
    frown
    +
    .icon-frown
    +
  • +
  • + +
    italic
    +
    .icon-italic
    +
  • +
  • + +
    slack-square-fill
    +
    .icon-slack-square-fill
    +
  • +
  • + +
    info-circle
    +
    .icon-info-circle
    +
  • +
  • + +
    code
    +
    .icon-code
    +
  • +
  • + +
    taobao-square-fill
    +
    .icon-taobao-square-fill
    +
  • +
  • + +
    left-circle
    +
    .icon-left-circle
    +
  • +
  • + +
    column-width
    +
    .icon-column-width
    +
  • +
  • + +
    weibo-square-fill
    +
    .icon-weibo-square-fill
    +
  • +
  • + +
    down-circle
    +
    .icon-down-circle
    +
  • +
  • + +
    check
    +
    .icon-check
    +
  • +
  • + +
    zhihu-square-fill
    +
    .icon-zhihu-square-fill
    +
  • +
  • + +
    EURO
    +
    .icon-EURO
    +
  • +
  • + +
    ellipsis
    +
    .icon-ellipsis
    +
  • +
  • + +
    zoom out
    +
    .icon-zoomout
    +
  • +
  • + +
    copyright
    +
    .icon-copyright
    +
  • +
  • + +
    dash
    +
    .icon-dash
    +
  • +
  • + +
    apartment
    +
    .icon-apartment
    +
  • +
  • + +
    minus-circle
    +
    .icon-minus-circle
    +
  • +
  • + +
    close
    +
    .icon-close
    +
  • +
  • + +
    audio
    +
    .icon-audio
    +
  • +
  • + +
    meh
    +
    .icon-meh
    +
  • +
  • + +
    enter
    +
    .icon-enter
    +
  • +
  • + +
    audio-fill
    +
    .icon-audio-fill
    +
  • +
  • + +
    plus-circle
    +
    .icon-plus-circle
    +
  • +
  • + +
    line
    +
    .icon-line
    +
  • +
  • + +
    robot
    +
    .icon-robot
    +
  • +
  • + +
    play-circle
    +
    .icon-play-circle
    +
  • +
  • + +
    minus
    +
    .icon-minus
    +
  • +
  • + +
    zoom in
    +
    .icon-zoomin
    +
  • +
  • + +
    question-circle
    +
    .icon-question-circle
    +
  • +
  • + +
    question
    +
    .icon-question
    +
  • +
  • + +
    robot-fill
    +
    .icon-robot-fill
    +
  • +
  • + +
    Pound
    +
    .icon-Pound
    +
  • +
  • + +
    rollback
    +
    .icon-rollback
    +
  • +
  • + +
    bug-fill
    +
    .icon-bug-fill
    +
  • +
  • + +
    right-circle
    +
    .icon-right-circle
    +
  • +
  • + +
    small-dash
    +
    .icon-small-dash
    +
  • +
  • + +
    bug
    +
    .icon-bug
    +
  • +
  • + +
    smile
    +
    .icon-smile
    +
  • +
  • + +
    pause
    +
    .icon-pause
    +
  • +
  • + +
    audio static
    +
    .icon-audiostatic
    +
  • +
  • + +
    trademark
    +
    .icon-trademark
    +
  • +
  • + +
    bg-colors
    +
    .icon-bg-colors
    +
  • +
  • + +
    comment
    +
    .icon-comment
    +
  • +
  • + +
    time-circle
    +
    .icon-time-circle
    +
  • +
  • + +
    crown
    +
    .icon-crown
    +
  • +
  • + +
    signal-fill
    +
    .icon-signal-fill
    +
  • +
  • + +
    time out
    +
    .icon-timeout
    +
  • +
  • + +
    drag
    +
    .icon-drag
    +
  • +
  • + +
    verified
    +
    .icon-verified
    +
  • +
  • + +
    earth
    +
    .icon-earth
    +
  • +
  • + +
    desktop
    +
    .icon-desktop
    +
  • +
  • + +
    shortcut-fill
    +
    .icon-shortcut-fill
    +
  • +
  • + +
    YUAN
    +
    .icon-YUAN
    +
  • +
  • + +
    gift
    +
    .icon-gift
    +
  • +
  • + +
    videocamera add
    +
    .icon-videocameraadd
    +
  • +
  • + +
    up-circle
    +
    .icon-up-circle
    +
  • +
  • + +
    stop
    +
    .icon-stop
    +
  • +
  • + +
    switch user
    +
    .icon-switchuser
    +
  • +
  • + +
    warning-circle
    +
    .icon-warning-circle
    +
  • +
  • + +
    fire
    +
    .icon-fire
    +
  • +
  • + +
    whatsapp
    +
    .icon-whatsapp
    +
  • +
  • + +
    sync
    +
    .icon-sync
    +
  • +
  • + +
    thunderbolt
    +
    .icon-thunderbolt
    +
  • +
  • + +
    appstore add
    +
    .icon-appstoreadd
    +
  • +
  • + +
    transaction
    +
    .icon-transaction
    +
  • +
  • + +
    check-circle-fill
    +
    .icon-check-circle-fill
    +
  • +
  • + +
    caret-down
    +
    .icon-caret-down
    +
  • +
  • + +
    undo
    +
    .icon-undo
    +
  • +
  • + +
    left-circle-fill
    +
    .icon-left-circle-fill
    +
  • +
  • + +
    backward
    +
    .icon-backward
    +
  • +
  • + +
    redo
    +
    .icon-redo
    +
  • +
  • + +
    down-circle-fill
    +
    .icon-down-circle-fill
    +
  • +
  • + +
    caret-up
    +
    .icon-caret-up
    +
  • +
  • + +
    reload
    +
    .icon-reload
    +
  • +
  • + +
    minus-circle-fill
    +
    .icon-minus-circle-fill
    +
  • +
  • + +
    caret-right
    +
    .icon-caret-right
    +
  • +
  • + +
    reload time
    +
    .icon-reloadtime
    +
  • +
  • + +
    close-circle-fill
    +
    .icon-close-circle-fill
    +
  • +
  • + +
    caret-left
    +
    .icon-caret-left
    +
  • +
  • + +
    message
    +
    .icon-message
    +
  • +
  • + +
    info-circle-fill
    +
    .icon-info-circle-fill
    +
  • +
  • + +
    fast-backward
    +
    .icon-fast-backward
    +
  • +
  • + +
    dashboard
    +
    .icon-dashboard
    +
  • +
  • + +
    up-circle-fill
    +
    .icon-up-circle-fill
    +
  • +
  • + +
    forward
    +
    .icon-forward
    +
  • +
  • + +
    issues close
    +
    .icon-issuesclose
    +
  • +
  • + +
    right-circle-fill
    +
    .icon-right-circle-fill
    +
  • +
  • + +
    fast-forward
    +
    .icon-fast-forward
    +
  • +
  • + +
    poweroff
    +
    .icon-poweroff
    +
  • +
  • + +
    plus-circle-fill
    +
    .icon-plus-circle-fill
    +
  • +
  • + +
    search
    +
    .icon-search
    +
  • +
  • + +
    logout
    +
    .icon-logout
    +
  • +
  • + +
    question-circle-fill
    +
    .icon-question-circle-fill
    +
  • +
  • + +
    retweet
    +
    .icon-retweet
    +
  • +
  • + +
    pie chart
    +
    .icon-piechart
    +
  • +
  • + +
    EURO-circle-fill
    +
    .icon-EURO-circle-fill
    +
  • +
  • + +
    login
    +
    .icon-login
    +
  • +
  • + +
    setting
    +
    .icon-setting
    +
  • +
  • + +
    frown-fill
    +
    .icon-frown-fill
    +
  • +
  • + +
    step-backward
    +
    .icon-step-backward
    +
  • +
  • + +
    eye
    +
    .icon-eye
    +
  • +
  • + +
    copyright-circle-fil
    +
    .icon-copyright-circle-fil
    +
  • +
  • + +
    step-forward
    +
    .icon-step-forward
    +
  • +
  • + +
    location
    +
    .icon-location
    +
  • +
  • + +
    CI-circle-fill
    +
    .icon-CI-circle-fill
    +
  • +
  • + +
    swap-right
    +
    .icon-swap-right
    +
  • +
  • + +
    edit-square
    +
    .icon-edit-square
    +
  • +
  • + +
    compass-fill
    +
    .icon-compass-fill
    +
  • +
  • + +
    swap-left
    +
    .icon-swap-left
    +
  • +
  • + +
    export
    +
    .icon-export
    +
  • +
  • + +
    Dollar-circle-fill
    +
    .icon-Dollar-circle-fill
    +
  • +
  • + +
    woman
    +
    .icon-woman
    +
  • +
  • + +
    save
    +
    .icon-save
    +
  • +
  • + +
    poweroff-circle-fill
    +
    .icon-poweroff-circle-fill
    +
  • +
  • + +
    plus
    +
    .icon-plus
    +
  • +
  • + +
    Import
    +
    .icon-Import
    +
  • +
  • + +
    meh-fill
    +
    .icon-meh-fill
    +
  • +
  • + +
    eye close-fill
    +
    .icon-eyeclose-fill
    +
  • +
  • + +
    app store
    +
    .icon-appstore
    +
  • +
  • + +
    play-circle-fill
    +
    .icon-play-circle-fill
    +
  • +
  • + +
    eye-close
    +
    .icon-eye-close
    +
  • +
  • + +
    close-square
    +
    .icon-close-square
    +
  • +
  • + +
    Pound-circle-fill
    +
    .icon-Pound-circle-fill
    +
  • +
  • + +
    clear
    +
    .icon-clear
    +
  • +
  • + +
    down-square
    +
    .icon-down-square
    +
  • +
  • + +
    smile-fill
    +
    .icon-smile-fill
    +
  • +
  • + +
    collapse
    +
    .icon-collapse
    +
  • +
  • + +
    layout
    +
    .icon-layout
    +
  • +
  • + +
    stop-fill
    +
    .icon-stop-fill
    +
  • +
  • + +
    expand
    +
    .icon-expand
    +
  • +
  • + +
    left-square
    +
    .icon-left-square
    +
  • +
  • + +
    warning-circle-fill
    +
    .icon-warning-circle-fill
    +
  • +
  • + +
    delete column
    +
    .icon-deletecolumn
    +
  • +
  • + +
    play-square
    +
    .icon-play-square
    +
  • +
  • + +
    time-circle-fill
    +
    .icon-time-circle-fill
    +
  • +
  • + +
    merge-cells
    +
    .icon-merge-cells
    +
  • +
  • + +
    control
    +
    .icon-control
    +
  • +
  • + +
    trademark-circle-fil
    +
    .icon-trademark-circle-fil
    +
  • +
  • + +
    subnode
    +
    .icon-subnode
    +
  • +
  • + +
    code library
    +
    .icon-codelibrary
    +
  • +
  • + +
    YUAN-circle-fill
    +
    .icon-YUAN-circle-fill
    +
  • +
  • + +
    rotate-left
    +
    .icon-rotate-left
    +
  • +
  • + +
    detail
    +
    .icon-detail
    +
  • +
  • + +
    heart-fill
    +
    .icon-heart-fill
    +
  • +
  • + +
    rotate-right
    +
    .icon-rotate-right
    +
  • +
  • + +
    minus-square
    +
    .icon-minus-square
    +
  • +
  • + +
    pie chart-circle-fil
    +
    .icon-piechart-circle-fil
    +
  • +
  • + +
    insert row below
    +
    .icon-insertrowbelow
    +
  • +
  • + +
    plus-square
    +
    .icon-plus-square
    +
  • +
  • + +
    dashboard-fill
    +
    .icon-dashboard-fill
    +
  • +
  • + +
    insert row above
    +
    .icon-insertrowabove
    +
  • +
  • + +
    right-square
    +
    .icon-right-square
    +
  • +
  • + +
    message-fill
    +
    .icon-message-fill
    +
  • +
  • + +
    table
    +
    .icon-table1
    +
  • +
  • + +
    project
    +
    .icon-project
    +
  • +
  • + +
    check-square-fill
    +
    .icon-check-square-fill
    +
  • +
  • + +
    solit-cells
    +
    .icon-solit-cells
    +
  • +
  • + +
    wallet
    +
    .icon-wallet
    +
  • +
  • + +
    down-square-fill
    +
    .icon-down-square-fill
    +
  • +
  • + +
    format painter
    +
    .icon-formatpainter
    +
  • +
  • + +
    up-square
    +
    .icon-up-square
    +
  • +
  • + +
    minus-square-fill
    +
    .icon-minus-square-fill
    +
  • +
  • + +
    insert row right
    +
    .icon-insertrowright
    +
  • +
  • + +
    calculator
    +
    .icon-calculator
    +
  • +
  • + +
    close-square-fill
    +
    .icon-close-square-fill
    +
  • +
  • + +
    format painter-fill
    +
    .icon-formatpainter-fill
    +
  • +
  • + +
    interation
    +
    .icon-interation
    +
  • +
  • + +
    code library-fill
    +
    .icon-codelibrary-fill
    +
  • +
  • + +
    insert row left
    +
    .icon-insertrowleft
    +
  • +
  • + +
    check-square
    +
    .icon-check-square
    +
  • +
  • + +
    left-square-fill
    +
    .icon-left-square-fill
    +
  • +
  • + +
    translate
    +
    .icon-translate
    +
  • +
  • + +
    border
    +
    .icon-border
    +
  • +
  • + +
    play-square-fill
    +
    .icon-play-square-fill
    +
  • +
  • + +
    delete row
    +
    .icon-deleterow
    +
  • +
  • + +
    border-outer
    +
    .icon-border-outer
    +
  • +
  • + +
    up-square-fill
    +
    .icon-up-square-fill
    +
  • +
  • + +
    sisternode
    +
    .icon-sisternode
    +
  • +
  • + +
    border-top
    +
    .icon-border-top
    +
  • +
  • + +
    right-square-fill
    +
    .icon-right-square-fill
    +
  • +
  • + +
    google-fill
    +
    .icon-google-fill
    +
  • +
  • + +
    border-bottom
    +
    .icon-border-bottom
    +
  • +
  • + +
    plus-square-fill
    +
    .icon-plus-square-fill
    +
  • +
  • + +
    Field-number
    +
    .icon-Field-number
    +
  • +
  • + +
    border-left
    +
    .icon-border-left
    +
  • +
  • + +
    account book-fill
    +
    .icon-accountbook-fill
    +
  • +
  • + +
    Field-String
    +
    .icon-Field-String
    +
  • +
  • + +
    border-right
    +
    .icon-border-right
    +
  • +
  • + +
    carry out-fill
    +
    .icon-carryout-fill
    +
  • +
  • + +
    Function
    +
    .icon-Function
    +
  • +
  • + +
    border-inner
    +
    .icon-border-inner
    +
  • +
  • + +
    calendar-fill
    +
    .icon-calendar-fill
    +
  • +
  • + +
    Field-time
    +
    .icon-Field-time
    +
  • +
  • + +
    border-verticle
    +
    .icon-border-verticle
    +
  • +
  • + +
    calculator-fill
    +
    .icon-calculator-fill
    +
  • +
  • + +
    GIF
    +
    .icon-GIF
    +
  • +
  • + +
    border-horizontal
    +
    .icon-border-horizontal
    +
  • +
  • + +
    interation-fill
    +
    .icon-interation-fill
    +
  • +
  • + +
    Partition
    +
    .icon-Partition
    +
  • +
  • + +
    radius-bottomleft
    +
    .icon-radius-bottomleft
    +
  • +
  • + +
    project-fill
    +
    .icon-project-fill
    +
  • +
  • + +
    index
    +
    .icon-index
    +
  • +
  • + +
    radius-bottomright
    +
    .icon-radius-bottomright
    +
  • +
  • + +
    detail-fill
    +
    .icon-detail-fill
    +
  • +
  • + +
    Stored procedure
    +
    .icon-Storedprocedure
    +
  • +
  • + +
    radius-upleft
    +
    .icon-radius-upleft
    +
  • +
  • + +
    save-fill
    +
    .icon-save-fill
    +
  • +
  • + +
    Field-Binary
    +
    .icon-Field-Binary
    +
  • +
  • + +
    radius-upright
    +
    .icon-radius-upright
    +
  • +
  • + +
    wallet-fill
    +
    .icon-wallet-fill
    +
  • +
  • + +
    Console-SQL
    +
    .icon-Console-SQL
    +
  • +
  • + +
    radius-setting
    +
    .icon-radius-setting
    +
  • +
  • + +
    control-fill
    +
    .icon-control-fill
    +
  • +
  • + +
    1:1
    +
    .icon-icon-test
    +
  • +
  • + +
    add user
    +
    .icon-adduser
    +
  • +
  • + +
    layout-fill
    +
    .icon-layout-fill
    +
  • +
  • + +
    aim
    +
    .icon-aim
    +
  • +
  • + +
    delete team
    +
    .icon-deleteteam
    +
  • +
  • + +
    app store-fill
    +
    .icon-appstore-fill
    +
  • +
  • + +
    compress
    +
    .icon-compress
    +
  • +
  • + +
    delete user
    +
    .icon-deleteuser
    +
  • +
  • + +
    mobile-fill
    +
    .icon-mobile-fill
    +
  • +
  • + +
    expend
    +
    .icon-expend
    +
  • +
  • + +
    addteam
    +
    .icon-addteam
    +
  • +
  • + +
    tablet-fill
    +
    .icon-tablet-fill
    +
  • +
  • + +
    folder-view
    +
    .icon-folder-view
    +
  • +
  • + +
    user
    +
    .icon-user
    +
  • +
  • + +
    book-fill
    +
    .icon-book-fill
    +
  • +
  • + +
    file-GIF
    +
    .icon-file-GIF
    +
  • +
  • + +
    team
    +
    .icon-team
    +
  • +
  • + +
    red envelope-fill
    +
    .icon-redenvelope-fill
    +
  • +
  • + +
    group
    +
    .icon-group
    +
  • +
  • + +
    area chart
    +
    .icon-areachart
    +
  • +
  • + +
    safety certificate-f
    +
    .icon-safetycertificate-f
    +
  • +
  • + +
    send
    +
    .icon-send
    +
  • +
  • + +
    line chart
    +
    .icon-linechart
    +
  • +
  • + +
    property safety-fill
    +
    .icon-propertysafety-fill
    +
  • +
  • + +
    Report
    +
    .icon-Report
    +
  • +
  • + +
    bar chart
    +
    .icon-barchart
    +
  • +
  • + +
    insurance-fill
    +
    .icon-insurance-fill
    +
  • +
  • + +
    View
    +
    .icon-View
    +
  • +
  • + +
    point map
    +
    .icon-pointmap
    +
  • +
  • + +
    security scan-fill
    +
    .icon-securityscan-fill
    +
  • +
  • + +
    shortcut
    +
    .icon-shortcut
    +
  • +
  • + +
    container
    +
    .icon-container
    +
  • +
  • + +
    file-exclamation-fil
    +
    .icon-file-exclamation-fil
    +
  • +
  • + +
    ungroup
    +
    .icon-ungroup
    +
  • +
  • + +
    database
    +
    .icon-database
    +
  • +
  • + +
    file-add-fill
    +
    .icon-file-add-fill
    +
  • +
  • + +
    sever
    +
    .icon-sever
    +
  • +
  • + +
    file-fill
    +
    .icon-file-fill
    +
  • +
  • + +
    mobile
    +
    .icon-mobile
    +
  • +
  • + +
    file-excel-fill
    +
    .icon-file-excel-fill
    +
  • +
  • + +
    tablet
    +
    .icon-tablet
    +
  • +
  • + +
    file-markdown-fill
    +
    .icon-file-markdown-fill
    +
  • +
  • + +
    red envelope
    +
    .icon-redenvelope
    +
  • +
  • + +
    file-text-fill
    +
    .icon-file-text-fill
    +
  • +
  • + +
    book
    +
    .icon-book
    +
  • +
  • + +
    file-ppt-fill
    +
    .icon-file-ppt-fill
    +
  • +
  • + +
    file done
    +
    .icon-filedone
    +
  • +
  • + +
    file-unknown-fill
    +
    .icon-file-unknown-fill
    +
  • +
  • + +
    reconciliation
    +
    .icon-reconciliation
    +
  • +
  • + +
    file-word-fill
    +
    .icon-file-word-fill
    +
  • +
  • + +
    file -exception
    +
    .icon-file-exception
    +
  • +
  • + +
    file-zip-fill
    +
    .icon-file-zip-fill
    +
  • +
  • + +
    file sync
    +
    .icon-filesync
    +
  • +
  • + +
    file-pdf-fill
    +
    .icon-file-pdf-fill
    +
  • +
  • + +
    file search
    +
    .icon-filesearch
    +
  • +
  • + +
    file-image-fill
    +
    .icon-file-image-fill
    +
  • +
  • + +
    solution
    +
    .icon-solution
    +
  • +
  • + +
    diff-fill
    +
    .icon-diff-fill
    +
  • +
  • + +
    file protect
    +
    .icon-fileprotect
    +
  • +
  • + +
    file-copy-fill
    +
    .icon-file-copy-fill
    +
  • +
  • + +
    file-add
    +
    .icon-file-add
    +
  • +
  • + +
    snippets-fill
    +
    .icon-snippets-fill
    +
  • +
  • + +
    file-excel
    +
    .icon-file-excel
    +
  • +
  • + +
    batch folding-fill
    +
    .icon-batchfolding-fill
    +
  • +
  • + +
    file-exclamation
    +
    .icon-file-exclamation
    +
  • +
  • + +
    reconciliation-fill
    +
    .icon-reconciliation-fill
    +
  • +
  • + +
    file-pdf
    +
    .icon-file-pdf
    +
  • +
  • + +
    folder-add-fill
    +
    .icon-folder-add-fill
    +
  • +
  • + +
    file-image
    +
    .icon-file-image
    +
  • +
  • + +
    folder-fill
    +
    .icon-folder-fill
    +
  • +
  • + +
    file-markdown
    +
    .icon-file-markdown
    +
  • +
  • + +
    folder-open-fill
    +
    .icon-folder-open-fill
    +
  • +
  • + +
    file-unknown
    +
    .icon-file-unknown
    +
  • +
  • + +
    database-fill
    +
    .icon-database-fill
    +
  • +
  • + +
    file-ppt
    +
    .icon-file-ppt
    +
  • +
  • + +
    container-fill
    +
    .icon-container-fill
    +
  • +
  • + +
    file-word
    +
    .icon-file-word
    +
  • +
  • + +
    sever-fill
    +
    .icon-sever-fill
    +
  • +
  • + +
    file
    +
    .icon-file
    +
  • +
  • + +
    calendar-check-fill
    +
    .icon-calendar-check-fill
    +
  • +
  • + +
    file-zip
    +
    .icon-file-zip
    +
  • +
  • + +
    image-fill
    +
    .icon-image-fill
    +
  • +
  • + +
    file-text
    +
    .icon-file-text
    +
  • +
  • + +
    id card-fill
    +
    .icon-idcard-fill
    +
  • +
  • + +
    file-copy
    +
    .icon-file-copy
    +
  • +
  • + +
    credit card-fill
    +
    .icon-creditcard-fill
    +
  • +
  • + +
    snippets
    +
    .icon-snippets
    +
  • +
  • + +
    fund-fill
    +
    .icon-fund-fill
    +
  • +
  • + +
    audit
    +
    .icon-audit
    +
  • +
  • + +
    read-fill
    +
    .icon-read-fill
    +
  • +
  • + +
    diff
    +
    .icon-diff
    +
  • +
  • + +
    contacts-fill
    +
    .icon-contacts-fill
    +
  • +
  • + +
    Batch folding
    +
    .icon-Batchfolding
    +
  • +
  • + +
    delete-fill
    +
    .icon-delete-fill
    +
  • +
  • + +
    security scan
    +
    .icon-securityscan
    +
  • +
  • + +
    notification-fill
    +
    .icon-notification-fill
    +
  • +
  • + +
    property safety
    +
    .icon-propertysafety
    +
  • +
  • + +
    flag-fill
    +
    .icon-flag-fill
    +
  • +
  • + +
    safety certificate
    +
    .icon-safetycertificate
    +
  • +
  • + +
    money collect-fill
    +
    .icon-moneycollect-fill
    +
  • +
  • + +
    insurance
    +
    .icon-insurance
    +
  • +
  • + +
    medicine box-fill
    +
    .icon-medicinebox-fill
    +
  • +
  • + +
    alert
    +
    .icon-alert
    +
  • +
  • + +
    rest-fill
    +
    .icon-rest-fill
    +
  • +
  • + +
    delete
    +
    .icon-delete
    +
  • +
  • + +
    shopping-fill
    +
    .icon-shopping-fill
    +
  • +
  • + +
    hourglass
    +
    .icon-hourglass
    +
  • +
  • + +
    skin-fill
    +
    .icon-skin-fill
    +
  • +
  • + +
    bulb
    +
    .icon-bulb
    +
  • +
  • + +
    video-fill
    +
    .icon-video-fill
    +
  • +
  • + +
    experiment
    +
    .icon-experiment
    +
  • +
  • + +
    sound-fill
    +
    .icon-sound-fill
    +
  • +
  • + +
    bell
    +
    .icon-bell
    +
  • +
  • + +
    bulb-fill
    +
    .icon-bulb-fill
    +
  • +
  • + +
    trophy
    +
    .icon-trophy
    +
  • +
  • + +
    bell-fill
    +
    .icon-bell-fill
    +
  • +
  • + +
    rest
    +
    .icon-rest
    +
  • +
  • + +
    filter-fill
    +
    .icon-filter-fill
    +
  • +
  • + +
    USB
    +
    .icon-USB
    +
  • +
  • + +
    fire-fill
    +
    .icon-fire-fill
    +
  • +
  • + +
    skin
    +
    .icon-skin
    +
  • +
  • + +
    funnel plot-fill
    +
    .icon-funnelplot-fill
    +
  • +
  • + +
    home
    +
    .icon-home
    +
  • +
  • + +
    gift-fill
    +
    .icon-gift-fill
    +
  • +
  • + +
    bank
    +
    .icon-bank
    +
  • +
  • + +
    hourglass-fill
    +
    .icon-hourglass-fill
    +
  • +
  • + +
    filter
    +
    .icon-filter
    +
  • +
  • + +
    home-fill
    +
    .icon-home-fill
    +
  • +
  • + +
    funnel plot
    +
    .icon-funnelplot
    +
  • +
  • + +
    trophy-fill
    +
    .icon-trophy-fill
    +
  • +
  • + +
    like
    +
    .icon-like
    +
  • +
  • + +
    location-fill
    +
    .icon-location-fill
    +
  • +
  • + +
    unlike
    +
    .icon-unlike
    +
  • +
  • + +
    cloud-fill
    +
    .icon-cloud-fill
    +
  • +
  • + +
    unlock
    +
    .icon-unlock
    +
  • +
  • + +
    customerservice-fill
    +
    .icon-customerservice-fill
    +
  • +
  • + +
    lock
    +
    .icon-lock
    +
  • +
  • + +
    experiment-fill
    +
    .icon-experiment-fill
    +
  • +
  • + +
    customerservice
    +
    .icon-customerservice
    +
  • +
  • + +
    eye-fill
    +
    .icon-eye-fill
    +
  • +
  • + +
    flag
    +
    .icon-flag
    +
  • +
  • + +
    like-fill
    +
    .icon-like-fill
    +
  • +
  • + +
    money collect
    +
    .icon-moneycollect
    +
  • +
  • + +
    lock-fill
    +
    .icon-lock-fill
    +
  • +
  • + +
    medicinebox
    +
    .icon-medicinebox
    +
  • +
  • + +
    unlike-fill
    +
    .icon-unlike-fill
    +
  • +
  • + +
    shop
    +
    .icon-shop
    +
  • +
  • + +
    star-fill
    +
    .icon-star-fill
    +
  • +
  • + +
    rocket
    +
    .icon-rocket
    +
  • +
  • + +
    unlock-fill
    +
    .icon-unlock-fill
    +
  • +
  • + +
    shopping
    +
    .icon-shopping
    +
  • +
  • + +
    alert-fill
    +
    .icon-alert-fill
    +
  • +
  • + +
    folder
    +
    .icon-folder
    +
  • +
  • + +
    api-fill
    +
    .icon-api-fill
    +
  • +
  • + +
    folder-open
    +
    .icon-folder-open
    +
  • +
  • + +
    highlight-fill
    +
    .icon-highlight-fill
    +
  • +
  • + +
    folder-add
    +
    .icon-folder-add
    +
  • +
  • + +
    phone-fill
    +
    .icon-phone-fill
    +
  • +
  • + +
    deployment unit
    +
    .icon-deploymentunit
    +
  • +
  • + +
    edit-fill
    +
    .icon-edit-fill
    +
  • +
  • + +
    account book
    +
    .icon-accountbook
    +
  • +
  • + +
    pushpin-fill
    +
    .icon-pushpin-fill
    +
  • +
  • + +
    contacts
    +
    .icon-contacts
    +
  • +
  • + +
    rocket-fill
    +
    .icon-rocket-fill
    +
  • +
  • + +
    carry out
    +
    .icon-carryout
    +
  • +
  • + +
    thunderbolt-fill
    +
    .icon-thunderbolt-fill
    +
  • +
  • + +
    calendar-check
    +
    .icon-calendar-check
    +
  • +
  • + +
    tag-fill
    +
    .icon-tag-fill
    +
  • +
  • + +
    calendar
    +
    .icon-calendar
    +
  • +
  • + +
    wrench-fill
    +
    .icon-wrench-fill
    +
  • +
  • + +
    scan
    +
    .icon-scan
    +
  • +
  • + +
    tags-fill
    +
    .icon-tags-fill
    +
  • +
  • + +
    select
    +
    .icon-select
    +
  • +
  • + +
    bank-fill
    +
    .icon-bank-fill
    +
  • +
  • + +
    box plot
    +
    .icon-boxplot
    +
  • +
  • + +
    camera-fill
    +
    .icon-camera-fill
    +
  • +
  • + +
    build
    +
    .icon-build
    +
  • +
  • + +
    error-fill
    +
    .icon-error-fill
    +
  • +
  • + +
    sliders
    +
    .icon-sliders
    +
  • +
  • + +
    crown-fill
    +
    .icon-crown-fill
    +
  • +
  • + +
    laptop
    +
    .icon-laptop
    +
  • +
  • + +
    mail-fill
    +
    .icon-mail-fill
    +
  • +
  • + +
    barcode
    +
    .icon-barcode
    +
  • +
  • + +
    car-fill
    +
    .icon-car-fill
    +
  • +
  • + +
    camera
    +
    .icon-camera
    +
  • +
  • + +
    printer-fill
    +
    .icon-printer-fill
    +
  • +
  • + +
    cluster
    +
    .icon-cluster
    +
  • +
  • + +
    shop-fill
    +
    .icon-shop-fill
    +
  • +
  • + +
    gateway
    +
    .icon-gateway
    +
  • +
  • + +
    setting-fill
    +
    .icon-setting-fill
    +
  • +
  • + +
    car
    +
    .icon-car
    +
  • +
  • + +
    USB-fill
    +
    .icon-USB-fill
    +
  • +
  • + +
    printer
    +
    .icon-printer
    +
  • +
  • + +
    golden-fill
    +
    .icon-golden-fill
    +
  • +
  • + +
    read
    +
    .icon-read
    +
  • +
  • + +
    build-fill
    +
    .icon-build-fill
    +
  • +
  • + +
    cloud-server
    +
    .icon-cloud-server
    +
  • +
  • + +
    box plot-fill
    +
    .icon-boxplot-fill
    +
  • +
  • + +
    cloud-upload
    +
    .icon-cloud-upload
    +
  • +
  • + +
    sliders-fill
    +
    .icon-sliders-fill
    +
  • +
  • + +
    cloud
    +
    .icon-cloud
    +
  • +
  • + +
    alibaba
    +
    .icon-alibaba
    +
  • +
  • + +
    cloud-download
    +
    .icon-cloud-download
    +
  • +
  • + +
    alibabacloud
    +
    .icon-alibabacloud
    +
  • +
  • + +
    cloud-sync
    +
    .icon-cloud-sync
    +
  • +
  • + +
    ant design
    +
    .icon-antdesign
    +
  • +
  • + +
    video
    +
    .icon-video
    +
  • +
  • + +
    ant-cloud
    +
    .icon-ant-cloud
    +
  • +
  • + +
    notification
    +
    .icon-notification
    +
  • +
  • + +
    behance
    +
    .icon-behance
    +
  • +
  • + +
    sound
    +
    .icon-sound
    +
  • +
  • + +
    google plus
    +
    .icon-googleplus
    +
  • +
  • + +
    radar chart
    +
    .icon-radarchart
    +
  • +
  • + +
    medium
    +
    .icon-medium
    +
  • +
  • + +
    qrcode
    +
    .icon-qrcode
    +
  • +
  • + +
    google
    +
    .icon-google
    +
  • +
  • + +
    fund
    +
    .icon-fund
    +
  • +
  • + +
    IE
    +
    .icon-IE
    +
  • +
  • + +
    image
    +
    .icon-image
    +
  • +
  • + +
    amazon
    +
    .icon-amazon
    +
  • +
  • + +
    mail
    +
    .icon-mail
    +
  • +
  • + +
    slack
    +
    .icon-slack
    +
  • +
  • + +
    table
    +
    .icon-table
    +
  • +
  • + +
    alipay
    +
    .icon-alipay
    +
  • +
  • + +
    id card
    +
    .icon-idcard
    +
  • +
  • + +
    taobao
    +
    .icon-taobao
    +
  • +
  • + +
    credit card
    +
    .icon-creditcard
    +
  • +
  • + +
    zhihu
    +
    .icon-zhihu
    +
  • +
  • + +
    heart
    +
    .icon-heart
    +
  • +
  • + +
    HTML5
    +
    .icon-HTML
    +
  • +
  • + +
    block
    +
    .icon-block
    +
  • +
  • + +
    linkedin
    +
    .icon-linkedin
    +
  • +
  • + +
    error
    +
    .icon-error
    +
  • +
  • + +
    yahoo
    +
    .icon-yahoo
    +
  • +
  • + +
    star
    +
    .icon-star
    +
  • +
  • + +
    facebook
    +
    .icon-facebook
    +
  • +
  • + +
    gold
    +
    .icon-gold
    +
  • +
  • + +
    skype
    +
    .icon-skype
    +
  • +
  • + +
    heat map
    +
    .icon-heatmap
    +
  • +
  • + +
    CodeSandbox
    +
    .icon-CodeSandbox
    +
  • +
  • + +
    wifi
    +
    .icon-wifi
    +
  • +
  • + +
    chrome
    +
    .icon-chrome
    +
  • +
  • + +
    attachment
    +
    .icon-attachment
    +
  • +
  • + +
    codepen
    +
    .icon-codepen
    +
  • +
  • + +
    edit
    +
    .icon-edit
    +
  • +
  • + +
    aliwangwang
    +
    .icon-aliwangwang
    +
  • +
  • + +
    key
    +
    .icon-key
    +
  • +
  • + +
    apple
    +
    .icon-apple
    +
  • +
  • + +
    api
    +
    .icon-api
    +
  • +
  • + +
    android
    +
    .icon-android
    +
  • +
  • + +
    disconnect
    +
    .icon-disconnect
    +
  • +
  • + +
    sketch
    +
    .icon-sketch
    +
  • +
  • + +
    highlight
    +
    .icon-highlight
    +
  • +
  • + +
    Gitlab
    +
    .icon-Gitlab
    +
  • +
  • + +
    monitor
    +
    .icon-monitor
    +
  • +
  • + +
    dribbble
    +
    .icon-dribbble
    +
  • +
  • + +
    link
    +
    .icon-link
    +
  • +
  • + +
    instagram
    +
    .icon-instagram
    +
  • +
  • + +
    man
    +
    .icon-man
    +
  • +
  • + +
    reddit
    +
    .icon-reddit
    +
  • +
  • + +
    percentage
    +
    .icon-percentage
    +
  • +
  • + +
    windows
    +
    .icon-windows
    +
  • +
  • + +
    pushpin
    +
    .icon-pushpin
    +
  • +
  • + +
    yuque
    +
    .icon-yuque
    +
  • +
  • + +
    phone
    +
    .icon-phone
    +
  • +
  • + +
    Youtube
    +
    .icon-Youtube
    +
  • +
  • + +
    shake
    +
    .icon-shake
    +
  • +
  • + +
    Gitlab-fill
    +
    .icon-Gitlab-fill
    +
  • +
  • + +
    tag
    +
    .icon-tag
    +
  • +
  • + +
    dropbox
    +
    .icon-dropbox
    +
  • +
  • + +
    wrench
    +
    .icon-wrench
    +
  • +
  • + +
    dingtalk
    +
    .icon-dingtalk
    +
  • +
  • + +
    tags
    +
    .icon-tags
    +
  • +
  • + +
    android-fill
    +
    .icon-android-fill
    +
  • +
  • + +
    scissor
    +
    .icon-scissor
    +
  • +
  • + +
    apple-fill
    +
    .icon-apple-fill
    +
  • +
  • + +
    mr
    +
    .icon-mr
    +
  • +
  • + +
    HTML5-fill
    +
    .icon-HTML-fill
    +
  • +
  • + +
    share
    +
    .icon-share
    +
  • +
  • + +
    windows-fill
    +
    .icon-windows-fill
    +
  • +
  • + +
    branches
    +
    .icon-branches
    +
  • +
  • + +
    QQ
    +
    .icon-QQ
    +
  • +
  • + +
    fork
    +
    .icon-fork
    +
  • +
  • + +
    twitter
    +
    .icon-twitter
    +
  • +
  • + +
    shrink
    +
    .icon-shrink
    +
  • +
  • + +
    skype-fill
    +
    .icon-skype-fill
    +
  • +
  • + +
    arrawsalt
    +
    .icon-arrawsalt
    +
  • +
  • + +
    weibo
    +
    .icon-weibo
    +
  • +
  • + +
    vertical right
    +
    .icon-verticalright
    +
  • +
  • + +
    yuque-fill
    +
    .icon-yuque-fill
    +
  • +
  • + +
    vertical left
    +
    .icon-verticalleft
    +
  • +
  • + +
    Youtube-fill
    +
    .icon-Youtube-fill
    +
  • +
  • + +
    right
    +
    .icon-right
    +
  • +
  • + +
    yahoo-fill
    +
    .icon-yahoo-fill
    +
  • +
  • + +
    left
    +
    .icon-left
    +
  • +
  • + +
    wechat-fill
    +
    .icon-wechat
    +
  • +
  • + +
    up
    +
    .icon-up
    +
  • +
  • + +
    chrome-fill
    +
    .icon-chrome-fill
    +
  • +
  • + +
    down
    +
    .icon-down
    +
  • +
  • + +
    alipay-circle-fill
    +
    .icon-alipay-circle-fill
    +
  • +
  • + +
    fullscreen
    +
    .icon-fullscreen
    +
  • +
  • + +
    aliwangwang-fill
    +
    .icon-aliwangwang-fill
    +
  • +
  • + +
    fullscreen-exit
    +
    .icon-fullscreen-exit
    +
  • +
  • + +
    behance-circle-fill
    +
    .icon-behance-circle-fill
    +
  • +
  • + +
    doubleleft
    +
    .icon-doubleleft
    +
  • +
  • + +
    amazon-circle-fill
    +
    .icon-amazon-circle-fill
    +
  • +
  • + +
    double right
    +
    .icon-doubleright
    +
  • +
  • + +
    codepen-circle-fill
    +
    .icon-codepen-circle-fill
    +
  • +
  • + +
    arrowright
    +
    .icon-arrowright
    +
  • +
  • + +
    CodeSandbox-circle-f
    +
    .icon-CodeSandbox-circle-f
    +
  • +
  • + +
    arrowup
    +
    .icon-arrowup
    +
  • +
  • + +
    dropbox-circle-fill
    +
    .icon-dropbox-circle-fill
    +
  • +
  • + +
    arrowleft
    +
    .icon-arrowleft
    +
  • +
  • + +
    github-fill
    +
    .icon-github-fill
    +
  • +
+
+
+
+ + + diff --git a/src/assets/styles/toc.css b/src/assets/styles/toc.css new file mode 100644 index 0000000..a8a0caa --- /dev/null +++ b/src/assets/styles/toc.css @@ -0,0 +1,271 @@ +/* Table of Contents ----------------------------------- */ + +.post-menu { + position: fixed; + display: table; + top: 0; + right: -18.125rem; + bottom: 0; + width: 17.5rem; + height: 100%; + background-color: #fafafa; + border-left: 0.0625rem solid #f0f0f0; + opacity: 1; + z-index: 880; + font-weight: 400; + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; +} + +.display-menu-tree .post-menu { + -webkit-transform: translateX(-18.125rem); + -moz-transform: translateX(-18.125rem); + -ms-transform: translateX(-18.125rem); + -o-transform: translateX(-18.125rem); + transform: translateX(-18.125rem); +} + +.post-menu::-webkit-scrollbar { + height: 8px; + width: 4px; +} + +.post-menu::-webkit-scrollbar-button { + height: 0; + width: 0; +} + +.post-menu::-webkit-scrollbar-button:start:decrement, +.post-menu::-webkit-scrollbar-button:end:increment { + display: block; +} + +.post-menu::-webkit-scrollbar-button:vertical:start:increment, +.post-menu::-webkit-scrollbar-button:vertical:end:decrement { + display: none; +} + +.post-menu::-webkit-scrollbar-track:vertical, +.post-menu::-webkit-scrollbar-track:horizontal { + background-clip: padding-box; + background-color: #191919; + border: 0 solid transparent; +} + +.post-menu::-webkit-scrollbar-track:hover { + background-color: #191919; + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.1); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.1); +} + +.post-menu::-webkit-scrollbar-track:active { + background-color: #191919; + -webkit-box-shadow: + inset 1px 0 0 rgba(0, 0, 0, 0.14), + inset -1px -1px 0 rgba(0, 0, 0, 0.07); + -moz-box-shadow: + inset 1px 0 0 rgba(0, 0, 0, 0.14), + inset -1px -1px 0 rgba(0, 0, 0, 0.07); + box-shadow: + inset 1px 0 0 rgba(0, 0, 0, 0.14), + inset -1px -1px 0 rgba(0, 0, 0, 0.07); +} + +.post-menu::-webkit-scrollbar-thumb { + background-clip: padding-box; + background-color: rgba(255, 255, 255, 0.3); + min-height: 40px; + padding-top: 100px; + border-radius: 2px; + -webkit-box-shadow: + inset 1px 1px 0 rgba(0, 0, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.07); + -moz-box-shadow: + inset 1px 1px 0 rgba(0, 0, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.07); + box-shadow: + inset 1px 1px 0 rgba(0, 0, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.07); +} + +.post-menu::-webkit-scrollbar-thumb:hover { + background-color: rgba(255, 255, 255, 0.4); + -webkit-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25); + -moz-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25); + box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25); +} + +.post-menu::-webkit-scrollbar-thumb:active { + background-color: rgba(255, 255, 255, 0.5); + -webkit-box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35); + -moz-box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35); + box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35); +} + +.post-menu::-webkit-scrollbar-thumb:vertical, +.post-menu::-webkit-scrollbar-thumb:horizontal { + border: 0 solid transparent; +} + +.toggle-menu-tree { + top: 0; + bottom: 0; + font-size: 1.375rem; + position: fixed; + right: 0; + margin-right: -5rem; + margin-bottom: auto; + margin-top: auto; + padding-left: 0.35rem; + color: #555; + width: 6.25rem; + height: 6.25rem; + background-color: rgba(255, 255, 255, 0.9); + cursor: pointer; + line-height: 6.25rem; + border-radius: 6.25rem; + border: 0.0625rem solid #f0f0f0; + opacity: 1; + font-family: -apple-system; + z-index: 890; + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; + -webkit-box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.117); + -moz-box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.117); + box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.117); +} + +.display-menu-tree .toggle-menu-tree { + background: #fafafa; + padding-left: 0; + width: 3.125rem; + height: 3.125rem; + line-height: 3.125rem; + text-align: center; + margin-right: -1.5625rem; + -webkit-transform: translateX(-17.5rem); + -moz-transform: translateX(-17.5rem); + -ms-transform: translateX(-17.5rem); + -o-transform: translateX(-17.5rem); + transform: translateX(-17.5rem); +} + +.toggle-menu-tree i { + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; +} + +.display-menu-tree .toggle-menu-tree i { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); +} + +.toggle-menu-tree:hover { + background: #fafafa; + -webkit-transform: translateX(-1.25rem); + -moz-transform: translateX(-1.25rem); + -ms-transform: translateX(-1.25rem); + -o-transform: translateX(-1.25rem); + transform: translateX(-1.25rem); +} + +.toggle-menu-tree.show { + right: 0; +} + +.toggle-menu-tree.hide { + right: -5.3125rem; +} + +.display-menu-tree .toggle-menu-tree.hide { + right: 0; +} + +.toc-wrap { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: -3rem; + overflow-x: hidden; + overflow-y: auto; + --webkit-overflow-scrolling: touch; +} + +.toc-content { + margin-right: 3rem; + padding-top: 2.875rem; + -webkit-transition: 0.5s ease all; + -moz-transition: 0.5s ease all; + -ms-transition: 0.5s ease all; + -o-transition: 0.5s ease all; + transition: 0.5s ease all; +} + +.post-menu-title { + font-size: 1.5rem; + text-align: left; + line-height: 3.6rem; + width: 100%; + font-weight: 700; + padding: 0 2.5rem; + color: #202020; +} + +.index-menu { + padding-top: 2rem; +} + +.index-menu-list { + line-height: 1.8em; + list-style: none; + padding: 0; +} + +.index-menu-item { + overflow: hidden; + text-overflow: ellipsis; +} + +.index-menu-item > .index-menu-list span.menu-content { + padding-left: 2rem; +} + +.index-menu-item > .index-menu-list > .index-menu-item > .index-menu-list span.menu-content { + padding-left: 4rem; +} + +.index-menu-item.current > a.index-menu-link { + background: #f5f5f5; + color: #1abc9c; + font-weight: 700; +} + +a.index-menu-link { + color: #555; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.85rem; + padding: 0.375rem 2.5rem; + position: relative; + display: block; +} + +a.index-menu-link:hover { + background: #efefef; + color: #333; +} diff --git a/src/components/page/friend/Friends.astro b/src/components/page/friend/Friends.astro index f00ee6f..228fd1c 100644 --- a/src/components/page/friend/Friends.astro +++ b/src/components/page/friend/Friends.astro @@ -26,15 +26,7 @@ const list = _.shuffle(friends);
-
- {friend.website} -
-
+
{friend.website}
{friend.description ? friend.description : ' '}
diff --git a/src/components/page/toc/TableOfContents.astro b/src/components/page/toc/TableOfContents.astro new file mode 100644 index 0000000..d567353 --- /dev/null +++ b/src/components/page/toc/TableOfContents.astro @@ -0,0 +1,34 @@ +--- +import TocItems from '@/components/page/toc/TocItems.astro'; +import { generateToC, type TocOpts } from '@/helpers/toc'; +import type { MarkdownHeading } from 'astro'; + +interface Props { + headings: MarkdownHeading[]; + toc: TocOpts | false; +} + +const { headings, toc } = Astro.props; +const items = generateToC(headings, toc); +--- + +{ + items.length > 0 && ( + + + + +
+
+
+

文章目录

+
+ +
+
+
+
+
+ + ) +} diff --git a/src/components/page/toc/TocItems.astro b/src/components/page/toc/TocItems.astro new file mode 100644 index 0000000..235803d --- /dev/null +++ b/src/components/page/toc/TocItems.astro @@ -0,0 +1,22 @@ +--- +import type { TocItem } from '@/helpers/toc'; + +interface Props { + items: TocItem[]; +} + +const { items } = Astro.props; +--- + +
    + { + items.map((item) => ( +
  • + + {item.text} + + {item.children.length > 0 && } +
  • + )) + } +
diff --git a/src/content/config.ts b/src/content/config.ts index cb4387f..4894bb5 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -22,6 +22,30 @@ const image = (fallbackImage: string) => .default(fallbackImage) .transform((file) => imageMetadata(file)); +// The default toc heading level. +const toc = () => + z + .union([ + z.object({ + // The level to start including headings at in the table of contents. Default: 2. + minHeadingLevel: z.number().int().min(1).max(6).optional().default(options.settings.toc.minHeadingLevel), + // The level to stop including headings at in the table of contents. Default: 3. + maxHeadingLevel: z.number().int().min(1).max(6).optional().default(options.settings.toc.maxHeadingLevel), + }), + z.boolean().transform((enabled) => + enabled + ? { + minHeadingLevel: options.settings.toc.minHeadingLevel, + maxHeadingLevel: options.settings.toc.maxHeadingLevel, + } + : false, + ), + ]) + .default(false) + .refine((toc) => (toc ? toc.minHeadingLevel <= toc.maxHeadingLevel : true), { + message: 'minHeadingLevel must be less than or equal to maxHeadingLevel', + }); + // Categories Collection const categoriesCollection = defineCollection({ type: 'data', @@ -70,6 +94,7 @@ const postsCollection = defineCollection({ summary: z.string().optional().default(''), cover: image(defaultCover), published: z.boolean().optional().default(true), + toc: toc(), }), }); @@ -84,6 +109,7 @@ const pagesCollection = defineCollection({ cover: image(defaultCover), published: z.boolean().optional().default(true), friend: z.boolean().optional().default(false), + toc: toc(), }), }); diff --git a/src/content/friends/index.yml b/src/content/friends/index.yml index 9303630..f96fa23 100644 --- a/src/content/friends/index.yml +++ b/src/content/friends/index.yml @@ -6,7 +6,6 @@ description: Trying to light up the dark homepage: https://cynosura.one poster: /images/links/cynosura.one.jpg - favicon: https://cynosura.one/img/favicon.ico - website: EEE.ME description: 途方に暮れて homepage: https://eee.me @@ -22,7 +21,6 @@ description: 这世界需要更多英雄 homepage: https://blog.mboker.cn poster: /images/links/mboker.cn.jpg - favicon: data:image/svg+xml,🌻 - website: 品味苏州 description: 吴侬软语、小桥流水的生活点滴 homepage: https://pwsz.com @@ -42,12 +40,11 @@ description: 茶盐观瑟丶行者自若 homepage: https://alexinea.com poster: /images/links/alexinea.com.jpg - favicon: https://alexinea.com/wp-content/uploads/2016/09/cropped-20160812.2-32x32.png - website: 贾顺名 description: 全沾码农的一些笔记 homepage: https://jiasm.github.io poster: /images/links/jiasm.github.io.jpg -- website: 白丁轶事 - FMoran的自留地 +- website: 白丁轶事 description: Everything should be hurry. homepage: https://fmoran.me poster: /images/links/fmoran.me.jpg @@ -55,22 +52,18 @@ description: 船长の部落格,记录有趣的事,分享技术经验。 homepage: https://captainofphb.me poster: /images/links/captainofphb.me.jpg - favicon: https://captainofphb.me/favicon.svg - website: 小萌博客 description: 互联网分享笔记 homepage: https://blog.luoli.net poster: /images/links/luoli.net.jpg - favicon: https://blog.luoli.net/usr/themes/MUltraMoe/images/favicon.png - website: LongLuo's Life Notes description: 每一天都是奇迹 homepage: https://www.longluo.me poster: /images/links/longluo.me.jpg - favicon: https://www.longluo.me/assets/logo/favicon-32x32.png - website: 佛爷小窝 description: 一个基于内容分享,创作与灵感结合的折腾笔记博客。 homepage: https://www.lafoyer.com poster: /images/links/lafoyer.com.jpg - favicon: https://www.boxmoe.com/file/100logo.png - website: 白熊阿丸的小屋 description: 在这里可以看到一个真实的我,我会在这里书写我的一切 homepage: https://bxaw.name diff --git a/src/content/posts/2024/2024-04-07-switch-blog-to-nextjs.mdx b/src/content/posts/2024/2024-04-07-switch-blog-to-nextjs.mdx index 77951b0..8b4db96 100644 --- a/src/content/posts/2024/2024-04-07-switch-blog-to-nextjs.mdx +++ b/src/content/posts/2024/2024-04-07-switch-blog-to-nextjs.mdx @@ -7,6 +7,9 @@ tags: - 博客 category: 编程 summary: 如你所见,当你访问这篇文章的时候,我已经把写了 13 年的博客程序从 WordPress 迁移到了自己用 Next.js 写的程序,这可能是我第 N 次尝试使用其他的方式写博客,但我想绝对不会是最后一次。 +toc: + minHeadingLevel: 2 + maxHeadingLevel: 3 cover: /images/2024/04/2024040719182310.jpg --- diff --git a/src/content/posts/2024/2024-06-30-pre-sale-housing.mdx b/src/content/posts/2024/2024-06-30-pre-sale-housing.mdx index 969761d..6059616 100644 --- a/src/content/posts/2024/2024-06-30-pre-sale-housing.mdx +++ b/src/content/posts/2024/2024-06-30-pre-sale-housing.mdx @@ -8,6 +8,7 @@ tags: - 期房 category: 杂谈 summary: 这篇文章试图从金融视角来期房销售的问题,也希望能回答相关投资理财遇到资损的问题。 +toc: true cover: /images/2024/06/2024063015135000.jpg --- diff --git a/src/content/posts/2024/2024-10-16-english-in-use.mdx b/src/content/posts/2024/2024-10-16-english-in-use.mdx index 88271d9..0c7b9b9 100644 --- a/src/content/posts/2024/2024-10-16-english-in-use.mdx +++ b/src/content/posts/2024/2024-10-16-english-in-use.mdx @@ -7,6 +7,7 @@ tags: - 学习 category: 杂谈 summary: 于我们成年人而言,虽已不能像儿童一样掌握一门外语,但是我们可通过“使用”的方式来习得英语。 +toc: true cover: /images/2024/10/2024101621070600.jpg --- diff --git a/src/helpers/schema.ts b/src/helpers/schema.ts index 2efb8fe..950f3ec 100644 --- a/src/helpers/schema.ts +++ b/src/helpers/schema.ts @@ -1,6 +1,6 @@ import { defaultCover } from '@/content/config.ts'; import options from '@/options'; -import { getCollection, getEntry, type Render } from 'astro:content'; +import { getCollection, type Render } from 'astro:content'; // Import the collections from the astro content. const categoriesCollection = await getCollection('categories'); @@ -24,7 +24,6 @@ export type Post = (typeof postsCollection)[number]['data'] & { slug: string; permalink: string; render: () => Render['.mdx']; - raw: () => Promise; }; export type Tag = (typeof tagsCollection)[number]['data'][number] & { counts: number; permalink: string }; @@ -36,10 +35,7 @@ export const pages: Page[] = pagesCollection .map((page) => ({ slug: page.slug, permalink: `/${page.slug}`, - render: async () => { - const entry = await getEntry('pages', page.slug); - return entry.render(); - }, + render: page.render, ...page.data, })); export const posts: Post[] = postsCollection @@ -47,14 +43,7 @@ export const posts: Post[] = postsCollection .map((post) => ({ slug: post.slug, permalink: `/posts/${post.slug}`, - render: async () => { - const entry = await getEntry('posts', post.slug); - return entry.render(); - }, - raw: async () => { - const entry = await getEntry('posts', post.slug); - return entry.body; - }, + render: post.render, ...post.data, })) .sort((left: Post, right: Post) => { diff --git a/src/helpers/toc.ts b/src/helpers/toc.ts new file mode 100644 index 0000000..6493e8d --- /dev/null +++ b/src/helpers/toc.ts @@ -0,0 +1,34 @@ +import type { MarkdownHeading } from 'astro'; + +export interface TocItem extends MarkdownHeading { + children: TocItem[]; +} + +export interface TocOpts { + minHeadingLevel: number; + maxHeadingLevel: number; +} + +// Convert the flat headings array generated by Astro into a nested tree structure. +export function generateToC(headings: MarkdownHeading[], opts: TocOpts | false): TocItem[] { + if (opts === false) { + return []; + } + + const { minHeadingLevel, maxHeadingLevel } = opts; + const toc: Array = []; + for (const heading of headings.filter(({ depth }) => depth >= minHeadingLevel && depth <= maxHeadingLevel)) { + injectChild(toc, { ...heading, children: [] }); + } + return toc; +} + +// Inject a ToC entry as deep in the tree as its `depth` property requires. +function injectChild(items: TocItem[], item: TocItem): void { + const lastItem = items.at(-1); + if (!lastItem || lastItem.depth >= item.depth) { + items.push(item); + } else { + injectChild(lastItem.children, item); + } +} diff --git a/src/layouts/PageLayout.astro b/src/layouts/PageLayout.astro index b92f82f..3fec3ee 100644 --- a/src/layouts/PageLayout.astro +++ b/src/layouts/PageLayout.astro @@ -4,6 +4,7 @@ import Image from '@/components/image/Image.astro'; import LikeButton from '@/components/like/LikeButton.astro'; import PageMeta from '@/components/meta/PageMeta.astro'; import Friends from '@/components/page/friend/Friends.astro'; +import TableOfContents from '@/components/page/toc/TableOfContents.astro'; import MusicPlayer from '@/components/player/MusicPlayer.astro'; import type { Page } from '@/helpers/schema'; import { urlJoin } from '@/helpers/tools'; @@ -15,7 +16,7 @@ interface Props { } const { page } = Astro.props; -const { Content } = await page.render(); +const { Content, headings } = await page.render(); --- @@ -25,6 +26,7 @@ const { Content } = await page.render();

{page.title}

+
+