mirror of
https://github.com/reonokiy/blog.nokiy.net.git
synced 2025-06-15 11:12:54 +02:00
✨ feat: add toc with accordion animation, add optional toc toggle, lower heti css priority
This commit is contained in:
parent
ab74c0abdf
commit
dc17f304c1
22 changed files with 280 additions and 167 deletions
|
@ -30,7 +30,6 @@
|
|||
"rehype-katex": "^7.0.1",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-toc": "^9.0.0",
|
||||
"sanitize-html": "^2.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
37
pnpm-lock.yaml
generated
37
pnpm-lock.yaml
generated
|
@ -59,9 +59,6 @@ importers:
|
|||
remark-math:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
remark-toc:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0
|
||||
sanitize-html:
|
||||
specifier: ^2.15.0
|
||||
version: 2.15.0
|
||||
|
@ -925,9 +922,6 @@ packages:
|
|||
'@types/sax@1.2.7':
|
||||
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
|
||||
|
||||
'@types/ungap__structured-clone@1.2.0':
|
||||
resolution: {integrity: sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==}
|
||||
|
||||
'@types/unist@2.0.11':
|
||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||
|
||||
|
@ -1691,8 +1685,8 @@ packages:
|
|||
duplexer@0.1.2:
|
||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||
|
||||
electron-to-chromium@1.5.126:
|
||||
resolution: {integrity: sha512-AtH1uLcTC72LA4vfYcEJJkrMk/MY/X0ub8Hv7QGAePW2JkeUFHEL/QfS4J77R6M87Sss8O0OcqReSaN1bpyA+Q==}
|
||||
electron-to-chromium@1.5.127:
|
||||
resolution: {integrity: sha512-Ke5OggqOtEqzCzcUyV+9jgO6L6sv1gQVKGtSExXHjD/FK0p4qzPZbrDsrCdy0DptcQprD0V80RCBYSWLMhTTgQ==}
|
||||
|
||||
emmet@2.4.11:
|
||||
resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==}
|
||||
|
@ -2553,9 +2547,6 @@ packages:
|
|||
mdast-util-to-string@4.0.0:
|
||||
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||
|
||||
mdast-util-toc@7.1.0:
|
||||
resolution: {integrity: sha512-2TVKotOQzqdY7THOdn2gGzS9d1Sdd66bvxUyw3aNpWfcPXCLYSJCCgfPy30sEtuzkDraJgqF35dzgmz6xlvH/w==}
|
||||
|
||||
mdn-data@2.0.28:
|
||||
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
|
||||
|
||||
|
@ -3071,9 +3062,6 @@ packages:
|
|||
remark-stringify@11.0.0:
|
||||
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
||||
|
||||
remark-toc@9.0.0:
|
||||
resolution: {integrity: sha512-KJ9txbo33GjDAV1baHFze7ij4G8c7SGYoY8Kzsm2gzFpbhL/bSoVpMMzGa3vrNDSWASNd/3ppAqL7cP2zD6JIA==}
|
||||
|
||||
request-light@0.5.8:
|
||||
resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==}
|
||||
|
||||
|
@ -4610,8 +4598,6 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 22.13.14
|
||||
|
||||
'@types/ungap__structured-clone@1.2.0': {}
|
||||
|
||||
'@types/unist@2.0.11': {}
|
||||
|
||||
'@types/unist@3.0.3': {}
|
||||
|
@ -5330,7 +5316,7 @@ snapshots:
|
|||
browserslist@4.24.4:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001707
|
||||
electron-to-chromium: 1.5.126
|
||||
electron-to-chromium: 1.5.127
|
||||
node-releases: 2.0.19
|
||||
update-browserslist-db: 1.1.3(browserslist@4.24.4)
|
||||
|
||||
|
@ -5589,7 +5575,7 @@ snapshots:
|
|||
|
||||
duplexer@0.1.2: {}
|
||||
|
||||
electron-to-chromium@1.5.126: {}
|
||||
electron-to-chromium@1.5.127: {}
|
||||
|
||||
emmet@2.4.11:
|
||||
dependencies:
|
||||
|
@ -6754,16 +6740,6 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
|
||||
mdast-util-toc@7.1.0:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
'@types/ungap__structured-clone': 1.2.0
|
||||
'@ungap/structured-clone': 1.3.0
|
||||
github-slugger: 2.0.0
|
||||
mdast-util-to-string: 4.0.0
|
||||
unist-util-is: 6.0.0
|
||||
unist-util-visit: 5.0.0
|
||||
|
||||
mdn-data@2.0.28: {}
|
||||
|
||||
mdn-data@2.0.30: {}
|
||||
|
@ -7518,11 +7494,6 @@ snapshots:
|
|||
mdast-util-to-markdown: 2.1.2
|
||||
unified: 11.0.5
|
||||
|
||||
remark-toc@9.0.0:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
mdast-util-toc: 7.1.0
|
||||
|
||||
request-light@0.5.8: {}
|
||||
|
||||
request-light@0.7.0: {}
|
||||
|
|
|
@ -33,6 +33,7 @@ const navItems = [
|
|||
---
|
||||
|
||||
<nav
|
||||
aria-label="Site Navigation"
|
||||
class:list={[
|
||||
isPost ? 'hidden lg:block' : '',
|
||||
'mb-10.5 text-3.6 font-semibold leading-8.75 font-navbar',
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { OverlayScrollbars } from 'overlayscrollbars'
|
||||
|
||||
function setupScrollbar() {
|
||||
// Add scrollbar to body
|
||||
const bodyElement = document.body
|
||||
if (!bodyElement.hasAttribute('data-scrollbar-initialized')) {
|
||||
OverlayScrollbars({
|
||||
|
@ -20,6 +21,7 @@ function setupScrollbar() {
|
|||
bodyElement.setAttribute('data-scrollbar-initialized', 'true')
|
||||
}
|
||||
|
||||
// Add scrollbar to code blocks
|
||||
const preElements = document.querySelectorAll('pre')
|
||||
preElements.forEach((pre) => {
|
||||
if (!pre.hasAttribute('data-scrollbar-initialized')) {
|
||||
|
@ -30,7 +32,7 @@ function setupScrollbar() {
|
|||
},
|
||||
}, {
|
||||
scrollbars: {
|
||||
theme: 'scrollbar-code',
|
||||
theme: 'scrollbar-widget',
|
||||
autoHide: 'leave',
|
||||
autoHideDelay: 500,
|
||||
},
|
||||
|
@ -39,6 +41,25 @@ function setupScrollbar() {
|
|||
pre.setAttribute('data-scrollbar-initialized', 'true')
|
||||
}
|
||||
})
|
||||
|
||||
// Add scrollbar to TOC content
|
||||
const tocElement = document.getElementById('toc-content')
|
||||
if (tocElement && !tocElement.hasAttribute('data-scrollbar-initialized')) {
|
||||
OverlayScrollbars({
|
||||
target: tocElement,
|
||||
cancel: {
|
||||
nativeScrollbarsOverlaid: true,
|
||||
},
|
||||
}, {
|
||||
scrollbars: {
|
||||
theme: 'scrollbar-widget',
|
||||
autoHide: 'leave',
|
||||
autoHideDelay: 500,
|
||||
},
|
||||
})
|
||||
|
||||
tocElement.setAttribute('data-scrollbar-initialized', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
setupScrollbar()
|
||||
|
@ -64,7 +85,7 @@ document.addEventListener('astro:after-swap', setupScrollbar)
|
|||
--os-handle-min-size: 12%;
|
||||
}
|
||||
|
||||
.scrollbar-code {
|
||||
.scrollbar-widget {
|
||||
--os-size: 0.6rem;
|
||||
--os-padding-perpendicular: 0.1rem;
|
||||
--os-padding-axis: 0.2rem;
|
||||
|
@ -80,7 +101,7 @@ document.addEventListener('astro:after-swap', setupScrollbar)
|
|||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.os-scrollbar {
|
||||
.os-scrollbar.scrollbar-body {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
|
112
src/components/Widgets/TOC.astro
Normal file
112
src/components/Widgets/TOC.astro
Normal file
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
import type { MarkdownHeading } from 'astro'
|
||||
import { ui } from '@/i18n/ui'
|
||||
import { getPageInfo } from '@/utils/page'
|
||||
|
||||
interface Props {
|
||||
headings: MarkdownHeading[]
|
||||
}
|
||||
|
||||
const { currentLang } = getPageInfo(Astro.url.pathname)
|
||||
const currentUI = ui[currentLang as keyof typeof ui]
|
||||
|
||||
const { headings = [] } = Astro.props
|
||||
const filteredHeadings = headings.filter(heading =>
|
||||
heading.depth >= 2
|
||||
&& heading.depth <= 4
|
||||
&& heading.text !== 'Footnotes',
|
||||
)
|
||||
---
|
||||
|
||||
{filteredHeadings.length > 0 && (
|
||||
<div
|
||||
class="relative mb-6 bg-secondary/5"
|
||||
border="~ secondary/5 rounded solid"
|
||||
transition="~ duration-300 ease-in-out"
|
||||
>
|
||||
{/* Accordion toggle for expandable TOC */}
|
||||
<input
|
||||
type="checkbox"
|
||||
id="toc-toggle"
|
||||
class="accordion-toggle"
|
||||
hidden
|
||||
/>
|
||||
<label
|
||||
for="toc-toggle"
|
||||
class="absolute inset-0 z-99 cursor-pointer"
|
||||
></label>
|
||||
|
||||
{/* TOC title bar */}
|
||||
<div class="h-12 w-full flex items-center bg-secondary/0">
|
||||
<span class="toc-title">
|
||||
{currentUI.toc}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Expandable content wrapper */}
|
||||
<div class="accordion-wrapper">
|
||||
<nav
|
||||
id="toc-content"
|
||||
class="accordion-content"
|
||||
aria-label="Table of Contents"
|
||||
>
|
||||
<ul class="toc-list">
|
||||
{filteredHeadings.map(heading => (
|
||||
<li
|
||||
class:list={{
|
||||
'ml-0': heading.depth === 2,
|
||||
'ml-4': heading.depth === 3,
|
||||
'ml-8': heading.depth === 4,
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href={`#${heading.slug}`}
|
||||
class:list={[
|
||||
{ 'toc-link-h2': heading.depth === 2 },
|
||||
{ 'toc-link-h3': heading.depth === 3 },
|
||||
{ 'toc-link-h4': heading.depth === 4 },
|
||||
]}
|
||||
>
|
||||
{heading.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- Override heti default styles -->
|
||||
<style>
|
||||
.toc-title {
|
||||
--at-apply: 'font-semibold pl-4 px-4 py-3';
|
||||
}
|
||||
.toc-list {
|
||||
--at-apply: 'list-none pl-0 space-y-2 mt-1 mb-4';
|
||||
}
|
||||
.toc-link-h2 {
|
||||
--at-apply: 'text-sm no-underline font-semibold';
|
||||
}
|
||||
.toc-link-h3 {
|
||||
--at-apply: 'text-sm no-underline font-normal';
|
||||
}
|
||||
.toc-link-h4 {
|
||||
--at-apply: 'text-sm no-underline font-normal';
|
||||
}
|
||||
/* Initial collapsed state with zero height grid row */
|
||||
.accordion-wrapper {
|
||||
--at-apply: 'grid rows-[0fr] duration-300 ease-in-out';
|
||||
}
|
||||
.accordion-content {
|
||||
--at-apply: 'overflow-hidden max-h-66 lg:max-h-82 z-99 pl-4';
|
||||
}
|
||||
|
||||
/* When toggle is checked, expand the wrapper to show content */
|
||||
.accordion-toggle:checked ~ .accordion-wrapper {
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
.accordion-toggle:checked ~ .accordion-wrapper .accordion-content {
|
||||
--at-apply: 'overflow-y-auto';
|
||||
}
|
||||
</style>
|
|
@ -14,7 +14,7 @@ const postsCollection = defineCollection({
|
|||
// Advanced
|
||||
draft: z.boolean().optional().default(false),
|
||||
pin: z.number().int().min(0).max(99).optional().default(0),
|
||||
toc: z.boolean().optional().default(false),
|
||||
toc: z.boolean().optional().default(true),
|
||||
lang: z.enum(['', ...allLocales]).optional().default(''),
|
||||
abbrlink: z.string().optional().default('').refine(
|
||||
abbrlink => !abbrlink || /^[a-z0-9\-]*$/.test(abbrlink),
|
||||
|
|
|
@ -231,7 +231,7 @@ Pins the article to the top. The higher the number, the higher the priority of t
|
|||
|
||||
#### toc
|
||||
|
||||
Enables the table of contents. This feature is not yet implemented.
|
||||
Generate table of contents. Set to false to disable automatic generation. Default is true.
|
||||
|
||||
#### lang
|
||||
|
||||
|
@ -243,15 +243,15 @@ Specifies the article language. Only one language can be specified. If not speci
|
|||
# moreLocales: ['es', 'ru']
|
||||
|
||||
# lang: ''
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
# lang: en
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
# lang: es
|
||||
src/content/posts/banana.md -> example.com/es/posts/banana/
|
||||
src/content/posts/apple.md -> example.com/es/posts/apple/
|
||||
# lang: ru
|
||||
src/content/posts/orange.md -> example.com/ru/posts/orange/
|
||||
src/content/posts/apple.md -> example.com/ru/posts/apple/
|
||||
```
|
||||
|
||||
#### abbrlink
|
|
@ -231,7 +231,7 @@ Fija el artículo en la parte superior. Cuanto mayor sea el número, mayor será
|
|||
|
||||
#### toc
|
||||
|
||||
Habilita la tabla de contenidos. Esta función aún no está implementada.
|
||||
¿Generar índice? Si se establece en false, se desactiva la generación automática. Valor predeterminado: true.
|
||||
|
||||
#### lang
|
||||
|
||||
|
@ -243,15 +243,15 @@ Especifica el idioma del artículo. Solo se puede especificar un idioma. Si no s
|
|||
# moreLocales: ['es', 'ru']
|
||||
|
||||
# lang: ''
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
# lang: en
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
# lang: es
|
||||
src/content/posts/banana.md -> example.com/es/posts/banana/
|
||||
src/content/posts/apple.md -> example.com/es/posts/apple/
|
||||
# lang: ru
|
||||
src/content/posts/orange.md -> example.com/ru/posts/orange/
|
||||
src/content/posts/apple.md -> example.com/ru/posts/apple/
|
||||
```
|
||||
|
||||
#### abbrlink
|
|
@ -231,7 +231,7 @@ abbrlink: theme-guide
|
|||
|
||||
#### toc
|
||||
|
||||
目次を有効にします。この機能はまだ実装されていません。
|
||||
目次を自動生成するかどうか。false に設定すると自動生成が無効になり、デフォルトは true。
|
||||
|
||||
#### lang
|
||||
|
||||
|
@ -243,15 +243,15 @@ abbrlink: theme-guide
|
|||
# moreLocales: ['es', 'ru']
|
||||
|
||||
# lang: ''
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
# lang: en
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
# lang: es
|
||||
src/content/posts/banana.md -> example.com/es/posts/banana/
|
||||
src/content/posts/apple.md -> example.com/es/posts/apple/
|
||||
# lang: ru
|
||||
src/content/posts/orange.md -> example.com/ru/posts/orange/
|
||||
src/content/posts/apple.md -> example.com/ru/posts/apple/
|
||||
```
|
||||
|
||||
#### abbrlink
|
|
@ -231,7 +231,7 @@ abbrlink: theme-guide
|
|||
|
||||
#### toc
|
||||
|
||||
Включает оглавление. Эта функция еще не реализована.
|
||||
Генерировать оглавление. При значении false автоматическая генерация отключается. По умолчанию: true.
|
||||
|
||||
#### lang
|
||||
|
||||
|
@ -243,15 +243,15 @@ abbrlink: theme-guide
|
|||
# moreLocales: ['es', 'ru']
|
||||
|
||||
# lang: ''
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
# lang: en
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
# lang: es
|
||||
src/content/posts/banana.md -> example.com/es/posts/banana/
|
||||
src/content/posts/apple.md -> example.com/es/posts/apple/
|
||||
# lang: ru
|
||||
src/content/posts/orange.md -> example.com/ru/posts/orange/
|
||||
src/content/posts/apple.md -> example.com/ru/posts/apple/
|
||||
```
|
||||
|
||||
#### abbrlink
|
|
@ -231,7 +231,7 @@ abbrlink: theme-guide
|
|||
|
||||
#### toc
|
||||
|
||||
是否開啟目錄。尚未實現該功能。
|
||||
是否生成目錄。設為 false 時停用自動生成,預設為 true。
|
||||
|
||||
#### lang
|
||||
|
||||
|
@ -243,15 +243,15 @@ abbrlink: theme-guide
|
|||
# moreLocales: ['es', 'ru']
|
||||
|
||||
# lang: ''
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
# lang: en
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
# lang: es
|
||||
src/content/posts/banana.md -> example.com/es/posts/banana/
|
||||
src/content/posts/apple.md -> example.com/es/posts/apple/
|
||||
# lang: ru
|
||||
src/content/posts/orange.md -> example.com/ru/posts/orange/
|
||||
src/content/posts/apple.md -> example.com/ru/posts/apple/
|
||||
```
|
||||
|
||||
#### abbrlink
|
|
@ -231,7 +231,7 @@ abbrlink: theme-guide
|
|||
|
||||
#### toc
|
||||
|
||||
是否开启目录。尚未实现该功能。
|
||||
是否生成目录。设为 false 时关闭自动生成,默认为 true。
|
||||
|
||||
#### lang
|
||||
|
||||
|
@ -243,15 +243,15 @@ abbrlink: theme-guide
|
|||
# moreLocales: ['es', 'ru']
|
||||
|
||||
# lang: ''
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
-> example.com/es/posts/apple/
|
||||
-> example.com/ru/posts/apple/
|
||||
# lang: en
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
src/content/posts/apple.md -> example.com/posts/apple/
|
||||
# lang: es
|
||||
src/content/posts/banana.md -> example.com/es/posts/banana/
|
||||
src/content/posts/apple.md -> example.com/es/posts/apple/
|
||||
# lang: ru
|
||||
src/content/posts/orange.md -> example.com/ru/posts/orange/
|
||||
src/content/posts/apple.md -> example.com/ru/posts/apple/
|
||||
```
|
||||
|
||||
#### abbrlink
|
|
@ -6,6 +6,7 @@ export const ui = {
|
|||
posts: '文章',
|
||||
tags: '标签',
|
||||
about: '关于',
|
||||
toc: '目录',
|
||||
},
|
||||
'zh-tw': {
|
||||
title: '重新編排',
|
||||
|
@ -14,6 +15,7 @@ export const ui = {
|
|||
posts: '文章',
|
||||
tags: '標籤',
|
||||
about: '關於',
|
||||
toc: '目錄',
|
||||
},
|
||||
'ja': {
|
||||
title: '再組版',
|
||||
|
@ -22,6 +24,7 @@ export const ui = {
|
|||
posts: '記事',
|
||||
tags: 'タグ',
|
||||
about: '概要',
|
||||
toc: '目次',
|
||||
},
|
||||
'en': {
|
||||
title: 'Retypeset',
|
||||
|
@ -30,6 +33,7 @@ export const ui = {
|
|||
posts: 'Posts',
|
||||
tags: 'Tags',
|
||||
about: 'About',
|
||||
toc: 'Table of Contents',
|
||||
},
|
||||
'es': {
|
||||
title: 'Retipografía',
|
||||
|
@ -38,6 +42,7 @@ export const ui = {
|
|||
posts: 'Artículos',
|
||||
tags: 'Etiquetas',
|
||||
about: 'Sobre',
|
||||
toc: 'Índice',
|
||||
},
|
||||
'ru': {
|
||||
title: 'Переверстка',
|
||||
|
@ -46,5 +51,6 @@ export const ui = {
|
|||
posts: 'Посты',
|
||||
tags: 'Теги',
|
||||
about: 'О себе',
|
||||
toc: 'Оглавление',
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { CollectionEntry } from 'astro:content'
|
|||
import Comments from '@/components/Comments/index.astro'
|
||||
import PostDate from '@/components/PostDate.astro'
|
||||
import GoBack from '@/components/Widgets/GoBack.astro'
|
||||
import TOC from '@/components/Widgets/TOC.astro'
|
||||
import { allLocales, defaultLocale, moreLocales } from '@/config'
|
||||
import { getTagPath } from '@/i18n/path'
|
||||
import Layout from '@/layouts/Layout.astro'
|
||||
|
@ -96,7 +97,7 @@ export async function getStaticPaths() {
|
|||
|
||||
const { post, lang, supportedLangs } = Astro.props
|
||||
const description = generateDescription(post, 'meta')
|
||||
const { Content, remarkPluginFrontmatter } = await post.render()
|
||||
const { Content, headings, remarkPluginFrontmatter } = await post.render()
|
||||
---
|
||||
|
||||
<Layout
|
||||
|
@ -132,6 +133,8 @@ const { Content, remarkPluginFrontmatter } = await post.render()
|
|||
minutes={remarkPluginFrontmatter.minutes}
|
||||
/>
|
||||
</div>
|
||||
<!-- TOC -->
|
||||
{post.data.toc && <TOC headings={headings} />}
|
||||
<!-- Content -->
|
||||
<Content />
|
||||
</article>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
}
|
||||
|
||||
html {
|
||||
--at-apply: 'bg-background c-secondary antialiased scrollbar-hidden';
|
||||
--at-apply: 'bg-background c-secondary antialiased scrollbar-hidden scroll-smooth';
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar {
|
||||
|
|
|
@ -18,165 +18,165 @@
|
|||
}
|
||||
|
||||
/* Links */
|
||||
.heti a {
|
||||
.heti :where(a) {
|
||||
--at-apply: 'underline decoration-secondary/40 underline-0.075em underline-offset-0.2em lg:underline-0.1em';
|
||||
--at-apply: 'font-medium transition-colors tracking-0 hover:(c-primary decoration-secondary/80) ';
|
||||
}
|
||||
|
||||
/* Paragraphs */
|
||||
.heti p {
|
||||
.heti :where(p) {
|
||||
--at-apply: 'mt-3 mb-4 text-justify';
|
||||
}
|
||||
.heti p:not(:lang(zh)):not(:lang(ja)):not(:lang(ko)) {
|
||||
.heti :where(p:not(:lang(zh)):not(:lang(ja)):not(:lang(ko))) {
|
||||
--at-apply: 'text-start';
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
.heti h1,
|
||||
.heti h2,
|
||||
.heti h3,
|
||||
.heti h4,
|
||||
.heti h5,
|
||||
.heti h6 {
|
||||
.heti :where(h1),
|
||||
.heti :where(h2),
|
||||
.heti :where(h3),
|
||||
.heti :where(h4),
|
||||
.heti :where(h5),
|
||||
.heti :where(h6) {
|
||||
--at-apply: 'mt-6 mb-3 font-semibold';
|
||||
}
|
||||
.heti h1 {
|
||||
.heti :where(h1) {
|
||||
--at-apply: 'text-8 leading-12';
|
||||
}
|
||||
.heti h2 {
|
||||
.heti :where(h2) {
|
||||
--at-apply: 'text-6 leading-9';
|
||||
}
|
||||
.heti h3 {
|
||||
.heti :where(h3) {
|
||||
--at-apply: 'text-5 leading-9';
|
||||
}
|
||||
.heti h4 {
|
||||
.heti :where(h4) {
|
||||
--at-apply: 'text-4.5 leading-6';
|
||||
}
|
||||
.heti h5 {
|
||||
.heti :where(h5) {
|
||||
--at-apply: 'text-4 leading-6';
|
||||
}
|
||||
.heti h6 {
|
||||
.heti :where(h6) {
|
||||
--at-apply: 'text-3.5 leading-6';
|
||||
}
|
||||
.heti h1,
|
||||
.heti h2,
|
||||
.heti h3 {
|
||||
.heti :where(h1),
|
||||
.heti :where(h2),
|
||||
.heti :where(h3) {
|
||||
--at-apply: 'tracking-0.05em';
|
||||
}
|
||||
.heti h1:not(:lang(zh)):not(:lang(ja)):not(:lang(ko)),
|
||||
.heti h2:not(:lang(zh)):not(:lang(ja)):not(:lang(ko)),
|
||||
.heti h3:not(:lang(zh)):not(:lang(ja)):not(:lang(ko)) {
|
||||
.heti :where(h1:not(:lang(zh)):not(:lang(ja)):not(:lang(ko))),
|
||||
.heti :where(h2:not(:lang(zh)):not(:lang(ja)):not(:lang(ko))),
|
||||
.heti :where(h3:not(:lang(zh)):not(:lang(ja)):not(:lang(ko))) {
|
||||
--at-apply: 'tracking-0';
|
||||
}
|
||||
.heti h1 + h2,
|
||||
.heti h2 + h3,
|
||||
.heti h3 + h4,
|
||||
.heti h4 + h5,
|
||||
.heti h5 + h6 {
|
||||
.heti :where(h1 + h2),
|
||||
.heti :where(h2 + h3),
|
||||
.heti :where(h3 + h4),
|
||||
.heti :where(h4 + h5),
|
||||
.heti :where(h5 + h6) {
|
||||
--at-apply: 'mt-3';
|
||||
}
|
||||
|
||||
/* Blockquotes */
|
||||
.heti blockquote {
|
||||
.heti :where(blockquote) {
|
||||
--at-apply: 'mt-3 mb-6 px-4 py-3 rounded';
|
||||
--at-apply: 'border-l-4 border-solid border-secondary/25 bg-secondary/5';
|
||||
}
|
||||
.heti blockquote blockquote {
|
||||
.heti blockquote :where(blockquote) {
|
||||
--at-apply: 'my-3';
|
||||
}
|
||||
.heti blockquote p {
|
||||
.heti blockquote :where(p) {
|
||||
--at-apply: 'my-2';
|
||||
}
|
||||
|
||||
/* Code Blocks */
|
||||
.heti pre {
|
||||
--at-apply: 'mt-3 mb-4 px-4 py-3 rounded bg-secondary/5! border border-solid border-secondary/7';
|
||||
.heti :where(pre) {
|
||||
--at-apply: 'mt-3 mb-4 px-4 py-3 rounded bg-secondary/5! border border-solid border-secondary/5';
|
||||
--at-apply: 'overflow-auto whitespace-pre scrollbar-hidden';
|
||||
}
|
||||
.heti pre code {
|
||||
.heti pre :where(code) {
|
||||
--at-apply: 'p-0 bg-secondary/0 tracking-0 border-none';
|
||||
}
|
||||
html.dark .heti pre span {
|
||||
html.dark .heti pre :where(span) {
|
||||
--at-apply: 'text-[var(--shiki-dark)]!';
|
||||
}
|
||||
|
||||
/* Inline Code */
|
||||
.heti code {
|
||||
--at-apply: 'p-0.5 bg-secondary/5 rounded text-0.85em border border-solid border-secondary/7';
|
||||
.heti :where(code) {
|
||||
--at-apply: 'p-0.5 bg-secondary/5 rounded text-0.85em border border-solid border-secondary/5';
|
||||
}
|
||||
|
||||
/* Horizontal Rules */
|
||||
.heti hr {
|
||||
.heti :where(hr) {
|
||||
--at-apply: 'border-secondary/25';
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.heti ul,
|
||||
.heti ol,
|
||||
.heti dl {
|
||||
.heti :where(ul),
|
||||
.heti :where(ol),
|
||||
.heti :where(dl) {
|
||||
--at-apply: 'mt-3 mb-6';
|
||||
}
|
||||
.heti ul,
|
||||
.heti ol {
|
||||
.heti :where(ul),
|
||||
.heti :where(ol) {
|
||||
--at-apply: 'pl-8';
|
||||
}
|
||||
.heti ul ul,
|
||||
.heti ul ol,
|
||||
.heti ol ul,
|
||||
.heti ol ol {
|
||||
.heti ul :where(ul),
|
||||
.heti ul :where(ol),
|
||||
.heti ol :where(ul),
|
||||
.heti ol :where(ol) {
|
||||
--at-apply: 'my-0';
|
||||
}
|
||||
.heti ul {
|
||||
.heti :where(ul) {
|
||||
--at-apply: 'list-disc';
|
||||
}
|
||||
.heti ol {
|
||||
.heti :where(ol) {
|
||||
--at-apply: 'list-decimal';
|
||||
}
|
||||
.heti ul ul,
|
||||
.heti ol ul {
|
||||
.heti ul :where(ul),
|
||||
.heti ol :where(ul) {
|
||||
--at-apply: 'list-circle';
|
||||
}
|
||||
.heti ul ul ul,
|
||||
.heti ul ol ul,
|
||||
.heti ol ul ul,
|
||||
.heti ol ol ul {
|
||||
.heti ul ul :where(ul),
|
||||
.heti ul ol :where(ul),
|
||||
.heti ol ul :where(ul),
|
||||
.heti ol ol :where(ul) {
|
||||
--at-apply: 'list-square';
|
||||
}
|
||||
.heti li {
|
||||
.heti :where(li) {
|
||||
--at-apply: 'list-unset';
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.heti table {
|
||||
.heti :where(table) {
|
||||
--at-apply: 'box-border table-fixed mt-3 mb-6 rounded-md break-words';
|
||||
--at-apply: 'border border-solid border-secondary/25 border-collapse';
|
||||
}
|
||||
.heti th,
|
||||
.heti td {
|
||||
.heti :where(th),
|
||||
.heti :where(td) {
|
||||
--at-apply: 'px-3 py-1.5 border border-solid border-secondary/40';
|
||||
}
|
||||
|
||||
/* Abbreviations */
|
||||
.heti abbr {
|
||||
.heti :where(abbr) {
|
||||
--at-apply: 'tracking-0';
|
||||
}
|
||||
.heti abbr[title] {
|
||||
.heti :where(abbr[title]) {
|
||||
--at-apply: 'mx-0.25 pb-0.25 border-dotted border-secondary border-b-1 no-underline cursor-help';
|
||||
}
|
||||
|
||||
/* Superscript and Subscript */
|
||||
.heti sub,
|
||||
.heti sup {
|
||||
.heti :where(sub),
|
||||
.heti :where(sup) {
|
||||
--at-apply: 'mx-0.15em relative text-0.75em leading-1 align-baseline';
|
||||
}
|
||||
.heti sub {
|
||||
.heti :where(sub) {
|
||||
--at-apply: 'bottom--0.25em';
|
||||
}
|
||||
.heti sup {
|
||||
.heti :where(sup) {
|
||||
--at-apply: 'top--0.5em';
|
||||
}
|
||||
.heti sub a,
|
||||
.heti sup a {
|
||||
.heti sub :where(a),
|
||||
.heti sup :where(a) {
|
||||
--at-apply: 'no-underline';
|
||||
}
|
||||
.heti sup:target,
|
||||
|
@ -189,16 +189,16 @@ html.dark .heti sup a:target {
|
|||
}
|
||||
|
||||
/* Keyboard Input */
|
||||
.heti kbd {
|
||||
.heti :where(kbd) {
|
||||
--at-apply: 'rounded border border-solid border-secondary/40 text-secondary';
|
||||
--at-apply: 'inline-block text-0.85em font-bold leading-none px-1 py-0.75 whitespace-nowrap';
|
||||
}
|
||||
|
||||
/* Highlighted Text */
|
||||
.heti mark {
|
||||
.heti :where(mark) {
|
||||
--at-apply: 'mx-0.25 px-0.25 py-0.5 text-inherit bg-#ff0';
|
||||
}
|
||||
html.dark .heti mark {
|
||||
html.dark .heti :where(mark) {
|
||||
--at-apply: 'bg-#4d4a00e0';
|
||||
}
|
||||
|
||||
|
@ -231,68 +231,68 @@ html.dark .heti mark {
|
|||
content: "Сноски";
|
||||
--at-apply: 'block text-5 font-semibold mt-6 mb-3';
|
||||
}
|
||||
.heti .data-footnote-backref {
|
||||
.heti :where(.data-footnote-backref) {
|
||||
--at-apply: 'no-underline';
|
||||
}
|
||||
|
||||
/* Bold */
|
||||
.heti b,
|
||||
.heti strong {
|
||||
.heti :where(b),
|
||||
.heti :where(strong) {
|
||||
--at-apply: 'font-semibold';
|
||||
}
|
||||
|
||||
/* Italic */
|
||||
.heti i {
|
||||
.heti :where(i) {
|
||||
--at-apply: 'font-italic';
|
||||
}
|
||||
|
||||
/* Emphasized */
|
||||
.heti em {
|
||||
.heti :where(em) {
|
||||
--at-apply: 'italic';
|
||||
}
|
||||
|
||||
/* Quotes */
|
||||
.heti q {
|
||||
.heti :where(q) {
|
||||
quotes: "「" "」" "『" "』";
|
||||
}
|
||||
.heti q:not(:lang(zh)):not(:lang(ja)):not(:lang(ko)) {
|
||||
.heti :where(q:not(:lang(zh)):not(:lang(ja)):not(:lang(ko))) {
|
||||
quotes: initial;
|
||||
quotes: auto;
|
||||
}
|
||||
|
||||
/* Wavy Underline */
|
||||
.heti u {
|
||||
.heti :where(u) {
|
||||
--at-apply: 'mx-0.25 underline decoration-wavy decoration-red underline-offset-4';
|
||||
}
|
||||
html.dark .heti u {
|
||||
html.dark .heti :where(u) {
|
||||
--at-apply: 'decoration-#A14F50';
|
||||
}
|
||||
|
||||
/* Strikethrough */
|
||||
.heti del,
|
||||
.heti s {
|
||||
.heti :where(del),
|
||||
.heti :where(s) {
|
||||
--at-apply: 'mx-0.25';
|
||||
}
|
||||
|
||||
/* Images */
|
||||
.heti figure {
|
||||
.heti :where(figure) {
|
||||
--at-apply: 'my-6';
|
||||
}
|
||||
.heti figure > figcaption {
|
||||
.heti figure > :where(figcaption) {
|
||||
--at-apply: 'text-center text-sm mt-3 text-secondary/75';
|
||||
}
|
||||
|
||||
/* Markdown Extensions Style >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
|
||||
.heti details {
|
||||
.heti :where(details) {
|
||||
--at-apply: 'my-4 px-4 py-3 border border-solid border-secondary/25 rounded cursor-pointer';
|
||||
}
|
||||
.heti details summary {
|
||||
.heti details :where(summary) {
|
||||
--at-apply: 'cursor-pointer';
|
||||
}
|
||||
.heti details[open] summary {
|
||||
.heti details[open] :where(summary) {
|
||||
--at-apply: 'border-b border-solid border-secondary/25 mb-3 pb-3';
|
||||
}
|
||||
/* .heti details summary {
|
||||
/* .heti details :where(summary) {
|
||||
list-style: none;
|
||||
}
|
||||
.heti details summary::-webkit-details-marker {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue