feat: add license block

This commit is contained in:
saicaca 2023-11-03 14:10:54 +08:00
parent a20c647495
commit c642691dc8
13 changed files with 111 additions and 8 deletions

View File

@ -27,7 +27,8 @@ export default defineConfig({
icon({ icon({
include: { include: {
'material-symbols': ['*'], 'material-symbols': ['*'],
'fa6-brands': ['*'] 'fa6-brands': ['*'],
'fa6-regular': ['*']
} }
}) })
], ],

View File

@ -30,6 +30,7 @@
"devDependencies": { "devDependencies": {
"@astrojs/ts-plugin": "^1.1.3", "@astrojs/ts-plugin": "^1.1.3",
"@iconify-json/fa6-brands": "^1.1.13", "@iconify-json/fa6-brands": "^1.1.13",
"@iconify-json/fa6-regular": "^1.1.13",
"@iconify-json/material-symbols": "^1.1.59", "@iconify-json/material-symbols": "^1.1.59",
"@rollup/plugin-yaml": "^4.1.2", "@rollup/plugin-yaml": "^4.1.2",
"@tailwindcss/typography": "^0.5.10", "@tailwindcss/typography": "^0.5.10",

View File

@ -58,6 +58,9 @@ devDependencies:
'@iconify-json/fa6-brands': '@iconify-json/fa6-brands':
specifier: ^1.1.13 specifier: ^1.1.13
version: 1.1.13 version: 1.1.13
'@iconify-json/fa6-regular':
specifier: ^1.1.13
version: 1.1.13
'@iconify-json/material-symbols': '@iconify-json/material-symbols':
specifier: ^1.1.59 specifier: ^1.1.59
version: 1.1.59 version: 1.1.59
@ -981,6 +984,12 @@ packages:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
dev: true dev: true
/@iconify-json/fa6-regular@1.1.13:
resolution: {integrity: sha512-wY5nEeWwg+fdDzfPdzOHcbgiN7Qk2bTyO0BreJUQJb8NKPcXivyHAlkWPc+wA0kQwfqrmekVCwWnrLoD7B9T0g==}
dependencies:
'@iconify/types': 2.0.0
dev: true
/@iconify-json/material-symbols@1.1.59: /@iconify-json/material-symbols@1.1.59:
resolution: {integrity: sha512-azzJVyS2AKQ4cxIUUmeMHaTVlU9aWM6M98NEqf8luZ1RJpVWuDdSjExY+x58Y39wPFTf+6hgvS4pRwCJRdpohw==} resolution: {integrity: sha512-azzJVyS2AKQ4cxIUUmeMHaTVlU9aWM6M98NEqf8luZ1RJpVWuDdSjExY+x58Y39wPFTf+6hgvS4pRwCJRdpohw==}
dependencies: dependencies:

View File

@ -88,6 +88,8 @@ color_set({
--codeblock-selection: oklch(0.40 0.08 var(--hue)) --codeblock-selection: oklch(0.40 0.08 var(--hue))
--codeblock-bg: oklch(0.2 0.015 var(--hue)) oklch(0.17 0.015 var(--hue)) --codeblock-bg: oklch(0.2 0.015 var(--hue)) oklch(0.17 0.015 var(--hue))
--license-block-bg: black(0.03) var(--codeblock-bg)
--link-hover: oklch(0.95 0.025 var(--hue)) oklch(0.40 0.08 var(--hue)) --link-hover: oklch(0.95 0.025 var(--hue)) oklch(0.40 0.08 var(--hue))
--link-active: oklch(0.90 0.05 var(--hue)) oklch(0.35 0.07 var(--hue)) --link-active: oklch(0.90 0.05 var(--hue)) oklch(0.35 0.07 var(--hue))

View File

@ -0,0 +1,47 @@
---
import {formatDateToYYYYMMDD} from "../../utils/date-utils";
interface Props {
title: string;
slug: string;
pubDate: Date;
class: string;
}
const { title, slug, pubDate } = Astro.props;
const className = Astro.props.class;
import { Icon } from 'astro-icon/components';
import {licenseConfig, profileConfig} from "../../config";
import {getFullUrl, getPostUrlBySlug} from "../../utils/url-utils";
import {i18n} from "../../i18n/translation";
import I18nKey from "../../i18n/i18nKey";
const profileConf = profileConfig;
const licenseConf = licenseConfig;
const postUrl = getFullUrl(getPostUrlBySlug(slug));
---
<div class=`relative transition overflow-hidden bg-[var(--license-block-bg)] py-5 px-6 ${className}`>
<div class="transition font-bold text-black/75 dark:text-white/75">
{title}
</div>
<a href={postUrl} class="link text-[var(--primary)]">
{postUrl}
</a>
<div class="flex gap-6 mt-2">
<div>
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.author)}</div>
<div class="transition text-black/75 dark:text-white/75">{profileConf.name}</div>
</div>
<div>
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.publishedAt)}</div>
<div class="transition text-black/75 dark:text-white/75">{formatDateToYYYYMMDD(pubDate)}</div>
</div>
<div>
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.license)}</div>
<a href={licenseConf.url} target="_blank" class="link text-[var(--primary)]">{licenseConf.name}</a>
</div>
</div>
<Icon name="fa6-brands:creative-commons" class="transition absolute right-6 top-1/2 -translate-y-1/2 text-black/5 dark:text-white/5" size="240"></Icon>
</div>

View File

@ -1,10 +1,11 @@
import type {NavBarConfig, ProfileConfig, SiteConfig} from "./types/config.ts"; import type {LicenseConfig, NavBarConfig, ProfileConfig, SiteConfig} from "./types/config.ts";
import {LinkPreset} from "./types/config.ts"; import {LinkPreset} from "./types/config.ts";
export const siteConfig: SiteConfig = { export const siteConfig: SiteConfig = {
title: 'Fuwari', title: 'Fuwari',
subtitle: 'Demo Site', subtitle: 'Demo Site',
lang: 'en-US', url: 'https://fuwari.vercel.app/',
lang: 'en',
themeHue: 250, themeHue: 250,
banner: { banner: {
enable: true, enable: true,
@ -45,3 +46,9 @@ export const profileConfig: ProfileConfig = {
} }
] ]
} }
export const licenseConfig: LicenseConfig = {
enable: true,
name: 'CC BY-NC-SA 4.0',
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
}

View File

@ -23,6 +23,10 @@ enum I18nKey {
primaryColor = "primaryColor", primaryColor = "primaryColor",
more = "more", more = "more",
author = "author",
publishedAt = "publishedAt",
license = "license",
} }
export default I18nKey; export default I18nKey;

View File

@ -26,4 +26,8 @@ export const en: Translation = {
[Key.primaryColor]: "Primary Color", [Key.primaryColor]: "Primary Color",
[Key.more]: "More", [Key.more]: "More",
[Key.author]: "Author",
[Key.publishedAt]: "Published at",
[Key.license]: "License",
}; };

View File

@ -26,4 +26,8 @@ export const zh_CN: Translation = {
[Key.primaryColor]: "主题色", [Key.primaryColor]: "主题色",
[Key.more]: "更多", [Key.more]: "更多",
[Key.author]: "作者",
[Key.publishedAt]: "发布于",
[Key.license]: "许可协议",
}; };

View File

@ -26,4 +26,8 @@ export const zh_TW: Translation = {
[Key.primaryColor]: "主題色", [Key.primaryColor]: "主題色",
[Key.more]: "更多", [Key.more]: "更多",
[Key.author]: "作者",
[Key.publishedAt]: "發佈於",
[Key.license]: "許可協議",
}; };

View File

@ -8,7 +8,8 @@ import Button from "../../components/control/Button.astro";
import {i18n} from "../../i18n/translation"; import {i18n} from "../../i18n/translation";
import I18nKey from "../../i18n/i18nKey"; import I18nKey from "../../i18n/i18nKey";
import {getPostUrlBySlug} from "../../utils/url-utils"; import {getPostUrlBySlug} from "../../utils/url-utils";
import CommentLayout from "../../components/comment/CommentLayout.astro"; import License from "../../components/misc/License.astro";
import {licenseConfig} from "../../config";
export async function getStaticPaths() { export async function getStaticPaths() {
const blogEntries = await getCollection('posts'); const blogEntries = await getCollection('posts');
@ -25,7 +26,7 @@ const { remarkPluginFrontmatter } = await entry.render();
--- ---
<MainGridLayout banner={entry.data.image} title={entry.data.title}> <MainGridLayout banner={entry.data.image} title={entry.data.title}>
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4"> <div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
<div class:list={["card-base z-10 px-4 md:px-9 pt-6 pb-10 relative w-full ", <div class:list={["card-base z-10 px-4 md:px-9 pt-6 pb-4 relative w-full ",
{} {}
]}> ]}>
<!-- word count and reading time --> <!-- word count and reading time -->
@ -73,13 +74,15 @@ const { remarkPluginFrontmatter } = await entry.render();
{!entry.data.image && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>} {!entry.data.image && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>}
<div class="prose dark:prose-invert prose-sm md:prose-base max-w-none custom-md <div class="mb-6 prose dark:prose-invert prose-sm md:prose-base max-w-none custom-md
"> ">
<!--<div class="prose dark:prose-invert max-w-none custom-md">--> <!--<div class="prose dark:prose-invert max-w-none custom-md">-->
<!--<div class="max-w-none custom-md">--> <!--<div class="max-w-none custom-md">-->
<Content /> <Content />
</div> </div>
{licenseConfig.enable && <License title={entry.data.title} slug={entry.slug} pubDate={entry.data.published} class="mb-6 rounded-xl"></License>}
</div> </div>
</div> </div>
@ -183,7 +186,7 @@ const { remarkPluginFrontmatter } = await entry.render();
&:after &:after
content: none content: none
img img
border-radius: 8px border-radius: 12px
hr hr
border-color: var(--line-divider) border-color: var(--line-divider)
border-style: dashed border-style: dashed

View File

@ -1,6 +1,7 @@
export type SiteConfig = { export type SiteConfig = {
title: string, title: string,
subtitle: string, subtitle: string,
url: string,
lang: string, lang: string,
@ -37,3 +38,9 @@ export type ProfileConfig = {
icon: string, icon: string,
}[], }[],
}; };
export type LicenseConfig = {
enable: boolean;
name: string,
url: string,
}

View File

@ -1,9 +1,16 @@
import {siteConfig} from "../config.ts";
export function pathsEqual(path1: string, path2: string) { export function pathsEqual(path1: string, path2: string) {
const normalizedPath1 = path1.replace(/^\/|\/$/g, '').toLowerCase(); const normalizedPath1 = path1.replace(/^\/|\/$/g, '').toLowerCase();
const normalizedPath2 = path2.replace(/^\/|\/$/g, '').toLowerCase(); const normalizedPath2 = path2.replace(/^\/|\/$/g, '').toLowerCase();
return normalizedPath1 === normalizedPath2; return normalizedPath1 === normalizedPath2;
} }
function joinUrl(...parts: string[]): string {
const joined = parts.join('/');
return joined.replace(/([^:]\/)\/+/g, '$1');
}
export function getPostUrlBySlug(slug: string): string | null { export function getPostUrlBySlug(slug: string): string | null {
if (!slug) if (!slug)
return null; return null;
@ -16,3 +23,6 @@ export function getCategoryUrl(category: string): string | null {
return `/archive/category/${category}`; return `/archive/category/${category}`;
} }
export function getFullUrl(path: string): string {
return joinUrl(siteConfig.url, path);
}