Compare commits
2 Commits
9e9e82c1dd
...
8da5cf2593
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8da5cf2593 | ||
|
|
05349b2628 |
14
.cursor/rules/ragflow-vuepress-plume-dataset.mdc
Normal file
14
.cursor/rules/ragflow-vuepress-plume-dataset.mdc
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
description: RAGFlow 检索 Plume 主题时必须使用 VuePress2-Plume 数据集,避免选错知识库
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# RAGFlow 与 Plume 文档
|
||||
|
||||
当任务涉及 **vuepress-theme-plume**、**Plume 主题**、**主题配置 / 博客 / 分类 / 标签 / 侧边栏** 等与 Plume 相关的问题,需要通过 **RAGFlow MCP**(`ragflow_retrieval`)查官方文档时:
|
||||
|
||||
- **必须使用数据集**:**VuePress2-Plume**(名称)。
|
||||
- 若接口需要 `dataset_ids`:当前知识库 ID 为 **`e57440b52aa711f1845c9627700ac9a4`**(与名称对应;若后台重建数据集请以维护者为准更新本条)。
|
||||
- **禁止**在未确认数据集的情况下用其它知识库代替 Plume 文档检索,以免污染答案或引发环境异常。
|
||||
|
||||
与 Plume 无关的问题(例如通用 VuePress 2 核心、本项目业务逻辑)再按需选择其它数据集或不指定 `dataset_ids`。
|
||||
17
.cursor/rules/si-meng-repo-and-deploy-context.mdc
Normal file
17
.cursor/rules/si-meng-repo-and-deploy-context.mdc
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
description: 双仓库结构、本地构建与发布流程;启动时加载项目上下文
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# SiMengWebSite_Notes — 仓库与部署上下文
|
||||
|
||||
在开始涉及 **构建、发布、静态资源路径、`_publish`** 或 **多仓库** 的任务前,请先阅读仓库根目录的 **`PROJECT_BRIEF.local.md`**(若存在)。该文件为本地说明,不进入 Git,含完整背景与命令示例。
|
||||
|
||||
## 速记(无本地文件时的兜底)
|
||||
|
||||
- **源码仓**:仓库根目录 → 远程示例 `SiMengWebSite_Notes.git`;日常开发与提交 Markdown / VuePress 配置。
|
||||
- **构建仓**:**`_publish/`** 内为 **独立 Git 仓库** → 远程示例 `build_notes_simengweb.git`;**只提交** `npm run docs:build` 产出(`docs/.vuepress/dist` 的内容同步至此)。
|
||||
- **原因**:业务服务器性能不足,**不在线上构建**;本地构建后推送构建仓,**服务器拉取更新**即可换页。
|
||||
- **`_publish/`** 与 **`PROJECT_BRIEF.local.md`** 已被根目录 `.gitignore` 忽略,勿把构建产物或该简介误提交进源码仓。
|
||||
|
||||
若缺少 `PROJECT_BRIEF.local.md`,可向维护者索取模板或根据上述速记操作,避免在源码仓中提交 `dist` 或误改发布仓历史。
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,6 +8,9 @@ docs/.vuepress/dist
|
||||
.DS_Store
|
||||
*.log
|
||||
|
||||
# Local project brief for agents (do not commit)
|
||||
PROJECT_BRIEF.local.md
|
||||
|
||||
# Build and Publish output
|
||||
_publish/
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import RImg from './theme/components/RImg.vue'
|
||||
import './theme/styles/custom.css'
|
||||
|
||||
export default defineClientConfig({
|
||||
enhance({ app }) {
|
||||
|
||||
@@ -3,12 +3,23 @@ import { plumeTheme } from 'vuepress-theme-plume'
|
||||
import { viteBundler } from '@vuepress/bundler-vite'
|
||||
import { commentPlugin } from '@vuepress/plugin-comment'
|
||||
import { umamiAnalyticsPlugin } from '@vuepress/plugin-umami-analytics'
|
||||
import { enrichBlogReadingTimePlugin } from './plugins/enrichBlogReadingTime'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
/** 博客目录名 → 中文分类展示(Plume 从路径自动生成分类,此处仅改展示名) */
|
||||
const BLOG_CATEGORY_LABEL_ZH: Record<string, string> = {
|
||||
blog: '博客',
|
||||
competition: '竞赛',
|
||||
technology: '技术',
|
||||
website: '网站',
|
||||
elysia: '爱莉希雅',
|
||||
collect: '合集',
|
||||
}
|
||||
|
||||
export default defineUserConfig({
|
||||
base: '/',
|
||||
lang: 'zh-CN',
|
||||
@@ -23,6 +34,7 @@ export default defineUserConfig({
|
||||
viteOptions: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@theme/Blog/VPPostItem.vue': path.resolve(__dirname, './theme/Blog/VPPostItem.vue'),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -62,7 +74,12 @@ export default defineUserConfig({
|
||||
layout: 'left',
|
||||
// width: 300,
|
||||
compact: true
|
||||
}
|
||||
},
|
||||
categoriesTransform: (categories) =>
|
||||
categories.map((c) => ({
|
||||
...c,
|
||||
name: BLOG_CATEGORY_LABEL_ZH[c.name] ?? c.name,
|
||||
})),
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -106,5 +123,6 @@ export default defineUserConfig({
|
||||
domains: ['www.simengweb.com'],
|
||||
cache: true
|
||||
}),
|
||||
enrichBlogReadingTimePlugin(),
|
||||
],
|
||||
})
|
||||
|
||||
61
docs/.vuepress/plugins/enrichBlogReadingTime.ts
Normal file
61
docs/.vuepress/plugins/enrichBlogReadingTime.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import fs from 'node:fs'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import type { App, PluginFunction } from 'vuepress'
|
||||
|
||||
const HMR_TAIL = `if (import.meta.webpackHot) {
|
||||
import.meta.webpackHot.accept()
|
||||
if (__VUE_HMR_RUNTIME__.updateBlogPostData) {
|
||||
__VUE_HMR_RUNTIME__.updateBlogPostData(blogPostData)
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(({ blogPostData }) => {
|
||||
__VUE_HMR_RUNTIME__.updateBlogPostData(blogPostData)
|
||||
})
|
||||
}
|
||||
`
|
||||
|
||||
async function enrichBlogData(app: App): Promise<void> {
|
||||
const tempFile = app.dir.temp('internal/blogData.js')
|
||||
if (!fs.existsSync(tempFile)) return
|
||||
|
||||
let blogPostData: Record<string, unknown>[]
|
||||
try {
|
||||
const url = `${pathToFileURL(tempFile).href}?t=${Date.now()}`
|
||||
const mod = await import(url) as { blogPostData: Record<string, unknown>[] }
|
||||
blogPostData = mod.blogPostData.map(p => ({ ...p }))
|
||||
}
|
||||
catch {
|
||||
return
|
||||
}
|
||||
|
||||
const byPath = new Map<string, { minutes: number; words: number }>()
|
||||
for (const page of app.pages) {
|
||||
const rt = page.data.readingTime as { minutes: number; words: number } | undefined
|
||||
if (rt && typeof rt.words === 'number') byPath.set(page.path, rt)
|
||||
}
|
||||
|
||||
for (const post of blogPostData) {
|
||||
const rt = byPath.get(post.path as string)
|
||||
if (rt) post.readingTime = rt
|
||||
}
|
||||
|
||||
await app.writeTemp(
|
||||
'internal/blogData.js',
|
||||
`export const blogPostData = ${JSON.stringify(blogPostData)}\n\n${HMR_TAIL}\n`,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在主题写入 blogData 之后,把各页的 readingTime(字数、分钟)并入博客列表数据,
|
||||
* 供自定义 VPPostItem 在 /blog/ 列表展示。
|
||||
*/
|
||||
export function enrichBlogReadingTimePlugin(): PluginFunction {
|
||||
return () => ({
|
||||
name: 'enrich-blog-reading-time',
|
||||
onPrepared: async (app: App) => {
|
||||
await enrichBlogData(app)
|
||||
},
|
||||
})
|
||||
}
|
||||
416
docs/.vuepress/theme/Blog/VPPostItem.vue
Normal file
416
docs/.vuepress/theme/Blog/VPPostItem.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ReadingTime } from '@vuepress/plugin-reading-time/client'
|
||||
import { getReadingTimeLocale, useReadingTimeLocaleConfig } from '@vuepress/plugin-reading-time/client'
|
||||
import type { BlogPostCoverStyle, ThemeBlogPostItem } from 'vuepress-theme-plume/shared'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import { isMobile as _isMobile } from '@vuepress/helper/client'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { withBase } from 'vuepress/client'
|
||||
import { useData, useInternalLink, useTagColors } from 'vuepress-theme-plume/composables'
|
||||
|
||||
type PostItem = ThemeBlogPostItem & { readingTime?: ReadingTime }
|
||||
|
||||
const props = defineProps<{
|
||||
post: PostItem
|
||||
index: number
|
||||
}>()
|
||||
|
||||
const isMobile = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
isMobile.value = _isMobile(navigator.userAgent)
|
||||
window.addEventListener('resize', () => {
|
||||
isMobile.value = _isMobile(navigator.userAgent)
|
||||
})
|
||||
})
|
||||
|
||||
const { blog } = useData()
|
||||
const colors = useTagColors()
|
||||
const { categories: categoriesLink, tags: tagsLink } = useInternalLink()
|
||||
const readingLocale = useReadingTimeLocaleConfig()
|
||||
|
||||
const createTime = computed(() => props.post.createTime?.split(/\s|T/)[0].replace(/\//g, '-'))
|
||||
const categoryList = computed(() => props.post.categoryList ?? [])
|
||||
|
||||
const readingTimeText = computed(() => {
|
||||
const rt = props.post.readingTime
|
||||
const loc = readingLocale.value
|
||||
if (!rt || !loc) return null
|
||||
return getReadingTimeLocale(rt, loc)
|
||||
})
|
||||
|
||||
const sticky = computed(() => {
|
||||
if (typeof props.post.sticky === 'boolean') {
|
||||
return props.post.sticky
|
||||
}
|
||||
else if (typeof props.post.sticky === 'number') {
|
||||
return props.post.sticky >= 0
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const tags = computed(() => {
|
||||
const tagTheme = blog.value.tagsTheme ?? 'colored'
|
||||
|
||||
return (props.post.tags ?? [])
|
||||
.slice(0, 4)
|
||||
.map(tag => ({
|
||||
name: tag,
|
||||
className: colors.value[tag] ? `vp-tag-${colors.value[tag]}` : `tag-${tagTheme}`,
|
||||
}))
|
||||
})
|
||||
|
||||
const cover = computed<BlogPostCoverStyle | null>(() => {
|
||||
if (!props.post.cover)
|
||||
return null
|
||||
const opt = blog.value.postCover ?? 'right'
|
||||
const options = typeof opt === 'string' ? { layout: opt } : opt
|
||||
return { layout: 'right', ratio: '4:3', ...options, ...props.post.coverStyle }
|
||||
})
|
||||
|
||||
const coverLayout = computed(() => {
|
||||
if (isMobile.value)
|
||||
return 'top'
|
||||
const layout = cover.value?.layout ?? 'right'
|
||||
const odd = (props.index + 1) % 2 === 1
|
||||
if (layout === 'odd-left')
|
||||
return odd ? 'left' : 'right'
|
||||
if (layout === 'odd-right')
|
||||
return odd ? 'right' : 'left'
|
||||
return layout
|
||||
})
|
||||
|
||||
const coverCompact = computed(() => {
|
||||
if (props.post.excerpt || coverLayout.value === 'top')
|
||||
return false
|
||||
return cover.value?.compact ?? false
|
||||
})
|
||||
|
||||
const coverStyles = computed(() => {
|
||||
if (!cover.value)
|
||||
return null
|
||||
let ratio: number
|
||||
if (typeof cover.value.ratio === 'number') {
|
||||
ratio = cover.value.ratio
|
||||
}
|
||||
else {
|
||||
const [w, h] = cover.value.ratio!.split(/[:/]/).map(Number)
|
||||
ratio = h / w
|
||||
}
|
||||
if (coverLayout.value === 'left' || coverLayout.value === 'right') {
|
||||
const w = cover.value.width ?? 240
|
||||
return { width: `${w}px`, height: `${w * ratio}px` }
|
||||
}
|
||||
|
||||
return { height: 0, paddingBottom: `${ratio * 100}%` }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="vp-blog-post-item" data-allow-mismatch
|
||||
:class="{ 'has-cover': post.cover, [coverLayout]: cover, 'draft': post.draft }"
|
||||
>
|
||||
<div
|
||||
v-if="post.cover" class="post-cover" data-allow-mismatch
|
||||
:class="{ compact: coverCompact }" :style="coverStyles"
|
||||
>
|
||||
<img :src="withBase(post.cover)" :alt="post.title" loading="lazy">
|
||||
</div>
|
||||
<div class="blog-post-item-content">
|
||||
<h3>
|
||||
<span v-if="sticky" class="sticky">TOP</span>
|
||||
<span v-if="post.draft" class="draft">DRAFT</span>
|
||||
<span v-if="post.encrypt" class="icon-lock vpi-lock" />
|
||||
<VPLink :href="post.path" :text="post.title" />
|
||||
</h3>
|
||||
<div class="post-meta">
|
||||
<div v-if="categoryList.length" class="category-list">
|
||||
<span class="icon vpi-folder" />
|
||||
<template v-for="(cate, i) in categoryList" :key="i">
|
||||
<VPLink :href="categoriesLink ? `${categoriesLink.link}?id=${cate.id}` : undefined">
|
||||
{{ cate.name }}
|
||||
</VPLink>
|
||||
<span v-if="i !== categoryList.length - 1">/</span>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="tags.length" class="tag-list">
|
||||
<span class="icon vpi-tag" />
|
||||
<template v-for="tag in tags" :key="tag.name">
|
||||
<VPLink
|
||||
class="tag"
|
||||
:class="tag.className"
|
||||
:href="tagsLink ? `${tagsLink.link}?tag=${tag.name}` : undefined"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</VPLink>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="readingTimeText" class="reading-time">
|
||||
<span class="icon vpi-books" />
|
||||
<span>{{ readingTimeText.words }}</span>
|
||||
<span>{{ readingTimeText.time }}</span>
|
||||
</div>
|
||||
<div v-if="createTime" class="create-time">
|
||||
<span class="icon vpi-clock" />
|
||||
<span>{{ createTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="post.excerpt" class="vp-doc excerpt" v-html="post.excerpt" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vp-blog-post-item {
|
||||
padding: 16px;
|
||||
margin: 0 -16px;
|
||||
background-color: var(--vp-c-bg);
|
||||
transition: background-color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.vp-blog-post-item.draft {
|
||||
background-color: var(--vp-c-warning-soft);
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover:where(.left, .right) {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 419px) {
|
||||
.vp-blog-post-item.has-cover:where(.left, .right) {
|
||||
display: block;
|
||||
gap: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover.right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.post-cover {
|
||||
position: relative;
|
||||
align-self: center;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover.left .post-cover.compact {
|
||||
margin: -24px 0 -24px -20px;
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover.right .post-cover.compact {
|
||||
margin: -24px -20px -24px 0;
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover.top .post-cover {
|
||||
margin: -16px -16px 16px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 419px) {
|
||||
.vp-blog-post-item.has-cover.top .post-cover {
|
||||
width: calc(100% + 40px);
|
||||
margin: -24px -20px 24px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.post-cover img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover:hover .post-cover img {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover.left .post-cover.compact {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover.right .post-cover.compact {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.blog-post-item-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.vp-blog-post-item.has-cover .blog-post-item-content {
|
||||
flex: 1 2;
|
||||
}
|
||||
|
||||
.blog-post-item-content .sticky,
|
||||
.blog-post-item-content .draft {
|
||||
display: inline-block;
|
||||
padding: 3px 6px;
|
||||
margin-right: 0.5rem;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
color: var(--vp-c-text-2);
|
||||
background-color: var(--vp-c-brand-soft);
|
||||
border-radius: 4px;
|
||||
transition: var(--vp-t-color);
|
||||
transition-property: color, background-color;
|
||||
}
|
||||
|
||||
.blog-post-item-content .draft {
|
||||
color: var(--vp-c-warning-1);
|
||||
background-color: var(--vp-c-warning-soft);
|
||||
}
|
||||
|
||||
.blog-post-item-content .icon-lock {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: 8px;
|
||||
margin-left: 3px;
|
||||
color: var(--vp-c-text-3);
|
||||
transition: var(--vp-t-color);
|
||||
transition-property: color;
|
||||
}
|
||||
|
||||
.blog-post-item-content h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.blog-post-item-content h3 a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.blog-post-item-content h3:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.blog-post-item-content h3:hover .sticky {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.blog-post-item-content .excerpt {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.vp-blog-post-item {
|
||||
padding: 24px 20px;
|
||||
margin: 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--vp-shadow-1);
|
||||
transition: var(--vp-t-color);
|
||||
transition-property: background-color, color, box-shadow;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.vp-blog-post-item:hover {
|
||||
box-shadow: var(--vp-shadow-2);
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta .reading-time {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta .tag-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta .tag-list .tag {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
margin-right: 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: var(--vp-tag-color);
|
||||
background-color: var(--vp-tag-bg);
|
||||
border-radius: 3px;
|
||||
transition: color var(--vp-t-color), background-color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta .tag-list .tag:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta .icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 0.3rem;
|
||||
color: var(--vp-c-text-3);
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.blog-post-item-content .post-meta a {
|
||||
font-weight: normal;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.excerpt.vp-doc :deep(p) {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.excerpt.vp-doc :deep(p:first-of-type) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.excerpt.vp-doc :deep(p:last-of-type) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.excerpt.vp-doc :deep(p strong) {
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.excerpt.vp-doc :deep(div[class*="language-"]) {
|
||||
margin: 16px -16px;
|
||||
}
|
||||
|
||||
@media (min-width: 496px) {
|
||||
.excerpt.vp-doc :deep(div[class*="language-"]) {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +1,31 @@
|
||||
/**
|
||||
* 博客:分类(中文展示名)与标签(英文)之间留出间距;分类路径本身前后略松一些
|
||||
*/
|
||||
.vp-blog-post-item .post-meta .category-list {
|
||||
padding: 0.3em 0.65em;
|
||||
margin-right: 0.75rem;
|
||||
margin-left: 0.15rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.vp-blog-post-item .post-meta .category-list .icon {
|
||||
margin-right: 0.45rem;
|
||||
}
|
||||
|
||||
.vp-blog-post-item .post-meta .category-list a + span {
|
||||
margin-inline: 0.2em;
|
||||
}
|
||||
|
||||
/* 文章页面包屑:各级目录名前后略留白 */
|
||||
.vp-breadcrumb ol {
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
.vp-breadcrumb ol li .breadcrumb:not(.current) {
|
||||
padding: 0.15em 0.5em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:root {
|
||||
/** 主题颜色 */
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 便宜免费的大模型 API 整合 ( 2025年11月11日 )
|
||||
createTime: 2025/11/11 13:54:02
|
||||
cover: /images/elysia/1.jpg
|
||||
coverStyle:
|
||||
layout: right
|
||||
permalink: /article/free_model_api/
|
||||
tags:
|
||||
- llm
|
||||
---
|
||||
|
||||
百度千帆、讯飞星火、腾讯混元均有免费在线额度,SCNet 提供 0.1 元/百万 tokens 的超低价大模型,轻量任务先薅免费,量大了再掏 0.1 元,稳!
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 第十四届蓝桥杯大赛软件赛国赛 Python 研究生组(正在更新)
|
||||
createTime: 2026/01/09 15:57:22
|
||||
cover: /images/elysia/8.jpg
|
||||
coverStyle:
|
||||
layout: right
|
||||
permalink: /archives/b1c77a1d-d402-4788-8049-fa3aeb12ebd0/
|
||||
tags:
|
||||
- contest
|
||||
---
|
||||
## 一、X 质数
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 码蹄杯 2024 年真题集详解
|
||||
createTime: 2026/01/09 16:24:00
|
||||
cover: /images/elysia/11.jpg
|
||||
coverStyle:
|
||||
layout: left
|
||||
permalink: /archives/d0ad06b9-d675-461c-a8ce-f47baeeb291d/
|
||||
tags:
|
||||
- contest
|
||||
---
|
||||
|
||||
码蹄杯真题库:2022年-2024年码蹄杯题集
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
---
|
||||
title: 爱莉希雅语录
|
||||
createTime: 2026/01/08 15:39:17
|
||||
cover: /images/elysia/2.png
|
||||
coverStyle:
|
||||
layout: left
|
||||
permalink: /archives/a5b3ea8e-7c3c-40a1-a737-26e911623da8/
|
||||
tags:
|
||||
- honkai-impact-3rd
|
||||
---
|
||||
嗨,亲爱的来访者♪ 欢迎来到这片收集了“真我”与“美丽”碎片的园圃。在这里,你会读到执拗花朵在暴雨中的坚持,也会听见逐火英桀们为文明奏响的最后颂歌。请怀着期待慢慢翻阅吧,愿这些如星光般的文字能陪你开启属于自己的闪耀旅程,毕竟……你本身就是这世间最瑰丽的馈赠呢♪
|
||||
<!-- more -->
|
||||

|
||||
1. 执拗的花朵永远不会因暴雨而褪去颜色,你的决心也一定能在绝境中绽放真我。
|
||||
|
||||
2. 愿你前行的道路有群星闪耀。愿你留下的足迹有百花绽放。你即是上帝的馈赠,世界因你而瑰丽。
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 在 Windows10 上部署 WSL2 并启动 ubuntu 虚拟机
|
||||
createTime: 2025/09/29 07:13:17
|
||||
cover: /images/elysia/3.jpg
|
||||
coverStyle:
|
||||
layout: right
|
||||
permalink: /article/deploying-wsl2-on-windows-10/
|
||||
tags:
|
||||
- wsl
|
||||
---
|
||||
|
||||
嗨呀~让我们在 Windows10 专业版上部署 WSL2 并启动 ubuntu 20.04 虚拟机吧~
|
||||
@@ -113,8 +112,6 @@ Press any key to continue...
|
||||
#### 错误码 0x80370102 虚拟化技术没有开启
|
||||
如果遇到这个错误,可能是因为虚拟化技术没有开启哦~如果是在真实机上操作的话,需要进入BIOS开启虚拟化技术呢~开启之后,可以在任务管理器 -> 性能 -> CPU 的详情页面中,看到下方的虚拟化状态显示为「已启用」哦~
|
||||
|
||||

|
||||
|
||||
如果是在虚拟机中操作的话,就需要启用嵌套虚拟化技术啦~以VMware为例:
|
||||
|
||||
先关闭虚拟机,然后找到虚拟机的.vmx配置文件(通常在虚拟机目录下),在文件末尾添加一行:`vhv.enable = "TRUE"`
|
||||
@@ -145,5 +142,4 @@ wsl --terminate Ubuntu-20.04
|
||||
接下来,我们还可以通过VS Code来进行开发哦~不过需要先安装一些扩展呢:'Remote - SSH'和'WSL'
|
||||
|
||||
安装好扩展后,在VS Code左侧打开远程资源管理器,就可以看到Ubuntu20.04虚拟机啦~直接点击连接就可以了哦~不过第一次连接可能需要启动一下虚拟机,会有点慢呢,耐心等待一下吧~
|
||||

|
||||
连接成功之后,就可以愉快地进行开发啦~是不是很简单呢~
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
title: WSL2 核心操作指南
|
||||
createTime: 2025/09/30 16:08:32
|
||||
cover: /images/elysia/4.jpg
|
||||
permalink: /article/operate-wsl2/
|
||||
tags:
|
||||
- wsl
|
||||
---
|
||||
这篇文章主要讲WSL2虚拟机核心操作哦~它基于轻量级Hyper-V运行,像贴心小精灵默默工作~还能用命令行精细控制!接下来讲启动/关闭、实例管理、资源配置、网络操作、备份迁移这五大操作,是不是很期待呢~♪
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 通过位运算快速生成所有的子序列
|
||||
createTime: 2026/01/09 16:15:00
|
||||
cover: /images/elysia/10.jpg
|
||||
coverStyle:
|
||||
layout: right
|
||||
permalink: /archives/ea20bdda-0d49-4472-a647-2e305a930d11/
|
||||
tags:
|
||||
- algorithms
|
||||
---
|
||||
## 一、子序列的本质
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 快速幂算法详解
|
||||
createTime: 2026/01/09 16:05:00
|
||||
cover: /images/elysia/9.jpg
|
||||
coverStyle:
|
||||
layout: left
|
||||
permalink: /archives/1325a3bf-91d7-43ff-9630-e894549e12c1/
|
||||
tags:
|
||||
- algorithms
|
||||
---
|
||||
## 简介
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: Python 字符串格式化全指南
|
||||
createTime: 2026/01/09 14:00:48
|
||||
cover: /images/elysia/7.jpg
|
||||
coverStyle:
|
||||
layout: left
|
||||
permalink: /archives/56ea3081-9c69-43d7-96c8-2812ec08be2c/
|
||||
tags:
|
||||
- python
|
||||
---
|
||||
|
||||
## 字符串格式化
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: 原码、反码、补码
|
||||
createTime: 2026/01/08 16:34:05
|
||||
cover: /images/elysia/6.jpg
|
||||
coverStyle:
|
||||
layout: right
|
||||
permalink: /archives/6f41cabe-41e6-4a09-9f1c-af7dd709a35d/
|
||||
tags:
|
||||
- cs-fundamentals
|
||||
---
|
||||
|
||||
欢迎来到 0 与 1 的魔法派对!这篇文章将带你揭开原码、反码与补码的奥秘,看计算机如何巧妙地用补码化减为加。让我们一起翻开这页,去捕捉二进制底层那份迷人的理性之美吧♪
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
title: 通过 EdgeOne Pages 搭建图床
|
||||
createTime: 2025/09/29 02:28:17
|
||||
cover: /images/elysia/5.jpg
|
||||
permalink: /article/8gihio2v/
|
||||
tags:
|
||||
- image-hosting
|
||||
---
|
||||
|
||||
各位~今天要给大家介绍一个超棒的图床搭建方法哦!EdgeOne Pages 是腾讯云提供的静态网站托管服务,而且还有免费额度可以使用呢,对于日常需求来说完全足够啦~
|
||||
|
||||
17
script/commit-notes.ps1
Normal file
17
script/commit-notes.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
# 提交笔记站源码变更(尊重 .gitignore,不会加入 _publish、dist、PROJECT_BRIEF.local.md 等)
|
||||
# 用法: .\script\commit-notes.ps1 -m "提交说明"
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $Message
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $root
|
||||
|
||||
git add docs/.vuepress docs/blog .cursor/rules script/commit-notes.ps1
|
||||
$staged = @(git diff --cached --name-only)
|
||||
if ($staged.Count -eq 0) {
|
||||
Write-Host "No staged changes."
|
||||
exit 0
|
||||
}
|
||||
git commit -m $Message
|
||||
Reference in New Issue
Block a user