Tolaria 下划线系统属性约定(ADR-0008):如何在 Markdown frontmatter 中隔离用户属性与系统内部属性

📅 发布时间:2026/9/13 23:46:20
Tolaria 下划线系统属性约定(ADR-0008):如何在 Markdown frontmatter 中隔离用户属性与系统内部属性
Tolaria 下划线系统属性约定ADR-0008如何在 Markdown frontmatter 中隔离用户属性与系统内部属性【免费下载链接】tolariaDesktop app to manage markdown knowledge bases项目地址: https://gitcode.com/GitHub_Trending/to/tolaria导读本文围绕 Tolaria基于 Tauri React 的 Markdown 知识库管理桌面应用的架构决策记录 ADR-0008《Underscore convention for system properties》展开剖析其下划线前缀 系统属性约定的设计动机、完整规则与双端实现。阅读本文后你将掌握如何在 note/type 文档的 frontmatter 中书写_前缀系统字段、哪些键被规范化canonical写入、如何兼容旧键读取、以及 Rust 与 TypeScript 两套解析器如何协同保证系统属性不会泄漏到属性面板、搜索与过滤中。背景属性面板为何会越用越乱Tolaria 采用文件系统即真相源filesystem source of truth见 ADR-0002的架构notes、types、projects 等实体的全部配置都存放在 Markdown 文件的 YAML frontmatter 中。随着功能扩张越来越多的配置型字段被塞进 frontmatter置顶属性pinned properties、类型图标type icons、颜色colors、侧边栏标签sidebar labels、排序字段sort order等。这些字段本质上属于系统内部元数据——它们驱动 UI 渲染与行为普通用户在日常记笔记时通常不应直接编辑。如果它们与用户自定义属性混在同一个 Properties 面板中面板就会变得杂乱甚至可能被用户误改导致界面异常。ADR-0008见 docs/adr/0008-underscore-system-properties.md要解决的核心问题正是如何用一条简单、可读、跨端统一的规则把用户可见属性与系统内部属性在 frontmatter 层面明确区分开。决策以_前缀声明系统属性ADR 的最终决策可以概括为一条铁律任何名称以_开头的 frontmatter 字段都是系统属性。它从 Properties 面板中隐藏、不暴露给搜索与过滤但在 raw editor原始编辑模式中仍然可编辑。frontmatter 解析器在把属性传给 UI 之前会过滤掉所有_*字段。这条规则带来三个直接后果隐藏而非删除系统属性只是从面向普通用户的界面属性面板、搜索、过滤器中隐去文件内容本身不受影响字段依然保留在 frontmatter 中。保留可访问性高级用户仍可通过 raw editor 直接读写这些字段规避了系统属性一旦隐藏就无法恢复的问题。一条通用规则不需要维护哪些键是系统键的硬编码清单来判断显隐前缀本身就足以表达身份。为什么选择下划线前缀备选方案对比ADR 文档记录了两个被否决的替代方案理解它们有助于把握这条约定的边界方案思路优点缺点结论Option A采用下划线前缀约定_icon、_color、_order、_pinned_properties规则简单、在原始文件中清晰可读、是普适的通用约定用户必须先知道这条约定才能访问系统字段✅ 选定Option B独立的 YAML 块或嵌套的_system:键分离更彻底解析更复杂破坏了扁平的 key-value frontmatter 模型❌ 否决Option C独立的 sidecar 文件如.meta.yml完全分离文件数量翻倍同步难度加大❌ 否决Option B 与 Option C 的共同问题是打破了 Tolaria 基于单一 Markdown 文件 扁平 frontmatter的数据模型参见 ADR-0006 扁平 vault 结构 与 ADR-0025 type 字段规范化。相比之下下划线前缀方案零解析负担、零额外文件代价仅仅是用户需要知道约定——而这一点由 UI 隐藏机制兜底普通用户根本无需关心。规范化系统属性清单与读写规则ADR-0008 明确定义了首批规范化的系统属性键canonical keys并规定了与旧键的兼容策略Canonical key旧键读取时兼容回退写入方_archivedArchived、archivedArchive 操作_trashedTrashed、trashedTrash 操作_trashed_atTrashed at、trashed_atTrash 操作_favorite—Favorite 切换_favorite_index—Favorite 排序两条核心规则写规则Write rule写入时永远使用带_前缀的 canonical 键。读规则Read rule读取时同时接受 canonical 键与 legacy 键不区分大小写但不在读取时重写文件——历史文件的迁移是另一件独立的事separate concern。这五对键是 ADR 记录的最低限实际仓库中系统元数据键族远比此更大。从当前源码的 src-tauri/src/frontmatter/keys.rs 可以看到完整的已知键表除上述五个外还包括_icon别名icon——类型/笔记图标_order别名order——排序权重_sidebar_label别名sidebar_label、sidebar label——侧边栏显示名_pinned_properties——置顶属性列表_sort别名sort——视图排序规则如title:asc_width别名width——编辑器/面板宽度模式_display、_list_properties_display——展示模式_organized——组织状态标记这些键的读取、写入、别名解析均集中在该表中维护并有专门的测试保证_pinned_properties、_list_properties_display等字段必须停留在 canonical 键表中见 keys.rs 测试。真实示例demo vault 中的类型文档仓库自带的演示 vault demo-vault-v2/type/project.md 使用了 legacy 风格书写--- type: Type icon: rocket color: blue sidebar label: Projects ---注意这里icon、color、sidebar label都对应带_前缀的 canonical 系统键。根据 ADR 的读规则Rust 解析器会把这些 legacy 键规范化识别为_icon、_color、_sidebar_label并阻止它们泄漏进用户属性集合。这也印证了 ADR 关于读取兼容旧键、写入才规范化的设计——现有 demo vault 无需批量改写即可正常运行。双端实现Rust 与 TypeScript 的协作过滤ADR 明确要求前端与后端解析器都过滤_*字段。当前仓库中这一承诺由两套独立实现共同完成。TypeScript 端systemMetadata.ts 与 frontmatter.ts前端核心在 src/utils/systemMetadata.tsSYSTEM_METADATA_ALIAS_GROUPS第 1-14 行定义了系统元数据的 alias 组与 Rust 端的键表一一对应normalizePropertyKey第 48-50 行把键名 trim、转小写、空白替换为下划线实现大小写不敏感isSystemMetadataKey第 70-73 行是判定入口canonical.startsWith(_) || CANONICAL_BY_ALIAS.has(normalizePropertyKey(key))——既识别_前缀也识别 legacy aliascanonicalFrontmatterWriteKey第 61-64 行保证写入时总是落到 canonical 键。frontmatter 解析器 src/utils/frontmatter.ts 在解析阶段就通过canonicalFrontmatterKey第 2 行导入、第 117-119 行、第 132-143 行使用做键的规范化与冲突归并确保 UI 拿到的属性集合中不会混入系统字段的重复变体。属性面板如何隐藏系统字段在 UI 状态层src/hooks/usePropertyPanelState.ts 的isHiddenPropertyKey决定了哪些键不进面板function isHiddenPropertyKey(key: string): boolean { const canonicalKey canonicalSystemMetadataKey(key) if (canonicalKey _icon) return false return SKIP_KEYS.has(key.toLowerCase()) || isSystemMetadataKey(key) }其中isSystemMetadataKey(key)兜底隐藏所有系统元数据键。值得注意的是_icon的特例放行——从代码结构看_icon虽然以_开头但在属性面板中仍作为可编辑图标字段向用户呈现其余系统键一律隐藏。这与 ADR 的隐藏但不删除、高级用户可经 raw editor 访问精神互补对高频且安全的_icon提供友好入口对_order、_pinned_properties等内部键则完全交给 raw editor。Rust 端FrontmatterKeyRule 与 is_reserved后端在 src-tauri/src/frontmatter/keys.rs 中用FrontmatterKeyRule结构统一描述每个已知键的read_key、write_key、aliases与是否canonicalize_on_write。关键判定是is_reserved第 144-146 行pub(crate) fn is_reserved(self) - bool { self.normalized().starts_with(_) || is_known_frontmatter_key(self) }任何以_开头的键即使不在已知键表中都被视为保留键normalized()第 140-142 行做 trim、转小写、空白转下划线的规范化与 TypeScript 端normalizePropertyKey行为一致。这个保留位被 src-tauri/src/vault/frontmatter.rs 等处的属性过滤逻辑引用当键被判定为 reserved 时它不会进入properties或relationships集合自然也就不会暴露给搜索、过滤与 UI。测试验证系统元数据不泄漏仓库用一组专门测试锁死了这条约定见 src-tauri/src/vault/system_metadata_tests.rsparses_canonical_system_metadata_keys_icon: rocket、_color: blue、_order: 4、_sidebar_label: Projects、_sort: title:asc能被正确解析为类型元数据字段parses_legacy_system_metadata_keys_without_property_leaksicon、color、order、sidebar label、sort这些 legacy 键被识别为系统字段且不会泄漏进 properties/relationshipsparses_known_aliases_from_central_key_rules_without_leaks混合大小写与混合风格Is A、_color、sidebar_label的键全部归一化且都不进入用户属性集合ignores_unknown_underscore_keys_in_properties_and_relationships未登记过的_internal: secret和_hidden_link: [[secret]]同样被过滤——未知的_键也不会泄漏而普通用户字段Owner正常保留ignores_invalid_note_width_modes_width: expanded非法值被静默忽略说明系统键不仅管显隐还会做取值合法性校验。这套测试与 ADR 的不重写旧键、只过滤不迁移策略互为印证无论是 canonical 还是 legacy 写法最终到达 UI 的properties都只包含用户可编辑的属性。实践指南如何正确使用系统属性约定结合 ADR 与源码给出可落地的操作建议新增系统级字段必须用_前缀。ADR 的 Consequences 明确要求未来所有系统级 frontmatter 字段都必须遵守_field_name命名约定并在 keys.rs 与 systemMetadata.ts 的 alias 表中登记别名与写规则。写入走 canonical读取兼容 legacy。业务代码写 frontmatter 时调用canonicalFrontmatterWriteKeyTS 端或依赖 Rust 端canonicalize_on_write规则统一写_前缀键读取时isSystemMetadataKey/is_reserved负责识别历史写法。不要在一次读写中顺手重写用户文件的旧键。普通用户通过属性面板高级用户通过 raw editor。面板已由isHiddenPropertyKey兜底隐藏系统键需要微调_order、_pinned_properties等时切到 raw editor 直接编辑即可保存后解析器会重新规范化。关注再评估触发条件。ADR 给出的 re-evaluation trigger 是当系统属性数量增长到足以支撑结构化子对象sub-object时应重新评估是否仍用扁平_前缀约定届时可回头参考 Option B 的嵌套方案。总结ADR-0008 用一条_前缀 系统属性的约定在不引入额外文件、不破坏扁平 frontmatter 模型的前提下干净地解决了配置型字段污染用户属性面板的问题。其价值在于约定与实现的双重一致性Rust 的is_reserved与 TypeScript 的isSystemMetadataKey共享同一套语义前缀 alias 表属性面板、搜索、过滤全部受益于解析阶段的统一过滤而 raw editor 始终保有对系统字段的完整访问能力。对于任何希望在 Markdown 文件里混存用户数据与应用配置的知识库类应用这套约定都提供了低成本、可迁移的参考范式。进一步阅读ADR 目录总览、frontmatter 字段参考、类型系统文档、属性相关概念。【免费下载链接】tolariaDesktop app to manage markdown knowledge bases项目地址: https://gitcode.com/GitHub_Trending/to/tolaria创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考