Merge branch 'saicaca:main' into main

This commit is contained in:
Roberto Rodríguez Márquez 2024-10-28 13:10:24 +01:00 committed by GitHub
commit 7f59fffbcd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 2507 additions and 2488 deletions

View File

@ -1,39 +1,39 @@
import sitemap from '@astrojs/sitemap'; import sitemap from '@astrojs/sitemap'
import svelte from "@astrojs/svelte" import svelte from '@astrojs/svelte'
import tailwind from "@astrojs/tailwind" import tailwind from '@astrojs/tailwind'
import swup from '@swup/astro'; import swup from '@swup/astro'
import Compress from "astro-compress" import Compress from 'astro-compress'
import icon from "astro-icon" import icon from 'astro-icon'
import { defineConfig } from "astro/config" import { defineConfig } from 'astro/config'
import Color from "colorjs.io" import Color from 'colorjs.io'
import rehypeAutolinkHeadings from "rehype-autolink-headings" import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypeComponents from "rehype-components"; /* Render the custom directive content */ import rehypeComponents from 'rehype-components' /* Render the custom directive content */
import rehypeKatex from "rehype-katex" import rehypeKatex from 'rehype-katex'
import rehypeSlug from "rehype-slug" import rehypeSlug from 'rehype-slug'
import remarkDirective from "remark-directive" /* Handle directives */ import remarkDirective from 'remark-directive' /* Handle directives */
import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives"; import remarkGithubAdmonitionsToDirectives from 'remark-github-admonitions-to-directives'
import remarkMath from "remark-math" import remarkMath from 'remark-math'
import { AdmonitionComponent } from "./src/plugins/rehype-component-admonition.mjs" import { AdmonitionComponent } from './src/plugins/rehype-component-admonition.mjs'
import { GithubCardComponent } from "./src/plugins/rehype-component-github-card.mjs" import { GithubCardComponent } from './src/plugins/rehype-component-github-card.mjs'
import {parseDirectiveNode} from "./src/plugins/remark-directive-rehype.js"; import { parseDirectiveNode } from './src/plugins/remark-directive-rehype.js'
import { remarkReadingTime } from "./src/plugins/remark-reading-time.mjs" import { remarkExcerpt } from './src/plugins/remark-excerpt.js'
import {remarkExcerpt} from "./src/plugins/remark-excerpt.js"; import { remarkReadingTime } from './src/plugins/remark-reading-time.mjs'
const oklchToHex = (str) => { const oklchToHex = str => {
const DEFAULT_HUE = 250 const DEFAULT_HUE = 250
const regex = /-?\d+(\.\d+)?/g const regex = /-?\d+(\.\d+)?/g
const matches = str.string.match(regex) const matches = str.string.match(regex)
const lch = [matches[0], matches[1], DEFAULT_HUE] const lch = [matches[0], matches[1], DEFAULT_HUE]
return new Color("oklch", lch).to("srgb").toString({ return new Color('oklch', lch).to('srgb').toString({
format: "hex", format: 'hex',
}) })
} }
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: "https://fuwari.vercel.app/", site: 'https://fuwari.vercel.app/',
base: "/", base: '/',
trailingSlash: "always", trailingSlash: 'always',
integrations: [ integrations: [
tailwind(), tailwind(),
swup({ swup({
@ -41,7 +41,7 @@ export default defineConfig({
animationClass: 'transition-swup-', // see https://swup.js.org/options/#animationselector animationClass: 'transition-swup-', // see https://swup.js.org/options/#animationselector
// the default value `transition-` cause transition delay // the default value `transition-` cause transition delay
// when the Tailwind class `transition-all` is used // when the Tailwind class `transition-all` is used
containers: ['main'], containers: ['main', '#toc'],
smoothScrolling: true, smoothScrolling: true,
cache: true, cache: true,
preload: true, preload: true,
@ -52,10 +52,10 @@ export default defineConfig({
}), }),
icon({ icon({
include: { include: {
"material-symbols": ["*"], 'material-symbols': ['*'],
"fa6-brands": ["*"], 'fa6-brands': ['*'],
"fa6-regular": ["*"], 'fa6-regular': ['*'],
"fa6-solid": ["*"], 'fa6-solid': ['*'],
}, },
}), }),
svelte(), svelte(),
@ -64,43 +64,53 @@ export default defineConfig({
CSS: false, CSS: false,
Image: false, Image: false,
Action: { Action: {
Passed: async () => true, // https://github.com/PlayForm/Compress/issues/376 Passed: async () => true, // https://github.com/PlayForm/Compress/issues/376
}, },
}), }),
], ],
markdown: { markdown: {
remarkPlugins: [remarkMath, remarkReadingTime, remarkExcerpt, remarkGithubAdmonitionsToDirectives, remarkDirective, parseDirectiveNode], remarkPlugins: [
remarkMath,
remarkReadingTime,
remarkExcerpt,
remarkGithubAdmonitionsToDirectives,
remarkDirective,
parseDirectiveNode,
],
rehypePlugins: [ rehypePlugins: [
rehypeKatex, rehypeKatex,
rehypeSlug, rehypeSlug,
[rehypeComponents, { [
components: { rehypeComponents,
github: GithubCardComponent, {
note: (x, y) => AdmonitionComponent(x, y, "note"), components: {
tip: (x, y) => AdmonitionComponent(x, y, "tip"), github: GithubCardComponent,
important: (x, y) => AdmonitionComponent(x, y, "important"), note: (x, y) => AdmonitionComponent(x, y, 'note'),
caution: (x, y) => AdmonitionComponent(x, y, "caution"), tip: (x, y) => AdmonitionComponent(x, y, 'tip'),
warning: (x, y) => AdmonitionComponent(x, y, "warning"), important: (x, y) => AdmonitionComponent(x, y, 'important'),
caution: (x, y) => AdmonitionComponent(x, y, 'caution'),
warning: (x, y) => AdmonitionComponent(x, y, 'warning'),
},
}, },
}], ],
[ [
rehypeAutolinkHeadings, rehypeAutolinkHeadings,
{ {
behavior: "append", behavior: 'append',
properties: { properties: {
className: ["anchor"], className: ['anchor'],
}, },
content: { content: {
type: "element", type: 'element',
tagName: "span", tagName: 'span',
properties: { properties: {
className: ["anchor-icon"], className: ['anchor-icon'],
'data-pagefind-ignore': true, 'data-pagefind-ignore': true,
}, },
children: [ children: [
{ {
type: "text", type: 'text',
value: "#", value: '#',
}, },
], ],
}, },
@ -113,12 +123,15 @@ export default defineConfig({
rollupOptions: { rollupOptions: {
onwarn(warning, warn) { onwarn(warning, warn) {
// temporarily suppress this warning // temporarily suppress this warning
if (warning.message.includes("is dynamically imported by") && warning.message.includes("but also statically imported by")) { if (
return; warning.message.includes('is dynamically imported by') &&
warning.message.includes('but also statically imported by')
) {
return
} }
warn(warning); warn(warning)
} },
} },
}, },
css: { css: {
preprocessorOptions: { preprocessorOptions: {

View File

@ -17,12 +17,12 @@
"@astrojs/check": "^0.9.3", "@astrojs/check": "^0.9.3",
"@astrojs/rss": "^4.0.7", "@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6", "@astrojs/sitemap": "^3.1.6",
"@astrojs/svelte": "^5.7.0", "@astrojs/svelte": "^5.7.1",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.1",
"@fontsource-variable/jetbrains-mono": "^5.0.22", "@fontsource-variable/jetbrains-mono": "^5.0.22",
"@fontsource/roboto": "^5.0.14", "@fontsource/roboto": "^5.0.14",
"@swup/astro": "^1.4.1", "@swup/astro": "^1.4.1",
"astro": "^4.15.0", "astro": "^4.15.9",
"astro-compress": "^2.3.1", "astro-compress": "^2.3.1",
"astro-icon": "^1.1.1", "astro-icon": "^1.1.1",
"colorjs.io": "^0.5.2", "colorjs.io": "^0.5.2",
@ -47,7 +47,7 @@
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@astrojs/ts-plugin": "^1.10.1", "@astrojs/ts-plugin": "^1.10.2",
"@biomejs/biome": "1.8.3", "@biomejs/biome": "1.8.3",
"@iconify-json/fa6-brands": "^1.1.22", "@iconify-json/fa6-brands": "^1.1.22",
"@iconify-json/fa6-regular": "^1.1.22", "@iconify-json/fa6-regular": "^1.1.22",

4102
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

225
src/base.css Normal file
View File

@ -0,0 +1,225 @@
/* The integration's default injected base.css file */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.card-base {
@apply rounded-[var(--radius-large)] overflow-hidden bg-[var(--card-bg)] transition;
}
h1, h2, h3, h4, h5, h6, p, a, span, li, ul, ol, blockquote, code, pre, table, th, td, strong {
@apply transition;
}
.card-shadow {
@apply drop-shadow-[0_2px_4px_rgba(0,0,0,0.005)]
}
.expand-animation {
@apply relative before:ease-out before:transition active:bg-none hover:before:bg-[var(--btn-plain-bg-hover)] active:before:bg-[var(--btn-plain-bg-active)] z-0
before:absolute before:rounded-[inherit] before:inset-0 before:scale-[0.85] hover:before:scale-100 before:-z-10
}
.link {
@apply transition rounded-md p-1 -m-1 expand-animation;
}
.link-lg {
@apply transition rounded-md p-1.5 -m-1.5 expand-animation;
}
.float-panel {
@apply top-[5.25rem] rounded-[var(--radius-large)] overflow-hidden bg-[var(--float-panel-bg)] transition shadow-xl dark:shadow-none
}
.float-panel-closed {
@apply -translate-y-1 opacity-0 pointer-events-none
}
.search-panel mark {
@apply bg-transparent text-[var(--primary)]
}
.btn-card {
@apply transition flex items-center justify-center bg-[var(--card-bg)] hover:bg-[var(--btn-card-bg-hover)]
active:bg-[var(--btn-card-bg-active)]
}
.btn-card.disabled {
@apply pointer-events-none text-black/10 dark:text-white/10
}
.btn-plain {
@apply transition relative flex items-center justify-center bg-none
text-black/75 hover:text-[var(--primary)] dark:text-white/75 dark:hover:text-[var(--primary)];
&:not(.scale-animation) {
@apply hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)]
}
&.scale-animation {
@apply expand-animation;
&.current-theme-btn {
@apply before:scale-100 before:opacity-100 before:bg-[var(--btn-plain-bg-hover)] text-[var(--primary)]
}
}
}
.btn-regular {
@apply transition flex items-center justify-center bg-[var(--btn-regular-bg)] hover:bg-[var(--btn-regular-bg-hover)] active:bg-[var(--btn-regular-bg-active)]
text-[var(--btn-content)] dark:text-white/75
}
.link-underline {
@apply transition underline decoration-2 decoration-dashed decoration-[var(--link-underline)]
hover:decoration-[var(--link-hover)] active:decoration-[var(--link-active)] underline-offset-[0.25rem]
}
.toc-hide,
.toc-not-ready {
@apply opacity-0 pointer-events-none
}
#toc-inner-wrapper {
mask-image: linear-gradient(to bottom, transparent 0%, black 2rem, black calc(100% - 2rem), transparent 100%);
}
.hide-scrollbar {
scrollbar-width: none;
-ms-overflow-style: none;
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.text-90 {
@apply text-black/90 dark:text-white/90
}
.text-75 {
@apply text-black/75 dark:text-white/75
}
.text-50 {
@apply text-black/50 dark:text-white/50
}
.text-30 {
@apply text-black/30 dark:text-white/30
}
.text-25 {
@apply text-black/25 dark:text-white/25
}
html.is-changing .transition-swup-fade {
@apply transition-all duration-200
}
html.is-animating .transition-swup-fade {
@apply opacity-0 translate-y-4
}
/* PhotoSwipe */
.pswp__button {
@apply transition bg-black/40 hover:bg-black/50 active:bg-black/60 flex items-center justify-center mr-0 w-12 h-12 !important;
}
.pswp__button--zoom, .pswp__button--close {
@apply mt-4 rounded-xl active:scale-90 !important;
}
.pswp__button--zoom {
@apply mr-2.5 !important;
}
.pswp__button--close {
@apply mr-4 !important;
}
.custom-md img, #post-cover img {
@apply cursor-zoom-in
}
}
@layer components {
.meta-icon {
@apply w-8 h-8 transition rounded-md flex items-center justify-center bg-[var(--btn-regular-bg)]
text-[var(--btn-content)] mr-2
}
.with-divider {
@apply before:content-['/'] before:ml-1.5 before:mr-1.5 before:text-[var(--meta-divider)] before:text-sm
before:font-medium before:first-of-type:hidden before:transition
}
}
@layer components {
.btn-regular-dark {
@apply flex items-center justify-center
bg-[oklch(0.45_0.01_var(--hue))] hover:bg-[oklch(0.50_0.01_var(--hue))] active:bg-[oklch(0.55_0.01_var(--hue))]
dark:bg-[oklch(0.30_0.02_var(--hue))] dark:hover:bg-[oklch(0.35_0.03_var(--hue))] dark:active:bg-[oklch(0.40_0.03_var(--hue))]
}
.btn-regular-dark.success {
@apply bg-[oklch(0.75_0.14_var(--hue))] dark:bg-[oklch(0.75_0.14_var(--hue))]
}
.copy-btn-icon {
@apply absolute top-1/2 left-1/2 transition -translate-x-1/2 -translate-y-1/2
}
.copy-btn .copy-icon {
@apply opacity-100 fill-white dark:fill-white/75
}
.copy-btn.success .copy-icon {
@apply opacity-0 fill-[var(--deep-text)]
}
.copy-btn .success-icon {
@apply opacity-0
}
.copy-btn.success .success-icon {
@apply opacity-100
}
}
@layer components {
.dash-line {
position: relative;
}
.dash-line::before {
content: "";
position: absolute;
width: 10%;
height: 100%;
top: 50%;
left: calc(50% - 1px);
border-left: 2px dashed var(--line-color);
pointer-events: none;
transition: all 0.3s;
transform: translateY(-50%);
}
}
@layer components {
.custom-md h1 {
@apply text-3xl
}
}
@keyframes fade-in-up {
0% {
transform: translateY(2rem);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.onload-animation {
opacity: 0;
animation: 300ms fade-in-up;
animation-fill-mode: forwards;
}
#navbar {
animation-delay: 0ms
}
#sidebar {
animation-delay: 100ms
}
#swup-container {
outline: none;
}
#content-wrapper {
animation-delay: var(--content-delay);
}
.footer {
animation-delay: 250ms;
}
#banner-credit {
animation-delay: 400ms;
}
.collapsed {
height: var(--collapsedHeight);
}

View File

@ -115,19 +115,4 @@ function formatTag(tag: string[]) {
</div> </div>
)) ))
} }
</div> </div>
<style>
@tailwind components;
@tailwind utilities;
@layer components {
.dash-line {
}
.dash-line::before {
content: "";
@apply w-[10%] h-full absolute -top-1/2 left-[calc(50%_-_1px)] -top-[50%] border-l-[2px]
border-dashed pointer-events-none border-[var(--line-color)] transition
}
}
</style>

View File

@ -49,9 +49,6 @@ rainbow-dark = linear-gradient(to right, oklch(0.70 0.10 0), oklch(0.70 0.10 30)
:root :root
--radius-large 1rem --radius-large 1rem
--banner-height-home 65vh
--banner-height 35vh
--content-delay 150ms --content-delay 150ms
color_set({ color_set({
@ -120,6 +117,10 @@ color_set({
--admonitions-color-important: oklch(0.7 0.14 310) oklch(0.75 0.14 310) --admonitions-color-important: oklch(0.7 0.14 310) oklch(0.75 0.14 310)
--admonitions-color-warning: oklch(0.7 0.14 60) oklch(0.75 0.14 60) --admonitions-color-warning: oklch(0.7 0.14 60) oklch(0.75 0.14 60)
--admonitions-color-caution: oklch(0.6 0.2 25) oklch(0.65 0.2 25) --admonitions-color-caution: oklch(0.6 0.2 25) oklch(0.65 0.2 25)
--toc-badge-bg: oklch(0.9 0.045 var(--hue)) var(--btn-regular-bg)
--toc-btn-hover: oklch(0.92 0.015 var(--hue)) oklch(0.22 0.02 var(--hue))
--toc-btn-active: oklch(0.90 0.015 var(--hue)) oklch(0.25 0.02 var(--hue))
}) })
@ -176,143 +177,4 @@ color_set({
--os-handle-bg-active: var(--scrollbar-bg-active-light); --os-handle-bg-active: var(--scrollbar-bg-active-light);
</style>
<style is:global lang="scss">
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.card-base {
@apply rounded-[var(--radius-large)] overflow-hidden bg-[var(--card-bg)] transition;
}
h1, h2, h3, h4, h5, h6, p, a, span, li, ul, ol, blockquote, code, pre, table, th, td, strong {
@apply transition;
}
.card-shadow {
@apply drop-shadow-[0_2px_4px_rgba(0,0,0,0.005)]
}
.expand-animation {
@apply relative before:ease-out before:transition active:bg-none hover:before:bg-[var(--btn-plain-bg-hover)] active:before:bg-[var(--btn-plain-bg-active)] z-0
before:absolute before:rounded-[inherit] before:inset-0 before:scale-[0.85] hover:before:scale-100 before:-z-10
}
.link {
@apply transition rounded-md p-1 -m-1 expand-animation;
}
.link-lg {
@apply transition rounded-md p-1.5 -m-1.5 expand-animation;
}
.float-panel {
@apply top-[5.25rem] rounded-[var(--radius-large)] overflow-hidden bg-[var(--float-panel-bg)] transition shadow-xl dark:shadow-none
}
.float-panel-closed {
@apply -translate-y-1 opacity-0 pointer-events-none
}
.search-panel mark {
@apply bg-transparent text-[var(--primary)]
}
.btn-card {
@apply transition flex items-center justify-center bg-[var(--card-bg)] hover:bg-[var(--btn-card-bg-hover)]
active:bg-[var(--btn-card-bg-active)]
}
.btn-card.disabled {
@apply pointer-events-none text-black/10 dark:text-white/10
}
.btn-plain {
@apply transition relative flex items-center justify-center bg-none
text-black/75 hover:text-[var(--primary)] dark:text-white/75 dark:hover:text-[var(--primary)];
&:not(.scale-animation) {
@apply hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)]
}
&.scale-animation {
@apply expand-animation;
&.current-theme-btn {
@apply before:scale-100 before:opacity-100 before:bg-[var(--btn-plain-bg-hover)] text-[var(--primary)]
}
}
}
.btn-regular {
@apply transition flex items-center justify-center bg-[var(--btn-regular-bg)] hover:bg-[var(--btn-regular-bg-hover)] active:bg-[var(--btn-regular-bg-active)]
text-[var(--btn-content)] dark:text-white/75
}
.link-underline {
@apply transition underline decoration-2 decoration-dashed decoration-[var(--link-underline)]
hover:decoration-[var(--link-hover)] active:decoration-[var(--link-active)] underline-offset-[0.25rem]
}
.text-90 {
@apply text-black/90 dark:text-white/90
}
.text-75 {
@apply text-black/75 dark:text-white/75
}
.text-50 {
@apply text-black/50 dark:text-white/50
}
.text-30 {
@apply text-black/30 dark:text-white/30
}
.text-25 {
@apply text-black/25 dark:text-white/25
}
html.is-changing .transition-swup-fade {
@apply transition-all duration-200
}
html.is-animating .transition-swup-fade {
@apply opacity-0 translate-y-4
}
/* PhotoSwipe */
.pswp__button {
@apply transition bg-black/40 hover:bg-black/50 active:bg-black/60 flex items-center justify-center mr-0 w-12 h-12 !important;
}
.pswp__button--zoom, .pswp__button--close {
@apply mt-4 rounded-xl active:scale-90 !important;
}
.pswp__button--zoom {
@apply mr-2.5 !important;
}
.pswp__button--close {
@apply mr-4 !important;
}
.custom-md img, #post-cover img {
@apply cursor-zoom-in
}
}
@keyframes fade-in-up {
0% {
transform: translateY(2rem);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.onload-animation {
opacity: 0;
animation: 300ms fade-in-up;
animation-fill-mode: forwards;
}
#navbar {
animation-delay: 0ms
}
#sidebar {
animation-delay: 100ms
}
#content-wrapper {
animation-delay: var(--content-delay);
}
.footer {
animation-delay: 250ms;
}
#banner-credit {
animation-delay: 400ms;
}
</style> </style>

View File

@ -67,9 +67,6 @@ let links: NavBarLink[] = navBarConfig.links.map(
</div> </div>
</div> </div>
<style lang="stylus">
</style>
<script> <script>
function switchTheme() { function switchTheme() {

View File

@ -59,19 +59,4 @@ const className = Astro.props.class
{!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>} {!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
</div> </div>
</div> </div>
</div> </div>
<style>
@tailwind components;
@layer components {
.meta-icon {
@apply w-8 h-8 transition rounded-md flex items-center justify-center bg-[var(--btn-regular-bg)]
text-[var(--btn-content)] mr-2
}
.with-divider {
@apply before:content-['/'] before:ml-1.5 before:mr-1.5 before:text-[var(--meta-divider)] before:text-sm
before:font-medium before:first-of-type:hidden before:transition
}
}
</style>

View File

@ -117,3 +117,9 @@ top-20 left-4 md:left-[unset] right-4 shadow-2xl rounded-2xl p-2">
</a> </a>
{/each} {/each}
</div> </div>
<style>
input:focus {
outline: 0;
}
</style>

View File

@ -25,7 +25,7 @@ import { Icon } from 'astro-icon/components'
font-weight: bold font-weight: bold
border: none border: none
position: fixed position: fixed
bottom: 15rem bottom: 10rem
opacity: 1 opacity: 1
cursor: pointer cursor: pointer
transform: translateX(5rem) transform: translateX(5rem)

View File

@ -7,7 +7,7 @@ interface Props {
} }
const className = Astro.props.class const className = Astro.props.class
--- ---
<div data-pagefind-body class=`prose dark:prose-invert prose-base max-w-none custom-md ${className}`> <div data-pagefind-body class=`prose dark:prose-invert prose-base !max-w-none custom-md ${className}`>
<!--<div class="prose dark:prose-invert max-w-none custom-md">--> <!--<div class="prose dark:prose-invert max-w-none custom-md">-->
<!--<div class="max-w-none custom-md">--> <!--<div class="max-w-none custom-md">-->
<slot/> <slot/>
@ -65,40 +65,6 @@ const className = Astro.props.class
} }
</script> </script>
<!-- Styles for copy-code-button -->
<style lang="css" is:global>
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-regular-dark {
@apply flex items-center justify-center
bg-[oklch(0.45_0.01_var(--hue))] hover:bg-[oklch(0.50_0.01_var(--hue))] active:bg-[oklch(0.55_0.01_var(--hue))]
dark:bg-[oklch(0.30_0.02_var(--hue))] dark:hover:bg-[oklch(0.35_0.03_var(--hue))] dark:active:bg-[oklch(0.40_0.03_var(--hue))]
}
.btn-regular-dark.success {
@apply bg-[oklch(0.75_0.14_var(--hue))] dark:bg-[oklch(0.75_0.14_var(--hue))]
}
.copy-btn-icon {
@apply absolute top-1/2 left-1/2 transition -translate-x-1/2 -translate-y-1/2
}
.copy-btn .copy-icon {
@apply opacity-100 fill-white dark:fill-white/75
}
.copy-btn.success .copy-icon {
@apply opacity-0 fill-[var(--deep-text)]
}
.copy-btn .success-icon {
@apply opacity-0
}
.copy-btn.success .success-icon {
@apply opacity-100
}
}
</style>
<style lang="stylus" is:global> <style lang="stylus" is:global>
.custom-md .custom-md
h1, h2, h3, h4, h5, h6 h1, h2, h3, h4, h5, h6
@ -475,17 +441,4 @@ const className = Astro.props.class
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1)
transition-duration: 0.15s transition-duration: 0.15s
</style>
<style lang="css" is:global>
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.custom-md h1 {
@apply text-3xl
}
}
</style> </style>

View File

@ -2,8 +2,17 @@
import Profile from './Profile.astro' import Profile from './Profile.astro'
import Tag from './Tags.astro' import Tag from './Tags.astro'
import Categories from './Categories.astro' import Categories from './Categories.astro'
import type { MarkdownHeading } from 'astro'
import TOC from './TOC.astro'
interface Props {
class? : string
headings? : MarkdownHeading[]
}
const className = Astro.props.class const className = Astro.props.class
const headings = Astro.props.headings
--- ---
<div id="sidebar" class:list={[className, "w-full"]}> <div id="sidebar" class:list={[className, "w-full"]}>
<div class="flex flex-col w-full gap-4 mb-4"> <div class="flex flex-col w-full gap-4 mb-4">

View File

@ -0,0 +1,75 @@
---
import type { MarkdownHeading } from 'astro';
import { siteConfig } from "../../config";
interface Props {
class?: string
headings: MarkdownHeading[]
}
let { headings = [] } = Astro.props;
// generate random headings, for testing
// headings = [
// { text: 'Heading 1', depth: 1, slug: 'heading-1' },
// { text: 'Heading 2', depth: 2, slug: 'heading-2' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 2', depth: 2, slug: 'heading-2' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 1', depth: 1, slug: 'heading-1' },
// { text: 'Heading 2', depth: 2, slug: 'heading-2' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 2', depth: 2, slug: 'heading-2' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// { text: 'Heading 3', depth: 3, slug: 'heading-3' },
// ]
let minDepth = 10;
for (const heading of headings) {
minDepth = Math.min(minDepth, heading.depth);
}
const className = Astro.props.class
const removeTailingHash = (text: string) => {
let lastIndexOfHash = text.lastIndexOf('#');
if (lastIndexOfHash != text.length - 1) {
return text;
}
return text.substring(0, lastIndexOfHash);
}
let heading1Count = 1;
const maxLevel = siteConfig.toc.depth;
---
<div class:list={[className]}>
{headings.filter((heading) => heading.depth < minDepth + maxLevel).map((heading) =>
<a href={`#${heading.slug}`} class="px-2 flex gap-2 relative transition w-full min-h-9 rounded-xl
hover:bg-[var(--toc-btn-hover)] active:bg-[var(--toc-btn-active)] py-2
">
<div class:list={["w-5 h-5 shrink-0 rounded-lg text-xs flex items-center justify-center font-bold",
{
"bg-[var(--toc-badge-bg)] text-[var(--btn-content)]": heading.depth == minDepth,
"ml-4": heading.depth == minDepth + 1,
"ml-8": heading.depth == minDepth + 2,
}
]}
>
{heading.depth == minDepth && heading1Count++}
{heading.depth == minDepth + 1 && <div class="w-2 h-2 rounded-[0.1875rem] bg-[var(--toc-badge-bg)]"></div>}
{heading.depth == minDepth + 2 && <div class="w-1.5 h-1.5 rounded-sm bg-black/5 dark:bg-white/10"></div>}
</div>
<div class:list={["text-sm", {
"text-50": heading.depth == minDepth || heading.depth == minDepth + 1,
"text-30": heading.depth == minDepth + 2,
}]}>{removeTailingHash(heading.text)}</div>
</a>
)}
</div>

View File

@ -24,6 +24,10 @@ export const siteConfig: SiteConfig = {
url: '' // (Optional) URL link to the original artwork or artist's page url: '' // (Optional) URL link to the original artwork or artist's page
} }
}, },
toc: {
enable: true, // Display the table of contents on the right side of the post
depth: 2 // Maximum heading depth to show in the table, from 1 to 3
},
favicon: [ // Leave this array empty to use the default favicon favicon: [ // Leave this array empty to use the default favicon
// { // {
// src: '/favicon/icon.png', // Path of the favicon, relative to the /public directory // src: '/favicon/icon.png', // Path of the favicon, relative to the /public directory

View File

@ -3,6 +3,17 @@ export const UNCATEGORIZED = '__uncategorized__'
export const PAGE_SIZE = 8 export const PAGE_SIZE = 8
export const LIGHT_MODE = 'light', export const LIGHT_MODE = 'light',
DARK_MODE = 'dark', DARK_MODE = 'dark',
AUTO_MODE = 'auto' AUTO_MODE = 'auto'
export const DEFAULT_THEME = AUTO_MODE export const DEFAULT_THEME = AUTO_MODE
// Banner height unit: vh
export const BANNER_HEIGHT = 35
export const BANNER_HEIGHT_EXTEND = 30
export const BANNER_HEIGHT_HOME = BANNER_HEIGHT + BANNER_HEIGHT_EXTEND
// The height the main panel overlaps the banner, unit: rem
export const MAIN_PANEL_OVERLAPS_BANNER_HEIGHT = 3.5
// Page width: rem
export const PAGE_WIDTH = 75

View File

@ -8,7 +8,7 @@ category: 'Examples'
draft: true draft: true
--- ---
## GitHub repository cards ## GitHub Repository Cards
You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API. You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API.
::github{repo="Fabrizz/MMM-OnSpotify"} ::github{repo="Fabrizz/MMM-OnSpotify"}
@ -43,6 +43,8 @@ Critical content demanding immediate user attention due to potential risks.
Negative potential consequences of an action. Negative potential consequences of an action.
::: :::
### Basic Syntax
```markdown ```markdown
:::note :::note
Highlights information that users should take into account, even when skimming. Highlights information that users should take into account, even when skimming.
@ -53,6 +55,8 @@ Optional information to help a user be more successful.
::: :::
``` ```
### Custom Titles
The title of the admonition can be customized. The title of the admonition can be customized.
:::note[MY CUSTOM TITLE] :::note[MY CUSTOM TITLE]
@ -65,6 +69,8 @@ This is a note with a custom title.
::: :::
``` ```
### GitHub Syntax
> [!TIP] > [!TIP]
> [The GitHub syntax](https://github.com/orgs/community/discussions/16925) is also supported. > [The GitHub syntax](https://github.com/orgs/community/discussions/16925) is also supported.

View File

@ -12,6 +12,10 @@ import {
DARK_MODE, DARK_MODE,
DEFAULT_THEME, DEFAULT_THEME,
LIGHT_MODE, LIGHT_MODE,
BANNER_HEIGHT,
BANNER_HEIGHT_EXTEND,
BANNER_HEIGHT_HOME,
PAGE_WIDTH,
} from '../constants/constants' } from '../constants/constants'
import { defaultFavicons } from '../constants/icon' import { defaultFavicons } from '../constants/icon'
import type { Favicon } from '../types/config' import type { Favicon } from '../types/config'
@ -61,12 +65,12 @@ if (!lang) {
const siteLang = lang.replace('_', '-') const siteLang = lang.replace('_', '-')
const bannerOffsetByPosition = { const bannerOffsetByPosition = {
'top': '30vh', top: `${BANNER_HEIGHT_EXTEND}vh`,
'center': '15vh', center: `${BANNER_HEIGHT_EXTEND / 2}vh`,
'bottom': '0' bottom: '0',
} }
const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'center'] const bannerOffset =
bannerOffsetByPosition[siteConfig.banner.position || 'center']
--- ---
<!DOCTYPE html> <!DOCTYPE html>
@ -105,8 +109,14 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
media={favicon.theme && `(prefers-color-scheme: ${favicon.theme})`} media={favicon.theme && `(prefers-color-scheme: ${favicon.theme})`}
/> />
))} ))}
<style define:vars={{
configHue,
'page-width': `${PAGE_WIDTH}rem`,
}}></style> <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->
<!-- Set the theme before the page is rendered to avoid a flash --> <!-- Set the theme before the page is rendered to avoid a flash -->
<script is:inline define:vars={{DEFAULT_THEME: DEFAULT_THEME, LIGHT_MODE: LIGHT_MODE, DARK_MODE: DARK_MODE, AUTO_MODE: AUTO_MODE}}> <script is:inline define:vars={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, AUTO_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH}}>
const theme = localStorage.getItem('theme') || DEFAULT_THEME; const theme = localStorage.getItem('theme') || DEFAULT_THEME;
switch (theme) { switch (theme) {
case LIGHT_MODE: case LIGHT_MODE:
@ -122,6 +132,17 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
document.documentElement.classList.remove('dark'); document.documentElement.classList.remove('dark');
} }
} }
// calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
offset = offset - offset % 4;
document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
// calculate the width of TOC widget
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize)
const mainGridWidthPx = PAGE_WIDTH * rootFontSize
const tocWidth = (window.innerWidth - mainGridWidthPx) / 2 - rootFontSize
document.documentElement.style.setProperty('--toc-width', `${tocWidth}px`);
</script> </script>
<slot name="head"></slot> <slot name="head"></slot>
@ -130,20 +151,12 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
<link rel="alternate" type="application/rss+xml" title={profileConfig.name} href={`${Astro.site}rss.xml`}/> <link rel="alternate" type="application/rss+xml" title={profileConfig.name} href={`${Astro.site}rss.xml`}/>
<style define:vars={{ configHue }}></style> <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->
</head> </head>
<body class=" min-h-screen transition " class:list={[{"lg:is-home": isHomePage, "enable-banner": enableBanner}]} <body class=" min-h-screen transition " class:list={[{"lg:is-home": isHomePage, "enable-banner": enableBanner}]}
data-overlayscrollbars-initialize data-overlayscrollbars-initialize
> >
<ConfigCarrier></ConfigCarrier> <ConfigCarrier></ConfigCarrier>
<GlobalStyles> <GlobalStyles>
{siteConfig.banner.enable && <div id="banner-wrapper" class="absolute -top-[30vh] w-full transition duration-700 overflow-hidden">
<ImageWrapper id="banner" alt="Banner image of the blog" class:list={["object-cover h-full transition duration-700 opacity-0 scale-105"]}
src={siteConfig.banner.src} position={siteConfig.banner.position}
>
</ImageWrapper>
</div>}
<slot /> <slot />
</GlobalStyles> </GlobalStyles>
@ -151,19 +164,22 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
<div id="page-height-extend" class="hidden h-[300vh]"></div> <div id="page-height-extend" class="hidden h-[300vh]"></div>
</body> </body>
</html> </html>
<style is:global> <style is:global>
:root { :root {
--hue: var(--configHue); --hue: var(--configHue);
--page-width: 75rem;
} }
</style> </style>
<style is:global define:vars={{ bannerOffset }}>
@tailwind components;
@tailwind utilities;
<style is:global define:vars={{
bannerOffset,
'banner-height-home': `${BANNER_HEIGHT_HOME}vh`,
'banner-height': `${BANNER_HEIGHT}vh`,
}}>
@tailwind components;
@layer components { @layer components {
.enable-banner.is-home #banner-wrapper { .enable-banner.is-home #banner-wrapper {
@apply h-[var(--banner-height-home)] translate-y-[30vh] @apply h-[var(--banner-height-home)] translate-y-[var(--banner-height-extend)]
} }
.enable-banner #banner-wrapper { .enable-banner #banner-wrapper {
@apply h-[var(--banner-height-home)] @apply h-[var(--banner-height-home)]
@ -176,19 +192,20 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
@apply h-[var(--banner-height-home)] translate-y-[var(--bannerOffset)] @apply h-[var(--banner-height-home)] translate-y-[var(--bannerOffset)]
} }
.enable-banner.is-home #main-grid { .enable-banner.is-home #main-grid {
@apply translate-y-[30vh]; @apply translate-y-[var(--banner-height-extend)];
} }
.enable-banner #top-row { .enable-banner #top-row {
@apply h-[calc(var(--banner-height-home)_-_4.5rem)] transition-all duration-300 @apply h-[calc(var(--banner-height-home)_-_4.5rem)] transition-all duration-300
} }
.enable-banner.is-home #sidebar-sticky { .enable-banner.is-home #sidebar-sticky {
@apply top-[calc(-30vh_+_1rem)] @apply top-[calc(1rem_-_var(--banner-height-extend))]
} }
.navbar-hidden { .navbar-hidden {
@apply opacity-0 -translate-y-16 @apply opacity-0 -translate-y-16
} }
} }
</style> </style>
<script> <script>
import 'overlayscrollbars/overlayscrollbars.css'; import 'overlayscrollbars/overlayscrollbars.css';
import { import {
@ -199,8 +216,15 @@ import {
} from 'overlayscrollbars'; } from 'overlayscrollbars';
import {getHue, getStoredTheme, setHue, setTheme} from "../utils/setting-utils"; import {getHue, getStoredTheme, setHue, setTheme} from "../utils/setting-utils";
import {pathsEqual, url} from "../utils/url-utils"; import {pathsEqual, url} from "../utils/url-utils";
import {
BANNER_HEIGHT,
BANNER_HEIGHT_HOME,
BANNER_HEIGHT_EXTEND,
MAIN_PANEL_OVERLAPS_BANNER_HEIGHT,
PAGE_WIDTH
} from "../constants/constants";
/* Preload fonts */ /* Preload fonts */
// (async function() { // (async function() {
// try { // try {
// await Promise.all([ // await Promise.all([
@ -342,15 +366,15 @@ const setup = () => {
} }
}) })
*/ */
// Remove the delay for the first time page load
window.swup.hooks.on('link:click', () => { window.swup.hooks.on('link:click', () => {
// Remove the delay for the first time page load
document.documentElement.style.setProperty('--content-delay', '0ms') document.documentElement.style.setProperty('--content-delay', '0ms')
// prevent elements from overlapping the navbar // prevent elements from overlapping the navbar
if (!bannerEnabled) { if (!bannerEnabled) {
return return
} }
let threshold = window.innerHeight * 0.30 - 72 - 16 let threshold = window.innerHeight * (BANNER_HEIGHT / 100) - 72 - 16
let navbar = document.getElementById('navbar-wrapper') let navbar = document.getElementById('navbar-wrapper')
if (!navbar || !document.body.classList.contains('lg:is-home')) { if (!navbar || !document.body.classList.contains('lg:is-home')) {
return return
@ -374,6 +398,12 @@ const setup = () => {
if (heightExtend) { if (heightExtend) {
heightExtend.classList.remove('hidden') heightExtend.classList.remove('hidden')
} }
// Hide the TOC while scrolling back to top
let toc = document.getElementById('toc-wrapper');
if (toc) {
toc.classList.add('toc-not-ready')
}
}); });
window.swup.hooks.on('page:view', () => { window.swup.hooks.on('page:view', () => {
// hide the temp high element when the transition is done // hide the temp high element when the transition is done
@ -383,11 +413,18 @@ const setup = () => {
} }
}); });
window.swup.hooks.on('visit:end', (visit: {to: {url: string}}) => { window.swup.hooks.on('visit:end', (visit: {to: {url: string}}) => {
// execute 1s later setTimeout(() => {
const heightExtend = document.getElementById('page-height-extend') const heightExtend = document.getElementById('page-height-extend')
if (heightExtend) { if (heightExtend) {
heightExtend.classList.add('hidden') heightExtend.classList.add('hidden')
} }
// Just make the transition looks better
const toc = document.getElementById('toc-wrapper');
if (toc) {
toc.classList.remove('toc-not-ready')
}
}, 200)
}); });
} }
if (window?.swup?.hooks) { if (window?.swup?.hooks) {
@ -397,22 +434,37 @@ if (window?.swup?.hooks) {
} }
let backToTopBtn = document.getElementById('back-to-top-btn'); let backToTopBtn = document.getElementById('back-to-top-btn');
let toc = document.getElementById('toc-wrapper');
let navbar = document.getElementById('navbar-wrapper') let navbar = document.getElementById('navbar-wrapper')
function scrollFunction() { function scrollFunction() {
let bannerHeight = window.innerHeight * (BANNER_HEIGHT / 100)
if (backToTopBtn) { if (backToTopBtn) {
if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) { if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
backToTopBtn.classList.remove('hide') backToTopBtn.classList.remove('hide')
} else { } else {
backToTopBtn.classList.add('hide') backToTopBtn.classList.add('hide')
} }
} }
if (bannerEnabled && toc) {
if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
toc.classList.remove('toc-hide')
} else {
toc.classList.add('toc-hide')
}
}
if (!bannerEnabled) return if (!bannerEnabled) return
if (navbar) { if (navbar) {
let threshold = window.innerHeight * 0.30 - 72 - 16 const NAVBAR_HEIGHT = 72
const MAIN_PANEL_EXCESS_HEIGHT = MAIN_PANEL_OVERLAPS_BANNER_HEIGHT * 16 // The height the main panel overlaps the banner
let bannerHeight = BANNER_HEIGHT
if (document.body.classList.contains('lg:is-home') && window.innerWidth >= 1024) { if (document.body.classList.contains('lg:is-home') && window.innerWidth >= 1024) {
threshold = window.innerHeight * 0.60 - 72 - 16 bannerHeight = BANNER_HEIGHT_HOME
} }
let threshold = window.innerHeight * (bannerHeight / 100) - NAVBAR_HEIGHT - MAIN_PANEL_EXCESS_HEIGHT - 16
if (document.body.scrollTop >= threshold || document.documentElement.scrollTop >= threshold) { if (document.body.scrollTop >= threshold || document.documentElement.scrollTop >= threshold) {
navbar.classList.add('navbar-hidden') navbar.classList.add('navbar-hidden')
} else { } else {
@ -422,6 +474,19 @@ function scrollFunction() {
} }
window.onscroll = scrollFunction window.onscroll = scrollFunction
window.onresize = () => {
// calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
offset = offset - offset % 4;
document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
// calculate the width of TOC widget
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize)
const mainGridWidthPx = PAGE_WIDTH * rootFontSize
const tocWidth = (window.innerWidth - mainGridWidthPx) / 2 - rootFontSize
document.documentElement.style.setProperty('--toc-width', `${tocWidth}px`);
}
</script> </script>
<script> <script>

View File

@ -6,6 +6,10 @@ import SideBar from '@components/widget/SideBar.astro'
import Layout from './Layout.astro' import Layout from './Layout.astro'
import { Icon } from 'astro-icon/components' import { Icon } from 'astro-icon/components'
import { siteConfig } from '../config' import { siteConfig } from '../config'
import type { MarkdownHeading } from 'astro'
import TOC from "../components/widget/TOC.astro";
import ImageWrapper from "../components/misc/ImageWrapper.astro";
import {BANNER_HEIGHT, BANNER_HEIGHT_EXTEND, MAIN_PANEL_OVERLAPS_BANNER_HEIGHT} from "../constants/constants";
interface Props { interface Props {
title?: string title?: string
@ -13,15 +17,20 @@ interface Props {
description?: string description?: string
lang?: string lang?: string
setOGTypeArticle?: boolean; setOGTypeArticle?: boolean;
headings? : MarkdownHeading[]
} }
const { title, banner, description, lang, setOGTypeArticle } = Astro.props const { title, banner, description, lang, setOGTypeArticle, headings = [] } = Astro.props
const hasBannerCredit = const hasBannerCredit =
siteConfig.banner.enable && siteConfig.banner.credit.enable siteConfig.banner.enable && siteConfig.banner.credit.enable
const hasBannerLink = !!siteConfig.banner.credit.url const hasBannerLink = !!siteConfig.banner.credit.url
const mainPanelTop = siteConfig.banner.enable ? `calc(${BANNER_HEIGHT}vh - ${MAIN_PANEL_OVERLAPS_BANNER_HEIGHT}rem)` : "5.5rem"
--- ---
<Layout title={title} banner={banner} description={description} lang={lang} setOGTypeArticle={setOGTypeArticle}> <Layout title={title} banner={banner} description={description} lang={lang} setOGTypeArticle={setOGTypeArticle}>
<!-- Navbar -->
<slot slot="head" name="head"></slot> <slot slot="head" name="head"></slot>
<div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-0 md:px-4 mx-auto" class:list={[""]}> <div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-0 md:px-4 mx-auto" class:list={[""]}>
<div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all"> <div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all">
@ -29,8 +38,18 @@ const hasBannerLink = !!siteConfig.banner.credit.url
</div> </div>
</div> </div>
<div class="absolute w-full" class:list={[{"top-[30vh]": siteConfig.banner.enable, "top-[5.5rem]": !siteConfig.banner.enable}]}> <!-- Banner -->
<div class="relative max-w-[var(--page-width)] mx-auto"> {siteConfig.banner.enable && <div id="banner-wrapper" class=`absolute z-10 w-full transition duration-700 overflow-hidden` style=`top: -${BANNER_HEIGHT_EXTEND}vh`>
<ImageWrapper id="banner" alt="Banner image of the blog" class:list={["object-cover h-full transition duration-700 opacity-0 scale-105"]}
src={siteConfig.banner.src} position={siteConfig.banner.position}
>
</ImageWrapper>
</div>}
<!-- Main content -->
<div class="absolute w-full z-30 pointer-events-none" style=`top: ${mainPanelTop}`>
<!-- The pointer-events-none here prevent blocking the click event of the TOC -->
<div class="relative max-w-[var(--page-width)] mx-auto pointer-events-auto">
<div id="main-grid" class="transition duration-700 w-full left-0 right-0 grid grid-cols-[17.5rem_auto] grid-rows-[auto_1fr_auto] lg:grid-rows-[auto] <div id="main-grid" class="transition duration-700 w-full left-0 right-0 grid grid-cols-[17.5rem_auto] grid-rows-[auto_1fr_auto] lg:grid-rows-[auto]
mx-auto gap-4 px-0 md:px-4" mx-auto gap-4 px-0 md:px-4"
> >
@ -48,7 +67,7 @@ const hasBannerLink = !!siteConfig.banner.credit.url
</a>} </a>}
<SideBar class="mb-4 row-start-2 row-end-3 col-span-2 lg:row-start-1 lg:row-end-2 lg:col-span-1 lg:max-w-[17.5rem] onload-animation"></SideBar> <SideBar class="mb-4 row-start-2 row-end-3 col-span-2 lg:row-start-1 lg:row-end-2 lg:col-span-1 lg:max-w-[17.5rem] onload-animation" headings={headings}></SideBar>
<main id="swup-container" class="transition-swup-fade col-span-2 lg:col-span-1 overflow-hidden"> <main id="swup-container" class="transition-swup-fade col-span-2 lg:col-span-1 overflow-hidden">
<div id="content-wrapper" class="onload-animation"> <div id="content-wrapper" class="onload-animation">
@ -65,7 +84,29 @@ const hasBannerLink = !!siteConfig.banner.credit.url
<Footer></Footer> <Footer></Footer>
</div> </div>
</div> </div>
<BackToTop></BackToTop> <BackToTop></BackToTop>
</div> </div>
</div> </div>
<!-- The things that should be under the banner, only the TOC for now -->
<div class="absolute w-full z-0">
<div class="relative max-w-[var(--page-width)] mx-auto">
<!-- TOC component -->
{siteConfig.toc.enable && <div id="toc-wrapper" class:list={["transition absolute top-0 -right-[var(--toc-width)] w-[var(--toc-width)] flex items-center",
{"toc-hide": siteConfig.banner.enable}]}
>
<div id="toc-inner-wrapper" class="fixed top-14 w-[var(--toc-width)] h-[calc(100vh_-_20rem)] overflow-y-scroll overflow-x-hidden hide-scrollbar">
<div id="toc" class="w-full h-full transition-swup-fade ">
<div class="h-8 w-full"></div>
<TOC headings={headings}></TOC>
<div class="h-8 w-full"></div>
</div>
</div>
</div>}
<!-- #toc needs to exist for Swup to work normally -->
{!siteConfig.toc.enable && <div id="toc"></div>}
</div>
</div>
</Layout> </Layout>

View File

@ -25,7 +25,7 @@ export async function getStaticPaths() {
} }
const { entry } = Astro.props const { entry } = Astro.props
const { Content } = await entry.render() const { Content, headings } = await entry.render()
const { remarkPluginFrontmatter } = await entry.render() const { remarkPluginFrontmatter } = await entry.render()
@ -45,7 +45,7 @@ const jsonLd = {
// TODO include cover image here // TODO include cover image here
} }
--- ---
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true}> <MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true} headings={headings}>
<script is:inline slot="head" type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script> <script is:inline slot="head" type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script>
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4"> <div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
<div id="post-container" class:list={["card-base z-10 px-6 md:px-9 pt-6 pb-4 relative w-full ", <div id="post-container" class:list={["card-base z-10 px-6 md:px-9 pt-6 pb-4 relative w-full ",
@ -72,7 +72,7 @@ const jsonLd = {
<div <div
data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title" data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title"
class="transition w-full block font-bold mb-3 class="transition w-full block font-bold mb-3
text-3xl md:text-[2.5rem]/[2.75rem] text-3xl md:text-[2.25rem]/[2.75rem]
text-black/90 dark:text-white/90 text-black/90 dark:text-white/90
md:before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)] md:before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-[0.75rem] before:left-[-1.125rem] before:absolute before:top-[0.75rem] before:left-[-1.125rem]

View File

@ -20,6 +20,10 @@ export type SiteConfig = {
url?: string url?: string
} }
} }
toc: {
enable: boolean
depth: 1 | 2 | 3
}
favicon: Favicon[] favicon: Favicon[]
} }