feat: optimize blog build and deployment

This commit is contained in:
liumangmang
2026-05-11 01:35:38 +08:00
parent b4653e209c
commit 1b3e4c33a4
21 changed files with 10015 additions and 523 deletions
+8
View File
@@ -1,5 +1,13 @@
node_modules
dist
src/.vuepress/dist
src/.vuepress/.cache
src/.vuepress/.temp
.cache
.temp
.npm
.env
.git
.gitignore
*.log
coverage
+1
View File
@@ -0,0 +1 @@
ENCRYPT_PASSWORD=local-dev-password
-1
View File
@@ -3,7 +3,6 @@ node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
# VuePress
src/.vuepress/.cache/
+7
View File
@@ -0,0 +1,7 @@
node_modules
src/.vuepress/.cache
src/.vuepress/.temp
src/.vuepress/dist
package-lock.json
*.md
src/**/*.md
+16
View File
@@ -0,0 +1,16 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"overrides": [
{
"files": "*.md",
"options": {
"proseWrap": "preserve"
}
}
]
}
+9 -2
View File
@@ -2,12 +2,19 @@ FROM node:20-alpine AS build
WORKDIR /app
ARG ENCRYPT_PASSWORD
ENV ENCRYPT_PASSWORD=$ENCRYPT_PASSWORD
COPY package.json package-lock.json ./
RUN npm ci
RUN --mount=type=cache,target=/root/.npm npm install --include=optional
COPY . .
RUN npm run docs:build
FROM nginx:1.25-alpine
FROM nginx:1.27-alpine
COPY --from=build /app/src/.vuepress/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget -qO- http://127.0.0.1/ >/dev/null 2>&1 || exit 1
+10 -4
View File
@@ -1,7 +1,13 @@
.PHONY: up down
.PHONY: up down ensure-env
up:
docker compose up -d --build
ensure-env:
@if [ ! -f .env ]; then \
cp .env.example .env; \
echo "Created .env from .env.example. Edit ENCRYPT_PASSWORD in .env for deployment."; \
fi
up: ensure-env
docker compose --env-file .env up -d --build
down:
docker compose down
docker compose --env-file .env down
+28
View File
@@ -3,6 +3,34 @@ services:
image: myblog:latest
build:
context: .
args:
ENCRYPT_PASSWORD: ${ENCRYPT_PASSWORD:?Set ENCRYPT_PASSWORD in .env or shell before building}
environment:
ENCRYPT_PASSWORD: ${ENCRYPT_PASSWORD:?Set ENCRYPT_PASSWORD in .env or shell before starting}
ports:
- '51888:80'
restart: unless-stopped
healthcheck:
test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1/']
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
logging:
driver: json-file
options:
max-size: '10m'
max-file: '3'
deploy:
resources:
limits:
cpus: '1.00'
memory: 256M
reservations:
memory: 64M
networks:
- myblog-net
networks:
myblog-net:
driver: bridge
+46
View File
@@ -0,0 +1,46 @@
import js from '@eslint/js';
import vue from 'eslint-plugin-vue';
import tseslint from 'typescript-eslint';
export default [
{
ignores: [
'node_modules/**',
'src/.vuepress/.cache/**',
'src/.vuepress/.temp/**',
'src/.vuepress/dist/**',
],
},
js.configs.recommended,
...tseslint.configs.recommended,
...vue.configs['flat/recommended'],
{
files: ['**/*.{ts,vue}'],
languageOptions: {
globals: {
clearTimeout: 'readonly',
console: 'readonly',
document: 'readonly',
fetch: 'readonly',
setTimeout: 'readonly',
window: 'readonly',
},
parserOptions: {
parser: tseslint.parser,
extraFileExtensions: ['.vue'],
},
},
rules: {
'vue/multi-word-component-names': 'off',
'vue/html-indent': ['error', 2],
'vue/max-attributes-per-line': 'off',
'vue/singleline-html-element-content-newline': 'off',
},
},
{
files: ['src/.vuepress/**/*.ts'],
rules: {
'no-console': 'off',
},
},
];
+45
View File
@@ -0,0 +1,45 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
application/javascript
application/json
application/xml
image/svg+xml
text/css
text/plain
text/xml;
location ~* \.(?:css|js|mjs|woff2?|ttf|otf|eot|ico|png|jpg|jpeg|gif|webp|avif|svg)$ {
try_files $uri =404;
access_log off;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.html$ {
try_files $uri =404;
add_header Cache-Control "no-cache";
}
location = /robots.txt {
try_files $uri =404;
add_header Cache-Control "public, max-age=3600";
}
location = /sitemap.xml {
try_files $uri =404;
add_header Cache-Control "public, max-age=3600";
}
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}
}
+9211
View File
File diff suppressed because it is too large Load Diff
+14 -6
View File
@@ -9,15 +9,23 @@
"docs:clean-dev": "vuepress-vite dev src --clean-cache",
"docs:dev": "vuepress-vite dev src",
"docs:update-package": "npx vp-update",
"format": "prettier \"*.{js,json,yml}\" \"src/.vuepress/**/*.{ts,vue,scss}\" --write",
"format:check": "prettier \"*.{js,json,yml}\" \"src/.vuepress/**/*.{ts,vue,scss}\" --check",
"lint": "eslint \"src/.vuepress/**/*.{ts,vue}\" eslint.config.js",
"update:browsers": "npx update-browserslist-db@latest"
},
"devDependencies": {
"@vuepress/bundler-vite": "2.0.0-rc.22",
"@vuepress/plugin-git": "2.0.0-rc.99",
"@vuepress/plugin-search": "2.0.0-rc.99",
"sass-embedded": "^1.87.0",
"@eslint/js": "^9.39.1",
"@vuepress/bundler-vite": "^2.0.0-rc.28",
"@vuepress/plugin-git": "^2.0.0-rc.128",
"@vuepress/plugin-search": "^2.0.0-rc.128",
"eslint": "^9.39.1",
"eslint-plugin-vue": "^10.6.1",
"prettier": "^3.6.2",
"sass-embedded": "^1.99.0",
"typescript-eslint": "^8.46.4",
"vue": "^3.5.13",
"vuepress": "2.0.0-rc.22",
"vuepress-theme-hope": "2.0.0-rc.85"
"vuepress": "^2.0.0-rc.28",
"vuepress-theme-hope": "^2.0.0-rc.106"
}
}
+144 -8
View File
@@ -1,12 +1,148 @@
<script setup lang="ts">
import BlogHero from "vuepress-theme-hope/blog/components/BlogHero.js";
import HitokotoBlogHero from "vuepress-theme-hope/presets/HitokotoBlogHero.js";
import { computed } from 'vue';
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { usePageFrontmatter, withBase } from 'vuepress/client';
import HeroSlideDownButton from 'vuepress-theme-hope/components/home/HeroSlideDownButton';
import 'vuepress-theme-hope/styles/blog/blog-hero.scss';
import 'vuepress-theme-hope/presets/hitokoto-blog-hero.scss';
interface BlogHomeFrontmatter {
bgImage?: string | false;
bgImageDark?: string | false;
bgImageStyle?: string | Record<string, string>;
hero?: boolean;
heroFullScreen?: boolean;
}
interface HitokotoResult {
hitokoto: string;
from?: string;
}
const fallbackText = '山高路远,看世界,也找自己。';
const fallbackAuthor = '氓氓小栈';
const defaultHero = '//theme-hope-assets.vuejs.press/hero/default.jpg';
const frontmatter = usePageFrontmatter<BlogHomeFrontmatter>();
const resolveImage = (image: string): string =>
image.startsWith('//') || /^https?:\/\//.test(image) ? image : withBase(image);
const background = computed(() => ({
image:
typeof frontmatter.value.bgImage === 'string'
? resolveImage(frontmatter.value.bgImage)
: frontmatter.value.bgImage === false
? null
: defaultHero,
imageDark:
typeof frontmatter.value.bgImageDark === 'string'
? resolveImage(frontmatter.value.bgImageDark)
: null,
style: frontmatter.value.bgImageStyle ?? null,
}));
const scrollDown = (): void => {
window.scrollTo({
top: window.innerHeight - (document.querySelector('[vp-navbar]')?.clientHeight ?? 0),
behavior: 'smooth',
});
};
const text = ref(fallbackText);
const display = ref(fallbackText);
const author = ref(fallbackAuthor);
let isMounted = false;
let timer: ReturnType<typeof setTimeout> | undefined;
const clearRenderTimer = (): void => {
if (timer) {
clearTimeout(timer);
timer = undefined;
}
};
const renderText = async (): Promise<void> => {
clearRenderTimer();
display.value = '';
let index = 0;
const renderNextWord = async (): Promise<void> => {
display.value += text.value[index] ?? '';
index += 1;
await nextTick();
if (isMounted && index < text.value.length) {
timer = setTimeout(() => {
void renderNextWord();
}, 120);
}
};
await renderNextWord();
};
const getHitokoto = async (): Promise<void> => {
try {
const response = await fetch('https://v1.hitokoto.cn');
const result = (await response.json()) as HitokotoResult;
if (result.hitokoto) {
text.value = result.hitokoto;
author.value = result.from ?? fallbackAuthor;
}
} catch (error) {
console.error('Failed to fetch hitokoto:', error);
}
};
watch(text, () => {
void renderText();
});
onMounted(() => {
isMounted = true;
void getHitokoto();
});
onUnmounted(() => {
isMounted = false;
clearRenderTimer();
});
</script>
<template>
<BlogHero>
<template #info="info">
<HitokotoBlogHero v-bind="info" />
</template>
</BlogHero>
</template>
<div
v-if="frontmatter.hero !== false"
class="vp-blog-hero"
:class="{
'hero-fullscreen': frontmatter.heroFullScreen,
'no-bg': !background.image,
}"
>
<div
v-if="background.image"
class="vp-blog-mask"
:class="{ light: background.imageDark }"
:style="[{ background: `url(${background.image}) center/cover no-repeat` }, background.style]"
/>
<div
v-if="background.imageDark"
class="vp-blog-mask dark"
:style="[
{ background: `url(${background.imageDark}) center/cover no-repeat` },
background.style,
]"
/>
<div class="hitokoto">
<p class="hitokoto-text">
<span>{{ display }}</span>
</p>
<p class="hitokoto-author">{{ author }}</p>
</div>
<HeroSlideDownButton v-if="frontmatter.heroFullScreen" @click="scrollDown" />
</div>
</template>
+24 -25
View File
@@ -1,38 +1,37 @@
import {defineUserConfig} from "vuepress";
import {viteBundler} from "@vuepress/bundler-vite";
import {getDirname, path} from "vuepress/utils";
import theme from "./theme.js";
import { defineUserConfig } from 'vuepress';
import { viteBundler } from '@vuepress/bundler-vite';
import { getDirname, path } from 'vuepress/utils';
import theme from './theme.js';
const __dirname = getDirname(import.meta.url);
export default defineUserConfig({
base: "/",
base: '/',
lang: "zh-CN",
title: "氓氓小栈",
description: "氓氓小栈",
lang: 'zh-CN',
title: '氓氓小栈',
description: '氓氓小栈',
theme,
bundler: viteBundler({
viteOptions: {
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
silenceDeprecations: ["if-function"],
},
},
},
bundler: viteBundler({
viteOptions: {
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
silenceDeprecations: ['if-function'],
},
},
}),
},
},
}),
alias: {
"@theme-hope/modules/blog/components/BlogHero": path.resolve(
__dirname,
"./components/BlogHero.vue",
'@theme-hope/components/blog/BlogHero': path.resolve(__dirname, './components/BlogHero.vue'),
'@theme-hope/modules/blog/components/BlogHero': path.resolve(
__dirname,
'./components/BlogHero.vue',
),
},
// 和 PWA 一起启用
// shouldPrefetch: false,
// VuePress 默认会预取页面资源;如后续启用 PWA,可按需显式调整。
});
+27 -27
View File
@@ -1,30 +1,30 @@
import {navbar} from "vuepress-theme-hope";
import { navbar } from 'vuepress-theme-hope';
export default navbar([
"/",
{
text: "编程",
icon: "mdi:code-tags",
link: "/programming/",
},
{
text: "工作",
icon: "mdi:briefcase",
link: "/work/",
},
{
text: "应用",
icon: "mdi:application",
link: "/apps/",
},
{
text: "工具箱",
icon: "mdi:toolbox",
link: "/tools/",
},
{
text: "AI",
icon: "mdi:robot-outline",
link: "/ai/",
},
'/',
{
text: '编程',
icon: 'mdi:code-tags',
link: '/programming/',
},
{
text: '工作',
icon: 'mdi:briefcase',
link: '/work/',
},
{
text: '应用',
icon: 'mdi:application',
link: '/apps/',
},
{
text: '工具箱',
icon: 'mdi:toolbox',
link: '/tools/',
},
{
text: 'AI',
icon: 'mdi:robot-outline',
link: '/ai/',
},
]);
+288 -319
View File
@@ -1,347 +1,316 @@
import {sidebar} from "vuepress-theme-hope";
import { sidebar } from 'vuepress-theme-hope';
export default sidebar({
"/programming/": [
'/programming/': [
{
text: '前端',
collapsible: true,
expanded: false,
icon: 'mdi:vuejs',
prefix: 'frontend/',
children: [
{
text: "前端",
collapsible: true,
expanded: false,
icon: "mdi:vuejs",
prefix: "frontend/",
children: [
{
text: "Vue",
icon: "mdi:vuejs",
collapsible: true,
prefix: "vue/",
children: "structure",
},
{
text: "CSS",
icon: "mdi:language-css3",
collapsible: true,
prefix: "css/",
children: "structure",
},
{
text: "HTML",
icon: "mdi:language-html5",
collapsible: true,
prefix: "html/",
children: "structure",
},
{
text: "开发工具",
icon: "mdi:tools",
collapsible: true,
prefix: "tools/",
children: "structure",
},
]
text: 'Vue',
icon: 'mdi:vuejs',
collapsible: true,
prefix: 'vue/',
children: 'structure',
},
{
text: "后端",
collapsible: true,
expanded: false,
icon: "mdi:code-tags",
prefix: "backend/",
children: [
{
text: "Java",
icon: "mdi:language-java",
collapsible: true,
prefix: "java/",
children: [
{
text: "框架",
icon: "mdi:code-braces",
collapsible: true,
prefix: "框架/",
children: "structure",
},
{
text: "功能整理",
icon: "mdi:puzzle",
collapsible: true,
prefix: "功能整理/",
children: [
"01XJar.md",
"02Maven.md",
"03WebSocket和HTTP关系.md",
"05防止表单和参数重复提交.md",
"06Spring Boot JAR 瘦身与加密.md",
"SDKMAN-Java-Maven版本管理.md",
"Windows-Jabba-Java版本管理.md",
],
},
{
text: "试题",
icon: "mdi:comment-question",
collapsible: true,
prefix: "AI试题/",
children: "structure",
},
]
},
{
text: "Go",
icon: "mdi:language-go",
collapsible: true,
prefix: "go/",
children: [
{
text: "Go基础语法",
icon: "mdi:book-open-outline",
collapsible: true,
prefix: "Go基础语法/",
children: [
"01Hello World.md",
"02变量与类型.md",
"03slice和map.md",
"04Struct、方法与接收者类型详解.md",
"05接口.md",
"06错误机制.md",
"07从零实现 Mini 日志库.md",
],
},
{
text: "Go并发模型",
icon: "mdi:run-fast",
collapsible: true,
prefix: "Go并发模型/",
children: [
"08Goroutine与GPM调度模型.md",
"09Channel与单向Channel.md",
"10select与超时控制.md",
"11context取消与超时.md",
"12Mutex与WaitGroup.md",
"13atomic原子操作.md",
"14并发爬虫实战.md",
],
},
{
text: "Web开发数据库",
icon: "mdi:database-outline",
collapsible: true,
prefix: "Web开发数据库/",
children: [
"README.md",
"15Gin基础入门.md",
"16Gin中间件.md",
"17GORM数据库.md",
"18GORM事务关联.md",
"19Viper配置管理.md",
"20Zap日志.md",
"21综合实战项目.md",
],
}
],
},
{
text: "C++",
icon: "mdi:language-cpp",
collapsible: true,
prefix: "c++/",
children: "structure",
},
]
text: 'CSS',
icon: 'mdi:language-css3',
collapsible: true,
prefix: 'css/',
children: 'structure',
},
{
text: "Linux",
collapsible: true,
expanded: false,
icon: "mdi:linux",
prefix: "linux/",
children: [
{
text: "基础",
icon: "mdi:console",
collapsible: true,
prefix: "基础/",
children: "structure",
},
{
text: "Linux_Mint",
icon: "simple-icons:linuxmint",
collapsible: true,
prefix: "Linux_Mint/",
children: "structure",
},
{
text: "凝思",
icon: "mdi:server",
collapsible: true,
prefix: "凝思/",
children: "structure",
},
]
text: 'HTML',
icon: 'mdi:language-html5',
collapsible: true,
prefix: 'html/',
children: 'structure',
},
{
text: "Docker",
collapsible: true,
expanded: false,
icon: "mdi:docker",
prefix: "docker/",
children: "structure",
}
],
"/work/": [
text: '开发工具',
icon: 'mdi:tools',
collapsible: true,
prefix: 'tools/',
children: 'structure',
},
],
},
{
text: '后端',
collapsible: true,
expanded: false,
icon: 'mdi:code-tags',
prefix: 'backend/',
children: [
{
text: "工作日志",
collapsible: true,
expanded: false,
icon: "mdi:file-document-outline",
prefix: "log/",
children: "structure",
text: 'Java',
icon: 'mdi:language-java',
collapsible: true,
prefix: 'java/',
children: [
{
text: '框架',
icon: 'mdi:code-braces',
collapsible: true,
prefix: '框架/',
children: 'structure',
},
{
text: '功能整理',
icon: 'mdi:puzzle',
collapsible: true,
prefix: '功能整理/',
children: 'structure',
},
{
text: '试题',
icon: 'mdi:comment-question',
collapsible: true,
prefix: 'AI试题/',
children: 'structure',
},
],
},
{
text: "项目总结",
collapsible: true,
expanded: false,
icon: "mdi:book-open-page-variant",
prefix: "project-summary/",
children: "structure",
text: 'Go',
icon: 'mdi:language-go',
collapsible: true,
prefix: 'go/',
children: [
{
text: 'Go基础语法',
icon: 'mdi:book-open-outline',
collapsible: true,
prefix: 'Go基础语法/',
children: 'structure',
},
{
text: 'Go并发模型',
icon: 'mdi:run-fast',
collapsible: true,
prefix: 'Go并发模型/',
children: 'structure',
},
{
text: 'Web开发数据库',
icon: 'mdi:database-outline',
collapsible: true,
prefix: 'Web开发数据库/',
children: [
'README.md',
'15Gin基础入门.md',
'16Gin中间件.md',
'17GORM数据库.md',
'18GORM事务关联.md',
'19Viper配置管理.md',
'20Zap日志.md',
'21综合实战项目.md',
],
},
],
},
{
text: "常用记录",
icon: "mdi:star",
link: "/work/常用.md",
text: 'C++',
icon: 'mdi:language-cpp',
collapsible: true,
prefix: 'c++/',
children: 'structure',
},
],
},
{
text: 'Linux',
collapsible: true,
expanded: false,
icon: 'mdi:linux',
prefix: 'linux/',
children: [
{
text: '基础',
icon: 'mdi:console',
collapsible: true,
prefix: '基础/',
children: 'structure',
},
{
text: "待办事项",
icon: "fa6-solid:list-check",
collapsible: true,
children: [
{
text: "待办首页",
icon: "mdi:home-outline",
link: "/work/todo/",
},
{
text: "4月待办",
icon: "mdi:calendar-month",
link: "/work/todo/2026-04.md",
},
{
text: "3月待办",
icon: "mdi:calendar-month",
link: "/work/todo/2026-03.md",
},
],
}
],
"/apps/": [
{
text: "自建应用",
icon: "mdi:apps",
collapsible: true,
children: "structure",
}
],
"/tools/": [
{
text: "工具箱",
icon: "mdi:toolbox",
collapsible: true,
children: [
"01gkd.md",
"02WSL2.md",
"03Scoop.md",
"04gitee-ssh.md",
"05Google.md",
"06MobaXterm.md"
],
},
],
"/ai/": [
{
text: "OpenCode",
icon: "fa6-solid:code",
collapsible: true,
children: [
{
text: "opencode-cli",
icon: "fa6-solid:terminal",
link: "opencode.md",
},
{
text: "opencode-tui",
icon: "fa6-solid:desktop",
link: "opencode-tui.md",
},
],
text: 'Linux_Mint',
icon: 'simple-icons:linuxmint',
collapsible: true,
prefix: 'Linux_Mint/',
children: 'structure',
},
{
text: "Superpowers",
icon: "fa6-solid:rocket",
collapsible: true,
prefix: "superpowers/",
children: [
{
text: "superpowers-总览",
icon: "fa6-solid:eye",
link: "opencode-superpowers-overview.md",
},
{
text: "superpowers",
icon: "fa6-solid:bolt",
link: "opencode-superpowers.md",
},
{
text: "skills-使用方案汇总",
icon: "fa6-solid:book",
link: "opencode-skills-playbook.md",
},
],
text: '凝思',
icon: 'mdi:server',
collapsible: true,
prefix: '凝思/',
children: 'structure',
},
],
},
{
text: 'Docker',
collapsible: true,
expanded: false,
icon: 'mdi:docker',
prefix: 'docker/',
children: 'structure',
},
],
'/work/': [
{
text: '工作日志',
collapsible: true,
expanded: false,
icon: 'mdi:file-document-outline',
prefix: 'log/',
children: 'structure',
},
{
text: '项目总结',
collapsible: true,
expanded: false,
icon: 'mdi:book-open-page-variant',
prefix: 'project-summary/',
children: 'structure',
},
{
text: '常用记录',
icon: 'mdi:star',
link: '/work/常用.md',
},
{
text: '待办事项',
icon: 'fa6-solid:list-check',
collapsible: true,
children: [
{
text: '待办首页',
icon: 'mdi:home-outline',
link: '/work/todo/',
},
{
text: "Claude Code",
icon: "fa6-solid:code-branch",
collapsible: true,
children: [
{
text: "多分支工作流实战总结(2026",
icon: "fa6-solid:code-merge",
link: "claude-code-branch-workflow-2026.md",
},
],
text: '4月待办',
icon: 'mdi:calendar-month',
link: '/work/todo/2026-04.md',
},
{
text: "ChatGPT",
icon: "fa6-solid:comments",
collapsible: true,
children: [
{
text: "chatgpt-使用记录与实践",
icon: "fa6-solid:message",
link: "chatgpt.md",
},
],
text: '3月待办',
icon: 'mdi:calendar-month',
link: '/work/todo/2026-03.md',
},
],
},
],
'/apps/': [
{
text: '自建应用',
icon: 'mdi:apps',
collapsible: true,
children: 'structure',
},
],
'/tools/': [
{
text: '工具箱',
icon: 'mdi:toolbox',
collapsible: true,
children: 'structure',
},
],
'/ai/': [
{
text: 'OpenCode',
icon: 'fa6-solid:code',
collapsible: true,
children: [
{
text: 'opencode-cli',
icon: 'fa6-solid:terminal',
link: 'opencode.md',
},
{
text: "OpenClaw",
icon: "fa6-solid:robot",
collapsible: true,
children: [
{
text: "openclaw-24h在线部署实战",
icon: "fa6-solid:server",
link: "openclaw.md",
},
],
text: 'opencode-tui',
icon: 'fa6-solid:desktop',
link: 'opencode-tui.md',
},
],
},
{
text: 'Superpowers',
icon: 'fa6-solid:rocket',
collapsible: true,
prefix: 'superpowers/',
children: [
{
text: 'superpowers-总览',
icon: 'fa6-solid:eye',
link: 'opencode-superpowers-overview.md',
},
{
text: "iFlow",
icon: "fa6-solid:diagram-project",
collapsible: true,
children: [
{
text: "iflow-流程编排实践记录",
icon: "fa6-solid:flow-chart",
link: "iflow.md",
},
],
text: 'superpowers',
icon: 'fa6-solid:bolt',
link: 'opencode-superpowers.md',
},
],
"/": false,
{
text: 'skills-使用方案汇总',
icon: 'fa6-solid:book',
link: 'opencode-skills-playbook.md',
},
],
},
{
text: 'Claude Code',
icon: 'fa6-solid:code-branch',
collapsible: true,
children: [
{
text: '多分支工作流实战总结(2026',
icon: 'fa6-solid:code-merge',
link: 'claude-code-branch-workflow-2026.md',
},
],
},
{
text: 'ChatGPT',
icon: 'fa6-solid:comments',
collapsible: true,
children: [
{
text: 'chatgpt-使用记录与实践',
icon: 'fa6-solid:message',
link: 'chatgpt.md',
},
],
},
{
text: 'OpenClaw',
icon: 'fa6-solid:robot',
collapsible: true,
children: [
{
text: 'openclaw-24h在线部署实战',
icon: 'fa6-solid:server',
link: 'openclaw.md',
},
],
},
{
text: 'iFlow',
icon: 'fa6-solid:diagram-project',
collapsible: true,
children: [
{
text: 'iflow-流程编排实践记录',
icon: 'fa6-solid:flow-chart',
link: 'iflow.md',
},
],
},
],
'/': false,
});
+1 -1
View File
@@ -1,2 +1,2 @@
// you can change config here
$theme-color: #DC143C;
$theme-color: #dc143c;
+2 -2
View File
@@ -4,11 +4,11 @@
/* .vuepress/styles/index.scss */
.hitokoto-text {
color: #DC143C !important; // 替换为你想要的颜色
color: #dc143c !important; // 替换为你想要的颜色
}
.hitokoto-author {
color: #DC143C!important; // 可选:也可自定义作者名颜色
color: #dc143c !important; // 可选:也可自定义作者名颜色
}
.vp-navbar .auto-link {
+130 -120
View File
@@ -1,126 +1,136 @@
import {hopeTheme} from "vuepress-theme-hope";
import { hopeTheme } from 'vuepress-theme-hope';
import navbar from './navbar.js';
import sidebar from './sidebar.js';
const encryptPassword = process.env.ENCRYPT_PASSWORD;
if (process.env.NODE_ENV === 'production' && !encryptPassword) {
throw new Error('Missing ENCRYPT_PASSWORD. Set it before running a production build.');
}
const accountsPassword = encryptPassword ?? 'local-dev-password';
import navbar from "./navbar.js";
import sidebar from "./sidebar.js";
//VuePress Theme Hope主题的博客
export default hopeTheme(
{
// 网站域名配置(用于SEO和RSS等功能)
hostname: "https://mangmang.fun/",
{
// 网站域名配置(用于SEO和RSS等功能)
hostname: 'https://mangmang.fun/',
// 加密配置
encrypt: {
config: {
"/accounts/": "362165265", // 账号密码页面访问密码
},
},
// 作者信息
author: {
name: "LiuMangMang",
},
// 网站许可证
license: "CC 4.0",
// 导航栏配置(从 navbar.js 文件导入)
navbar,
// 侧边栏配置(从 sidebar.js 文件导入)
sidebar,
// 网站logo设置
logo: "logo/transparentLogo.png", // 亮色模式下的logo
logoDark: "logo/transparentLogo.png", // 暗色模式下的logo
// 支持简写仓库名称,会解析到 GitHub 上,同时也可以是一个完整的 URL
// repo: "https://gitea.mangmang.fun/mangmang/blog",
// 默认从 `repo` 内容中推断为以下之一:
// "GitHub" / "GitLab" / "Gitee" / "Bitbucket" / "Source"
repoLabel: "Gitea",
repoDisplay: true,
// 是否显示编辑链接
editLink: true,
// 文档源码目录
docsDir: "src",
// 页脚配置
footer: "<a href='https://beian.miit.gov.cn/' target='_blank'>蜀ICP备2025176018号-1</a> | Powered by VuePress | Theme by Hope",
displayFooter: true,
// 是否显示最后更新时间
lastUpdated: false,
// 深色模式配置(toggle表示用户可以切换)
darkmode: "toggle",
// 博客相关配置
blog: {
description: "一个后端开发者",
intro: "/intro.html"
},
// Markdown增强功能配置
markdown: {
align: true, // 启用文本对齐
attrs: true, // 启用属性支持
codeTabs: true, // 启用代码选项卡
component: true, // 启用组件支持
demo: true, // 启用演示支持
figure: true, // 启用图片描述
gfm: true, // 启用GitHub风格的Markdown
imgLazyload: true, // 启用图片懒加载
imgSize: true, // 启用图片尺寸设置
include: true, // 启用文件包含
mark: true, // 启用标记高亮
plantuml: true, // 启用PlantUML图表
spoiler: true, // 启用剧透标记
// 文本样式化配置
stylize: [
{
matcher: "Recommended",
replacer: ({tag}) => {
if (tag === "em")
return {
tag: "Badge",
attrs: {type: "tip"},
content: "Recommended",
};
},
},
],
sub: true, // 启用下标
sup: true, // 启用上标
tabs: true, // 启用选项卡
tasklist: true, // 启用任务列表
vPre: true, // 启用v-pre支持
},
// 插件配置
plugins: {
// 启用博客功能
blog: true,
// 启用本地搜索
search: true,
// 组件配置
components: {
components: ["Badge", "VPCard"],
},
// 图标配置
icon: {
prefix: "fa6-solid:",
},
},
// 加密配置
encrypt: {
config: {
'/accounts/': accountsPassword, // 账号密码页面访问密码
},
},
{
// 自定义主题配置
custom: true
}
// 作者信息
author: {
name: 'LiuMangMang',
},
// 网站许可证
license: 'CC 4.0',
// 导航栏配置(从 navbar.js 文件导入)
navbar,
// 侧边栏配置(从 sidebar.js 文件导入)
sidebar,
// 网站logo设置
logo: 'logo/transparentLogo.png', // 亮色模式下的logo
logoDark: 'logo/transparentLogo.png', // 暗色模式下的logo
// 支持简写仓库名称,会解析到 GitHub 上,同时也可以是一个完整的 URL
// repo: "https://gitea.mangmang.fun/mangmang/blog",
// 默认从 `repo` 内容中推断为以下之一:
// "GitHub" / "GitLab" / "Gitee" / "Bitbucket" / "Source"
repoLabel: 'Gitea',
repoDisplay: true,
// 是否显示编辑链接
editLink: true,
// 文档源码目录
docsDir: 'src',
// 页脚配置
footer:
"<a href='https://beian.miit.gov.cn/' target='_blank'>蜀ICP备2025176018号-1</a> | Powered by VuePress | Theme by Hope",
displayFooter: true,
// 是否显示最后更新时间
lastUpdated: true,
// 深色模式配置(toggle表示用户可以切换)
darkmode: 'toggle',
// 博客相关配置
blog: {
description: '一个后端开发者',
intro: '/intro.html',
},
// Markdown增强功能配置
markdown: {
align: true, // 启用文本对齐
attrs: true, // 启用属性支持
codeTabs: true, // 启用代码选项卡
component: true, // 启用组件支持
demo: true, // 启用演示支持
figure: true, // 启用图片描述
gfm: true, // 启用GitHub风格的Markdown
imgLazyload: true, // 启用图片懒加载
imgSize: true, // 启用图片尺寸设置
include: true, // 启用文件包含
mark: true, // 启用标记高亮
plantuml: true, // 启用PlantUML图表
spoiler: true, // 启用剧透标记
// 文本样式化配置
stylize: [
{
matcher: 'Recommended',
replacer: ({ tag }) => {
if (tag === 'em')
return {
tag: 'Badge',
attrs: { type: 'tip' },
content: 'Recommended',
};
},
},
],
sub: true, // 启用下标
sup: true, // 启用上标
tabs: true, // 启用选项卡
tasklist: true, // 启用任务列表
vPre: true, // 启用v-pre支持
},
// 插件配置
plugins: {
// 启用博客功能
blog: true,
// 启用本地搜索
search: true,
// 组件配置
components: {
components: ['Badge', 'VPCard'],
},
// 图标配置
icon: {
prefix: 'fa6-solid:',
},
},
},
{
// 自定义主题配置
custom: true,
},
);
+1 -1
View File
@@ -1,6 +1,6 @@
---
home: true
layout: BlogHome
layout: Blog
hero: true
title: 博客主页
heroText: ''
+3 -7
View File
@@ -2,13 +2,9 @@
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"skipLibCheck": true,
"target": "ES2022"
},
"include": [
"src/.vuepress/**/*.ts",
"src/.vuepress/**/*.vue"
],
"exclude": [
"node_modules"
]
"include": ["src/.vuepress/**/*.ts", "src/.vuepress/**/*.vue"],
"exclude": ["node_modules"]
}