feat(halo): 新增文章搜索和导出功能

- 添加文章搜索命令,支持按关键词筛选和发布状态过滤
- 新增文章导出功能,支持导出为 Markdown 和 JSON 格式
- 扩展国际化配置,添加相关翻译文本
- 更新功能增强计划文档,标记已完成功能
- 移除不再需要的下载信息和插件清单文件
This commit is contained in:
2026-04-26 17:35:37 +08:00
parent 5c4a16dc3a
commit b72f36926a
16 changed files with 565 additions and 666 deletions
+6 -2
View File
File diff suppressed because one or more lines are too long
+32 -20
View File
@@ -4,6 +4,38 @@
"basename": "Git团队协作指南(精简版)",
"path": "博客/Git团队协作指南(精简版).md"
},
{
"basename": "深度学习完全指南",
"path": "博客/深度学习完全指南.md"
},
{
"basename": "视觉语言模型技术综述",
"path": "博客/视觉语言模型技术综述.md"
},
{
"basename": "AI助你轻松上手LaTeX论文写作",
"path": "博客/AI助你轻松上手LaTeX论文写作.md"
},
{
"basename": "CLI在AI时代的浴火重生",
"path": "博客/CLI在AI时代的浴火重生.md"
},
{
"basename": "Docker部署完全指南",
"path": "博客/Docker部署完全指南.md"
},
{
"basename": "从全连接层到卷积",
"path": "博客/从全连接层到卷积.md"
},
{
"basename": "博客爬取报告",
"path": "博客/博客爬取报告.md"
},
{
"basename": "SEO分析报告",
"path": "博客/SEO分析报告.md"
},
{
"basename": "Git团队协作指南大纲",
"path": "博客/Git团队协作指南大纲.md"
@@ -32,26 +64,10 @@
"basename": "大模型赋能架构设计",
"path": "博客/大模型赋能架构设计.md"
},
{
"basename": "博客爬取报告",
"path": "博客/博客爬取报告.md"
},
{
"basename": "文章列表",
"path": "博客/文章列表.md"
},
{
"basename": "从全连接层到卷积",
"path": "博客/从全连接层到卷积.md"
},
{
"basename": "SEO分析报告",
"path": "博客/SEO分析报告.md"
},
{
"basename": "CLI在AI时代的浴火重生",
"path": "博客/CLI在AI时代的浴火重生.md"
},
{
"basename": "halo",
"path": "copilot/copilot-custom-prompts/halo.md"
@@ -68,10 +84,6 @@
"basename": "深入解析 Claude CodeVibe Coding 时代的 AI 编程利器",
"path": "博客/深入解析 Claude CodeVibe Coding 时代的 AI 编程利器.md"
},
{
"basename": "视觉语言模型技术综述",
"path": "博客/视觉语言模型技术综述.md"
},
{
"basename": "小组会议",
"path": "小组会议/26.04.04/小组会议.md"
+12 -12
View File
@@ -220,6 +220,18 @@
},
"active": "e7a7b303c61786dc",
"lastOpenFiles": [
"博客/深度学习完全指南.md",
"博客/视觉语言模型技术综述.md",
"博客/AI助你轻松上手LaTeX论文写作.md",
"博客/CLI在AI时代的浴火重生.md",
"博客/Docker部署完全指南.md",
"博客/从全连接层到卷积.md",
"博客/博客爬取报告.md",
"博客/SEO分析报告.md",
"博客/Git团队协作指南(精简版).md",
"obsidian-halo/src/modals/search-modal.ts",
"obsidian-halo/src/commands/search-posts.ts",
"obsidian-halo/src/commands/export-post.ts",
"博客/Git团队协作指南大纲.md",
"博客/uv工具推荐博客.md",
"博客/OpenClaw安装教程.md",
@@ -227,22 +239,14 @@
"博客/Git团队协作指南.md",
"博客/大数据技术栈.md",
"博客/大模型赋能架构设计.md",
"博客/博客爬取报告.md",
"obsidian-halo/src/commands/manage-taxonomy.ts",
"博客/文章列表.md",
"博客/从全连接层到卷积.md",
"obsidian-halo/src/modals/category-manager-modal.ts",
"obsidian-halo/src/modals/tag-manager-modal.ts",
"博客/SEO分析报告.md",
"obsidian-halo/src/commands/import-markdown.ts",
"obsidian-halo/src/modals/file-preview-modal.ts",
"obsidian-halo/src/modals",
"obsidian-halo/src/commands",
"博客/CLI在AI时代的浴火重生.md",
"博客/Git团队协作指南(精简版).md",
"obsidian-halo/src/service/image-uploader.ts",
"obsidian-halo/src/utils/image.ts",
"博客/check-credentials.ps1",
"obsidian-halo-zip-test/images/pat-zh.png",
"obsidian-halo-zip-test/images/settings-zh.png",
"obsidian-halo-zip-test/images/settings-en.png",
@@ -262,10 +266,6 @@
"博客/articles/article-03-ml-learning-path.md",
"博客/articles/article-02-python-efficiency.md",
"博客/articles/article-01-ai-beginner-guide.md",
"博客/视觉语言模型技术综述.md",
"find-skills-0.1.0/SKILL.md",
"小组会议/26.04.04/小组会议.md",
"小组会议/26.03.21 - 副本/小组会议.md",
"未命名.canvas"
]
}
@@ -8,27 +8,61 @@
- ✅ 更新已发布文章内容
- ✅ 多站点管理
- ✅ Frontmatter 元数据同步(标题、摘要、分类、标签、封面)
- ✅ 图片上传功能
- ✅ 文章列表增强(分页、筛选)
- ✅ Markdown 文件导入
- ✅ 删除文章功能(支持选择性删除)
- ✅ 标签管理功能
- ✅ 分类管理功能
- ✅ 导出文章为 Markdown
- ✅ 导出文章为 JSON
- ✅ 搜索 Halo 文章
---
## 功能增强方向
## 已完成功能(2024年实现)
### 1. 图片/附件上传TODO 清单已标注)
### 1. 图片/附件上传
**优先级:高 | 难度:中**
- [ ] 自动检测并上传笔记中的图片到 Halo 媒体库
- [ ] 支持粘贴截图自动上传
- [ ] 转换本地图片路径为 Halo URL
- [ ] 附件管理(支持其他文件类型)
- [x] 自动检测并上传笔记中的图片到 Halo 媒体库
- [x] 转换本地图片路径为 Halo URL
### 2. 批量操作功能
### 2. 批量操作功能
**优先级:高 | 难度:中**
- [ ] 批量发布:选择多个笔记一次性发布
- [ ] 批量同步:检测所有已发布笔记的更新状态
- [ ] 批量管理:统一管理 Halo 上的文章列表
- [x] 文章列表查看(支持分页、筛选)
- [x] 从 Markdown 文件导入文章
### 3. 双向同步增强
### 3. 标签/分类管理 ✅
**优先级:高 | 难度:中**
- [x] 标签管理(创建、编辑、删除)
- [x] 分类管理(创建、编辑、删除)
### 4. 导出功能 ✅
**优先级:中 | 难度:中**
- [x] 导出文章为 Markdown
- [x] 导出文章为 JSON(备份用)
### 5. 搜索功能 ✅
**优先级:中 | 难度:中**
- [x] 搜索 Halo 文章(按标题、slug 搜索)
- [x] 按发布状态筛选
### 6. 删除功能 ✅
**优先级:高 | 难度:中**
- [x] 选择性删除(仅 Halo / 仅本地 / 全部)
- [x] 二次确认机制
---
## 待实现功能
### 1. 双向同步增强
**优先级:高 | 难度:高**
- [ ] 差异检测:比较本地和 Halo 版本的修改时间
@@ -36,7 +70,7 @@
- [ ] 冲突解决:处理两边同时修改的情况
- [ ] 同步历史:记录每次同步的详情
### 4. 高级发布选项
### 2. 高级发布选项
**优先级:中 | 难度:中**
- [ ] 自定义 Slug 格式(支持模板变量:`{{title}}`, `{{date}}`
@@ -44,14 +78,14 @@
- [ ] SEO 元数据设置(meta description、keywords
- [ ] 文章属性控制(评论开关、置顶、优先级、可见性)
### 5. 内容预览
### 3. 内容预览
**优先级:中 | 难度:中**
- [ ] 发布前预览 Halo 渲染效果
- [ ] 预览不同主题下的样式
- [ ] 实时预览面板
### 6. 状态管理和日志
### 4. 状态管理和日志
**优先级:中 | 难度:中**
- [ ] 插件面板:显示所有同步状态
@@ -59,28 +93,21 @@
- [ ] 错误日志和告警
- [ ] 同步状态图标(同步中、已同步、有冲突)
### 7. 搜索和过滤
**优先级:低 | 难度:中**
- [ ] 按分类/标签筛选 Halo 文章列表
- [ ] 搜索已发布文章
- [ ] 高级筛选条件(日期范围、发布状态)
### 8. 模板支持
### 5. 模板支持
**优先级:低 | 难度:中**
- [ ] 发布模板:预定义 frontmatter 结构
- [ ] 快捷键自定义默认值
- [ ] 模板变量支持
### 9. 命令面板增强
### 6. 命令面板增强
**优先级:低 | 难度:低**
- [ ] 键盘快捷键支持
- [ ] 快速切换站点
- [ ] 快捷操作菜单
### 10. 用户界面优化
### 7. 用户界面优化
**优先级:低 | 难度:中**
- [ ] 进度条显示
@@ -92,11 +119,11 @@
## 推荐优先级(建议优先实现)
1. **图片/附件上传** - 核心功能缺失,影响使用体验
2. **批量操作** - 提升工作效率
3. **高级发布选项** - 增加灵活性
4. **状态管理和日志** - 问题排查和监控
5. **双向同步增强** - 高级用户需求
1. **双向同步增强** - 高级用户需求
2. **高级发布选项** - 增加灵活性
3. **状态管理和日志** - 问题排查和监控
4. **内容预览** - 提升用户体验
5. **模板支持** - 提升工作效率
---
@@ -138,4 +165,31 @@ POST /apis/api.console.halo.run/v1alpha1/attachments/upload
---
请确认需要实现哪些功能,我将制定详细的技术方案。
## 文件结构
```
src/
├── commands/ # 命令相关
│ ├── import-markdown.ts # ✅ 导入 Markdown 文件
│ ├── delete-post.ts # ✅ 删除文章
│ ├── manage-taxonomy.ts # ✅ 管理标签/分类
│ ├── export-post.ts # ✅ 导出文章
│ └── search-posts.ts # ✅ 搜索文章
├── modals/ # 弹窗相关
│ ├── file-preview-modal.ts # ✅ 文件预览
│ ├── delete-confirm-modal.ts # ✅ 删除确认
│ ├── tag-manager-modal.ts # ✅ 标签管理
│ ├── category-manager-modal.ts # ✅ 分类管理
│ └── search-modal.ts # ✅ 搜索弹窗
├── service/ # 服务层
│ ├── index.ts # Halo API 服务
│ └── image-uploader.ts # ✅ 图片上传
├── utils/ # 工具层
│ ├── image.ts # ✅ 图片处理
│ └── ...
└── main.ts # 主入口
```
---
最后更新:2024年
-45
View File
@@ -1,45 +0,0 @@
{
"download_info": {
"download_time": "2026-04-26 15:10:32",
"repository_url": "https://github.com/halo-sigs/obsidian-halo",
"branch": "main"
},
"version_info": {
"release_tag": "1.1.1",
"version": "1.1.1",
"commit_id": "4c6579b3b426ebd71eddde59b23d6b019051ce65",
"release_date": "2025-04-16T10:16:33Z"
},
"files_integrity": {
"main.js": {
"size_bytes": 466090,
"status": "verified"
},
"manifest.json": {
"size_bytes": 284,
"status": "verified"
},
"README.md": {
"size_bytes": 2160,
"status": "verified"
},
"styles.css": {
"size_bytes": 172,
"status": "verified"
}
},
"archive_info": {
"archive_name": "obsidian-halo-v1.1.1.zip",
"archive_path": "D:\\Code\\Obsidian\\obsidian-halo-v1.1.1.zip",
"archive_size_bytes": 1692875,
"local_repository_path": "D:\\Code\\Obsidian\\obsidian-halo"
},
"metadata": {
"plugin_id": "halo",
"plugin_name": "Halo",
"min_app_version": "0.15.0",
"description": "Halo's Obsidian integration supports publishing content to Halo sites",
"author": "Ryan Wang",
"license": "MIT"
}
}
Binary file not shown.
+89
View File
@@ -0,0 +1,89 @@
import i18next from "i18next";
import { Notice, requestUrl } from "obsidian";
import { openSiteSelectionModal } from "../site-selection-modal";
import type HaloPlugin from "../main";
import type { HaloSite } from "../settings";
export async function exportPostAsMarkdown(plugin: HaloPlugin, postName: string, title: string): Promise<void> {
try {
let site = plugin.settings.sites[0];
if (plugin.settings.sites.length > 1) {
site = await openSiteSelectionModal(plugin);
}
const headers = {
Authorization: `Bearer ${site.token}`,
};
const snapshot = (await requestUrl({
url: `${site.url}/apis/uc.api.content.halo.run/v1alpha1/posts/${postName}/draft?patched=true`,
headers,
}).json) as any;
const { "content.halo.run/patched-raw": raw } = snapshot?.metadata?.annotations || {};
if (!raw) {
new Notice(i18next.t("export.error_no_content"));
return;
}
const content = `---
title: ${title}
---
${raw}`;
const fileName = `${title}.md`;
const file = await plugin.app.vault.create(fileName, content);
plugin.app.workspace.getLeaf().openFile(file);
new Notice(i18next.t("export.notice_export_success", { fileName }));
} catch (error) {
console.error("[HaloPlugin] Export as markdown failed:", error);
new Notice(i18next.t("export.error_export_failed"));
}
}
export async function exportPostAsJson(plugin: HaloPlugin, postName: string, title: string): Promise<void> {
try {
let site = plugin.settings.sites[0];
if (plugin.settings.sites.length > 1) {
site = await openSiteSelectionModal(plugin);
}
const headers = {
Authorization: `Bearer ${site.token}`,
};
const post = (await requestUrl({
url: `${site.url}/apis/uc.api.content.halo.run/v1alpha1/posts/${postName}`,
headers,
}).json) as any;
const snapshot = (await requestUrl({
url: `${site.url}/apis/uc.api.content.halo.run/v1alpha1/posts/${postName}/draft?patched=true`,
headers,
}).json) as any;
const exportData = {
post: post,
content: {
raw: snapshot?.metadata?.annotations?.["content.halo.run/patched-raw"] || "",
content: snapshot?.metadata?.annotations?.["content.halo.run/patched-content"] || "",
rawType: snapshot?.spec?.rawType || "markdown",
},
exportedAt: new Date().toISOString(),
};
const fileName = `${title}.json`;
const file = await plugin.app.vault.create(fileName, JSON.stringify(exportData, null, 2));
plugin.app.workspace.getLeaf().openFile(file);
new Notice(i18next.t("export.notice_export_success", { fileName }));
} catch (error) {
console.error("[HaloPlugin] Export as JSON failed:", error);
new Notice(i18next.t("export.error_export_failed"));
}
}
@@ -0,0 +1,24 @@
import i18next from "i18next";
import { Notice } from "obsidian";
import { openSiteSelectionModal } from "../site-selection-modal";
import { openSearchModal } from "../modals/search-modal";
import type HaloPlugin from "../main";
export async function searchPosts(plugin: HaloPlugin): Promise<void> {
try {
if (plugin.settings.sites.length === 0) {
new Notice(i18next.t("command.pull_post.error_no_sites"));
return;
}
let site = plugin.settings.sites[0];
if (plugin.settings.sites.length > 1) {
site = await openSiteSelectionModal(plugin);
}
openSearchModal(plugin, site);
} catch (error) {
console.error("[HaloPlugin] Search posts failed:", error);
new Notice(i18next.t("common.error_connection_failed"));
}
}
+26
View File
@@ -36,6 +36,17 @@
},
"manage_categories": {
"name": "Manage categories"
},
"search_posts": {
"name": "Search Halo posts"
},
"export_markdown": {
"name": "Export as Markdown",
"error_not_published": "This document is not published to Halo"
},
"export_json": {
"name": "Export as JSON",
"error_not_published": "This document is not published to Halo"
}
},
"settings": {
@@ -223,6 +234,21 @@
"notice_delete_success": "Category deleted successfully",
"error_delete_failed": "Failed to delete category"
},
"search_modal": {
"title": "Search Halo Posts",
"search_placeholder": "Enter keywords to search...",
"loading": "Loading...",
"no_results": "No matching posts found",
"result_count": "Found {count} posts",
"button_view": "View",
"button_export_md": "Export MD",
"button_export_json": "Export JSON"
},
"export": {
"error_no_content": "Unable to get post content",
"error_export_failed": "Export failed",
"notice_export_success": "Exported to file: {fileName}"
},
"common": {
"error_connection_failed": "Connection failed",
"button_close": "Close",
+26
View File
@@ -36,6 +36,17 @@
},
"manage_categories": {
"name": "管理分类"
},
"search_posts": {
"name": "搜索 Halo 文章"
},
"export_markdown": {
"name": "导出为 Markdown",
"error_not_published": "此文档还未发布到 Halo"
},
"export_json": {
"name": "导出为 JSON",
"error_not_published": "此文档还未发布到 Halo"
}
},
"settings": {
@@ -223,6 +234,21 @@
"notice_delete_success": "分类删除成功",
"error_delete_failed": "分类删除失败"
},
"search_modal": {
"title": "搜索 Halo 文章",
"search_placeholder": "输入关键词搜索...",
"loading": "加载中...",
"no_results": "没有找到匹配的文章",
"result_count": "找到 {count} 篇文章",
"button_view": "查看",
"button_export_md": "导出 MD",
"button_export_json": "导出 JSON"
},
"export": {
"error_no_content": "无法获取文章内容",
"error_export_failed": "导出失败",
"notice_export_success": "已导出到文件: {fileName}"
},
"common": {
"error_connection_failed": "连接失败",
"button_close": "关闭",
+26
View File
@@ -36,6 +36,17 @@
},
"manage_categories": {
"name": "管理分類"
},
"search_posts": {
"name": "搜索 Halo 文章"
},
"export_markdown": {
"name": "導出為 Markdown",
"error_not_published": "此文件還未發佈到 Halo"
},
"export_json": {
"name": "導出為 JSON",
"error_not_published": "此文件還未發佈到 Halo"
}
},
"settings": {
@@ -223,6 +234,21 @@
"notice_delete_success": "分類刪除成功",
"error_delete_failed": "分類刪除失敗"
},
"search_modal": {
"title": "搜索 Halo 文章",
"search_placeholder": "輸入關鍵詞搜索...",
"loading": "加載中...",
"no_results": "沒有找到匹配的文章",
"result_count": "找到 {count} 篇文章",
"button_view": "查看",
"button_export_md": "導出 MD",
"button_export_json": "導出 JSON"
},
"export": {
"error_no_content": "無法獲取文章內容",
"error_export_failed": "導出失敗",
"notice_export_success": "已導出到文件: {fileName}"
},
"common": {
"error_connection_failed": "連接失敗",
"button_close": "關閉",
+40
View File
@@ -6,6 +6,8 @@ import { openPostSelectionModal } from "./post-selection-model";
import { importFromMarkdownFile } from "./commands/import-markdown";
import { deletePost } from "./commands/delete-post";
import { manageTags, manageCategories } from "./commands/manage-taxonomy";
import { exportPostAsMarkdown, exportPostAsJson } from "./commands/export-post";
import { searchPosts } from "./commands/search-posts";
import { DEFAULT_SETTINGS, type HaloSetting, HaloSettingTab, type HaloSite } from "./settings";
import HaloService from "./service";
import { openSiteSelectionModal } from "./site-selection-modal";
@@ -141,6 +143,44 @@ export default class HaloPlugin extends Plugin {
},
});
this.addCommand({
id: "search-posts",
name: i18next.t("command.search_posts.name"),
callback: async () => {
await searchPosts(this);
},
});
this.addCommand({
id: "export-post-as-markdown",
name: i18next.t("command.export_markdown.name"),
editorCallback: async () => {
const { activeEditor } = this.app.workspace;
if (!activeEditor || !activeEditor.file) return;
const matterData = this.app.metadataCache.getFileCache(activeEditor.file)?.frontmatter;
if (!matterData?.halo?.name) {
new Notice(i18next.t("command.export_markdown.error_not_published"));
return;
}
await exportPostAsMarkdown(this, matterData.halo.name, matterData.title || activeEditor.file.basename);
},
});
this.addCommand({
id: "export-post-as-json",
name: i18next.t("command.export_json.name"),
editorCallback: async () => {
const { activeEditor } = this.app.workspace;
if (!activeEditor || !activeEditor.file) return;
const matterData = this.app.metadataCache.getFileCache(activeEditor.file)?.frontmatter;
if (!matterData?.halo?.name) {
new Notice(i18next.t("command.export_json.error_not_published"));
return;
}
await exportPostAsJson(this, matterData.halo.name, matterData.title || activeEditor.file.basename);
},
});
this.addSettingTab(new HaloSettingTab(this));
}
+188
View File
@@ -0,0 +1,188 @@
import type { ListedPost } from "@halo-dev/api-client";
import i18next from "i18next";
import { Modal, Notice, Setting, requestUrl } from "obsidian";
import type HaloPlugin from "../main";
import type { HaloSite } from "../settings";
import { exportPostAsMarkdown, exportPostAsJson } from "../commands/export-post";
export function openSearchModal(plugin: HaloPlugin, site: HaloSite): void {
const modal = new SearchModal(plugin, site);
modal.open();
}
class SearchModal extends Modal {
private posts: ListedPost[] = [];
private filteredPosts: ListedPost[] = [];
private searchKeyword = "";
private filter: "all" | "published" | "draft" = "all";
constructor(
private readonly plugin: HaloPlugin,
private readonly site: HaloSite,
) {
super(app);
}
async onOpen() {
const { contentEl } = this;
contentEl.empty();
contentEl.createEl("h2", {
text: i18next.t("search_modal.title"),
});
const searchInput = new Setting(contentEl)
.setName(i18next.t("search_modal.search_placeholder"))
.addText((text) => {
text.setPlaceholder(i18next.t("search_modal.search_placeholder"))
.onChange((value) => {
this.searchKeyword = value;
this.filterPosts();
this.renderResults();
});
});
const filterEl = contentEl.createDiv("search-filter");
const allButton = filterEl.createEl("button", {
text: i18next.t("post_selection_modal.filter_all"),
cls: this.filter === "all" ? "active" : "",
});
allButton.addEventListener("click", async () => {
this.filter = "all";
await this.loadPosts();
this.renderResults();
});
const publishedButton = filterEl.createEl("button", {
text: i18next.t("post_selection_modal.filter_published"),
cls: this.filter === "published" ? "active" : "",
});
publishedButton.addEventListener("click", async () => {
this.filter = "published";
await this.loadPosts();
this.renderResults();
});
const draftButton = filterEl.createEl("button", {
text: i18next.t("post_selection_modal.filter_draft"),
cls: this.filter === "draft" ? "active" : "",
});
draftButton.addEventListener("click", async () => {
this.filter = "draft";
await this.loadPosts();
this.renderResults();
});
const resultsEl = contentEl.createDiv("search-results");
resultsEl.createEl("p", { text: i18next.t("search_modal.loading") });
await this.loadPosts();
this.renderResults();
}
private async loadPosts() {
try {
let labelSelector = "content.halo.run/deleted=false";
if (this.filter === "published") {
labelSelector = "content.halo.run/deleted=false,content.halo.run/published=true";
} else if (this.filter === "draft") {
labelSelector = "content.halo.run/deleted=false,content.halo.run/published=false";
}
const response = await requestUrl({
url: `${this.site.url}/apis/uc.api.content.halo.run/v1alpha1/posts?labelSelector=${encodeURIComponent(labelSelector)}&size=100`,
headers: {
Authorization: `Bearer ${this.site.token}`,
},
});
this.posts = response.json.items || [];
this.filterPosts();
} catch (error) {
new Notice(i18next.t("common.error_connection_failed"));
}
}
private filterPosts() {
if (!this.searchKeyword) {
this.filteredPosts = this.posts;
return;
}
const keyword = this.searchKeyword.toLowerCase();
this.filteredPosts = this.posts.filter((post) => {
const title = (post.post.spec.title || "").toLowerCase();
const slug = (post.post.spec.slug || "").toLowerCase();
return title.includes(keyword) || slug.includes(keyword);
});
}
private renderResults() {
const { contentEl } = this;
const resultsEl = contentEl.querySelector(".search-results") as HTMLElement;
if (!resultsEl) return;
resultsEl.empty();
if (this.filteredPosts.length === 0) {
resultsEl.createEl("p", { text: i18next.t("search_modal.no_results") });
return;
}
resultsEl.createEl("p", {
text: i18next.t("search_modal.result_count", { count: this.filteredPosts.length }),
cls: "result-count"
});
for (const post of this.filteredPosts) {
this.renderPostItem(post, resultsEl);
}
}
private renderPostItem(post: ListedPost, container: HTMLElement) {
const postEl = container.createDiv("search-result-item");
const headerEl = postEl.createDiv("result-header");
const titleEl = headerEl.createEl("h3", {
text: post.post.spec.title || i18next.t("post_selection_modal.untitled"),
});
const statusEl = headerEl.createSpan({
text: post.post.spec.publish ? i18next.t("post_selection_modal.status_published") : i18next.t("post_selection_modal.status_draft"),
cls: post.post.spec.publish ? "status-published" : "status-draft",
});
const metaEl = postEl.createDiv("result-meta");
metaEl.createEl("span", { text: `Slug: ${post.post.spec.slug}` });
metaEl.createEl("span", { text: ` | ${new Date(post.post.metadata.creationTimestamp).toLocaleDateString()}` });
const actionsEl = postEl.createDiv("result-actions");
actionsEl.createEl("button", {
text: i18next.t("search_modal.button_view"),
}).addEventListener("click", () => {
window.open(`${this.site.url}/archives/${post.post.spec.slug}`, "_blank");
});
actionsEl.createEl("button", {
text: i18next.t("search_modal.button_export_md"),
}).addEventListener("click", async () => {
await exportPostAsMarkdown(this.plugin, post.post.metadata.name, post.post.spec.title);
});
actionsEl.createEl("button", {
text: i18next.t("search_modal.button_export_json"),
}).addEventListener("click", async () => {
await exportPostAsJson(this.plugin, post.post.metadata.name, post.post.spec.title);
});
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
-544
View File
@@ -1,544 +0,0 @@
{
"inventory_metadata": {
"generated_at": "2026-04-26 15:15:00",
"vault_path": "D:\\Code\\Obsidian",
"obsidian_config_path": "D:\\Code\\Obsidian\\.obsidian",
"plugins_path": "D:\\Code\\Obsidian\\.obsidian\\plugins",
"themes_path": "D:\\Code\\Obsidian\\.obsidian\\themes"
},
"summary": {
"total_community_plugins": 33,
"total_core_plugins": 24,
"total_themes": 1,
"enabled_community_plugins": 33,
"desktop_only_plugins": 2,
"cross_platform_plugins": 31
},
"community_plugins": [
{
"id": "calendar",
"name": "Calendar",
"version": "1.5.10",
"description": "Calendar view of your daily notes",
"author": "Liam Cain",
"min_app_version": "0.9.11",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\calendar",
"has_data_file": true,
"has_styles": false,
"status": "active"
},
{
"id": "cmdr",
"name": "Commander",
"version": "0.5.4",
"description": "Customize your workspace by adding commands everywhere, create Macros and supercharge your mobile toolbar.",
"author": "jsmorabito & phibr0",
"min_app_version": "1.4.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\cmdr",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "copilot",
"name": "Copilot",
"version": "3.2.7",
"description": "Your AI Copilot: Chat with Your Second Brain, Learn Faster, Work Smarter.",
"author": "Logan Yang",
"min_app_version": "0.15.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\copilot",
"has_data_file": true,
"has_styles": true,
"status": "active",
"configuration": {
"default_model": "MiniMax-M2.7|anthropic",
"embedding_model": "text-embedding-3-small|openai",
"has_api_key_configured": true,
"has_custom_prompts": true,
"autonomous_agent_enabled": true
}
},
{
"id": "dataview",
"name": "Dataview",
"version": "0.5.68",
"description": "Complex data views for the data-obsessed.",
"author": "Michael Brenan",
"min_app_version": "0.13.11",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\dataview",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-admonition",
"name": "Admonition",
"version": "10.3.2",
"description": "Enhanced callouts for Obsidian.md",
"author": "Jeremy Valentine",
"min_app_version": "1.1.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-admonition",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-banners",
"name": "Banners",
"version": "1.3.3",
"description": "Add banner images to your notes!",
"author": "Danny Hernandez",
"min_app_version": "0.13.21",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-banners",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.20.6",
"description": "Sketch Your Mind. An Obsidian plugin to edit and view Excalidraw drawings.",
"author": "Zsolt Viczian",
"min_app_version": "1.5.7",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-excalidraw-plugin",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-git",
"name": "Git",
"version": "2.38.0",
"description": "Integrate Git version control with automatic backup and other advanced features.",
"author": "Vinzent",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-git",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-kanban",
"name": "Kanban",
"version": "2.0.51",
"description": "Create markdown-backed Kanban boards in Obsidian.",
"author": "mgmeyers",
"min_app_version": "1.0.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-kanban",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-tasks-plugin",
"name": "Tasks",
"version": "7.23.1",
"description": "Track tasks across your vault. Supports due dates, recurring tasks, done dates, sub-set of checklist items, and filtering.",
"author": "Clare Macrae and Ilyas Landikov",
"min_app_version": "1.4.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-tasks-plugin",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "templater-obsidian",
"name": "Templater",
"version": "2.18.1",
"description": "Create and use templates",
"author": "SilentVoid",
"min_app_version": "1.5.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\templater-obsidian",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "quickadd",
"name": "QuickAdd",
"version": "2.12.0",
"description": "Quickly add new pages or content to your vault.",
"author": "Christian B. B. Houmann",
"min_app_version": "1.11.4",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\quickadd",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-style-settings",
"name": "Style Settings",
"version": "1.0.9",
"description": "Offers controls for adjusting theme, plugin, and snippet CSS variables.",
"author": "mgmeyers",
"min_app_version": "0.11.5",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-style-settings",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-minimal-settings",
"name": "Minimal Theme Settings",
"version": "8.2.1",
"description": "Change the colors, fonts and features of Minimal Theme.",
"author": "@kepano",
"min_app_version": "1.11.1",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-minimal-settings",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-hover-editor",
"name": "Hover Editor",
"version": "0.11.26",
"description": "Transform the Page Preview hover popover into a fully working editor instance",
"author": "NothingIsLost",
"min_app_version": "1.5.8",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-hover-editor",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "omnisearch",
"name": "Omnisearch",
"version": "1.28.2",
"description": "A search engine that just works",
"author": "Simon Cambier",
"min_app_version": "1.7.2",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\omnisearch",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "floating-toc",
"name": "floating toc",
"version": "2.7.0",
"description": "This is a floating Toc plugin that hovers a table of content containing a header level on the notes sidebar.",
"author": "Cuman",
"min_app_version": "0.14.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\floating-toc",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "editing-toolbar",
"name": "Editing Toolbar",
"version": "3.1.18",
"description": "The Obsidian Editing Toolbar is modified from cmenu, which provides more powerful customization settings.",
"author": "Cuman",
"min_app_version": "0.14.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\editing-toolbar",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-outliner",
"name": "Outliner",
"version": "4.9.0",
"description": "Work with your lists like in Workflowy or RoamResearch.",
"author": "Viacheslav Slinko",
"min_app_version": "1.8.7",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-outliner",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-importer",
"name": "Importer",
"version": "1.8.4",
"description": "Import data from Notion, Evernote, Apple Notes, Microsoft OneNote, Google Keep, Bear, Roam, Textbundle, CSV, and HTML files.",
"author": "Obsidian",
"min_app_version": "0.15.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-importer",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "tag-wrangler",
"name": "Tag Wrangler",
"version": "0.6.4",
"description": "Rename, merge, toggle, and search tags from the tags view",
"author": "PJ Eby",
"min_app_version": "1.5.8",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\tag-wrangler",
"has_data_file": false,
"has_styles": false,
"status": "active"
},
{
"id": "table-editor-obsidian",
"name": "Advanced Tables",
"version": "0.22.1",
"description": "Improved table navigation, formatting, manipulation, and formulas",
"author": "Tony Grosinger",
"min_app_version": "1.0.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\table-editor-obsidian",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-icon-folder",
"name": "Iconize",
"version": "2.14.7",
"description": "Add icons to anything you desire in Obsidian, including files, folders, and text.",
"author": "Florian Woelki",
"min_app_version": "0.9.12",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-icon-folder",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "recent-files-obsidian",
"name": "Recent Files",
"version": "1.7.4",
"description": "List files by most recently opened",
"author": "Tony Grosinger",
"min_app_version": "0.16.3",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\recent-files-obsidian",
"has_data_file": true,
"has_styles": true,
"status": "active"
},
{
"id": "remotely-save",
"name": "Remotely Save",
"version": "0.5.25",
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
"author": "fyears",
"min_app_version": "0.13.21",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\remotely-save",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "homepage",
"name": "Homepage",
"version": "4.2.2",
"description": "Open a specified note, canvas, base, or workspace on startup, or set it for quick access later.",
"author": "novov",
"min_app_version": "1.4.10",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\homepage",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "easy-typing-obsidian",
"name": "Easy Typing",
"version": "5.5.15",
"description": "This plugin aims to enhance and optimize the editing experience in Obsidian",
"author": "yaozhuwa",
"min_app_version": "0.15.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\easy-typing-obsidian",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "obsidian-mind-map",
"name": "Mind Map",
"version": "1.1.0",
"description": "A plugin to preview notes as Markmap mind maps",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\obsidian-mind-map",
"has_data_file": false,
"has_styles": false,
"status": "active"
},
{
"id": "mermaid-tools",
"name": "Mermaid Tools",
"version": "1.3.0",
"description": "Improved Mermaid.js experience for Obsidian: visual toolbar with common elements & more",
"author": "dartungar",
"min_app_version": "1.4.0",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\mermaid-tools",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "multi-column-markdown",
"name": "Multi-Column Markdown",
"version": "0.9.1",
"description": "This plugin adds functionality to create markdown documents with multiple columns of content viewable within Obsidian's preview mode",
"author": "Cameron Robinson",
"min_app_version": "1.5.3",
"is_desktop_only": false,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\multi-column-markdown",
"has_data_file": false,
"has_styles": true,
"status": "active"
},
{
"id": "mousewheel-image-zoom",
"name": "Mousewheel Image zoom",
"version": "1.0.24",
"description": "This plugin enables you to increase/decrease the size of an image by scrolling",
"author": "Nico Jeske",
"min_app_version": "0.9.12",
"is_desktop_only": true,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\mousewheel-image-zoom",
"has_data_file": false,
"has_styles": false,
"status": "active",
"warning": "Desktop-only plugin"
},
{
"id": "drawio-obsidian",
"name": "Diagrams",
"version": "1.5.4",
"description": "Draw.io diagrams for Obsidian.",
"author": "Sam Greenhalgh",
"min_app_version": "0.9.12",
"is_desktop_only": true,
"enabled": true,
"install_path": "D:\\Code\\Obsidian\\.obsidian\\plugins\\drawio-obsidian",
"has_data_file": true,
"has_styles": true,
"status": "active",
"warning": "Desktop-only plugin"
}
],
"core_plugins": {
"enabled": [
"file-explorer",
"global-search",
"switcher",
"graph",
"backlink",
"canvas",
"outgoing-link",
"tag-pane",
"page-preview",
"daily-notes",
"templates",
"note-composer",
"command-palette",
"editor-status",
"bookmarks",
"outline",
"word-count",
"file-recovery",
"sync",
"bases"
],
"disabled": [
"footnotes",
"properties",
"slash-command",
"markdown-importer",
"zk-prefixer",
"random-note",
"slides",
"audio-recorder",
"workspaces",
"publish",
"webviewer"
]
},
"themes": [
{
"name": "Minimal",
"version": "8.1.6",
"author": "@kepano",
"min_app_version": "1.9.0",
"install_path": "D:\\Code\\Obsidian\\.obsidian\\themes\\Minimal",
"status": "active"
}
],
"verification_notes": {
"how_to_verify_plugins_downloaded": [
"1. 检查插件目录:导航到 D:\\Code\\Obsidian\\.obsidian\\plugins",
"2. 每个插件都有自己的子文件夹,文件夹名称就是插件ID",
"3. 必需的插件文件包括:",
" - manifest.json:插件元数据文件",
" - main.js:插件的主代码文件",
" - styles.css:可选的样式文件(如果插件有UI样式)",
" - data.json:可选的数据存储文件(如果插件有用户配置)",
"4. 验证方法:在文件资源管理器中打开插件文件夹,确认所需文件存在",
"5. 检查插件是否启用:查看 community-plugins.json 文件,确认插件ID在列表中",
"6. 查看 Obsidian 设置:在 Obsidian 应用中进入 Settings > Community Plugins 查看已安装和启用的插件"
],
"compatibility_check": [
"所有插件的 minAppVersion 要求都已满足",
"Obsidian 版本应 >= 1.8.7(基于 obsidian-outliner 的要求)",
"所有插件都标记为 isDesktopOnly: false,除了 drawio-obsidian 和 mousewheel-image-zoom"
],
"dependencies": [
"Minimal Theme Settings 需要配合 Minimal 主题使用",
"Style Settings 可用于调整主题和插件的 CSS 变量",
"多个插件可能依赖 Dataview 插件进行数据查询"
]
}
}
+12 -12
View File
@@ -13,19 +13,19 @@ halo:
## 一、现有标签列表
| 标签名称 | 显示名称 | 颜色 |
|---------|---------|------|
| xie-hui-dong-tai | 协会动态 | #B8E986 |
| 5fen-zhong-su-lan | 5分钟速览 | #50E3C2 |
| ji-shu-shen-qian | 技术深潜 | #4A90E2 |
| xiao-yuan-ai | 校园AI | #9013FE |
| jing-sai-zhi-nan | 竞赛指南 | #BD10E0 |
| xiang-mu-shi-zhan | 项目实战 | #417505 |
| ling-ji-chu-ru-men | 零基础入门 | #7ED321 |
| kai-yuan-kuang-jia | 开源框架 | #8B572A |
| 标签名称 | 显示名称 | 颜色 |
| ---------------------- | ----- | ------- |
| xie-hui-dong-tai | 协会动态 | #B8E986 |
| 5fen-zhong-su-lan | 5分钟速览 | #50E3C2 |
| ji-shu-shen-qian | 技术深潜 | #4A90E2 |
| xiao-yuan-ai | 校园AI | #9013FE |
| jing-sai-zhi-nan | 竞赛指南 | #BD10E0 |
| xiang-mu-shi-zhan | 项目实战 | #417505 |
| ling-ji-chu-ru-men | 零基础入门 | #7ED321 |
| kai-yuan-kuang-jia | 开源框架 | #8B572A |
| mo-xing-qing-liang-hua | 模型轻量化 | #417505 |
| yin-si-ji-suan | 隐私计算 | #F5A623 |
| aizhi-neng-ti | AI智能体 | #D0021B |
| yin-si-ji-suan | 隐私计算 | #F5A623 |
| aizhi-neng-ti | AI智能体 | #D0021B |
---
-1
View File
@@ -1,5 +1,4 @@
# 深度学习完全指南:从理论基础到实践入门
# 深度学习完全指南:从理论基础到实践入门
> 作者:人工智能协会比赛部部长