feat: post styles, next/prev post btn, display-settings, etc.

(cherry picked from commit b7ddd92729d52a8c43d72010b743f7e3477f1001)
This commit is contained in:
saicaca 2023-10-02 01:52:08 +08:00
parent 8ed0aa071f
commit 26408b0b7e
19 changed files with 258 additions and 55 deletions

View File

@ -12,7 +12,6 @@
"dependencies": {
"@astrojs/check": "^0.2.0",
"@astrojs/tailwind": "^4.0.0",
"@astrojs/ts-plugin": "^1.1.3",
"@fontsource/roboto": "^5.0.8",
"astro": "^3.0.10",
"astro-icon": "^1.0.0-next.2",
@ -23,6 +22,7 @@
"typescript": "^5.2.2"
},
"devDependencies": {
"@astrojs/ts-plugin": "^1.1.3",
"@iconify-json/fa6-brands": "^1.1.13",
"@iconify-json/material-symbols": "^1.1.57",
"@rollup/plugin-yaml": "^4.1.1",

View File

@ -11,9 +11,6 @@ dependencies:
'@astrojs/tailwind':
specifier: ^4.0.0
version: 4.0.0(astro@3.0.10)(tailwindcss@3.3.3)
'@astrojs/ts-plugin':
specifier: ^1.1.3
version: 1.1.3
'@fontsource/roboto':
specifier: ^5.0.8
version: 5.0.8
@ -40,6 +37,9 @@ dependencies:
version: 5.2.2
devDependencies:
'@astrojs/ts-plugin':
specifier: ^1.1.3
version: 1.1.3
'@iconify-json/fa6-brands':
specifier: ^1.1.13
version: 1.1.13
@ -103,7 +103,6 @@ packages:
/@astrojs/compiler@1.5.7:
resolution: {integrity: sha512-dFU7GAMbpTUGPkRoCoMQrGFlTe3qIiQMSOxIXp/nB1Do4My9uogjEmBHdR5Cwr4i6rc5/1R3Od9v8kU/pkHXGQ==}
dev: false
/@astrojs/compiler@2.0.1:
resolution: {integrity: sha512-DfBR7Cf+tOgQ4n7TIgTtU5x5SEA/08DNshpEPcT+91A0KbBlmUOYMBM/O6qAaHkmVo1KIoXQYhAmfdTT1zx9PQ==}
@ -216,7 +215,7 @@ packages:
'@volar/language-core': 1.10.1
'@volar/typescript': 1.10.1
vscode-languageserver-textdocument: 1.0.8
dev: false
dev: true
/@babel/code-frame@7.22.13:
resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==}
@ -1117,7 +1116,6 @@ packages:
resolution: {integrity: sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==}
dependencies:
'@volar/source-map': 1.10.1
dev: false
/@volar/language-server@1.10.1:
resolution: {integrity: sha512-UXgRMAPKoy4EZBcBT1SFp8YIb5AJqe7Is1/TnqRUq0LBBV2M7HpEeHHI8E4fy05Eg4TlSVRcrlZtiTrY9fRjJg==}
@ -1148,13 +1146,11 @@ packages:
resolution: {integrity: sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==}
dependencies:
muggle-string: 0.3.1
dev: false
/@volar/typescript@1.10.1:
resolution: {integrity: sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==}
dependencies:
'@volar/language-core': 1.10.1
dev: false
/@vscode/emmet-helper@2.9.2:
resolution: {integrity: sha512-MaGuyW+fa13q3aYsluKqclmh62Hgp0BpKIqS66fCxfOaBcVQ1OnMQxRRgQUYnCkxFISAQlkJ0qWWPyXjro1Qrg==}
@ -3029,7 +3025,6 @@ packages:
/muggle-string@0.3.1:
resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
dev: false
/mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
@ -4328,7 +4323,6 @@ packages:
/vscode-languageserver-textdocument@1.0.8:
resolution: {integrity: sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==}
dev: false
/vscode-languageserver-types@3.17.3:
resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==}

View File

@ -69,7 +69,7 @@ function formatDate(date: Date) {
</div>
{group.posts.map(post => (
<a href={getPostUrlBySlug(post.slug)} class="group">
<Button light height="40px" class="w-full hover:text-[initial]">
<Button light height="40px" class="w-full rounded-lg hover:text-[initial]">
<div class="flex flex-row justify-start items-center h-full">
<!-- date -->
<div class="w-[10%] transition text-sm text-right text-black/50 dark:text-white/50">{formatDate(post.data.pubDate)}</div>

View File

@ -43,6 +43,9 @@ color_set(colors)
if length(value) > 1
{key}: oklch_fallback(value[1])
rainbow-light = linear-gradient(to right, oklch(0.80 0.10 0), oklch(0.80 0.10 30), oklch(0.80 0.10 60), oklch(0.80 0.10 90), oklch(0.80 0.10 120), oklch(0.80 0.10 150), oklch(0.80 0.10 180), oklch(0.80 0.10 210), oklch(0.80 0.10 240), oklch(0.80 0.10 270), oklch(0.80 0.10 300), oklch(0.80 0.10 330), oklch(0.80 0.10 360))
rainbow-dark = linear-gradient(to right, oklch(0.70 0.10 0), oklch(0.70 0.10 30), oklch(0.70 0.10 60), oklch(0.70 0.10 90), oklch(0.70 0.10 120), oklch(0.70 0.10 150), oklch(0.70 0.10 180), oklch(0.70 0.10 210), oklch(0.70 0.10 240), oklch(0.70 0.10 270), oklch(0.70 0.10 300), oklch(0.70 0.10 330), oklch(0.70 0.10 360))
:root
--radius-large 16px
@ -53,7 +56,7 @@ color_set({
--primary: oklch(0.70 0.14 var(--hue))
--card-bg: white oklch(0.25 0.02 var(--hue))
--btn-content: oklch(0.55 0.12 var(--hue))
--btn-content: oklch(0.55 0.12 var(--hue)) oklch(0.75 0.1 var(--hue))
--btn-regular-bg: oklch(0.95 0.025 var(--hue)) oklch(0.38 0.04 var(--hue))
@ -70,6 +73,8 @@ color_set({
--line-color: black(0.1) white(0.1)
--meta-divider: black(0.2) white(0.2)
--selection-bg: oklch(0.90 0.05 var(--hue)) oklch(0.40 0.08 var(--hue))
--color-selection-bar: rainbow-light rainbow-dark
})

View File

@ -1,25 +1,32 @@
---
import Button from "./control/Button.astro";
import { Icon } from 'astro-icon/components';
import DisplaySetting from "./widget/DisplaySetting.astro";
const className = Astro.props.class;
---
<div class:list={[
className,
"card-base max-w-[var(--page-width)] h-[72px] rounded-t-none mx-auto flex items-center justify-between px-4"]}>
<a href="/"><Button height="52px" class="px-5 font-bold" light>
<a href="/"><Button height="52px" class="px-5 font-bold rounded-lg" light>
<div class="flex flex-row text-[var(--primary)] items-center text-md">
<Icon name="material-symbols:home-outline-rounded" size={28} class="mb-1 mr-2" />
<div class="top-2"></div>Vivia Preview
</div>
</Button></a>
<div>
<a href="/"><Button light class="font-bold px-5">Home</Button></a>
<a href="/archive"><Button light class="font-bold px-5">Archive</Button></a>
<a href="/about"><Button light class="font-bold px-5">About</Button></a>
<a href="/"><Button light class="font-bold px-5 rounded-lg">Home</Button></a>
<a href="/archive"><Button light class="font-bold px-5 rounded-lg">Archive</Button></a>
<a href="/about"><Button light class="font-bold px-5 rounded-lg">About</Button></a>
</div>
<div>
<Button id="scheme-switch" iconName="material-symbols:wb-sunny-outline-rounded" iconSize={20} isIcon light></Button>
<div class="flex">
<div>
<Button class="rounded-lg" id="display-settings-switch" iconName="material-symbols:palette-outline" iconSize={20} isIcon light></Button>
</div>
<div>
<Button class="rounded-lg" id="scheme-switch" iconName="material-symbols:wb-sunny-outline-rounded" iconSize={20} isIcon light></Button>
</div>
</div>
<DisplaySetting></DisplaySetting>
</div>
@ -40,13 +47,15 @@ function switchTheme() {
function loadThemeSwitchScript() {
let switchBtn = document.getElementById("scheme-switch");
if (switchBtn === null) {
console.log("test")
}
switchBtn.addEventListener("click", function () {
console.log("test")
switchTheme()
});
let settingBtn = document.getElementById("display-settings-switch");
settingBtn.addEventListener("click", function () {
let settingPanel = document.getElementById("display-setting");
settingPanel.classList.toggle("closed");
});
}
loadThemeSwitchScript();

View File

@ -68,7 +68,7 @@ const className = Astro.props.class;
@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)] dark:text-[var(--primary)] mr-2
text-[var(--btn-content)] mr-2
}
.with-divider {
@apply before:content-['/'] before:mx-[6px] before:text-[var(--meta-divider)] before:text-sm

View File

@ -43,7 +43,7 @@ const { remarkPluginFrontmatter } = await entry.render();
</a>
<!-- metadata -->
<PostMetadata pubDate={pubDate} tags={tags} categories={categories} class="mb-4"></PostMetadata>
<PostMetadata pubDate={pubDate} tags={tags} categories={categories} class:list={{"mb-4": description, "mb-6": !description}}></PostMetadata>
<div class="transition text-black/75 dark:text-white/75 mb-4">
{ description }
@ -67,7 +67,7 @@ const { remarkPluginFrontmatter } = await entry.render();
<Icon name="material-symbols:chevron-right-rounded"
class="transition opacity-0 group-hover:opacity-100 text-white text-5xl"></Icon>
</div>
<ImageBox src="https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg"
<ImageBox src={cover}
class="w-full h-full">
</ImageBox>
</a>}

View File

@ -5,7 +5,7 @@ import Button from "./Button.astro";
<!-- There can't be a filter on parent element, or it will break `fixed` -->
<div class="back-to-top-wrapper" transition:persist>
<div id="back-to-top-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden" onclick="topFunction()">
<div id="back-to-top-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden transition" onclick="topFunction()">
<Button card height="60px" width="60px">
<Icon name="material-symbols:keyboard-arrow-up-rounded" class="mx-auto"></Icon>
</Button>

View File

@ -34,7 +34,6 @@ import { Icon } from 'astro-icon/components';
class:list={[
className,
`
rounded-lg
transition
h-[var(--height)]
`,
@ -60,7 +59,7 @@ import { Icon } from 'astro-icon/components';
'dark:hover:bg-[oklch(0.45_0.045_var(--hue))]': regular,
'dark:active:bg-[oklch(0.5_0.05_var(--hue))]': regular,
'card-base': card,
'bg-[var(--card-bg)]': card,
'enabled:hover:bg-[var(--btn-card-bg-hover)]': card,
'enabled:active:bg-[var(--btn-card-bg-active)]': card,
'disabled:text-black/10': card,

View File

@ -8,7 +8,7 @@ interface Props {
const { size, dot, href }: Props = Astro.props;
---
<a href={href}>
<Button regular height="32px" class="text-[15px] px-3 flex flex-row items-center">
<Button regular height="32px" class="text-[15px] px-3 flex flex-row items-center rounded-lg">
{dot && <div class="h-1 w-1 bg-[var(--btn-content)] dark:bg-[var(--card-bg)] transition rounded-md mr-2"></div>}
<slot></slot>
</Button>

View File

@ -55,7 +55,7 @@ const commonUrl: string = parts.slice(0, -1).join('/') + '/';
<div class:list={[className, "flex flex-row gap-3 justify-center"]}>
<a href={page.url.prev}>
<Button isIcon card iconName="material-symbols:chevron-left-rounded" class="text-[var(--primary)]" iconSize={28}
<Button isIcon card iconName="material-symbols:chevron-left-rounded" class="text-[var(--primary)] rounded-lg" iconSize={28}
disabled = {page.url.prev == undefined}
></Button>
</a>
@ -70,14 +70,14 @@ const commonUrl: string = parts.slice(0, -1).join('/') + '/';
{p}
</div>
return <a href={commonUrl + p}>
<Button card iconName="material-symbols:chevron-left-rounded" height="44px" width="44px">
<Button card iconName="material-symbols:chevron-left-rounded" class="rounded-lg" height="44px" width="44px">
{p}
</Button>
</a>
})}
</div>
<a href={page.url.next}>
<Button isIcon card iconName="material-symbols:chevron-right-rounded" class="text-[var(--primary)]" iconSize={28}
<Button isIcon card iconName="material-symbols:chevron-right-rounded" class="text-[var(--primary)] rounded-lg" iconSize={28}
disabled = {page.url.next == undefined}
></Button>
</a>

View File

@ -0,0 +1,135 @@
---
import {getConfig} from "../../utils/config-utils";
const hueSet: number[] = [0, 30, 60, 180, 250, 270, 300, 330, 345];
const enableBanner = getConfig().banner.enable;
---
<div id="display-setting" class:list={["card-base closed absolute transition-all w-[320px] fixed right-4 border-[var(--primary)] px-4 py-4",
{"border-[3px]": !enableBanner}
]}>
<div class="flex flex-row gap-2 mb-3 items-center justify-between">
<div class="font-bold text-lg text-neutral-900 dark:text-neutral-100 transition relative ml-3
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:left-[-12px] before:top-[5.5px]"
>
Primary Color
</div>
<div id="hueValue" class="transition bg-[var(--btn-regular-bg)] w-10 h-7 rounded-md flex justify-center font-bold transition text-sm items-center text-[var(--btn-content)]">
{0}
</div>
</div>
<div id="preset-list" class="flex flex-row gap-1 mb-4 hidden">
{hueSet.map((hue) => <div
class="h-7 w-8 rounded-md cursor-pointer
bg-[oklch(0.75_0.14_var(--hue))]
hover:bg-[oklch(0.70_0.12_var(--hue))]
active:bg-[oklch(0.65_0.11_var(--hue))]
"
style=`--hue: ${hue}` data-hue={hue}
>
</div>)}
</div>
<div class="w-full h-6 px-1 bg-[oklch(0.80_0.10_0)] dark:bg-[oklch(0.70_0.10_0)] rounded select-none">
<input type="range" min="0" max="360" value="0" class="slider" id="colorSlider" step="5" style="width: 100%;">
</div>
</div>
<script is:raw>
(function () {
let presetList = document.getElementById("preset-list");
let output = document.getElementById("hueValue");
let slider = document.getElementById("colorSlider");
output.innerHTML = slider.value; // Display the default slider value
let r = document.querySelector(':root');
function setHue(hue) {
localStorage.setItem('hue', hue);
output.innerHTML = hue;
slider.value = hue;
r.style.setProperty(`--hue`, hue);
}
let storedHue = localStorage.getItem('hue');
if (storedHue) {
setHue(storedHue);
}
presetList.onclick = function(event) {
let hue = event.target.dataset.hue;
if (hue) {
setHue(hue);
}
}
slider.oninput = function() {
let hue = this.value;
output.innerHTML = this.value;
setHue(hue);
}
document.addEventListener("click", event => {
var cDom = document.getElementById("display-setting");
let settingBtn = document.getElementById("display-settings-switch");
var tDom = event.target;
if (cDom == tDom || cDom.contains(tDom) || settingBtn == tDom || settingBtn.contains(tDom)) {
return;
}
cDom.classList.add("closed");
});
})();
</script>
<style lang="stylus" is:global>
#display-setting
input[type="range"]
-webkit-appearance: none;
height: 24px;
background-image: var(--color-selection-bar)
transition: background-image 0.15s ease-in-out
/* Input Thumb */
::-webkit-slider-thumb
-webkit-appearance: none;
height: 16px;
width: 8px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.7);
box-shadow: none;
::-moz-range-thumb
-webkit-appearance: none;
height: 24px;
width: 10px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.7);
box-shadow: none;
&::-ms-thumb
-webkit-appearance: none;
height: 24px;
width: 10px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.7);
box-shadow: none;
</style>
<style>
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
#display-setting {
@apply top-[84px]
}
#display-setting.closed {
@apply top-[76px] opacity-0 pointer-events-none
}
}
</style>

View File

@ -1,6 +1,6 @@
---
import ImageBox from "../misc/ImageBox.astro";
import ButtonLight from "../control/Button.astro";
import Button from "../control/Button.astro";
import {getConfig} from "../../utils/config-utils";
interface props {
@ -18,7 +18,7 @@ const vConf = getConfig();
<div class="flex gap-2 mx-2 justify-center mb-4">
{vConf.profile.links.map(item =>
<a href={item.url} target="_blank">
<ButtonLight isIcon iconName={item.icon} regular height="40px"></ButtonLight>
<Button isIcon iconName={item.icon} regular height="40px" class="rounded-lg"></Button>
</a>
)}
</div>

View File

@ -46,11 +46,14 @@ const myFade = {
// defines global css variables
// why doing this in Layout instead of GlobalStyles: https://github.com/withastro/astro/issues/6728#issuecomment-1502203757
const viConf = getConfig();
const hue = viConf.appearance.hue;
const configHue = viConf.appearance.hue;
if (!banner || typeof banner !== 'string' || banner.trim() === '') {
banner = viConf.banner.url;
}
// TODO don't use post cover as banner for now
banner = viConf.banner.url;
---
<!DOCTYPE html>
@ -71,7 +74,7 @@ if (!banner || typeof banner !== 'string' || banner.trim() === '') {
<link rel="icon" media="(prefers-color-scheme: dark)" href="/favicon/favicon-dark-180.png" sizes="180x180">
<link rel="icon" media="(prefers-color-scheme: dark)" href="/favicon/favicon-dark-192.png" sizes="192x192">
<style define:vars={{ hue }}></style> <!-- defines global css variables -->
<style define:vars={{ configHue }}></style> <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why -->
<title>{title}</title>
</head>
@ -82,7 +85,7 @@ if (!banner || typeof banner !== 'string' || banner.trim() === '') {
id="banner-wrapper"
>
<!-- TODO the transition here is not correct -->
<ImageBox id="boxtest" class="object-center object-cover h-full"
<ImageBox id="boxtest" class:list={["object-center object-cover h-full", {"hidden": !viConf.banner.enable}]}
src={banner} transition:animate="fade"
>
</ImageBox>
@ -93,6 +96,7 @@ if (!banner || typeof banner !== 'string' || banner.trim() === '') {
</html>
<style is:global>
:root {
--hue: var(--configHue);
--accent: 136, 58, 234;
--accent-light: 224, 204, 250;
--accent-dark: 49, 10, 101;
@ -169,7 +173,13 @@ function loadTheme() {
document.documentElement.classList.remove('dark');
}
}
loadTheme();
function loadHue() {
const hue = localStorage.hue;
if (hue) {
document.documentElement.style.setProperty('--hue', hue);
}
}
function setBannerHeight() {
const banner = document.getElementById('banner-wrapper');
@ -182,10 +192,15 @@ function setBannerHeight() {
}
}
/* Load light/dark mode setting */
/* Load settings when entering the site */
loadTheme();
loadHue();
/* Load settings before swapping */
/* astro:after-swap event happened before swap animation */
document.addEventListener('astro:after-swap', () => {
setBannerHeight();
loadTheme();
loadHue();
}, { once: false });
</script>

View File

@ -5,10 +5,12 @@ import SideBar from "../components/widget/SideBar.astro";
import {pathsEqual} from "../utils/url-utils";
import Footer from "../components/Footer.astro";
import BackToTop from "../components/control/BackToTop.astro";
import DisplaySetting from "../components/widget/DisplaySetting.astro";
import {getConfig} from "../utils/config-utils";
interface Props {
title: string;
banner: string;
banner?: string;
}
const { title, banner } = Astro.props;
@ -18,14 +20,16 @@ const isHomePage = pathsEqual(Astro.url.pathname, '/') || pathsEqual(Astro.url.p
const pageWidth = "1200px";
const sidebarWidth = "280px";
const enableBanner = getConfig().banner.enable;
---
<Layout title={title} banner={banner}>
<div class=`max-w-[1200px] min-h-screen grid grid-cols-[280px_auto] grid-rows-[auto_auto_1fr_auto] lg:grid-rows-[auto_1fr_auto] mx-auto gap-4 relative overflow-hidden `
transition:animate="none"
>
<div id="top-row" class="col-span-2 grid-rows-1" class:list={{
'min-h-[calc(var(--banner-height-home)_-_72px)]': isHomePage,
'min-h-[calc(var(--banner-height)_-_72px)]': !isHomePage}}
<div id="top-row" class="col-span-2 grid-rows-1 z-50" class:list={{
'min-h-[calc(var(--banner-height-home)_-_72px)]': enableBanner && isHomePage,
'min-h-[calc(var(--banner-height)_-_72px)]': enableBanner && !isHomePage,}}
>
<Navbar transition:animate="fade" transition:persist></Navbar>
</div>
@ -37,8 +41,8 @@ const sidebarWidth = "280px";
</div>
<div class="grid-rows-3 col-span-2 mt-4" transition:persist>
<Footer></Footer>
<div class="grid-rows-3 col-span-2 mt-4" transition:persist transition:animate="fade">
<Footer transition:persist></Footer>
</div>
<BackToTop></BackToTop>
</div>

View File

@ -6,8 +6,9 @@ import ImageBox from "../../components/misc/ImageBox.astro";
import {Icon} from "astro-icon/components";
import {formatDateToYYYYMMDD} from "../../utils/date-utils";
import PostMetadata from "../../components/PostMetadata.astro";
// 1. 为每个集合条目生成一个新路径
import {getPostUrlBySlug} from "../../utils/content-utils";
import Button from "../../components/control/Button.astro";
import {getConfig} from "../../utils/config-utils";
export async function getStaticPaths() {
const blogEntries = await getCollection('posts');
@ -15,16 +16,17 @@ export async function getStaticPaths() {
params: { slug: entry.slug }, props: { entry },
}));
}
// 2. 当渲染的时候,你可以直接从属性中得到条目
const { entry } = Astro.props;
const { Content } = await entry.render();
const { remarkPluginFrontmatter } = await entry.render();
const enableBanner = getConfig().banner.enable;
---
<MainGridLayout banner={entry.data.cover}>
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative">
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
<div class:list={["card-base z-10 px-9 py-6 relative w-full ",
{}
]}>
@ -59,15 +61,39 @@ const { remarkPluginFrontmatter } = await entry.render();
categories={entry.data.categories}
></PostMetadata>
<div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>
<!-- always show cover as long as it has one -->
{entry.data.cover &&
<ImageBox src={entry.data.cover} class="mb-8 rounded-xl"/>
}
{!entry.data.cover && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>}
<div class="prose dark:prose-invert max-w-none prose-h1:text-3xl">
<Content />
</div>
</div>
</div>
<div class="flex justify-between gap-4 overflow-hidden w-full">
<a href={getPostUrlBySlug(entry.data.prevSlug)} class="w-full font-bold overflow-hidden">
{entry.data.prevSlug && <Button class="w-full max-w-full h-10 px-4 rounded-2xl flex items-center justify-start gap-4" card height="60px">
<Icon name="material-symbols:chevron-left-rounded" size={32} class="text-[var(--primary)]" />
<div class="overflow-hidden overflow-ellipsis whitespace-nowrap max-w-[calc(100%_-_48px)] text-black/75 dark:text-white/75">
{entry.data.prevTitle}
</div>
</Button>}
</a>
<a href={getPostUrlBySlug(entry.data.nextSlug)} class="w-full font-bold overflow-hidden">
{entry.data.nextSlug && <Button class="w-full max-w-full h-10 px-4 rounded-2xl flex items-center justify-end gap-4" card height="60px">
<div class="overflow-hidden overflow-ellipsis whitespace-nowrap max-w-[calc(100%_-_48px)] text-black/75 dark:text-white/75">
{entry.data.nextTitle}
</div>
<Icon name="material-symbols:chevron-right-rounded" size={32} class="text-[var(--primary)]" />
</Button>}
</a>
</div>
</MainGridLayout>

View File

@ -1,12 +1,23 @@
import {getCollection} from "astro:content";
import {CollectionEntry, getCollection} from "astro:content";
export async function getSortedPosts() {
const allBlogPosts = await getCollection("posts");
return allBlogPosts.sort((a, b) => {
const sorted = allBlogPosts.sort((a, b) => {
const dateA = new Date(a.data.pubDate);
const dateB = new Date(b.data.pubDate);
return dateA > dateB ? -1 : 1;
});
for (let i = 1; i < sorted.length; i++) {
sorted[i].data.nextSlug = sorted[i - 1].slug;
sorted[i].data.nextTitle = sorted[i - 1].data.title;
}
for (let i = 0; i < sorted.length - 1; i++) {
sorted[i].data.prevSlug = sorted[i + 1].slug;
sorted[i].data.prevTitle = sorted[i + 1].data.title;
}
return sorted;
}
export function getPostUrlBySlug(slug: string): string {

View File

@ -8,5 +8,9 @@
"name": "@astrojs/ts-plugin"
}
]
}
},
"include": [
"src/**/**",
"src/**/**/**",
]
}

View File

@ -2,6 +2,7 @@ appearance:
hue: 250
banner:
enable: true
url: https://saicaca.github.io/vivia-preview/assets/banner.jpg
profile: