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

View File

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

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
--radius-large 1rem
--banner-height-home 65vh
--banner-height 35vh
--content-delay 150ms
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-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)
--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);
</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>

View File

@ -67,9 +67,6 @@ let links: NavBarLink[] = navBarConfig.links.map(
</div>
</div>
<style lang="stylus">
</style>
<script>
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>}
</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>
</div>

View File

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

View File

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

View File

@ -7,7 +7,7 @@ interface Props {
}
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="max-w-none custom-md">-->
<slot/>
@ -65,40 +65,6 @@ const className = Astro.props.class
}
</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>
.custom-md
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-duration: 0.15s
</style>
<style lang="css" is:global>
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.custom-md h1 {
@apply text-3xl
}
}
</style>

View File

@ -2,8 +2,17 @@
import Profile from './Profile.astro'
import Tag from './Tags.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 headings = Astro.props.headings
---
<div id="sidebar" class:list={[className, "w-full"]}>
<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
}
},
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
// {
// 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 LIGHT_MODE = 'light',
DARK_MODE = 'dark',
AUTO_MODE = 'auto'
DARK_MODE = 'dark',
AUTO_MODE = 'auto'
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
---
## 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.
::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.
:::
### Basic Syntax
```markdown
:::note
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.
:::note[MY CUSTOM TITLE]
@ -65,6 +69,8 @@ This is a note with a custom title.
:::
```
### GitHub Syntax
> [!TIP]
> [The GitHub syntax](https://github.com/orgs/community/discussions/16925) is also supported.

View File

@ -12,6 +12,10 @@ import {
DARK_MODE,
DEFAULT_THEME,
LIGHT_MODE,
BANNER_HEIGHT,
BANNER_HEIGHT_EXTEND,
BANNER_HEIGHT_HOME,
PAGE_WIDTH,
} from '../constants/constants'
import { defaultFavicons } from '../constants/icon'
import type { Favicon } from '../types/config'
@ -61,12 +65,12 @@ if (!lang) {
const siteLang = lang.replace('_', '-')
const bannerOffsetByPosition = {
'top': '30vh',
'center': '15vh',
'bottom': '0'
top: `${BANNER_HEIGHT_EXTEND}vh`,
center: `${BANNER_HEIGHT_EXTEND / 2}vh`,
bottom: '0',
}
const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'center']
const bannerOffset =
bannerOffsetByPosition[siteConfig.banner.position || 'center']
---
<!DOCTYPE html>
@ -105,8 +109,14 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
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 -->
<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;
switch (theme) {
case LIGHT_MODE:
@ -122,6 +132,17 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
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>
<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`}/>
<style define:vars={{ configHue }}></style> <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->
</head>
<body class=" min-h-screen transition " class:list={[{"lg:is-home": isHomePage, "enable-banner": enableBanner}]}
data-overlayscrollbars-initialize
>
<ConfigCarrier></ConfigCarrier>
<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 />
</GlobalStyles>
@ -151,19 +164,22 @@ const bannerOffset = bannerOffsetByPosition[siteConfig.banner.position || 'cente
<div id="page-height-extend" class="hidden h-[300vh]"></div>
</body>
</html>
<style is:global>
:root {
--hue: var(--configHue);
--page-width: 75rem;
}
</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 {
.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 {
@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)]
}
.enable-banner.is-home #main-grid {
@apply translate-y-[30vh];
@apply translate-y-[var(--banner-height-extend)];
}
.enable-banner #top-row {
@apply h-[calc(var(--banner-height-home)_-_4.5rem)] transition-all duration-300
}
.enable-banner.is-home #sidebar-sticky {
@apply top-[calc(-30vh_+_1rem)]
@apply top-[calc(1rem_-_var(--banner-height-extend))]
}
.navbar-hidden {
@apply opacity-0 -translate-y-16
}
}
</style>
<script>
import 'overlayscrollbars/overlayscrollbars.css';
import {
@ -199,8 +216,15 @@ import {
} from 'overlayscrollbars';
import {getHue, getStoredTheme, setHue, setTheme} from "../utils/setting-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() {
// try {
// await Promise.all([
@ -342,15 +366,15 @@ const setup = () => {
}
})
*/
// Remove the delay for the first time page load
window.swup.hooks.on('link:click', () => {
// Remove the delay for the first time page load
document.documentElement.style.setProperty('--content-delay', '0ms')
// prevent elements from overlapping the navbar
if (!bannerEnabled) {
return
}
let threshold = window.innerHeight * 0.30 - 72 - 16
let threshold = window.innerHeight * (BANNER_HEIGHT / 100) - 72 - 16
let navbar = document.getElementById('navbar-wrapper')
if (!navbar || !document.body.classList.contains('lg:is-home')) {
return
@ -374,6 +398,12 @@ const setup = () => {
if (heightExtend) {
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', () => {
// 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}}) => {
// execute 1s later
const heightExtend = document.getElementById('page-height-extend')
if (heightExtend) {
heightExtend.classList.add('hidden')
}
setTimeout(() => {
const heightExtend = document.getElementById('page-height-extend')
if (heightExtend) {
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) {
@ -397,22 +434,37 @@ if (window?.swup?.hooks) {
}
let backToTopBtn = document.getElementById('back-to-top-btn');
let toc = document.getElementById('toc-wrapper');
let navbar = document.getElementById('navbar-wrapper')
function scrollFunction() {
let bannerHeight = window.innerHeight * (BANNER_HEIGHT / 100)
if (backToTopBtn) {
if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) {
if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
backToTopBtn.classList.remove('hide')
} else {
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 (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) {
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) {
navbar.classList.add('navbar-hidden')
} else {
@ -422,6 +474,19 @@ function 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>

View File

@ -6,6 +6,10 @@ import SideBar from '@components/widget/SideBar.astro'
import Layout from './Layout.astro'
import { Icon } from 'astro-icon/components'
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 {
title?: string
@ -13,15 +17,20 @@ interface Props {
description?: string
lang?: string
setOGTypeArticle?: boolean;
headings? : MarkdownHeading[]
}
const { title, banner, description, lang, setOGTypeArticle } = Astro.props
const { title, banner, description, lang, setOGTypeArticle, headings = [] } = Astro.props
const hasBannerCredit =
siteConfig.banner.enable && siteConfig.banner.credit.enable
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}>
<!-- Navbar -->
<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="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all">
@ -29,8 +38,18 @@ const hasBannerLink = !!siteConfig.banner.credit.url
</div>
</div>
<div class="absolute w-full" class:list={[{"top-[30vh]": siteConfig.banner.enable, "top-[5.5rem]": !siteConfig.banner.enable}]}>
<div class="relative max-w-[var(--page-width)] mx-auto">
<!-- Banner -->
{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]
mx-auto gap-4 px-0 md:px-4"
>
@ -48,7 +67,7 @@ const hasBannerLink = !!siteConfig.banner.credit.url
</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">
<div id="content-wrapper" class="onload-animation">
@ -65,7 +84,29 @@ const hasBannerLink = !!siteConfig.banner.credit.url
<Footer></Footer>
</div>
</div>
<BackToTop></BackToTop>
</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>

View File

@ -25,7 +25,7 @@ export async function getStaticPaths() {
}
const { entry } = Astro.props
const { Content } = await entry.render()
const { Content, headings } = await entry.render()
const { remarkPluginFrontmatter } = await entry.render()
@ -45,7 +45,7 @@ const jsonLd = {
// 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>
<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 ",
@ -72,7 +72,7 @@ const jsonLd = {
<div
data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title"
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
md:before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-[0.75rem] before:left-[-1.125rem]

View File

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