文件格式参考

更新日期:2026-07-17

本页是Plainva仓库中每一个文件在磁盘上的精确格式约定。它的写作目的是让一个工具——或者另一个程序、脚本或AI助手——可以直接读取并安全地编辑仓库文件,而不必经过Plainva的用户界面。如果你只使用这款应用本身,永远不需要用到这一页;其他手册页面涵盖了日常使用方法。

这里的一切都是纯粹的UTF-8文本。笔记是带有YAML Frontmatter的Markdown;数据库是YAML。没有任何东西是私有专用格式,也没有任何东西被隐藏。

黄金法则(请先读这里)

  1. 笔记才是真相来源。.base只是一个视图。 属性的保存在每篇笔记各自的Frontmatter中——绝不在.base里。要修改一个值,请编辑笔记本身。
  2. 笔记始终保持Obsidian原生格式。 在笔记的Frontmatter中,只写入普通的标量和列表(字符串、数字、布尔值、ISO日期、YAML列表)。绝不要在笔记中写入嵌套对象,也不要写入”激活/已选中”这类标记。
  3. .base只使用Obsidian的四个顶层键filtersformulaspropertiesviews)。添加任何其他顶层键都会让Obsidian拒绝整个文件。所有Plainva专有的数据都放在嵌套的plainva:子键之下。
  4. 保留你不理解的内容。 未知的键必须原样经受住一次读取/写入的往返过程。不要”清理”你不认识的键。
  5. 写入UTF-8无BOM,使用LF换行符。

仓库概览

仓库就是一个普通文件夹。你会遇到的文件类型:

文件是什么能否作为文本编辑
*.md一篇笔记:YAML Frontmatter + Markdown正文
*.base笔记之上的数据库视图(YAML)
index.md一个文件夹受管理的目录(保留名称)能,但需谨慎——参见index.md
log.md保留名称,目前未使用不要碰
图片、PDF等附件不能(二进制)
.plainva/Plainva的内部文件夹(备份、状态)不能——永远不要碰

保留名称index.mdlog.md永远不是普通笔记;不要在这些名称下创建普通内容。


笔记(.md

笔记是一个Markdown文件。文件最顶端有一个可选的YAML Frontmatter块(位于两行---之间),其中保存着属性;随后是Markdown正文。

---
type: Note
okf_version: "0.1"
tags: [project, active]
status: In progress
due: 2026-07-20
plainva:
  icon: "🚀"
  header_color: "#2f6f6f"
---
# My Project

A **bold** thought that links to [[Another Note]].

- [ ] First task

OKF Frontmatter字段

Plainva遵循OKF(Open Knowledge Format),这是一种最小化的约定。两个顶层字段:

字段类型含义
type字符串这是什么类型的文档(NoteDaily NoteProject……)。这是OKF实际要求的唯一字段。
okf_version字符串该文件所依据的约定版本,例如"0.1"。请加上引号,让YAML把它保留为字符串。

一个没有type的文件依然可以正常打开;它只是”不符合OKF约定”而已。单独缺少okf_version并不算违反约定。当你创建一篇新笔记时,添加type(以及okf_version)是良好的实践。完整的原理说明见OKF

属性值的序列化

每个Frontmatter键就是一个属性。请用其类型对应的原生YAML形式来写值:

属性类型YAML形式示例
文本标量字符串title: Hello
数字数字priority: 3
复选框布尔值done: true
日期ISO日期字符串due: 2026-07-20
日期和时间ISO日期时间字符串at: 2026-07-20T14:30:00
列表字符串的YAML列表authors: [Ada, Alan]
标签字符串的YAML列表tags: [project, active]
单选 / 状态单个标量字符串status: Done
多选字符串的YAML列表labels: [urgent, later]
URL / 邮箱 / 电话标量字符串site: https://example.org
关联(单个)Wiki链接字符串project: "[[Project Alpha]]"
关联(多个)Wiki链接字符串的YAML列表related: ["[[A]]", "[[B]]"]

单选/状态属性的”激活”值就是这个普通标量本身。允许选项的集合及其颜色保存在笔记中——它们保存在管理它的.base里(参见选项与颜色)。这样笔记就能保持100%的Obsidian原生格式。

请为Wiki链接的值加上引号("[[X]]")。不加引号的[[X]]在YAML中是一个流序列(flow sequence),不会按你的本意解析。

笔记中的plainva:命名空间

Plainva特有的笔记附加内容被统一归入一个单独的plainva:键之下,好让其他编辑器可以忽略它们:

含义
iconemoji字符,或lucide:<kebab命名>文档图标(Notion风格)
icon_color十六进制颜色(#rgb / #rrggbb / #rrggbbaalucide:图标的着色(emoji会忽略它)
header_color十六进制颜色全宽页眉色带
tasksfalse将此笔记的复选框从任务视图中排除
templateFor指向.base文件的Wiki链接列表把一个模板分配给列出的数据库(仅对模板文件夹内的笔记有意义)
pim映射(见下文)把笔记与一个外部日历日程、任务或邮件关联起来的锚点

以上这些都是可选的。如果一个都不写,就完全省略plainva:这个键。无效的值在读取时会被忽略,绝不会被当作错误。

pim是PIM集成功能的锚点(参见日历与外部任务邮件捕获)。当一篇笔记镜像一个外部对象时,Plainva会写入这样一个小型映射:uid加上account,并根据种类,搭配calendar(会议笔记)、kind: task + list(已同步的任务),或kind: email + mailbox(已捕获的邮件)。工具应当原样保留它;删除它只会让笔记与其远端对象脱离关联(远端不会删除任何内容)。示例:

plainva:
  pim:
    kind: task
    uid: MTIzNDU2
    account: 3f9c21ab
    list: MDEyMzQ1

templateFor是模板分配功能的字段契约(参见数据库(.base)):在模板文件夹内的笔记上,它列出了哪些数据库的条目菜单会默认显示该模板。值是完整的Wiki链接,包含.base扩展名——可以是裸链接("[[Tasks.base]]"会匹配任意文件夹中同名的文件,因此在单纯的文件夹移动后依然有效),也可以是带路径限定的链接("[[Projekte/Tasks.base]]"只匹配这个确切路径)。Plainva写入的都是裸链接,只有当存在两个同名的.base文件时,才会加上路径限定。也可以用一个标量代替列表。当从该模板创建一个条目时,templateFor——与其他plainva:键不同——不会被复制到新笔记中。

链接


数据库(.base

.base文件是YAML格式。它存储的是笔记之上的一个视图——哪些笔记(来源)、如何展示它们(视图)、如何筛选和排序,以及列的模式(schema)。它不存储任何笔记的值。该格式与Obsidian的Bases插件兼容。

硬性规则——违反其中任何一条,Obsidian就会拒绝整个文件

Plainva本身会在下次保存时自动修复违反后两条规则的旧文件,但直接写入文件的工具必须一开始就把它们写对。

属性标识符:何时使用note.前缀

这是最常见的绊脚石,因此明确说明:

位置形式示例
properties:映射的键带前缀note.statusfile.name
视图的order:列表带前缀[file.name, note.status]
视图的sort[].property带前缀note.due
筛选表达式内部裸键(不带前缀)status == "Done"
plainva子键内部(groupBydateFieldendFieldsubItemsProperty裸键(不带前缀)groupBy: status

经验法则:面向Obsidian的结构性字段使用note.<key>(内置字段如file.namefile.folderfile.mtime则使用file.<x>);而筛选公式内部或**plainva区块**内部的一切,都使用裸的Frontmatter键。

顶层键

plainva:子键映射表

所有Plainva专有的内容都被划入了命名空间。共有三个位置:

properties[<note.key>].plainva——每一列:

含义
input下方输入类型之一该列的字段类型
options选项对象的列表单选/状态/多选的精选值
relationBase相对于仓库的.base路径关联的目标数据库(参见关联
relationLimitone基数:单个链接。省略即为不限。
reverseOf{ base, property }标记一个计算得出的反向关联列(没有input

views[i].plainva——每一个视图:

含义
renderboard / calendar / timeline / graph / pinboard仅Plainva支持的视图类型(见下文)
groupBy裸属性键看板的分组列
dateField裸属性键日历/时间轴的起始日期
endField裸属性键时间轴的结束日期
coverImage裸属性键画廊的封面图属性
subItemsProperty裸属性键用于子项嵌套的自关联父级列
widthsid到像素的映射列宽
dateFormat字符串每视图的日期格式(default是隐含的——可省略)
pinboardOrder相对于仓库的路径列表未固定的便签板卡片的手动排序
pinboardPinned相对于仓库的路径列表已固定的卡片;列表顺序即为该分区的顺序
pinboardFilterBytags或裸的多选属性键便签板芯片栏的标签来源(tags是隐含的——可省略)

除了plainva区块之外,一个视图还可以携带一个原生的**views[i].filters对象——即按视图设置的属性筛选条件**(采用与文件级别filters相同的单根and/or/not语法)。Plainva会把属性筛选规则保存在这里,每个视图各存一份,因此每个视图可以独立筛选;文件级别的filters此时就只保留来源。Obsidian会原生地按视图应用views[i].filters

views[0].plainva——文件级别的键,仅允许出现在第一个视图上

含义
fileIconColor十六进制颜色数据库图标的着色(文件树/标签页/页眉)
newItemFolder相对于仓库的文件夹”新建”按钮存放新条目的位置
newItemTemplate相对于仓库的.md路径新条目的默认模板
contextFilters裸属性键的列表自我引用(“当前笔记”)筛选(见下文)

contextFilters是Plainva中与Notion”此页面”筛选相对应的功能。每一项都是一个属性键;当该数据库被嵌入到某篇笔记中时,会通过这个属性把它的行限定到那篇宿主笔记(这一步是通过链接索引解析的——拥有该关联的属性或普通链接属性会匹配指向宿主笔记的行,计算得出的反向列则匹配宿主笔记所指向的内容)。它特意不会写入原生的filters,因此Obsidian会忽略它并显示全部行;在Plainva中单独打开时(没有宿主笔记),它同样会被丢弃,也会显示全部行。多个条目之间按AND逻辑组合(须同时满足)。

输入类型

plainva.input是以下之一:

text  number  checkbox  date  datetime
select  status  multiselect
list  tags  url  email  phone
relation

一个计算得出的反向没有input——它仅通过reverseOf来标识。

选项与颜色

单选/状态/多选列可以携带一个精选的选项列表。每个选项:

options:
  - value: Open          # 必填
    color: amber         # 可选的调色板名称(见下文)
    group: Active        # 可选;仅STATUS类型使用——把选项排列为阶段
  - value: Done
    color: green
    group: Closed

color是一个调色板名称,不是CSS颜色。有效名称:graytealbluegreenambercoralpurplepink。未知的颜色会回退到由值本身派生出的颜色。

视图类型

views[i].type在磁盘上是一个原生的Obsidian类型。仅Plainva支持的渲染方式会被写成type: table加上一个plainva.render提示,这样Obsidian就会把它们降级显示为一个普通表格:

你想要的磁盘上的typeplainva.render
表格table
列表list
画廊cards
看板tableboard
日历tablecalendar
时间轴tabletimeline

筛选

filters选择哪些笔记属于这个数据库,并对其加以限定。

来源条件决定成员资格:

多个来源就是多个条目而已。完全没有filters = 仓库中的每篇笔记。

属性条件存放在哪里: 在文件级别,filters适用于所有视图。而Plainva会把属性筛选规则按视图分别保存在views[i].filters中(结构同样是单根的),文件级别只保留来源,这样每个视图就可以独立筛选。这两种写法对Obsidian来说都是合法的;一个工具可以写入其中任意一种。一个在文件级别带有属性条件的旧文件仍然可以正常使用——Plainva会在下一次保存时把这些条件分发到每个视图中。

属性条件使用裸属性名和以下运算符:

运算符表达式
等于status == "Done"
不等于status != "Done"
包含contains(labels, "urgent")
不包含!contains(labels, "urgent")
大于 / 小于priority > "2"priority < "5"
至少 / 至多priority >= "2"priority <= "5"
为空status == ""
不为空status != ""

结构(单根!): and / or / not三者之一,其条目是条件字符串——或者一层嵌套的{and:[...]} / {or:[...]}分组对象(Notion风格的分组)。以下示例组合了一个来源、一个条件和一个OR分组:

filters:
  and:
    - 'file.folder == "Projects"'
    - 'status != "Done"'
    - or:
        - 'priority == "1"'
        - 'priority == "2"'

一个完整的、带注释的.base示例

filters:
  and:
    - 'file.folder == "Projects"'          # 来源:Projects文件夹中的笔记
properties:
  note.status:                             # 列ID带有note.前缀
    displayName: Status                    # 可选的Obsidian列标签
    plainva:
      input: status
      options:
        - value: Open
          color: amber
          group: Active
        - value: Done
          color: green
          group: Closed
views:
  - type: table                            # 第一个视图:还携带文件级别的键
    name: All projects                     # 每个视图都需要一个名称
    order: [file.name, note.status]        # order使用带note.前缀的id
    plainva:
      fileIconColor: "#2f6f6f"
      newItemFolder: Projects
  - type: table                            # 看板就是原生表格 + 渲染提示
    name: Board
    plainva:
      render: board
      groupBy: status                      # groupBy使用裸键

关联(双向的契约)

关联把笔记彼此链接起来。这是手动编写时最容易出错的部分,因为它横跨三个地方。请让这三处保持一致。

  1. 值保存在来源笔记的Frontmatter中,以Wiki链接(或其列表)的形式:

    ---
    type: Task
    project: "[[Project Alpha]]"
    ---
  2. 来源.base声明关联列relationBase = 目标数据库;relationLimit: one表示单个链接):

    properties:
      note.project:
        plainva:
          input: relation
          relationBase: Projects.base
          relationLimit: one
  3. 目标.base可以用一个计算列展示反向关联。 它的值保存在任何地方——它们是从来源笔记的链接中派生出来的:

    properties:
      note.tasks:
        plainva:
          reverseOf:
            base: Tasks.base       # 来源的.base(相对于仓库的路径)
            property: project      # 裸的来源属性键

完整示例:任务 ↔ 项目

Tasks.base

filters:
  and:
    - 'file.folder == "Tasks"'
properties:
  note.status:
    plainva:
      input: status
      options:
        - value: Open
          color: amber
        - value: Done
          color: green
  note.project:
    plainva:
      input: relation
      relationBase: Projects.base
      relationLimit: one
views:
  - type: table
    name: All tasks
    order: [file.name, note.status, note.project]

Projects.base

filters:
  and:
    - 'file.folder == "Projects"'
properties:
  note.tasks:
    plainva:
      reverseOf:
        base: Tasks.base
        property: project
views:
  - type: table
    name: All projects
    order: [file.name, note.tasks]

Tasks/Write proposal.md

---
type: Task
okf_version: "0.1"
status: Open
project: "[[Project Alpha]]"
---
# Write proposal

Projects/Project Alpha.md

---
type: Project
okf_version: "0.1"
---
# Project Alpha

结果:在Projects.base中,Project Alpha的计算列tasks会列出”Write proposal”,因为那篇任务的project字段链接回了它。请注意,Project Alpha.md没有tasks:这个键——反向的一侧是计算出来的,从不存储。

关联的禁忌事项

自关联与子项

对于目标是同一个数据库的关联,让relationBase指向这同一个.base。要在表格视图中把子项嵌套在父项之下,请把views[i].plainva.subItemsProperty设置为裸的父级关联键。循环引用会被正确处理;关闭子项后,行会保持平铺,值仍会保留。


index.md(文件夹目录)

index.md是一个文件夹目录的保留名称。

如果你打算手动生成一个文件夹概览,安全的做法是不要添加该标记——这样Plainva就永远不会覆盖它。


关系图视图(plainva.render: "graph"

关系图视图的存储方式与其他每一个非原生视图相同:type: table加上渲染提示。它的选项存放在同一个views[i].plainva命名空间下:

views:
  - type: table
    name: Net
    plainva:
      render: graph
      graphEdges: [projekt]        # relation property keys drawn as edges
      graphColorBy: status         # select/status property -> node color
      graphSizeBy: prio            # number property -> node size
      graphShowExternal: true      # include relation targets outside the view
      graphShowIncoming: true      # 来自其他数据库、指向这些条目的关联(例如某个项目的任务)

所有关系图选项键都是可选的;未设置时应完全省略。Obsidian会把同一个文件渲染为一张普通表格,并且不能报错。

一个看板视图(plainva.render: "board")还可以额外携带views[i].plainva.boardColumnOrder——一份分组列键的列表(__UNGROUPED__标记无值的列),用于记住手动设定的列顺序。单选/状态看板则改为对属性的options重新排序。未设置时省略该键。

便签板视图(plainva.render: "pinboard"

便签板的存储方式与其他每一个非原生视图相同:type: table加上渲染提示。它的键存放在同一个views[i].plainva命名空间下:

views:
  - type: table
    name: Pinboard
    plainva:
      render: pinboard
      pinboardOrder:                  # 未固定卡片的手动排序
        - "Notes/Groceries.md"
      pinboardPinned:                 # 已固定;列表顺序 = 分区顺序
        - "Notes/Idea.md"
      pinboardFilterBy: note.labels   # 芯片栏的标签来源;省略即为tags

规则:已固定的路径不会在pinboardOrder中重复出现。两个列表都不包含的卡片会显示在最上方,按创建时间从新到旧排列。如果某个条目对应的文件已不存在,或已离开来源集合,则会被忽略,并在下一次保存时被清理。当一篇笔记被重命名或移动时,Plainva会自动更新这两个列表中对应的路径;外部工具也必须做到同样的处理。Obsidian会忽略这些键,并将该视图显示为一个表格。

不要碰的内容与安全性

另请参阅