This commit is contained in:
2025-07-18 21:37:37 +08:00
commit a009f760d6
114 changed files with 17824 additions and 0 deletions

114
src/utils/content-utils.ts Normal file
View File

@@ -0,0 +1,114 @@
import { type CollectionEntry, getCollection } from "astro:content";
import I18nKey from "@i18n/i18nKey";
import { i18n } from "@i18n/translation";
import { getCategoryUrl } from "@utils/url-utils.ts";
// // Retrieve posts and sort them by publication date
async function getRawSortedPosts() {
const allBlogPosts = await getCollection("posts", ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
});
const sorted = allBlogPosts.sort((a, b) => {
const dateA = new Date(a.data.published);
const dateB = new Date(b.data.published);
return dateA > dateB ? -1 : 1;
});
return sorted;
}
export async function getSortedPosts() {
const sorted = await getRawSortedPosts();
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 type PostForList = {
slug: string;
data: CollectionEntry<"posts">["data"];
};
export async function getSortedPostsList(): Promise<PostForList[]> {
const sortedFullPosts = await getRawSortedPosts();
// delete post.body
const sortedPostsList = sortedFullPosts.map((post) => ({
slug: post.slug,
data: post.data,
}));
return sortedPostsList;
}
export type Tag = {
name: string;
count: number;
};
export async function getTagList(): Promise<Tag[]> {
const allBlogPosts = await getCollection<"posts">("posts", ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
});
const countMap: { [key: string]: number } = {};
allBlogPosts.map((post: { data: { tags: string[] } }) => {
post.data.tags.map((tag: string) => {
if (!countMap[tag]) countMap[tag] = 0;
countMap[tag]++;
});
});
// sort tags
const keys: string[] = Object.keys(countMap).sort((a, b) => {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
return keys.map((key) => ({ name: key, count: countMap[key] }));
}
export type Category = {
name: string;
count: number;
url: string;
};
export async function getCategoryList(): Promise<Category[]> {
const allBlogPosts = await getCollection<"posts">("posts", ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
});
const count: { [key: string]: number } = {};
allBlogPosts.map((post: { data: { category: string | null } }) => {
if (!post.data.category) {
const ucKey = i18n(I18nKey.uncategorized);
count[ucKey] = count[ucKey] ? count[ucKey] + 1 : 1;
return;
}
const categoryName =
typeof post.data.category === "string"
? post.data.category.trim()
: String(post.data.category).trim();
count[categoryName] = count[categoryName] ? count[categoryName] + 1 : 1;
});
const lst = Object.keys(count).sort((a, b) => {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
const ret: Category[] = [];
for (const c of lst) {
ret.push({
name: c,
count: count[c],
url: getCategoryUrl(c),
});
}
return ret;
}

3
src/utils/date-utils.ts Normal file
View File

@@ -0,0 +1,3 @@
export function formatDateToYYYYMMDD(date: Date): string {
return date.toISOString().substring(0, 10);
}

View File

@@ -0,0 +1,61 @@
import {
AUTO_MODE,
DARK_MODE,
DEFAULT_THEME,
LIGHT_MODE,
} from "@constants/constants.ts";
import { expressiveCodeConfig } from "@/config";
import type { LIGHT_DARK_MODE } from "@/types/config";
export function getDefaultHue(): number {
const fallback = "250";
const configCarrier = document.getElementById("config-carrier");
return Number.parseInt(configCarrier?.dataset.hue || fallback);
}
export function getHue(): number {
const stored = localStorage.getItem("hue");
return stored ? Number.parseInt(stored) : getDefaultHue();
}
export function setHue(hue: number): void {
localStorage.setItem("hue", String(hue));
const r = document.querySelector(":root") as HTMLElement;
if (!r) {
return;
}
r.style.setProperty("--hue", String(hue));
}
export function applyThemeToDocument(theme: LIGHT_DARK_MODE) {
switch (theme) {
case LIGHT_MODE:
document.documentElement.classList.remove("dark");
break;
case DARK_MODE:
document.documentElement.classList.add("dark");
break;
case AUTO_MODE:
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
break;
}
// Set the theme for Expressive Code
document.documentElement.setAttribute(
"data-theme",
expressiveCodeConfig.theme,
);
}
export function setTheme(theme: LIGHT_DARK_MODE): void {
localStorage.setItem("theme", theme);
applyThemeToDocument(theme);
}
export function getStoredTheme(): LIGHT_DARK_MODE {
return (localStorage.getItem("theme") as LIGHT_DARK_MODE) || DEFAULT_THEME;
}

44
src/utils/url-utils.ts Normal file
View File

@@ -0,0 +1,44 @@
import I18nKey from "@i18n/i18nKey";
import { i18n } from "@i18n/translation";
export function pathsEqual(path1: string, path2: string) {
const normalizedPath1 = path1.replace(/^\/|\/$/g, "").toLowerCase();
const normalizedPath2 = path2.replace(/^\/|\/$/g, "").toLowerCase();
return normalizedPath1 === normalizedPath2;
}
function joinUrl(...parts: string[]): string {
const joined = parts.join("/");
return joined.replace(/\/+/g, "/");
}
export function getPostUrlBySlug(slug: string): string {
return url(`/posts/${slug}/`);
}
export function getTagUrl(tag: string): string {
if (!tag) return url("/archive/");
return url(`/archive/?tag=${encodeURIComponent(tag.trim())}`);
}
export function getCategoryUrl(category: string | null): string {
if (
!category ||
category.trim() === "" ||
category.trim().toLowerCase() === i18n(I18nKey.uncategorized).toLowerCase()
)
return url("/archive/?uncategorized=true");
return url(`/archive/?category=${encodeURIComponent(category.trim())}`);
}
export function getDir(path: string): string {
const lastSlashIndex = path.lastIndexOf("/");
if (lastSlashIndex < 0) {
return "/";
}
return path.substring(0, lastSlashIndex + 1);
}
export function url(path: string) {
return joinUrl("", import.meta.env.BASE_URL, path);
}