feat: add FrontMatter CMS, biome, translation, etc.
* add Frontmatter CMS * add biome * update * update * fixed & add docs * fix translation.ts * fix translation
This commit is contained in:
parent
f9a78b3e3b
commit
197d524b53
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"taxonomy": {
|
||||
"tags": [
|
||||
"Blogging",
|
||||
"Customization",
|
||||
"Demo",
|
||||
"Example",
|
||||
"Fuwari",
|
||||
"Markdown",
|
||||
"Video"
|
||||
],
|
||||
"categories": []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["biomejs.biome", "astro-build.astro-vscode"]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit",
|
||||
"quickfix.biome": "always",
|
||||
"source.organizeImports.biome": "always"
|
||||
},
|
||||
"frontMatter.dashboard.openOnStart": false
|
||||
}
|
16
README.md
16
README.md
|
@ -25,7 +25,7 @@ Fuwari (not the final name maybe) is a static blog template built with [Astro](h
|
|||
|
||||
1. [Generate a new repository](https://github.com/saicaca/fuwari/generate) from this template.
|
||||
2. Edit the config file `src/config.ts` to customize your blog.
|
||||
3. Run `npm run new-post -- <filename>` to create a new post and edit it in `src/content/posts/`.
|
||||
3. Run `pnpm run new-post -- <filename>` to create a new post and edit it in `src/content/posts/`.
|
||||
4. Deploy your blog to Vercel, Netlify, GitHub Pages, etc. following [the guides](https://docs.astro.build/en/guides/deploy/).
|
||||
|
||||
## ⚙️ Frontmatter of Posts
|
||||
|
@ -47,10 +47,10 @@ All commands are run from the root of the project, from a terminal:
|
|||
|
||||
| Command | Action |
|
||||
|:---------------------------------|:-------------------------------------------------|
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
| `npm run new-post -- <filename>` | Create a new post |
|
||||
| `pnpm install` | Installs dependencies |
|
||||
| `pnpm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `pnpm run build` | Build your production site to `./dist/` |
|
||||
| `pnpm run preview` | Preview your build locally, before deploying |
|
||||
| `pnpm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `pnpm run astro -- --help` | Get help using the Astro CLI |
|
||||
| `pnpm run new-post -- <filename>` | Create a new post |
|
||||
|
|
109
astro.config.mjs
109
astro.config.mjs
|
@ -1,57 +1,74 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import yaml from '@rollup/plugin-yaml';
|
||||
import icon from "astro-icon";
|
||||
import tailwind from "@astrojs/tailwind"
|
||||
import yaml from "@rollup/plugin-yaml"
|
||||
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 rehypeKatex from "rehype-katex"
|
||||
import rehypeSlug from "rehype-slug"
|
||||
import remarkMath from "remark-math"
|
||||
import { remarkReadingTime } from "./src/plugins/remark-reading-time.mjs"
|
||||
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
import {remarkReadingTime} from "./src/plugins/remark-reading-time.mjs";
|
||||
|
||||
import rehypeKatex from "rehype-katex";
|
||||
|
||||
import Color from 'colorjs.io';
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
|
||||
// https://astro.build/config
|
||||
|
||||
|
||||
const oklchToHex = function (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"});
|
||||
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",
|
||||
})
|
||||
}
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://fuwari.vercel.app/',
|
||||
base: '/',
|
||||
site: "https://fuwari.vercel.app/",
|
||||
base: "/",
|
||||
integrations: [
|
||||
tailwind(),
|
||||
icon({
|
||||
include: {
|
||||
'material-symbols': ['*'],
|
||||
'fa6-brands': ['*'],
|
||||
'fa6-regular': ['*'],
|
||||
'fa6-solid': ['*']
|
||||
}
|
||||
})
|
||||
"material-symbols": ["*"],
|
||||
"fa6-brands": ["*"],
|
||||
"fa6-regular": ["*"],
|
||||
"fa6-solid": ["*"],
|
||||
},
|
||||
}),
|
||||
Compress({
|
||||
Image: false,
|
||||
}),
|
||||
],
|
||||
markdown: {
|
||||
remarkPlugins: [remarkMath, remarkReadingTime],
|
||||
rehypePlugins: [rehypeKatex, rehypeSlug,
|
||||
[rehypeAutolinkHeadings, {
|
||||
behavior: 'append',
|
||||
properties: {className: ['anchor']},
|
||||
rehypePlugins: [
|
||||
rehypeKatex,
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
behavior: "append",
|
||||
properties: {
|
||||
className: ["anchor"],
|
||||
},
|
||||
content: {
|
||||
type: 'element',
|
||||
tagName: 'span',
|
||||
properties: {className: ['anchor-icon']},
|
||||
children: [{type: 'text', value: '#'}]
|
||||
}}]]
|
||||
type: "element",
|
||||
tagName: "span",
|
||||
properties: {
|
||||
className: ["anchor-icon"],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "text",
|
||||
value: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
redirects: {
|
||||
'/': '/page/1',
|
||||
"/": "/page/1",
|
||||
},
|
||||
vite: {
|
||||
plugins: [yaml()],
|
||||
|
@ -59,10 +76,10 @@ export default defineConfig({
|
|||
preprocessorOptions: {
|
||||
stylus: {
|
||||
define: {
|
||||
oklchToHex: oklchToHex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
oklchToHex: oklchToHex,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.4.1/schema.json",
|
||||
"extends": [],
|
||||
"files": { "ignoreUnknown": true },
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"formatWithErrors": false,
|
||||
"ignore": [],
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80
|
||||
},
|
||||
"javascript": {
|
||||
"parser": {
|
||||
"unsafeParameterDecoratorsEnabled": true
|
||||
},
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"jsxQuoteStyle": "single",
|
||||
"trailingComma": "all",
|
||||
"semicolons": "asNeeded",
|
||||
"arrowParentheses": "asNeeded"
|
||||
}
|
||||
},
|
||||
"json": {
|
||||
"parser": { "allowComments": true },
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"ignore": [],
|
||||
"rules": {
|
||||
"a11y": {
|
||||
"recommended": true
|
||||
},
|
||||
"complexity": {
|
||||
"recommended": true
|
||||
},
|
||||
"correctness": {
|
||||
"recommended": true
|
||||
},
|
||||
"performance": {
|
||||
"recommended": true
|
||||
},
|
||||
"security": {
|
||||
"recommended": true
|
||||
},
|
||||
"style": {
|
||||
"recommended": true
|
||||
},
|
||||
"suspicious": {
|
||||
"recommended": true
|
||||
},
|
||||
"nursery": {
|
||||
"recommended": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
|
||||
"frontMatter.framework.id": "astro",
|
||||
"frontMatter.preview.host": "http://localhost:4321",
|
||||
"frontMatter.content.publicFolder": "public",
|
||||
"frontMatter.content.pageFolders": [
|
||||
{
|
||||
"title": "posts",
|
||||
"path": "[[workspace]]/src/content/posts"
|
||||
}
|
||||
],
|
||||
"frontMatter.taxonomy.contentTypes": [
|
||||
{
|
||||
"name": "default",
|
||||
"pageBundle": true,
|
||||
"previewPath": "'blog'",
|
||||
"filePrefix": null,
|
||||
"clearEmpty": true,
|
||||
"fields": [
|
||||
{
|
||||
"title": "title",
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"single": true
|
||||
},
|
||||
{
|
||||
"title": "description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "published",
|
||||
"name": "published",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "preview",
|
||||
"name": "image",
|
||||
"type": "image",
|
||||
"isPreviewImage": true
|
||||
},
|
||||
{
|
||||
"title": "tags",
|
||||
"name": "tags",
|
||||
"type": "list"
|
||||
},
|
||||
{
|
||||
"title": "category",
|
||||
"name": "category",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "draft",
|
||||
"name": "draft",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
|
@ -8,37 +8,41 @@
|
|||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"new-post": "node scripts/new-post.js"
|
||||
"new-post": "node scripts/new-post.js",
|
||||
"format": "biome format --write ./src",
|
||||
"lint": "biome check --apply ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.2.0",
|
||||
"@astrojs/tailwind": "^5.0.2",
|
||||
"@astrojs/vue": "^3.0.1",
|
||||
"@fontsource-variable/jetbrains-mono": "^5.0.18",
|
||||
"@astrojs/check": "^0.3.4",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@astrojs/vue": "^4.0.8",
|
||||
"@fontsource-variable/jetbrains-mono": "^5.0.19",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"astro": "^3.5.0",
|
||||
"astro-icon": "1.0.0-next.2",
|
||||
"astro": "^4.1.1",
|
||||
"astro-icon": "1.0.2",
|
||||
"colorjs.io": "^0.4.5",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"overlayscrollbars": "^2.4.4",
|
||||
"overlayscrollbars": "^2.4.6",
|
||||
"reading-time": "^1.5.0",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"sharp": "^0.32.6",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tailwindcss": "^3.3.7",
|
||||
"typescript": "^5.2.2",
|
||||
"valine": "^1.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/ts-plugin": "^1.1.3",
|
||||
"@iconify-json/fa6-brands": "^1.1.13",
|
||||
"@iconify-json/fa6-regular": "^1.1.13",
|
||||
"@iconify-json/fa6-solid": "^1.1.15",
|
||||
"@iconify-json/material-symbols": "^1.1.59",
|
||||
"@astrojs/ts-plugin": "^1.3.1",
|
||||
"@biomejs/biome": "1.4.1",
|
||||
"@iconify-json/fa6-brands": "^1.1.18",
|
||||
"@iconify-json/fa6-regular": "^1.1.18",
|
||||
"@iconify-json/fa6-solid": "^1.1.20",
|
||||
"@iconify-json/material-symbols": "^1.1.69",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"astro-compress": "github:astro-community/AstroCompress#no-sharp",
|
||||
"stylus": "^0.59.0"
|
||||
}
|
||||
}
|
||||
|
|
3541
pnpm-lock.yaml
3541
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,41 +1,40 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
function getDate() {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0'); //月份从0开始,所以要加1
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0") //月份从0开始,所以要加1
|
||||
const day = String(today.getDate()).padStart(2, "0")
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error(`Error: No filename argument provided
|
||||
Usage: npm run new-post -- <filename>`);
|
||||
process.exit(1); // Terminate the script and return error code 1
|
||||
Usage: npm run new-post -- <filename>`)
|
||||
process.exit(1) // Terminate the script and return error code 1
|
||||
}
|
||||
|
||||
let fileName = args[0];
|
||||
let fileName = args[0]
|
||||
|
||||
// Add .md extension if not present
|
||||
const fileExtensionRegex = /\.(md|mdx)$/i;
|
||||
const fileExtensionRegex = /\.(md|mdx)$/i
|
||||
if (!fileExtensionRegex.test(fileName)) {
|
||||
fileName += '.md';
|
||||
fileName += ".md"
|
||||
}
|
||||
|
||||
const targetDir = './src/content/posts/';
|
||||
const fullPath = path.join(targetDir, fileName);
|
||||
const targetDir = "./src/content/posts/"
|
||||
const fullPath = path.join(targetDir, fileName)
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
console.error(`Error:File ${fullPath} already exists `);
|
||||
process.exit(1);
|
||||
console.error(`Error:File ${fullPath} already exists `)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const content =
|
||||
`---
|
||||
const content = `---
|
||||
title: ${args[0]}
|
||||
published: ${getDate()}
|
||||
description:
|
||||
|
@ -43,8 +42,8 @@ image:
|
|||
tags: []
|
||||
category:
|
||||
---
|
||||
`;
|
||||
`
|
||||
|
||||
fs.writeFileSync(path.join(targetDir, fileName), content);
|
||||
fs.writeFileSync(path.join(targetDir, fileName), content)
|
||||
|
||||
console.log(`Post ${fullPath} created`);
|
||||
console.log(`Post ${fullPath} created`)
|
||||
|
|
|
@ -11,6 +11,7 @@ interface Props {
|
|||
image: string;
|
||||
description: string;
|
||||
words: number;
|
||||
draft: boolean;
|
||||
}
|
||||
const { entry, title, url, published, tags, category, image, description, words } = Astro.props;
|
||||
const className = Astro.props.class;
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import type {LicenseConfig, NavBarConfig, ProfileConfig, SiteConfig} from "./types/config.ts";
|
||||
import {LinkPreset} from "./types/config.ts";
|
||||
import type {
|
||||
LicenseConfig,
|
||||
NavBarConfig,
|
||||
ProfileConfig,
|
||||
SiteConfig,
|
||||
} from './types/config.ts'
|
||||
import { LinkPreset } from './types/config.ts'
|
||||
|
||||
export const siteConfig: SiteConfig = {
|
||||
title: 'Fuwari',
|
||||
|
@ -9,7 +14,7 @@ export const siteConfig: SiteConfig = {
|
|||
banner: {
|
||||
enable: true,
|
||||
src: 'assets/images/demo-banner.png',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const navBarConfig: NavBarConfig = {
|
||||
|
@ -21,8 +26,8 @@ export const navBarConfig: NavBarConfig = {
|
|||
name: 'GitHub',
|
||||
url: 'https://github.com/saicaca/fuwari',
|
||||
external: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const profileConfig: ProfileConfig = {
|
||||
|
@ -34,16 +39,18 @@ export const profileConfig: ProfileConfig = {
|
|||
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',
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const licenseConfig: LicenseConfig = {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { z, defineCollection } from "astro:content";
|
||||
import { defineCollection, z } from 'astro:content'
|
||||
|
||||
const blogCollection = defineCollection({
|
||||
const postsCollection = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
published: z.date(),
|
||||
draft: z.boolean(),
|
||||
description: z.string().optional(),
|
||||
image: z.string().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
category: z.string().optional(),
|
||||
})
|
||||
}),
|
||||
})
|
||||
export const collections = {
|
||||
'blog': blogCollection,
|
||||
posts: postsCollection,
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: 'Cover Image Example'
|
||||
title: "Cover Image Example"
|
||||
published: 2023-09-01
|
||||
description: 'How to set a cover image using the cover attribute.'
|
||||
image: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg'
|
||||
description: "How to set a cover image using the cover attribute."
|
||||
image: "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg"
|
||||
tags: ["Fuwari", "Blogging", "Customization"]
|
||||
category:
|
||||
category: test
|
||||
draft: false
|
||||
---
|
||||
|
||||
## Set the cover image using the `image` attribute
|
||||
|
@ -20,4 +21,5 @@ published: 2023-10-05
|
|||
image: "/images/my-cover-image.jpg"
|
||||
---
|
||||
```
|
||||
|
||||
Web URLs are also supported.
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
title: Draft Example
|
||||
published: 2024-01-11T04:40:26.381Z
|
||||
tags: [Markdown, Blogging, Demo]
|
||||
category: Example
|
||||
draft: true
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
# This Article is a Draft
|
||||
|
||||
This article is currently in a draft state and is not published. Therefore, it will not be visible to the general audience. The content is still a work in progress and may require further editing and review.
|
||||
|
||||
When the article is ready for publication, you can update the "draft" field to "false" in the Frontmatter:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Draft Example
|
||||
published: 2024-01-11T04:40:26.381Z
|
||||
tags: [Markdown, Blogging, Demo]
|
||||
category: Example
|
||||
draft: false
|
||||
---
|
|
@ -2,22 +2,21 @@
|
|||
title: Markdown Example
|
||||
published: 2023-10-01
|
||||
description: A simple example of a Markdown blog post.
|
||||
image:
|
||||
tags: [Markdown, Blogging, Demo]
|
||||
category: Example
|
||||
draft: false
|
||||
---
|
||||
|
||||
An h1 header
|
||||
============
|
||||
# An h1 header
|
||||
|
||||
Paragraphs are separated by a blank line.
|
||||
|
||||
2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
|
||||
2nd paragraph. _Italic_, **bold**, and `monospace`. Itemized lists
|
||||
look like:
|
||||
|
||||
* this one
|
||||
* that one
|
||||
* the other one
|
||||
- this one
|
||||
- that one
|
||||
- the other one
|
||||
|
||||
Note that --- not considering the asterisk --- the actual text
|
||||
content starts at 4-columns in.
|
||||
|
@ -32,10 +31,7 @@ Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
|
|||
in chapters 12--14"). Three dots ... will be converted to an ellipsis.
|
||||
Unicode is supported. ☺
|
||||
|
||||
|
||||
|
||||
An h2 header
|
||||
------------
|
||||
## An h2 header
|
||||
|
||||
Here's a numbered list:
|
||||
|
||||
|
@ -52,35 +48,33 @@ from the left side). Here's a code sample:
|
|||
As you probably guessed, indented 4 spaces. By the way, instead of
|
||||
indenting the block, you can use delimited blocks, if you like:
|
||||
|
||||
~~~
|
||||
```
|
||||
define foobar() {
|
||||
print "Welcome to flavor country!";
|
||||
}
|
||||
~~~
|
||||
```
|
||||
|
||||
(which makes copying & pasting easier). You can optionally mark the
|
||||
delimited block for Pandoc to syntax highlight it:
|
||||
|
||||
~~~python
|
||||
```python
|
||||
import time
|
||||
# Quick, count to ten!
|
||||
for i in range(10):
|
||||
# (but not *too* quick)
|
||||
time.sleep(0.5)
|
||||
print i
|
||||
~~~
|
||||
```
|
||||
|
||||
|
||||
|
||||
### An h3 header ###
|
||||
### An h3 header
|
||||
|
||||
Now a nested list:
|
||||
|
||||
1. First, get these ingredients:
|
||||
|
||||
* carrots
|
||||
* celery
|
||||
* lentils
|
||||
- carrots
|
||||
- celery
|
||||
- lentils
|
||||
|
||||
2. Boil some water.
|
||||
|
||||
|
@ -109,7 +103,9 @@ doc](#an-h2-header). Here's a footnote [^1].
|
|||
Tables can look like this:
|
||||
|
||||
size material color
|
||||
---- ------------ ------------
|
||||
|
||||
---
|
||||
|
||||
9 leather brown
|
||||
10 hemp canvas natural
|
||||
11 glass transparent
|
||||
|
@ -119,9 +115,12 @@ Table: Shoes, their sizes, and what they're made of
|
|||
(The above is the caption for the table.) Pandoc also supports
|
||||
multi-line tables:
|
||||
|
||||
-------- -----------------------
|
||||
---
|
||||
|
||||
keyword text
|
||||
-------- -----------------------
|
||||
|
||||
---
|
||||
|
||||
red Sunsets, apples, and
|
||||
other red or reddish
|
||||
things.
|
||||
|
@ -129,11 +128,12 @@ things.
|
|||
green Leaves, grass, frogs
|
||||
and other things it's
|
||||
not easy being.
|
||||
-------- -----------------------
|
||||
|
||||
---
|
||||
|
||||
A horizontal rule follows.
|
||||
|
||||
***
|
||||
---
|
||||
|
||||
Here's a definition list:
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
title: Include Video in the Posts
|
||||
published: 2022-08-01
|
||||
description: This post demonstrates how to include embedded video in a blog post.
|
||||
image:
|
||||
tags: [Example, Video]
|
||||
category: Example
|
||||
draft: false
|
||||
---
|
||||
|
||||
Just copy the embed code from YouTube or other platforms, and paste it in the markdown file.
|
||||
|
||||
```yaml
|
||||
|
@ -19,7 +20,9 @@ published: 2023-10-19
|
|||
```
|
||||
|
||||
## YouTube
|
||||
|
||||
<iframe width="100%" height="468" src="https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||
|
||||
## Bilibili
|
||||
|
||||
<iframe width="100%" height="468" src="//player.bilibili.com/player.html?bvid=BV1fK4y1s7Qf&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
|
|
@ -0,0 +1,2 @@
|
|||
/// <reference types="astro/client" />
|
||||
/// <reference path="../.astro/types.d.ts" />
|
|
@ -1,4 +1,4 @@
|
|||
declare module "*.yml" {
|
||||
const value: any;
|
||||
export default value;
|
||||
declare module '*.yml' {
|
||||
const value: unknown
|
||||
export default value
|
||||
}
|
|
@ -1,32 +1,32 @@
|
|||
enum I18nKey {
|
||||
home = "home",
|
||||
about = "about",
|
||||
archive = "archive",
|
||||
home = 'home',
|
||||
about = 'about',
|
||||
archive = 'archive',
|
||||
|
||||
tags = "tags",
|
||||
categories = "categories",
|
||||
recentPosts = "recentPosts",
|
||||
tags = 'tags',
|
||||
categories = 'categories',
|
||||
recentPosts = 'recentPosts',
|
||||
|
||||
comments = "comments",
|
||||
comments = 'comments',
|
||||
|
||||
untitled = "untitled",
|
||||
uncategorized = "uncategorized",
|
||||
noTags = "noTags",
|
||||
untitled = 'untitled',
|
||||
uncategorized = 'uncategorized',
|
||||
noTags = 'noTags',
|
||||
|
||||
wordCount = "wordCount",
|
||||
wordsCount = "wordsCount",
|
||||
minuteCount = "minuteCount",
|
||||
minutesCount = "minutesCount",
|
||||
postCount = "postCount",
|
||||
postsCount = "postsCount",
|
||||
wordCount = 'wordCount',
|
||||
wordsCount = 'wordsCount',
|
||||
minuteCount = 'minuteCount',
|
||||
minutesCount = 'minutesCount',
|
||||
postCount = 'postCount',
|
||||
postsCount = 'postsCount',
|
||||
|
||||
primaryColor = "primaryColor",
|
||||
primaryColor = 'primaryColor',
|
||||
|
||||
more = "more",
|
||||
more = 'more',
|
||||
|
||||
author = "author",
|
||||
publishedAt = "publishedAt",
|
||||
license = "license",
|
||||
author = 'author',
|
||||
publishedAt = 'publishedAt',
|
||||
license = 'license',
|
||||
}
|
||||
|
||||
export default I18nKey;
|
||||
export default I18nKey
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
import type { Translation } from "../translation.ts";
|
||||
import Key from "../i18nKey.ts";
|
||||
import Key from '../i18nKey.ts'
|
||||
import type { Translation } from '../translation.ts'
|
||||
|
||||
export const en: Translation = {
|
||||
[Key.home]: "Home",
|
||||
[Key.about]: "About",
|
||||
[Key.archive]: "Archive",
|
||||
[Key.home]: 'Home',
|
||||
[Key.about]: 'About',
|
||||
[Key.archive]: 'Archive',
|
||||
|
||||
[Key.tags]: "Tags",
|
||||
[Key.categories]: "Categories",
|
||||
[Key.recentPosts]: "Recent Posts",
|
||||
[Key.tags]: 'Tags',
|
||||
[Key.categories]: 'Categories',
|
||||
[Key.recentPosts]: 'Recent Posts',
|
||||
|
||||
[Key.comments]: "Comments",
|
||||
[Key.comments]: 'Comments',
|
||||
|
||||
[Key.untitled]: "Untitled",
|
||||
[Key.uncategorized]: "Uncategorized",
|
||||
[Key.noTags]: "No Tags",
|
||||
[Key.untitled]: 'Untitled',
|
||||
[Key.uncategorized]: 'Uncategorized',
|
||||
[Key.noTags]: 'No Tags',
|
||||
|
||||
[Key.wordCount]: "word",
|
||||
[Key.wordsCount]: "words",
|
||||
[Key.minuteCount]: "minute",
|
||||
[Key.minutesCount]: "minutes",
|
||||
[Key.postCount]: "post",
|
||||
[Key.postsCount]: "posts",
|
||||
[Key.wordCount]: 'word',
|
||||
[Key.wordsCount]: 'words',
|
||||
[Key.minuteCount]: 'minute',
|
||||
[Key.minutesCount]: 'minutes',
|
||||
[Key.postCount]: 'post',
|
||||
[Key.postsCount]: 'posts',
|
||||
|
||||
[Key.primaryColor]: "Primary Color",
|
||||
[Key.primaryColor]: 'Primary Color',
|
||||
|
||||
[Key.more]: "More",
|
||||
[Key.more]: 'More',
|
||||
|
||||
[Key.author]: "Author",
|
||||
[Key.publishedAt]: "Published at",
|
||||
[Key.license]: "License",
|
||||
};
|
||||
[Key.author]: 'Author',
|
||||
[Key.publishedAt]: 'Published at',
|
||||
[Key.license]: 'License',
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import Key from '../i18nKey.ts'
|
||||
import type { Translation } from '../translation.ts'
|
||||
|
||||
export const ja: Translation = {
|
||||
[Key.home]: 'Home',
|
||||
[Key.about]: 'About',
|
||||
[Key.archive]: 'Archive',
|
||||
|
||||
[Key.tags]: 'タグ',
|
||||
[Key.categories]: 'カテゴリ',
|
||||
[Key.recentPosts]: '最近の投稿',
|
||||
|
||||
[Key.comments]: 'コメント',
|
||||
|
||||
[Key.untitled]: 'タイトルなし',
|
||||
[Key.uncategorized]: 'カテゴリなし',
|
||||
[Key.noTags]: 'タグなし',
|
||||
|
||||
[Key.wordCount]: '文字',
|
||||
[Key.wordsCount]: '文字',
|
||||
[Key.minuteCount]: '分',
|
||||
[Key.minutesCount]: '分',
|
||||
[Key.postCount]: 'post',
|
||||
[Key.postsCount]: 'posts',
|
||||
|
||||
[Key.primaryColor]: '原色',
|
||||
|
||||
[Key.more]: 'もっと',
|
||||
|
||||
[Key.author]: '作者',
|
||||
[Key.publishedAt]: '公開日',
|
||||
[Key.license]: 'ライセンス',
|
||||
}
|
|
@ -1,33 +1,33 @@
|
|||
import type { Translation } from "../translation.ts";
|
||||
import Key from "../i18nKey.ts";
|
||||
import Key from '../i18nKey.ts'
|
||||
import type { Translation } from '../translation.ts'
|
||||
|
||||
export const zh_CN: Translation = {
|
||||
[Key.home]: "主页",
|
||||
[Key.about]: "关于",
|
||||
[Key.archive]: "归档",
|
||||
[Key.home]: '主页',
|
||||
[Key.about]: '关于',
|
||||
[Key.archive]: '归档',
|
||||
|
||||
[Key.tags]: "标签",
|
||||
[Key.categories]: "分类",
|
||||
[Key.recentPosts]: "最新文章",
|
||||
[Key.tags]: '标签',
|
||||
[Key.categories]: '分类',
|
||||
[Key.recentPosts]: '最新文章',
|
||||
|
||||
[Key.comments]: "评论",
|
||||
[Key.comments]: '评论',
|
||||
|
||||
[Key.untitled]: "无标题",
|
||||
[Key.uncategorized]: "未分类",
|
||||
[Key.noTags]: "无标签",
|
||||
[Key.untitled]: '无标题',
|
||||
[Key.uncategorized]: '未分类',
|
||||
[Key.noTags]: '无标签',
|
||||
|
||||
[Key.wordCount]: "字",
|
||||
[Key.wordsCount]: "字",
|
||||
[Key.minuteCount]: "分钟",
|
||||
[Key.minutesCount]: "分钟",
|
||||
[Key.postCount]: "篇文章",
|
||||
[Key.postsCount]: "篇文章",
|
||||
[Key.wordCount]: '字',
|
||||
[Key.wordsCount]: '字',
|
||||
[Key.minuteCount]: '分钟',
|
||||
[Key.minutesCount]: '分钟',
|
||||
[Key.postCount]: '篇文章',
|
||||
[Key.postsCount]: '篇文章',
|
||||
|
||||
[Key.primaryColor]: "主题色",
|
||||
[Key.primaryColor]: '主题色',
|
||||
|
||||
[Key.more]: "更多",
|
||||
[Key.more]: '更多',
|
||||
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "发布于",
|
||||
[Key.license]: "许可协议",
|
||||
};
|
||||
[Key.author]: '作者',
|
||||
[Key.publishedAt]: '发布于',
|
||||
[Key.license]: '许可协议',
|
||||
}
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
import type { Translation } from "../translation.ts";
|
||||
import Key from "../i18nKey.ts";
|
||||
import Key from '../i18nKey.ts'
|
||||
import type { Translation } from '../translation.ts'
|
||||
|
||||
export const zh_TW: Translation = {
|
||||
[Key.home]: "首頁",
|
||||
[Key.about]: "關於",
|
||||
[Key.archive]: "彙整",
|
||||
[Key.home]: '首頁',
|
||||
[Key.about]: '關於',
|
||||
[Key.archive]: '彙整',
|
||||
|
||||
[Key.tags]: "標籤",
|
||||
[Key.categories]: "分類",
|
||||
[Key.recentPosts]: "最新文章",
|
||||
[Key.tags]: '標籤',
|
||||
[Key.categories]: '分類',
|
||||
[Key.recentPosts]: '最新文章',
|
||||
|
||||
[Key.comments]: "評論",
|
||||
[Key.comments]: '評論',
|
||||
|
||||
[Key.untitled]: "無標題",
|
||||
[Key.uncategorized]: "未分類",
|
||||
[Key.noTags]: "無標籤",
|
||||
[Key.untitled]: '無標題',
|
||||
[Key.uncategorized]: '未分類',
|
||||
[Key.noTags]: '無標籤',
|
||||
|
||||
[Key.wordCount]: "字",
|
||||
[Key.wordsCount]: "字",
|
||||
[Key.minuteCount]: "分鐘",
|
||||
[Key.minutesCount]: "分鐘",
|
||||
[Key.postCount]: "篇文章",
|
||||
[Key.postsCount]: "篇文章",
|
||||
[Key.wordCount]: '字',
|
||||
[Key.wordsCount]: '字',
|
||||
[Key.minuteCount]: '分鐘',
|
||||
[Key.minutesCount]: '分鐘',
|
||||
[Key.postCount]: '篇文章',
|
||||
[Key.postsCount]: '篇文章',
|
||||
|
||||
[Key.primaryColor]: "主題色",
|
||||
[Key.primaryColor]: '主題色',
|
||||
|
||||
[Key.more]: "更多",
|
||||
[Key.more]: '更多',
|
||||
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "發佈於",
|
||||
[Key.license]: "許可協議",
|
||||
};
|
||||
[Key.author]: '作者',
|
||||
[Key.publishedAt]: '發佈於',
|
||||
[Key.license]: '許可協議',
|
||||
}
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
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 {siteConfig} from "../config.ts";
|
||||
import { siteConfig } from '../config.ts'
|
||||
import type I18nKey from './i18nKey.ts'
|
||||
import { en } from './languages/en.ts'
|
||||
import { ja } from './languages/ja.ts'
|
||||
import { zh_CN } from './languages/zh_CN.ts'
|
||||
import { zh_TW } from './languages/zh_TW.ts'
|
||||
|
||||
export type Translation = {
|
||||
[K in I18nKey]: string;
|
||||
[K in I18nKey]: string
|
||||
}
|
||||
|
||||
const defaultTranslation = en;
|
||||
const defaultTranslation = en
|
||||
|
||||
const map: { [key: string]: Translation } = {
|
||||
"en": en,
|
||||
"en_us": en,
|
||||
"en_gb": en,
|
||||
"en_au": en,
|
||||
"zh_cn": zh_CN,
|
||||
"zh_tw": zh_TW,
|
||||
en: en,
|
||||
en_us: en,
|
||||
en_gb: en,
|
||||
en_au: en,
|
||||
zh_cn: zh_CN,
|
||||
zh_tw: zh_TW,
|
||||
ja: ja,
|
||||
ja_jp: ja,
|
||||
}
|
||||
|
||||
export function getTranslation(lang: string): Translation {
|
||||
lang = lang.toLowerCase();
|
||||
return map[lang] || defaultTranslation;
|
||||
lang = lang.toLowerCase()
|
||||
return map[lang] || defaultTranslation
|
||||
}
|
||||
|
||||
export function i18n(key: I18nKey): string {
|
||||
const lang = siteConfig.lang || "en";
|
||||
return getTranslation(lang)[key];
|
||||
const lang = siteConfig.lang || 'en'
|
||||
return getTranslation(lang)[key]
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
import GlobalStyles from "../components/GlobalStyles.astro";
|
||||
import GlobalStyles from "@components/GlobalStyles.astro";
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
import { ViewTransitions } from 'astro:transitions';
|
||||
import ImageBox from "../components/misc/ImageBox.astro";
|
||||
import ImageBox from "@components/misc/ImageBox.astro";
|
||||
|
||||
import { fade } from 'astro:transitions';
|
||||
import {pathsEqual} from "../utils/url-utils";
|
||||
import ConfigCarrier from "../components/ConfigCarrier.astro";
|
||||
import {siteConfig} from "../config";
|
||||
import {pathsEqual} from "@utils/url-utils";
|
||||
import ConfigCarrier from "@components/ConfigCarrier.astro";
|
||||
import {siteConfig} from "@/config";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
---
|
||||
import Layout from "./Layout.astro";
|
||||
import Navbar from "../components/Navbar.astro";
|
||||
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 {siteConfig} from "../config";
|
||||
import Navbar from "@components/Navbar.astro";
|
||||
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 {siteConfig} from "@/config";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
|
|
@ -5,7 +5,7 @@ import MainGridLayout from "../layouts/MainGridLayout.astro";
|
|||
import { getEntry } from 'astro:content'
|
||||
import {i18n} from "../i18n/translation";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import Markdown from "../components/misc/Markdown.astro";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
|
||||
const aboutPost = await getEntry('spec', 'about')
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
import {getCategoryList, getSortedPosts} from "../../../utils/content-utils";
|
||||
import MainGridLayout from "../../../layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "../../../components/ArchivePanel.astro";
|
||||
import {i18n} from "../../../i18n/translation";
|
||||
import I18nKey from "../../../i18n/i18nKey";
|
||||
import {getCategoryList, getSortedPosts} from "@utils/content-utils";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
|
||||
|
||||
export async function getStaticPaths() {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
import { getCollection, getEntry } from "astro:content";
|
||||
import MainGridLayout from "../../layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "../../components/ArchivePanel.astro";
|
||||
import {i18n} from "../../i18n/translation";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
---
|
||||
|
||||
<MainGridLayout title={i18n(I18nKey.archive)}>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
---
|
||||
|
||||
import {getSortedPosts} from "../../../utils/content-utils";
|
||||
import MainGridLayout from "../../../layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "../../../components/ArchivePanel.astro";
|
||||
import {i18n} from "../../../i18n/translation";
|
||||
import I18nKey from "../../../i18n/i18nKey";
|
||||
import {getSortedPosts} from "@utils/content-utils";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
|
||||
|
||||
export async function getStaticPaths() {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
import MainGridLayout from "../../layouts/MainGridLayout.astro";
|
||||
import TitleCard from "../../components/TitleCardNew.astro";
|
||||
import Pagination from "../../components/control/Pagination.astro";
|
||||
import {getSortedPosts} from "../../utils/content-utils";
|
||||
import {getPostUrlBySlug} from "../../utils/url-utils";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import TitleCard from "@components/TitleCardNew.astro";
|
||||
import Pagination from "@components/control/Pagination.astro";
|
||||
import {getSortedPosts} from "@utils/content-utils";
|
||||
import {getPostUrlBySlug} from "@utils/url-utils";
|
||||
|
||||
export async function getStaticPaths({ paginate }) {
|
||||
const allBlogPosts = await getSortedPosts();
|
||||
|
@ -17,7 +17,13 @@ const {page} = Astro.props;
|
|||
<!-- 显示当前页面。也可以使用 Astro.params.page -->
|
||||
<MainGridLayout>
|
||||
<div class="flex flex-col gap-4 mb-4">
|
||||
{page.data.map(entry =>
|
||||
{page.data.map((entry: { data: { draft: boolean; title: string; tags: string[]; category: string; published: Date; image: string; description: string; }; slug: string; }) => {
|
||||
// ここで draft が true の場合は何もレンダリングしない
|
||||
if (import.meta.env.PROD && entry.data.draft) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TitleCard
|
||||
entry={entry}
|
||||
title={entry.data.title}
|
||||
|
@ -27,8 +33,19 @@ const {page} = Astro.props;
|
|||
url={getPostUrlBySlug(entry.slug)}
|
||||
image={entry.data.image}
|
||||
description={entry.data.description}
|
||||
draft={entry.data.draft}
|
||||
></TitleCard>
|
||||
)}
|
||||
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Pagination class="mx-auto" page={page}></Pagination>
|
||||
</MainGridLayout>
|
||||
|
||||
<script>
|
||||
if (import.meta.env.DEV) {
|
||||
console.log("開発環境");
|
||||
} else {
|
||||
console.log("本番環境");
|
||||
}
|
||||
</script>
|
|
@ -1,19 +1,21 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import MainGridLayout from "../../layouts/MainGridLayout.astro";
|
||||
import ImageBox from "../../components/misc/ImageBox.astro";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
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 {i18n} from "../../i18n/translation";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import {getPostUrlBySlug} from "../../utils/url-utils";
|
||||
import License from "../../components/misc/License.astro";
|
||||
import {licenseConfig} from "../../config";
|
||||
import Markdown from "../../components/misc/Markdown.astro";
|
||||
import PostMetadata from "@components/PostMetadata.astro";
|
||||
import Button from "@components/control/Button.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import {getPostUrlBySlug} from "@utils/url-utils";
|
||||
import License from "@components/misc/License.astro";
|
||||
import {licenseConfig} from "src/config";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const blogEntries = await getCollection('posts');
|
||||
const blogEntries = await getCollection('posts', ({ data }) => {
|
||||
return import.meta.env.PROD ? data.draft !== true : true;
|
||||
});
|
||||
return blogEntries.map(entry => ({
|
||||
params: { slug: entry.slug }, props: { entry },
|
||||
}));
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import getReadingTime from 'reading-time';
|
||||
import { toString } from 'mdast-util-to-string';
|
||||
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
|
||||
import { toString } from 'mdast-util-to-string'
|
||||
import getReadingTime from 'reading-time'
|
||||
|
||||
export function remarkReadingTime() {
|
||||
return function (tree, { data }) {
|
||||
const textOnPage = toString(tree);
|
||||
const readingTime = getReadingTime(textOnPage);
|
||||
data.astro.frontmatter.minutes = Math.max(1, Math.round(readingTime.minutes));
|
||||
data.astro.frontmatter.words = readingTime.words;
|
||||
};
|
||||
return (tree, { data }) => {
|
||||
const textOnPage = toString(tree)
|
||||
const readingTime = getReadingTime(textOnPage)
|
||||
data.astro.frontmatter.minutes = Math.max(
|
||||
1,
|
||||
Math.round(readingTime.minutes),
|
||||
)
|
||||
data.astro.frontmatter.words = readingTime.words
|
||||
}
|
||||
}
|
|
@ -1,45 +1,45 @@
|
|||
export type SiteConfig = {
|
||||
title: string,
|
||||
subtitle: string,
|
||||
title: string
|
||||
subtitle: string
|
||||
|
||||
lang: string,
|
||||
lang: string
|
||||
|
||||
themeHue: number,
|
||||
themeHue: number
|
||||
banner: {
|
||||
enable: boolean,
|
||||
src: string,
|
||||
enable: boolean
|
||||
src: string
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export enum LinkPreset {
|
||||
Home,
|
||||
Archive,
|
||||
About,
|
||||
Home = 0,
|
||||
Archive = 1,
|
||||
About = 2,
|
||||
}
|
||||
|
||||
export type NavBarLink = {
|
||||
name: string,
|
||||
url: string,
|
||||
name: string
|
||||
url: string
|
||||
external?: boolean
|
||||
}
|
||||
|
||||
export type NavBarConfig = {
|
||||
links: (NavBarLink | LinkPreset)[],
|
||||
links: (NavBarLink | LinkPreset)[]
|
||||
}
|
||||
|
||||
export type ProfileConfig = {
|
||||
avatar?: string,
|
||||
name: string,
|
||||
bio?: string,
|
||||
avatar?: string
|
||||
name: string
|
||||
bio?: string
|
||||
links: {
|
||||
name: string,
|
||||
url: string,
|
||||
icon: string,
|
||||
}[],
|
||||
};
|
||||
name: string
|
||||
url: string
|
||||
icon: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export type LicenseConfig = {
|
||||
enable: boolean;
|
||||
name: string,
|
||||
url: string,
|
||||
enable: boolean
|
||||
name: string
|
||||
url: string
|
||||
}
|
|
@ -1,74 +1,74 @@
|
|||
import {CollectionEntry, getCollection} from "astro:content";
|
||||
import { getCollection } from 'astro:content'
|
||||
|
||||
export async function getSortedPosts() {
|
||||
const allBlogPosts = await getCollection("posts");
|
||||
const allBlogPosts = await getCollection('posts')
|
||||
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;
|
||||
});
|
||||
const dateA = new Date(a.data.published)
|
||||
const dateB = new Date(b.data.published)
|
||||
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;
|
||||
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;
|
||||
sorted[i].data.prevSlug = sorted[i + 1].slug
|
||||
sorted[i].data.prevTitle = sorted[i + 1].data.title
|
||||
}
|
||||
|
||||
return sorted;
|
||||
return sorted
|
||||
}
|
||||
|
||||
export type Tag = {
|
||||
name: string;
|
||||
count: number;
|
||||
name: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export async function getTagList(): Promise<Tag[]> {
|
||||
const allBlogPosts = await getCollection("posts");
|
||||
const allBlogPosts = await getCollection('posts')
|
||||
|
||||
const countMap: { [key: string]: number } = {};
|
||||
allBlogPosts.map((post) => {
|
||||
const countMap: { [key: string]: number } = {}
|
||||
allBlogPosts.map(post => {
|
||||
post.data.tags.map((tag: string) => {
|
||||
if (!countMap[tag]) countMap[tag] = 0;
|
||||
countMap[tag]++;
|
||||
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 a.toLowerCase().localeCompare(b.toLowerCase())
|
||||
})
|
||||
|
||||
return keys.map((key) => ({name: key, count: countMap[key]}));
|
||||
return keys.map(key => ({ name: key, count: countMap[key] }))
|
||||
}
|
||||
|
||||
export type Category = {
|
||||
name: string;
|
||||
count: number;
|
||||
name: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export async function getCategoryList(): Promise<Category[]> {
|
||||
const allBlogPosts = await getCollection("posts");
|
||||
let count : {[key: string]: number} = {};
|
||||
allBlogPosts.map((post) => {
|
||||
const allBlogPosts = await getCollection('posts')
|
||||
const count: { [key: string]: number } = {}
|
||||
allBlogPosts.map(post => {
|
||||
if (!post.data.category) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!count[post.data.category]) {
|
||||
count[post.data.category] = 0;
|
||||
count[post.data.category] = 0
|
||||
}
|
||||
count[post.data.category]++;
|
||||
});
|
||||
count[post.data.category]++
|
||||
})
|
||||
|
||||
let lst = Object.keys(count).sort((a, b) => {
|
||||
return a.toLowerCase().localeCompare(b.toLowerCase());
|
||||
});
|
||||
const lst = Object.keys(count).sort((a, b) => {
|
||||
return a.toLowerCase().localeCompare(b.toLowerCase())
|
||||
})
|
||||
|
||||
let ret : Category[] = [];
|
||||
const ret: Category[] = []
|
||||
for (const c of lst) {
|
||||
ret.push({name: c, count: count[c]});
|
||||
ret.push({ name: c, count: count[c] })
|
||||
}
|
||||
return ret;
|
||||
return ret
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
export function formatDateToYYYYMMDD(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
|
||||
export function pathsEqual(path1: string, path2: string) {
|
||||
const normalizedPath1 = path1.replace(/^\/|\/$/g, '').toLowerCase();
|
||||
const normalizedPath2 = path2.replace(/^\/|\/$/g, '').toLowerCase();
|
||||
return normalizedPath1 === normalizedPath2;
|
||||
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, '$1');
|
||||
const joined = parts.join('/')
|
||||
return joined.replace(/([^:]\/)\/+/g, '$1')
|
||||
}
|
||||
|
||||
export function getPostUrlBySlug(slug: string): string | null {
|
||||
if (!slug)
|
||||
return null;
|
||||
return `/posts/${slug}`;
|
||||
if (!slug) return null
|
||||
return `/posts/${slug}`
|
||||
}
|
||||
|
||||
export function getCategoryUrl(category: string): string | null {
|
||||
if (!category)
|
||||
return null;
|
||||
return `/archive/category/${category}`;
|
||||
if (!category) return null
|
||||
return `/archive/category/${category}`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
const defaultTheme = require("tailwindcss/defaultTheme")
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
darkMode: 'class', // allows toggling dark mode manually
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||
darkMode: "class", // allows toggling dark mode manually
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Roboto', 'sans-serif', ...defaultTheme.fontFamily.sans],
|
||||
}
|
||||
sans: ["Roboto", "sans-serif", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
],
|
||||
},
|
||||
plugins: [require("@tailwindcss/typography")],
|
||||
}
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"strictNullChecks": true,
|
||||
"allowJs": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "@astrojs/ts-plugin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/**",
|
||||
"src/**/**/**",
|
||||
]
|
||||
],
|
||||
"paths": {
|
||||
"@components/*": ["src/components/*"],
|
||||
"@assets/*": ["src/assets/*"],
|
||||
"@utils/*": ["src/utils/*"],
|
||||
"@i18n/*": ["src/i18n/*"],
|
||||
"@layouts/*": ["src/layouts/*"],
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
Loading…
Reference in New Issue