Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import markdownItWsClassstyles from "./markdown-it-ws-classstyles";
import markdownItWsRenderInline from "./markdown-it-ws-render-inline";
import markdownItWsRename from "./markdown-it-ws-rename";
import markdownItWsTopicTitle from "./markdown-it-ws-topicTitle";
import { markdownItCollapsed } from "./markdownItCollapsed.mts";
import { markdownItWsCollapsed } from "./markdown-it-ws-collapsed.mts";
import generateSidebar from "./config/sidebar.config";
import markdownItWsRemoveCodeAttr from "./markdown-it-ws-remove-code-attr";
import {markdownItTrimBrSpaces} from "./markdown-it-trim-br-spaces";
import {markdownItKtorAutoPicture} from "./markdown-it-ktor-auto-picture";

const mkDiffGrammarPath = resolve(__dirname, './shiki-mk-diff.json')
const mkDiffGrammar = JSON.parse(readFileSync(mkDiffGrammarPath, 'utf-8'))
Expand Down Expand Up @@ -69,8 +71,9 @@ function createContainer(md, name, className, defaultTitle) {
// https://vitepress.dev/reference/site-config
export default defineConfig({
cleanUrls: true,
lastUpdated: true,
lastUpdated: false,
ignoreDeadLinks: true,
metaChunk: true,
lang: 'zh-Hans',
title: 'Open AIDoc',
head: [
Expand Down Expand Up @@ -176,12 +179,12 @@ export default defineConfig({
themeConfig: {
nav: [
{ text: 'Kotlin', link: '/zh-Hant/kotlin/home' },
{ text: 'Kotlin Multiplatform', link: '/zh-Hantkmp/get-started' },
{ text: 'Ktor', link: '/zh-Hantktor/welcome'},
{ text: 'Koog', link: '/zh-Hantkoog/' },
{ text: 'Koin', link: '/zh-Hantkoin/setup/koin' },
{ text: 'SQLDelight', link: '/zh-Hantsqldelight/index' },
{ text: 'Coil', link: '/zh-Hantcoil/overview' },
{ text: 'Kotlin Multiplatform', link: '/zh-Hant/kmp/get-started' },
{ text: 'Ktor', link: '/zh-Hant/ktor/welcome'},
{ text: 'Koog', link: '/zh-Hant/koog/' },
{ text: 'Koin', link: '/zh-Hant/koin/setup/koin' },
{ text: 'SQLDelight', link: '/zh-Hant/sqldelight/index' },
{ text: 'Coil', link: '/zh-Hant/coil/overview' },
],
sidebar: {
"/zh-Hant/koin/": generateSidebar(SiteLocaleConfig['zh-Hant'], DocsTypeConfig.koin),
Expand Down Expand Up @@ -344,9 +347,11 @@ export default defineConfig({
md.use(markdownItMkCodeTabs)
md.use(markdownItMkLinks)
md.use(markdownItDiffTitleWrapper)
md.use(markdownItCollapsed)
md.use(markdownItWsCollapsed)
md.use(markdownItTrimBrSpaces)
md.use(markdownItWsVars);
md.use(markdownItMKVars);
md.use(markdownItKtorAutoPicture);

md.use(markdownItWsRename)
md.use(markdownItWsTopicTitle)
Expand Down
99 changes: 99 additions & 0 deletions docs/.vitepress/markdown-it-ktor-auto-picture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// .vitepress/plugins/markdownItKtorAutoPicture.js

/**
* 用于自动处理 ktor 图片的深浅色主题。
* 此版本增加了路径判断逻辑,仅对 'ktor' 目录下的 Markdown 文件生效。
*
* 它会同时处理:
* 1. 原始 HTML 块 (html_block) 中的 <img> 标签。
* 2. 标准 Markdown 语法 (image) 的 ![alt](src)。
*/
export const markdownItKtorAutoPicture = (md) => {
console.log('[AutoPicture] 插件已初始化 (v2,带 ktor 路径过滤)');

// 定义一个正则表达式来匹配所有 'ktor' 目录下的路径
// /(^|\/)ktor\// 匹配 "ktor/" 或 ".../ktor/"
const ktorPathRegex = /(^|\/)ktor\//;

// --- 1. 修改原始 HTML 块的渲染规则 ---
const defaultHtmlBlockRenderer = md.renderer.rules.html_block;
md.renderer.rules.html_block = (tokens, idx, options, env, self) => {
const pagePath = env.relativePath || ''; // 获取当前处理的 md 文件路径

// [新增逻辑] 如果文件路径不符合要求,则直接跳过,不进行任何处理
if (!ktorPathRegex.test(pagePath)) {
return defaultHtmlBlockRenderer(tokens, idx, options, env, self);
}

const content = tokens[idx].content;
const imgTagRegex = /<img[^>]*>/g;

if (!content.match(imgTagRegex)) {
return defaultHtmlBlockRenderer(tokens, idx, options, env, self);
}

console.log(`[AutoPicture] ✅ 在 Ktor 路径 (${pagePath}) 下发现 <img> 块,开始处理...`);

const newContent = content.replace(imgTagRegex, (imgTag) => {
if (imgTag.includes('data-light-src')) return imgTag;

const srcMatch = imgTag.match(/src="([^"]+)"/);
if (!srcMatch || !srcMatch[1] || srcMatch[1].startsWith('http')) {
return imgTag;
}
const src = srcMatch[1];

const dotIndex = src.lastIndexOf('.');
if (dotIndex === -1) return imgTag;

const lightSrc = src;
const darkSrc = `${src.substring(0, dotIndex)}_dark${src.substring(dotIndex)}`;

console.log(`[AutoPicture] - 正在处理: ${lightSrc} -> 生成暗色版路径: ${darkSrc}`);

let modifiedTag = imgTag;
if (/class="[^"]+"/.test(modifiedTag)) {
modifiedTag = modifiedTag.replace(/class="([^"]+)"/, 'class="$1 themed-image"');
} else {
modifiedTag = modifiedTag.replace('<img ', '<img class="themed-image" ');
}

const closingTag = modifiedTag.endsWith('/>') ? '/>' : '>';
let tagBody = modifiedTag.slice(0, modifiedTag.length - closingTag.length).trim();
tagBody += ` data-light-src="${lightSrc}" data-dark-src="${darkSrc}"`;

return tagBody + ' ' + closingTag;
});

tokens[idx].content = newContent;
return defaultHtmlBlockRenderer(tokens, idx, options, env, self);
};

// --- 2. 修改标准 Markdown 图片的渲染规则 ---
const defaultImageRenderer = md.renderer.rules.image;
md.renderer.rules.image = (tokens, idx, options, env, self) => {
const pagePath = env.relativePath || ''; // 获取当前处理的 md 文件路径

// [新增逻辑] 同样在这里添加路径判断
if (!ktorPathRegex.test(pagePath)) {
return defaultImageRenderer(tokens, idx, options, env, self);
}

const token = tokens[idx];
const src = token.attrGet('src');

if (src && !src.startsWith('http')) {
const dotIndex = src.lastIndexOf('.');
if (dotIndex !== -1) {
console.log(`[AutoPicture] ✅ 正在处理 Ktor 路径 (${pagePath})下的 Markdown 图片: ${src}`);
const lightSrc = src;
const darkSrc = `${src.substring(0, dotIndex)}_dark${src.substring(dotIndex)}`;

token.attrSet('data-light-src', lightSrc);
token.attrSet('data-dark-src', darkSrc);
token.attrJoin('class', 'themed-image');
}
}
return defaultImageRenderer(tokens, idx, options, env, self);
};
};
13 changes: 13 additions & 0 deletions docs/.vitepress/markdown-it-trim-br-spaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import MarkdownIt from 'markdown-it';

// 自定义 MarkdownIt 插件来去除不必要的空行
export const markdownItTrimBrSpaces: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
const originalParse = md.parse;

md.parse = function (src, env) {
// 移除 <br/> 后面的多余空格
const formattedSrc = src.replace(/<br\/>\s+/g, '<br/>');

return originalParse.call(this, formattedSrc, env);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function formatForVitePress(text) {
/**
* A markdown-it plugin to transform custom collapsible blocks into VitePress details containers.
*/
export const markdownItCollapsed: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
export const markdownItWsCollapsed: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
// 保存原始的 parse 方法
const originalParse = md.parse;

Expand Down
34 changes: 33 additions & 1 deletion docs/.vitepress/styles/doc-cards.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
transition: var(--global-transition);
text-decoration: none;
color: inherit;
height: 260px;
min-height: 260px;
height: auto;
position: relative;
box-shadow: var(--global-shadow);
width: 100%;
Expand Down Expand Up @@ -94,6 +95,7 @@

font-size: 2.2rem;
font-weight: 800;
line-height: 1.2;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
letter-spacing: -0.02em;
Expand Down Expand Up @@ -202,4 +204,34 @@
z-index: 10;
text-transform: uppercase;
border: 1px solid rgba(255, 255, 255, 0.3);
}

/* 当屏幕宽度小于或等于 768px 时(覆盖大部分平板和手机) */
@media (max-width: 768px) {
.cardContent {
/* 将大内边距缩小,为内容腾出空间 */
padding: 1.5rem;
}

.card:hover .cardContent {
/* 相应地调整 hover 效果的内边距,虽然在移动端不常用 */
padding-left: 2rem;
}

.cardDescription {
font-size: 1rem;
max-width: 100%; /* 在移动端允许描述占据更多宽度 */
}
}

/* 当屏幕宽度小于或等于 480px 时(针对更小的手机屏幕) */
@media (max-width: 480px) {
.cardContent {
/* 在非常小的屏幕上可以进一步减小内边距 */
padding: 1.5rem 1.2rem; /* 上下 1.5rem, 左右 1.2rem */
}

.card .cardTitle {
font-size: 1.6rem;
}
}
89 changes: 89 additions & 0 deletions docs/.vitepress/theme/image-switcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// .vitepress/theme/image-switcher.js

// 定义一个正则表达式来匹配所有 'ktor' 相关的页面路径
// /\/ktor\// 会匹配 /ktor/、/zh-Hant/ktor/ 等路径
const ktorPathRegex = /\/ktor\//;

/**
* [带日志和路径判断版] 根据当前颜色主题,更新页面上所有被标记的图片的 src 属性。
* @param {boolean} isDark - 当前是否为暗色模式。
*/
function updateImageSources(isDark) {
// [新增逻辑] 在执行任何操作前,先检查当前页面的路径
// window.location.pathname 获取的是 URL 中域名之后的部分 (例如 /zh-Hant/ktor/...)
if (!ktorPathRegex.test(window.location.pathname)) {
// 如果不是 ktor 页面,则不执行任何操作,直接返回
return;
}

console.log(`[ImageSwitcher] Ktor page detected. Calling updateImageSources. isDark: ${isDark}`);

const images = document.querySelectorAll('img.themed-image[data-dark-src]');
console.log(`[ImageSwitcher] Found ${images.length} themed images to update.`);

images.forEach((img, index) => {
const lightSrc = img.dataset.lightSrc;
const darkSrc = img.dataset.darkSrc;

// ... (后续的图片处理逻辑保持不变)
if (isDark) {
if (img.src === darkSrc) return;

const imageChecker = new Image();
imageChecker.src = darkSrc;
imageChecker.onload = () => img.src = darkSrc;
imageChecker.onerror = () => {
console.error(`[ImageSwitcher] Dark image FAILED to load at path: ${darkSrc}. Falling back to light source.`);
if (img.src !== lightSrc) img.src = lightSrc;
};
} else {
if (img.src !== lightSrc) img.src = lightSrc;
}
});
}

/**
* [带日志版] 设置一个观察器来监听 VitePress 的主题切换。
*/
function setupThemeObserver() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
const isDark = document.documentElement.classList.contains('dark');
// updateImageSources 函数内部会自己判断路径,所以这里直接调用即可
updateImageSources(isDark);
}
});
});

observer.observe(document.documentElement, { attributes: true });
}

/**
* [带日志版] 这是需要导出的主函数。
*/
export function installImageSwitcher() {
if (typeof window === 'undefined') return;

window.requestAnimationFrame(() => {
console.log('[ImageSwitcher] Client script installed.');

// 1. 初始加载时立即检查并更新一次
const isInitiallyDark = document.documentElement.classList.contains('dark');
updateImageSources(isInitiallyDark);

// 2. 监听主题切换
setupThemeObserver();

// 3. 监听 VitePress 页面导航 (SPA 切换)
const appElement = document.getElementById('app');
if (appElement) {
const pageObserver = new MutationObserver(() => {
const isDark = document.documentElement.classList.contains('dark');
// 页面切换后,同样调用 updateImageSources,它会自己判断新页面的路径
updateImageSources(isDark);
});
pageObserver.observe(appElement, { childList: true, subtree: true });
}
});
}
3 changes: 3 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Highlight from "../component/Highlight.vue";
import Note from "../component/Note.vue";
import Tip from "../component/Tip.vue";
import YouTubeVideo from "../component/YouTubeVideo.vue";
import { installImageSwitcher } from './image-switcher.js';

export default {
extends: DefaultTheme,
Expand Down Expand Up @@ -94,6 +95,8 @@ export default {

app.component('YouTubeVideo', YouTubeVideo)

installImageSwitcher();

// Use our custom i18n implementation
app.use(createI18n())

Expand Down
16 changes: 12 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
"version": "1.0.0",
"main": "index.js",
"scripts": {
"preinstall": "npx only-allow pnpm",
"test": "echo \"Error: no test specified\" && exit 1",
"docs:dev": "vitepress dev docs",
"docs:build": "node --max-old-space-size=16384 node_modules/vitepress/bin/vitepress.js build docs",
"docs:build": "node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build docs",
"docs:preview": "vitepress preview docs"
},
"pnpm": {
"overrides": {
"vite": "npm:rolldown-vite@latest"
}
},
"keywords": [],
"author": "",
"license": "ISC",
Expand All @@ -19,12 +25,14 @@
"fs-extra": "^11.3.0",
"glob": "^11.0.3",
"gray-matter": "^4.0.3",
"js-yaml": "^4.1.0",
"markdown-it-container": "^4.0.0",
"p-limit": "^6.2.0",
"shiki": "^3.7.0",
"vitepress": "^1.6.3",
"js-yaml": "^4.1.0",
"xml2js": "^0.6.2"
"vitepress": "^2.0.0-alpha.11",
"xml2js": "^0.6.2",
"@vueuse/core": "^13.7.0",
"oxc-minify": "^0.82.2"
},
"dependencies": {
"aos": "^2.3.4"
Expand Down
Loading