Foam 0.46 新特性深度解析:Smart Folders、Foam CLI、图谱重构与 Query 体系
Foam 0.46 新特性深度解析:Smart Folders、Foam CLI、图谱重构与 Query 体系
导读
Foam 是一个运行于 VS Code 的个人知识管理(PKM)系统,通过 Wikilinks、Tag、双链图谱把零散的 Markdown 笔记编织成可检索、可导航的知识库。本文以仓库内 packages/foam-vscode/WHATS_NEW.md(更新日志)为主线,逐项拆解 Foam 近期版本引入的核心能力:用查询驱动的 Smart Folders(智能文件夹)、无需打开 VS Code 即可操作工作区的 Foam CLI、支持节点分组与命名视图的 图谱重构、可嵌入笔记的 Block 引用、以及贯穿编辑器/CLI/MCP 三端的 Foam Query 查询体系。读完本文,你将掌握这些功能的配置语法、使用流程与其底层实现原理,能够在自己的笔记工作区中直接落地。
一、Smart Folders:用查询驱动侧边栏视图
1.1 什么是 Smart Folders
Smart Folders 是 Foam 把“已保存的查询”呈现在 VS Code 侧边栏的方式。每个 Smart Folder 就是一个存放在 .foam/queries/<id>.yaml 的查询文件——它不是磁盘上的真实目录,而是一个动态视图:filter 命中哪些笔记,面板里就显示哪些笔记。官方文档把它定位为“在大型工作区中导航,而不必被僵化的标签层级锁死”的工具(见 docs/user/features/smart-folders.md)。
其价值在于:当你想查看“所有标记为 #wip 且未归档的笔记”时,不需要为每类场景新建文件夹,只需一个查询文件即可随时在侧边栏生成视图。
1.2 查询语法与配置示例
Smart Folders 的过滤语法与 foam-query 代码块完全一致。WHATS_NEW.md 给出的标准示例是创建一个 “Work in Progress” 智能文件夹:
# .foam/queries/work-in-progress.yaml
name: Work in Progress
filter:
and:
- tag: '#wip'
- not:
tag: '#archive'
sort: title ASC
将其中的要点展开:
name(可选):面板中显示的文件夹名,缺省时由文件名人性化生成;filter:笔记筛选条件,支持and/or/not组合结构化过滤器;sort:排序规则,如title ASC、backlink-count DESC;- 此外还可选
description、limit等字段,最小的合法文件只需一行filter: "#wip"。
1.3 创建、编辑与删除的完整流程
在侧边栏 Smart Folders 面板点击 Create Smart Folder,或执行命令 Foam: Create Smart Folder,向导会依次询问:
- 文件夹名称(如 "Work in Progress");
- 需要包含的标签(多选;按 Esc 跳过,直接手动编辑 YAML)。
Foam 随即在 .foam/queries/<name>.yaml 创建文件并自动打开编辑。编辑时点击面板中的铅笔图标或直接改 YAML 文件,变更会被自动监听并刷新;删除时点击垃圾桶图标并确认即可(详见 docs/user/features/smart-folders.md)。
1.4 源码实现:从 YAML 文件到树形面板
Smart Folders 功能在仓库中由三个模块协作完成(目录 packages/foam-vscode/src/vscode/features/smart-folders):
smart-folder-storage.ts:VS Code 侧对核心QueryStore的适配层。它用vscode.workspace.createFileSystemWatcher监听.foam/queries/目录的增删改,并在内存中缓存LoadedQuery,使面板刷新时无需重新读取全部 YAML。smart-folders-explorer.ts:树形视图提供器。每个 Smart Folder 对应一个SmartFolderTreeItem,展开时调用executeQuery执行查询并列出命中的笔记 URI;若 YAML 存在解析错误,则显示SmartFolderErrorTreeItem(带 warning 图标,点击可直接打开查询文件)。值得注意的细节是,查询结果通过buildFolderTree按工作区相对路径组装成文件夹树,并支持“按文件夹分组 / 平铺列表”两种展示模式(groupBy状态持久化在ContextMemento中)。create-smart-folder.ts:创建向导。通过 Quick Pick 选取现有工作区标签,用buildOrFilter把多个标签组装为or过滤器;工作区无标签时则回退为*过滤器,交由用户后续手动编辑 YAML 细化。index.ts:功能激活入口,注册create/edit/delete/refresh等命令,并监听foam.graph.onDidUpdate与工作区信任状态变更自动刷新面板。
一个关键约束:Smart Folder 查询可能包含 Jexl 表达式,因此只有当工作区处于**可信(trusted)**状态时才会执行(isTrusted: () => vscode.workspace.isTrusted),与 foam-query 块共用同一信任门控,默认失败安全(fail-safe)。
1.5 面板视图选项
在面板标题栏可以:
- Group By Folder / Flat list:按工作区路径分组显示命中笔记,或平铺成扁平列表;
- Refresh:手动重新执行所有查询。
二、Foam CLI:在终端里操作工作区
2.1 安装与快速上手
WHATS_NEW.md 指出,Foam 现在随附命令行界面(CLI),让你无需打开 VS Code 就能与工作区交互。两种使用方式:
# 免安装直接运行
npx foam-cli <command> [options]
# 或全局安装
npm install -g foam-cli
foam <command> [options]
快速上手(来源 packages/foam-cli/README.md):
cd /path/to/your/notes
foam # 查看可用命令
foam list notes # 列出所有笔记
foam lint # 运行工作区检查
2.2 工作区解析与全局选项
工作区根目录按以下顺序解析:
--workspace <dir>参数;FOAM_WORKSPACE环境变量;- 当前工作目录。
全局选项包括 --format text|json、--color / --no-color、--help、--version。所有命令都支持 --format json,便于脚本化消费;JSON 输出会自动禁用颜色以保证机器可读(实现见 packages/foam-cli/src/index.ts)。
从 packages/foam-cli/src/index.ts 的帮助文本可以看到,CLI 命令覆盖了 lint、list、note、outline、links、graph、daily、tag、grep、search、rename、query、mcp、update、config 等操作,其中 query 子命令正是为 Smart Folders 设计的(见下文)。
2.3 用 foam query 管理 Smart Folders
WHATS_NEW.md 明确说明:“Queries used for Smart Folders are also exposed in the CLI and via MCP tools.” 在 CLI 中,foam query 提供三个子命令(实现见 packages/foam-cli/src/commands/query.ts):
foam query <subcommand> [options]
Subcommands:
list List all saved queries
run <id> Run a saved query and print matching notes
show <id> Print the YAML body of a saved query
Common options:
--workspace <dir> Workspace root (default: FOAM_WORKSPACE env var, then cwd)
--format <fmt> Output: text (default) or json
示例:
foam query list # 列出所有已保存查询(含命中数量)
foam query run work-in-progress # 执行查询并打印匹配笔记的路径
foam query show work-in-progress # 打印该查询的 YAML 内容
实现细节上,list 会对每个查询调用 countMatches 统计当前命中数;run 通过 executeQuery 执行查询并输出匹配笔记的工作区相对路径;两者均支持 --format json 结构化输出。此外 CLI 还提供 foam update 检查并提示更新命令(npm install -g foam-cli@latest,见 packages/foam-cli/src/commands/update.ts)。
2.4 MCP 工具:让 AI Agent 也能执行查询
同一批查询通过 Model Context Protocol 暴露给 AI 客户端。在 packages/foam-mcp/src/tools/queries.ts 中注册了三个工具:
list_queries:列出工作区内所有已保存查询(.foam/queries/下的 YAML),含id、name、description、matchCount、errors;get_query:按id返回某个查询的描述符(filter、sort、limit 等)与元数据;run_query:执行查询并返回匹配资源——可以传id运行已保存查询,或传descriptor运行临时查询(两者必须恰好提供一个)。
这说明 Smart Folders 的查询语法是三端共享的:VS Code 面板、CLI、MCP 工具最终都调用 @foam/core 中的 executeQuery(详见后文)。
三、图谱重构:节点分组与命名视图
3.1 从 “Show Graph” 到交互式知识网络
WHATS_NEW.md 总结了图谱视图的三大改进:更优的 UX 重设计、节点分组(Node Groups)、新增命名视图(Graph Views)。图谱将笔记与标签呈现为节点,笔记间的链接与文件-标签关系呈现为边,节点随连接数增大而放大(见 docs/user/features/graph-view.md)。
打开方式:命令面板(Ctrl+Shift+P / Cmd+Shift+P)→ “Foam: Show Graph” → 回车。默认点击节点会在编辑器中打开源文件;若希望打开 Markdown 预览,可配置:
"foam.graph.navigateToPreview": true
3.2 节点分组(Groups)的匹配规则
图谱右上角的 Groups 面板控制节点可见性与配色,由三部分组成:
- Color by:默认配色策略——
None(各节点类型独立配色)、Type(按 frontmattertype属性)、Directory(按所在目录); - Built-in types:
tag、attachment、image、placeholder四类节点的复选框与色点,取消勾选可隐藏,点击色点可改色; - Custom groups:自定义分组规则,按属性匹配笔记子集并赋色。
自定义分组支持的匹配属性(详见 docs/user/features/graph-view.md):
| 属性 | 匹配方式 | 示例 |
|---|---|---|
type |
精确匹配笔记类型 | project |
path |
文件路径子串匹配 | journal |
tag |
精确匹配标签 | daily |
title |
标题子串匹配 | 任意标题片段 |
| 任意 frontmatter 键 | 精确匹配自定义属性 | 任意属性值 |
/regex/ 语法可用于模式匹配,例如 /^2024/ 匹配以 2024 开头的路径。分组叠加在默认配色之上,最后一个匹配的分组获胜;取消勾选某分组会隐藏仅属于该分组的笔记。
源码层,packages/foam-graph/src/lib/groups.ts 中的 matchesGroup 实现了上述匹配逻辑——若 match.value 以 /…/ 包裹则编译为正则(编译失败回退为精确匹配),resolveGroupColor 遍历启用的分组、后匹配者覆盖先匹配者,与文档所述“last match wins”完全一致。
3.3 命名视图:把图谱配置存进 settings.json
在 foam.graph.views 中预定义视图,名为 "Default" 的视图会在图谱打开时自动应用。完整示例:
"foam.graph.views": [
{
"name": "Default",
"colorBy": "directory",
"show": {
"tag": { "enabled": false },
"placeholder": { "enabled": false }
}
},
{
"name": "Journal",
"colorBy": "directory",
"show": {
"tag": { "enabled": false },
"placeholder": { "enabled": false }
},
"groups": [
{
"id": "journal",
"label": "path=journal",
"color": "#6bcb77",
"enabled": true,
"match": { "property": "path", "value": "journal" }
}
]
}
]
视图配置字段一览(均可选):
| 字段 | 说明 |
|---|---|
name |
面板标题显示名;用 "Default" 可在打开时自动应用 |
colorBy |
"none" / "directory" / "type" |
groups |
自定义分组规则数组 |
show |
内置类型配置,如 { "tag": { "enabled": true, "color": "#ff0000" } } |
background / fontSize / fontFamily / lineColor |
背景、字号、字体、连线颜色覆盖 |
可通过 keybindings.json 为命名视图绑定快捷键:
{
"key": "ctrl+shift+j",
"command": "foam-vscode.show-graph",
"args": { "view": "Journal" }
}
也可以不定义命名视图,直接内联传入配置(args.config)。当同时提供 view 与 config 时,config 会合并覆盖在命名视图之上——这套解析逻辑实现在 packages/foam-vscode/src/vscode/features/graph-webview/index.ts 的 resolveViewStyle / mergeStyles / viewConfigToStyle 中。
遗留配置提示:
foam.graph.style已标记为废弃(deprecated),建议改用foam.graph.views并定义一个"Default"视图来替代旧样式;但旧配置仍会作为最底层样式生效(见 docs/user/features/graph-view.md)。
四、Block 引用:链接到笔记内的具体区块
4.1 锚点语法
WHATS_NEW.md 提醒:Foam 已在编辑器与预览中完整支持区块的引用与嵌入(Block references)。Block anchors 允许你链接到笔记中的某个段落、列表项、标题或引用块,而不只是整篇笔记或章节标题(详见 docs/user/features/block-anchors.md)。
在任意块元素末尾放置 ^your-id(ID 可含字母、数字与连字符),^id 标记在预览中隐藏,属于元数据而非可见文本:
This is an important finding from the experiment. ^key-finding
## Methodology ^methodology
- Mix dry ingredients thoroughly ^dry-step
- 2 cups flour
- 1 tsp salt
多行段落把锚点放在最后一行末尾;列表项的锚点作用于整个条目(含子项);要锚定整个列表,可在最后一项之后紧接着写 ^shopping-list(不留空行);代码块与表格则把 ^id 写在闭合围栏/表格之后的自成一行(允许一个空行,兼容格式化器自动插入空行的情况)。
4.2 链接、嵌入与重命名
- 链接:
[[research-notes#^key-insight]]直接跳到区块;在 wikilink 内输入#^会获得块 ID 自动补全;支持显示文本[[research-notes#^key-insight|See the key insight]]。 - 嵌入:
![[research-notes#^key-insight]]仅内联展示被引用区块的内容,而非整篇笔记。 - 重命名:光标放在
^blockid锚点上按F2,Foam 会同步更新锚点及工作区内所有引用它的 wikilink(对应重命名提供器见 packages/foam-vscode/src/vscode/features/editing/block-rename-provider.ts)。 - 诊断:当块链接指向目标笔记中不存在的
^id时,Foam 给出警告并提供快速修复(从可用块 ID 中选择);同一文件内重复使用相同^id也会被标记,快速修复会替换为全新生成的唯一 ID。
五、Foam Queries:在预览中嵌入动态笔记列表
5.1 基本用法
WHATS_NEW.md 展示了 foam-query 代码块——直接在 Markdown 预览中嵌入动态、自动更新的笔记列表:
```foam-query
filter: "#project"
sort: title ASC
select: [title, path]
```
渲染结果是一个指向匹配笔记的可点击链接列表,且会随着笔记变化自动更新。查询会渲染在 Markdown 预览中(而非编辑器内),结果可点击跳回对应笔记。
5.2 查询选项与过滤器全解
查询选项包括:
filter:选择包含哪些笔记;select:选择展示哪些字段,默认title与path;sort:排序,如title ASC、backlink-count DESC(排序字段需同时出现在select中,否则投影后不可用);limit/offset:只显示前 n 条 / 跳过前 n 条;format:渲染为list、table或count。
过滤器分为简写与结构化两类(完整语法见 docs/user/features/foam-queries.md):
简写过滤器:
"#tag":含有该标签的笔记;"[[note-id]]":链接到或自该笔记的笔记(使用与 wikilink 相同的标识符);"/regex/":路径匹配正则的笔记;"*":全部笔记。
结构化过滤器(YAML):
```foam-query
filter:
and:
- tag: "#research"
- not:
path: "^/archive/"
select: [title, tags, backlink-count]
sort: title ASC
```
支持的过滤器键(可组合 and / or / not):
| 键 | 含义 |
|---|---|
tag |
含有该标签(如 tag: "#research") |
type |
Foam 检测到的文件类型——note(Markdown)或 image/attachment;注意这不是笔记 frontmatter 里的 type,按 frontmatter 过滤需用 jexl |
path |
路径匹配正则(如 path: "^/projects/") |
title |
标题匹配正则 |
links_to / links_from |
链接到/被给定笔记标识符链接;"$current" 指代包含查询的当前笔记 |
jexl |
针对每条笔记求值的 Jexl 表达式,如 `"resource.tags |
and / or / not |
逻辑组合 |
5.3 可展示字段与表格定制
可 select 的字段包括:title、path、filename、folder、extension、type、tags、aliases、sections、blocks、properties(可用 properties.<name> 取单项)、backlink-count、outlink-count、body(全文,frontmatter 已剔除、保留 H1)、content(同 body 但不含 H1 标题)、section[Label](指定小节内容)。选择多个字段时默认渲染为表格。
表格支持自定义列头与单元格链接行为:
```foam-query
filter: "#projects"
format: table
select:
- field: title
link: false # 将 title 渲染为纯文本
- field: filename
link: true # 改为让 filename 可点击
- properties.Status
```
默认只有 title 列可点击(链接到所在行笔记),其余字段为纯文本;{ field, label, link } 对象形式可逐项覆盖。注意 body、content、section[...] 在 VS Code Web 中不受支持(会显示内联警告)。
5.4 JavaScript 查询(foam-query-js)
当 YAML 不够用时,可用 foam-query-js 代码块,但**仅限可信工作区(trusted workspace)**执行——在可信工作区中它拥有与 VS Code 其余部分相同的权限(可读写文件、发起网络请求),因此只应在你信任的笔记中使用(详见 docs/user/features/foam-queries.md):
```foam-query-js
const recentResearch = foam.pages('#research')
.sortBy('title')
.limit(5)
.format('list');
render('Recent research notes:');
render(recentResearch);
```
foam.pages(filter?) 返回查询构建器,提供 where(fn)、sortBy(field, direction?)、limit(n)、offset(n)、select(fields)、format(fmt)、toArray() 等方法;foam.current 指代包含查询的笔记 URI(无活动文档时为 null),可写出相对当前笔记的查询。相较之下,jexl 过滤器在任意工作区都可运行——Jexl 语言设计上即为沙箱(无宿主全局、无文件系统、无网络)。
5.5 查询引擎的实现路径
整个查询体系在 @foam/core 中实现(核心文件 packages/foam-core/src/query/index.ts):
parseFilter:递归地把QueryFilter编译为资源谓词;path/title正则先经tryBuildUserRegex用safe-regex2做“灾难性回溯”防护,非法或不安全的正则被拒绝并产生警告;jexl通过独立的Jexl实例编译求值,内置length、lower、upper三个 transform。值得注意的历史包袱:旧的expression字段因曾使用eval()存在 RCE 风险,现已移除求值——遗留查询会匹配空集并给出弃用警告。projectResource:按select投影字段,uri恒定写入每行结果;body/content/section<a href="https://link.gitcode.com/i/84896a91a54d02a77e3bb1d6299c81fa" target="_blank">...]属于“需源字段”,必须由宿主注入SourceReader才能解析(VS Code 端实现见 [packages/foam-vscode/src/vscode/features/preview/foam-query-renderer.ts)。executeQuery:执行顺序为“先 filter → 排序 → offset/limit → 源字段懒加载”,这样select: [body]的大范围查询不会同步读取所有命中笔记(注释中明确说明此优化意图)。QueryResult:foam-query-js暴露的流式构建器,其.where()会切换到慢路径(先取全字段再过滤、排序、切片)。
六、Footnotes:脚注的渲染与导航
WHATS_NEW.md 提到 Foam 现已在编辑器和 Markdown 预览中支持脚注的渲染与导航。使用标准 Markdown 脚注语法即可(详见 docs/user/features/footnotes.md):
The study confirmed the hypothesis.[^1] Further work is needed.[^note]
[^1]: Smith et al., 2023, Journal of Examples.
[^note]: See the appendix for the full methodology.
脚注 ID 可以是数字或单词,定义可出现在文件任意位置(Foam 不依赖位置查找)。交互细节:
- 悬停引用
[^1]可内联查看定义; - 按
F12(或Ctrl+Click/Cmd+Click)从引用跳转到定义; - 脚注引用沿用 wikilink 的着色样式,预览中脚注渲染在笔记底部;
- 同一脚注可被多次引用:
This point[^caveat] is elaborated elsewhere.[^caveat]。
七、Notes Explorer 过滤:在大库中快速定位
最后,WHATS_NEW.md 提到 Notes Explorer 面板现已支持过滤,便于在大型笔记库中导航。相关实现见 packages/foam-vscode/src/vscode/features/notes/notes-explorer.ts:面板提供 foam-vscode.views.notes-explorer.set-filter 命令,弹出输入框后按 标题或路径 实时过滤(res.title 或 res.uri.path 的 toLowerCase().includes(needle) 匹配);过滤激活时折叠目录会被自动展开,并可通过 clear-filter 命令清除。同时该面板还内置“仅笔记 / 全部资源”切换、连接数显示(↑links ↓backlinks)、层级/扁平视图切换等能力。
八、把这些能力串起来:一个完整的实战闭环
以“管理进行中项目”为例,上述特性可以无缝衔接成一套工作流:
- 定义查询:在
.foam/queries/work-in-progress.yaml写入filter: { and: [ { tag: '#wip' }, { not: { tag: '#archive' } } ] },sort: title ASC; - 侧边栏查看:Smart Folders 面板即时出现该文件夹,命中笔记按目录分组展开;
- 终端批处理:
foam query run work-in-progress在 CI 或脚本中列出所有进行中的笔记路径; - AI 集成:通过 MCP 工具
run_query(传id或临时descriptor)让 Agent 查询工作区状态; - 可视化:在图谱中为
#wip笔记建立自定义分组并配色,随时观察项目网络结构; - 细节锚定:在预览中用
foam-query块嵌入相关笔记列表,用![[note#^block]]嵌入关键结论段落。
参考文档与源码位置
- 更新日志: packages/foam-vscode/WHATS_NEW.md
- Smart Folders 使用指南: docs/user/features/smart-folders.md|实现:packages/foam-vscode/src/vscode/features/smart-folders
- Foam Queries 语法手册: docs/user/features/foam-queries.md|引擎:packages/foam-core/src/query/index.ts
- 图谱视图指南: docs/user/features/graph-view.md|分组匹配:packages/foam-graph/src/lib/groups.ts
- Block 锚点: docs/user/features/block-anchors.md
- 脚注: docs/user/features/footnotes.md
- CLI: packages/foam-cli/README.md|query 子命令:packages/foam-cli/src/commands/query.ts
- MCP 查询工具: packages/foam-mcp/src/tools/queries.ts