feat: use .ts instead of .yaml for configurations

This commit is contained in:
saicaca
2023-10-23 17:45:07 +08:00
parent 9f213d525f
commit 1ae4a8eee9
23 changed files with 4259 additions and 375 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB

View File

@@ -1,8 +1,8 @@
---
import {getConfig} from "../utils/config-utils";
import {siteConfig} from "../config";
---
<div id="config-carrier" data-hue={getConfig().appearance.hue}>
<div id="config-carrier" data-hue={siteConfig.themeHue}>
</div>

View File

@@ -1,12 +1,12 @@
---
import {getConfig} from "../utils/config-utils";
import {profileConfig} from "../config";
---
<div class="card-base max-w-[var(--page-width)] min-h-[72px] rounded-b-none mx-auto flex items-center px-6">
<div class="text-black/50 dark:text-white/50 text-sm">
© 2023 {getConfig().profile.author}. All Rights Reserved.
© 2023 {profileConfig.name}. All Rights Reserved.
<br>
Powered by <a class="link text-[var(--primary)]" target="_blank" href="https://github.com/saicaca/fuwari">Fuwari</a>
</div>

View File

@@ -75,6 +75,8 @@ color_set({
--deep-text: oklch(0.25 0.02 var(--hue))
--title-active: oklch(0.6 0.1 var(--hue))
--line-divider: black(0.08) white(0.08)
--line-color: black(0.1) white(0.1)

View File

@@ -2,20 +2,41 @@
import Button from "./control/Button.astro";
import { Icon } from 'astro-icon/components';
import DisplaySetting from "./widget/DisplaySetting.astro";
import {getConfig} from "../utils/config-utils";
import I18nKey from "../i18n/i18nKey";
import {i18n} from "../i18n/translation";
import {LinkPreset, NavBarLink} from "../types/config";
import {navBarConfig, siteConfig} from "../config";
const className = Astro.props.class;
function isI18nKey(key: string): key is I18nKey {
return Object.values(I18nKey).includes(key);
}
function getLinkName(name: string) {
if (isI18nKey(name)) {
return i18n(name);
let links: NavBarLink[] = navBarConfig.links.map((item) => {
if (typeof item === "number") {
return getLinkPresetInfo(item)
}
return item;
});
function getLinkPresetInfo(p: LinkPreset): NavBarLink {
switch (p) {
case LinkPreset.Home:
return {
name: i18n(I18nKey.home),
url: "/page/1"
};
case LinkPreset.Archive:
return {
name: i18n(I18nKey.archive),
url: "/archive"
};
case LinkPreset.About:
return {
name: i18n(I18nKey.about),
url: "/about"
};
}
return name;
}
---
@@ -25,12 +46,12 @@ function getLinkName(name: string) {
<a href="/page/1"><Button height="52px" class="px-5 font-bold rounded-lg active:scale-95" 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" />
{getConfig().title}
{siteConfig.title}
</div>
</Button></a>
<div>
{Object.keys(getConfig().menu).map((key) => {
return <a aria-label={getLinkName(key)} href={getConfig().menu[key]}><Button light class="font-bold px-5 rounded-lg active:scale-95">{getLinkName(key)}</Button></a>
{links.map((l) => {
return <a aria-label={l.name} href={l.url} target={l.external ? "_blank" : null}><Button light class="font-bold px-5 rounded-lg active:scale-95">{l.name}</Button></a>;
})}
</div>
<div class="flex">

View File

@@ -35,6 +35,7 @@ const { remarkPluginFrontmatter } = await entry.render();
class="transition w-full block font-bold mb-3 text-3xl
text-black/90 dark:text-white/90
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute md:before:top-[35px] before:left-[18px]
">
@@ -44,7 +45,7 @@ const { remarkPluginFrontmatter } = await entry.render();
<!-- metadata -->
<PostMetadata published={published} 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">
<div class="transition text-black/75 dark:text-white/75 mb-3.5">
{ description }
</div>
@@ -61,8 +62,8 @@ const { remarkPluginFrontmatter } = await entry.render();
"max-h-[20vh] md:max-h-none mx-4 mt-4 md:mx-0 md:mt-0",
"md:w-[var(--coverWidth)] relative md:absolute md:top-3 md:bottom-3 md:right-3 rounded-xl overflow-hidden active:scale-95"
]} >
<div class="absolute z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div>
<div class="absolute z-20 w-full h-full flex items-center justify-center ">
<div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div>
<div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center ">
<Icon name="material-symbols:chevron-right-rounded"
class="transition opacity-0 group-hover:opacity-100 text-white text-5xl">
</Icon>

View File

@@ -5,11 +5,23 @@ interface Props {
class?: string;
alt?: string
}
import { Image } from 'astro:assets';
const {id, src, alt} = Astro.props;
const className = Astro.props.class;
const isLocal = !(src.startsWith('/') || src.startsWith('http') || src.startsWith('https') || src.startsWith('data:'));
let img;
if (isLocal) {
img = (await import("../../" + src)).default;
}
---
<div class:list={[className, 'overflow-hidden relative']}>
<div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50"></div>
<img src={src} alt={alt} class="w-full h-full object-center object-cover">
<div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div>
{isLocal && <Image src={img} alt={alt || ""} class="w-full h-full object-center object-cover" />}
{!isLocal && <img src={src} alt={alt || ""} class="w-full h-full object-center object-cover" />}
</div>

View File

@@ -1,9 +1,9 @@
---
import {getConfig} from "../../utils/config-utils";
import {i18n} from "../../i18n/translation";
import I18nKey from "../../i18n/i18nKey";
import {siteConfig} from "../../config";
const enableBanner = getConfig().banner.enable;
const enableBanner = siteConfig.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",

View File

@@ -1,30 +1,30 @@
---
import ImageBox from "../misc/ImageBox.astro";
import Button from "../control/Button.astro";
import {getConfig} from "../../utils/config-utils";
import {Icon} from "astro-icon/components";
import {profileConfig} from "../../config";
interface props {
}
const className = Astro.props
const vConf = getConfig();
const config = profileConfig;
---
<div class="card-base">
<a aria-label="Go to About Page" href="/about" class="group block relative m-3 overflow-hidden rounded-xl active:scale-95">
<div class="absolute transition group-hover:bg-black/30 group-active:bg-black/50 w-full h-full z-50 flex items-center justify-center">
<div class="absolute transition pointer-events-none group-hover:bg-black/30 group-active:bg-black/50 w-full h-full z-50 flex items-center justify-center">
<Icon name="material-symbols:person-outline-rounded"
class="transition opacity-0 group-hover:opacity-100 text-white text-6xl">
</Icon>
</div>
<ImageBox src={vConf.profile.avatar} alt="Profile Image of the Author" class="mt-1 mx-auto w-[240px] lg:w-full h-full lg:mt-0 "></ImageBox>
<ImageBox src={config.avatar} alt="Profile Image of the Author" class="mt-1 mx-auto w-[240px] lg:w-full h-full lg:mt-0 "></ImageBox>
</a>
<div class="font-bold text-xl text-center mb-1 dark:text-neutral-50 transition">{vConf.profile.author}</div>
<div class="font-bold text-xl text-center mb-1 dark:text-neutral-50 transition">{config.name}</div>
<div class="h-1 w-5 bg-[var(--primary)] mx-auto rounded-full mb-3 transition"></div>
<div class="text-center text-neutral-400 mb-2 transition">{vConf.profile.subtitle}</div>
<div class="text-center text-neutral-400 mb-2 transition">{config.bio}</div>
<div class="flex gap-2 mx-2 justify-center mb-4">
{vConf.profile.links.map(item =>
{config.links.map(item =>
<a aria-label={item.name} href={item.url} target="_blank">
<Button isIcon iconName={item.icon} regular height="40px" class="rounded-lg active:scale-90"></Button>
</a>

47
src/config.ts Normal file
View File

@@ -0,0 +1,47 @@
import type {NavBarConfig, ProfileConfig, SiteConfig} from "./types/config.ts";
import {LinkPreset} from "./types/config.ts";
export const siteConfig: SiteConfig = {
title: 'Fuwari',
subtitle: 'Demo Site',
lang: 'en-US',
themeHue: 250,
banner: {
enable: true,
src: 'assets/images/demo-banner.png',
}
}
export const navBarConfig: NavBarConfig = {
links: [
LinkPreset.Home,
LinkPreset.Archive,
LinkPreset.About,
{
name: 'GitHub',
url: 'https://github.com/saicaca/fuwari',
external: true,
}
]
}
export const profileConfig: ProfileConfig = {
avatar: 'assets/images/demo-avatar.png',
name: 'Lorem Ipsum',
bio: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
links: [
{
name: 'Twitter',
icon: 'fa6-brands:twitter',
url: 'https://twitter.com',
}, {
name: 'Steam',
icon: 'fa6-brands:steam',
url: 'https://store.steampowered.com',
}, {
name: 'GitHub',
icon: 'fa6-brands:github',
url: 'https://github.com/saicaca/fuwari',
}
]
}

View File

@@ -6,16 +6,17 @@ image: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753
tags: ["Fuwari", "Blogging", "Customization"]
---
# How to set a cover image using the cover attribute
## Set the cover image using the `image` attribute
Select an Image: Before you start, make sure you have an image you want to use as a cover. Let's assume its filename is my-cover-image.jpg and it's located in an images directory at the root of your site.
Edit your article: At the top of your Markdown file, include the frontmatter section. Add a cover attribute and specify the path to your cover image.
Edit your article: At the top of your Markdown file, include the frontmatter section. Set the `image` attribute to the path of your image file.
```markdown
---
title: "Your Article Title"
published: 2023-10-05
cover: "/images/my-cover-image.jpg"
image: "/images/my-cover-image.jpg"
---
```
Web URLs are also supported.

View File

@@ -2,7 +2,7 @@ import {en} from "./languages/en.ts";
import {zh_TW} from "./languages/zh_TW.ts";
import {zh_CN} from "./languages/zh_CN.ts";
import type I18nKey from "./i18nKey.ts";
import {getConfig} from "../utils/config-utils.ts";
import {siteConfig} from "../config.ts";
export type Translation = {
[K in I18nKey]: string;
@@ -25,6 +25,6 @@ export function getTranslation(lang: string): Translation {
}
export function i18n(key: I18nKey): string {
const lang = getConfig().lang || "en";
const lang = siteConfig.lang || "en";
return getTranslation(lang)[key];
}

View File

@@ -7,9 +7,9 @@ import { ViewTransitions } from 'astro:transitions';
import ImageBox from "../components/misc/ImageBox.astro";
import { fade } from 'astro:transitions';
import {getConfig} from "../utils/config-utils";
import {pathsEqual} from "../utils/url-utils";
import ConfigCarrier from "../components/ConfigCarrier.astro";
import {siteConfig} from "../config";
interface Props {
title: string;
@@ -46,18 +46,19 @@ 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 configHue = viConf.appearance.hue;
const configHue = siteConfig.themeHue;
if (!banner || typeof banner !== 'string' || banner.trim() === '') {
banner = viConf.banner.url;
banner = siteConfig.banner.src;
}
// TODO don't use post cover as banner for now
banner = viConf.banner.url;
banner = siteConfig.banner.src;
let pageTitle = getConfig().title;
let pageTitle;
if (title) {
pageTitle = `${title} - ${pageTitle}`;
pageTitle = `${title} - ${siteConfig.title}`;
} else {
pageTitle = `${siteConfig.title} - ${siteConfig.subtitle}`;
}
---
@@ -94,8 +95,8 @@ if (title) {
class:list={{'banner-home': isHomePage, 'banner-else': !isHomePage}}
>
<ImageBox id="boxtest" alt="Banner image of the blog" class:list={["object-center object-cover h-full", {"hidden": !viConf.banner.enable}]}
src={banner} transition:animate="fade"
<ImageBox id="boxtest" alt="Banner image of the blog" class:list={["object-center object-cover h-full", {"hidden": !siteConfig.banner.enable}]}
src={siteConfig.banner.src} transition:animate="fade"
>
</ImageBox>
</div>

View File

@@ -6,7 +6,7 @@ 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";
import {siteConfig} from "../config";
interface Props {
title: string;
@@ -17,8 +17,7 @@ const { title, banner } = Astro.props;
const isHomePage = pathsEqual(Astro.url.pathname, '/') || pathsEqual(Astro.url.pathname, '/page/1');
const enableBanner = getConfig().banner.enable;
const enableBanner = siteConfig.banner.enable;
---

View File

@@ -5,7 +5,6 @@ import ImageBox from "../../components/misc/ImageBox.astro";
import {Icon} from "astro-icon/components";
import PostMetadata from "../../components/PostMetadata.astro";
import Button from "../../components/control/Button.astro";
import {getConfig} from "../../utils/config-utils";
import {i18n} from "../../i18n/translation";
import I18nKey from "../../i18n/i18nKey";
import {getPostUrlBySlug} from "../../utils/url-utils";
@@ -109,16 +108,30 @@ const { remarkPluginFrontmatter } = await entry.render();
<style lang="stylus" is:global>
.custom-md
a
position: relative
background: none
margin: -4px
padding: 4px
border-radius: 6px
color: var(--primary)
text-decoration none
text-decoration-line: none;
/*&:after*/
/* content: ''*/
/* position: absolute*/
/* left: 2px*/
/* right: 2px*/
/* bottom: 4px*/
/* height: 6px*/
/* border-radius: 3px*/
/* background: var(--link-hover)*/
/* transition: background 0.15s ease-in-out;*/
/* z-index: -1;*/
&:hover
background: var(--link-hover)
&:active
background: var(--link-active)
/*&:after*/
/* background: var(--link-active)*/
code
font-family: monospace
background: var(--inline-code-bg)
@@ -132,6 +145,9 @@ const { remarkPluginFrontmatter } = await entry.render();
content: none
pre
background: var(--codeblock-bg) !important
border-radius: 12px
padding-left: 20px
padding-right: 20px
code
padding: 0
background: none
@@ -172,7 +188,7 @@ const { remarkPluginFrontmatter } = await entry.render();
border-color: var(--line-divider)
border-style: dashed
iframe
border-radius: 8px
border-radius: 12px
margin-left: auto
margin-right: auto
max-width: 100%

39
src/types/config.ts Normal file
View File

@@ -0,0 +1,39 @@
export type SiteConfig = {
title: string,
subtitle: string,
lang: string,
themeHue: number,
banner: {
enable: boolean,
src: string,
}
};
export enum LinkPreset {
Home,
Archive,
About,
}
export type NavBarLink = {
name: string,
url: string,
external?: boolean
}
export type NavBarConfig = {
links: (NavBarLink | LinkPreset)[],
}
export type ProfileConfig = {
avatar?: string,
name: string,
bio?: string,
links: {
name: string,
url: string,
icon: string,
}[],
};

View File

@@ -1,40 +0,0 @@
// @ts-ignore
import _config from '../../fuwari.config.yml';
interface FuwariConfig {
title: string;
menu: {
[key: string]: string;
};
lang: string;
appearance: {
hue: number;
};
favicon: string;
banner: {
enable: boolean;
url: string;
position: string;
onAllPages: boolean;
};
sidebar: {
widgets: {
normal: string | string[];
sticky: string | string[];
};
};
profile: {
avatar: string;
author: string;
subtitle: string;
links: {
name: string;
icon: string;
url: string;
}[];
}
}
const config: FuwariConfig = _config;
export const getConfig = () => config;