Merge branch 'saicaca:main' into main
This commit is contained in:
commit
7f59fffbcd
121
astro.config.mjs
121
astro.config.mjs
@ -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: {
|
||||
|
@ -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
4102
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
225
src/base.css
Normal file
225
src/base.css
Normal 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);
|
||||
}
|
@ -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>
|
@ -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>
|
@ -67,9 +67,6 @@ let links: NavBarLink[] = navBarConfig.links.map(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="stylus">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
function switchTheme() {
|
||||
|
@ -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>
|
@ -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>
|
@ -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)
|
||||
|
@ -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>
|
@ -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">
|
||||
|
75
src/components/widget/TOC.astro
Normal file
75
src/components/widget/TOC.astro
Normal 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>
|
@ -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
|
||||
|
@ -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
|
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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]
|
||||
|
@ -20,6 +20,10 @@ export type SiteConfig = {
|
||||
url?: string
|
||||
}
|
||||
}
|
||||
toc: {
|
||||
enable: boolean
|
||||
depth: 1 | 2 | 3
|
||||
}
|
||||
|
||||
favicon: Favicon[]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user